1'use strict'; 2 3var Tokenizer = require('../tokenization/tokenizer'), 4 OpenElementStack = require('./open_element_stack'), 5 FormattingElementList = require('./formatting_element_list'), 6 LocationInfoMixin = require('./location_info_mixin'), 7 DefaultTreeAdapter = require('../tree_adapters/default'), 8 Doctype = require('../common/doctype'), 9 ForeignContent = require('../common/foreign_content'), 10 Utils = require('../common/utils'), 11 UNICODE = require('../common/unicode'), 12 HTML = require('../common/html'); 13 14//Aliases 15var $ = HTML.TAG_NAMES, 16 NS = HTML.NAMESPACES, 17 ATTRS = HTML.ATTRS; 18 19//Default options 20var DEFAULT_OPTIONS = { 21 decodeHtmlEntities: true, 22 locationInfo: false 23}; 24 25//Misc constants 26var SEARCHABLE_INDEX_DEFAULT_PROMPT = 'This is a searchable index. Enter search keywords: ', 27 SEARCHABLE_INDEX_INPUT_NAME = 'isindex', 28 HIDDEN_INPUT_TYPE = 'hidden'; 29 30//Adoption agency loops iteration count 31var AA_OUTER_LOOP_ITER = 8, 32 AA_INNER_LOOP_ITER = 3; 33 34//Insertion modes 35var INITIAL_MODE = 'INITIAL_MODE', 36 BEFORE_HTML_MODE = 'BEFORE_HTML_MODE', 37 BEFORE_HEAD_MODE = 'BEFORE_HEAD_MODE', 38 IN_HEAD_MODE = 'IN_HEAD_MODE', 39 AFTER_HEAD_MODE = 'AFTER_HEAD_MODE', 40 IN_BODY_MODE = 'IN_BODY_MODE', 41 TEXT_MODE = 'TEXT_MODE', 42 IN_TABLE_MODE = 'IN_TABLE_MODE', 43 IN_TABLE_TEXT_MODE = 'IN_TABLE_TEXT_MODE', 44 IN_CAPTION_MODE = 'IN_CAPTION_MODE', 45 IN_COLUMN_GROUP_MODE = 'IN_COLUMN_GROUP_MODE', 46 IN_TABLE_BODY_MODE = 'IN_TABLE_BODY_MODE', 47 IN_ROW_MODE = 'IN_ROW_MODE', 48 IN_CELL_MODE = 'IN_CELL_MODE', 49 IN_SELECT_MODE = 'IN_SELECT_MODE', 50 IN_SELECT_IN_TABLE_MODE = 'IN_SELECT_IN_TABLE_MODE', 51 IN_TEMPLATE_MODE = 'IN_TEMPLATE_MODE', 52 AFTER_BODY_MODE = 'AFTER_BODY_MODE', 53 IN_FRAMESET_MODE = 'IN_FRAMESET_MODE', 54 AFTER_FRAMESET_MODE = 'AFTER_FRAMESET_MODE', 55 AFTER_AFTER_BODY_MODE = 'AFTER_AFTER_BODY_MODE', 56 AFTER_AFTER_FRAMESET_MODE = 'AFTER_AFTER_FRAMESET_MODE'; 57 58//Insertion mode reset map 59var INSERTION_MODE_RESET_MAP = {}; 60 61INSERTION_MODE_RESET_MAP[$.TR] = IN_ROW_MODE; 62INSERTION_MODE_RESET_MAP[$.TBODY] = 63INSERTION_MODE_RESET_MAP[$.THEAD] = 64INSERTION_MODE_RESET_MAP[$.TFOOT] = IN_TABLE_BODY_MODE; 65INSERTION_MODE_RESET_MAP[$.CAPTION] = IN_CAPTION_MODE; 66INSERTION_MODE_RESET_MAP[$.COLGROUP] = IN_COLUMN_GROUP_MODE; 67INSERTION_MODE_RESET_MAP[$.TABLE] = IN_TABLE_MODE; 68INSERTION_MODE_RESET_MAP[$.BODY] = IN_BODY_MODE; 69INSERTION_MODE_RESET_MAP[$.FRAMESET] = IN_FRAMESET_MODE; 70 71//Template insertion mode switch map 72var TEMPLATE_INSERTION_MODE_SWITCH_MAP = {}; 73 74TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.CAPTION] = 75TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.COLGROUP] = 76TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TBODY] = 77TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TFOOT] = 78TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.THEAD] = IN_TABLE_MODE; 79TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.COL] = IN_COLUMN_GROUP_MODE; 80TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TR] = IN_TABLE_BODY_MODE; 81TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TD] = 82TEMPLATE_INSERTION_MODE_SWITCH_MAP[$.TH] = IN_ROW_MODE; 83 84//Token handlers map for insertion modes 85var _ = {}; 86 87_[INITIAL_MODE] = {}; 88_[INITIAL_MODE][Tokenizer.CHARACTER_TOKEN] = 89_[INITIAL_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenInInitialMode; 90_[INITIAL_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = ignoreToken; 91_[INITIAL_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 92_[INITIAL_MODE][Tokenizer.DOCTYPE_TOKEN] = doctypeInInitialMode; 93_[INITIAL_MODE][Tokenizer.START_TAG_TOKEN] = 94_[INITIAL_MODE][Tokenizer.END_TAG_TOKEN] = 95_[INITIAL_MODE][Tokenizer.EOF_TOKEN] = tokenInInitialMode; 96 97_[BEFORE_HTML_MODE] = {}; 98_[BEFORE_HTML_MODE][Tokenizer.CHARACTER_TOKEN] = 99_[BEFORE_HTML_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenBeforeHtml; 100_[BEFORE_HTML_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = ignoreToken; 101_[BEFORE_HTML_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 102_[BEFORE_HTML_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 103_[BEFORE_HTML_MODE][Tokenizer.START_TAG_TOKEN] = startTagBeforeHtml; 104_[BEFORE_HTML_MODE][Tokenizer.END_TAG_TOKEN] = endTagBeforeHtml; 105_[BEFORE_HTML_MODE][Tokenizer.EOF_TOKEN] = tokenBeforeHtml; 106 107_[BEFORE_HEAD_MODE] = {}; 108_[BEFORE_HEAD_MODE][Tokenizer.CHARACTER_TOKEN] = 109_[BEFORE_HEAD_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenBeforeHead; 110_[BEFORE_HEAD_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = ignoreToken; 111_[BEFORE_HEAD_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 112_[BEFORE_HEAD_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 113_[BEFORE_HEAD_MODE][Tokenizer.START_TAG_TOKEN] = startTagBeforeHead; 114_[BEFORE_HEAD_MODE][Tokenizer.END_TAG_TOKEN] = endTagBeforeHead; 115_[BEFORE_HEAD_MODE][Tokenizer.EOF_TOKEN] = tokenBeforeHead; 116 117_[IN_HEAD_MODE] = {}; 118_[IN_HEAD_MODE][Tokenizer.CHARACTER_TOKEN] = 119_[IN_HEAD_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenInHead; 120_[IN_HEAD_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 121_[IN_HEAD_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 122_[IN_HEAD_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 123_[IN_HEAD_MODE][Tokenizer.START_TAG_TOKEN] = startTagInHead; 124_[IN_HEAD_MODE][Tokenizer.END_TAG_TOKEN] = endTagInHead; 125_[IN_HEAD_MODE][Tokenizer.EOF_TOKEN] = tokenInHead; 126 127_[AFTER_HEAD_MODE] = {}; 128_[AFTER_HEAD_MODE][Tokenizer.CHARACTER_TOKEN] = 129_[AFTER_HEAD_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenAfterHead; 130_[AFTER_HEAD_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 131_[AFTER_HEAD_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 132_[AFTER_HEAD_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 133_[AFTER_HEAD_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterHead; 134_[AFTER_HEAD_MODE][Tokenizer.END_TAG_TOKEN] = endTagAfterHead; 135_[AFTER_HEAD_MODE][Tokenizer.EOF_TOKEN] = tokenAfterHead; 136 137_[IN_BODY_MODE] = {}; 138_[IN_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; 139_[IN_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 140_[IN_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 141_[IN_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 142_[IN_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 143_[IN_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagInBody; 144_[IN_BODY_MODE][Tokenizer.END_TAG_TOKEN] = endTagInBody; 145_[IN_BODY_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 146 147_[TEXT_MODE] = {}; 148_[TEXT_MODE][Tokenizer.CHARACTER_TOKEN] = 149_[TEXT_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = 150_[TEXT_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 151_[TEXT_MODE][Tokenizer.COMMENT_TOKEN] = 152_[TEXT_MODE][Tokenizer.DOCTYPE_TOKEN] = 153_[TEXT_MODE][Tokenizer.START_TAG_TOKEN] = ignoreToken; 154_[TEXT_MODE][Tokenizer.END_TAG_TOKEN] = endTagInText; 155_[TEXT_MODE][Tokenizer.EOF_TOKEN] = eofInText; 156 157_[IN_TABLE_MODE] = {}; 158_[IN_TABLE_MODE][Tokenizer.CHARACTER_TOKEN] = 159_[IN_TABLE_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = 160_[IN_TABLE_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = characterInTable; 161_[IN_TABLE_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 162_[IN_TABLE_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 163_[IN_TABLE_MODE][Tokenizer.START_TAG_TOKEN] = startTagInTable; 164_[IN_TABLE_MODE][Tokenizer.END_TAG_TOKEN] = endTagInTable; 165_[IN_TABLE_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 166 167_[IN_TABLE_TEXT_MODE] = {}; 168_[IN_TABLE_TEXT_MODE][Tokenizer.CHARACTER_TOKEN] = characterInTableText; 169_[IN_TABLE_TEXT_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 170_[IN_TABLE_TEXT_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInTableText; 171_[IN_TABLE_TEXT_MODE][Tokenizer.COMMENT_TOKEN] = 172_[IN_TABLE_TEXT_MODE][Tokenizer.DOCTYPE_TOKEN] = 173_[IN_TABLE_TEXT_MODE][Tokenizer.START_TAG_TOKEN] = 174_[IN_TABLE_TEXT_MODE][Tokenizer.END_TAG_TOKEN] = 175_[IN_TABLE_TEXT_MODE][Tokenizer.EOF_TOKEN] = tokenInTableText; 176 177_[IN_CAPTION_MODE] = {}; 178_[IN_CAPTION_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; 179_[IN_CAPTION_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 180_[IN_CAPTION_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 181_[IN_CAPTION_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 182_[IN_CAPTION_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 183_[IN_CAPTION_MODE][Tokenizer.START_TAG_TOKEN] = startTagInCaption; 184_[IN_CAPTION_MODE][Tokenizer.END_TAG_TOKEN] = endTagInCaption; 185_[IN_CAPTION_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 186 187_[IN_COLUMN_GROUP_MODE] = {}; 188_[IN_COLUMN_GROUP_MODE][Tokenizer.CHARACTER_TOKEN] = 189_[IN_COLUMN_GROUP_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenInColumnGroup; 190_[IN_COLUMN_GROUP_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 191_[IN_COLUMN_GROUP_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 192_[IN_COLUMN_GROUP_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 193_[IN_COLUMN_GROUP_MODE][Tokenizer.START_TAG_TOKEN] = startTagInColumnGroup; 194_[IN_COLUMN_GROUP_MODE][Tokenizer.END_TAG_TOKEN] = endTagInColumnGroup; 195_[IN_COLUMN_GROUP_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 196 197_[IN_TABLE_BODY_MODE] = {}; 198_[IN_TABLE_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = 199_[IN_TABLE_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = 200_[IN_TABLE_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = characterInTable; 201_[IN_TABLE_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 202_[IN_TABLE_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 203_[IN_TABLE_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagInTableBody; 204_[IN_TABLE_BODY_MODE][Tokenizer.END_TAG_TOKEN] = endTagInTableBody; 205_[IN_TABLE_BODY_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 206 207_[IN_ROW_MODE] = {}; 208_[IN_ROW_MODE][Tokenizer.CHARACTER_TOKEN] = 209_[IN_ROW_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = 210_[IN_ROW_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = characterInTable; 211_[IN_ROW_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 212_[IN_ROW_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 213_[IN_ROW_MODE][Tokenizer.START_TAG_TOKEN] = startTagInRow; 214_[IN_ROW_MODE][Tokenizer.END_TAG_TOKEN] = endTagInRow; 215_[IN_ROW_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 216 217_[IN_CELL_MODE] = {}; 218_[IN_CELL_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; 219_[IN_CELL_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 220_[IN_CELL_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 221_[IN_CELL_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 222_[IN_CELL_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 223_[IN_CELL_MODE][Tokenizer.START_TAG_TOKEN] = startTagInCell; 224_[IN_CELL_MODE][Tokenizer.END_TAG_TOKEN] = endTagInCell; 225_[IN_CELL_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 226 227_[IN_SELECT_MODE] = {}; 228_[IN_SELECT_MODE][Tokenizer.CHARACTER_TOKEN] = insertCharacters; 229_[IN_SELECT_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 230_[IN_SELECT_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 231_[IN_SELECT_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 232_[IN_SELECT_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 233_[IN_SELECT_MODE][Tokenizer.START_TAG_TOKEN] = startTagInSelect; 234_[IN_SELECT_MODE][Tokenizer.END_TAG_TOKEN] = endTagInSelect; 235_[IN_SELECT_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 236 237_[IN_SELECT_IN_TABLE_MODE] = {}; 238_[IN_SELECT_IN_TABLE_MODE][Tokenizer.CHARACTER_TOKEN] = insertCharacters; 239_[IN_SELECT_IN_TABLE_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 240_[IN_SELECT_IN_TABLE_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 241_[IN_SELECT_IN_TABLE_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 242_[IN_SELECT_IN_TABLE_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 243_[IN_SELECT_IN_TABLE_MODE][Tokenizer.START_TAG_TOKEN] = startTagInSelectInTable; 244_[IN_SELECT_IN_TABLE_MODE][Tokenizer.END_TAG_TOKEN] = endTagInSelectInTable; 245_[IN_SELECT_IN_TABLE_MODE][Tokenizer.EOF_TOKEN] = eofInBody; 246 247_[IN_TEMPLATE_MODE] = {}; 248_[IN_TEMPLATE_MODE][Tokenizer.CHARACTER_TOKEN] = characterInBody; 249_[IN_TEMPLATE_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 250_[IN_TEMPLATE_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 251_[IN_TEMPLATE_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 252_[IN_TEMPLATE_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 253_[IN_TEMPLATE_MODE][Tokenizer.START_TAG_TOKEN] = startTagInTemplate; 254_[IN_TEMPLATE_MODE][Tokenizer.END_TAG_TOKEN] = endTagInTemplate; 255_[IN_TEMPLATE_MODE][Tokenizer.EOF_TOKEN] = eofInTemplate; 256 257_[AFTER_BODY_MODE] = {}; 258_[AFTER_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = 259_[AFTER_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenAfterBody; 260_[AFTER_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 261_[AFTER_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendCommentToRootHtmlElement; 262_[AFTER_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 263_[AFTER_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterBody; 264_[AFTER_BODY_MODE][Tokenizer.END_TAG_TOKEN] = endTagAfterBody; 265_[AFTER_BODY_MODE][Tokenizer.EOF_TOKEN] = stopParsing; 266 267_[IN_FRAMESET_MODE] = {}; 268_[IN_FRAMESET_MODE][Tokenizer.CHARACTER_TOKEN] = 269_[IN_FRAMESET_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 270_[IN_FRAMESET_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 271_[IN_FRAMESET_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 272_[IN_FRAMESET_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 273_[IN_FRAMESET_MODE][Tokenizer.START_TAG_TOKEN] = startTagInFrameset; 274_[IN_FRAMESET_MODE][Tokenizer.END_TAG_TOKEN] = endTagInFrameset; 275_[IN_FRAMESET_MODE][Tokenizer.EOF_TOKEN] = stopParsing; 276 277_[AFTER_FRAMESET_MODE] = {}; 278_[AFTER_FRAMESET_MODE][Tokenizer.CHARACTER_TOKEN] = 279_[AFTER_FRAMESET_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 280_[AFTER_FRAMESET_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = insertCharacters; 281_[AFTER_FRAMESET_MODE][Tokenizer.COMMENT_TOKEN] = appendComment; 282_[AFTER_FRAMESET_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 283_[AFTER_FRAMESET_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterFrameset; 284_[AFTER_FRAMESET_MODE][Tokenizer.END_TAG_TOKEN] = endTagAfterFrameset; 285_[AFTER_FRAMESET_MODE][Tokenizer.EOF_TOKEN] = stopParsing; 286 287_[AFTER_AFTER_BODY_MODE] = {}; 288_[AFTER_AFTER_BODY_MODE][Tokenizer.CHARACTER_TOKEN] = tokenAfterAfterBody; 289_[AFTER_AFTER_BODY_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = tokenAfterAfterBody; 290_[AFTER_AFTER_BODY_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 291_[AFTER_AFTER_BODY_MODE][Tokenizer.COMMENT_TOKEN] = appendCommentToDocument; 292_[AFTER_AFTER_BODY_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 293_[AFTER_AFTER_BODY_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterAfterBody; 294_[AFTER_AFTER_BODY_MODE][Tokenizer.END_TAG_TOKEN] = tokenAfterAfterBody; 295_[AFTER_AFTER_BODY_MODE][Tokenizer.EOF_TOKEN] = stopParsing; 296 297_[AFTER_AFTER_FRAMESET_MODE] = {}; 298_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.CHARACTER_TOKEN] = 299_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.NULL_CHARACTER_TOKEN] = ignoreToken; 300_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.WHITESPACE_CHARACTER_TOKEN] = whitespaceCharacterInBody; 301_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.COMMENT_TOKEN] = appendCommentToDocument; 302_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.DOCTYPE_TOKEN] = ignoreToken; 303_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.START_TAG_TOKEN] = startTagAfterAfterFrameset; 304_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.END_TAG_TOKEN] = ignoreToken; 305_[AFTER_AFTER_FRAMESET_MODE][Tokenizer.EOF_TOKEN] = stopParsing; 306 307//Searchable index building utils (<isindex> tag) 308function getSearchableIndexFormAttrs(isindexStartTagToken) { 309 var indexAction = Tokenizer.getTokenAttr(isindexStartTagToken, ATTRS.ACTION), 310 attrs = []; 311 312 if (indexAction !== null) { 313 attrs.push({ 314 name: ATTRS.ACTION, 315 value: indexAction 316 }); 317 } 318 319 return attrs; 320} 321 322function getSearchableIndexLabelText(isindexStartTagToken) { 323 var indexPrompt = Tokenizer.getTokenAttr(isindexStartTagToken, ATTRS.PROMPT); 324 325 return indexPrompt === null ? SEARCHABLE_INDEX_DEFAULT_PROMPT : indexPrompt; 326} 327 328function getSearchableIndexInputAttrs(isindexStartTagToken) { 329 var isindexAttrs = isindexStartTagToken.attrs, 330 inputAttrs = []; 331 332 for (var i = 0; i < isindexAttrs.length; i++) { 333 var name = isindexAttrs[i].name; 334 335 if (name !== ATTRS.NAME && name !== ATTRS.ACTION && name !== ATTRS.PROMPT) 336 inputAttrs.push(isindexAttrs[i]); 337 } 338 339 inputAttrs.push({ 340 name: ATTRS.NAME, 341 value: SEARCHABLE_INDEX_INPUT_NAME 342 }); 343 344 return inputAttrs; 345} 346 347//Parser 348var Parser = module.exports = function (treeAdapter, options) { 349 this.treeAdapter = treeAdapter || DefaultTreeAdapter; 350 this.options = Utils.mergeOptions(DEFAULT_OPTIONS, options); 351 this.scriptHandler = null; 352 353 if (this.options.locationInfo) 354 LocationInfoMixin.assign(this); 355}; 356 357//API 358Parser.prototype.parse = function (html) { 359 var document = this.treeAdapter.createDocument(); 360 361 this._reset(html, document, null); 362 this._runParsingLoop(); 363 364 return document; 365}; 366 367Parser.prototype.parseFragment = function (html, fragmentContext) { 368 //NOTE: use <template> element as a fragment context if context element was not provided, 369 //so we will parse in "forgiving" manner 370 if (!fragmentContext) 371 fragmentContext = this.treeAdapter.createElement($.TEMPLATE, NS.HTML, []); 372 373 //NOTE: create fake element which will be used as 'document' for fragment parsing. 374 //This is important for jsdom there 'document' can't be recreated, therefore 375 //fragment parsing causes messing of the main `document`. 376 var documentMock = this.treeAdapter.createElement('documentmock', NS.HTML, []); 377 378 this._reset(html, documentMock, fragmentContext); 379 380 if (this.treeAdapter.getTagName(fragmentContext) === $.TEMPLATE) 381 this._pushTmplInsertionMode(IN_TEMPLATE_MODE); 382 383 this._initTokenizerForFragmentParsing(); 384 this._insertFakeRootElement(); 385 this._resetInsertionMode(); 386 this._findFormInFragmentContext(); 387 this._runParsingLoop(); 388 389 var rootElement = this.treeAdapter.getFirstChild(documentMock), 390 fragment = this.treeAdapter.createDocumentFragment(); 391 392 this._adoptNodes(rootElement, fragment); 393 394 return fragment; 395}; 396 397//Reset state 398Parser.prototype._reset = function (html, document, fragmentContext) { 399 this.tokenizer = new Tokenizer(html, this.options); 400 401 this.stopped = false; 402 403 this.insertionMode = INITIAL_MODE; 404 this.originalInsertionMode = ''; 405 406 this.document = document; 407 this.fragmentContext = fragmentContext; 408 409 this.headElement = null; 410 this.formElement = null; 411 412 this.openElements = new OpenElementStack(this.document, this.treeAdapter); 413 this.activeFormattingElements = new FormattingElementList(this.treeAdapter); 414 415 this.tmplInsertionModeStack = []; 416 this.tmplInsertionModeStackTop = -1; 417 this.currentTmplInsertionMode = null; 418 419 this.pendingCharacterTokens = []; 420 this.hasNonWhitespacePendingCharacterToken = false; 421 422 this.framesetOk = true; 423 this.skipNextNewLine = false; 424 this.fosterParentingEnabled = false; 425}; 426 427//Parsing loop 428Parser.prototype._iterateParsingLoop = function () { 429 this._setupTokenizerCDATAMode(); 430 431 var token = this.tokenizer.getNextToken(); 432 433 if (this.skipNextNewLine) { 434 this.skipNextNewLine = false; 435 436 if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN && token.chars[0] === '\n') { 437 if (token.chars.length === 1) 438 return; 439 440 token.chars = token.chars.substr(1); 441 } 442 } 443 444 if (this._shouldProcessTokenInForeignContent(token)) 445 this._processTokenInForeignContent(token); 446 447 else 448 this._processToken(token); 449}; 450 451Parser.prototype._runParsingLoop = function () { 452 while (!this.stopped) 453 this._iterateParsingLoop(); 454}; 455 456//Text parsing 457Parser.prototype._setupTokenizerCDATAMode = function () { 458 var current = this._getAdjustedCurrentElement(); 459 460 this.tokenizer.allowCDATA = current && current !== this.document && 461 this.treeAdapter.getNamespaceURI(current) !== NS.HTML && 462 (!this._isHtmlIntegrationPoint(current)) && 463 (!this._isMathMLTextIntegrationPoint(current)); 464}; 465 466Parser.prototype._switchToTextParsing = function (currentToken, nextTokenizerState) { 467 this._insertElement(currentToken, NS.HTML); 468 this.tokenizer.state = nextTokenizerState; 469 this.originalInsertionMode = this.insertionMode; 470 this.insertionMode = TEXT_MODE; 471}; 472 473//Fragment parsing 474Parser.prototype._getAdjustedCurrentElement = function () { 475 return this.openElements.stackTop === 0 && this.fragmentContext ? 476 this.fragmentContext : 477 this.openElements.current; 478}; 479 480Parser.prototype._findFormInFragmentContext = function () { 481 var node = this.fragmentContext; 482 483 do { 484 if (this.treeAdapter.getTagName(node) === $.FORM) { 485 this.formElement = node; 486 break; 487 } 488 489 node = this.treeAdapter.getParentNode(node); 490 } while (node); 491}; 492 493Parser.prototype._initTokenizerForFragmentParsing = function () { 494 var tn = this.treeAdapter.getTagName(this.fragmentContext); 495 496 if (tn === $.TITLE || tn === $.TEXTAREA) 497 this.tokenizer.state = Tokenizer.MODE.RCDATA; 498 499 else if (tn === $.STYLE || tn === $.XMP || tn === $.IFRAME || 500 tn === $.NOEMBED || tn === $.NOFRAMES || tn === $.NOSCRIPT) { 501 this.tokenizer.state = Tokenizer.MODE.RAWTEXT; 502 } 503 504 else if (tn === $.SCRIPT) 505 this.tokenizer.state = Tokenizer.MODE.SCRIPT_DATA; 506 507 else if (tn === $.PLAINTEXT) 508 this.tokenizer.state = Tokenizer.MODE.PLAINTEXT; 509}; 510 511//Tree mutation 512Parser.prototype._setDocumentType = function (token) { 513 this.treeAdapter.setDocumentType(this.document, token.name, token.publicId, token.systemId); 514}; 515 516Parser.prototype._attachElementToTree = function (element) { 517 if (this._shouldFosterParentOnInsertion()) 518 this._fosterParentElement(element); 519 520 else { 521 var parent = this.openElements.currentTmplContent || this.openElements.current; 522 523 this.treeAdapter.appendChild(parent, element); 524 } 525}; 526 527Parser.prototype._appendElement = function (token, namespaceURI) { 528 var element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs); 529 530 this._attachElementToTree(element); 531}; 532 533Parser.prototype._insertElement = function (token, namespaceURI) { 534 var element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs); 535 536 this._attachElementToTree(element); 537 this.openElements.push(element); 538}; 539 540Parser.prototype._insertTemplate = function (token) { 541 var tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs), 542 content = this.treeAdapter.createDocumentFragment(); 543 544 this.treeAdapter.appendChild(tmpl, content); 545 this._attachElementToTree(tmpl); 546 this.openElements.push(tmpl); 547}; 548 549Parser.prototype._insertFakeRootElement = function () { 550 var element = this.treeAdapter.createElement($.HTML, NS.HTML, []); 551 552 this.treeAdapter.appendChild(this.openElements.current, element); 553 this.openElements.push(element); 554}; 555 556Parser.prototype._appendCommentNode = function (token, parent) { 557 var commentNode = this.treeAdapter.createCommentNode(token.data); 558 559 this.treeAdapter.appendChild(parent, commentNode); 560}; 561 562Parser.prototype._insertCharacters = function (token) { 563 if (this._shouldFosterParentOnInsertion()) 564 this._fosterParentText(token.chars); 565 566 else { 567 var parent = this.openElements.currentTmplContent || this.openElements.current; 568 569 this.treeAdapter.insertText(parent, token.chars); 570 } 571}; 572 573Parser.prototype._adoptNodes = function (donor, recipient) { 574 while (true) { 575 var child = this.treeAdapter.getFirstChild(donor); 576 577 if (!child) 578 break; 579 580 this.treeAdapter.detachNode(child); 581 this.treeAdapter.appendChild(recipient, child); 582 } 583}; 584 585//Token processing 586Parser.prototype._shouldProcessTokenInForeignContent = function (token) { 587 var current = this._getAdjustedCurrentElement(); 588 589 if (!current || current === this.document) 590 return false; 591 592 var ns = this.treeAdapter.getNamespaceURI(current); 593 594 if (ns === NS.HTML) 595 return false; 596 597 if (this.treeAdapter.getTagName(current) === $.ANNOTATION_XML && ns === NS.MATHML && 598 token.type === Tokenizer.START_TAG_TOKEN && token.tagName === $.SVG) { 599 return false; 600 } 601 602 var isCharacterToken = token.type === Tokenizer.CHARACTER_TOKEN || 603 token.type === Tokenizer.NULL_CHARACTER_TOKEN || 604 token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN, 605 isMathMLTextStartTag = token.type === Tokenizer.START_TAG_TOKEN && 606 token.tagName !== $.MGLYPH && 607 token.tagName !== $.MALIGNMARK; 608 609 if ((isMathMLTextStartTag || isCharacterToken) && this._isMathMLTextIntegrationPoint(current)) 610 return false; 611 612 if ((token.type === Tokenizer.START_TAG_TOKEN || isCharacterToken) && this._isHtmlIntegrationPoint(current)) 613 return false; 614 615 return token.type !== Tokenizer.EOF_TOKEN; 616}; 617 618Parser.prototype._processToken = function (token) { 619 _[this.insertionMode][token.type](this, token); 620}; 621 622Parser.prototype._processTokenInBodyMode = function (token) { 623 _[IN_BODY_MODE][token.type](this, token); 624}; 625 626Parser.prototype._processTokenInForeignContent = function (token) { 627 if (token.type === Tokenizer.CHARACTER_TOKEN) 628 characterInForeignContent(this, token); 629 630 else if (token.type === Tokenizer.NULL_CHARACTER_TOKEN) 631 nullCharacterInForeignContent(this, token); 632 633 else if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN) 634 insertCharacters(this, token); 635 636 else if (token.type === Tokenizer.COMMENT_TOKEN) 637 appendComment(this, token); 638 639 else if (token.type === Tokenizer.START_TAG_TOKEN) 640 startTagInForeignContent(this, token); 641 642 else if (token.type === Tokenizer.END_TAG_TOKEN) 643 endTagInForeignContent(this, token); 644}; 645 646Parser.prototype._processFakeStartTagWithAttrs = function (tagName, attrs) { 647 var fakeToken = this.tokenizer.buildStartTagToken(tagName); 648 649 fakeToken.attrs = attrs; 650 this._processToken(fakeToken); 651}; 652 653Parser.prototype._processFakeStartTag = function (tagName) { 654 var fakeToken = this.tokenizer.buildStartTagToken(tagName); 655 656 this._processToken(fakeToken); 657 return fakeToken; 658}; 659 660Parser.prototype._processFakeEndTag = function (tagName) { 661 var fakeToken = this.tokenizer.buildEndTagToken(tagName); 662 663 this._processToken(fakeToken); 664 return fakeToken; 665}; 666 667//Integration points 668Parser.prototype._isMathMLTextIntegrationPoint = function (element) { 669 var tn = this.treeAdapter.getTagName(element), 670 ns = this.treeAdapter.getNamespaceURI(element); 671 672 return ForeignContent.isMathMLTextIntegrationPoint(tn, ns); 673}; 674 675Parser.prototype._isHtmlIntegrationPoint = function (element) { 676 var tn = this.treeAdapter.getTagName(element), 677 ns = this.treeAdapter.getNamespaceURI(element), 678 attrs = this.treeAdapter.getAttrList(element); 679 680 return ForeignContent.isHtmlIntegrationPoint(tn, ns, attrs); 681}; 682 683//Active formatting elements reconstruction 684Parser.prototype._reconstructActiveFormattingElements = function () { 685 var listLength = this.activeFormattingElements.length; 686 687 if (listLength) { 688 var unopenIdx = listLength, 689 entry = null; 690 691 do { 692 unopenIdx--; 693 entry = this.activeFormattingElements.entries[unopenIdx]; 694 695 if (entry.type === FormattingElementList.MARKER_ENTRY || this.openElements.contains(entry.element)) { 696 unopenIdx++; 697 break; 698 } 699 } while (unopenIdx > 0); 700 701 for (var i = unopenIdx; i < listLength; i++) { 702 entry = this.activeFormattingElements.entries[i]; 703 this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element)); 704 entry.element = this.openElements.current; 705 } 706 } 707}; 708 709//Close elements 710Parser.prototype._closeTableCell = function () { 711 if (this.openElements.hasInTableScope($.TD)) 712 this._processFakeEndTag($.TD); 713 714 else 715 this._processFakeEndTag($.TH); 716}; 717 718Parser.prototype._closePElement = function () { 719 this.openElements.generateImpliedEndTagsWithExclusion($.P); 720 this.openElements.popUntilTagNamePopped($.P); 721}; 722 723//Insertion modes 724Parser.prototype._resetInsertionMode = function () { 725 for (var i = this.openElements.stackTop, last = false; i >= 0; i--) { 726 var element = this.openElements.items[i]; 727 728 if (i === 0) { 729 last = true; 730 731 if (this.fragmentContext) 732 element = this.fragmentContext; 733 } 734 735 var tn = this.treeAdapter.getTagName(element), 736 newInsertionMode = INSERTION_MODE_RESET_MAP[tn]; 737 738 if (newInsertionMode) { 739 this.insertionMode = newInsertionMode; 740 break; 741 } 742 743 else if (!last && (tn === $.TD || tn === $.TH)) { 744 this.insertionMode = IN_CELL_MODE; 745 break; 746 } 747 748 else if (!last && tn === $.HEAD) { 749 this.insertionMode = IN_HEAD_MODE; 750 break; 751 } 752 753 else if (tn === $.SELECT) { 754 this._resetInsertionModeForSelect(i); 755 break; 756 } 757 758 else if (tn === $.TEMPLATE) { 759 this.insertionMode = this.currentTmplInsertionMode; 760 break; 761 } 762 763 else if (tn === $.HTML) { 764 this.insertionMode = this.headElement ? AFTER_HEAD_MODE : BEFORE_HEAD_MODE; 765 break; 766 } 767 768 else if (last) { 769 this.insertionMode = IN_BODY_MODE; 770 break; 771 } 772 } 773}; 774 775Parser.prototype._resetInsertionModeForSelect = function (selectIdx) { 776 if (selectIdx > 0) { 777 for (var i = selectIdx - 1; i > 0; i--) { 778 var ancestor = this.openElements.items[i], 779 tn = this.treeAdapter.getTagName(ancestor); 780 781 if (tn === $.TEMPLATE) 782 break; 783 784 else if (tn === $.TABLE) { 785 this.insertionMode = IN_SELECT_IN_TABLE_MODE; 786 return; 787 } 788 } 789 } 790 791 this.insertionMode = IN_SELECT_MODE; 792}; 793 794Parser.prototype._pushTmplInsertionMode = function (mode) { 795 this.tmplInsertionModeStack.push(mode); 796 this.tmplInsertionModeStackTop++; 797 this.currentTmplInsertionMode = mode; 798}; 799 800Parser.prototype._popTmplInsertionMode = function () { 801 this.tmplInsertionModeStack.pop(); 802 this.tmplInsertionModeStackTop--; 803 this.currentTmplInsertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStackTop]; 804}; 805 806//Foster parenting 807Parser.prototype._isElementCausesFosterParenting = function (element) { 808 var tn = this.treeAdapter.getTagName(element); 809 810 return tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn == $.THEAD || tn === $.TR; 811}; 812 813Parser.prototype._shouldFosterParentOnInsertion = function () { 814 return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.current); 815}; 816 817Parser.prototype._findFosterParentingLocation = function () { 818 var location = { 819 parent: null, 820 beforeElement: null 821 }; 822 823 for (var i = this.openElements.stackTop; i >= 0; i--) { 824 var openElement = this.openElements.items[i], 825 tn = this.treeAdapter.getTagName(openElement), 826 ns = this.treeAdapter.getNamespaceURI(openElement); 827 828 if (tn === $.TEMPLATE && ns === NS.HTML) { 829 location.parent = this.treeAdapter.getChildNodes(openElement)[0]; 830 break; 831 } 832 833 else if (tn === $.TABLE) { 834 location.parent = this.treeAdapter.getParentNode(openElement); 835 836 if (location.parent) 837 location.beforeElement = openElement; 838 else 839 location.parent = this.openElements.items[i - 1]; 840 841 break; 842 } 843 } 844 845 if (!location.parent) 846 location.parent = this.openElements.items[0]; 847 848 return location; 849}; 850 851Parser.prototype._fosterParentElement = function (element) { 852 var location = this._findFosterParentingLocation(); 853 854 if (location.beforeElement) 855 this.treeAdapter.insertBefore(location.parent, element, location.beforeElement); 856 else 857 this.treeAdapter.appendChild(location.parent, element); 858}; 859 860Parser.prototype._fosterParentText = function (chars) { 861 var location = this._findFosterParentingLocation(); 862 863 if (location.beforeElement) 864 this.treeAdapter.insertTextBefore(location.parent, chars, location.beforeElement); 865 else 866 this.treeAdapter.insertText(location.parent, chars); 867}; 868 869//Special elements 870Parser.prototype._isSpecialElement = function (element) { 871 var tn = this.treeAdapter.getTagName(element), 872 ns = this.treeAdapter.getNamespaceURI(element); 873 874 return HTML.SPECIAL_ELEMENTS[ns][tn]; 875}; 876 877//Adoption agency algorithm 878//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency) 879//------------------------------------------------------------------ 880 881//Steps 5-8 of the algorithm 882function aaObtainFormattingElementEntry(p, token) { 883 var formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName); 884 885 if (formattingElementEntry) { 886 if (!p.openElements.contains(formattingElementEntry.element)) { 887 p.activeFormattingElements.removeEntry(formattingElementEntry); 888 formattingElementEntry = null; 889 } 890 891 else if (!p.openElements.hasInScope(token.tagName)) 892 formattingElementEntry = null; 893 } 894 895 else 896 genericEndTagInBody(p, token); 897 898 return formattingElementEntry; 899} 900 901//Steps 9 and 10 of the algorithm 902function aaObtainFurthestBlock(p, formattingElementEntry) { 903 var furthestBlock = null; 904 905 for (var i = p.openElements.stackTop; i >= 0; i--) { 906 var element = p.openElements.items[i]; 907 908 if (element === formattingElementEntry.element) 909 break; 910 911 if (p._isSpecialElement(element)) 912 furthestBlock = element; 913 } 914 915 if (!furthestBlock) { 916 p.openElements.popUntilElementPopped(formattingElementEntry.element); 917 p.activeFormattingElements.removeEntry(formattingElementEntry); 918 } 919 920 return furthestBlock; 921} 922 923//Step 13 of the algorithm 924function aaInnerLoop(p, furthestBlock, formattingElement) { 925 var element = null, 926 lastElement = furthestBlock, 927 nextElement = p.openElements.getCommonAncestor(furthestBlock); 928 929 for (var i = 0; i < AA_INNER_LOOP_ITER; i++) { 930 element = nextElement; 931 932 //NOTE: store next element for the next loop iteration (it may be deleted from the stack by step 9.5) 933 nextElement = p.openElements.getCommonAncestor(element); 934 935 var elementEntry = p.activeFormattingElements.getElementEntry(element); 936 937 if (!elementEntry) { 938 p.openElements.remove(element); 939 continue; 940 } 941 942 if (element === formattingElement) 943 break; 944 945 element = aaRecreateElementFromEntry(p, elementEntry); 946 947 if (lastElement === furthestBlock) 948 p.activeFormattingElements.bookmark = elementEntry; 949 950 p.treeAdapter.detachNode(lastElement); 951 p.treeAdapter.appendChild(element, lastElement); 952 lastElement = element; 953 } 954 955 return lastElement; 956} 957 958//Step 13.7 of the algorithm 959function aaRecreateElementFromEntry(p, elementEntry) { 960 var ns = p.treeAdapter.getNamespaceURI(elementEntry.element), 961 newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs); 962 963 p.openElements.replace(elementEntry.element, newElement); 964 elementEntry.element = newElement; 965 966 return newElement; 967} 968 969//Step 14 of the algorithm 970function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) { 971 if (p._isElementCausesFosterParenting(commonAncestor)) 972 p._fosterParentElement(lastElement); 973 974 else { 975 var tn = p.treeAdapter.getTagName(commonAncestor), 976 ns = p.treeAdapter.getNamespaceURI(commonAncestor); 977 978 if (tn === $.TEMPLATE && ns === NS.HTML) 979 commonAncestor = p.treeAdapter.getChildNodes(commonAncestor)[0]; 980 981 p.treeAdapter.appendChild(commonAncestor, lastElement); 982 } 983} 984 985//Steps 15-19 of the algorithm 986function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) { 987 var ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element), 988 token = formattingElementEntry.token, 989 newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs); 990 991 p._adoptNodes(furthestBlock, newElement); 992 p.treeAdapter.appendChild(furthestBlock, newElement); 993 994 p.activeFormattingElements.insertElementAfterBookmark(newElement, formattingElementEntry.token); 995 p.activeFormattingElements.removeEntry(formattingElementEntry); 996 997 p.openElements.remove(formattingElementEntry.element); 998 p.openElements.insertAfter(furthestBlock, newElement); 999} 1000 1001//Algorithm entry point 1002function callAdoptionAgency(p, token) { 1003 for (var i = 0; i < AA_OUTER_LOOP_ITER; i++) { 1004 var formattingElementEntry = aaObtainFormattingElementEntry(p, token, formattingElementEntry); 1005 1006 if (!formattingElementEntry) 1007 break; 1008 1009 var furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry); 1010 1011 if (!furthestBlock) 1012 break; 1013 1014 p.activeFormattingElements.bookmark = formattingElementEntry; 1015 1016 var lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element), 1017 commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element); 1018 1019 p.treeAdapter.detachNode(lastElement); 1020 aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement); 1021 aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry); 1022 } 1023} 1024 1025 1026//Generic token handlers 1027//------------------------------------------------------------------ 1028function ignoreToken(p, token) { 1029 //NOTE: do nothing =) 1030} 1031 1032function appendComment(p, token) { 1033 p._appendCommentNode(token, p.openElements.currentTmplContent || p.openElements.current) 1034} 1035 1036function appendCommentToRootHtmlElement(p, token) { 1037 p._appendCommentNode(token, p.openElements.items[0]); 1038} 1039 1040function appendCommentToDocument(p, token) { 1041 p._appendCommentNode(token, p.document); 1042} 1043 1044function insertCharacters(p, token) { 1045 p._insertCharacters(token); 1046} 1047 1048function stopParsing(p, token) { 1049 p.stopped = true; 1050} 1051 1052//12.2.5.4.1 The "initial" insertion mode 1053//------------------------------------------------------------------ 1054function doctypeInInitialMode(p, token) { 1055 p._setDocumentType(token); 1056 1057 if (token.forceQuirks || Doctype.isQuirks(token.name, token.publicId, token.systemId)) 1058 p.treeAdapter.setQuirksMode(p.document); 1059 1060 p.insertionMode = BEFORE_HTML_MODE; 1061} 1062 1063function tokenInInitialMode(p, token) { 1064 p.treeAdapter.setQuirksMode(p.document); 1065 p.insertionMode = BEFORE_HTML_MODE; 1066 p._processToken(token); 1067} 1068 1069 1070//12.2.5.4.2 The "before html" insertion mode 1071//------------------------------------------------------------------ 1072function startTagBeforeHtml(p, token) { 1073 if (token.tagName === $.HTML) { 1074 p._insertElement(token, NS.HTML); 1075 p.insertionMode = BEFORE_HEAD_MODE; 1076 } 1077 1078 else 1079 tokenBeforeHtml(p, token); 1080} 1081 1082function endTagBeforeHtml(p, token) { 1083 var tn = token.tagName; 1084 1085 if (tn === $.HTML || tn === $.HEAD || tn === $.BODY || tn === $.BR) 1086 tokenBeforeHtml(p, token); 1087} 1088 1089function tokenBeforeHtml(p, token) { 1090 p._insertFakeRootElement(); 1091 p.insertionMode = BEFORE_HEAD_MODE; 1092 p._processToken(token); 1093} 1094 1095 1096//12.2.5.4.3 The "before head" insertion mode 1097//------------------------------------------------------------------ 1098function startTagBeforeHead(p, token) { 1099 var tn = token.tagName; 1100 1101 if (tn === $.HTML) 1102 startTagInBody(p, token); 1103 1104 else if (tn === $.HEAD) { 1105 p._insertElement(token, NS.HTML); 1106 p.headElement = p.openElements.current; 1107 p.insertionMode = IN_HEAD_MODE; 1108 } 1109 1110 else 1111 tokenBeforeHead(p, token); 1112} 1113 1114function endTagBeforeHead(p, token) { 1115 var tn = token.tagName; 1116 1117 if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) 1118 tokenBeforeHead(p, token); 1119} 1120 1121function tokenBeforeHead(p, token) { 1122 p._processFakeStartTag($.HEAD); 1123 p._processToken(token); 1124} 1125 1126 1127//12.2.5.4.4 The "in head" insertion mode 1128//------------------------------------------------------------------ 1129function startTagInHead(p, token) { 1130 var tn = token.tagName; 1131 1132 if (tn === $.HTML) 1133 startTagInBody(p, token); 1134 1135 else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || 1136 tn === $.COMMAND || tn === $.LINK || tn === $.META) { 1137 p._appendElement(token, NS.HTML); 1138 } 1139 1140 else if (tn === $.TITLE) 1141 p._switchToTextParsing(token, Tokenizer.MODE.RCDATA); 1142 1143 //NOTE: here we assume that we always act as an interactive user agent with enabled scripting, so we parse 1144 //<noscript> as a rawtext. 1145 else if (tn === $.NOSCRIPT || tn === $.NOFRAMES || tn === $.STYLE) 1146 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); 1147 1148 else if (tn === $.SCRIPT) 1149 p._switchToTextParsing(token, Tokenizer.MODE.SCRIPT_DATA); 1150 1151 else if (tn === $.TEMPLATE) { 1152 p._insertTemplate(token, NS.HTML); 1153 p.activeFormattingElements.insertMarker(); 1154 p.framesetOk = false; 1155 p.insertionMode = IN_TEMPLATE_MODE; 1156 p._pushTmplInsertionMode(IN_TEMPLATE_MODE); 1157 } 1158 1159 else if (tn !== $.HEAD) 1160 tokenInHead(p, token); 1161} 1162 1163function endTagInHead(p, token) { 1164 var tn = token.tagName; 1165 1166 if (tn === $.HEAD) { 1167 p.openElements.pop(); 1168 p.insertionMode = AFTER_HEAD_MODE; 1169 } 1170 1171 else if (tn === $.BODY || tn === $.BR || tn === $.HTML) 1172 tokenInHead(p, token); 1173 1174 else if (tn === $.TEMPLATE && p.openElements.tmplCount > 0) { 1175 p.openElements.generateImpliedEndTags(); 1176 p.openElements.popUntilTemplatePopped(); 1177 p.activeFormattingElements.clearToLastMarker(); 1178 p._popTmplInsertionMode(); 1179 p._resetInsertionMode(); 1180 } 1181} 1182 1183function tokenInHead(p, token) { 1184 p._processFakeEndTag($.HEAD); 1185 p._processToken(token); 1186} 1187 1188 1189//12.2.5.4.6 The "after head" insertion mode 1190//------------------------------------------------------------------ 1191function startTagAfterHead(p, token) { 1192 var tn = token.tagName; 1193 1194 if (tn === $.HTML) 1195 startTagInBody(p, token); 1196 1197 else if (tn === $.BODY) { 1198 p._insertElement(token, NS.HTML); 1199 p.framesetOk = false; 1200 p.insertionMode = IN_BODY_MODE; 1201 } 1202 1203 else if (tn === $.FRAMESET) { 1204 p._insertElement(token, NS.HTML); 1205 p.insertionMode = IN_FRAMESET_MODE; 1206 } 1207 1208 else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META || 1209 tn === $.NOFRAMES || tn === $.SCRIPT || tn === $.STYLE || tn === $.TEMPLATE || tn === $.TITLE) { 1210 p.openElements.push(p.headElement); 1211 startTagInHead(p, token); 1212 p.openElements.remove(p.headElement); 1213 } 1214 1215 else if (tn !== $.HEAD) 1216 tokenAfterHead(p, token); 1217} 1218 1219function endTagAfterHead(p, token) { 1220 var tn = token.tagName; 1221 1222 if (tn === $.BODY || tn === $.HTML || tn === $.BR) 1223 tokenAfterHead(p, token); 1224 1225 else if (tn === $.TEMPLATE) 1226 endTagInHead(p, token); 1227} 1228 1229function tokenAfterHead(p, token) { 1230 p._processFakeStartTag($.BODY); 1231 p.framesetOk = true; 1232 p._processToken(token); 1233} 1234 1235 1236//12.2.5.4.7 The "in body" insertion mode 1237//------------------------------------------------------------------ 1238function whitespaceCharacterInBody(p, token) { 1239 p._reconstructActiveFormattingElements(); 1240 p._insertCharacters(token); 1241} 1242 1243function characterInBody(p, token) { 1244 p._reconstructActiveFormattingElements(); 1245 p._insertCharacters(token); 1246 p.framesetOk = false; 1247} 1248 1249function htmlStartTagInBody(p, token) { 1250 if (p.openElements.tmplCount === 0) 1251 p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs); 1252} 1253 1254function bodyStartTagInBody(p, token) { 1255 var bodyElement = p.openElements.tryPeekProperlyNestedBodyElement(); 1256 1257 if (bodyElement && p.openElements.tmplCount === 0) { 1258 p.framesetOk = false; 1259 p.treeAdapter.adoptAttributes(bodyElement, token.attrs); 1260 } 1261} 1262 1263function framesetStartTagInBody(p, token) { 1264 var bodyElement = p.openElements.tryPeekProperlyNestedBodyElement(); 1265 1266 if (p.framesetOk && bodyElement) { 1267 p.treeAdapter.detachNode(bodyElement); 1268 p.openElements.popAllUpToHtmlElement(); 1269 p._insertElement(token, NS.HTML); 1270 p.insertionMode = IN_FRAMESET_MODE; 1271 } 1272} 1273 1274function addressStartTagInBody(p, token) { 1275 if (p.openElements.hasInButtonScope($.P)) 1276 p._closePElement(); 1277 1278 p._insertElement(token, NS.HTML); 1279} 1280 1281function numberedHeaderStartTagInBody(p, token) { 1282 if (p.openElements.hasInButtonScope($.P)) 1283 p._closePElement(); 1284 1285 var tn = p.openElements.currentTagName; 1286 1287 if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) 1288 p.openElements.pop(); 1289 1290 p._insertElement(token, NS.HTML); 1291} 1292 1293function preStartTagInBody(p, token) { 1294 if (p.openElements.hasInButtonScope($.P)) 1295 p._closePElement(); 1296 1297 p._insertElement(token, NS.HTML); 1298 //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move 1299 //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.) 1300 p.skipNextNewLine = true; 1301 p.framesetOk = false; 1302} 1303 1304function formStartTagInBody(p, token) { 1305 var inTemplate = p.openElements.tmplCount > 0; 1306 1307 if (!p.formElement || inTemplate) { 1308 if (p.openElements.hasInButtonScope($.P)) 1309 p._closePElement(); 1310 1311 p._insertElement(token, NS.HTML); 1312 1313 if (!inTemplate) 1314 p.formElement = p.openElements.current; 1315 } 1316} 1317 1318function listItemStartTagInBody(p, token) { 1319 p.framesetOk = false; 1320 1321 for (var i = p.openElements.stackTop; i >= 0; i--) { 1322 var element = p.openElements.items[i], 1323 tn = p.treeAdapter.getTagName(element); 1324 1325 if ((token.tagName === $.LI && tn === $.LI) || 1326 ((token.tagName === $.DD || token.tagName === $.DT) && (tn === $.DD || tn == $.DT))) { 1327 p._processFakeEndTag(tn); 1328 break; 1329 } 1330 1331 if (tn !== $.ADDRESS && tn !== $.DIV && tn !== $.P && p._isSpecialElement(element)) 1332 break; 1333 } 1334 1335 if (p.openElements.hasInButtonScope($.P)) 1336 p._closePElement(); 1337 1338 p._insertElement(token, NS.HTML); 1339} 1340 1341function plaintextStartTagInBody(p, token) { 1342 if (p.openElements.hasInButtonScope($.P)) 1343 p._closePElement(); 1344 1345 p._insertElement(token, NS.HTML); 1346 p.tokenizer.state = Tokenizer.MODE.PLAINTEXT; 1347} 1348 1349function buttonStartTagInBody(p, token) { 1350 if (p.openElements.hasInScope($.BUTTON)) { 1351 p._processFakeEndTag($.BUTTON); 1352 buttonStartTagInBody(p, token); 1353 } 1354 1355 else { 1356 p._reconstructActiveFormattingElements(); 1357 p._insertElement(token, NS.HTML); 1358 p.framesetOk = false; 1359 } 1360} 1361 1362function aStartTagInBody(p, token) { 1363 var activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName($.A); 1364 1365 if (activeElementEntry) { 1366 p._processFakeEndTag($.A); 1367 p.openElements.remove(activeElementEntry.element); 1368 p.activeFormattingElements.removeEntry(activeElementEntry); 1369 } 1370 1371 p._reconstructActiveFormattingElements(); 1372 p._insertElement(token, NS.HTML); 1373 p.activeFormattingElements.pushElement(p.openElements.current, token); 1374} 1375 1376function bStartTagInBody(p, token) { 1377 p._reconstructActiveFormattingElements(); 1378 p._insertElement(token, NS.HTML); 1379 p.activeFormattingElements.pushElement(p.openElements.current, token); 1380} 1381 1382function nobrStartTagInBody(p, token) { 1383 p._reconstructActiveFormattingElements(); 1384 1385 if (p.openElements.hasInScope($.NOBR)) { 1386 p._processFakeEndTag($.NOBR); 1387 p._reconstructActiveFormattingElements(); 1388 } 1389 1390 p._insertElement(token, NS.HTML); 1391 p.activeFormattingElements.pushElement(p.openElements.current, token); 1392} 1393 1394function appletStartTagInBody(p, token) { 1395 p._reconstructActiveFormattingElements(); 1396 p._insertElement(token, NS.HTML); 1397 p.activeFormattingElements.insertMarker(); 1398 p.framesetOk = false; 1399} 1400 1401function tableStartTagInBody(p, token) { 1402 if (!p.treeAdapter.isQuirksMode(p.document) && p.openElements.hasInButtonScope($.P)) 1403 p._closePElement(); 1404 1405 p._insertElement(token, NS.HTML); 1406 p.framesetOk = false; 1407 p.insertionMode = IN_TABLE_MODE; 1408} 1409 1410function areaStartTagInBody(p, token) { 1411 p._reconstructActiveFormattingElements(); 1412 p._appendElement(token, NS.HTML); 1413 p.framesetOk = false; 1414} 1415 1416function inputStartTagInBody(p, token) { 1417 p._reconstructActiveFormattingElements(); 1418 p._appendElement(token, NS.HTML); 1419 1420 var inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE); 1421 1422 if (!inputType || inputType.toLowerCase() !== HIDDEN_INPUT_TYPE) 1423 p.framesetOk = false; 1424 1425} 1426 1427function paramStartTagInBody(p, token) { 1428 p._appendElement(token, NS.HTML); 1429} 1430 1431function hrStartTagInBody(p, token) { 1432 if (p.openElements.hasInButtonScope($.P)) 1433 p._closePElement(); 1434 1435 p._appendElement(token, NS.HTML); 1436 p.framesetOk = false; 1437} 1438 1439function imageStartTagInBody(p, token) { 1440 token.tagName = $.IMG; 1441 areaStartTagInBody(p, token); 1442} 1443 1444function isindexStartTagInBody(p, token) { 1445 if (!p.formElement || p.openElements.tmplCount > 0) { 1446 p._processFakeStartTagWithAttrs($.FORM, getSearchableIndexFormAttrs(token)); 1447 p._processFakeStartTag($.HR); 1448 p._processFakeStartTag($.LABEL); 1449 p.treeAdapter.insertText(p.openElements.current, getSearchableIndexLabelText(token)); 1450 p._processFakeStartTagWithAttrs($.INPUT, getSearchableIndexInputAttrs(token)); 1451 p._processFakeEndTag($.LABEL); 1452 p._processFakeStartTag($.HR); 1453 p._processFakeEndTag($.FORM); 1454 } 1455} 1456 1457function textareaStartTagInBody(p, token) { 1458 p._insertElement(token, NS.HTML); 1459 //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move 1460 //on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.) 1461 p.skipNextNewLine = true; 1462 p.tokenizer.state = Tokenizer.MODE.RCDATA; 1463 p.originalInsertionMode = p.insertionMode; 1464 p.framesetOk = false; 1465 p.insertionMode = TEXT_MODE; 1466} 1467 1468function xmpStartTagInBody(p, token) { 1469 if (p.openElements.hasInButtonScope($.P)) 1470 p._closePElement(); 1471 1472 p._reconstructActiveFormattingElements(); 1473 p.framesetOk = false; 1474 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); 1475} 1476 1477function iframeStartTagInBody(p, token) { 1478 p.framesetOk = false; 1479 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); 1480} 1481 1482//NOTE: here we assume that we always act as an user agent with enabled plugins, so we parse 1483//<noembed> as a rawtext. 1484function noembedStartTagInBody(p, token) { 1485 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT); 1486} 1487 1488function selectStartTagInBody(p, token) { 1489 p._reconstructActiveFormattingElements(); 1490 p._insertElement(token, NS.HTML); 1491 p.framesetOk = false; 1492 1493 if (p.insertionMode === IN_TABLE_MODE || p.insertionMode === IN_CAPTION_MODE || 1494 p.insertionMode === IN_TABLE_BODY_MODE || p.insertionMode === IN_ROW_MODE || 1495 p.insertionMode === IN_CELL_MODE) { 1496 p.insertionMode = IN_SELECT_IN_TABLE_MODE; 1497 } 1498 1499 else 1500 p.insertionMode = IN_SELECT_MODE; 1501} 1502 1503function optgroupStartTagInBody(p, token) { 1504 if (p.openElements.currentTagName === $.OPTION) 1505 p._processFakeEndTag($.OPTION); 1506 1507 p._reconstructActiveFormattingElements(); 1508 p._insertElement(token, NS.HTML); 1509} 1510 1511function rpStartTagInBody(p, token) { 1512 if (p.openElements.hasInScope($.RUBY)) 1513 p.openElements.generateImpliedEndTags(); 1514 1515 p._insertElement(token, NS.HTML); 1516} 1517 1518function menuitemStartTagInBody(p, token) { 1519 p._appendElement(token, NS.HTML); 1520} 1521 1522function mathStartTagInBody(p, token) { 1523 p._reconstructActiveFormattingElements(); 1524 1525 ForeignContent.adjustTokenMathMLAttrs(token); 1526 ForeignContent.adjustTokenXMLAttrs(token); 1527 1528 if (token.selfClosing) 1529 p._appendElement(token, NS.MATHML); 1530 else 1531 p._insertElement(token, NS.MATHML); 1532} 1533 1534function svgStartTagInBody(p, token) { 1535 p._reconstructActiveFormattingElements(); 1536 1537 ForeignContent.adjustTokenSVGAttrs(token); 1538 ForeignContent.adjustTokenXMLAttrs(token); 1539 1540 if (token.selfClosing) 1541 p._appendElement(token, NS.SVG); 1542 else 1543 p._insertElement(token, NS.SVG); 1544} 1545 1546function genericStartTagInBody(p, token) { 1547 p._reconstructActiveFormattingElements(); 1548 p._insertElement(token, NS.HTML); 1549} 1550 1551//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here. 1552//It's faster than using dictionary. 1553function startTagInBody(p, token) { 1554 var tn = token.tagName; 1555 1556 switch (tn.length) { 1557 case 1: 1558 if (tn === $.I || tn === $.S || tn === $.B || tn === $.U) 1559 bStartTagInBody(p, token); 1560 1561 else if (tn === $.P) 1562 addressStartTagInBody(p, token); 1563 1564 else if (tn === $.A) 1565 aStartTagInBody(p, token); 1566 1567 else 1568 genericStartTagInBody(p, token); 1569 1570 break; 1571 1572 case 2: 1573 if (tn === $.DL || tn === $.OL || tn === $.UL) 1574 addressStartTagInBody(p, token); 1575 1576 else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) 1577 numberedHeaderStartTagInBody(p, token); 1578 1579 else if (tn === $.LI || tn === $.DD || tn === $.DT) 1580 listItemStartTagInBody(p, token); 1581 1582 else if (tn === $.EM || tn === $.TT) 1583 bStartTagInBody(p, token); 1584 1585 else if (tn === $.BR) 1586 areaStartTagInBody(p, token); 1587 1588 else if (tn === $.HR) 1589 hrStartTagInBody(p, token); 1590 1591 else if (tn === $.RP || tn === $.RT) 1592 rpStartTagInBody(p, token); 1593 1594 else if (tn !== $.TH && tn !== $.TD && tn !== $.TR) 1595 genericStartTagInBody(p, token); 1596 1597 break; 1598 1599 case 3: 1600 if (tn === $.DIV || tn === $.DIR || tn === $.NAV) 1601 addressStartTagInBody(p, token); 1602 1603 else if (tn === $.PRE) 1604 preStartTagInBody(p, token); 1605 1606 else if (tn === $.BIG) 1607 bStartTagInBody(p, token); 1608 1609 else if (tn === $.IMG || tn === $.WBR) 1610 areaStartTagInBody(p, token); 1611 1612 else if (tn === $.XMP) 1613 xmpStartTagInBody(p, token); 1614 1615 else if (tn === $.SVG) 1616 svgStartTagInBody(p, token); 1617 1618 else if (tn !== $.COL) 1619 genericStartTagInBody(p, token); 1620 1621 break; 1622 1623 case 4: 1624 if (tn === $.HTML) 1625 htmlStartTagInBody(p, token); 1626 1627 else if (tn === $.BASE || tn === $.LINK || tn === $.META) 1628 startTagInHead(p, token); 1629 1630 else if (tn === $.BODY) 1631 bodyStartTagInBody(p, token); 1632 1633 else if (tn === $.MAIN || tn === $.MENU) 1634 addressStartTagInBody(p, token); 1635 1636 else if (tn === $.FORM) 1637 formStartTagInBody(p, token); 1638 1639 else if (tn === $.CODE || tn === $.FONT) 1640 bStartTagInBody(p, token); 1641 1642 else if (tn === $.NOBR) 1643 nobrStartTagInBody(p, token); 1644 1645 else if (tn === $.AREA) 1646 areaStartTagInBody(p, token); 1647 1648 else if (tn === $.MATH) 1649 mathStartTagInBody(p, token); 1650 1651 else if (tn !== $.HEAD) 1652 genericStartTagInBody(p, token); 1653 1654 break; 1655 1656 case 5: 1657 if (tn === $.STYLE || tn === $.TITLE) 1658 startTagInHead(p, token); 1659 1660 else if (tn === $.ASIDE) 1661 addressStartTagInBody(p, token); 1662 1663 else if (tn === $.SMALL) 1664 bStartTagInBody(p, token); 1665 1666 else if (tn === $.TABLE) 1667 tableStartTagInBody(p, token); 1668 1669 else if (tn === $.EMBED) 1670 areaStartTagInBody(p, token); 1671 1672 else if (tn === $.INPUT) 1673 inputStartTagInBody(p, token); 1674 1675 else if (tn === $.PARAM || tn === $.TRACK) 1676 paramStartTagInBody(p, token); 1677 1678 else if (tn === $.IMAGE) 1679 imageStartTagInBody(p, token); 1680 1681 else if (tn !== $.FRAME && tn !== $.TBODY && tn !== $.TFOOT && tn !== $.THEAD) 1682 genericStartTagInBody(p, token); 1683 1684 break; 1685 1686 case 6: 1687 if (tn === $.SCRIPT) 1688 startTagInHead(p, token); 1689 1690 else if (tn === $.CENTER || tn === $.FIGURE || tn === $.FOOTER || tn === $.HEADER || tn === $.HGROUP) 1691 addressStartTagInBody(p, token); 1692 1693 else if (tn === $.BUTTON) 1694 buttonStartTagInBody(p, token); 1695 1696 else if (tn === $.STRIKE || tn === $.STRONG) 1697 bStartTagInBody(p, token); 1698 1699 else if (tn === $.APPLET || tn === $.OBJECT) 1700 appletStartTagInBody(p, token); 1701 1702 else if (tn === $.KEYGEN) 1703 areaStartTagInBody(p, token); 1704 1705 else if (tn === $.SOURCE) 1706 paramStartTagInBody(p, token); 1707 1708 else if (tn === $.IFRAME) 1709 iframeStartTagInBody(p, token); 1710 1711 else if (tn === $.SELECT) 1712 selectStartTagInBody(p, token); 1713 1714 else if (tn === $.OPTION) 1715 optgroupStartTagInBody(p, token); 1716 1717 else 1718 genericStartTagInBody(p, token); 1719 1720 break; 1721 1722 case 7: 1723 if (tn === $.BGSOUND || tn === $.COMMAND) 1724 startTagInHead(p, token); 1725 1726 else if (tn === $.DETAILS || tn === $.ADDRESS || tn === $.ARTICLE || tn === $.SECTION || tn === $.SUMMARY) 1727 addressStartTagInBody(p, token); 1728 1729 else if (tn === $.LISTING) 1730 preStartTagInBody(p, token); 1731 1732 else if (tn === $.MARQUEE) 1733 appletStartTagInBody(p, token); 1734 1735 else if (tn === $.ISINDEX) 1736 isindexStartTagInBody(p, token); 1737 1738 else if (tn === $.NOEMBED) 1739 noembedStartTagInBody(p, token); 1740 1741 else if (tn !== $.CAPTION) 1742 genericStartTagInBody(p, token); 1743 1744 break; 1745 1746 case 8: 1747 if (tn === $.BASEFONT || tn === $.MENUITEM) 1748 menuitemStartTagInBody(p, token); 1749 1750 else if (tn === $.FRAMESET) 1751 framesetStartTagInBody(p, token); 1752 1753 else if (tn === $.FIELDSET) 1754 addressStartTagInBody(p, token); 1755 1756 else if (tn === $.TEXTAREA) 1757 textareaStartTagInBody(p, token); 1758 1759 else if (tn === $.TEMPLATE) 1760 startTagInHead(p, token); 1761 1762 else if (tn === $.NOSCRIPT) 1763 noembedStartTagInBody(p, token); 1764 1765 else if (tn === $.OPTGROUP) 1766 optgroupStartTagInBody(p, token); 1767 1768 else if (tn !== $.COLGROUP) 1769 genericStartTagInBody(p, token); 1770 1771 break; 1772 1773 case 9: 1774 if (tn === $.PLAINTEXT) 1775 plaintextStartTagInBody(p, token); 1776 1777 else 1778 genericStartTagInBody(p, token); 1779 1780 break; 1781 1782 case 10: 1783 if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) 1784 addressStartTagInBody(p, token); 1785 1786 else 1787 genericStartTagInBody(p, token); 1788 1789 break; 1790 1791 default: 1792 genericStartTagInBody(p, token); 1793 } 1794} 1795 1796function bodyEndTagInBody(p, token) { 1797 if (p.openElements.hasInScope($.BODY)) 1798 p.insertionMode = AFTER_BODY_MODE; 1799 1800 else 1801 token.ignored = true; 1802} 1803 1804function htmlEndTagInBody(p, token) { 1805 var fakeToken = p._processFakeEndTag($.BODY); 1806 1807 if (!fakeToken.ignored) 1808 p._processToken(token); 1809} 1810 1811function addressEndTagInBody(p, token) { 1812 var tn = token.tagName; 1813 1814 if (p.openElements.hasInScope(tn)) { 1815 p.openElements.generateImpliedEndTags(); 1816 p.openElements.popUntilTagNamePopped(tn); 1817 } 1818} 1819 1820function formEndTagInBody(p, token) { 1821 var inTemplate = p.openElements.tmplCount > 0, 1822 formElement = p.formElement; 1823 1824 if (!inTemplate) 1825 p.formElement = null; 1826 1827 if ((formElement || inTemplate) && p.openElements.hasInScope($.FORM)) { 1828 p.openElements.generateImpliedEndTags(); 1829 1830 if (inTemplate) 1831 p.openElements.popUntilTagNamePopped($.FORM); 1832 1833 else 1834 p.openElements.remove(formElement); 1835 } 1836} 1837 1838function pEndTagInBody(p, token) { 1839 if (p.openElements.hasInButtonScope($.P)) { 1840 p.openElements.generateImpliedEndTagsWithExclusion($.P); 1841 p.openElements.popUntilTagNamePopped($.P); 1842 } 1843 1844 else { 1845 p._processFakeStartTag($.P); 1846 p._processToken(token); 1847 } 1848} 1849 1850function liEndTagInBody(p, token) { 1851 if (p.openElements.hasInListItemScope($.LI)) { 1852 p.openElements.generateImpliedEndTagsWithExclusion($.LI); 1853 p.openElements.popUntilTagNamePopped($.LI); 1854 } 1855} 1856 1857function ddEndTagInBody(p, token) { 1858 var tn = token.tagName; 1859 1860 if (p.openElements.hasInScope(tn)) { 1861 p.openElements.generateImpliedEndTagsWithExclusion(tn); 1862 p.openElements.popUntilTagNamePopped(tn); 1863 } 1864} 1865 1866function numberedHeaderEndTagInBody(p, token) { 1867 if (p.openElements.hasNumberedHeaderInScope()) { 1868 p.openElements.generateImpliedEndTags(); 1869 p.openElements.popUntilNumberedHeaderPopped(); 1870 } 1871} 1872 1873function appletEndTagInBody(p, token) { 1874 var tn = token.tagName; 1875 1876 if (p.openElements.hasInScope(tn)) { 1877 p.openElements.generateImpliedEndTags(); 1878 p.openElements.popUntilTagNamePopped(tn); 1879 p.activeFormattingElements.clearToLastMarker(); 1880 } 1881} 1882 1883function brEndTagInBody(p, token) { 1884 p._processFakeStartTag($.BR); 1885} 1886 1887function genericEndTagInBody(p, token) { 1888 var tn = token.tagName; 1889 1890 for (var i = p.openElements.stackTop; i > 0; i--) { 1891 var element = p.openElements.items[i]; 1892 1893 if (p.treeAdapter.getTagName(element) === tn) { 1894 p.openElements.generateImpliedEndTagsWithExclusion(tn); 1895 p.openElements.popUntilElementPopped(element); 1896 break; 1897 } 1898 1899 if (p._isSpecialElement(element)) 1900 break; 1901 } 1902} 1903 1904//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here. 1905//It's faster than using dictionary. 1906function endTagInBody(p, token) { 1907 var tn = token.tagName; 1908 1909 switch (tn.length) { 1910 case 1: 1911 if (tn === $.A || tn === $.B || tn === $.I || tn === $.S || tn == $.U) 1912 callAdoptionAgency(p, token); 1913 1914 else if (tn === $.P) 1915 pEndTagInBody(p, token); 1916 1917 else 1918 genericEndTagInBody(p, token); 1919 1920 break; 1921 1922 case 2: 1923 if (tn == $.DL || tn === $.UL || tn === $.OL) 1924 addressEndTagInBody(p, token); 1925 1926 else if (tn === $.LI) 1927 liEndTagInBody(p, token); 1928 1929 else if (tn === $.DD || tn === $.DT) 1930 ddEndTagInBody(p, token); 1931 1932 else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) 1933 numberedHeaderEndTagInBody(p, token); 1934 1935 else if (tn === $.BR) 1936 brEndTagInBody(p, token); 1937 1938 else if (tn === $.EM || tn === $.TT) 1939 callAdoptionAgency(p, token); 1940 1941 else 1942 genericEndTagInBody(p, token); 1943 1944 break; 1945 1946 case 3: 1947 if (tn === $.BIG) 1948 callAdoptionAgency(p, token); 1949 1950 else if (tn === $.DIR || tn === $.DIV || tn === $.NAV) 1951 addressEndTagInBody(p, token); 1952 1953 else 1954 genericEndTagInBody(p, token); 1955 1956 break; 1957 1958 case 4: 1959 if (tn === $.BODY) 1960 bodyEndTagInBody(p, token); 1961 1962 else if (tn === $.HTML) 1963 htmlEndTagInBody(p, token); 1964 1965 else if (tn === $.FORM) 1966 formEndTagInBody(p, token); 1967 1968 else if (tn === $.CODE || tn === $.FONT || tn === $.NOBR) 1969 callAdoptionAgency(p, token); 1970 1971 else if (tn === $.MAIN || tn === $.MENU) 1972 addressEndTagInBody(p, token); 1973 1974 else 1975 genericEndTagInBody(p, token); 1976 1977 break; 1978 1979 case 5: 1980 if (tn === $.ASIDE) 1981 addressEndTagInBody(p, token); 1982 1983 else if (tn === $.SMALL) 1984 callAdoptionAgency(p, token); 1985 1986 else 1987 genericEndTagInBody(p, token); 1988 1989 break; 1990 1991 case 6: 1992 if (tn === $.CENTER || tn === $.FIGURE || tn === $.FOOTER || tn === $.HEADER || tn === $.HGROUP) 1993 addressEndTagInBody(p, token); 1994 1995 else if (tn === $.APPLET || tn === $.OBJECT) 1996 appletEndTagInBody(p, token); 1997 1998 else if (tn == $.STRIKE || tn === $.STRONG) 1999 callAdoptionAgency(p, token); 2000 2001 else 2002 genericEndTagInBody(p, token); 2003 2004 break; 2005 2006 case 7: 2007 if (tn === $.ADDRESS || tn === $.ARTICLE || tn === $.DETAILS || tn === $.SECTION || tn === $.SUMMARY) 2008 addressEndTagInBody(p, token); 2009 2010 else if (tn === $.MARQUEE) 2011 appletEndTagInBody(p, token); 2012 2013 else 2014 genericEndTagInBody(p, token); 2015 2016 break; 2017 2018 case 8: 2019 if (tn === $.FIELDSET) 2020 addressEndTagInBody(p, token); 2021 2022 else if (tn === $.TEMPLATE) 2023 endTagInHead(p, token); 2024 2025 else 2026 genericEndTagInBody(p, token); 2027 2028 break; 2029 2030 case 10: 2031 if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) 2032 addressEndTagInBody(p, token); 2033 2034 else 2035 genericEndTagInBody(p, token); 2036 2037 break; 2038 2039 default : 2040 genericEndTagInBody(p, token); 2041 } 2042} 2043 2044function eofInBody(p, token) { 2045 if (p.tmplInsertionModeStackTop > -1) 2046 eofInTemplate(p, token); 2047 2048 else 2049 p.stopped = true; 2050} 2051 2052//12.2.5.4.8 The "text" insertion mode 2053//------------------------------------------------------------------ 2054function endTagInText(p, token) { 2055 if (!p.fragmentContext && p.scriptHandler && token.tagName === $.SCRIPT) 2056 p.scriptHandler(p.document, p.openElements.current); 2057 2058 p.openElements.pop(); 2059 p.insertionMode = p.originalInsertionMode; 2060} 2061 2062 2063function eofInText(p, token) { 2064 p.openElements.pop(); 2065 p.insertionMode = p.originalInsertionMode; 2066 p._processToken(token); 2067} 2068 2069 2070//12.2.5.4.9 The "in table" insertion mode 2071//------------------------------------------------------------------ 2072function characterInTable(p, token) { 2073 var curTn = p.openElements.currentTagName; 2074 2075 if (curTn === $.TABLE || curTn === $.TBODY || curTn === $.TFOOT || curTn === $.THEAD || curTn === $.TR) { 2076 p.pendingCharacterTokens = []; 2077 p.hasNonWhitespacePendingCharacterToken = false; 2078 p.originalInsertionMode = p.insertionMode; 2079 p.insertionMode = IN_TABLE_TEXT_MODE; 2080 p._processToken(token); 2081 } 2082 2083 else 2084 tokenInTable(p, token); 2085} 2086 2087function captionStartTagInTable(p, token) { 2088 p.openElements.clearBackToTableContext(); 2089 p.activeFormattingElements.insertMarker(); 2090 p._insertElement(token, NS.HTML); 2091 p.insertionMode = IN_CAPTION_MODE; 2092} 2093 2094function colgroupStartTagInTable(p, token) { 2095 p.openElements.clearBackToTableContext(); 2096 p._insertElement(token, NS.HTML); 2097 p.insertionMode = IN_COLUMN_GROUP_MODE; 2098} 2099 2100function colStartTagInTable(p, token) { 2101 p._processFakeStartTag($.COLGROUP); 2102 p._processToken(token); 2103} 2104 2105function tbodyStartTagInTable(p, token) { 2106 p.openElements.clearBackToTableContext(); 2107 p._insertElement(token, NS.HTML); 2108 p.insertionMode = IN_TABLE_BODY_MODE; 2109} 2110 2111function tdStartTagInTable(p, token) { 2112 p._processFakeStartTag($.TBODY); 2113 p._processToken(token); 2114} 2115 2116function tableStartTagInTable(p, token) { 2117 var fakeToken = p._processFakeEndTag($.TABLE); 2118 2119 //NOTE: The fake end tag token here can only be ignored in the fragment case. 2120 if (!fakeToken.ignored) 2121 p._processToken(token); 2122} 2123 2124function inputStartTagInTable(p, token) { 2125 var inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE); 2126 2127 if (inputType && inputType.toLowerCase() === HIDDEN_INPUT_TYPE) 2128 p._appendElement(token, NS.HTML); 2129 2130 else 2131 tokenInTable(p, token); 2132} 2133 2134function formStartTagInTable(p, token) { 2135 if (!p.formElement && p.openElements.tmplCount === 0) { 2136 p._insertElement(token, NS.HTML); 2137 p.formElement = p.openElements.current; 2138 p.openElements.pop(); 2139 } 2140} 2141 2142function startTagInTable(p, token) { 2143 var tn = token.tagName; 2144 2145 switch (tn.length) { 2146 case 2: 2147 if (tn === $.TD || tn === $.TH || tn === $.TR) 2148 tdStartTagInTable(p, token); 2149 2150 else 2151 tokenInTable(p, token); 2152 2153 break; 2154 2155 case 3: 2156 if (tn === $.COL) 2157 colStartTagInTable(p, token); 2158 2159 else 2160 tokenInTable(p, token); 2161 2162 break; 2163 2164 case 4: 2165 if (tn === $.FORM) 2166 formStartTagInTable(p, token); 2167 2168 else 2169 tokenInTable(p, token); 2170 2171 break; 2172 2173 case 5: 2174 if (tn === $.TABLE) 2175 tableStartTagInTable(p, token); 2176 2177 else if (tn === $.STYLE) 2178 startTagInHead(p, token); 2179 2180 else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) 2181 tbodyStartTagInTable(p, token); 2182 2183 else if (tn === $.INPUT) 2184 inputStartTagInTable(p, token); 2185 2186 else 2187 tokenInTable(p, token); 2188 2189 break; 2190 2191 case 6: 2192 if (tn === $.SCRIPT) 2193 startTagInHead(p, token); 2194 2195 else 2196 tokenInTable(p, token); 2197 2198 break; 2199 2200 case 7: 2201 if (tn === $.CAPTION) 2202 captionStartTagInTable(p, token); 2203 2204 else 2205 tokenInTable(p, token); 2206 2207 break; 2208 2209 case 8: 2210 if (tn === $.COLGROUP) 2211 colgroupStartTagInTable(p, token); 2212 2213 else if (tn === $.TEMPLATE) 2214 startTagInHead(p, token); 2215 2216 else 2217 tokenInTable(p, token); 2218 2219 break; 2220 2221 default: 2222 tokenInTable(p, token); 2223 } 2224 2225} 2226 2227function endTagInTable(p, token) { 2228 var tn = token.tagName; 2229 2230 if (tn === $.TABLE) { 2231 if (p.openElements.hasInTableScope($.TABLE)) { 2232 p.openElements.popUntilTagNamePopped($.TABLE); 2233 p._resetInsertionMode(); 2234 } 2235 2236 else 2237 token.ignored = true; 2238 } 2239 2240 else if (tn === $.TEMPLATE) 2241 endTagInHead(p, token); 2242 2243 else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML && 2244 tn !== $.TBODY && tn !== $.TD && tn !== $.TFOOT && tn !== $.TH && tn !== $.THEAD && tn !== $.TR) { 2245 tokenInTable(p, token); 2246 } 2247} 2248 2249function tokenInTable(p, token) { 2250 var savedFosterParentingState = p.fosterParentingEnabled; 2251 2252 p.fosterParentingEnabled = true; 2253 p._processTokenInBodyMode(token); 2254 p.fosterParentingEnabled = savedFosterParentingState; 2255} 2256 2257 2258//12.2.5.4.10 The "in table text" insertion mode 2259//------------------------------------------------------------------ 2260function whitespaceCharacterInTableText(p, token) { 2261 p.pendingCharacterTokens.push(token); 2262} 2263 2264function characterInTableText(p, token) { 2265 p.pendingCharacterTokens.push(token); 2266 p.hasNonWhitespacePendingCharacterToken = true; 2267} 2268 2269function tokenInTableText(p, token) { 2270 if (p.hasNonWhitespacePendingCharacterToken) { 2271 for (var i = 0; i < p.pendingCharacterTokens.length; i++) 2272 tokenInTable(p, p.pendingCharacterTokens[i]); 2273 } 2274 2275 else { 2276 for (var i = 0; i < p.pendingCharacterTokens.length; i++) 2277 p._insertCharacters(p.pendingCharacterTokens[i]); 2278 } 2279 2280 p.insertionMode = p.originalInsertionMode; 2281 p._processToken(token); 2282} 2283 2284 2285//12.2.5.4.11 The "in caption" insertion mode 2286//------------------------------------------------------------------ 2287function startTagInCaption(p, token) { 2288 var tn = token.tagName; 2289 2290 if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || tn === $.TBODY || 2291 tn === $.TD || tn === $.TFOOT || tn === $.TH || tn === $.THEAD || tn === $.TR) { 2292 var fakeToken = p._processFakeEndTag($.CAPTION); 2293 2294 //NOTE: The fake end tag token here can only be ignored in the fragment case. 2295 if (!fakeToken.ignored) 2296 p._processToken(token); 2297 } 2298 2299 else 2300 startTagInBody(p, token); 2301} 2302 2303function endTagInCaption(p, token) { 2304 var tn = token.tagName; 2305 2306 if (tn === $.CAPTION) { 2307 if (p.openElements.hasInTableScope($.CAPTION)) { 2308 p.openElements.generateImpliedEndTags(); 2309 p.openElements.popUntilTagNamePopped($.CAPTION); 2310 p.activeFormattingElements.clearToLastMarker(); 2311 p.insertionMode = IN_TABLE_MODE; 2312 } 2313 2314 else 2315 token.ignored = true; 2316 } 2317 2318 else if (tn === $.TABLE) { 2319 var fakeToken = p._processFakeEndTag($.CAPTION); 2320 2321 //NOTE: The fake end tag token here can only be ignored in the fragment case. 2322 if (!fakeToken.ignored) 2323 p._processToken(token); 2324 } 2325 2326 else if (tn !== $.BODY && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML && tn !== $.TBODY && 2327 tn !== $.TD && tn !== $.TFOOT && tn !== $.TH && tn !== $.THEAD && tn !== $.TR) { 2328 endTagInBody(p, token); 2329 } 2330} 2331 2332 2333//12.2.5.4.12 The "in column group" insertion mode 2334//------------------------------------------------------------------ 2335function startTagInColumnGroup(p, token) { 2336 var tn = token.tagName; 2337 2338 if (tn === $.HTML) 2339 startTagInBody(p, token); 2340 2341 else if (tn === $.COL) 2342 p._appendElement(token, NS.HTML); 2343 2344 else if (tn === $.TEMPLATE) 2345 startTagInHead(p, token); 2346 2347 else 2348 tokenInColumnGroup(p, token); 2349} 2350 2351function endTagInColumnGroup(p, token) { 2352 var tn = token.tagName; 2353 2354 if (tn === $.COLGROUP) { 2355 if (p.openElements.currentTagName !== $.COLGROUP) 2356 token.ignored = true; 2357 2358 else { 2359 p.openElements.pop(); 2360 p.insertionMode = IN_TABLE_MODE; 2361 } 2362 } 2363 2364 else if (tn === $.TEMPLATE) 2365 endTagInHead(p, token); 2366 2367 else if (tn !== $.COL) 2368 tokenInColumnGroup(p, token); 2369} 2370 2371function tokenInColumnGroup(p, token) { 2372 var fakeToken = p._processFakeEndTag($.COLGROUP); 2373 2374 //NOTE: The fake end tag token here can only be ignored in the fragment case. 2375 if (!fakeToken.ignored) 2376 p._processToken(token); 2377} 2378 2379//12.2.5.4.13 The "in table body" insertion mode 2380//------------------------------------------------------------------ 2381function startTagInTableBody(p, token) { 2382 var tn = token.tagName; 2383 2384 if (tn === $.TR) { 2385 p.openElements.clearBackToTableBodyContext(); 2386 p._insertElement(token, NS.HTML); 2387 p.insertionMode = IN_ROW_MODE; 2388 } 2389 2390 else if (tn === $.TH || tn === $.TD) { 2391 p._processFakeStartTag($.TR); 2392 p._processToken(token); 2393 } 2394 2395 else if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || 2396 tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) { 2397 2398 if (p.openElements.hasTableBodyContextInTableScope()) { 2399 p.openElements.clearBackToTableBodyContext(); 2400 p._processFakeEndTag(p.openElements.currentTagName); 2401 p._processToken(token); 2402 } 2403 } 2404 2405 else 2406 startTagInTable(p, token); 2407} 2408 2409function endTagInTableBody(p, token) { 2410 var tn = token.tagName; 2411 2412 if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) { 2413 if (p.openElements.hasInTableScope(tn)) { 2414 p.openElements.clearBackToTableBodyContext(); 2415 p.openElements.pop(); 2416 p.insertionMode = IN_TABLE_MODE; 2417 } 2418 } 2419 2420 else if (tn === $.TABLE) { 2421 if (p.openElements.hasTableBodyContextInTableScope()) { 2422 p.openElements.clearBackToTableBodyContext(); 2423 p._processFakeEndTag(p.openElements.currentTagName); 2424 p._processToken(token); 2425 } 2426 } 2427 2428 else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP || 2429 tn !== $.HTML && tn !== $.TD && tn !== $.TH && tn !== $.TR) { 2430 endTagInTable(p, token); 2431 } 2432} 2433 2434//12.2.5.4.14 The "in row" insertion mode 2435//------------------------------------------------------------------ 2436function startTagInRow(p, token) { 2437 var tn = token.tagName; 2438 2439 if (tn === $.TH || tn === $.TD) { 2440 p.openElements.clearBackToTableRowContext(); 2441 p._insertElement(token, NS.HTML); 2442 p.insertionMode = IN_CELL_MODE; 2443 p.activeFormattingElements.insertMarker(); 2444 } 2445 2446 else if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || tn === $.TBODY || 2447 tn === $.TFOOT || tn === $.THEAD || tn === $.TR) { 2448 var fakeToken = p._processFakeEndTag($.TR); 2449 2450 //NOTE: The fake end tag token here can only be ignored in the fragment case. 2451 if (!fakeToken.ignored) 2452 p._processToken(token); 2453 } 2454 2455 else 2456 startTagInTable(p, token); 2457} 2458 2459function endTagInRow(p, token) { 2460 var tn = token.tagName; 2461 2462 if (tn === $.TR) { 2463 if (p.openElements.hasInTableScope($.TR)) { 2464 p.openElements.clearBackToTableRowContext(); 2465 p.openElements.pop(); 2466 p.insertionMode = IN_TABLE_BODY_MODE; 2467 } 2468 2469 else 2470 token.ignored = true; 2471 } 2472 2473 else if (tn === $.TABLE) { 2474 var fakeToken = p._processFakeEndTag($.TR); 2475 2476 //NOTE: The fake end tag token here can only be ignored in the fragment case. 2477 if (!fakeToken.ignored) 2478 p._processToken(token); 2479 } 2480 2481 else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) { 2482 if (p.openElements.hasInTableScope(tn)) { 2483 p._processFakeEndTag($.TR); 2484 p._processToken(token); 2485 } 2486 } 2487 2488 else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP || 2489 tn !== $.HTML && tn !== $.TD && tn !== $.TH) { 2490 endTagInTable(p, token); 2491 } 2492} 2493 2494 2495//12.2.5.4.15 The "in cell" insertion mode 2496//------------------------------------------------------------------ 2497function startTagInCell(p, token) { 2498 var tn = token.tagName; 2499 2500 if (tn === $.CAPTION || tn === $.COL || tn === $.COLGROUP || tn === $.TBODY || 2501 tn === $.TD || tn === $.TFOOT || tn === $.TH || tn === $.THEAD || tn === $.TR) { 2502 2503 if (p.openElements.hasInTableScope($.TD) || p.openElements.hasInTableScope($.TH)) { 2504 p._closeTableCell(); 2505 p._processToken(token); 2506 } 2507 } 2508 2509 else 2510 startTagInBody(p, token); 2511} 2512 2513function endTagInCell(p, token) { 2514 var tn = token.tagName; 2515 2516 if (tn === $.TD || tn === $.TH) { 2517 if (p.openElements.hasInTableScope(tn)) { 2518 p.openElements.generateImpliedEndTags(); 2519 p.openElements.popUntilTagNamePopped(tn); 2520 p.activeFormattingElements.clearToLastMarker(); 2521 p.insertionMode = IN_ROW_MODE; 2522 } 2523 } 2524 2525 else if (tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR) { 2526 if (p.openElements.hasInTableScope(tn)) { 2527 p._closeTableCell(); 2528 p._processToken(token); 2529 } 2530 } 2531 2532 else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML) 2533 endTagInBody(p, token); 2534} 2535 2536//12.2.5.4.16 The "in select" insertion mode 2537//------------------------------------------------------------------ 2538function startTagInSelect(p, token) { 2539 var tn = token.tagName; 2540 2541 if (tn === $.HTML) 2542 startTagInBody(p, token); 2543 2544 else if (tn === $.OPTION) { 2545 if (p.openElements.currentTagName === $.OPTION) 2546 p._processFakeEndTag($.OPTION); 2547 2548 p._insertElement(token, NS.HTML); 2549 } 2550 2551 else if (tn === $.OPTGROUP) { 2552 if (p.openElements.currentTagName === $.OPTION) 2553 p._processFakeEndTag($.OPTION); 2554 2555 if (p.openElements.currentTagName === $.OPTGROUP) 2556 p._processFakeEndTag($.OPTGROUP); 2557 2558 p._insertElement(token, NS.HTML); 2559 } 2560 2561 else if (tn === $.SELECT) 2562 p._processFakeEndTag($.SELECT); 2563 2564 else if (tn === $.INPUT || tn === $.KEYGEN || tn === $.TEXTAREA) { 2565 if (p.openElements.hasInSelectScope($.SELECT)) { 2566 p._processFakeEndTag($.SELECT); 2567 p._processToken(token); 2568 } 2569 } 2570 2571 else if (tn === $.SCRIPT || tn === $.TEMPLATE) 2572 startTagInHead(p, token); 2573} 2574 2575function endTagInSelect(p, token) { 2576 var tn = token.tagName; 2577 2578 if (tn === $.OPTGROUP) { 2579 var prevOpenElement = p.openElements.items[p.openElements.stackTop - 1], 2580 prevOpenElementTn = prevOpenElement && p.treeAdapter.getTagName(prevOpenElement); 2581 2582 if (p.openElements.currentTagName === $.OPTION && prevOpenElementTn === $.OPTGROUP) 2583 p._processFakeEndTag($.OPTION); 2584 2585 if (p.openElements.currentTagName === $.OPTGROUP) 2586 p.openElements.pop(); 2587 } 2588 2589 else if (tn === $.OPTION) { 2590 if (p.openElements.currentTagName === $.OPTION) 2591 p.openElements.pop(); 2592 } 2593 2594 else if (tn === $.SELECT && p.openElements.hasInSelectScope($.SELECT)) { 2595 p.openElements.popUntilTagNamePopped($.SELECT); 2596 p._resetInsertionMode(); 2597 } 2598 2599 else if (tn === $.TEMPLATE) 2600 endTagInHead(p, token); 2601} 2602 2603//12.2.5.4.17 The "in select in table" insertion mode 2604//------------------------------------------------------------------ 2605function startTagInSelectInTable(p, token) { 2606 var tn = token.tagName; 2607 2608 if (tn === $.CAPTION || tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || 2609 tn === $.THEAD || tn === $.TR || tn === $.TD || tn === $.TH) { 2610 p._processFakeEndTag($.SELECT); 2611 p._processToken(token); 2612 } 2613 2614 else 2615 startTagInSelect(p, token); 2616} 2617 2618function endTagInSelectInTable(p, token) { 2619 var tn = token.tagName; 2620 2621 if (tn === $.CAPTION || tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || 2622 tn === $.THEAD || tn === $.TR || tn === $.TD || tn === $.TH) { 2623 if (p.openElements.hasInTableScope(tn)) { 2624 p._processFakeEndTag($.SELECT); 2625 p._processToken(token); 2626 } 2627 } 2628 2629 else 2630 endTagInSelect(p, token); 2631} 2632 2633//12.2.5.4.18 The "in template" insertion mode 2634//------------------------------------------------------------------ 2635function startTagInTemplate(p, token) { 2636 var tn = token.tagName; 2637 2638 if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META || 2639 tn === $.NOFRAMES || tn === $.SCRIPT || tn === $.STYLE || tn === $.TEMPLATE || tn === $.TITLE) { 2640 startTagInHead(p, token); 2641 } 2642 2643 else { 2644 var newInsertionMode = TEMPLATE_INSERTION_MODE_SWITCH_MAP[tn] || IN_BODY_MODE; 2645 2646 p._popTmplInsertionMode(); 2647 p._pushTmplInsertionMode(newInsertionMode); 2648 p.insertionMode = newInsertionMode; 2649 p._processToken(token); 2650 } 2651} 2652 2653function endTagInTemplate(p, token) { 2654 if (token.tagName === $.TEMPLATE) 2655 endTagInHead(p, token); 2656} 2657 2658function eofInTemplate(p, token) { 2659 if (p.openElements.tmplCount > 0) { 2660 p.openElements.popUntilTemplatePopped(); 2661 p.activeFormattingElements.clearToLastMarker(); 2662 p._popTmplInsertionMode(); 2663 p._resetInsertionMode(); 2664 p._processToken(token); 2665 } 2666 2667 else 2668 p.stopped = true; 2669} 2670 2671 2672//12.2.5.4.19 The "after body" insertion mode 2673//------------------------------------------------------------------ 2674function startTagAfterBody(p, token) { 2675 if (token.tagName === $.HTML) 2676 startTagInBody(p, token); 2677 2678 else 2679 tokenAfterBody(p, token); 2680} 2681 2682function endTagAfterBody(p, token) { 2683 if (token.tagName === $.HTML) { 2684 if (!p.fragmentContext) 2685 p.insertionMode = AFTER_AFTER_BODY_MODE; 2686 } 2687 2688 else 2689 tokenAfterBody(p, token); 2690} 2691 2692function tokenAfterBody(p, token) { 2693 p.insertionMode = IN_BODY_MODE; 2694 p._processToken(token); 2695} 2696 2697//12.2.5.4.20 The "in frameset" insertion mode 2698//------------------------------------------------------------------ 2699function startTagInFrameset(p, token) { 2700 var tn = token.tagName; 2701 2702 if (tn === $.HTML) 2703 startTagInBody(p, token); 2704 2705 else if (tn === $.FRAMESET) 2706 p._insertElement(token, NS.HTML); 2707 2708 else if (tn === $.FRAME) 2709 p._appendElement(token, NS.HTML); 2710 2711 else if (tn === $.NOFRAMES) 2712 startTagInHead(p, token); 2713} 2714 2715function endTagInFrameset(p, token) { 2716 if (token.tagName === $.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) { 2717 p.openElements.pop(); 2718 2719 if (!p.fragmentContext && p.openElements.currentTagName !== $.FRAMESET) 2720 p.insertionMode = AFTER_FRAMESET_MODE; 2721 } 2722} 2723 2724//12.2.5.4.21 The "after frameset" insertion mode 2725//------------------------------------------------------------------ 2726function startTagAfterFrameset(p, token) { 2727 var tn = token.tagName; 2728 2729 if (tn === $.HTML) 2730 startTagInBody(p, token); 2731 2732 else if (tn === $.NOFRAMES) 2733 startTagInHead(p, token); 2734} 2735 2736function endTagAfterFrameset(p, token) { 2737 if (token.tagName === $.HTML) 2738 p.insertionMode = AFTER_AFTER_FRAMESET_MODE; 2739} 2740 2741//12.2.5.4.22 The "after after body" insertion mode 2742//------------------------------------------------------------------ 2743function startTagAfterAfterBody(p, token) { 2744 if (token.tagName === $.HTML) 2745 startTagInBody(p, token); 2746 2747 else 2748 tokenAfterAfterBody(p, token); 2749} 2750 2751function tokenAfterAfterBody(p, token) { 2752 p.insertionMode = IN_BODY_MODE; 2753 p._processToken(token); 2754} 2755 2756//12.2.5.4.23 The "after after frameset" insertion mode 2757//------------------------------------------------------------------ 2758function startTagAfterAfterFrameset(p, token) { 2759 var tn = token.tagName; 2760 2761 if (tn === $.HTML) 2762 startTagInBody(p, token); 2763 2764 else if (tn === $.NOFRAMES) 2765 startTagInHead(p, token); 2766} 2767 2768 2769//12.2.5.5 The rules for parsing tokens in foreign content 2770//------------------------------------------------------------------ 2771function nullCharacterInForeignContent(p, token) { 2772 token.chars = UNICODE.REPLACEMENT_CHARACTER; 2773 p._insertCharacters(token); 2774} 2775 2776function characterInForeignContent(p, token) { 2777 p._insertCharacters(token); 2778 p.framesetOk = false; 2779} 2780 2781function startTagInForeignContent(p, token) { 2782 if (ForeignContent.causesExit(token) && !p.fragmentContext) { 2783 while (p.treeAdapter.getNamespaceURI(p.openElements.current) !== NS.HTML && 2784 (!p._isMathMLTextIntegrationPoint(p.openElements.current)) && 2785 (!p._isHtmlIntegrationPoint(p.openElements.current))) { 2786 p.openElements.pop(); 2787 } 2788 2789 p._processToken(token); 2790 } 2791 2792 else { 2793 var current = p._getAdjustedCurrentElement(), 2794 currentNs = p.treeAdapter.getNamespaceURI(current); 2795 2796 if (currentNs === NS.MATHML) 2797 ForeignContent.adjustTokenMathMLAttrs(token); 2798 2799 else if (currentNs === NS.SVG) { 2800 ForeignContent.adjustTokenSVGTagName(token); 2801 ForeignContent.adjustTokenSVGAttrs(token); 2802 } 2803 2804 ForeignContent.adjustTokenXMLAttrs(token); 2805 2806 if (token.selfClosing) 2807 p._appendElement(token, currentNs); 2808 else 2809 p._insertElement(token, currentNs); 2810 } 2811} 2812 2813function endTagInForeignContent(p, token) { 2814 for (var i = p.openElements.stackTop; i > 0; i--) { 2815 var element = p.openElements.items[i]; 2816 2817 if (p.treeAdapter.getNamespaceURI(element) === NS.HTML) { 2818 p._processToken(token); 2819 break; 2820 } 2821 2822 if (p.treeAdapter.getTagName(element).toLowerCase() === token.tagName) { 2823 p.openElements.popUntilElementPopped(element); 2824 break; 2825 } 2826 } 2827} 2828