#
# "grows downwards" means it's visualized like this: (index: el, names)
#
-# 6: g "start of the list", "topmost"
+# 6: g "start of the list", "topmost", "first"
# 5: f
-# 4: e "previous" (to d), "above"
+# 4: e "previous" (to d), "above", "before"
# 3: d (previous/next are relative to this element)
# 2: c "next", "after", "lower", "below"
# 1: b
TYPE_COMMENT = 2
TYPE_DOCTYPE = 3
# the following types are emited by the tokenizer, but shouldn't end up in the tree:
-TYPE_OPEN_TAG = 4 # name, [attributes ([key,value]...) in reverse order], [children]
+TYPE_START_TAG = 4 # name, [attributes ([key,value]...) in reverse order], [children]
TYPE_END_TAG = 5 # name
TYPE_EOF = 6
-TYPE_MARKER = 7 # http://www.w3.org/TR/html5/syntax.html#reconstruct-the-active-formatting-elements
+TYPE_AFE_MARKER = 7 # http://www.w3.org/TR/html5/syntax.html#reconstruct-the-active-formatting-elements
TYPE_AAA_BOOKMARK = 8 # http://www.w3.org/TR/html5/syntax.html#adoption-agency-algorithm
# namespace constants
@name = args.name ? '' # tag name
@text = args.text ? '' # contents for text/comment nodes
@attrs = args.attrs ? {}
- @attrs_a = args.attr_k ? [] # attrs in progress, TYPE_OPEN_TAG only
+ @attrs_a = args.attr_k ? [] # attrs in progress, TYPE_START_TAG only
@children = args.children ? []
@namespace = args.namespace ? NS_HTML
@parent = args.parent ? null
when TYPE_DOCTYPE
ret += 'doctype'
# FIXME
- when TYPE_MARKER
+ when TYPE_AFE_MARKER
ret += 'marker'
when TYPE_AAA_BOOKMARK
ret += 'aaa_bookmark'
# helpers: (only take args that are normally known when parser creates nodes)
new_open_tag = (name) ->
- return new Node TYPE_OPEN_TAG, name: name
+ return new Node TYPE_START_TAG, name: name
new_end_tag = (name) ->
return new Node TYPE_END_TAG, name: name
new_element = (name) ->
return new Node TYPE_COMMENT, text: txt
new_eof_token = ->
return new Node TYPE_EOF
+new_afe_marker = ->
+ return new Node TYPE_AFE_MARKER
new_aaa_bookmark = ->
return new Node TYPE_AAA_BOOKMARK
u: true
}
+foster_parenting_targets = {
+ table: true
+ tbody: true
+ tfoot: true
+ thead: true
+ tr: true
+}
+
# all html I presume
end_tag_implied = {
dd: true
# declare tree and tokenizer variables so they're in scope below
tree = null
open_els = [] # stack of open elements
- tree_state = null
+ insertion_mode = null
tok_state = null
tok_cur_tag = null # partially parsed tag
flag_frameset_ok = null
flag_parsing = null
flag_foster_parenting = null
+ form_element_pointer = null
afe = [] # active formatting elements
parse_error = ->
return false
return false
+ # 8.2.3.1 ...
+ # http://www.w3.org/TR/html5/syntax.html#reset-the-insertion-mode-appropriately
+ reset_insertion_mode = ->
+ # 1. Let last be false.
+ last = false
+ # 2. Let node be the last node in the stack of open elements.
+ node_i = 0
+ node = open_els[node_i]
+ # 3. Loop: If node is the first node in the stack of open elements,
+ # then set last to true, and, if the parser was originally created as
+ # part of the HTML fragment parsing algorithm (fragment case) set node
+ # to the context element.
+ loop
+ if node_i is open_els.length - 1
+ last = true
+ # fixfull (fragment case)
+
+ # 4. If node is a select element, run these substeps:
+ if node.name is 'select'
+ # 1. If last is true, jump to the step below labeled done.
+ unless last
+ # 2. Let ancestor be node.
+ ancestor_i = node_i
+ ancestor = node
+ # 3. Loop: If ancestor is the first node in the stack of
+ # open elements, jump to the step below labeled done.
+ loop
+ if ancestor_i is open_els.length - 1
+ break
+ # 4. Let ancestor be the node before ancestor in the stack
+ # of open elements.
+ ancestor_i += 1
+ ancestor = open_els[ancestor_i]
+ # 5. If ancestor is a template node, jump to the step below
+ # labeled done.
+ if ancestor.name is 'template'
+ break
+ # 6. If ancestor is a table node, switch the insertion mode
+ # to "in select in table" and abort these steps.
+ if ancestor.name is 'table'
+ insertion_mode = ins_mode_in_select_in_table
+ return
+ # 7. Jump back to the step labeled loop.
+ # 8. Done: Switch the insertion mode to "in select" and abort
+ # these steps.
+ insertion_mode = ins_mode_in_select
+ return
+ # 5. If node is a td or th element and last is false, then switch
+ # the insertion mode to "in cell" and abort these steps.
+ if (node.name is 'td' or node.name is 'th') and last is false
+ insertion_mode = ins_mode_in_cell
+ return
+ # 6. If node is a tr element, then switch the insertion mode to "in
+ # row" and abort these steps.
+ if node.name is 'tr'
+ insertion_mode = ins_mode_in_row
+ return
+ # 7. If node is a tbody, thead, or tfoot element, then switch the
+ # insertion mode to "in table body" and abort these steps.
+ if node.name is 'tbody' or node.name is 'thead' or node.name is 'tfoot'
+ insertion_mode = ins_mode_in_table_body
+ return
+ # 8. If node is a caption element, then switch the insertion mode
+ # to "in caption" and abort these steps.
+ if node.name is 'caption'
+ insertion_mode = ins_mode_in_caption
+ return
+ # 9. If node is a colgroup element, then switch the insertion mode
+ # to "in column group" and abort these steps.
+ if node.name is 'colgroup'
+ insertion_mode = ins_mode_in_column_group
+ return
+ # 10. If node is a table element, then switch the insertion mode to
+ # "in table" and abort these steps.
+ if node.name is 'table'
+ insertion_mode = ins_mode_in_table
+ return
+ # 11. If node is a template element, then switch the insertion mode
+ # to the current template insertion mode and abort these steps.
+ # fixfull (template insertion mode stack)
+
+ # 12. If node is a head element and last is true, then switch the
+ # insertion mode to "in body" ("in body"! not "in head"!) and abort
+ # these steps. (fragment case)
+ if node.name is 'head' and last
+ insertion_mode = ins_mode_in_body
+ return
+ # 13. If node is a head element and last is false, then switch the
+ # insertion mode to "in head" and abort these steps.
+ if node.name is 'head' and last is false
+ insertion_mode = ins_mode_in_head
+ return
+ # 14. If node is a body element, then switch the insertion mode to
+ # "in body" and abort these steps.
+ if node.name is 'body'
+ insertion_mode = ins_mode_in_body
+ return
+ # 15. If node is a frameset element, then switch the insertion mode
+ # to "in frameset" and abort these steps. (fragment case)
+ if node.name is 'frameset'
+ insertion_mode = ins_mode_in_frameset
+ return
+ # 16. If node is an html element, run these substeps:
+ if node.name is 'html'
+ # 1. If the head element pointer is null, switch the insertion
+ # mode to "before head" and abort these steps. (fragment case)
+ # fixfull (fragment case)
+
+ # 2. Otherwise, the head element pointer is not null, switch
+ # the insertion mode to "after head" and abort these steps.
+ insertion_mode = ins_mode_in_body # FIXME fixfull
+ return
+ # 17. If last is true, then switch the insertion mode to "in body"
+ # and abort these steps. (fragment case)
+ if last
+ insertion_mode = ins_mode_in_body
+ return
+ # 18. Let node now be the node before node in the stack of open
+ # elements.
+ node_i += 1
+ node = open_els[node_i]
+ # 19. Return to the step labeled loop.
+
# http://www.w3.org/TR/html5/syntax.html#reconstruct-the-active-formatting-elements
# this implementation is structured (mostly) as described at the link above.
# capitalized comments are the "labels" described at the link above.
reconstruct_active_formatting_elements = ->
return if afe.length is 0
- if afe[0].type is TYPE_MARKER or afe[0] in open_els
+ if afe[0].type is TYPE_AFE_MARKER or afe[0] in open_els
return
# Rewind
i = 0
if i is afe.length - 1
break
i += 1
- if afe[i].type is TYPE_MARKER or afe[i] in open_els
+ if afe[i].type is TYPE_AFE_MARKER or afe[i] in open_els
i -= 1 # Advance
break
# Create
# the list otherwise, and has the tag name subject.
fe = null
for t, fe_of_afe in afe
- if t.type is TYPE_MARKER
+ if t.type is TYPE_AFE_MARKER
break
if t.name is subject
fe = t
# If foster parenting is enabled and target is a table, tbody, tfoot,
# thead, or tr element Foster parenting happens when content is
# misnested in tables.
- if flag_foster_parenting and target.name in foster_parenting_targets
- console.log "foster parenting isn't implemented yet" # TODO
- # 1. Let last template be the last template element in the stack of
- # open elements, if any.
- # 2. Let last table be the last table element in the stack of open
- # elements, if any.
-
- # 3. If there is a last template and either there is no last table,
- # or there is one, but last template is lower (more recently added)
- # than last table in the stack of open elements, then: let adjusted
- # insertion location be inside last template's template contents,
- # after its last child (if any), and abort these substeps.
-
- # 4. If there is no last table, then let adjusted insertion
- # location be inside the first element in the stack of open
- # elements (the html element), after its last child (if any), and
- # abort these substeps. (fragment case)
-
- # 5. If last table has a parent element, then let adjusted
- # insertion location be inside last table's parent element,
- # immediately before last table, and abort these substeps.
-
- # 6. Let previous element be the element immediately above last
- # table in the stack of open elements.
-
- # 7. Let adjusted insertion location be inside previous element,
- # after its last child (if any).
-
- # Note: These steps are involved in part because it's possible for
- # elements, the table element in this case in particular, to have
- # been moved by a script around in the DOM, or indeed removed from
- # the DOM entirely, after the element was inserted by the parser.
+ if flag_foster_parenting and foster_parenting_targets[target.name]
+ loop # once. this is here so we can ``break`` to "abort these substeps"
+ # 1. Let last template be the last template element in the
+ # stack of open elements, if any.
+ last_template = null
+ last_template_i = null
+ for el, i in open_els
+ if el.name is 'template'
+ last_template = el
+ last_template_i = i
+ break
+ # 2. Let last table be the last table element in the stack of
+ # open elements, if any.
+ last_table = null
+ last_table_i
+ for el, i in open_els
+ if el.name is 'table'
+ last_table = el
+ last_table_i = i
+ break
+ # 3. If there is a last template and either there is no last
+ # table, or there is one, but last template is lower (more
+ # recently added) than last table in the stack of open
+ # elements, then: let adjusted insertion location be inside
+ # last template's template contents, after its last child (if
+ # any), and abort these substeps.
+ if last_template and (last_table is null or last_template_i < last_table_i)
+ target = template # fixfull should be it's contents
+ target_i = target.children.length
+ break
+ # 4. If there is no last table, then let adjusted insertion
+ # location be inside the first element in the stack of open
+ # elements (the html element), after its last child (if any),
+ # and abort these substeps. (fragment case)
+ if last_table is null
+ # this is odd
+ target = open_els[open_els.length - 1]
+ target_i = target.children.length
+ # 5. If last table has a parent element, then let adjusted
+ # insertion location be inside last table's parent element,
+ # immediately before last table, and abort these substeps.
+ if last_table.parent?
+ for c, i in last_table.parent.children
+ if c is last_table
+ target = last_table.parent
+ target_i = i
+ break
+ break
+ # 6. Let previous element be the element immediately above last
+ # table in the stack of open elements.
+ #
+ # huh? how could it not have a parent?
+ previous_element = open_els[last_table_i + 1]
+ # 7. Let adjusted insertion location be inside previous
+ # element, after its last child (if any).
+ target = previous_element
+ target_i = target.children.length
+ # Note: These steps are involved in part because it's possible
+ # for elements, the table element in this case in particular,
+ # to have been moved by a script around in the DOM, or indeed
+ # removed from the DOM entirely, after the element was inserted
+ # by the parser.
+ break # don't really loop
else
# Otherwise Let adjusted insertion location be inside target, after
# its last child (if any).
# 3. If the adjusted insertion location is inside a template element,
# let it instead be inside the template element's template contents,
- # after its last child (if any). TODO
+ # after its last child (if any).
+ # fixfull (template)
# 4. Return the adjusted insertion location.
return [target, target_i]
# http://www.w3.org/TR/html5/syntax.html#create-an-element-for-the-token
# aka create_an_element_for_token
token_to_element = (t, namespace, intended_parent) ->
- t.type = TYPE_TAG # not TYPE_OPEN_TAG
+ t.type = TYPE_TAG # not TYPE_START_TAG
# convert attributes into a hash
attrs = {}
while t.attrs_a.length
if namespace?
el.namespace = namespace
dest = adjusted_insertion_location override_target
- if el.type is TYPE_OPEN_TAG # means it's a "token"
+ if el.type is TYPE_START_TAG # means it's a "token"
el = token_to_element el, namespace, dest[0]
unless el.namespace?
namespace = dest.namespace
return el
# http://www.w3.org/TR/html5/syntax.html#insert-a-comment
- tree_insert_a_comment = (t) ->
- # FIXME read spec for "adjusted insertion location, etc, this might be wrong
- open_els[0].children.push t
+ # position should be [node, index_within_children]
+ tree_insert_a_comment = (t, position = null) ->
+ position ?= adjusted_insertion_location()
+ position[0].children.splice position[1], 0, t
# 8.2.5.3 http://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags
# http://www.w3.org/TR/html5/syntax.html#generate-implied-end-tags
if special_elements[node.name]? # FIXME check namespac too
parse_error()
return
- tree_in_body = (t) ->
+ ins_mode_in_body = (t) ->
switch t.type
when TYPE_TEXT
switch t.text
tree_insert_a_comment t
when TYPE_DOCTYPE
parse_error()
- when TYPE_OPEN_TAG
+ when TYPE_START_TAG
switch t.name
when 'html'
parse_error()
# is not in table scope).
found = false
for el in afe
- if el.type is TYPE_MARKER
+ if el.type is TYPE_AFE_MARKER
break
if el.name is 'a'
found = el
reconstruct_active_formatting_elements()
el = tree_insert_element t
afe.unshift el
+ when 'table'
+ # fixfull quirksmode thing
+ close_p_if_in_button_scope()
+ insert_html_element t
+ insertion_mode = ins_mode_in_table
# TODO lots more to implement here
else # any other start tag
reconstruct_active_formatting_elements()
in_body_any_other_end_tag t.name
return
+ ins_mode_in_table_else = (t) ->
+ parse_error()
+ flag_foster_parenting = true # FIXME
+ ins_mode_in_body t
+ flag_foster_parenting = false
+ can_in_table = {
+ 'table': true
+ 'tbody': true
+ 'tfoot': true
+ 'thead': true
+ 'tr': true
+ }
+ clear_to_table_stopers = {
+ 'table': true
+ 'template': true
+ 'html': true
+ }
+ clear_stack_to_table_context = ->
+ loop
+ if clear_to_table_stopers[open_els[0].name]?
+ break
+ open_els.shift()
+ return
+ clear_to_table_body_stopers = {
+ 'tbody': true
+ 'tfoot': true
+ 'thead': true
+ 'template': true
+ 'html': true
+ }
+ clear_stack_to_table_body_context = ->
+ loop
+ if clear_to_table_body_stopers[open_els[0].name]?
+ break
+ open_els.shift()
+ return
+ clear_to_table_row_stopers = {
+ 'tr': true
+ 'template': true
+ 'html': true
+ }
+ clear_stack_to_table_row_context = ->
+ loop
+ if clear_to_table_row_stopers[open_els[0].name]?
+ break
+ open_els.shift()
+ return
+ clear_afe_to_marker = ->
+ loop
+ el = afe.shift()
+ if el.type is TYPE_AFE_MARKER
+ return
+ ins_mode_in_table = (t) ->
+ switch t.type
+ when TYPE_TEXT
+ if can_in_table[t.name]
+ original_insertion_mode = insertion_mode
+ insertion_mode = ins_mode_in_table_text
+ insertion_mode t
+ else
+ ins_mode_in_table_else t
+ when TYPE_COMMENT
+ tree_insert_a_comment t
+ when TYPE_DOCTYPE
+ parse_error()
+ when TYPE_START_TAG
+ switch t.name
+ when 'caption'
+ clear_stack_to_table_context()
+ afe.unshift new_afe_marker()
+ insert_html_element t
+ insertion_mode = ins_mode_in_caption
+ when 'colgroup'
+ clear_stack_to_table_context()
+ insert_html_element t
+ insertion_mode = ins_mode_in_column_group
+ when 'col'
+ clear_stack_to_table_context()
+ insert_html_element new_open_tag 'colgroup'
+ insertion_mode = ins_mode_in_column_group
+ insertion_mode t
+ when 'tbody', 'tfoot', 'thead'
+ clear_stack_to_table_context()
+ insert_html_element t
+ insertion_mode = ins_mode_in_table_body
+ when 'td', 'th', 'tr'
+ clear_stack_to_table_context()
+ insert_html_element new_open_tag 'tbody'
+ insertion_mode = ins_mode_in_table_body
+ insertion_mode t
+ when 'table'
+ parse_error()
+ if is_in_table_scope 'table'
+ loop
+ el = open_els.shift()
+ if el.name is 'table'
+ break
+ reset_insertion_mode()
+ insertion_mode t
+ when 'style', 'script', 'template'
+ ins_mode_in_head t
+ when 'input'
+ if token_is_input_hidden t
+ ins_mode_in_table_else t
+ else
+ parse_error()
+ insert_html_element t
+ open_els.shift()
+ # fixfull acknowledge sef-closing flag
+ when 'form'
+ parse_error()
+ if form_element_pointer?
+ return
+ if template_tag_is_open()
+ return
+ form_element_pointer = insert_html_element t
+ open_els.shift()
+ else
+ ins_mode_in_table_else t
+ when TYPE_END_TAG
+ switch t.name
+ when 'table'
+ if is_in_table_scope 'table'
+ loop
+ el = open_els.shift()
+ if el.name is 'table'
+ break
+ reset_insertion_mode()
+ else
+ parse_error
+ when 'body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr'
+ parse_error()
+ when 'template'
+ ins_mode_in_head t
+ else
+ ins_mode_in_table_else t
+ when TYPE_EOF
+ ins_mode_in_body t
+ else
+ ins_mode_in_table_else t
+
+
+ ins_mode_in_table_text = (t) ->
+ switch t.type
+ when TYPE_TEXT
+ switch t.text
+ when "\u0000"
+ parse_error()
+ return
+ console.log "unimplemented ins_mode_in_table_text"
+ # FIXME CONTINUE
+
+ ins_mode_in_table_body = (t) ->
+ if t.type is TYPE_START_TAG and t.name is 'tr'
+ clear_stack_to_table_body_context()
+ insert_html_element t
+ return
+ if t.type is TYPE_START_TAG and (t.name is 'th' or t.name is 'td')
+ parse_error()
+ clear_stack_to_table_body_context()
+ insert_html_element new_open_tag 'tr'
+ insertion_mode = ins_mode_in_row
+ insertion_mode t
+ return
+ if t.type is TYPE_END_TAG and (t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead')
+ unless is_in_table_scope t.name # fixfull check namespace
+ parse_error()
+ return
+ clear_stack_to_table_body_context()
+ open_els.shift()
+ insertion_mode = ins_mode_in_table
+ return
+ if (t.type is TYPE_START_TAG and (t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead')) or (t.type is TYPE_END_TAG and t.name is 'table')
+ has = false
+ for el in open_els
+ if el.name is 'tbody' or el.name is 'tfoot' or el.name is 'thead'
+ has = true
+ break
+ if table_scopers[el.name]
+ break
+ if !has
+ parse_error()
+ return
+ clear_stack_to_table_body_context()
+ open_els.shift()
+ insertion_mode = ins_mode_in_table
+ insertion_mode t
+ return
+ if t.type is TYPE_END_TAG and (t.name is 'body' or t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'html' or t.name is 'td' or t.name is 'th' or t.name is 'tr')
+ parse_error()
+ return
+ # Anything else
+ ins_mode_in_table t
+
+ ins_mode_in_row = (t) ->
+ if t.type is TYPE_START_TAG and (t.name is 'th' or t.name is 'td')
+ clear_stack_to_table_row_context()
+ insert_html_element t
+ insertion_mode = ins_mode_in_cell
+ afe.unshift new_afe_marker()
+ return
+ if t.type is TYPE_END_TAG and t.name is 'tr'
+ if is_in_table_scope 'tr'
+ clear_stack_to_table_row_context()
+ open_els.shift()
+ insertion_mode = ins_mode_in_table_body
+ else
+ parse_error()
+ return
+ if (t.type is TYPE_START_TAG and (t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead' or t.name is 'tr')) or t.type is TYPE_END_TAG and t.name is 'table'
+ if is_in_table_scope 'tr'
+ clear_stack_to_table_row_context()
+ open_els.shift()
+ insertion_mode = ins_mode_in_table_body
+ insertion_mode t
+ else
+ parse_error()
+ return
+ if t.type is TYPE_END_TAG and (t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead')
+ if is_in_table_scope t.name # fixfull namespace
+ if is_in_table_scope 'tr'
+ clear_stack_to_table_row_context()
+ open_els.shift()
+ insertion_mode = ins_mode_in_table_body
+ insertion_mode t
+ else
+ parse_error()
+ return
+ if t.type is TYPE_END_TAG and (t.name is 'body' or t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'html' or t.name is 'td' or t.name is 'th')
+ parse_error()
+ return
+ # Anything else
+ ins_mode_in_table t
+
+ # http://www.w3.org/TR/html5/syntax.html#close-the-cell
+ close_the_cell = ->
+ generate_implied_end_tags()
+ unless open_els[0].name is 'td' or open_els[0] is 'th'
+ parse_error()
+ loop
+ el = open_els.shift()
+ if el.name is 'td' or el.name is 'th'
+ break
+ clear_afe_to_marker()
+ insertion_mode = ins_mode_in_row
+
+ # http://www.w3.org/TR/html5/syntax.html#parsing-main-intd
+ ins_mode_in_cell = (t) ->
+ if t.type is TYPE_END_TAG and (t.name is 'td' or t.name is 'th')
+ if is_in_table_scope t.name
+ generate_implied_end_tags()
+ if open_els[0].name isnt t.name
+ parse_error
+ loop
+ el = open_els.shift()
+ if el.name is t.name
+ break
+ clear_afe_to_marker()
+ insertion_mode = ins_mode_in_row
+ else
+ parse_error()
+ return
+ if t.type is TYPE_START_TAG and (t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'tbody' or t.name is 'td' or t.name is 'tfoot' or t.name is 'th' or t.name is 'thead' or t.name is 'tr')
+ has = false
+ for el in open_els
+ if el.name is 'td' or el.name is 'th'
+ has = true
+ break
+ if table_scopers[el.name]
+ break
+ if !has
+ parse_error()
+ return
+ close_the_cell()
+ insertion_mode t
+ return
+ if t.type is TYPE_END_TAG and (t.name is 'body' or t.name is 'caption' or t.name is 'col' or t.name is 'colgroup' or t.name is 'html')
+ parse_error()
+ return
+ if t.type is TYPE_END_TAG and (t.name is 'table' or t.name is 'tbody' or t.name is 'tfoot' or t.name is 'thead' or t.name is 'tr')
+ if is_in_table_scope t.name # fixfull namespace
+ close_the_cell()
+ insertion_mode t
+ else
+ parse_error()
+ return
+ # Anything Else
+ ins_mode_in_body t
+
# the functions below implement the tokenizer stats described here:
# http://www.w3.org/TR/html5/syntax.html#tokenization
# see comments on TYPE_TAG/etc for the structure of this data
tree = new Node TYPE_TAG, name: 'html', namespace: NS_HTML
open_els = [tree]
- tree_state = tree_in_body
+ insertion_mode = ins_mode_in_body
flag_frameset_ok = true
flag_parsing = true
flag_foster_parenting = false
+ form_element_pointer = null
afe = [] # active formatting elements
# tokenizer initialization
while flag_parsing
t = tok_state()
if t?
- tree_state t
+ insertion_mode t
return tree.children
# everything below is tests on the above
html: '<a>1<b>2</a>3</b>',
expected: 'tag:"a",{},[text:"1",tag:"b",{},[text:"2"]],tag:"b",{},[text:"3"]',
errors: 2
-test_parser name: "html5lib aaa 5", \
+test_parser name: "html5lib aaa 5 (two divs deep)", \
html: '<a>1<div>2<div>3</a>4</div>5</div>',
expected: 'tag:"a",{},[text:"1"],tag:"div",{},[tag:"a",{},[text:"2"],tag:"div",{},[tag:"a",{},[text:"3"],text:"4"],text:"5"]',
errors: 3
+test_parser name: "html5lib aaa 6 (foster parenting)", \
+ html: '<table><a>1<p>2</a>3</p>',
+ expected: 'tag:"a",{},[text:"1"],tag:"p",{},[tag:"a",{},[text:"2"],text:"3"],tag:"table",{},[]',
+ errors: 10
+test_parser name: "html5lib aaa 11 (table with foster parenting, formatting el and td)", \
+ html: '<table><a>1<td>2</td>3</table>',
+ expected: 'tag:"a",{},[text:"1"],tag:"a",{},[text:"3"],tag:"table",{},[tag:"tbody",{},[tag:"tr",{},[tag:"td",{},[text:"2"]]]]',
+ errors: 10