+ tok_cur_tag = null # partially parsed tag
+ flag_frameset_ok = null
+ flag_parsing = null
+
+ parse_error = ->
+ if parse_error_cb?
+ parse_error_cb cur
+ else
+ console.log "Parse error at character #{cur} of #{txt.length}"
+
+
+ # 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.type is TYPE_TAG and t.name is 'template'
+ return true
+ return false
+ is_in_scope = (tag_name) ->
+ for t of open_tags
+ if t.name is tag_name
+ return true
+ if t.name of scopers
+ return false
+ 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].name is 'p'
+ 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].name 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
+ dest = open_tags[0].children
+ if dest.length > 0 and dest[dest.length - 1].type is TYPE_TEXT
+ dest[dest.length - 1].text += t.text
+ else
+ dest.push t
+
+ # FIXME read spec, do this right
+ # note: this assumes it's an open tag
+ tree_insert_tag = (t) ->
+ t.type = TYPE_TAG # not TYPE_OPEN_TAG
+ # convert attributes into a hash
+ while t.attrs_a.length
+ a = t.attrs_a.pop()
+ t.attrs[a[0]] = a[1] # TODO check what to do with dupilcate attrs
+ open_tags[0].children.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].children.push t
+
+ # 8.2.5.4 http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody
+ tree_in_body = (t) ->
+ switch t.type
+ when TYPE_TEXT
+ switch t.text
+ 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.name
+ when 'html'
+ parse_error()
+ return if template_tag_is_open()
+ root_attrs = open_tags[open_tags.length - 1].children
+ for k, v of t.attrs
+ 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].name 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.name]?
+ parse_error()
+ break
+ # TODO stack of template insertion modes thing
+ flag_parsing = false # stop parsing
+ when TYPE_END_TAG
+ switch t.name
+ 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.name is t.name
+ # FIXME generate implied end tags except those with name==t.name
+ parse_error() unless i is 0
+ while i > 0
+ open_tags.shift()
+ i -= 1
+ open_tags.shift()
+ return
+ if special_elements[node.name]?
+ parse_error()
+ return