class CssParser::RuleSet

def compute_dimensions_shorthand(values)

def compute_dimensions_shorthand(values)
  # All four sides are equal, returning single value
  return [:top] if values.values.uniq.count == 1
  # `/* top | right | bottom | left */`
  return DIMENSION_DIRECTIONS if values[:left] != values[:right]
  # Vertical are the same & horizontal are the same, `/* vertical | horizontal */`
  return [:top, :left] if values[:top] == values[:bottom]
  [:top, :left, :bottom]
end

def create_background_shorthand! # :nodoc:

:nodoc:
Leaves properties declared !important alone.

converts them into a shorthand CSS background property.
Looks for long format CSS background properties (e.g. background-color) and
def create_background_shorthand! # :nodoc:
  # When we have a background-size property we must separate it and distinguish it from
  # 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 (declaration = declarations['background-size']) && !declaration.important
    declarations['background-position'] ||= '0% 0%'
    declaration.value = "/ #{declaration.value}"
  end
  create_shorthand_properties! BACKGROUND_PROPERTIES, 'background'
end

def create_border_shorthand! # :nodoc:

:nodoc:
TODO: this is extremely similar to create_background_shorthand! and should be combined

Should be run after create_dimensions_shorthand!
Combine border-color, border-style and border-width into border
def create_border_shorthand! # :nodoc:
  values = BORDER_STYLE_PROPERTIES.filter_map do |property|
    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...)
    next if /\s/.match?(declaration.value.gsub(/,\s/, ',').strip)
    declaration.value
  end
  return if values.size != BORDER_STYLE_PROPERTIES.size
  BORDER_STYLE_PROPERTIES.each do |property|
    declarations.delete(property)
  end
  declarations['border'] = values.join(' ')
end

def create_dimensions_shorthand! # :nodoc:

:nodoc:
and converts them into shorthand CSS properties.
Looks for long format CSS dimensional properties (margin, padding, border-color, border-style and border-width)
def create_dimensions_shorthand! # :nodoc:
  return if declarations.size < NUMBER_OF_DIMENSIONS
  DIMENSIONS.each do |property, dimensions|
    values = DIMENSION_DIRECTIONS.each_with_index.with_object({}) do |(side, index), result|
      next unless (declaration = declarations[dimensions[index]])
      result[side] = declaration.value
    end
    # All four dimensions must be present
    next if values.size != dimensions.size
    new_value = values.values_at(*compute_dimensions_shorthand(values)).join(' ').strip
    declarations[property] = new_value unless new_value.empty?
    # Delete the longhand values
    dimensions.each { |d| declarations.delete(d) }
  end
end

def create_font_shorthand! # :nodoc:

:nodoc:
font properties must be present in order to create a shorthand declaration.
tries to convert them into a shorthand CSS font property. All
Looks for long format CSS font properties (e.g. font-weight) and
def create_font_shorthand! # :nodoc:
  return unless FONT_STYLE_PROPERTIES.all? { |prop| declarations.key?(prop) }
  new_value = +''
  ['font-style', 'font-variant', 'font-weight'].each do |property|
    unless declarations[property].value == 'normal'
      new_value << declarations[property].value << ' '
    end
  end
  new_value << declarations['font-size'].value
  unless declarations['line-height'].value == 'normal'
    new_value << '/' << declarations['line-height'].value
  end
  new_value << ' ' << declarations['font-family'].value
  declarations['font'] = new_value.gsub(/\s+/, ' ')
  FONT_STYLE_PROPERTIES.each { |prop| declarations.delete(prop) }
end

def create_list_style_shorthand! # :nodoc:

:nodoc:
Leaves properties declared !important alone.

converts them into a shorthand CSS list-style property.
Looks for long format CSS list-style properties (e.g. list-style-type) and
def create_list_style_shorthand! # :nodoc:
  create_shorthand_properties! LIST_STYLE_PROPERTIES, 'list-style'
end

def create_shorthand!

Create shorthand declarations (e.g. +margin+ or +font+) whenever possible.
def create_shorthand!
  create_background_shorthand!
  create_dimensions_shorthand!
  # border must be shortened after dimensions
  create_border_shorthand!
  create_font_shorthand!
  create_list_style_shorthand!
end

def create_shorthand_properties!(properties, shorthand_property) # :nodoc:

:nodoc:
Combine several properties into a shorthand one
def create_shorthand_properties!(properties, shorthand_property) # :nodoc:
  values = []
  properties_to_delete = []
  properties.each do |property|
    next unless (declaration = declarations[property])
    next if declaration.important
    values << declaration.value
    properties_to_delete << property
  end
  return if values.length <= 1
  properties_to_delete.each do |property|
    declarations.delete(property)
  end
  declarations[shorthand_property] = values.join(' ')
end

def declarations_to_s(options = {})

Return all declarations as a string.
def declarations_to_s(options = {})
  declarations.to_s(options)
end

def each_declaration # :yields: property, value, is_important

:yields: property, value, is_important
Iterate through declarations.
def each_declaration # :yields: property, value, is_important
  declarations.each do |property_name, value|
    yield property_name, value.value, value.important
  end
end

def each_selector(options = {}) # :yields: selector, declarations, specificity

:yields: selector, declarations, specificity
end
...
ruleset.each_selector do |sel, dec, spec|
==== Example

- +force_important+ -- boolean
Options

Iterate through selectors.
def each_selector(options = {}) # :yields: selector, declarations, specificity
  decs = declarations.to_s(options)
  if @specificity
    @selectors.each { |sel| yield sel.strip, decs, @specificity }
  else
    @selectors.each { |sel| yield sel.strip, decs, CssParser.calculate_specificity(sel) }
  end
end

def expand_background_shorthand! # :nodoc:

:nodoc:
See http://www.w3.org/TR/CSS21/colors.html#propdef-background

into their constituent parts.
Convert shorthand background declarations (e.g. background: url("chess.png") gray 50% repeat fixed;)
def expand_background_shorthand! # :nodoc:
  return unless (declaration = declarations['background'])
  value = declaration.value.dup
  replacement =
    if value.match(CssParser::RE_INHERIT)
      BACKGROUND_PROPERTIES.to_h { |key| [key, 'inherit'] }
    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

def expand_border_shorthand! # :nodoc:

:nodoc:
Additional splitting happens in expand_dimensions_shorthand!
Split shorthand border declarations (e.g. border: 1px red;)
def expand_border_shorthand! # :nodoc:
  BORDER_PROPERTIES.each do |k|
    next unless (declaration = declarations[k])
    value = declaration.value.dup
    replacement = {
      "#{k}-width" => value.slice!(CssParser::RE_BORDER_UNITS),
      "#{k}-color" => value.slice!(CssParser::RE_COLOUR),
      "#{k}-style" => value.slice!(CssParser::RE_BORDER_STYLE)
    }
    declarations.replace_declaration!(k, replacement, preserve_importance: true)
  end
end

def expand_dimensions_shorthand! # :nodoc:

:nodoc:
into their constituent parts. Handles margin, padding, border-color, border-style and border-width.
Split shorthand dimensional declarations (e.g. margin: 0px auto;)
def expand_dimensions_shorthand! # :nodoc:
  DIMENSIONS.each do |property, (top, right, bottom, left)|
    next unless (declaration = declarations[property])
    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
    # can split easily on spaces.
    #
    # TODO: rgba, hsl, hsla
    value.gsub!(RE_COLOUR) { |c| c.gsub(/(\s*,\s*)/, ',') }
    matches = split_value_preserving_function_whitespace(value)
    case matches.length
    when 1
      values = matches.to_a * 4
    when 2
      values = matches.to_a * 2
    when 3
      values = matches.to_a
      values << matches[1] # left = right
    when 4
      values = matches.to_a
    else
      raise ArgumentError, "Cannot parse #{value}"
    end
    replacement = [top, right, bottom, left].zip(values).to_h
    declarations.replace_declaration!(property, replacement, preserve_importance: true)
  end
end

def expand_font_shorthand! # :nodoc:

:nodoc:
into their constituent parts.
Convert shorthand font declarations (e.g. font: 300 italic 11px/14px verdana, helvetica, sans-serif;)
def expand_font_shorthand! # :nodoc:
  return unless (declaration = declarations['font'])
  # reset properties to 'normal' per http://www.w3.org/TR/CSS21/fonts.html#font-shorthand
  font_props = {
    'font-style' => 'normal',
    'font-variant' => 'normal',
    'font-weight' => 'normal',
    'font-size' => 'normal',
    'line-height' => 'normal'
  }
  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 |m|
    m.strip!
    m.gsub!(/;$/, '')
    if in_fonts
      if font_props.key?('font-family')
        font_props['font-family'] += ", #{m}"
      else
        font_props['font-family'] = m
      end
    elsif /normal|inherit/i.match?(m)
      FONT_WEIGHT_PROPERTIES.each do |font_prop|
        font_props[font_prop] ||= m
      end
    elsif /italic|oblique/i.match?(m)
      font_props['font-style'] = m
    elsif /small-caps/i.match?(m)
      font_props['font-variant'] = m
    elsif /[1-9]00$|bold|bolder|lighter/i.match?(m)
      font_props['font-weight'] = m
    elsif CssParser::FONT_UNITS_RX.match?(m)
      if m.include?('/')
        font_props['font-size'], font_props['line-height'] = m.split('/', 2)
      else
        font_props['font-size'] = m
      end
      in_fonts = true
    end
  end
  declarations.replace_declaration!('font', font_props, preserve_importance: true)
end

def expand_list_style_shorthand! # :nodoc:

:nodoc:
See http://www.w3.org/TR/CSS21/generate.html#lists

into their constituent parts.
Convert shorthand list-style declarations (e.g. list-style: lower-alpha outside;)
def expand_list_style_shorthand! # :nodoc:
  return unless (declaration = declarations['list-style'])
  value = declaration.value.dup
  replacement =
    if CssParser::RE_INHERIT.match?(value)
      LIST_STYLE_PROPERTIES.to_h { |key| [key, 'inherit'] }
    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

def expand_shorthand!

Split shorthand declarations (e.g. +margin+ or +font+) into their constituent parts.
def expand_shorthand!
  # border must be expanded before dimensions
  expand_border_shorthand!
  expand_dimensions_shorthand!
  expand_font_shorthand!
  expand_background_shorthand!
  expand_list_style_shorthand!
end

def extract_background_size_from(value)

def extract_background_size_from(value)
  size = value.slice!(CssParser::RE_BACKGROUND_SIZE)
  size.sub(%r{^\s*/\s*}, '') if size
end

def get_value(property)

Get the value of a property
def get_value(property)
  return '' unless (value = declarations[property])
  "#{value};"
end

def initialize(*args, selectors: nil, block: nil, offset: nil, filename: nil, specificity: nil) # rubocop:disable Metrics/ParameterLists

rubocop:disable Metrics/ParameterLists
def initialize(*args, selectors: nil, block: nil, offset: nil, filename: nil, specificity: nil) # rubocop:disable Metrics/ParameterLists
  if args.any?
    if selectors || block || offset || filename || specificity
      raise ArgumentError, "don't mix positional and keyword arguments"
    end
    warn '[DEPRECATION] positional arguments are deprecated use keyword instead.', uplevel: 1
    case args.length
    when 2
      selectors, block = args
    when 3
      selectors, block, specificity = args
    when 4
      filename, offset, selectors, block = args
    when 5
      filename, offset, selectors, block, specificity = args
    else
      raise ArgumentError
    end
  end
  @selectors = []
  @specificity = specificity
  unless offset.nil? == filename.nil?
    raise ArgumentError, 'require both offset and filename or no offset and no filename'
  end
  @offset = offset
  @filename = filename
  parse_selectors!(selectors) if selectors
  parse_declarations!(block)
end

def parse_declarations!(block) # :nodoc:

:nodoc:
def parse_declarations!(block) # :nodoc:
  self.declarations = Declarations.new
  return unless block
  continuation = nil
  block.split(SEMICOLON) do |decs|
    decs = (continuation ? "#{continuation};#{decs}" : decs)
    if unmatched_open_parenthesis?(decs)
      # Semicolon happened within parenthesis, so it is a part of the value
      # the rest of the value is in the next segment
      continuation = decs
      next
    end
    next unless (colon = decs.index(COLON))
    property = decs[0, colon]
    value = decs[(colon + 1)..]
    property.strip!
    value.strip!
    next if property.empty? || value.empty? || value.casecmp?(IMPORTANT)
    add_declaration!(property, value)
    continuation = nil
  end
end

def parse_selectors!(selectors) # :nodoc:

:nodoc:
++
TODO: way too simplistic
--
def parse_selectors!(selectors) # :nodoc:
  @selectors = selectors.split(',').map do |s|
    s.gsub!(/\s+/, ' ')
    s.strip!
    s
  end
end

def split_value_preserving_function_whitespace(value)

def split_value_preserving_function_whitespace(value)
  split_value = value.gsub(RE_FUNCTIONS) do |c|
    c.gsub!(/\s+/, WHITESPACE_REPLACEMENT)
    c
  end
  matches = split_value.strip.split(/\s+/)
  matches.each do |c|
    c.gsub!(WHITESPACE_REPLACEMENT, ' ')
  end
end

def to_s

Return the CSS rule set as a string.
def to_s
  "#{@selectors.join(',')} { #{declarations} }"
end

def unmatched_open_parenthesis?(declarations)

def unmatched_open_parenthesis?(declarations)
  (lparen_index = declarations.index(LPAREN)) && !declarations.index(RPAREN, lparen_index)
end