css_parser 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c049fe40a4b774b56e00c790cf18b78c8a94298185435471893cedff65a79c17
4
- data.tar.gz: fd0600e1859e8bd188a12df4ccd252a1f75f4ae2f98707249c7750e622d5e2b4
3
+ metadata.gz: ecd734b97658689e087ca19039ea51377c23d9e1c4be6099b084006150255fce
4
+ data.tar.gz: d5a326fac6e3b57fe75fa3bc664ae8511a0e0dcf34f80dd5736ee8cc9709f5ee
5
5
  SHA512:
6
- metadata.gz: 53b2f435c485d393778679d3202943a99b83607af4ce5edac9a15aac4461fb252371b43d98b7bf1886254b143da9bff5a61492d7a36e74e44e77b44f8ba38318
7
- data.tar.gz: ab5fb0504811ebd05eb09e830447f832b67aad5e5559dd4be9e562c20d5bb4cc786165eb961b39867e8316c72ce90015de16e1857154784b4bf0fc700b92a34b
6
+ metadata.gz: ca0d59324036c9071c5f957235f10022d3db4841d4e4782e147f30e2cde1a1cd46873ede55db01b260676fc7289c0e7c4ad7e0e6ac7dbd0b38a1de8aa13333a1
7
+ data.tar.gz: da5fb20229ecfac93b0436a0ed6954c109908429628cd635751c58fd9b3d029227da06a60bb80f5f4bd2fac55851ac258e7ff59d09fea59e93157f38d21c0fac
@@ -22,6 +22,7 @@ module CssParser
22
22
 
23
23
  RE_URI = /(url\(\s*(\s*#{RE_STRING}\s*)\s*\))|(url\(\s*([!#$%&*\-~]|#{RE_NON_ASCII}|#{RE_ESCAPE})*\s*)\)/ixm.freeze
24
24
  URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im.freeze
25
+ URI_RX_OR_NONE = Regexp.union(URI_RX, /none/i)
25
26
  RE_GRADIENT = /[-a-z]*gradient\([-a-z0-9 .,#%()]*\)/im.freeze
26
27
 
27
28
  # Initial parsing
@@ -43,6 +44,7 @@ module CssParser
43
44
  'upper-latin', 'hebrew', 'armenian', 'georgian', 'cjk-ideographic', 'hiragana',
44
45
  'hira-gana-iroha', 'katakana-iroha', 'katakana', 'none'
45
46
  )
47
+ RE_IMAGE = Regexp.union(CssParser::URI_RX, CssParser::RE_GRADIENT, /none/i)
46
48
 
47
49
  STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
48
50
  STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
@@ -44,9 +44,7 @@ module CssParser
44
44
  end
45
45
 
46
46
  def to_s
47
- return value unless important
48
-
49
- "#{value} !important"
47
+ important ? "#{value} !important" : value
50
48
  end
51
49
 
52
50
  def ==(other)
@@ -88,15 +86,11 @@ module CssParser
88
86
 
89
87
  if value.is_a?(Value)
90
88
  declarations[property] = value
91
- return
92
- end
93
-
94
- if value.to_s.strip.empty?
95
- delete(property)
96
- return
89
+ elsif value.to_s.strip.empty?
90
+ delete property
91
+ else
92
+ declarations[property] = Value.new(value)
97
93
  end
98
-
99
- declarations[property] = Value.new(value)
100
94
  end
101
95
  alias add_declaration! []=
102
96
 
@@ -153,14 +147,18 @@ module CssParser
153
147
 
154
148
  # We should preserve subsequent declarations of the same properties
155
149
  # and prior important ones if replacement one is not important
156
- replacements = replacement_declarations.each.with_object({}) do |(key, value), result|
157
- # Replacement property doesn't exist, adding
158
- next result[key] = value unless declarations.key?(key)
159
-
160
- # Replacement property is important while existing one is not,
161
- # replacing unconditionally
162
- if value.important && !declarations[key].important
163
- result[key] = value
150
+ replacements = replacement_declarations.each.with_object({}) do |(key, replacement), result|
151
+ existing = declarations[key]
152
+
153
+ # No existing -> set
154
+ unless existing
155
+ result[key] = replacement
156
+ next
157
+ end
158
+
159
+ # Replacement more important than existing -> replace
160
+ if replacement.important && !existing.important
161
+ result[key] = replacement
164
162
  replaced_index = replacement_keys.index(key)
165
163
  replacement_keys.delete_at(replaced_index)
166
164
  replacement_values.delete_at(replaced_index)
@@ -168,13 +166,12 @@ module CssParser
168
166
  next
169
167
  end
170
168
 
171
- # Existing value is important while replacing is not, existing one
172
- # takes precedence
173
- next if !value.important && declarations[key].important
169
+ # Existing is more important than replacement -> keep
170
+ next if !replacement.important && existing.important
174
171
 
175
- # Importance of existing and replacement values are the same,
172
+ # Existing and replacement importance are the same,
176
173
  # value which is declared later wins
177
- result[key] = value if property_index > replacement_keys.index(key)
174
+ result[key] = replacement if property_index > replacement_keys.index(key)
178
175
  end
179
176
 
180
177
  return if replacements.empty?
@@ -245,9 +242,9 @@ module CssParser
245
242
 
246
243
  # Get the value of a property
247
244
  def get_value(property)
248
- return '' unless declarations.key?(property)
245
+ return '' unless (value = declarations[property])
249
246
 
250
- "#{declarations[property]};"
247
+ "#{value};"
251
248
  end
252
249
  alias [] get_value
253
250
 
@@ -301,19 +298,23 @@ module CssParser
301
298
  #
302
299
  # See http://www.w3.org/TR/CSS21/colors.html#propdef-background
303
300
  def expand_background_shorthand! # :nodoc:
304
- return unless declarations.key?('background')
305
-
306
- value = declarations['background'].value.dup
307
-
308
- replacement = BACKGROUND_PROPERTIES.map { |key| [key, 'inherit'] }.to_h if value.match(CssParser::RE_INHERIT)
309
- replacement ||= {
310
- 'background-image' => value.slice!(Regexp.union(CssParser::URI_RX, CssParser::RE_GRADIENT, /none/i)),
311
- 'background-attachment' => value.slice!(CssParser::RE_SCROLL_FIXED),
312
- 'background-repeat' => value.slice!(CssParser::RE_REPEAT),
313
- 'background-color' => value.slice!(CssParser::RE_COLOUR),
314
- 'background-size' => extract_background_size_from(value),
315
- 'background-position' => value.slice!(CssParser::RE_BACKGROUND_POSITION)
316
- }
301
+ return unless (declaration = declarations['background'])
302
+
303
+ value = declaration.value.dup
304
+
305
+ replacement =
306
+ if value.match(CssParser::RE_INHERIT)
307
+ BACKGROUND_PROPERTIES.map { |key| [key, 'inherit'] }.to_h
308
+ else
309
+ {
310
+ 'background-image' => value.slice!(CssParser::RE_IMAGE),
311
+ 'background-attachment' => value.slice!(CssParser::RE_SCROLL_FIXED),
312
+ 'background-repeat' => value.slice!(CssParser::RE_REPEAT),
313
+ 'background-color' => value.slice!(CssParser::RE_COLOUR),
314
+ 'background-size' => extract_background_size_from(value),
315
+ 'background-position' => value.slice!(CssParser::RE_BACKGROUND_POSITION)
316
+ }
317
+ end
317
318
 
318
319
  declarations.replace_declaration!('background', replacement, preserve_importance: true)
319
320
  end
@@ -328,9 +329,9 @@ module CssParser
328
329
  # Additional splitting happens in expand_dimensions_shorthand!
329
330
  def expand_border_shorthand! # :nodoc:
330
331
  BORDER_PROPERTIES.each do |k|
331
- next unless declarations.key?(k)
332
+ next unless (declaration = declarations[k])
332
333
 
333
- value = declarations[k].value.dup
334
+ value = declaration.value.dup
334
335
 
335
336
  replacement = {
336
337
  "#{k}-width" => value.slice!(CssParser::RE_BORDER_UNITS),
@@ -346,9 +347,9 @@ module CssParser
346
347
  # into their constituent parts. Handles margin, padding, border-color, border-style and border-width.
347
348
  def expand_dimensions_shorthand! # :nodoc:
348
349
  DIMENSIONS.each do |property, (top, right, bottom, left)|
349
- next unless declarations.key?(property)
350
+ next unless (declaration = declarations[property])
350
351
 
351
- value = declarations[property].value.dup
352
+ value = declaration.value.dup
352
353
 
353
354
  # RGB and HSL values in borders are the only units that can have spaces (within params).
354
355
  # We cheat a bit here by stripping spaces after commas in RGB and HSL values so that we
@@ -369,38 +370,39 @@ module CssParser
369
370
  values << matches[1] # left = right
370
371
  when 4
371
372
  values = matches.to_a
373
+ else
374
+ raise ArgumentError, "Cannot parse #{value}"
372
375
  end
373
376
 
374
377
  t, r, b, l = values
378
+ replacement = {top => t, right => r, bottom => b, left => l}
375
379
 
376
- declarations.replace_declaration!(
377
- property,
378
- {top => t, right => r, bottom => b, left => l},
379
- preserve_importance: true
380
- )
380
+ declarations.replace_declaration!(property, replacement, preserve_importance: true)
381
381
  end
382
382
  end
383
383
 
384
384
  # Convert shorthand font declarations (e.g. <tt>font: 300 italic 11px/14px verdana, helvetica, sans-serif;</tt>)
385
385
  # into their constituent parts.
386
386
  def expand_font_shorthand! # :nodoc:
387
- return unless declarations.key?('font')
388
-
389
- font_props = {}
387
+ return unless (declaration = declarations['font'])
390
388
 
391
389
  # reset properties to 'normal' per http://www.w3.org/TR/CSS21/fonts.html#font-shorthand
392
- ['font-style', 'font-variant', 'font-weight', 'font-size', 'line-height'].each do |prop|
393
- font_props[prop] = 'normal'
394
- end
390
+ font_props = {
391
+ 'font-style' => 'normal',
392
+ 'font-variant' => 'normal',
393
+ 'font-weight' => 'normal',
394
+ 'font-size' => 'normal',
395
+ 'line-height' => 'normal'
396
+ }
395
397
 
396
- value = declarations['font'].value.dup
398
+ value = declaration.value.dup
397
399
  value.gsub!(%r{/\s+}, '/') # handle spaces between font size and height shorthand (e.g. 14px/ 16px)
398
400
 
399
401
  in_fonts = false
400
402
 
401
- matches = value.scan(/("(.*[^"])"|'(.*[^'])'|(\w[^ ,]+))/)
402
- matches.each do |match|
403
- m = match[0].to_s.strip
403
+ matches = value.scan(/"(?:.*[^"])"|'(?:.*[^'])'|(?:\w[^ ,]+)/)
404
+ matches.each do |m|
405
+ m.strip!
404
406
  m.gsub!(/;$/, '')
405
407
 
406
408
  if in_fonts
@@ -411,7 +413,7 @@ module CssParser
411
413
  end
412
414
  elsif m =~ /normal|inherit/i
413
415
  ['font-style', 'font-weight', 'font-variant'].each do |font_prop|
414
- font_props[font_prop] = m unless font_props.key?(font_prop)
416
+ font_props[font_prop] ||= m
415
417
  end
416
418
  elsif m =~ /italic|oblique/i
417
419
  font_props['font-style'] = m
@@ -420,8 +422,8 @@ module CssParser
420
422
  elsif m =~ /[1-9]00$|bold|bolder|lighter/i
421
423
  font_props['font-weight'] = m
422
424
  elsif m =~ CssParser::FONT_UNITS_RX
423
- if m =~ %r{/}
424
- font_props['font-size'], font_props['line-height'] = m.split('/')
425
+ if m.include?('/')
426
+ font_props['font-size'], font_props['line-height'] = m.split('/', 2)
425
427
  else
426
428
  font_props['font-size'] = m
427
429
  end
@@ -437,16 +439,20 @@ module CssParser
437
439
  #
438
440
  # See http://www.w3.org/TR/CSS21/generate.html#lists
439
441
  def expand_list_style_shorthand! # :nodoc:
440
- return unless declarations.key?('list-style')
441
-
442
- value = declarations['list-style'].value.dup
443
-
444
- replacement = LIST_STYLE_PROPERTIES.map { |key| [key, 'inherit'] }.to_h if value =~ CssParser::RE_INHERIT
445
- replacement ||= {
446
- 'list-style-type' => value.slice!(CssParser::RE_LIST_STYLE_TYPE),
447
- 'list-style-position' => value.slice!(CssParser::RE_INSIDE_OUTSIDE),
448
- 'list-style-image' => value.slice!(Regexp.union(CssParser::URI_RX, /none/i))
449
- }
442
+ return unless (declaration = declarations['list-style'])
443
+
444
+ value = declaration.value.dup
445
+
446
+ replacement =
447
+ if value =~ CssParser::RE_INHERIT
448
+ LIST_STYLE_PROPERTIES.map { |key| [key, 'inherit'] }.to_h
449
+ else
450
+ {
451
+ 'list-style-type' => value.slice!(CssParser::RE_LIST_STYLE_TYPE),
452
+ 'list-style-position' => value.slice!(CssParser::RE_INSIDE_OUTSIDE),
453
+ 'list-style-image' => value.slice!(CssParser::URI_RX_OR_NONE)
454
+ }
455
+ end
450
456
 
451
457
  declarations.replace_declaration!('list-style', replacement, preserve_importance: true)
452
458
  end
@@ -466,10 +472,11 @@ module CssParser
466
472
  values = []
467
473
  properties_to_delete = []
468
474
  properties.each do |property|
469
- if declarations.key?(property) and not declarations[property].important
470
- values << declarations[property].value
471
- properties_to_delete << property
472
- end
475
+ next unless (declaration = declarations[property])
476
+ next if declaration.important
477
+
478
+ values << declaration.value
479
+ properties_to_delete << property
473
480
  end
474
481
 
475
482
  return if values.length <= 1
@@ -490,12 +497,9 @@ module CssParser
490
497
  # background-position by preceding it with a backslash. In this case we also need to
491
498
  # have a background-position property, so we set it if it's missing.
492
499
  # http://www.w3schools.com/cssref/css3_pr_background.asp
493
- if declarations.key?('background-size') and not declarations['background-size'].important
494
- unless declarations.key?('background-position')
495
- declarations['background-position'] = '0% 0%'
496
- end
497
-
498
- declarations['background-size'].value = "/ #{declarations['background-size'].value}"
500
+ if (declaration = declarations['background-size']) && !declaration.important
501
+ declarations['background-position'] ||= '0% 0%'
502
+ declaration.value = "/ #{declaration.value}"
499
503
  end
500
504
 
501
505
  create_shorthand_properties! BACKGROUND_PROPERTIES, 'background'
@@ -509,12 +513,15 @@ module CssParser
509
513
  values = []
510
514
 
511
515
  BORDER_STYLE_PROPERTIES.each do |property|
512
- next unless declarations.key?(property) and not declarations[property].important
516
+ next unless (declaration = declarations[property])
517
+ next if declaration.important
518
+
513
519
  # can't merge if any value contains a space (i.e. has multiple values)
514
520
  # we temporarily remove any spaces after commas for the check (inside rgba, etc...)
515
- return nil if declarations[property].value.gsub(/,\s/, ',').strip =~ /\s/
521
+ return nil if declaration.value.gsub(/,\s/, ',').strip =~ /\s/
522
+
523
+ values << declaration.value
516
524
 
517
- values << declarations[property].value
518
525
  declarations.delete(property)
519
526
  end
520
527
 
@@ -529,10 +536,10 @@ module CssParser
529
536
  return if declarations.size < NUMBER_OF_DIMENSIONS
530
537
 
531
538
  DIMENSIONS.each do |property, dimensions|
532
- values = %i[top right bottom left].each_with_index.with_object({}) do |(side, index), result|
533
- next unless declarations.key?(dimensions[index])
539
+ values = [:top, :right, :bottom, :left].each_with_index.with_object({}) do |(side, index), result|
540
+ next unless (declaration = declarations[dimensions[index]])
534
541
 
535
- result[side] = declarations[dimensions[index]].value
542
+ result[side] = declaration.value
536
543
  end
537
544
 
538
545
  # All four dimensions must be present
@@ -586,15 +593,15 @@ module CssParser
586
593
 
587
594
  def compute_dimensions_shorthand(values)
588
595
  # All four sides are equal, returning single value
589
- return %i[top] if values.values.uniq.count == 1
596
+ return [:top] if values.values.uniq.count == 1
590
597
 
591
598
  # `/* top | right | bottom | left */`
592
- return %i[top right bottom left] if values[:left] != values[:right]
599
+ return [:top, :right, :bottom, :left] if values[:left] != values[:right]
593
600
 
594
601
  # Vertical are the same & horizontal are the same, `/* vertical | horizontal */`
595
- return %i[top left] if values[:top] == values[:bottom]
602
+ return [:top, :left] if values[:top] == values[:bottom]
596
603
 
597
- %i[top left bottom]
604
+ [:top, :left, :bottom]
598
605
  end
599
606
 
600
607
  def parse_declarations!(block) # :nodoc:
@@ -604,10 +611,10 @@ module CssParser
604
611
 
605
612
  continuation = nil
606
613
  block.split(/[;$]+/m).each do |decs|
607
- decs = continuation ? continuation + decs : decs
614
+ decs = (continuation ? continuation + decs : decs)
608
615
  if decs =~ /\([^)]*\Z/ # if it has an unmatched parenthesis
609
616
  continuation = "#{decs};"
610
- elsif (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.+?)(;?\s*\Z)/i))
617
+ elsif (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.+?)(?:;?\s*\Z)/i))
611
618
  # skip end_of_declaration
612
619
  property = matches[1]
613
620
  value = matches[2]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CssParser
4
- VERSION = '1.8.0'.freeze
4
+ VERSION = '1.9.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: css_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.0
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dunae
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-21 00:00:00.000000000 Z
11
+ date: 2021-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable