Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
grosser committed Feb 1, 2021
1 parent 2d5c1b1 commit aceabca
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 88 deletions.
2 changes: 2 additions & 0 deletions lib/css_parser/regexps.rb
Expand Up @@ -22,6 +22,7 @@ def self.regex_possible_values(*values)

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

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

STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
Expand Down
183 changes: 95 additions & 88 deletions lib/css_parser/rule_set.rb
Expand Up @@ -44,9 +44,7 @@ def value=(value)
end

def to_s
return value unless important

"#{value} !important"
important ? "#{value} !important" : value
end

def ==(other)
Expand Down Expand Up @@ -88,15 +86,11 @@ def []=(property, value)

if value.is_a?(Value)
declarations[property] = value
return
end

if value.to_s.strip.empty?
delete(property)
return
elsif value.to_s.strip.empty?
delete property
else
declarations[property] = Value.new(value)
end

declarations[property] = Value.new(value)
end
alias add_declaration! []=

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

# We should preserve subsequent declarations of the same properties
# and prior important ones if replacement one is not important
replacements = replacement_declarations.each.with_object({}) do |(key, value), result|
# Replacement property doesn't exist, adding
next result[key] = value unless declarations.key?(key)

# Replacement property is important while existing one is not,
# replacing unconditionally
if value.important && !declarations[key].important
result[key] = value
replacements = replacement_declarations.each.with_object({}) do |(key, replacement), merged|
existing = declarations[key]

# No existing -> set
unless existing
merged[key] = replacement
next
end

# Replacement more important than existing -> replace
if replacement.important && !existing.important
merged[key] = replacement
replaced_index = replacement_keys.index(key)
replacement_keys.delete_at(replaced_index)
replacement_values.delete_at(replaced_index)
property_index -= 1 if replaced_index < property_index
next
end

# Existing value is important while replacing is not, existing one
# takes precedence
next if !value.important && declarations[key].important
# Existing is more important than replacement -> keep
next if !replacement.important && existing.important

# Importance of existing and replacement values are the same,
# Existing and replacement importance are the same,
# value which is declared later wins
result[key] = value if property_index > replacement_keys.index(key)
merged[key] = replacement if property_index > replacement_keys.index(key)
end

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

# Get the value of a property
def get_value(property)
return '' unless declarations.key?(property)
return '' unless (value = declarations[property])

"#{declarations[property]};"
"#{value};"
end
alias [] get_value

Expand Down Expand Up @@ -301,19 +298,23 @@ def expand_shorthand!
#
# See http://www.w3.org/TR/CSS21/colors.html#propdef-background
def expand_background_shorthand! # :nodoc:
return unless declarations.key?('background')

value = declarations['background'].value.dup

replacement = BACKGROUND_PROPERTIES.map { |key| [key, 'inherit'] }.to_h if value.match(CssParser::RE_INHERIT)
replacement ||= {
'background-image' => value.slice!(Regexp.union(CssParser::URI_RX, CssParser::RE_GRADIENT, /none/i)),
'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)
}
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 All @@ -328,9 +329,9 @@ def extract_background_size_from(value)
# Additional splitting happens in expand_dimensions_shorthand!
def expand_border_shorthand! # :nodoc:
BORDER_PROPERTIES.each do |k|
next unless declarations.key?(k)
next unless (declaration = declarations[k])

value = declarations[k].value.dup
value = declaration.value.dup

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

value = declarations[property].value.dup
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
Expand All @@ -369,38 +370,39 @@ def expand_dimensions_shorthand! # :nodoc:
values << matches[1] # left = right
when 4
values = matches.to_a
else
raise ArgumentError, "Cannot parse #{value}"
end

t, r, b, l = values
replacement = {top => t, right => r, bottom => b, left => l}

declarations.replace_declaration!(
property,
{top => t, right => r, bottom => b, left => l},
preserve_importance: true
)
declarations.replace_declaration!(property, replacement, preserve_importance: true)
end
end

# Convert shorthand font declarations (e.g. <tt>font: 300 italic 11px/14px verdana, helvetica, sans-serif;</tt>)
# into their constituent parts.
def expand_font_shorthand! # :nodoc:
return unless declarations.key?('font')

font_props = {}
return unless (declaration = declarations['font'])

# reset properties to 'normal' per http://www.w3.org/TR/CSS21/fonts.html#font-shorthand
['font-style', 'font-variant', 'font-weight', 'font-size', 'line-height'].each do |prop|
font_props[prop] = 'normal'
end
font_props = {
'font-style' => 'normal',
'font-variant' => 'normal',
'font-weight' => 'normal',
'font-size' => 'normal',
'line-height' => 'normal'
}

value = declarations['font'].value.dup
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 |match|
m = match[0].to_s.strip
matches = value.scan(/"(?:.*[^"])"|'(?:.*[^'])'|(?:\w[^ ,]+)/)
matches.each do |m|
m.strip!
m.gsub!(/;$/, '')

if in_fonts
Expand All @@ -411,7 +413,7 @@ def expand_font_shorthand! # :nodoc:
end
elsif m =~ /normal|inherit/i
['font-style', 'font-weight', 'font-variant'].each do |font_prop|
font_props[font_prop] = m unless font_props.key?(font_prop)
font_props[font_prop] ||= m
end
elsif m =~ /italic|oblique/i
font_props['font-style'] = m
Expand All @@ -420,8 +422,8 @@ def expand_font_shorthand! # :nodoc:
elsif m =~ /[1-9]00$|bold|bolder|lighter/i
font_props['font-weight'] = m
elsif m =~ CssParser::FONT_UNITS_RX
if m =~ %r{/}
font_props['font-size'], font_props['line-height'] = m.split('/')
if m.include?('/')
font_props['font-size'], font_props['line-height'] = m.split('/', 2)
else
font_props['font-size'] = m
end
Expand All @@ -437,16 +439,20 @@ def expand_font_shorthand! # :nodoc:
#
# See http://www.w3.org/TR/CSS21/generate.html#lists
def expand_list_style_shorthand! # :nodoc:
return unless declarations.key?('list-style')

value = declarations['list-style'].value.dup

replacement = LIST_STYLE_PROPERTIES.map { |key| [key, 'inherit'] }.to_h if value =~ CssParser::RE_INHERIT
replacement ||= {
'list-style-type' => value.slice!(CssParser::RE_LIST_STYLE_TYPE),
'list-style-position' => value.slice!(CssParser::RE_INSIDE_OUTSIDE),
'list-style-image' => value.slice!(Regexp.union(CssParser::URI_RX, /none/i))
}
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 All @@ -466,10 +472,11 @@ def create_shorthand_properties!(properties, shorthand_property) # :nodoc:
values = []
properties_to_delete = []
properties.each do |property|
if declarations.key?(property) and not declarations[property].important
values << declarations[property].value
properties_to_delete << property
end
next unless (declaration = declarations[property])
next if declaration.important

values << declaration.value
properties_to_delete << property
end

return if values.length <= 1
Expand All @@ -490,12 +497,9 @@ def create_background_shorthand! # :nodoc:
# 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 declarations.key?('background-size') and not declarations['background-size'].important
unless declarations.key?('background-position')
declarations['background-position'] = '0% 0%'
end

declarations['background-size'].value = "/ #{declarations['background-size'].value}"
if (declaration = declarations['background-size']) && !declaration.important
declarations['background-position'] ||= '0% 0%'
declaration.value = "/ #{declaration.value}"
end

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

BORDER_STYLE_PROPERTIES.each do |property|
next unless declarations.key?(property) and not declarations[property].important
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...)
return nil if declarations[property].value.gsub(/,\s/, ',').strip =~ /\s/
return nil if declaration.value.gsub(/,\s/, ',').strip =~ /\s/

values << declaration.value

values << declarations[property].value
declarations.delete(property)
end

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

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

result[side] = declarations[dimensions[index]].value
result[side] = declaration.value
end

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

continuation = nil
block.split(/[;$]+/m).each do |decs|
decs = continuation ? continuation + decs : decs
decs = (continuation ? continuation + decs : decs)
if decs =~ /\([^)]*\Z/ # if it has an unmatched parenthesis
continuation = "#{decs};"
elsif (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.+?)(;?\s*\Z)/i))
elsif (matches = decs.match(/\s*(.[^:]*)\s*:\s*(.+?)(?:;?\s*\Z)/i))
# skip end_of_declaration
property = matches[1]
value = matches[2]
Expand Down

0 comments on commit aceabca

Please sign in to comment.