Class: HTML5::Phase

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/html5/html5parser/phase.rb

Overview

Base class for helper objects that implement each phase of processing.

Handler methods should be in the following order (they can be omitted):

* EOF
* Comment
* Doctype
* SpaceCharacters
* Characters
* StartTag
  - startTag* methods
* EndTag
  - endTag* methods

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parser, tree) ⇒ Phase

Returns a new instance of Phase.



82
83
84
# File 'lib/html5/html5parser/phase.rb', line 82

def initialize(parser, tree)
  @parser, @tree = parser, tree
end

Class Method Details

.end_tag_handlersObject



72
73
74
# File 'lib/html5/html5parser/phase.rb', line 72

def self.end_tag_handlers
  @end_tag_handlers ||= Hash.new('endTagOther')
end

.handle_end(*tags) ⇒ Object

Declare what end tags this Phase handles. Behaves like handle_start.



78
79
80
# File 'lib/html5/html5parser/phase.rb', line 78

def self.handle_end(*tags)
  end_tag_handlers.update tag_handlers('endTag', *tags)
end

.handle_start(*tags) ⇒ Object

Declare what start tags this Phase handles. Can be called more than once.

Example usage:

handle_start 'html'
# html start tags will be handled by a method named 'startTagHtml'

handle_start %( base link meta )
# base, link and meta start tags will be handled by a method named 'startTagBaseLinkMeta'

handle_start %( li dt dd ) => 'ListItem'
# li, dt, and dd start tags will be handled by a method named 'startTagListItem'


68
69
70
# File 'lib/html5/html5parser/phase.rb', line 68

def self.handle_start(*tags)
  start_tag_handlers.update tag_handlers('startTag', *tags)
end

.start_tag_handlersObject



51
52
53
# File 'lib/html5/html5parser/phase.rb', line 51

def self.start_tag_handlers
  @start_tag_handlers ||= Hash.new('startTagOther')
end

.tag_handlers(prefix, *tags) ⇒ Object

The following example call:

tag_handlers('startTag', 'html', %w( base link meta ), %w( li dt dd ) => 'ListItem')

…would return a hash equal to this:

{ 'html' => 'startTagHtml',
  'base' => 'startTagBaseLinkMeta',
  'link' => 'startTagBaseLinkMeta',
  'meta' => 'startTagBaseLinkMeta',
  'li'   => 'startTagListItem',
  'dt'   => 'startTagListItem',
  'dd'   => 'startTagListItem'  }


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/html5/html5parser/phase.rb', line 35

def self.tag_handlers(prefix, *tags)
  mapping = {}
  if tags.last.is_a?(Hash)
    tags.pop.each do |names, handler_method_suffix|
      handler_method = prefix + handler_method_suffix
      Array(names).each {|name| mapping[name] = handler_method }
    end
  end
  tags.each do |names|
    names = Array(names)
    handler_method = prefix + names.map {|name| name.capitalize }.join
    names.each {|name| mapping[name] = handler_method }
  end
  mapping
end

Instance Method Details

#assert(value) ⇒ Object



137
138
139
# File 'lib/html5/html5parser/phase.rb', line 137

def assert(value)
  throw AssertionError.new unless value
end

#in_scope?(*args) ⇒ Boolean

Returns:

  • (Boolean)


141
142
143
# File 'lib/html5/html5parser/phase.rb', line 141

def in_scope?(*args)
  @tree.elementInScope(*args)
end

#process_eofObject



86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/html5/html5parser/phase.rb', line 86

def process_eof
  @tree.generateImpliedEndTags

  if @tree.open_elements.length > 2
    parse_error("expected-closing-tag-but-got-eof")
  elsif @tree.open_elements.length == 2 and @tree.open_elements[1].name != 'body'
    # This happens for framesets or something?
    parse_error("expected-closing-tag-but-got-eof")
  elsif @parser.inner_html and @tree.open_elements.length > 1 
    # XXX This is not what the specification says. Not sure what to do here.
    parse_error("eof-in-innerhtml")
  end
  # Betting ends.
end

#processComment(data) ⇒ Object



101
102
103
104
105
# File 'lib/html5/html5parser/phase.rb', line 101

def processComment(data)
  # For most phases the following is correct. Where it's not it will be
  # overridden.
  @tree.insert_comment(data, @tree.open_elements.last)
end

#processDoctype(name, publicId, systemId, correct) ⇒ Object



107
108
109
# File 'lib/html5/html5parser/phase.rb', line 107

def processDoctype(name, publicId, systemId, correct)
  parse_error("unexpected-doctype")
end

#processEndTag(name) ⇒ Object



133
134
135
# File 'lib/html5/html5parser/phase.rb', line 133

def processEndTag(name)
  send self.class.end_tag_handlers[name], name
end

#processSpaceCharacters(data) ⇒ Object



111
112
113
# File 'lib/html5/html5parser/phase.rb', line 111

def processSpaceCharacters(data)
  @tree.insertText(data)
end

#processStartTag(name, attributes) ⇒ Object



115
116
117
# File 'lib/html5/html5parser/phase.rb', line 115

def processStartTag(name, attributes)
  send self.class.start_tag_handlers[name], name, attributes
end

#remove_open_elements_until(name = nil) ⇒ Object



145
146
147
148
149
150
151
152
# File 'lib/html5/html5parser/phase.rb', line 145

def remove_open_elements_until(name=nil)
  finished = false
  until finished
    element = @tree.open_elements.pop
    finished = name.nil? ? yield(element) : element.name == name
  end
  return element
end

#startTagHtml(name, attributes) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/html5/html5parser/phase.rb', line 119

def startTagHtml(name, attributes)
  if @parser.first_start_tag == false and name == 'html'
     parse_error("non-html-root")
  end
  # XXX Need a check here to see if the first start tag token emitted is
  # this token... If it's not, invoke parse_error.
  attributes.each do |attr, value|
    unless @tree.open_elements.first.attributes.has_key?(attr)
      @tree.open_elements.first.attributes[attr] = value
    end
  end
  @parser.first_start_tag = false
end