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 +4 -4
- data/lib/css_parser/regexps.rb +2 -0
- data/lib/css_parser/rule_set.rb +100 -93
- data/lib/css_parser/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecd734b97658689e087ca19039ea51377c23d9e1c4be6099b084006150255fce
|
4
|
+
data.tar.gz: d5a326fac6e3b57fe75fa3bc664ae8511a0e0dcf34f80dd5736ee8cc9709f5ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca0d59324036c9071c5f957235f10022d3db4841d4e4782e147f30e2cde1a1cd46873ede55db01b260676fc7289c0e7c4ad7e0e6ac7dbd0b38a1de8aa13333a1
|
7
|
+
data.tar.gz: da5fb20229ecfac93b0436a0ed6954c109908429628cd635751c58fd9b3d029227da06a60bb80f5f4bd2fac55851ac258e7ff59d09fea59e93157f38d21c0fac
|
data/lib/css_parser/regexps.rb
CHANGED
@@ -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
|
data/lib/css_parser/rule_set.rb
CHANGED
@@ -44,9 +44,7 @@ module CssParser
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def to_s
|
47
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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,
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
172
|
-
|
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
|
-
#
|
172
|
+
# Existing and replacement importance are the same,
|
176
173
|
# value which is declared later wins
|
177
|
-
result[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
|
245
|
+
return '' unless (value = declarations[property])
|
249
246
|
|
250
|
-
"#{
|
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
|
305
|
-
|
306
|
-
value =
|
307
|
-
|
308
|
-
replacement =
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
332
|
+
next unless (declaration = declarations[k])
|
332
333
|
|
333
|
-
value =
|
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
|
350
|
+
next unless (declaration = declarations[property])
|
350
351
|
|
351
|
-
value =
|
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
|
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
|
-
|
393
|
-
|
394
|
-
|
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 =
|
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(/
|
402
|
-
matches.each do |
|
403
|
-
m
|
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]
|
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
|
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
|
441
|
-
|
442
|
-
value =
|
443
|
-
|
444
|
-
replacement =
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
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
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
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
|
494
|
-
|
495
|
-
|
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
|
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
|
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 =
|
533
|
-
next unless declarations
|
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] =
|
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
|
596
|
+
return [:top] if values.values.uniq.count == 1
|
590
597
|
|
591
598
|
# `/* top | right | bottom | left */`
|
592
|
-
return
|
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
|
602
|
+
return [:top, :left] if values[:top] == values[:bottom]
|
596
603
|
|
597
|
-
|
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*(.+?)(
|
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]
|
data/lib/css_parser/version.rb
CHANGED
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.
|
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-
|
11
|
+
date: 2021-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|