Module CssParser
In: lib/css_parser.rb
lib/css_parser/parser.rb
lib/css_parser/regexps.rb
lib/css_parser/rule_set.rb

Methods

Classes and Modules

Class CssParser::CircularReferenceError
Class CssParser::Parser
Class CssParser::RemoteFileError
Class CssParser::RuleSet

Public Class methods

Calculates the specificity of a CSS selector per www.w3.org/TR/CSS21/cascade.html#specificity

Returns an integer.

Example

 CssParser.calculate_specificity('#content div p:first-line a:link')
 => 114

[Source]

     # File lib/css_parser.rb, line 108
108:   def CssParser.calculate_specificity(selector)
109:     a = 0
110:     b = selector.scan(/\#/).length
111:     c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX).length
112:     d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX).length
113: 
114:     (a.to_s + b.to_s + c.to_s + d.to_s).to_i
115:   rescue
116:     return 0
117:   end

Make url() links absolute.

Takes a block of CSS and returns it with all relative URIs converted to absolute URIs.

"For CSS style sheets, the base URI is that of the style sheet, not that of the source document." per www.w3.org/TR/CSS21/syndata.html#uri

Returns a string.

Example

 CssParser.convert_uris("body { background: url('../style/yellow.png?abc=123') };",
              "http://example.org/style/basic.css").inspect
 => "body { background: url('http://example.org/style/yellow.png?abc=123') };"

[Source]

     # File lib/css_parser.rb, line 132
132:   def self.convert_uris(css, base_uri)
133:     out = ''
134:     base_uri = URI.parse(base_uri) unless base_uri.kind_of?(URI)
135: 
136:     out = css.gsub(URI_RX) do |s|
137:       uri = $1.to_s
138:       uri.gsub!(/["']+/, '')
139:       # Don't process URLs that are already absolute
140:       unless uri =~ /^[a-z]+\:\/\//i
141:         begin
142:           uri = base_uri.merge(uri) 
143:         rescue; end
144:       end
145:       "url('" + uri.to_s + "')"
146:     end
147:     out
148:   end

Merge multiple CSS RuleSets by cascading according to the CSS 2.1 cascading rules (www.w3.org/TR/REC-CSS2/cascade.html#cascading-order).

Takes one or more RuleSet objects.

Returns a RuleSet.

Cascading

If a RuleSet object has its specificity defined, that specificity is used in the cascade calculations.

If no specificity is explicitly set and the RuleSet has one selector, the specificity is calculated using that selector.

If no selectors or multiple selectors are present, the specificity is treated as 0.

Example 1

  rs1 = RuleSet.new(nil, 'color: black;')
  rs2 = RuleSet.new(nil, 'margin: 0px;')

  merged = CssParser.merge(rs1, rs2)

  puts merged
  => "{ margin: 0px; color: black; }"

Example 2

  rs1 = RuleSet.new(nil, 'background-color: black;')
  rs2 = RuleSet.new(nil, 'background-image: none;')

  merged = CssParser.merge(rs1, rs2)

  puts merged
  => "{ background: none black; }"

[Source]

    # File lib/css_parser.rb, line 48
48:   def CssParser.merge(*rule_sets)
49:     @folded_declaration_cache = {}
50: 
51:     # in case called like CssParser.merge([rule_set, rule_set])
52:     rule_sets.flatten! if rule_sets[0].kind_of?(Array)
53:     
54:     unless rule_sets.all? {|rs| rs.kind_of?(CssParser::RuleSet)}
55:       raise ArgumentError, "all parameters must be CssParser::RuleSets."
56:     end
57: 
58:     return rule_sets[0] if rule_sets.length == 1
59: 
60:     # Internal storage of CSS properties that we will keep
61:     properties = {}
62: 
63:     rule_sets.each do |rule_set|
64:       rule_set.expand_shorthand!
65:       
66:       specificity = rule_set.specificity
67:       unless specificity
68:         if rule_set.selectors.length == 1
69:           specificity = calculate_specificity(rule_set.selectors[0])
70:         else
71:           specificity = 0
72:         end
73:       end
74: 
75:       rule_set.each_declaration do |property, value, is_important|
76:         # Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
77:         if not properties.has_key?(property) or
78:                is_important or # step 2
79:                properties[property][:specificity] < specificity or # step 3
80:                properties[property][:specificity] == specificity # step 4    
81:           properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}            
82:         end
83:       end
84:     end
85: 
86:     merged = RuleSet.new(nil, nil)
87: 
88:     # TODO: what about important
89:     properties.each do |property, details|
90:       merged[property.strip] = details[:value].strip
91:     end
92: 
93:     merged.create_shorthand!
94:     merged
95:   end

[Validate]