css_parser 1.8.0 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
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