static public Method

Selector.new(selector, *values)

Selector.new(string, [values ...])  selector

Creates a new selector from a CSS 2 selector expression.

The first argument is the selector expression. All other arguments are used for value substitution.

Throws InvalidSelectorError is the selector expression is invalid.

Source Code

# File action_controller/vendor/html-scanner/html/selector.rb, line 239
def initialize(selector, *values)
  raise ArgumentError, "CSS expression cannot be empty" if selector.empty?
  @source = ""
  values = values[0] if values.size == 1 && values[0].is_a?(Array)

  # We need a copy to determine if we failed to parse, and also
  # preserve the original pass by-ref statement.
  statement = selector.strip.dup

  # Create a simple selector, along with negation.
  simple_selector(statement, values).each { |name, value| instance_variable_set("@#{name}", value) }

  @alternates = []
  @depends = nil

  # Alternative selector.
  if statement.sub!(/^\s*,\s*/, "")
    second = Selector.new(statement, values)
    @alternates << second
    # If there are alternate selectors, we group them in the top selector.
    if alternates = second.instance_variable_get(:@alternates)
      second.instance_variable_set(:@alternates, [])
      @alternates.concat alternates
    end
    @source << " , " << second.to_s
  # Sibling selector: create a dependency into second selector that will
  # match element immediately following this one.
  elsif statement.sub!(/^\s*\+\s*/, "")
    second = next_selector(statement, values)
    @depends = lambda do |element, first|
      if element = next_element(element)
        second.match(element, first)
      end
    end
    @source << " + " << second.to_s
  # Adjacent selector: create a dependency into second selector that will
  # match all elements following this one.
  elsif statement.sub!(/^\s*~\s*/, "")
    second = next_selector(statement, values)
    @depends = lambda do |element, first|
      matches = []
      while element = next_element(element)
        if subset = second.match(element, first)
          if first && !subset.empty?
            matches << subset.first
            break
          else
            matches.concat subset
          end
        end
      end
      matches.empty? ? nil : matches
    end
    @source << " ~ " << second.to_s
  # Child selector: create a dependency into second selector that will
  # match a child element of this one.
  elsif statement.sub!(/^\s*>\s*/, "")
    second = next_selector(statement, values)
    @depends = lambda do |element, first|
      matches = []
      element.children.each do |child|
        if child.tag? && subset = second.match(child, first)
          if first && !subset.empty?
            matches << subset.first
            break
          else
            matches.concat subset
          end
        end
      end
      matches.empty? ? nil : matches
    end
    @source << " > " << second.to_s
  # Descendant selector: create a dependency into second selector that
  # will match all descendant elements of this one. Note,
  elsif statement =~ /^\s+\S+/ && statement != selector
    second = next_selector(statement, values)
    @depends = lambda do |element, first|
      matches = []
      stack = element.children.reverse
      while node = stack.pop
        next unless node.tag?
        if subset = second.match(node, first)
          if first && !subset.empty?
            matches << subset.first
            break
          else
            matches.concat subset
          end
        elsif children = node.children
          stack.concat children.reverse
        end
      end
      matches.empty? ? nil : matches
    end
    @source << " " << second.to_s
  else
    # The last selector is where we check that we parsed
    # all the parts.
    unless statement.empty? || statement.strip.empty?
      raise ArgumentError, "Invalid selector: #{statement}"
    end
  end
end
Comments

Have your say
Please use Textile formatting (click here for a cheat sheet). Use <code/> and <pre/> for code samples.
Click here to login with OpenID to to post comments.