class HTML::Node
:nodoc:
The base class of all nodes, textual and otherwise, in an HTML document.
def ==(node)
def ==(node) return false unless self.class == node.class && children.size == node.children.size equivalent = true children.size.times do |i| equivalent &&= children[i] == node.children[i] end equivalent end
def find(conditions)
Search the children of this node for the first node for which #find
def find(conditions) conditions = validate_conditions(conditions) @children.each do |child| node = child.find(conditions) return node if node end nil end
def find_all(conditions)
Search for all nodes that match the given conditions, and return them
def find_all(conditions) conditions = validate_conditions(conditions) matches = [] matches << self if match(conditions) @children.each do |child| matches.concat child.find_all(conditions) end matches end
def initialize(parent, line=0, pos=0)
def initialize(parent, line=0, pos=0) @parent = parent @children = [] @line, @position = line, pos end
def match(conditions)
Returns false (subclasses must override this to provide specific matching
def match(conditions) false end
def parse(parent, line, pos, content, strict=true)
def parse(parent, line, pos, content, strict=true) if content !~ /^<\S/ Text.new(parent, line, pos, content) else scanner = StringScanner.new(content) unless scanner.skip(/</) if strict raise "expected <" else return Text.new(parent, line, pos, content) end end if scanner.skip(/!\[CDATA\[/) unless scanner.skip_until(/\]\]>/) if strict raise "expected ]]> (got #{scanner.rest.inspect} for #{content})" else scanner.skip_until(/\Z/) end end return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, '')) end closing = ( scanner.scan(/\//) ? :close : nil ) return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/) name.downcase! unless closing scanner.skip(/\s*/) attributes = {} while attr = scanner.scan(/[-\w:]+/) value = true if scanner.scan(/\s*=\s*/) if delim = scanner.scan(/['"]/) value = "" while text = scanner.scan(/[^#{delim}\\]+|./) case text when "\\" then value << text break if scanner.eos? value << scanner.getch when delim break else value << text end end else value = scanner.scan(/[^\s>\/]+/) end end attributes[attr.downcase] = value scanner.skip(/\s*/) end closing = ( scanner.scan(/\//) ? :self : nil ) end unless scanner.scan(/\s*>/) if strict raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})" else # throw away all text until we find what we're looking for scanner.skip_until(/>/) or scanner.terminate end end Tag.new(parent, line, pos, name, attributes, closing) end end
def tag?
Returns +false+. Subclasses may override this if they define a kind of
def tag? false end
def to_s
def to_s @children.join() end
def validate_conditions(conditions)
def validate_conditions(conditions) Conditions === conditions ? conditions : Conditions.new(conditions) end