require'pathname'require'uri'moduleSass::Tree# A static node reprenting a CSS rule.## @see Sass::TreeclassRuleNode<Node# The character used to include the parent selectorPARENT='&'# The CSS selector for this rule,# interspersed with {Sass::Script::Node}s# representing `#{}`-interpolation.# Any adjacent strings will be merged together.## @return [Array<String, Sass::Script::Node>]attr_accessor:rule# The CSS selector for this rule,# without any unresolved interpolation# but with parent references still intact.# It's only set once {Tree::Node#perform} has been called.## @return [Selector::CommaSequence]attr_accessor:parsed_rules# The CSS selector for this rule,# without any unresolved interpolation or parent references.# It's only set once {Tree::Node#cssize} has been called.## @return [Selector::CommaSequence]attr_accessor:resolved_rules# How deep this rule is indented# relative to a base-level rule.# This is only greater than 0 in the case that:## * This node is in a CSS tree# * The style is :nested# * This is a child rule of another rule# * The parent rule has properties, and thus will be rendered## @return [Fixnum]attr_accessor:tabs# Whether or not this rule is the last rule in a nested group.# This is only set in a CSS tree.## @return [Boolean]attr_accessor:group_end# @param rule [Array<String, Sass::Script::Node>]# The CSS rule. See \{#rule}definitialize(rule)merged=Sass::Util.merge_adjacent_strings(rule)@rule=Sass::Util.strip_string_array(merged)@tabs=0super()end# Compares the contents of two rules.## @param other [Object] The object to compare with# @return [Boolean] Whether or not this node and the other object# are the samedef==(other)self.class==other.class&&rule==other.rule&&superend# Adds another {RuleNode}'s rules to this one's.## @param node [RuleNode] The other nodedefadd_rules(node)@rule=Sass::Util.strip_string_array(Sass::Util.merge_adjacent_strings(@rule+["\n"]+node.rule))end# @return [Boolean] Whether or not this rule is continued on the next linedefcontinued?last=@rule.lastlast.is_a?(String)&&last[-1]==?,end# @see Node#to_sassdefto_sass(tabs,opts={})name=selector_to_sass(rule,opts)name="\\"+nameifname[0]==?:name.gsub(/^/,' '*tabs)+children_to_src(tabs,opts,:sass)end# @see Node#to_scssdefto_scss(tabs,opts={})name=selector_to_scss(rule,tabs,opts)res=name+children_to_src(tabs,opts,:scss)ifchildren.last.is_a?(CommentNode)&&children.last.silentres.slice!(-3..-1)res<<"\n"<<(' '*tabs)<<"}\n"endresend# Extends this Rule's selector with the given `extends`.## @see Node#do_extenddefdo_extend(extends)node=dupnode.resolved_rules=resolved_rules.do_extend(extends)nodeendprotected# Computes the CSS for the rule.## @param tabs [Fixnum] The level of indentation for the CSS# @return [String] The resulting CSSdef_to_s(tabs)output_style=styletabs=tabs+self.tabsrule_separator=output_style==:compressed?',':', 'line_separator=caseoutput_stylewhen:nested,:expanded;"\n"when:compressed;""else;" "endrule_indent=' '*(tabs-1)per_rule_indent,total_indent=[:nested,:expanded].include?(output_style)?[rule_indent,'']:['',rule_indent]total_rule=total_indent+resolved_rules.members.map{|seq|seq.to_a.join.gsub(/([^,])\n/m,style==:compressed?'\1 ':"\\1\n")}.join(rule_separator).split("\n").mapdo|line|per_rule_indent+line.stripend.join(line_separator)to_return=''old_spaces=' '*(tabs-1)spaces=' '*tabsifoutput_style!=:compressedif@options[:debug_info]to_return<<debug_info_rule.to_s(tabs)<<"\n"elsif@options[:line_comments]to_return<<"#{old_spaces}/* line #{line}"iffilenamerelative_filename=if@options[:css_filename]beginPathname.new(filename).relative_path_from(Pathname.new(File.dirname(@options[:css_filename]))).to_srescueArgumentErrornilendendrelative_filename||=filenameto_return<<", #{relative_filename}"endto_return<<" */\n"endendifoutput_style==:compactproperties=children.map{|a|a.to_s(1)}.join(' ')to_return<<"#{total_rule} { #{properties} }#{"\n"ifgroup_end}"elsifoutput_style==:compressedproperties=children.map{|a|a.to_s(1)}.join(';')to_return<<"#{total_rule}{#{properties}}"elseproperties=children.map{|a|a.to_s(tabs+1)}.join("\n")end_props=(output_style==:expanded?"\n"+old_spaces:' ')to_return<<"#{total_rule} {\n#{properties}#{end_props}}#{"\n"ifgroup_end}"endto_returnend# Runs SassScript interpolation in the selector,# and then parses the result into a {Sass::Selector::CommaSequence}.## @param environment [Sass::Environment] The lexical environment containing# variable and mixin valuesdefperform!(environment)@parsed_rules=Sass::SCSS::StaticParser.new(run_interp(@rule,environment),self.line).parse_selector(self.filename)superend# Converts nested rules into a flat list of rules.## @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]# The extensions defined for this tree# @param parent [RuleNode, nil] The parent node of this node,# or nil if the parent isn't a {RuleNode}def_cssize(extends,parent)node=superrules=node.children.select{|c|c.is_a?(RuleNode)||c.is_a?(MediaNode)}props=node.children.reject{|c|c.is_a?(RuleNode)||c.is_a?(MediaNode)||c.invisible?}unlessprops.empty?node.children=propsrules.each{|r|r.tabs+=1}ifstyle==:nestedrules.unshift(node)endrules.last.group_end=trueunlessparent||rules.empty?rulesend# Resolves parent references and nested selectors,# and updates the indentation based on the parent's indentation.## @param extends [Sass::Util::SubsetMap{Selector::Simple => Selector::Sequence}]# The extensions defined for this tree# @param parent [RuleNode, nil] The parent node of this node,# or nil if the parent isn't a {RuleNode}# @raise [Sass::SyntaxError] if the rule has no parents but uses `&`defcssize!(extends,parent)# It's possible for resolved_rules to be set if we've duplicated this node during @media bubblingself.resolved_rules||=@parsed_rules.resolve_parent_refs(parent&&parent.resolved_rules)superend# Returns an error message if the given child node is invalid,# and false otherwise.## {ExtendNode}s are valid within {RuleNode}s.## @param child [Tree::Node] A potential child node.# @return [Boolean, String] Whether or not the child node is valid,# as well as the error message to display if it is invaliddefinvalid_child?(child)superunlesschild.is_a?(ExtendNode)end# A hash that will be associated with this rule in the CSS document# if the {file:SASS_REFERENCE.md#debug_info-option `:debug_info` option} is enabled.# This data is used by e.g. [the FireSass Firebug extension](https://addons.mozilla.org/en-US/firefox/addon/103988).## @return [{#to_s => #to_s}]defdebug_info{:filename=>filename&&("file://"+URI.escape(File.expand_path(filename))),:line=>self.line}endprivatedefdebug_info_rulenode=DirectiveNode.new("@media -sass-debug-info")debug_info.map{|k,v|[k.to_s,v.to_s]}.sort.eachdo|k,v|rule=RuleNode.new([""])rule.resolved_rules=Sass::Selector::CommaSequence.new([Sass::Selector::Sequence.new([Sass::Selector::SimpleSequence.new([Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/,"\\\\\\0"),nil)])])])prop=PropNode.new([""],"",:new)prop.resolved_name="font-family"prop.resolved_value=Sass::SCSS::RX.escape_ident(v.to_s)rule<<propnode<<ruleendnode.options=@options.merge(:debug_info=>false,:line_comments=>false,:style=>:compressed)nodeendendend