Class: CssParser::RuleSet
- Inherits:
-
Object
- Object
- CssParser::RuleSet
- Extended by:
- Forwardable
- Defined in:
- lib/css_parser/rule_set.rb
Direct Known Subclasses
Defined Under Namespace
Classes: Declarations
Constant Summary collapse
- RE_ELEMENTS_AND_PSEUDO_ELEMENTS =
Patterns for specificity calculations
/((^|[\s+>]+)\w+|:(first-line|first-letter|before|after))/i.freeze
- RE_NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES =
/(\.\w+)|(\[\w+)|(:(link|first-child|lang))/i.freeze
- BACKGROUND_PROPERTIES =
['background-color', 'background-image', 'background-repeat', 'background-position', 'background-size', 'background-attachment'].freeze
- LIST_STYLE_PROPERTIES =
['list-style-type', 'list-style-position', 'list-style-image'].freeze
- FONT_STYLE_PROPERTIES =
['font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', 'font-family'].freeze
- BORDER_STYLE_PROPERTIES =
['border-width', 'border-style', 'border-color'].freeze
- BORDER_PROPERTIES =
['border', 'border-left', 'border-right', 'border-top', 'border-bottom'].freeze
- NUMBER_OF_DIMENSIONS =
4
- DIMENSIONS =
[ ['margin', %w[margin-top margin-right margin-bottom margin-left]], ['padding', %w[padding-top padding-right padding-bottom padding-left]], ['border-color', %w[border-top-color border-right-color border-bottom-color border-left-color]], ['border-style', %w[border-top-style border-right-style border-bottom-style border-left-style]], ['border-width', %w[border-top-width border-right-width border-bottom-width border-left-width]] ].freeze
- WHITESPACE_REPLACEMENT =
'___SPACE___'
Instance Attribute Summary collapse
-
#selectors ⇒ Object
readonly
Array of selector strings.
-
#specificity ⇒ Object
Integer with the specificity to use for this RuleSet.
Instance Method Summary collapse
- #add_declaration! ⇒ Object (also: #[]=)
-
#create_background_shorthand! ⇒ Object
Looks for long format CSS background properties (e.g.
background-color
) and converts them into a shorthand CSSbackground
property. -
#create_border_shorthand! ⇒ Object
Combine border-color, border-style and border-width into border Should be run after create_dimensions_shorthand!.
-
#create_dimensions_shorthand! ⇒ Object
Looks for long format CSS dimensional properties (margin, padding, border-color, border-style and border-width) and converts them into shorthand CSS properties.
-
#create_font_shorthand! ⇒ Object
Looks for long format CSS font properties (e.g.
font-weight
) and tries to convert them into a shorthand CSSfont
property. -
#create_list_style_shorthand! ⇒ Object
Looks for long format CSS list-style properties (e.g.
list-style-type
) and converts them into a shorthand CSSlist-style
property. -
#create_shorthand! ⇒ Object
Create shorthand declarations (e.g.
margin
orfont
) whenever possible. -
#create_shorthand_properties!(properties, shorthand_property) ⇒ Object
Combine several properties into a shorthand one.
-
#declarations_to_s(options = {}) ⇒ Object
Return all declarations as a string.
- #delete ⇒ Object (also: #remove_declaration!)
-
#each_declaration ⇒ Object
Iterate through declarations.
-
#each_selector(options = {}) ⇒ Object
Iterate through selectors.
-
#expand_background_shorthand! ⇒ Object
Convert shorthand background declarations (e.g.
background: url("chess.png") gray 50% repeat fixed;
) into their constituent parts. -
#expand_border_shorthand! ⇒ Object
Split shorthand border declarations (e.g.
border: 1px red;
) Additional splitting happens in expand_dimensions_shorthand!. -
#expand_dimensions_shorthand! ⇒ Object
Split shorthand dimensional declarations (e.g.
margin: 0px auto;
) into their constituent parts. -
#expand_font_shorthand! ⇒ Object
Convert shorthand font declarations (e.g.
font: 300 italic 11px/14px verdana, helvetica, sans-serif;
) into their constituent parts. -
#expand_list_style_shorthand! ⇒ Object
Convert shorthand list-style declarations (e.g.
list-style: lower-alpha outside;
) into their constituent parts. -
#expand_shorthand! ⇒ Object
Split shorthand declarations (e.g.
margin
orfont
) into their constituent parts. - #extract_background_size_from(value) ⇒ Object
-
#get_value(property) ⇒ Object
(also: #[])
Get the value of a property.
-
#initialize(selectors, block, specificity = nil) ⇒ RuleSet
constructor
A new instance of RuleSet.
-
#to_s ⇒ Object
Return the CSS rule set as a string.
Constructor Details
#initialize(selectors, block, specificity = nil) ⇒ RuleSet
Returns a new instance of RuleSet.
240 241 242 243 244 245 |
# File 'lib/css_parser/rule_set.rb', line 240 def initialize(selectors, block, specificity = nil) @selectors = [] @specificity = specificity parse_selectors!(selectors) if selectors parse_declarations!(block) end |
Instance Attribute Details
#selectors ⇒ Object (readonly)
Array of selector strings.
227 228 229 |
# File 'lib/css_parser/rule_set.rb', line 227 def selectors @selectors end |
#specificity ⇒ Object
Integer with the specificity to use for this RuleSet.
230 231 232 |
# File 'lib/css_parser/rule_set.rb', line 230 def specificity @specificity end |
Instance Method Details
#add_declaration! ⇒ Object Also known as: []=
236 |
# File 'lib/css_parser/rule_set.rb', line 236 def_delegators :declarations, :add_declaration!, :delete |
#create_background_shorthand! ⇒ Object
Looks for long format CSS background properties (e.g. background-color
) and converts them into a shorthand CSS background
property.
Leaves properties declared !important alone.
498 499 500 501 502 503 504 505 506 507 508 509 |
# File 'lib/css_parser/rule_set.rb', line 498 def create_background_shorthand! # :nodoc: # When we have a background-size property we must separate it and distinguish it from # background-position by preceding it with a backslash. In this case we also need to # have a background-position property, so we set it if it's missing. # http://www.w3schools.com/cssref/css3_pr_background.asp if (declaration = declarations['background-size']) && !declaration.important declarations['background-position'] ||= '0% 0%' declaration.value = "/ #{declaration.value}" end create_shorthand_properties! BACKGROUND_PROPERTIES, 'background' end |
#create_border_shorthand! ⇒ Object
Combine border-color, border-style and border-width into border Should be run after create_dimensions_shorthand!
TODO: this is extremely similar to create_background_shorthand! and should be combined
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 |
# File 'lib/css_parser/rule_set.rb', line 515 def create_border_shorthand! # :nodoc: values = BORDER_STYLE_PROPERTIES.map do |property| next unless (declaration = declarations[property]) next if declaration.important # can't merge if any value contains a space (i.e. has multiple values) # we temporarily remove any spaces after commas for the check (inside rgba, etc...) next if declaration.value.gsub(/,\s/, ',').strip =~ /\s/ declaration.value end.compact return if values.size != BORDER_STYLE_PROPERTIES.size BORDER_STYLE_PROPERTIES.each do |property| declarations.delete(property) end declarations['border'] = values.join(' ') end |
#create_dimensions_shorthand! ⇒ Object
Looks for long format CSS dimensional properties (margin, padding, border-color, border-style and border-width) and converts them into shorthand CSS properties.
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 |
# File 'lib/css_parser/rule_set.rb', line 537 def create_dimensions_shorthand! # :nodoc: return if declarations.size < NUMBER_OF_DIMENSIONS DIMENSIONS.each do |property, dimensions| values = [:top, :right, :bottom, :left].each_with_index.with_object({}) do |(side, index), result| next unless (declaration = declarations[dimensions[index]]) result[side] = declaration.value end # All four dimensions must be present next if values.size != dimensions.size new_value = values.values_at(*compute_dimensions_shorthand(values)).join(' ').strip declarations[property] = new_value unless new_value.empty? # Delete the longhand values dimensions.each { |d| declarations.delete(d) } end end |
#create_font_shorthand! ⇒ Object
Looks for long format CSS font properties (e.g. font-weight
) and tries to convert them into a shorthand CSS font
property. All font properties must be present in order to create a shorthand declaration.
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 |
# File 'lib/css_parser/rule_set.rb', line 561 def create_font_shorthand! # :nodoc: return unless FONT_STYLE_PROPERTIES.all? { |prop| declarations.key?(prop) } new_value = String.new ['font-style', 'font-variant', 'font-weight'].each do |property| unless declarations[property].value == 'normal' new_value << declarations[property].value << ' ' end end new_value << declarations['font-size'].value unless declarations['line-height'].value == 'normal' new_value << '/' << declarations['line-height'].value end new_value << ' ' << declarations['font-family'].value declarations['font'] = new_value.gsub(/\s+/, ' ') FONT_STYLE_PROPERTIES.each { |prop| declarations.delete(prop) } end |
#create_list_style_shorthand! ⇒ Object
Looks for long format CSS list-style properties (e.g. list-style-type
) and converts them into a shorthand CSS list-style
property.
Leaves properties declared !important alone.
588 589 590 |
# File 'lib/css_parser/rule_set.rb', line 588 def create_list_style_shorthand! # :nodoc: create_shorthand_properties! LIST_STYLE_PROPERTIES, 'list-style' end |
#create_shorthand! ⇒ Object
Create shorthand declarations (e.g. margin
or font
) whenever possible.
464 465 466 467 468 469 470 471 |
# File 'lib/css_parser/rule_set.rb', line 464 def create_shorthand! create_background_shorthand! create_dimensions_shorthand! # border must be shortened after dimensions create_border_shorthand! create_font_shorthand! create_list_style_shorthand! end |
#create_shorthand_properties!(properties, shorthand_property) ⇒ Object
Combine several properties into a shorthand one
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
# File 'lib/css_parser/rule_set.rb', line 474 def create_shorthand_properties!(properties, shorthand_property) # :nodoc: values = [] properties_to_delete = [] properties.each do |property| next unless (declaration = declarations[property]) next if declaration.important values << declaration.value properties_to_delete << property end return if values.length <= 1 properties_to_delete.each do |property| declarations.delete(property) end declarations[shorthand_property] = values.join(' ') end |
#declarations_to_s(options = {}) ⇒ Object
Return all declarations as a string.
281 282 283 |
# File 'lib/css_parser/rule_set.rb', line 281 def declarations_to_s( = {}) declarations.to_s() end |
#delete ⇒ Object Also known as: remove_declaration!
236 |
# File 'lib/css_parser/rule_set.rb', line 236 def_delegators :declarations, :add_declaration!, :delete |
#each_declaration ⇒ Object
Iterate through declarations.
274 275 276 277 278 |
# File 'lib/css_parser/rule_set.rb', line 274 def each_declaration # :yields: property, value, is_important declarations.each do |property_name, value| yield property_name, value.value, value.important end end |
#each_selector(options = {}) ⇒ Object
Iterate through selectors.
Options
-
force_important
– boolean
Example
ruleset.each_selector do |sel, dec, spec|
...
end
264 265 266 267 268 269 270 271 |
# File 'lib/css_parser/rule_set.rb', line 264 def each_selector( = {}) # :yields: selector, declarations, specificity decs = declarations.to_s() if @specificity @selectors.each { |sel| yield sel.strip, decs, @specificity } else @selectors.each { |sel| yield sel.strip, decs, CssParser.calculate_specificity(sel) } end end |
#expand_background_shorthand! ⇒ Object
Convert shorthand background declarations (e.g. background: url("chess.png") gray 50% repeat fixed;
) into their constituent parts.
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/css_parser/rule_set.rb', line 304 def # :nodoc: return unless (declaration = declarations['background']) value = declaration.value.dup replacement = if value.match(CssParser::RE_INHERIT) BACKGROUND_PROPERTIES.map { |key| [key, 'inherit'] }.to_h else { 'background-image' => value.slice!(CssParser::RE_IMAGE), 'background-attachment' => value.slice!(CssParser::RE_SCROLL_FIXED), 'background-repeat' => value.slice!(CssParser::RE_REPEAT), 'background-color' => value.slice!(CssParser::RE_COLOUR), 'background-size' => extract_background_size_from(value), 'background-position' => value.slice!(CssParser::RE_BACKGROUND_POSITION) } end declarations.replace_declaration!('background', replacement, preserve_importance: true) end |
#expand_border_shorthand! ⇒ Object
Split shorthand border declarations (e.g. border: 1px red;
) Additional splitting happens in expand_dimensions_shorthand!
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/css_parser/rule_set.rb', line 334 def # :nodoc: BORDER_PROPERTIES.each do |k| next unless (declaration = declarations[k]) value = declaration.value.dup replacement = { "#{k}-width" => value.slice!(CssParser::RE_BORDER_UNITS), "#{k}-color" => value.slice!(CssParser::RE_COLOUR), "#{k}-style" => value.slice!(CssParser::RE_BORDER_STYLE) } declarations.replace_declaration!(k, replacement, preserve_importance: true) end end |
#expand_dimensions_shorthand! ⇒ Object
Split shorthand dimensional declarations (e.g. margin: 0px auto;
) into their constituent parts. Handles margin, padding, border-color, border-style and border-width.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/css_parser/rule_set.rb', line 352 def # :nodoc: DIMENSIONS.each do |property, (top, right, bottom, left)| next unless (declaration = declarations[property]) value = declaration.value.dup # RGB and HSL values in borders are the only units that can have spaces (within params). # We cheat a bit here by stripping spaces after commas in RGB and HSL values so that we # can split easily on spaces. # # TODO: rgba, hsl, hsla value.gsub!(RE_COLOUR) { |c| c.gsub(/(\s*,\s*)/, ',') } matches = split_value_preserving_function_whitespace(value) case matches.length when 1 values = matches.to_a * 4 when 2 values = matches.to_a * 2 when 3 values = matches.to_a values << matches[1] # left = right when 4 values = matches.to_a else raise ArgumentError, "Cannot parse #{value}" end replacement = [top, right, bottom, left].zip(values).to_h declarations.replace_declaration!(property, replacement, preserve_importance: true) end end |
#expand_font_shorthand! ⇒ Object
Convert shorthand font declarations (e.g. font: 300 italic 11px/14px verdana, helvetica, sans-serif;
) into their constituent parts.
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/css_parser/rule_set.rb', line 389 def # :nodoc: return unless (declaration = declarations['font']) # reset properties to 'normal' per http://www.w3.org/TR/CSS21/fonts.html#font-shorthand font_props = { 'font-style' => 'normal', 'font-variant' => 'normal', 'font-weight' => 'normal', 'font-size' => 'normal', 'line-height' => 'normal' } value = declaration.value.dup value.gsub!(%r{/\s+}, '/') # handle spaces between font size and height shorthand (e.g. 14px/ 16px) in_fonts = false matches = value.scan(/"(?:.*[^"])"|'(?:.*[^'])'|(?:\w[^ ,]+)/) matches.each do |m| m.strip! m.gsub!(/;$/, '') if in_fonts if font_props.key?('font-family') font_props['font-family'] += ", #{m}" else font_props['font-family'] = m end elsif m =~ /normal|inherit/i ['font-style', 'font-weight', 'font-variant'].each do |font_prop| font_props[font_prop] ||= m end elsif m =~ /italic|oblique/i font_props['font-style'] = m elsif m =~ /small-caps/i font_props['font-variant'] = m elsif m =~ /[1-9]00$|bold|bolder|lighter/i font_props['font-weight'] = m elsif m =~ CssParser::FONT_UNITS_RX if m.include?('/') font_props['font-size'], font_props['line-height'] = m.split('/', 2) else font_props['font-size'] = m end in_fonts = true end end declarations.replace_declaration!('font', font_props, preserve_importance: true) end |
#expand_list_style_shorthand! ⇒ Object
Convert shorthand list-style declarations (e.g. list-style: lower-alpha outside;
) into their constituent parts.
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 |
# File 'lib/css_parser/rule_set.rb', line 444 def # :nodoc: return unless (declaration = declarations['list-style']) value = declaration.value.dup replacement = if value =~ CssParser::RE_INHERIT LIST_STYLE_PROPERTIES.map { |key| [key, 'inherit'] }.to_h else { 'list-style-type' => value.slice!(CssParser::RE_LIST_STYLE_TYPE), 'list-style-position' => value.slice!(CssParser::RE_INSIDE_OUTSIDE), 'list-style-image' => value.slice!(CssParser::URI_RX_OR_NONE) } end declarations.replace_declaration!('list-style', replacement, preserve_importance: true) end |
#expand_shorthand! ⇒ Object
Split shorthand declarations (e.g. margin
or font
) into their constituent parts.
291 292 293 294 295 296 297 298 |
# File 'lib/css_parser/rule_set.rb', line 291 def # border must be expanded before dimensions end |
#extract_background_size_from(value) ⇒ Object
326 327 328 329 330 |
# File 'lib/css_parser/rule_set.rb', line 326 def extract_background_size_from(value) size = value.slice!(CssParser::RE_BACKGROUND_SIZE) size.sub(%r{^\s*/\s*}, '') if size end |
#get_value(property) ⇒ Object Also known as: []
Get the value of a property
248 249 250 251 252 |
# File 'lib/css_parser/rule_set.rb', line 248 def get_value(property) return '' unless (value = declarations[property]) "#{value};" end |
#to_s ⇒ Object
Return the CSS rule set as a string.
286 287 288 |
# File 'lib/css_parser/rule_set.rb', line 286 def to_s "#{@selectors.join(',')} { #{declarations} }" end |