+
+ # the functions below impliment the Tree Contstruction algorithm
+ # http://www.w3.org/TR/html5/syntax.html#tree-construction
+
+ # But first... the helpers
+ template_tag_is_open = ->
+ for t of open_tags
+ if t[0] is TYPE_TAG and t[1] is 'template'
+ return true
+ return false
+ is_in_scope = (tag_name) ->
+ for t of open_tags
+ if t[0] is TYPE_TAG and t[1] is tag_name
+ return true
+ # FIXME bail if in scopers
+ return false
+
+ reconstruct_active_formatting_elements = ->
+ # FIXME implement this
+
+ # http://www.w3.org/TR/html5/syntax.html#close-a-p-element
+ # FIXME implement this
+ close_p_if_in_button_scope = ->
+ if open_tags[0][1] is 'p' # FIXME
+ open_tags.pop()
+ return
+ #p = find_button_scope 'p'
+ #if p?
+ # TODO generate_implied_end_tags except for p tags
+ # TODO parse_error unless open_tags[0][1] is 'p'
+ # TODO pop stack until 'p' popped
+
+
+
+ # http://www.w3.org/TR/html5/syntax.html#insert-a-character
+ tree_insert_a_character = (t) ->
+ # FIXME read spec for "adjusted insertion location, etc, this might be wrong
+ if open_tags[0][3].length > 0 and open_tags[0][3][open_tags[0][3].length - 1][0] is TYPE_TEXT
+ open_tags[0][3][open_tags[0][3].length - 1][1] += t[1]
+ else
+ open_tags[0][3].push t
+
+ # FIXME read spec, do this right
+ # note: this assumes it's an open tag
+ tree_insert_tag = (t) ->
+ t[0] = TYPE_TAG # not TYPE_OPEN_TAG
+ # convert attributes into a hash
+ attrs = {}
+ while t[2].length
+ a = t[2].pop()
+ attrs[a[0]] = a[1]
+ t[2] = attrs
+ open_tags[0][3].push t
+ open_tags.unshift t
+
+ # 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_tags[0][3].push t
+
+ # 8.2.5.4 http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody
+ tree_in_body = (t) ->
+ switch t[0]
+ when TYPE_TEXT
+ switch t[1]
+ when "\u0000"
+ parse_error()
+ when "\t", "\u000a", "\u000c", "\u000d", ' '
+ reconstruct_active_formatting_elements()
+ tree_insert_a_character t
+ else
+ reconstruct_active_formatting_elements()
+ tree_insert_a_character t
+ flag_frameset_ok = false
+ when TYPE_COMMENT
+ tree_insert_a_comment t
+ when TYPE_DOCTYPE
+ parse_error()
+ when TYPE_OPEN_TAG
+ switch t[1]
+ when 'html'
+ parse_error()
+ return if template_tag_is_open()
+ root_attrs = open_tags[open_tags.length - 1][3]
+ for k, v of t[2]
+ root_attrs[k] = v unless root_attrs[k]?
+ when 'base', 'basefont', 'bgsound', 'link', 'meta', 'noframes', 'script', 'style', 'template', 'title'
+ # FIXME also do this for </template> (end tag)
+ return tree_in_head t
+ when 'body'
+ parse_error()
+ # TODO
+ when 'frameset'
+ parse_error()
+ # TODO
+ when 'address', 'article', 'aside', 'blockquote', 'center', 'details', 'dialog', 'dir', 'div', 'dl', 'fieldset', 'figcaption', 'figure', 'footer', 'header', 'hgroup', 'main', 'nav', 'ol', 'p', 'section', 'summary', 'ul'
+ close_p_if_in_button_scope()
+ tree_insert_tag t
+ when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
+ close_p_if_in_button_scope()
+ if open_tags[0][1] in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
+ parse_error()
+ open_tags.shift()
+ tree_insert_tag t
+ # TODO lots more to implement here
+ else # any other start tag
+ reconstruct_active_formatting_elements()
+ tree_insert_tag t
+ when TYPE_EOF
+ ok_tags = {
+ dd: true, dt: true, li: true, p: true, tbody: true, td: true,
+ tfoot: true, th: true, thead: true, tr: true, body: true, html: true,
+ }
+ for t in open_tags
+ unless ok_tags[t[1]]?
+ parse_error()
+ break
+ # TODO stack of template insertion modes thing
+ flag_parsing = false # stop parsing
+ when TYPE_END_TAG
+ switch t[1]
+ when 'body'
+ unless is_in_scope 'body'
+ parse_error()
+ return
+ # TODO implement parse error and move to tree_after_body
+ when 'html'
+ unless is_in_scope 'body' # weird, but it's what the spec says
+ parse_error()
+ return
+ # TODO implement parse error and move to tree_after_body, reprocess
+ # TODO lots more close tags to implement here
+ else
+ for node, i in open_tags
+ if node[1] is t[1]
+ # FIXME generate implied end tags except those with name==t[1]
+ parse_error() unless i is 0
+ while i > 0
+ open_tags.shift()
+ i -= 1
+ open_tags.shift()
+ return
+ if special_elements[node[1]]?
+ parse_error()
+ return
+
+