Skip to content

Commit aceabca

Browse files
committed
cleanup
1 parent 2d5c1b1 commit aceabca

2 files changed

Lines changed: 97 additions & 88 deletions

File tree

lib/css_parser/regexps.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def self.regex_possible_values(*values)
2222

2323
RE_URI = /(url\(\s*(\s*#{RE_STRING}\s*)\s*\))|(url\(\s*([!#$%&*\-~]|#{RE_NON_ASCII}|#{RE_ESCAPE})*\s*)\)/ixm.freeze
2424
URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im.freeze
25+
URI_RX_OR_NONE = Regexp.union(URI_RX, /none/i)
2526
RE_GRADIENT = /[-a-z]*gradient\([-a-z0-9 .,#%()]*\)/im.freeze
2627

2728
# Initial parsing
@@ -43,6 +44,7 @@ def self.regex_possible_values(*values)
4344
'upper-latin', 'hebrew', 'armenian', 'georgian', 'cjk-ideographic', 'hiragana',
4445
'hira-gana-iroha', 'katakana-iroha', 'katakana', 'none'
4546
)
47+
RE_IMAGE = Regexp.union(CssParser::URI_RX, CssParser::RE_GRADIENT, /none/i)
4648

4749
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
4850
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze

lib/css_parser/rule_set.rb

Lines changed: 95 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ def value=(value)
4444
end
4545

4646
def to_s
47-
return value unless important
48-
49-
"#{value} !important"
47+
important ? "#{value} !important" : value
5048
end
5149

5250
def ==(other)
@@ -88,15 +86,11 @@ def []=(property, value)
8886

8987
if value.is_a?(Value)
9088
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)
9793
end
98-
99-
declarations[property] = Value.new(value)
10094
end
10195
alias add_declaration! []=
10296

@@ -153,28 +147,31 @@ def replace_declaration!(property, replacements, preserve_importance: false)
153147

154148
# We should preserve subsequent declarations of the same properties
155149
# 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), merged|
151+
existing = declarations[key]
152+
153+
# No existing -> set
154+
unless existing
155+
merged[key] = replacement
156+
next
157+
end
158+
159+
# Replacement more important than existing -> replace
160+
if replacement.important && !existing.important
161+
merged[key] = replacement
164162
replaced_index = replacement_keys.index(key)
165163
replacement_keys.delete_at(replaced_index)
166164
replacement_values.delete_at(replaced_index)
167165
property_index -= 1 if replaced_index < property_index
168166
next
169167
end
170168

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
174171

175-
# Importance of existing and replacement values are the same,
172+
# Existing and replacement importance are the same,
176173
# value which is declared later wins
177-
result[key] = value if property_index > replacement_keys.index(key)
174+
merged[key] = replacement if property_index > replacement_keys.index(key)
178175
end
179176

180177
return if replacements.empty?
@@ -245,9 +242,9 @@ def initialize(selectors, block, specificity = nil)
245242

246243
# Get the value of a property
247244
def get_value(property)
248-
return '' unless declarations.key?(property)
245+
return '' unless (value = declarations[property])
249246

250-
"#{declarations[property]};"
247+
"#{value};"
251248
end
252249
alias [] get_value
253250

@@ -301,19 +298,23 @@ def expand_shorthand!
301298
#
302299
# See http://www.w3.org/TR/CSS21/colors.html#propdef-background
303300
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
317318

318319
declarations.replace_declaration!('background', replacement, preserve_importance: true)
319320
end
@@ -328,9 +329,9 @@ def extract_background_size_from(value)
328329
# Additional splitting happens in expand_dimensions_shorthand!
329330
def expand_border_shorthand! # :nodoc:
330331
BORDER_PROPERTIES.each do |k|
331-
next unless declarations.key?(k)
332+
next unless (declaration = declarations[k])
332333

333-
value = declarations[k].value.dup
334+
value = declaration.value.dup
334335

335336
replacement = {
336337
"#{k}-width" => value.slice!(CssParser::RE_BORDER_UNITS),
@@ -346,9 +347,9 @@ def expand_border_shorthand! # :nodoc:
346347
# into their constituent parts. Handles margin, padding, border-color, border-style and border-width.
347348
def expand_dimensions_shorthand! # :nodoc:
348349
DIMENSIONS.each do |property, (top, right, bottom, left)|
349-
next unless declarations.key?(property)
350+
next unless (declaration = declarations[property])
350351

351-
value = declarations[property].value.dup
352+
value = declaration.value.dup
352353

353354
# RGB and HSL values in borders are the only units that can have spaces (within params).
354355
# We cheat a bit here by stripping spaces after commas in RGB and HSL values so that we
@@ -369,38 +370,39 @@ def expand_dimensions_shorthand! # :nodoc:
369370
values << matches[1] # left = right
370371
when 4
371372
values = matches.to_a
373+
else
374+
raise ArgumentError, "Cannot parse #{value}"
372375
end
373376

374377
t, r, b, l = values
378+
replacement = {top => t, right => r, bottom => b, left => l}
375379

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)
381381
end
382382
end
383383

384384
# Convert shorthand font declarations (e.g. <tt>font: 300 italic 11px/14px verdana, helvetica, sans-serif;</tt>)
385385
# into their constituent parts.
386386
def expand_font_shorthand! # :nodoc:
387-
return unless declarations.key?('font')
388-
389-
font_props = {}
387+
return unless (declaration = declarations['font'])
390388

391389
# 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+
}
395397

396-
value = declarations['font'].value.dup
398+
value = declaration.value.dup
397399
value.gsub!(%r{/\s+}, '/') # handle spaces between font size and height shorthand (e.g. 14px/ 16px)
398400

399401
in_fonts = false
400402

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!
404406
m.gsub!(/;$/, '')
405407

406408
if in_fonts
@@ -411,7 +413,7 @@ def expand_font_shorthand! # :nodoc:
411413
end
412414
elsif m =~ /normal|inherit/i
413415
['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
415417
end
416418
elsif m =~ /italic|oblique/i
417419
font_props['font-style'] = m
@@ -420,8 +422,8 @@ def expand_font_shorthand! # :nodoc:
420422
elsif m =~ /[1-9]00$|bold|bolder|lighter/i
421423
font_props['font-weight'] = m
422424
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)
425427
else
426428
font_props['font-size'] = m
427429
end
@@ -437,16 +439,20 @@ def expand_font_shorthand! # :nodoc:
437439
#
438440
# See http://www.w3.org/TR/CSS21/generate.html#lists
439441
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
450456

451457
declarations.replace_declaration!('list-style', replacement, preserve_importance: true)
452458
end
@@ -466,10 +472,11 @@ def create_shorthand_properties!(properties, shorthand_property) # :nodoc:
466472
values = []
467473
properties_to_delete = []
468474
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
473480
end
474481

475482
return if values.length <= 1
@@ -490,12 +497,9 @@ def create_background_shorthand! # :nodoc:
490497
# background-position by preceding it with a backslash. In this case we also need to
491498
# have a background-position property, so we set it if it's missing.
492499
# 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}"
499503
end
500504

501505
create_shorthand_properties! BACKGROUND_PROPERTIES, 'background'
@@ -509,12 +513,15 @@ def create_border_shorthand! # :nodoc:
509513
values = []
510514

511515
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+
513519
# can't merge if any value contains a space (i.e. has multiple values)
514520
# 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
516524

517-
values << declarations[property].value
518525
declarations.delete(property)
519526
end
520527

@@ -530,9 +537,9 @@ def create_dimensions_shorthand! # :nodoc:
530537

531538
DIMENSIONS.each do |property, dimensions|
532539
values = [:top, :right, :bottom, :left].each_with_index.with_object({}) do |(side, index), result|
533-
next unless declarations.key?(dimensions[index])
540+
next unless (declaration = declarations[dimensions[index]])
534541

535-
result[side] = declarations[dimensions[index]].value
542+
result[side] = declaration.value
536543
end
537544

538545
# All four dimensions must be present
@@ -604,10 +611,10 @@ def parse_declarations!(block) # :nodoc:
604611

605612
continuation = nil
606613
block.split(/[;$]+/m).each do |decs|
607-
decs = continuation ? continuation + decs : decs
614+
decs = (continuation ? continuation + decs : decs)
608615
if decs =~ /\([^)]*\Z/ # if it has an unmatched parenthesis
609616
continuation = "#{decs};"
610-
elsif (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.+?)(;?\s*\Z)/i))
617+
elsif (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.+?)(?:;?\s*\Z)/i))
611618
# skip end_of_declaration
612619
property = matches[1]
613620
value = matches[2]

0 commit comments

Comments
 (0)