You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							2819 lines
						
					
					
						
							86 KiB
						
					
					
				
			
		
		
	
	
							2819 lines
						
					
					
						
							86 KiB
						
					
					
				'use strict'; | 
						|
 | 
						|
var Tokenizer = require('../tokenizer'), | 
						|
    OpenElementStack = require('./open_element_stack'), | 
						|
    FormattingElementList = require('./formatting_element_list'), | 
						|
    LocationInfoParserMixin = require('../extensions/location_info/parser_mixin'), | 
						|
    defaultTreeAdapter = require('../tree_adapters/default'), | 
						|
    mergeOptions = require('../utils/merge_options'), | 
						|
    doctype = require('../common/doctype'), | 
						|
    foreignContent = require('../common/foreign_content'), | 
						|
    UNICODE = require('../common/unicode'), | 
						|
    HTML = require('../common/html'); | 
						|
 | 
						|
//Aliases | 
						|
var $ = HTML.TAG_NAMES, | 
						|
    NS = HTML.NAMESPACES, | 
						|
    ATTRS = HTML.ATTRS; | 
						|
 | 
						|
var DEFAULT_OPTIONS = { | 
						|
    locationInfo: false, | 
						|
    treeAdapter: defaultTreeAdapter | 
						|
}; | 
						|
 | 
						|
//Misc constants | 
						|
var HIDDEN_INPUT_TYPE = 'hidden'; | 
						|
 | 
						|
//Adoption agency loops iteration count | 
						|
var AA_OUTER_LOOP_ITER = 8, | 
						|
    AA_INNER_LOOP_ITER = 3; | 
						|
 | 
						|
//Insertion modes | 
						|
var INITIAL_MODE = 'INITIAL_MODE', | 
						|
    BEFORE_HTML_MODE = 'BEFORE_HTML_MODE', | 
						|
    BEFORE_HEAD_MODE = 'BEFORE_HEAD_MODE', | 
						|
    IN_HEAD_MODE = 'IN_HEAD_MODE', | 
						|
    AFTER_HEAD_MODE = 'AFTER_HEAD_MODE', | 
						|
    IN_BODY_MODE = 'IN_BODY_MODE', | 
						|
    TEXT_MODE = 'TEXT_MODE', | 
						|
    IN_TABLE_MODE = 'IN_TABLE_MODE', | 
						|
    IN_TABLE_TEXT_MODE = 'IN_TABLE_TEXT_MODE', | 
						|
    IN_CAPTION_MODE = 'IN_CAPTION_MODE', | 
						|
    IN_COLUMN_GROUP_MODE = 'IN_COLUMN_GROUP_MODE', | 
						|
    IN_TABLE_BODY_MODE = 'IN_TABLE_BODY_MODE', | 
						|
    IN_ROW_MODE = 'IN_ROW_MODE', | 
						|
    IN_CELL_MODE = 'IN_CELL_MODE', | 
						|
    IN_SELECT_MODE = 'IN_SELECT_MODE', | 
						|
    IN_SELECT_IN_TABLE_MODE = 'IN_SELECT_IN_TABLE_MODE', | 
						|
    IN_TEMPLATE_MODE = 'IN_TEMPLATE_MODE', | 
						|
    AFTER_BODY_MODE = 'AFTER_BODY_MODE', | 
						|
    IN_FRAMESET_MODE = 'IN_FRAMESET_MODE', | 
						|
    AFTER_FRAMESET_MODE = 'AFTER_FRAMESET_MODE', | 
						|
    AFTER_AFTER_BODY_MODE = 'AFTER_AFTER_BODY_MODE', | 
						|
    AFTER_AFTER_FRAMESET_MODE = 'AFTER_AFTER_FRAMESET_MODE'; | 
						|
 | 
						|
//Insertion mode reset map | 
						|
var INSERTION_MODE_RESET_MAP = Object.create(null); | 
						|
 | 
						|
INSERTION_MODE_RESET_MAP[$.TR] = IN_ROW_MODE; | 
						|
INSERTION_MODE_RESET_MAP[$.TBODY] = | 
						|
INSERTION_MODE_RESET_MAP[$.THEAD] = | 
						|
INSERTION_MODE_RESET_MAP[$.TFOOT] = IN_TABLE_BODY_MODE; | 
						|
INSERTION_MODE_RESET_MAP[$.CAPTION] = IN_CAPTION_MODE; | 
						|
INSERTION_MODE_RESET_MAP[$.COLGROUP] = IN_COLUMN_GROUP_MODE; | 
						|
INSERTION_MODE_RESET_MAP[$.TABLE] = IN_TABLE_MODE; | 
						|
INSERTION_MODE_RESET_MAP[$.BODY] = IN_BODY_MODE; | 
						|
INSERTION_MODE_RESET_MAP[$.FRAMESET] = IN_FRAMESET_MODE; | 
						|
 | 
						|
//Template insertion mode switch map | 
						|
var TEMPLATE_INSERTION_MODE_SWITCH_MAP = Object.create(null); | 
						|
 | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.CAPTION] = | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.COLGROUP] = | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TBODY] = | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TFOOT] = | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.THEAD] = IN_TABLE_MODE; | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.COL] = IN_COLUMN_GROUP_MODE; | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TR] = IN_TABLE_BODY_MODE; | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TD] = | 
						|
TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TH] = IN_ROW_MODE; | 
						|
 | 
						|
//Token handlers map for insertion modes | 
						|
var _ = Object.create(null); | 
						|
 | 
						|
_[INITIAL_MODE] = Object.create(null); | 
						|
_[INITIAL_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[INITIAL_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenInInitialMode; | 
						|
_[INITIAL_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[INITIAL_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[INITIAL_MODE][Tokenizer.DOCTYPE_TOKEN] = doctypeInInitialMode; | 
						|
_[INITIAL_MODE][Tokenizer.START_TAG_TOKEN] = | 
						|
_[INITIAL_MODE][Tokenizer.END_TAG_TOKEN] = | 
						|
_[INITIAL_MODE][Tokenizer.EOF_TOKEN] = tokenInInitialMode; | 
						|
 | 
						|
_[BEFORE_HTML_MODE] = Object.create(null); | 
						|
_[BEFORE_HTML_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[BEFORE_HTML_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenBeforeHtml; | 
						|
_[BEFORE_HTML_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[BEFORE_HTML_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[BEFORE_HTML_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[BEFORE_HTML_MODE][Tokenizer.START_TAG_TOKEN] = startTagBeforeHtml; | 
						|
_[BEFORE_HTML_MODE][Tokenizer.END_TAG_TOKEN] = endTagBeforeHtml; | 
						|
_[BEFORE_HTML_MODE][Tokenizer.EOF_TOKEN] = tokenBeforeHtml; | 
						|
 | 
						|
_[BEFORE_HEAD_MODE] = Object.create(null); | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenBeforeHead; | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.START_TAG_TOKEN] = startTagBeforeHead; | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.END_TAG_TOKEN] = endTagBeforeHead; | 
						|
_[BEFORE_HEAD_MODE][Tokenizer.EOF_TOKEN] = tokenBeforeHead; | 
						|
 | 
						|
_[IN_HEAD_MODE] = Object.create(null); | 
						|
_[IN_HEAD_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[IN_HEAD_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenInHead; | 
						|
_[IN_HEAD_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_HEAD_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_HEAD_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_HEAD_MODE][Tokenizer.START_TAG_TOKEN] = startTagInHead; | 
						|
_[IN_HEAD_MODE][Tokenizer.END_TAG_TOKEN] = endTagInHead; | 
						|
_[IN_HEAD_MODE][Tokenizer.EOF_TOKEN] = tokenInHead; | 
						|
 | 
						|
_[AFTER_HEAD_MODE] = Object.create(null); | 
						|
_[AFTER_HEAD_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[AFTER_HEAD_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenAfterHead; | 
						|
_[AFTER_HEAD_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[AFTER_HEAD_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[AFTER_HEAD_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[AFTER_HEAD_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterHead; | 
						|
_[AFTER_HEAD_MODE][Tokenizer.END_TAG_TOKEN] = endTagAfterHead; | 
						|
_[AFTER_HEAD_MODE][Tokenizer.EOF_TOKEN] = tokenAfterHead; | 
						|
 | 
						|
_[IN_BODY_MODE] = Object.create(null); | 
						|
_[IN_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; | 
						|
_[IN_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[IN_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagInBody; | 
						|
_[IN_BODY_MODE][Tokenizer.END_TAG_TOKEN] = endTagInBody; | 
						|
_[IN_BODY_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[TEXT_MODE] = Object.create(null); | 
						|
_[TEXT_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[TEXT_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = | 
						|
_[TEXT_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[TEXT_MODE][Tokenizer.COMMENT_TOKEN] = | 
						|
_[TEXT_MODE][Tokenizer.DOCTYPE_TOKEN] = | 
						|
_[TEXT_MODE][Tokenizer.START_TAG_TOKEN] = ignoreToken; | 
						|
_[TEXT_MODE][Tokenizer.END_TAG_TOKEN] = endTagInText; | 
						|
_[TEXT_MODE][Tokenizer.EOF_TOKEN] = eofInText; | 
						|
 | 
						|
_[IN_TABLE_MODE] = Object.create(null); | 
						|
_[IN_TABLE_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[IN_TABLE_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = | 
						|
_[IN_TABLE_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = characterInTable; | 
						|
_[IN_TABLE_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_TABLE_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_TABLE_MODE][Tokenizer.START_TAG_TOKEN] = startTagInTable; | 
						|
_[IN_TABLE_MODE][Tokenizer.END_TAG_TOKEN] = endTagInTable; | 
						|
_[IN_TABLE_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_TABLE_TEXT_MODE] = Object.create(null); | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.CHARACTER_TOKEN] = characterInTableText; | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInTableText; | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.COMMENT_TOKEN] = | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.DOCTYPE_TOKEN] = | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.START_TAG_TOKEN] = | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.END_TAG_TOKEN] = | 
						|
_[IN_TABLE_TEXT_MODE][Tokenizer.EOF_TOKEN] = tokenInTableText; | 
						|
 | 
						|
_[IN_CAPTION_MODE] = Object.create(null); | 
						|
_[IN_CAPTION_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; | 
						|
_[IN_CAPTION_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_CAPTION_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[IN_CAPTION_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_CAPTION_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_CAPTION_MODE][Tokenizer.START_TAG_TOKEN] = startTagInCaption; | 
						|
_[IN_CAPTION_MODE][Tokenizer.END_TAG_TOKEN] = endTagInCaption; | 
						|
_[IN_CAPTION_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_COLUMN_GROUP_MODE] = Object.create(null); | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenInColumnGroup; | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.START_TAG_TOKEN] = startTagInColumnGroup; | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.END_TAG_TOKEN] = endTagInColumnGroup; | 
						|
_[IN_COLUMN_GROUP_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_TABLE_BODY_MODE] = Object.create(null); | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = characterInTable; | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagInTableBody; | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.END_TAG_TOKEN] = endTagInTableBody; | 
						|
_[IN_TABLE_BODY_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_ROW_MODE] = Object.create(null); | 
						|
_[IN_ROW_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[IN_ROW_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = | 
						|
_[IN_ROW_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = characterInTable; | 
						|
_[IN_ROW_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_ROW_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_ROW_MODE][Tokenizer.START_TAG_TOKEN] = startTagInRow; | 
						|
_[IN_ROW_MODE][Tokenizer.END_TAG_TOKEN] = endTagInRow; | 
						|
_[IN_ROW_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_CELL_MODE] = Object.create(null); | 
						|
_[IN_CELL_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; | 
						|
_[IN_CELL_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_CELL_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[IN_CELL_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_CELL_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_CELL_MODE][Tokenizer.START_TAG_TOKEN] = startTagInCell; | 
						|
_[IN_CELL_MODE][Tokenizer.END_TAG_TOKEN] = endTagInCell; | 
						|
_[IN_CELL_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_SELECT_MODE] = Object.create(null); | 
						|
_[IN_SELECT_MODE][Tokenizer.CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_SELECT_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_SELECT_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_SELECT_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_SELECT_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_SELECT_MODE][Tokenizer.START_TAG_TOKEN] = startTagInSelect; | 
						|
_[IN_SELECT_MODE][Tokenizer.END_TAG_TOKEN] = endTagInSelect; | 
						|
_[IN_SELECT_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_SELECT_IN_TABLE_MODE] = Object.create(null); | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.START_TAG_TOKEN] = startTagInSelectInTable; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.END_TAG_TOKEN] = endTagInSelectInTable; | 
						|
_[IN_SELECT_IN_TABLE_MODE][Tokenizer.EOF_TOKEN] = eofInBody; | 
						|
 | 
						|
_[IN_TEMPLATE_MODE] = Object.create(null); | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.START_TAG_TOKEN] = startTagInTemplate; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.END_TAG_TOKEN] = endTagInTemplate; | 
						|
_[IN_TEMPLATE_MODE][Tokenizer.EOF_TOKEN] = eofInTemplate; | 
						|
 | 
						|
_[AFTER_BODY_MODE] = Object.create(null); | 
						|
_[AFTER_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[AFTER_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenAfterBody; | 
						|
_[AFTER_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[AFTER_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendCommentToRootHtmlElement; | 
						|
_[AFTER_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[AFTER_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterBody; | 
						|
_[AFTER_BODY_MODE][Tokenizer.END_TAG_TOKEN] = endTagAfterBody; | 
						|
_[AFTER_BODY_MODE][Tokenizer.EOF_TOKEN] = stopParsing; | 
						|
 | 
						|
_[IN_FRAMESET_MODE] = Object.create(null); | 
						|
_[IN_FRAMESET_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[IN_FRAMESET_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[IN_FRAMESET_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[IN_FRAMESET_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[IN_FRAMESET_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[IN_FRAMESET_MODE][Tokenizer.START_TAG_TOKEN] = startTagInFrameset; | 
						|
_[IN_FRAMESET_MODE][Tokenizer.END_TAG_TOKEN] = endTagInFrameset; | 
						|
_[IN_FRAMESET_MODE][Tokenizer.EOF_TOKEN] = stopParsing; | 
						|
 | 
						|
_[AFTER_FRAMESET_MODE] = Object.create(null); | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterFrameset; | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.END_TAG_TOKEN] = endTagAfterFrameset; | 
						|
_[AFTER_FRAMESET_MODE][Tokenizer.EOF_TOKEN] = stopParsing; | 
						|
 | 
						|
_[AFTER_AFTER_BODY_MODE] = Object.create(null); | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = tokenAfterAfterBody; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenAfterAfterBody; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendCommentToDocument; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterAfterBody; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.END_TAG_TOKEN] = tokenAfterAfterBody; | 
						|
_[AFTER_AFTER_BODY_MODE][Tokenizer.EOF_TOKEN] = stopParsing; | 
						|
 | 
						|
_[AFTER_AFTER_FRAMESET_MODE] = Object.create(null); | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.CHARACTER_TOKEN] = | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.COMMENT_TOKEN] = appendCommentToDocument; | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterAfterFrameset; | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.END_TAG_TOKEN] = ignoreToken; | 
						|
_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.EOF_TOKEN] = stopParsing; | 
						|
 | 
						|
 | 
						|
//Parser | 
						|
var Parser = module.exports = function (options) { | 
						|
    this.options = mergeOptions(DEFAULT_OPTIONS, options); | 
						|
 | 
						|
    this.treeAdapter = this.options.treeAdapter; | 
						|
    this.pendingScript = null; | 
						|
 | 
						|
    if (this.options.locationInfo) | 
						|
        new LocationInfoParserMixin(this); | 
						|
}; | 
						|
 | 
						|
// API | 
						|
Parser.prototype.parse = function (html) { | 
						|
    var document = this.treeAdapter.createDocument(); | 
						|
 | 
						|
    this._bootstrap(document, null); | 
						|
    this.tokenizer.write(html, true); | 
						|
    this._runParsingLoop(null); | 
						|
 | 
						|
    return document; | 
						|
}; | 
						|
 | 
						|
Parser.prototype.parseFragment = function (html, fragmentContext) { | 
						|
    //NOTE: use <template> element as a fragment context if context element was not provided, | 
						|
    //so we will parse in "forgiving" manner | 
						|
    if (!fragmentContext) | 
						|
        fragmentContext = this.treeAdapter.createElement($.TEMPLATE, NS.HTML, []); | 
						|
 | 
						|
    //NOTE: create fake element which will be used as 'document' for fragment parsing. | 
						|
    //This is important for jsdom there 'document' can't be recreated, therefore | 
						|
    //fragment parsing causes messing of the main `document`. | 
						|
    var documentMock = this.treeAdapter.createElement('documentmock', NS.HTML, []); | 
						|
 | 
						|
    this._bootstrap(documentMock, fragmentContext); | 
						|
 | 
						|
    if (this.treeAdapter.getTagName(fragmentContext) === $.TEMPLATE) | 
						|
        this._pushTmplInsertionMode(IN_TEMPLATE_MODE); | 
						|
 | 
						|
    this._initTokenizerForFragmentParsing(); | 
						|
    this._insertFakeRootElement(); | 
						|
    this._resetInsertionMode(); | 
						|
    this._findFormInFragmentContext(); | 
						|
    this.tokenizer.write(html, true); | 
						|
    this._runParsingLoop(null); | 
						|
 | 
						|
    var rootElement = this.treeAdapter.getFirstChild(documentMock), | 
						|
        fragment = this.treeAdapter.createDocumentFragment(); | 
						|
 | 
						|
    this._adoptNodes(rootElement, fragment); | 
						|
 | 
						|
    return fragment; | 
						|
}; | 
						|
 | 
						|
//Bootstrap parser | 
						|
Parser.prototype._bootstrap = function (document, fragmentContext) { | 
						|
    this.tokenizer = new Tokenizer(this.options); | 
						|
 | 
						|
    this.stopped = false; | 
						|
 | 
						|
    this.insertionMode = INITIAL_MODE; | 
						|
    this.originalInsertionMode = ''; | 
						|
 | 
						|
    this.document = document; | 
						|
    this.fragmentContext = fragmentContext; | 
						|
 | 
						|
    this.headElement = null; | 
						|
    this.formElement = null; | 
						|
 | 
						|
    this.openElements = new OpenElementStack(this.document, this.treeAdapter); | 
						|
    this.activeFormattingElements = new FormattingElementList(this.treeAdapter); | 
						|
 | 
						|
    this.tmplInsertionModeStack = []; | 
						|
    this.tmplInsertionModeStackTop = -1; | 
						|
    this.currentTmplInsertionMode = null; | 
						|
 | 
						|
    this.pendingCharacterTokens = []; | 
						|
    this.hasNonWhitespacePendingCharacterToken = false; | 
						|
 | 
						|
    this.framesetOk = true; | 
						|
    this.skipNextNewLine = false; | 
						|
    this.fosterParentingEnabled = false; | 
						|
}; | 
						|
 | 
						|
//Parsing loop | 
						|
Parser.prototype._runParsingLoop  = function (scriptHandler) { | 
						|
    while (!this.stopped) { | 
						|
        this._setupTokenizerCDATAMode(); | 
						|
 | 
						|
        var token = this.tokenizer.getNextToken(); | 
						|
 | 
						|
        if (token.type === Tokenizer.HIBERNATION_TOKEN) | 
						|
            break; | 
						|
 | 
						|
        if (this.skipNextNewLine) { | 
						|
            this.skipNextNewLine = false; | 
						|
 | 
						|
            if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN && token.chars[0] === '\n') { | 
						|
                if (token.chars.length === 1) | 
						|
                    continue; | 
						|
 | 
						|
                token.chars = token.chars.substr(1); | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        this._processInputToken(token); | 
						|
 | 
						|
        if (scriptHandler && this.pendingScript) | 
						|
            break; | 
						|
    } | 
						|
}; | 
						|
 | 
						|
Parser.prototype.runParsingLoopForCurrentChunk = function (writeCallback, scriptHandler) { | 
						|
    this._runParsingLoop(scriptHandler); | 
						|
 | 
						|
    if (scriptHandler && this.pendingScript) { | 
						|
        var script = this.pendingScript; | 
						|
 | 
						|
        this.pendingScript = null; | 
						|
 | 
						|
        scriptHandler(script); | 
						|
 | 
						|
        return; | 
						|
    } | 
						|
 | 
						|
    if (writeCallback) | 
						|
        writeCallback(); | 
						|
}; | 
						|
 | 
						|
//Text parsing | 
						|
Parser.prototype._setupTokenizerCDATAMode = function () { | 
						|
    var current = this._getAdjustedCurrentElement(); | 
						|
 | 
						|
    this.tokenizer.allowCDATA = current && current !== this.document && | 
						|
                                this.treeAdapter.getNamespaceURI(current) !== NS.HTML && !this._isIntegrationPoint(current); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._switchToTextParsing = function (currentToken, nextTokenizerState) { | 
						|
    this._insertElement(currentToken, NS.HTML); | 
						|
    this.tokenizer.state = nextTokenizerState; | 
						|
    this.originalInsertionMode = this.insertionMode; | 
						|
    this.insertionMode = TEXT_MODE; | 
						|
}; | 
						|
 | 
						|
Parser.prototype.switchToPlaintextParsing = function () { | 
						|
    this.insertionMode = TEXT_MODE; | 
						|
    this.originalInsertionMode = IN_BODY_MODE; | 
						|
    this.tokenizer.state = Tokenizer.MODE.PLAINTEXT; | 
						|
}; | 
						|
 | 
						|
//Fragment parsing | 
						|
Parser.prototype._getAdjustedCurrentElement = function () { | 
						|
    return this.openElements.stackTop === 0 && this.fragmentContext ? | 
						|
        this.fragmentContext : | 
						|
        this.openElements.current; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._findFormInFragmentContext = function () { | 
						|
    var node = this.fragmentContext; | 
						|
 | 
						|
    do { | 
						|
        if (this.treeAdapter.getTagName(node) === $.FORM) { | 
						|
            this.formElement = node; | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        node = this.treeAdapter.getParentNode(node); | 
						|
    } while (node); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._initTokenizerForFragmentParsing = function () { | 
						|
    if (this.treeAdapter.getNamespaceURI(this.fragmentContext) === NS.HTML) { | 
						|
        var tn = this.treeAdapter.getTagName(this.fragmentContext); | 
						|
 | 
						|
        if (tn === $.TITLE || tn === $.TEXTAREA) | 
						|
            this.tokenizer.state = Tokenizer.MODE.RCDATA; | 
						|
 | 
						|
        else if (tn === $.STYLE || tn === $.XMP || tn === $.IFRAME || | 
						|
                 tn === $.NOEMBED || tn === $.NOFRAMES || tn === $.NOSCRIPT) | 
						|
            this.tokenizer.state = Tokenizer.MODE.RAWTEXT; | 
						|
 | 
						|
        else if (tn === $.SCRIPT) | 
						|
            this.tokenizer.state = Tokenizer.MODE.SCRIPT_DATA; | 
						|
 | 
						|
        else if (tn === $.PLAINTEXT) | 
						|
            this.tokenizer.state = Tokenizer.MODE.PLAINTEXT; | 
						|
    } | 
						|
}; | 
						|
 | 
						|
//Tree mutation | 
						|
Parser.prototype._setDocumentType = function (token) { | 
						|
    this.treeAdapter.setDocumentType(this.document, token.name, token.publicId, token.systemId); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._attachElementToTree = function (element) { | 
						|
    if (this._shouldFosterParentOnInsertion()) | 
						|
        this._fosterParentElement(element); | 
						|
 | 
						|
    else { | 
						|
        var parent = this.openElements.currentTmplContent || this.openElements.current; | 
						|
 | 
						|
        this.treeAdapter.appendChild(parent, element); | 
						|
    } | 
						|
}; | 
						|
 | 
						|
Parser.prototype._appendElement = function (token, namespaceURI) { | 
						|
    var element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs); | 
						|
 | 
						|
    this._attachElementToTree(element); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._insertElement = function (token, namespaceURI) { | 
						|
    var element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs); | 
						|
 | 
						|
    this._attachElementToTree(element); | 
						|
    this.openElements.push(element); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._insertFakeElement = function (tagName) { | 
						|
    var element = this.treeAdapter.createElement(tagName, NS.HTML, []); | 
						|
 | 
						|
    this._attachElementToTree(element); | 
						|
    this.openElements.push(element); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._insertTemplate = function (token) { | 
						|
    var tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs), | 
						|
        content = this.treeAdapter.createDocumentFragment(); | 
						|
 | 
						|
    this.treeAdapter.setTemplateContent(tmpl, content); | 
						|
    this._attachElementToTree(tmpl); | 
						|
    this.openElements.push(tmpl); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._insertFakeRootElement = function () { | 
						|
    var element = this.treeAdapter.createElement($.HTML, NS.HTML, []); | 
						|
 | 
						|
    this.treeAdapter.appendChild(this.openElements.current, element); | 
						|
    this.openElements.push(element); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._appendCommentNode = function (token, parent) { | 
						|
    var commentNode = this.treeAdapter.createCommentNode(token.data); | 
						|
 | 
						|
    this.treeAdapter.appendChild(parent, commentNode); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._insertCharacters = function (token) { | 
						|
    if (this._shouldFosterParentOnInsertion()) | 
						|
        this._fosterParentText(token.chars); | 
						|
 | 
						|
    else { | 
						|
        var parent = this.openElements.currentTmplContent || this.openElements.current; | 
						|
 | 
						|
        this.treeAdapter.insertText(parent, token.chars); | 
						|
    } | 
						|
}; | 
						|
 | 
						|
Parser.prototype._adoptNodes = function (donor, recipient) { | 
						|
    while (true) { | 
						|
        var child = this.treeAdapter.getFirstChild(donor); | 
						|
 | 
						|
        if (!child) | 
						|
            break; | 
						|
 | 
						|
        this.treeAdapter.detachNode(child); | 
						|
        this.treeAdapter.appendChild(recipient, child); | 
						|
    } | 
						|
}; | 
						|
 | 
						|
//Token processing | 
						|
Parser.prototype._shouldProcessTokenInForeignContent = function (token) { | 
						|
    var current = this._getAdjustedCurrentElement(); | 
						|
 | 
						|
    if (!current || current === this.document) | 
						|
        return false; | 
						|
 | 
						|
    var ns = this.treeAdapter.getNamespaceURI(current); | 
						|
 | 
						|
    if (ns === NS.HTML) | 
						|
        return false; | 
						|
 | 
						|
    if (this.treeAdapter.getTagName(current) === $.ANNOTATION_XML && ns === NS.MATHML && | 
						|
        token.type === Tokenizer.START_TAG_TOKEN && token.tagName === $.SVG) | 
						|
        return false; | 
						|
 | 
						|
    var isCharacterToken = token.type === Tokenizer.CHARACTER_TOKEN || | 
						|
                           token.type === Tokenizer.NULL_CHARACTER_TOKEN || | 
						|
                           token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN, | 
						|
        isMathMLTextStartTag = token.type === Tokenizer.START_TAG_TOKEN && | 
						|
                               token.tagName !== $.MGLYPH && | 
						|
                               token.tagName !== $.MALIGNMARK; | 
						|
 | 
						|
    if ((isMathMLTextStartTag || isCharacterToken) && this._isIntegrationPoint(current, NS.MATHML)) | 
						|
        return false; | 
						|
 | 
						|
    if ((token.type === Tokenizer.START_TAG_TOKEN || isCharacterToken) && this._isIntegrationPoint(current, NS.HTML)) | 
						|
        return false; | 
						|
 | 
						|
    return token.type !== Tokenizer.EOF_TOKEN; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._processToken = function (token) { | 
						|
    _[this.insertionMode][token.type](this, token); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._processTokenInBodyMode = function (token) { | 
						|
    _[IN_BODY_MODE][token.type](this, token); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._processTokenInForeignContent = function (token) { | 
						|
    if (token.type === Tokenizer.CHARACTER_TOKEN) | 
						|
        characterInForeignContent(this, token); | 
						|
 | 
						|
    else if (token.type === Tokenizer.NULL_CHARACTER_TOKEN) | 
						|
        nullCharacterInForeignContent(this, token); | 
						|
 | 
						|
    else if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN) | 
						|
        insertCharacters(this, token); | 
						|
 | 
						|
    else if (token.type === Tokenizer.COMMENT_TOKEN) | 
						|
        appendComment(this, token); | 
						|
 | 
						|
    else if (token.type === Tokenizer.START_TAG_TOKEN) | 
						|
        startTagInForeignContent(this, token); | 
						|
 | 
						|
    else if (token.type === Tokenizer.END_TAG_TOKEN) | 
						|
        endTagInForeignContent(this, token); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._processInputToken = function (token) { | 
						|
    if (this._shouldProcessTokenInForeignContent(token)) | 
						|
        this._processTokenInForeignContent(token); | 
						|
 | 
						|
    else | 
						|
        this._processToken(token); | 
						|
}; | 
						|
 | 
						|
//Integration points | 
						|
Parser.prototype._isIntegrationPoint = function (element, foreignNS) { | 
						|
    var tn = this.treeAdapter.getTagName(element), | 
						|
        ns = this.treeAdapter.getNamespaceURI(element), | 
						|
        attrs = this.treeAdapter.getAttrList(element); | 
						|
 | 
						|
    return foreignContent.isIntegrationPoint(tn, ns, attrs, foreignNS); | 
						|
}; | 
						|
 | 
						|
//Active formatting elements reconstruction | 
						|
Parser.prototype._reconstructActiveFormattingElements = function () { | 
						|
    var listLength = this.activeFormattingElements.length; | 
						|
 | 
						|
    if (listLength) { | 
						|
        var unopenIdx = listLength, | 
						|
            entry = null; | 
						|
 | 
						|
        do { | 
						|
            unopenIdx--; | 
						|
            entry = this.activeFormattingElements.entries[unopenIdx]; | 
						|
 | 
						|
            if (entry.type === FormattingElementList.MARKER_ENTRY || this.openElements.contains(entry.element)) { | 
						|
                unopenIdx++; | 
						|
                break; | 
						|
            } | 
						|
        } while (unopenIdx > 0); | 
						|
 | 
						|
        for (var i = unopenIdx; i < listLength; i++) { | 
						|
            entry = this.activeFormattingElements.entries[i]; | 
						|
            this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element)); | 
						|
            entry.element = this.openElements.current; | 
						|
        } | 
						|
    } | 
						|
}; | 
						|
 | 
						|
//Close elements | 
						|
Parser.prototype._closeTableCell = function () { | 
						|
    this.openElements.generateImpliedEndTags(); | 
						|
    this.openElements.popUntilTableCellPopped(); | 
						|
    this.activeFormattingElements.clearToLastMarker(); | 
						|
    this.insertionMode = IN_ROW_MODE; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._closePElement = function () { | 
						|
    this.openElements.generateImpliedEndTagsWithExclusion($.P); | 
						|
    this.openElements.popUntilTagNamePopped($.P); | 
						|
}; | 
						|
 | 
						|
//Insertion modes | 
						|
Parser.prototype._resetInsertionMode = function () { | 
						|
    for (var i = this.openElements.stackTop, last = false; i >= 0; i--) { | 
						|
        var element = this.openElements.items[i]; | 
						|
 | 
						|
        if (i === 0) { | 
						|
            last = true; | 
						|
 | 
						|
            if (this.fragmentContext) | 
						|
                element = this.fragmentContext; | 
						|
        } | 
						|
 | 
						|
        var tn = this.treeAdapter.getTagName(element), | 
						|
            newInsertionMode = INSERTION_MODE_RESET_MAP[tn]; | 
						|
 | 
						|
        if (newInsertionMode) { | 
						|
            this.insertionMode = newInsertionMode; | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (!last && (tn === $.TD || tn === $.TH)) { | 
						|
            this.insertionMode = IN_CELL_MODE; | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (!last && tn === $.HEAD) { | 
						|
            this.insertionMode = IN_HEAD_MODE; | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (tn === $.SELECT) { | 
						|
            this._resetInsertionModeForSelect(i); | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (tn === $.TEMPLATE) { | 
						|
            this.insertionMode = this.currentTmplInsertionMode; | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (tn === $.HTML) { | 
						|
            this.insertionMode = this.headElement ? AFTER_HEAD_MODE : BEFORE_HEAD_MODE; | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (last) { | 
						|
            this.insertionMode = IN_BODY_MODE; | 
						|
            break; | 
						|
        } | 
						|
    } | 
						|
}; | 
						|
 | 
						|
Parser.prototype._resetInsertionModeForSelect = function (selectIdx) { | 
						|
    if (selectIdx > 0) { | 
						|
        for (var i = selectIdx - 1; i > 0; i--) { | 
						|
            var ancestor = this.openElements.items[i], | 
						|
                tn = this.treeAdapter.getTagName(ancestor); | 
						|
 | 
						|
            if (tn === $.TEMPLATE) | 
						|
                break; | 
						|
 | 
						|
            else if (tn === $.TABLE) { | 
						|
                this.insertionMode = IN_SELECT_IN_TABLE_MODE; | 
						|
                return; | 
						|
            } | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    this.insertionMode = IN_SELECT_MODE; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._pushTmplInsertionMode = function (mode) { | 
						|
    this.tmplInsertionModeStack.push(mode); | 
						|
    this.tmplInsertionModeStackTop++; | 
						|
    this.currentTmplInsertionMode = mode; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._popTmplInsertionMode = function () { | 
						|
    this.tmplInsertionModeStack.pop(); | 
						|
    this.tmplInsertionModeStackTop--; | 
						|
    this.currentTmplInsertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStackTop]; | 
						|
}; | 
						|
 | 
						|
//Foster parenting | 
						|
Parser.prototype._isElementCausesFosterParenting = function (element) { | 
						|
    var tn = this.treeAdapter.getTagName(element); | 
						|
 | 
						|
    return tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._shouldFosterParentOnInsertion = function () { | 
						|
    return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.current); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._findFosterParentingLocation = function () { | 
						|
    var location = { | 
						|
        parent: null, | 
						|
        beforeElement: null | 
						|
    }; | 
						|
 | 
						|
    for (var i = this.openElements.stackTop; i >= 0; i--) { | 
						|
        var openElement = this.openElements.items[i], | 
						|
            tn = this.treeAdapter.getTagName(openElement), | 
						|
            ns = this.treeAdapter.getNamespaceURI(openElement); | 
						|
 | 
						|
        if (tn === $.TEMPLATE && ns === NS.HTML) { | 
						|
            location.parent = this.treeAdapter.getTemplateContent(openElement); | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        else if (tn === $.TABLE) { | 
						|
            location.parent = this.treeAdapter.getParentNode(openElement); | 
						|
 | 
						|
            if (location.parent) | 
						|
                location.beforeElement = openElement; | 
						|
            else | 
						|
                location.parent = this.openElements.items[i - 1]; | 
						|
 | 
						|
            break; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    if (!location.parent) | 
						|
        location.parent = this.openElements.items[0]; | 
						|
 | 
						|
    return location; | 
						|
}; | 
						|
 | 
						|
Parser.prototype._fosterParentElement = function (element) { | 
						|
    var location = this._findFosterParentingLocation(); | 
						|
 | 
						|
    if (location.beforeElement) | 
						|
        this.treeAdapter.insertBefore(location.parent, element, location.beforeElement); | 
						|
    else | 
						|
        this.treeAdapter.appendChild(location.parent, element); | 
						|
}; | 
						|
 | 
						|
Parser.prototype._fosterParentText = function (chars) { | 
						|
    var location = this._findFosterParentingLocation(); | 
						|
 | 
						|
    if (location.beforeElement) | 
						|
        this.treeAdapter.insertTextBefore(location.parent, chars, location.beforeElement); | 
						|
    else | 
						|
        this.treeAdapter.insertText(location.parent, chars); | 
						|
}; | 
						|
 | 
						|
//Special elements | 
						|
Parser.prototype._isSpecialElement = function (element) { | 
						|
    var tn = this.treeAdapter.getTagName(element), | 
						|
        ns = this.treeAdapter.getNamespaceURI(element); | 
						|
 | 
						|
    return HTML.SPECIAL_ELEMENTS[ns][tn]; | 
						|
}; | 
						|
 | 
						|
//Adoption agency algorithm | 
						|
//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency) | 
						|
//------------------------------------------------------------------ | 
						|
 | 
						|
//Steps 5-8 of the algorithm | 
						|
function aaObtainFormattingElementEntry(p, token) { | 
						|
    var formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName); | 
						|
 | 
						|
    if (formattingElementEntry) { | 
						|
        if (!p.openElements.contains(formattingElementEntry.element)) { | 
						|
            p.activeFormattingElements.removeEntry(formattingElementEntry); | 
						|
            formattingElementEntry = null; | 
						|
        } | 
						|
 | 
						|
        else if (!p.openElements.hasInScope(token.tagName)) | 
						|
            formattingElementEntry = null; | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        genericEndTagInBody(p, token); | 
						|
 | 
						|
    return formattingElementEntry; | 
						|
} | 
						|
 | 
						|
//Steps 9 and 10 of the algorithm | 
						|
function aaObtainFurthestBlock(p, formattingElementEntry) { | 
						|
    var furthestBlock = null; | 
						|
 | 
						|
    for (var i = p.openElements.stackTop; i >= 0; i--) { | 
						|
        var element = p.openElements.items[i]; | 
						|
 | 
						|
        if (element === formattingElementEntry.element) | 
						|
            break; | 
						|
 | 
						|
        if (p._isSpecialElement(element)) | 
						|
            furthestBlock = element; | 
						|
    } | 
						|
 | 
						|
    if (!furthestBlock) { | 
						|
        p.openElements.popUntilElementPopped(formattingElementEntry.element); | 
						|
        p.activeFormattingElements.removeEntry(formattingElementEntry); | 
						|
    } | 
						|
 | 
						|
    return furthestBlock; | 
						|
} | 
						|
 | 
						|
//Step 13 of the algorithm | 
						|
function aaInnerLoop(p, furthestBlock, formattingElement) { | 
						|
    var lastElement = furthestBlock, | 
						|
        nextElement = p.openElements.getCommonAncestor(furthestBlock); | 
						|
 | 
						|
    for (var i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) { | 
						|
        //NOTE: store next element for the next loop iteration (it may be deleted from the stack by step 9.5) | 
						|
        nextElement = p.openElements.getCommonAncestor(element); | 
						|
 | 
						|
        var elementEntry = p.activeFormattingElements.getElementEntry(element), | 
						|
            counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER, | 
						|
            shouldRemoveFromOpenElements = !elementEntry || counterOverflow; | 
						|
 | 
						|
        if (shouldRemoveFromOpenElements) { | 
						|
            if (counterOverflow) | 
						|
                p.activeFormattingElements.removeEntry(elementEntry); | 
						|
 | 
						|
            p.openElements.remove(element); | 
						|
        } | 
						|
 | 
						|
        else { | 
						|
            element = aaRecreateElementFromEntry(p, elementEntry); | 
						|
 | 
						|
            if (lastElement === furthestBlock) | 
						|
                p.activeFormattingElements.bookmark = elementEntry; | 
						|
 | 
						|
            p.treeAdapter.detachNode(lastElement); | 
						|
            p.treeAdapter.appendChild(element, lastElement); | 
						|
            lastElement = element; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    return lastElement; | 
						|
} | 
						|
 | 
						|
//Step 13.7 of the algorithm | 
						|
function aaRecreateElementFromEntry(p, elementEntry) { | 
						|
    var ns = p.treeAdapter.getNamespaceURI(elementEntry.element), | 
						|
        newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs); | 
						|
 | 
						|
    p.openElements.replace(elementEntry.element, newElement); | 
						|
    elementEntry.element = newElement; | 
						|
 | 
						|
    return newElement; | 
						|
} | 
						|
 | 
						|
//Step 14 of the algorithm | 
						|
function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) { | 
						|
    if (p._isElementCausesFosterParenting(commonAncestor)) | 
						|
        p._fosterParentElement(lastElement); | 
						|
 | 
						|
    else { | 
						|
        var tn = p.treeAdapter.getTagName(commonAncestor), | 
						|
            ns = p.treeAdapter.getNamespaceURI(commonAncestor); | 
						|
 | 
						|
        if (tn === $.TEMPLATE && ns === NS.HTML) | 
						|
            commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor); | 
						|
 | 
						|
        p.treeAdapter.appendChild(commonAncestor, lastElement); | 
						|
    } | 
						|
} | 
						|
 | 
						|
//Steps 15-19 of the algorithm | 
						|
function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) { | 
						|
    var ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element), | 
						|
        token = formattingElementEntry.token, | 
						|
        newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs); | 
						|
 | 
						|
    p._adoptNodes(furthestBlock, newElement); | 
						|
    p.treeAdapter.appendChild(furthestBlock, newElement); | 
						|
 | 
						|
    p.activeFormattingElements.insertElementAfterBookmark(newElement, formattingElementEntry.token); | 
						|
    p.activeFormattingElements.removeEntry(formattingElementEntry); | 
						|
 | 
						|
    p.openElements.remove(formattingElementEntry.element); | 
						|
    p.openElements.insertAfter(furthestBlock, newElement); | 
						|
} | 
						|
 | 
						|
//Algorithm entry point | 
						|
function callAdoptionAgency(p, token) { | 
						|
    var formattingElementEntry; | 
						|
 | 
						|
    for (var i = 0; i < AA_OUTER_LOOP_ITER; i++) { | 
						|
        formattingElementEntry = aaObtainFormattingElementEntry(p, token, formattingElementEntry); | 
						|
 | 
						|
        if (!formattingElementEntry) | 
						|
            break; | 
						|
 | 
						|
        var furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry); | 
						|
 | 
						|
        if (!furthestBlock) | 
						|
            break; | 
						|
 | 
						|
        p.activeFormattingElements.bookmark = formattingElementEntry; | 
						|
 | 
						|
        var lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element), | 
						|
            commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element); | 
						|
 | 
						|
        p.treeAdapter.detachNode(lastElement); | 
						|
        aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement); | 
						|
        aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry); | 
						|
    } | 
						|
} | 
						|
 | 
						|
 | 
						|
//Generic token handlers | 
						|
//------------------------------------------------------------------ | 
						|
function ignoreToken() { | 
						|
    //NOTE: do nothing =) | 
						|
} | 
						|
 | 
						|
function appendComment(p, token) { | 
						|
    p._appendCommentNode(token, p.openElements.currentTmplContent || p.openElements.current); | 
						|
} | 
						|
 | 
						|
function appendCommentToRootHtmlElement(p, token) { | 
						|
    p._appendCommentNode(token, p.openElements.items[0]); | 
						|
} | 
						|
 | 
						|
function appendCommentToDocument(p, token) { | 
						|
    p._appendCommentNode(token, p.document); | 
						|
} | 
						|
 | 
						|
function insertCharacters(p, token) { | 
						|
    p._insertCharacters(token); | 
						|
} | 
						|
 | 
						|
function stopParsing(p) { | 
						|
    p.stopped = true; | 
						|
} | 
						|
 | 
						|
//12.2.5.4.1 The "initial" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function doctypeInInitialMode(p, token) { | 
						|
    p._setDocumentType(token); | 
						|
 | 
						|
    var mode = token.forceQuirks ? | 
						|
        HTML.DOCUMENT_MODE.QUIRKS : | 
						|
        doctype.getDocumentMode(token.name, token.publicId, token.systemId); | 
						|
 | 
						|
    p.treeAdapter.setDocumentMode(p.document, mode); | 
						|
 | 
						|
    p.insertionMode = BEFORE_HTML_MODE; | 
						|
} | 
						|
 | 
						|
function tokenInInitialMode(p, token) { | 
						|
    p.treeAdapter.setDocumentMode(p.document, HTML.DOCUMENT_MODE.QUIRKS); | 
						|
    p.insertionMode = BEFORE_HTML_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.2 The "before html" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagBeforeHtml(p, token) { | 
						|
    if (token.tagName === $.HTML) { | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.insertionMode = BEFORE_HEAD_MODE; | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        tokenBeforeHtml(p, token); | 
						|
} | 
						|
 | 
						|
function endTagBeforeHtml(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML || tn === $.HEAD || tn === $.BODY || tn === $.BR) | 
						|
        tokenBeforeHtml(p, token); | 
						|
} | 
						|
 | 
						|
function tokenBeforeHtml(p, token) { | 
						|
    p._insertFakeRootElement(); | 
						|
    p.insertionMode = BEFORE_HEAD_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.3 The "before head" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagBeforeHead(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.HEAD) { | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.headElement = p.openElements.current; | 
						|
        p.insertionMode = IN_HEAD_MODE; | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        tokenBeforeHead(p, token); | 
						|
} | 
						|
 | 
						|
function endTagBeforeHead(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) | 
						|
        tokenBeforeHead(p, token); | 
						|
} | 
						|
 | 
						|
function tokenBeforeHead(p, token) { | 
						|
    p._insertFakeElement($.HEAD); | 
						|
    p.headElement = p.openElements.current; | 
						|
    p.insertionMode = IN_HEAD_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.4 The "in head" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInHead(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META) | 
						|
        p._appendElement(token, NS.HTML); | 
						|
 | 
						|
    else if (tn === $.TITLE) | 
						|
        p._switchToTextParsing(token, Tokenizer.MODE.RCDATA); | 
						|
 | 
						|
    //NOTE: here we assume that we always act as an interactive user agent with enabled scripting, so we parse | 
						|
    //<noscript> as a rawtext. | 
						|
    else if (tn === $.NOSCRIPT || tn === $.NOFRAMES || tn === $.STYLE) | 
						|
        p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); | 
						|
 | 
						|
    else if (tn === $.SCRIPT) | 
						|
        p._switchToTextParsing(token, Tokenizer.MODE.SCRIPT_DATA); | 
						|
 | 
						|
    else if (tn === $.TEMPLATE) { | 
						|
        p._insertTemplate(token, NS.HTML); | 
						|
        p.activeFormattingElements.insertMarker(); | 
						|
        p.framesetOk = false; | 
						|
        p.insertionMode = IN_TEMPLATE_MODE; | 
						|
        p._pushTmplInsertionMode(IN_TEMPLATE_MODE); | 
						|
    } | 
						|
 | 
						|
    else if (tn !== $.HEAD) | 
						|
        tokenInHead(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInHead(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HEAD) { | 
						|
        p.openElements.pop(); | 
						|
        p.insertionMode = AFTER_HEAD_MODE; | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.BODY || tn === $.BR || tn === $.HTML) | 
						|
        tokenInHead(p, token); | 
						|
 | 
						|
    else if (tn === $.TEMPLATE && p.openElements.tmplCount > 0) { | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
        p.openElements.popUntilTagNamePopped($.TEMPLATE); | 
						|
        p.activeFormattingElements.clearToLastMarker(); | 
						|
        p._popTmplInsertionMode(); | 
						|
        p._resetInsertionMode(); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function tokenInHead(p, token) { | 
						|
    p.openElements.pop(); | 
						|
    p.insertionMode = AFTER_HEAD_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.6 The "after head" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagAfterHead(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.BODY) { | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.framesetOk = false; | 
						|
        p.insertionMode = IN_BODY_MODE; | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.FRAMESET) { | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.insertionMode = IN_FRAMESET_MODE; | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META || | 
						|
             tn === $.NOFRAMES || tn === $.SCRIPT || tn === $.STYLE || tn === $.TEMPLATE || tn === $.TITLE) { | 
						|
        p.openElements.push(p.headElement); | 
						|
        startTagInHead(p, token); | 
						|
        p.openElements.remove(p.headElement); | 
						|
    } | 
						|
 | 
						|
    else if (tn !== $.HEAD) | 
						|
        tokenAfterHead(p, token); | 
						|
} | 
						|
 | 
						|
function endTagAfterHead(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.BODY || tn === $.HTML || tn === $.BR) | 
						|
        tokenAfterHead(p, token); | 
						|
 | 
						|
    else if (tn === $.TEMPLATE) | 
						|
        endTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
function tokenAfterHead(p, token) { | 
						|
    p._insertFakeElement($.BODY); | 
						|
    p.insertionMode = IN_BODY_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.7 The "in body" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function whitespaceCharacterInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertCharacters(token); | 
						|
} | 
						|
 | 
						|
function characterInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertCharacters(token); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function htmlStartTagInBody(p, token) { | 
						|
    if (p.openElements.tmplCount === 0) | 
						|
        p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs); | 
						|
} | 
						|
 | 
						|
function bodyStartTagInBody(p, token) { | 
						|
    var bodyElement = p.openElements.tryPeekProperlyNestedBodyElement(); | 
						|
 | 
						|
    if (bodyElement && p.openElements.tmplCount === 0) { | 
						|
        p.framesetOk = false; | 
						|
        p.treeAdapter.adoptAttributes(bodyElement, token.attrs); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function framesetStartTagInBody(p, token) { | 
						|
    var bodyElement = p.openElements.tryPeekProperlyNestedBodyElement(); | 
						|
 | 
						|
    if (p.framesetOk && bodyElement) { | 
						|
        p.treeAdapter.detachNode(bodyElement); | 
						|
        p.openElements.popAllUpToHtmlElement(); | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.insertionMode = IN_FRAMESET_MODE; | 
						|
    } | 
						|
} | 
						|
 | 
						|
function addressStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function numberedHeaderStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    var tn = p.openElements.currentTagName; | 
						|
 | 
						|
    if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) | 
						|
        p.openElements.pop(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function preStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move | 
						|
    //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.) | 
						|
    p.skipNextNewLine = true; | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function formStartTagInBody(p, token) { | 
						|
    var inTemplate = p.openElements.tmplCount > 0; | 
						|
 | 
						|
    if (!p.formElement || inTemplate) { | 
						|
        if (p.openElements.hasInButtonScope($.P)) | 
						|
            p._closePElement(); | 
						|
 | 
						|
        p._insertElement(token, NS.HTML); | 
						|
 | 
						|
        if (!inTemplate) | 
						|
            p.formElement = p.openElements.current; | 
						|
    } | 
						|
} | 
						|
 | 
						|
function listItemStartTagInBody(p, token) { | 
						|
    p.framesetOk = false; | 
						|
 | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    for (var i = p.openElements.stackTop; i >= 0; i--) { | 
						|
        var element = p.openElements.items[i], | 
						|
            elementTn = p.treeAdapter.getTagName(element), | 
						|
            closeTn = null; | 
						|
 | 
						|
        if (tn === $.LI && elementTn === $.LI) | 
						|
            closeTn = $.LI; | 
						|
 | 
						|
        else if ((tn === $.DD || tn === $.DT) && (elementTn === $.DD || elementTn === $.DT)) | 
						|
            closeTn = elementTn; | 
						|
 | 
						|
        if (closeTn) { | 
						|
            p.openElements.generateImpliedEndTagsWithExclusion(closeTn); | 
						|
            p.openElements.popUntilTagNamePopped(closeTn); | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        if (elementTn !== $.ADDRESS && elementTn !== $.DIV && elementTn !== $.P && p._isSpecialElement(element)) | 
						|
            break; | 
						|
    } | 
						|
 | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function plaintextStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.tokenizer.state = Tokenizer.MODE.PLAINTEXT; | 
						|
} | 
						|
 | 
						|
function buttonStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInScope($.BUTTON)) { | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
        p.openElements.popUntilTagNamePopped($.BUTTON); | 
						|
    } | 
						|
 | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function aStartTagInBody(p, token) { | 
						|
    var activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName($.A); | 
						|
 | 
						|
    if (activeElementEntry) { | 
						|
        callAdoptionAgency(p, token); | 
						|
        p.openElements.remove(activeElementEntry.element); | 
						|
        p.activeFormattingElements.removeEntry(activeElementEntry); | 
						|
    } | 
						|
 | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.activeFormattingElements.pushElement(p.openElements.current, token); | 
						|
} | 
						|
 | 
						|
function bStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.activeFormattingElements.pushElement(p.openElements.current, token); | 
						|
} | 
						|
 | 
						|
function nobrStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
 | 
						|
    if (p.openElements.hasInScope($.NOBR)) { | 
						|
        callAdoptionAgency(p, token); | 
						|
        p._reconstructActiveFormattingElements(); | 
						|
    } | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.activeFormattingElements.pushElement(p.openElements.current, token); | 
						|
} | 
						|
 | 
						|
function appletStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.activeFormattingElements.insertMarker(); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function tableStartTagInBody(p, token) { | 
						|
    if (p.treeAdapter.getDocumentMode(p.document) !== HTML.DOCUMENT_MODE.QUIRKS && p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.framesetOk = false; | 
						|
    p.insertionMode = IN_TABLE_MODE; | 
						|
} | 
						|
 | 
						|
function areaStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._appendElement(token, NS.HTML); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function inputStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._appendElement(token, NS.HTML); | 
						|
 | 
						|
    var inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE); | 
						|
 | 
						|
    if (!inputType || inputType.toLowerCase() !== HIDDEN_INPUT_TYPE) | 
						|
        p.framesetOk = false; | 
						|
 | 
						|
} | 
						|
 | 
						|
function paramStartTagInBody(p, token) { | 
						|
    p._appendElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function hrStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    if (p.openElements.currentTagName === $.MENUITEM) | 
						|
        p.openElements.pop(); | 
						|
 | 
						|
    p._appendElement(token, NS.HTML); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function imageStartTagInBody(p, token) { | 
						|
    token.tagName = $.IMG; | 
						|
    areaStartTagInBody(p, token); | 
						|
} | 
						|
 | 
						|
function textareaStartTagInBody(p, token) { | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move | 
						|
    //on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.) | 
						|
    p.skipNextNewLine = true; | 
						|
    p.tokenizer.state = Tokenizer.MODE.RCDATA; | 
						|
    p.originalInsertionMode = p.insertionMode; | 
						|
    p.framesetOk = false; | 
						|
    p.insertionMode = TEXT_MODE; | 
						|
} | 
						|
 | 
						|
function xmpStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p.framesetOk = false; | 
						|
    p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); | 
						|
} | 
						|
 | 
						|
function iframeStartTagInBody(p, token) { | 
						|
    p.framesetOk = false; | 
						|
    p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); | 
						|
} | 
						|
 | 
						|
//NOTE: here we assume that we always act as an user agent with enabled plugins, so we parse | 
						|
//<noembed> as a rawtext. | 
						|
function noembedStartTagInBody(p, token) { | 
						|
    p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); | 
						|
} | 
						|
 | 
						|
function selectStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.framesetOk = false; | 
						|
 | 
						|
    if (p.insertionMode === IN_TABLE_MODE || | 
						|
        p.insertionMode === IN_CAPTION_MODE || | 
						|
        p.insertionMode === IN_TABLE_BODY_MODE || | 
						|
        p.insertionMode === IN_ROW_MODE || | 
						|
        p.insertionMode === IN_CELL_MODE) | 
						|
 | 
						|
        p.insertionMode = IN_SELECT_IN_TABLE_MODE; | 
						|
 | 
						|
    else | 
						|
        p.insertionMode = IN_SELECT_MODE; | 
						|
} | 
						|
 | 
						|
function optgroupStartTagInBody(p, token) { | 
						|
    if (p.openElements.currentTagName === $.OPTION) | 
						|
        p.openElements.pop(); | 
						|
 | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function rbStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInScope($.RUBY)) | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function rtStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInScope($.RUBY)) | 
						|
        p.openElements.generateImpliedEndTagsWithExclusion($.RTC); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function menuitemStartTagInBody(p, token) { | 
						|
    if (p.openElements.currentTagName === $.MENUITEM) | 
						|
        p.openElements.pop(); | 
						|
 | 
						|
    // TODO needs clarification, see https://github.com/whatwg/html/pull/907/files#r73505877 | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function menuStartTagInBody(p, token) { | 
						|
    if (p.openElements.hasInButtonScope($.P)) | 
						|
        p._closePElement(); | 
						|
 | 
						|
    if (p.openElements.currentTagName === $.MENUITEM) | 
						|
        p.openElements.pop(); | 
						|
 | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
function mathStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
 | 
						|
    foreignContent.adjustTokenMathMLAttrs(token); | 
						|
    foreignContent.adjustTokenXMLAttrs(token); | 
						|
 | 
						|
    if (token.selfClosing) | 
						|
        p._appendElement(token, NS.MATHML); | 
						|
    else | 
						|
        p._insertElement(token, NS.MATHML); | 
						|
} | 
						|
 | 
						|
function svgStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
 | 
						|
    foreignContent.adjustTokenSVGAttrs(token); | 
						|
    foreignContent.adjustTokenXMLAttrs(token); | 
						|
 | 
						|
    if (token.selfClosing) | 
						|
        p._appendElement(token, NS.SVG); | 
						|
    else | 
						|
        p._insertElement(token, NS.SVG); | 
						|
} | 
						|
 | 
						|
function genericStartTagInBody(p, token) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
} | 
						|
 | 
						|
//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here. | 
						|
//It's faster than using dictionary. | 
						|
function startTagInBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    switch (tn.length) { | 
						|
        case 1: | 
						|
            if (tn === $.I || tn === $.S || tn === $.B || tn === $.U) | 
						|
                bStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.P) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.A) | 
						|
                aStartTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 2: | 
						|
            if (tn === $.DL || tn === $.OL || tn === $.UL) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) | 
						|
                numberedHeaderStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.LI || tn === $.DD || tn === $.DT) | 
						|
                listItemStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.EM || tn === $.TT) | 
						|
                bStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.BR) | 
						|
                areaStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.HR) | 
						|
                hrStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.RB) | 
						|
                rbStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.RT || tn === $.RP) | 
						|
                rtStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn !== $.TH && tn !== $.TD && tn !== $.TR) | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 3: | 
						|
            if (tn === $.DIV || tn === $.DIR || tn === $.NAV) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.PRE) | 
						|
                preStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.BIG) | 
						|
                bStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.IMG || tn === $.WBR) | 
						|
                areaStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.XMP) | 
						|
                xmpStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.SVG) | 
						|
                svgStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.RTC) | 
						|
                rbStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn !== $.COL) | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 4: | 
						|
            if (tn === $.HTML) | 
						|
                htmlStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.BASE || tn === $.LINK || tn === $.META) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.BODY) | 
						|
                bodyStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.MAIN) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.FORM) | 
						|
                formStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.CODE || tn === $.FONT) | 
						|
                bStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.NOBR) | 
						|
                nobrStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.AREA) | 
						|
                areaStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.MATH) | 
						|
                mathStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.MENU) | 
						|
                menuStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn !== $.HEAD) | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 5: | 
						|
            if (tn === $.STYLE || tn === $.TITLE) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.ASIDE) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.SMALL) | 
						|
                bStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.TABLE) | 
						|
                tableStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.EMBED) | 
						|
                areaStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.INPUT) | 
						|
                inputStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.PARAM || tn === $.TRACK) | 
						|
                paramStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.IMAGE) | 
						|
                imageStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn !== $.FRAME && tn !== $.TBODY && tn !== $.TFOOT && tn !== $.THEAD) | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 6: | 
						|
            if (tn === $.SCRIPT) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.CENTER || tn === $.FIGURE || tn === $.FOOTER || tn === $.HEADER || tn === $.HGROUP) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.BUTTON) | 
						|
                buttonStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.STRIKE || tn === $.STRONG) | 
						|
                bStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.APPLET || tn === $.OBJECT) | 
						|
                appletStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.KEYGEN) | 
						|
                areaStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.SOURCE) | 
						|
                paramStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.IFRAME) | 
						|
                iframeStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.SELECT) | 
						|
                selectStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.OPTION) | 
						|
                optgroupStartTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 7: | 
						|
            if (tn === $.BGSOUND) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.DETAILS || tn === $.ADDRESS || tn === $.ARTICLE || tn === $.SECTION || tn === $.SUMMARY) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.LISTING) | 
						|
                preStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.MARQUEE) | 
						|
                appletStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.NOEMBED) | 
						|
                noembedStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn !== $.CAPTION) | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 8: | 
						|
            if (tn === $.BASEFONT) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.MENUITEM) | 
						|
                menuitemStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.FRAMESET) | 
						|
                framesetStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.FIELDSET) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.TEXTAREA) | 
						|
                textareaStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.TEMPLATE) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.NOSCRIPT) | 
						|
                noembedStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.OPTGROUP) | 
						|
                optgroupStartTagInBody(p, token); | 
						|
 | 
						|
            else if (tn !== $.COLGROUP) | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 9: | 
						|
            if (tn === $.PLAINTEXT) | 
						|
                plaintextStartTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 10: | 
						|
            if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) | 
						|
                addressStartTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericStartTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        default: | 
						|
            genericStartTagInBody(p, token); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function bodyEndTagInBody(p) { | 
						|
    if (p.openElements.hasInScope($.BODY)) | 
						|
        p.insertionMode = AFTER_BODY_MODE; | 
						|
} | 
						|
 | 
						|
function htmlEndTagInBody(p, token) { | 
						|
    if (p.openElements.hasInScope($.BODY)) { | 
						|
        p.insertionMode = AFTER_BODY_MODE; | 
						|
        p._processToken(token); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function addressEndTagInBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (p.openElements.hasInScope(tn)) { | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
        p.openElements.popUntilTagNamePopped(tn); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function formEndTagInBody(p) { | 
						|
    var inTemplate = p.openElements.tmplCount > 0, | 
						|
        formElement = p.formElement; | 
						|
 | 
						|
    if (!inTemplate) | 
						|
        p.formElement = null; | 
						|
 | 
						|
    if ((formElement || inTemplate) && p.openElements.hasInScope($.FORM)) { | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
 | 
						|
        if (inTemplate) | 
						|
            p.openElements.popUntilTagNamePopped($.FORM); | 
						|
 | 
						|
        else | 
						|
            p.openElements.remove(formElement); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function pEndTagInBody(p) { | 
						|
    if (!p.openElements.hasInButtonScope($.P)) | 
						|
        p._insertFakeElement($.P); | 
						|
 | 
						|
    p._closePElement(); | 
						|
} | 
						|
 | 
						|
function liEndTagInBody(p) { | 
						|
    if (p.openElements.hasInListItemScope($.LI)) { | 
						|
        p.openElements.generateImpliedEndTagsWithExclusion($.LI); | 
						|
        p.openElements.popUntilTagNamePopped($.LI); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function ddEndTagInBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (p.openElements.hasInScope(tn)) { | 
						|
        p.openElements.generateImpliedEndTagsWithExclusion(tn); | 
						|
        p.openElements.popUntilTagNamePopped(tn); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function numberedHeaderEndTagInBody(p) { | 
						|
    if (p.openElements.hasNumberedHeaderInScope()) { | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
        p.openElements.popUntilNumberedHeaderPopped(); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function appletEndTagInBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (p.openElements.hasInScope(tn)) { | 
						|
        p.openElements.generateImpliedEndTags(); | 
						|
        p.openElements.popUntilTagNamePopped(tn); | 
						|
        p.activeFormattingElements.clearToLastMarker(); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function brEndTagInBody(p) { | 
						|
    p._reconstructActiveFormattingElements(); | 
						|
    p._insertFakeElement($.BR); | 
						|
    p.openElements.pop(); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function genericEndTagInBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    for (var i = p.openElements.stackTop; i > 0; i--) { | 
						|
        var element = p.openElements.items[i]; | 
						|
 | 
						|
        if (p.treeAdapter.getTagName(element) === tn) { | 
						|
            p.openElements.generateImpliedEndTagsWithExclusion(tn); | 
						|
            p.openElements.popUntilElementPopped(element); | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        if (p._isSpecialElement(element)) | 
						|
            break; | 
						|
    } | 
						|
} | 
						|
 | 
						|
//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here. | 
						|
//It's faster than using dictionary. | 
						|
function endTagInBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    switch (tn.length) { | 
						|
        case 1: | 
						|
            if (tn === $.A || tn === $.B || tn === $.I || tn === $.S || tn === $.U) | 
						|
                callAdoptionAgency(p, token); | 
						|
 | 
						|
            else if (tn === $.P) | 
						|
                pEndTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 2: | 
						|
            if (tn === $.DL || tn === $.UL || tn === $.OL) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.LI) | 
						|
                liEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.DD || tn === $.DT) | 
						|
                ddEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) | 
						|
                numberedHeaderEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.BR) | 
						|
                brEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.EM || tn === $.TT) | 
						|
                callAdoptionAgency(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 3: | 
						|
            if (tn === $.BIG) | 
						|
                callAdoptionAgency(p, token); | 
						|
 | 
						|
            else if (tn === $.DIR || tn === $.DIV || tn === $.NAV) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 4: | 
						|
            if (tn === $.BODY) | 
						|
                bodyEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.HTML) | 
						|
                htmlEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.FORM) | 
						|
                formEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.CODE || tn === $.FONT || tn === $.NOBR) | 
						|
                callAdoptionAgency(p, token); | 
						|
 | 
						|
            else if (tn === $.MAIN || tn === $.MENU) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 5: | 
						|
            if (tn === $.ASIDE) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.SMALL) | 
						|
                callAdoptionAgency(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 6: | 
						|
            if (tn === $.CENTER || tn === $.FIGURE || tn === $.FOOTER || tn === $.HEADER || tn === $.HGROUP) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.APPLET || tn === $.OBJECT) | 
						|
                appletEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.STRIKE || tn === $.STRONG) | 
						|
                callAdoptionAgency(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 7: | 
						|
            if (tn === $.ADDRESS || tn === $.ARTICLE || tn === $.DETAILS || tn === $.SECTION || tn === $.SUMMARY) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.MARQUEE) | 
						|
                appletEndTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 8: | 
						|
            if (tn === $.FIELDSET) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else if (tn === $.TEMPLATE) | 
						|
                endTagInHead(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 10: | 
						|
            if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) | 
						|
                addressEndTagInBody(p, token); | 
						|
 | 
						|
            else | 
						|
                genericEndTagInBody(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        default : | 
						|
            genericEndTagInBody(p, token); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function eofInBody(p, token) { | 
						|
    if (p.tmplInsertionModeStackTop > -1) | 
						|
        eofInTemplate(p, token); | 
						|
 | 
						|
    else | 
						|
        p.stopped = true; | 
						|
} | 
						|
 | 
						|
//12.2.5.4.8 The "text" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function endTagInText(p, token) { | 
						|
    if (token.tagName === $.SCRIPT) | 
						|
        p.pendingScript = p.openElements.current; | 
						|
 | 
						|
    p.openElements.pop(); | 
						|
    p.insertionMode = p.originalInsertionMode; | 
						|
} | 
						|
 | 
						|
 | 
						|
function eofInText(p, token) { | 
						|
    p.openElements.pop(); | 
						|
    p.insertionMode = p.originalInsertionMode; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.9 The "in table" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function characterInTable(p, token) { | 
						|
    var curTn = p.openElements.currentTagName; | 
						|
 | 
						|
    if (curTn === $.TABLE || curTn === $.TBODY || curTn === $.TFOOT || curTn === $.THEAD || curTn === $.TR) { | 
						|
        p.pendingCharacterTokens = []; | 
						|
        p.hasNonWhitespacePendingCharacterToken = false; | 
						|
        p.originalInsertionMode = p.insertionMode; | 
						|
        p.insertionMode = IN_TABLE_TEXT_MODE; | 
						|
        p._processToken(token); | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        tokenInTable(p, token); | 
						|
} | 
						|
 | 
						|
function captionStartTagInTable(p, token) { | 
						|
    p.openElements.clearBackToTableContext(); | 
						|
    p.activeFormattingElements.insertMarker(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.insertionMode = IN_CAPTION_MODE; | 
						|
} | 
						|
 | 
						|
function colgroupStartTagInTable(p, token) { | 
						|
    p.openElements.clearBackToTableContext(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.insertionMode = IN_COLUMN_GROUP_MODE; | 
						|
} | 
						|
 | 
						|
function colStartTagInTable(p, token) { | 
						|
    p.openElements.clearBackToTableContext(); | 
						|
    p._insertFakeElement($.COLGROUP); | 
						|
    p.insertionMode = IN_COLUMN_GROUP_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
function tbodyStartTagInTable(p, token) { | 
						|
    p.openElements.clearBackToTableContext(); | 
						|
    p._insertElement(token, NS.HTML); | 
						|
    p.insertionMode = IN_TABLE_BODY_MODE; | 
						|
} | 
						|
 | 
						|
function tdStartTagInTable(p, token) { | 
						|
    p.openElements.clearBackToTableContext(); | 
						|
    p._insertFakeElement($.TBODY); | 
						|
    p.insertionMode = IN_TABLE_BODY_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
function tableStartTagInTable(p, token) { | 
						|
    if (p.openElements.hasInTableScope($.TABLE)) { | 
						|
        p.openElements.popUntilTagNamePopped($.TABLE); | 
						|
        p._resetInsertionMode(); | 
						|
        p._processToken(token); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function inputStartTagInTable(p, token) { | 
						|
    var inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE); | 
						|
 | 
						|
    if (inputType && inputType.toLowerCase() === HIDDEN_INPUT_TYPE) | 
						|
        p._appendElement(token, NS.HTML); | 
						|
 | 
						|
    else | 
						|
        tokenInTable(p, token); | 
						|
} | 
						|
 | 
						|
function formStartTagInTable(p, token) { | 
						|
    if (!p.formElement && p.openElements.tmplCount === 0) { | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.formElement = p.openElements.current; | 
						|
        p.openElements.pop(); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function startTagInTable(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    switch (tn.length) { | 
						|
        case 2: | 
						|
            if (tn === $.TD || tn === $.TH || tn === $.TR) | 
						|
                tdStartTagInTable(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 3: | 
						|
            if (tn === $.COL) | 
						|
                colStartTagInTable(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 4: | 
						|
            if (tn === $.FORM) | 
						|
                formStartTagInTable(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 5: | 
						|
            if (tn === $.TABLE) | 
						|
                tableStartTagInTable(p, token); | 
						|
 | 
						|
            else if (tn === $.STYLE) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) | 
						|
                tbodyStartTagInTable(p, token); | 
						|
 | 
						|
            else if (tn === $.INPUT) | 
						|
                inputStartTagInTable(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 6: | 
						|
            if (tn === $.SCRIPT) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 7: | 
						|
            if (tn === $.CAPTION) | 
						|
                captionStartTagInTable(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        case 8: | 
						|
            if (tn === $.COLGROUP) | 
						|
                colgroupStartTagInTable(p, token); | 
						|
 | 
						|
            else if (tn === $.TEMPLATE) | 
						|
                startTagInHead(p, token); | 
						|
 | 
						|
            else | 
						|
                tokenInTable(p, token); | 
						|
 | 
						|
            break; | 
						|
 | 
						|
        default: | 
						|
            tokenInTable(p, token); | 
						|
    } | 
						|
 | 
						|
} | 
						|
 | 
						|
function endTagInTable(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.TABLE) { | 
						|
        if (p.openElements.hasInTableScope($.TABLE)) { | 
						|
            p.openElements.popUntilTagNamePopped($.TABLE); | 
						|
            p._resetInsertionMode(); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TEMPLATE) | 
						|
        endTagInHead(p, token); | 
						|
 | 
						|
    else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML && | 
						|
             tn !== $.TBODY && tn !== $.TD && tn !== $.TFOOT && tn !== $.TH && tn !== $.THEAD && tn !== $.TR) | 
						|
        tokenInTable(p, token); | 
						|
} | 
						|
 | 
						|
function tokenInTable(p, token) { | 
						|
    var savedFosterParentingState = p.fosterParentingEnabled; | 
						|
 | 
						|
    p.fosterParentingEnabled = true; | 
						|
    p._processTokenInBodyMode(token); | 
						|
    p.fosterParentingEnabled = savedFosterParentingState; | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.10 The "in table text" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function whitespaceCharacterInTableText(p, token) { | 
						|
    p.pendingCharacterTokens.push(token); | 
						|
} | 
						|
 | 
						|
function characterInTableText(p, token) { | 
						|
    p.pendingCharacterTokens.push(token); | 
						|
    p.hasNonWhitespacePendingCharacterToken = true; | 
						|
} | 
						|
 | 
						|
function tokenInTableText(p, token) { | 
						|
    var i = 0; | 
						|
 | 
						|
    if (p.hasNonWhitespacePendingCharacterToken) { | 
						|
        for (; i < p.pendingCharacterTokens.length; i++) | 
						|
            tokenInTable(p, p.pendingCharacterTokens[i]); | 
						|
    } | 
						|
 | 
						|
    else { | 
						|
        for (; i < p.pendingCharacterTokens.length; i++) | 
						|
            p._insertCharacters(p.pendingCharacterTokens[i]); | 
						|
    } | 
						|
 | 
						|
    p.insertionMode = p.originalInsertionMode; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.11 The "in caption" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInCaption(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || tn === $.TBODY || | 
						|
        tn === $.TD || tn === $.TFOOT || tn === $.TH || tn === $.THEAD || tn === $.TR) { | 
						|
        if (p.openElements.hasInTableScope($.CAPTION)) { | 
						|
            p.openElements.generateImpliedEndTags(); | 
						|
            p.openElements.popUntilTagNamePopped($.CAPTION); | 
						|
            p.activeFormattingElements.clearToLastMarker(); | 
						|
            p.insertionMode = IN_TABLE_MODE; | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        startTagInBody(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInCaption(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.CAPTION || tn === $.TABLE) { | 
						|
        if (p.openElements.hasInTableScope($.CAPTION)) { | 
						|
            p.openElements.generateImpliedEndTags(); | 
						|
            p.openElements.popUntilTagNamePopped($.CAPTION); | 
						|
            p.activeFormattingElements.clearToLastMarker(); | 
						|
            p.insertionMode = IN_TABLE_MODE; | 
						|
 | 
						|
            if (tn === $.TABLE) | 
						|
                p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn !== $.BODY && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML && tn !== $.TBODY && | 
						|
             tn !== $.TD && tn !== $.TFOOT && tn !== $.TH && tn !== $.THEAD && tn !== $.TR) | 
						|
        endTagInBody(p, token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.12 The "in column group" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInColumnGroup(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.COL) | 
						|
        p._appendElement(token, NS.HTML); | 
						|
 | 
						|
    else if (tn === $.TEMPLATE) | 
						|
        startTagInHead(p, token); | 
						|
 | 
						|
    else | 
						|
        tokenInColumnGroup(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInColumnGroup(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.COLGROUP) { | 
						|
        if (p.openElements.currentTagName === $.COLGROUP) { | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_MODE; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TEMPLATE) | 
						|
        endTagInHead(p, token); | 
						|
 | 
						|
    else if (tn !== $.COL) | 
						|
        tokenInColumnGroup(p, token); | 
						|
} | 
						|
 | 
						|
function tokenInColumnGroup(p, token) { | 
						|
    if (p.openElements.currentTagName === $.COLGROUP) { | 
						|
        p.openElements.pop(); | 
						|
        p.insertionMode = IN_TABLE_MODE; | 
						|
        p._processToken(token); | 
						|
    } | 
						|
} | 
						|
 | 
						|
//12.2.5.4.13 The "in table body" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInTableBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.TR) { | 
						|
        p.openElements.clearBackToTableBodyContext(); | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.insertionMode = IN_ROW_MODE; | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TH || tn === $.TD) { | 
						|
        p.openElements.clearBackToTableBodyContext(); | 
						|
        p._insertFakeElement($.TR); | 
						|
        p.insertionMode = IN_ROW_MODE; | 
						|
        p._processToken(token); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || | 
						|
             tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) { | 
						|
 | 
						|
        if (p.openElements.hasTableBodyContextInTableScope()) { | 
						|
            p.openElements.clearBackToTableBodyContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_MODE; | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        startTagInTable(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInTableBody(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) { | 
						|
        if (p.openElements.hasInTableScope(tn)) { | 
						|
            p.openElements.clearBackToTableBodyContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_MODE; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TABLE) { | 
						|
        if (p.openElements.hasTableBodyContextInTableScope()) { | 
						|
            p.openElements.clearBackToTableBodyContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_MODE; | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP || | 
						|
             tn !== $.HTML && tn !== $.TD && tn !== $.TH && tn !== $.TR) | 
						|
        endTagInTable(p, token); | 
						|
} | 
						|
 | 
						|
//12.2.5.4.14 The "in row" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInRow(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.TH || tn === $.TD) { | 
						|
        p.openElements.clearBackToTableRowContext(); | 
						|
        p._insertElement(token, NS.HTML); | 
						|
        p.insertionMode = IN_CELL_MODE; | 
						|
        p.activeFormattingElements.insertMarker(); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || tn === $.TBODY || | 
						|
             tn === $.TFOOT || tn === $.THEAD || tn === $.TR) { | 
						|
        if (p.openElements.hasInTableScope($.TR)) { | 
						|
            p.openElements.clearBackToTableRowContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_BODY_MODE; | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        startTagInTable(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInRow(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.TR) { | 
						|
        if (p.openElements.hasInTableScope($.TR)) { | 
						|
            p.openElements.clearBackToTableRowContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_BODY_MODE; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TABLE) { | 
						|
        if (p.openElements.hasInTableScope($.TR)) { | 
						|
            p.openElements.clearBackToTableRowContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_BODY_MODE; | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) { | 
						|
        if (p.openElements.hasInTableScope(tn) || p.openElements.hasInTableScope($.TR)) { | 
						|
            p.openElements.clearBackToTableRowContext(); | 
						|
            p.openElements.pop(); | 
						|
            p.insertionMode = IN_TABLE_BODY_MODE; | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP || | 
						|
             tn !== $.HTML && tn !== $.TD && tn !== $.TH) | 
						|
        endTagInTable(p, token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.15 The "in cell" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInCell(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || tn === $.TBODY || | 
						|
        tn === $.TD || tn === $.TFOOT || tn === $.TH || tn === $.THEAD || tn === $.TR) { | 
						|
 | 
						|
        if (p.openElements.hasInTableScope($.TD) || p.openElements.hasInTableScope($.TH)) { | 
						|
            p._closeTableCell(); | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        startTagInBody(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInCell(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.TD || tn === $.TH) { | 
						|
        if (p.openElements.hasInTableScope(tn)) { | 
						|
            p.openElements.generateImpliedEndTags(); | 
						|
            p.openElements.popUntilTagNamePopped(tn); | 
						|
            p.activeFormattingElements.clearToLastMarker(); | 
						|
            p.insertionMode = IN_ROW_MODE; | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR) { | 
						|
        if (p.openElements.hasInTableScope(tn)) { | 
						|
            p._closeTableCell(); | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML) | 
						|
        endTagInBody(p, token); | 
						|
} | 
						|
 | 
						|
//12.2.5.4.16 The "in select" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInSelect(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.OPTION) { | 
						|
        if (p.openElements.currentTagName === $.OPTION) | 
						|
            p.openElements.pop(); | 
						|
 | 
						|
        p._insertElement(token, NS.HTML); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.OPTGROUP) { | 
						|
        if (p.openElements.currentTagName === $.OPTION) | 
						|
            p.openElements.pop(); | 
						|
 | 
						|
        if (p.openElements.currentTagName === $.OPTGROUP) | 
						|
            p.openElements.pop(); | 
						|
 | 
						|
        p._insertElement(token, NS.HTML); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.INPUT || tn === $.KEYGEN || tn === $.TEXTAREA || tn === $.SELECT) { | 
						|
        if (p.openElements.hasInSelectScope($.SELECT)) { | 
						|
            p.openElements.popUntilTagNamePopped($.SELECT); | 
						|
            p._resetInsertionMode(); | 
						|
 | 
						|
            if (tn !== $.SELECT) | 
						|
                p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.SCRIPT || tn === $.TEMPLATE) | 
						|
        startTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInSelect(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.OPTGROUP) { | 
						|
        var prevOpenElement = p.openElements.items[p.openElements.stackTop - 1], | 
						|
            prevOpenElementTn = prevOpenElement && p.treeAdapter.getTagName(prevOpenElement); | 
						|
 | 
						|
        if (p.openElements.currentTagName === $.OPTION && prevOpenElementTn === $.OPTGROUP) | 
						|
            p.openElements.pop(); | 
						|
 | 
						|
        if (p.openElements.currentTagName === $.OPTGROUP) | 
						|
            p.openElements.pop(); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.OPTION) { | 
						|
        if (p.openElements.currentTagName === $.OPTION) | 
						|
            p.openElements.pop(); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.SELECT && p.openElements.hasInSelectScope($.SELECT)) { | 
						|
        p.openElements.popUntilTagNamePopped($.SELECT); | 
						|
        p._resetInsertionMode(); | 
						|
    } | 
						|
 | 
						|
    else if (tn === $.TEMPLATE) | 
						|
        endTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
//12.2.5.4.17 The "in select in table" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInSelectInTable(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.CAPTION || tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || | 
						|
        tn === $.THEAD || tn === $.TR || tn === $.TD || tn === $.TH) { | 
						|
        p.openElements.popUntilTagNamePopped($.SELECT); | 
						|
        p._resetInsertionMode(); | 
						|
        p._processToken(token); | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        startTagInSelect(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInSelectInTable(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.CAPTION || tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || | 
						|
        tn === $.THEAD || tn === $.TR || tn === $.TD || tn === $.TH) { | 
						|
        if (p.openElements.hasInTableScope(tn)) { | 
						|
            p.openElements.popUntilTagNamePopped($.SELECT); | 
						|
            p._resetInsertionMode(); | 
						|
            p._processToken(token); | 
						|
        } | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        endTagInSelect(p, token); | 
						|
} | 
						|
 | 
						|
//12.2.5.4.18 The "in template" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInTemplate(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META || | 
						|
        tn === $.NOFRAMES || tn === $.SCRIPT || tn === $.STYLE || tn === $.TEMPLATE || tn === $.TITLE) | 
						|
        startTagInHead(p, token); | 
						|
 | 
						|
    else { | 
						|
        var newInsertionMode = TEMPLATE_INSERTION_MODE_SWITCH_MAP[tn] || IN_BODY_MODE; | 
						|
 | 
						|
        p._popTmplInsertionMode(); | 
						|
        p._pushTmplInsertionMode(newInsertionMode); | 
						|
        p.insertionMode = newInsertionMode; | 
						|
        p._processToken(token); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function endTagInTemplate(p, token) { | 
						|
    if (token.tagName === $.TEMPLATE) | 
						|
        endTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
function eofInTemplate(p, token) { | 
						|
    if (p.openElements.tmplCount > 0) { | 
						|
        p.openElements.popUntilTagNamePopped($.TEMPLATE); | 
						|
        p.activeFormattingElements.clearToLastMarker(); | 
						|
        p._popTmplInsertionMode(); | 
						|
        p._resetInsertionMode(); | 
						|
        p._processToken(token); | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        p.stopped = true; | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.4.19 The "after body" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagAfterBody(p, token) { | 
						|
    if (token.tagName === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else | 
						|
        tokenAfterBody(p, token); | 
						|
} | 
						|
 | 
						|
function endTagAfterBody(p, token) { | 
						|
    if (token.tagName === $.HTML) { | 
						|
        if (!p.fragmentContext) | 
						|
            p.insertionMode = AFTER_AFTER_BODY_MODE; | 
						|
    } | 
						|
 | 
						|
    else | 
						|
        tokenAfterBody(p, token); | 
						|
} | 
						|
 | 
						|
function tokenAfterBody(p, token) { | 
						|
    p.insertionMode = IN_BODY_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
//12.2.5.4.20 The "in frameset" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagInFrameset(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.FRAMESET) | 
						|
        p._insertElement(token, NS.HTML); | 
						|
 | 
						|
    else if (tn === $.FRAME) | 
						|
        p._appendElement(token, NS.HTML); | 
						|
 | 
						|
    else if (tn === $.NOFRAMES) | 
						|
        startTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
function endTagInFrameset(p, token) { | 
						|
    if (token.tagName === $.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) { | 
						|
        p.openElements.pop(); | 
						|
 | 
						|
        if (!p.fragmentContext && p.openElements.currentTagName !== $.FRAMESET) | 
						|
            p.insertionMode = AFTER_FRAMESET_MODE; | 
						|
    } | 
						|
} | 
						|
 | 
						|
//12.2.5.4.21 The "after frameset" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagAfterFrameset(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.NOFRAMES) | 
						|
        startTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
function endTagAfterFrameset(p, token) { | 
						|
    if (token.tagName === $.HTML) | 
						|
        p.insertionMode = AFTER_AFTER_FRAMESET_MODE; | 
						|
} | 
						|
 | 
						|
//12.2.5.4.22 The "after after body" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagAfterAfterBody(p, token) { | 
						|
    if (token.tagName === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else | 
						|
        tokenAfterAfterBody(p, token); | 
						|
} | 
						|
 | 
						|
function tokenAfterAfterBody(p, token) { | 
						|
    p.insertionMode = IN_BODY_MODE; | 
						|
    p._processToken(token); | 
						|
} | 
						|
 | 
						|
//12.2.5.4.23 The "after after frameset" insertion mode | 
						|
//------------------------------------------------------------------ | 
						|
function startTagAfterAfterFrameset(p, token) { | 
						|
    var tn = token.tagName; | 
						|
 | 
						|
    if (tn === $.HTML) | 
						|
        startTagInBody(p, token); | 
						|
 | 
						|
    else if (tn === $.NOFRAMES) | 
						|
        startTagInHead(p, token); | 
						|
} | 
						|
 | 
						|
 | 
						|
//12.2.5.5 The rules for parsing tokens in foreign content | 
						|
//------------------------------------------------------------------ | 
						|
function nullCharacterInForeignContent(p, token) { | 
						|
    token.chars = UNICODE.REPLACEMENT_CHARACTER; | 
						|
    p._insertCharacters(token); | 
						|
} | 
						|
 | 
						|
function characterInForeignContent(p, token) { | 
						|
    p._insertCharacters(token); | 
						|
    p.framesetOk = false; | 
						|
} | 
						|
 | 
						|
function startTagInForeignContent(p, token) { | 
						|
    if (foreignContent.causesExit(token) && !p.fragmentContext) { | 
						|
        while (p.treeAdapter.getNamespaceURI(p.openElements.current) !== NS.HTML && !p._isIntegrationPoint(p.openElements.current)) | 
						|
            p.openElements.pop(); | 
						|
 | 
						|
        p._processToken(token); | 
						|
    } | 
						|
 | 
						|
    else { | 
						|
        var current = p._getAdjustedCurrentElement(), | 
						|
            currentNs = p.treeAdapter.getNamespaceURI(current); | 
						|
 | 
						|
        if (currentNs === NS.MATHML) | 
						|
            foreignContent.adjustTokenMathMLAttrs(token); | 
						|
 | 
						|
        else if (currentNs === NS.SVG) { | 
						|
            foreignContent.adjustTokenSVGTagName(token); | 
						|
            foreignContent.adjustTokenSVGAttrs(token); | 
						|
        } | 
						|
 | 
						|
        foreignContent.adjustTokenXMLAttrs(token); | 
						|
 | 
						|
        if (token.selfClosing) | 
						|
            p._appendElement(token, currentNs); | 
						|
        else | 
						|
            p._insertElement(token, currentNs); | 
						|
    } | 
						|
} | 
						|
 | 
						|
function endTagInForeignContent(p, token) { | 
						|
    for (var i = p.openElements.stackTop; i > 0; i--) { | 
						|
        var element = p.openElements.items[i]; | 
						|
 | 
						|
        if (p.treeAdapter.getNamespaceURI(element) === NS.HTML) { | 
						|
            p._processToken(token); | 
						|
            break; | 
						|
        } | 
						|
 | 
						|
        if (p.treeAdapter.getTagName(element).toLowerCase() === token.tagName) { | 
						|
            p.openElements.popUntilElementPopped(element); | 
						|
            break; | 
						|
        } | 
						|
    } | 
						|
}
 | 
						|
 |