7986 lines
816 KiB
JavaScript
7986 lines
816 KiB
JavaScript
|
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.parse5 = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const { DOCUMENT_MODE } = require('./html');
|
||
|
|
||
|
//Const
|
||
|
const VALID_DOCTYPE_NAME = 'html';
|
||
|
const VALID_SYSTEM_ID = 'about:legacy-compat';
|
||
|
const QUIRKS_MODE_SYSTEM_ID = 'http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd';
|
||
|
|
||
|
const QUIRKS_MODE_PUBLIC_ID_PREFIXES = [
|
||
|
'+//silmaril//dtd html pro v0r11 19970101//',
|
||
|
'-//as//dtd html 3.0 aswedit + extensions//',
|
||
|
'-//advasoft ltd//dtd html 3.0 aswedit + extensions//',
|
||
|
'-//ietf//dtd html 2.0 level 1//',
|
||
|
'-//ietf//dtd html 2.0 level 2//',
|
||
|
'-//ietf//dtd html 2.0 strict level 1//',
|
||
|
'-//ietf//dtd html 2.0 strict level 2//',
|
||
|
'-//ietf//dtd html 2.0 strict//',
|
||
|
'-//ietf//dtd html 2.0//',
|
||
|
'-//ietf//dtd html 2.1e//',
|
||
|
'-//ietf//dtd html 3.0//',
|
||
|
'-//ietf//dtd html 3.2 final//',
|
||
|
'-//ietf//dtd html 3.2//',
|
||
|
'-//ietf//dtd html 3//',
|
||
|
'-//ietf//dtd html level 0//',
|
||
|
'-//ietf//dtd html level 1//',
|
||
|
'-//ietf//dtd html level 2//',
|
||
|
'-//ietf//dtd html level 3//',
|
||
|
'-//ietf//dtd html strict level 0//',
|
||
|
'-//ietf//dtd html strict level 1//',
|
||
|
'-//ietf//dtd html strict level 2//',
|
||
|
'-//ietf//dtd html strict level 3//',
|
||
|
'-//ietf//dtd html strict//',
|
||
|
'-//ietf//dtd html//',
|
||
|
'-//metrius//dtd metrius presentational//',
|
||
|
'-//microsoft//dtd internet explorer 2.0 html strict//',
|
||
|
'-//microsoft//dtd internet explorer 2.0 html//',
|
||
|
'-//microsoft//dtd internet explorer 2.0 tables//',
|
||
|
'-//microsoft//dtd internet explorer 3.0 html strict//',
|
||
|
'-//microsoft//dtd internet explorer 3.0 html//',
|
||
|
'-//microsoft//dtd internet explorer 3.0 tables//',
|
||
|
'-//netscape comm. corp.//dtd html//',
|
||
|
'-//netscape comm. corp.//dtd strict html//',
|
||
|
"-//o'reilly and associates//dtd html 2.0//",
|
||
|
"-//o'reilly and associates//dtd html extended 1.0//",
|
||
|
"-//o'reilly and associates//dtd html extended relaxed 1.0//",
|
||
|
'-//sq//dtd html 2.0 hotmetal + extensions//',
|
||
|
'-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//',
|
||
|
'-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//',
|
||
|
'-//spyglass//dtd html 2.0 extended//',
|
||
|
'-//sun microsystems corp.//dtd hotjava html//',
|
||
|
'-//sun microsystems corp.//dtd hotjava strict html//',
|
||
|
'-//w3c//dtd html 3 1995-03-24//',
|
||
|
'-//w3c//dtd html 3.2 draft//',
|
||
|
'-//w3c//dtd html 3.2 final//',
|
||
|
'-//w3c//dtd html 3.2//',
|
||
|
'-//w3c//dtd html 3.2s draft//',
|
||
|
'-//w3c//dtd html 4.0 frameset//',
|
||
|
'-//w3c//dtd html 4.0 transitional//',
|
||
|
'-//w3c//dtd html experimental 19960712//',
|
||
|
'-//w3c//dtd html experimental 970421//',
|
||
|
'-//w3c//dtd w3 html//',
|
||
|
'-//w3o//dtd w3 html 3.0//',
|
||
|
'-//webtechs//dtd mozilla html 2.0//',
|
||
|
'-//webtechs//dtd mozilla html//'
|
||
|
];
|
||
|
|
||
|
const QUIRKS_MODE_NO_SYSTEM_ID_PUBLIC_ID_PREFIXES = QUIRKS_MODE_PUBLIC_ID_PREFIXES.concat([
|
||
|
'-//w3c//dtd html 4.01 frameset//',
|
||
|
'-//w3c//dtd html 4.01 transitional//'
|
||
|
]);
|
||
|
|
||
|
const QUIRKS_MODE_PUBLIC_IDS = ['-//w3o//dtd w3 html strict 3.0//en//', '-/w3c/dtd html 4.0 transitional/en', 'html'];
|
||
|
const LIMITED_QUIRKS_PUBLIC_ID_PREFIXES = ['-//w3c//dtd xhtml 1.0 frameset//', '-//w3c//dtd xhtml 1.0 transitional//'];
|
||
|
|
||
|
const LIMITED_QUIRKS_WITH_SYSTEM_ID_PUBLIC_ID_PREFIXES = LIMITED_QUIRKS_PUBLIC_ID_PREFIXES.concat([
|
||
|
'-//w3c//dtd html 4.01 frameset//',
|
||
|
'-//w3c//dtd html 4.01 transitional//'
|
||
|
]);
|
||
|
|
||
|
//Utils
|
||
|
function enquoteDoctypeId(id) {
|
||
|
const quote = id.indexOf('"') !== -1 ? "'" : '"';
|
||
|
|
||
|
return quote + id + quote;
|
||
|
}
|
||
|
|
||
|
function hasPrefix(publicId, prefixes) {
|
||
|
for (let i = 0; i < prefixes.length; i++) {
|
||
|
if (publicId.indexOf(prefixes[i]) === 0) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//API
|
||
|
exports.isConforming = function(token) {
|
||
|
return (
|
||
|
token.name === VALID_DOCTYPE_NAME &&
|
||
|
token.publicId === null &&
|
||
|
(token.systemId === null || token.systemId === VALID_SYSTEM_ID)
|
||
|
);
|
||
|
};
|
||
|
|
||
|
exports.getDocumentMode = function(token) {
|
||
|
if (token.name !== VALID_DOCTYPE_NAME) {
|
||
|
return DOCUMENT_MODE.QUIRKS;
|
||
|
}
|
||
|
|
||
|
const systemId = token.systemId;
|
||
|
|
||
|
if (systemId && systemId.toLowerCase() === QUIRKS_MODE_SYSTEM_ID) {
|
||
|
return DOCUMENT_MODE.QUIRKS;
|
||
|
}
|
||
|
|
||
|
let publicId = token.publicId;
|
||
|
|
||
|
if (publicId !== null) {
|
||
|
publicId = publicId.toLowerCase();
|
||
|
|
||
|
if (QUIRKS_MODE_PUBLIC_IDS.indexOf(publicId) > -1) {
|
||
|
return DOCUMENT_MODE.QUIRKS;
|
||
|
}
|
||
|
|
||
|
let prefixes = systemId === null ? QUIRKS_MODE_NO_SYSTEM_ID_PUBLIC_ID_PREFIXES : QUIRKS_MODE_PUBLIC_ID_PREFIXES;
|
||
|
|
||
|
if (hasPrefix(publicId, prefixes)) {
|
||
|
return DOCUMENT_MODE.QUIRKS;
|
||
|
}
|
||
|
|
||
|
prefixes =
|
||
|
systemId === null ? LIMITED_QUIRKS_PUBLIC_ID_PREFIXES : LIMITED_QUIRKS_WITH_SYSTEM_ID_PUBLIC_ID_PREFIXES;
|
||
|
|
||
|
if (hasPrefix(publicId, prefixes)) {
|
||
|
return DOCUMENT_MODE.LIMITED_QUIRKS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return DOCUMENT_MODE.NO_QUIRKS;
|
||
|
};
|
||
|
|
||
|
exports.serializeContent = function(name, publicId, systemId) {
|
||
|
let str = '!DOCTYPE ';
|
||
|
|
||
|
if (name) {
|
||
|
str += name;
|
||
|
}
|
||
|
|
||
|
if (publicId) {
|
||
|
str += ' PUBLIC ' + enquoteDoctypeId(publicId);
|
||
|
} else if (systemId) {
|
||
|
str += ' SYSTEM';
|
||
|
}
|
||
|
|
||
|
if (systemId !== null) {
|
||
|
str += ' ' + enquoteDoctypeId(systemId);
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
};
|
||
|
|
||
|
},{"./html":4}],2:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
module.exports = {
|
||
|
controlCharacterInInputStream: 'control-character-in-input-stream',
|
||
|
noncharacterInInputStream: 'noncharacter-in-input-stream',
|
||
|
surrogateInInputStream: 'surrogate-in-input-stream',
|
||
|
nonVoidHtmlElementStartTagWithTrailingSolidus: 'non-void-html-element-start-tag-with-trailing-solidus',
|
||
|
endTagWithAttributes: 'end-tag-with-attributes',
|
||
|
endTagWithTrailingSolidus: 'end-tag-with-trailing-solidus',
|
||
|
unexpectedSolidusInTag: 'unexpected-solidus-in-tag',
|
||
|
unexpectedNullCharacter: 'unexpected-null-character',
|
||
|
unexpectedQuestionMarkInsteadOfTagName: 'unexpected-question-mark-instead-of-tag-name',
|
||
|
invalidFirstCharacterOfTagName: 'invalid-first-character-of-tag-name',
|
||
|
unexpectedEqualsSignBeforeAttributeName: 'unexpected-equals-sign-before-attribute-name',
|
||
|
missingEndTagName: 'missing-end-tag-name',
|
||
|
unexpectedCharacterInAttributeName: 'unexpected-character-in-attribute-name',
|
||
|
unknownNamedCharacterReference: 'unknown-named-character-reference',
|
||
|
missingSemicolonAfterCharacterReference: 'missing-semicolon-after-character-reference',
|
||
|
unexpectedCharacterAfterDoctypeSystemIdentifier: 'unexpected-character-after-doctype-system-identifier',
|
||
|
unexpectedCharacterInUnquotedAttributeValue: 'unexpected-character-in-unquoted-attribute-value',
|
||
|
eofBeforeTagName: 'eof-before-tag-name',
|
||
|
eofInTag: 'eof-in-tag',
|
||
|
missingAttributeValue: 'missing-attribute-value',
|
||
|
missingWhitespaceBetweenAttributes: 'missing-whitespace-between-attributes',
|
||
|
missingWhitespaceAfterDoctypePublicKeyword: 'missing-whitespace-after-doctype-public-keyword',
|
||
|
missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers:
|
||
|
'missing-whitespace-between-doctype-public-and-system-identifiers',
|
||
|
missingWhitespaceAfterDoctypeSystemKeyword: 'missing-whitespace-after-doctype-system-keyword',
|
||
|
missingQuoteBeforeDoctypePublicIdentifier: 'missing-quote-before-doctype-public-identifier',
|
||
|
missingQuoteBeforeDoctypeSystemIdentifier: 'missing-quote-before-doctype-system-identifier',
|
||
|
missingDoctypePublicIdentifier: 'missing-doctype-public-identifier',
|
||
|
missingDoctypeSystemIdentifier: 'missing-doctype-system-identifier',
|
||
|
abruptDoctypePublicIdentifier: 'abrupt-doctype-public-identifier',
|
||
|
abruptDoctypeSystemIdentifier: 'abrupt-doctype-system-identifier',
|
||
|
cdataInHtmlContent: 'cdata-in-html-content',
|
||
|
incorrectlyOpenedComment: 'incorrectly-opened-comment',
|
||
|
eofInScriptHtmlCommentLikeText: 'eof-in-script-html-comment-like-text',
|
||
|
eofInDoctype: 'eof-in-doctype',
|
||
|
nestedComment: 'nested-comment',
|
||
|
abruptClosingOfEmptyComment: 'abrupt-closing-of-empty-comment',
|
||
|
eofInComment: 'eof-in-comment',
|
||
|
incorrectlyClosedComment: 'incorrectly-closed-comment',
|
||
|
eofInCdata: 'eof-in-cdata',
|
||
|
absenceOfDigitsInNumericCharacterReference: 'absence-of-digits-in-numeric-character-reference',
|
||
|
nullCharacterReference: 'null-character-reference',
|
||
|
surrogateCharacterReference: 'surrogate-character-reference',
|
||
|
characterReferenceOutsideUnicodeRange: 'character-reference-outside-unicode-range',
|
||
|
controlCharacterReference: 'control-character-reference',
|
||
|
noncharacterCharacterReference: 'noncharacter-character-reference',
|
||
|
missingWhitespaceBeforeDoctypeName: 'missing-whitespace-before-doctype-name',
|
||
|
missingDoctypeName: 'missing-doctype-name',
|
||
|
invalidCharacterSequenceAfterDoctypeName: 'invalid-character-sequence-after-doctype-name',
|
||
|
duplicateAttribute: 'duplicate-attribute',
|
||
|
nonConformingDoctype: 'non-conforming-doctype',
|
||
|
missingDoctype: 'missing-doctype',
|
||
|
misplacedDoctype: 'misplaced-doctype',
|
||
|
endTagWithoutMatchingOpenElement: 'end-tag-without-matching-open-element',
|
||
|
closingOfElementWithOpenChildElements: 'closing-of-element-with-open-child-elements',
|
||
|
disallowedContentInNoscriptInHead: 'disallowed-content-in-noscript-in-head',
|
||
|
openElementsLeftAfterEof: 'open-elements-left-after-eof',
|
||
|
abandonedHeadElementChild: 'abandoned-head-element-child',
|
||
|
misplacedStartTagForHeadElement: 'misplaced-start-tag-for-head-element',
|
||
|
nestedNoscriptInHead: 'nested-noscript-in-head',
|
||
|
eofInElementThatCanContainOnlyText: 'eof-in-element-that-can-contain-only-text'
|
||
|
};
|
||
|
|
||
|
},{}],3:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Tokenizer = require('../tokenizer');
|
||
|
const HTML = require('./html');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = HTML.TAG_NAMES;
|
||
|
const NS = HTML.NAMESPACES;
|
||
|
const ATTRS = HTML.ATTRS;
|
||
|
|
||
|
//MIME types
|
||
|
const MIME_TYPES = {
|
||
|
TEXT_HTML: 'text/html',
|
||
|
APPLICATION_XML: 'application/xhtml+xml'
|
||
|
};
|
||
|
|
||
|
//Attributes
|
||
|
const DEFINITION_URL_ATTR = 'definitionurl';
|
||
|
const ADJUSTED_DEFINITION_URL_ATTR = 'definitionURL';
|
||
|
const SVG_ATTRS_ADJUSTMENT_MAP = {
|
||
|
attributename: 'attributeName',
|
||
|
attributetype: 'attributeType',
|
||
|
basefrequency: 'baseFrequency',
|
||
|
baseprofile: 'baseProfile',
|
||
|
calcmode: 'calcMode',
|
||
|
clippathunits: 'clipPathUnits',
|
||
|
diffuseconstant: 'diffuseConstant',
|
||
|
edgemode: 'edgeMode',
|
||
|
filterunits: 'filterUnits',
|
||
|
glyphref: 'glyphRef',
|
||
|
gradienttransform: 'gradientTransform',
|
||
|
gradientunits: 'gradientUnits',
|
||
|
kernelmatrix: 'kernelMatrix',
|
||
|
kernelunitlength: 'kernelUnitLength',
|
||
|
keypoints: 'keyPoints',
|
||
|
keysplines: 'keySplines',
|
||
|
keytimes: 'keyTimes',
|
||
|
lengthadjust: 'lengthAdjust',
|
||
|
limitingconeangle: 'limitingConeAngle',
|
||
|
markerheight: 'markerHeight',
|
||
|
markerunits: 'markerUnits',
|
||
|
markerwidth: 'markerWidth',
|
||
|
maskcontentunits: 'maskContentUnits',
|
||
|
maskunits: 'maskUnits',
|
||
|
numoctaves: 'numOctaves',
|
||
|
pathlength: 'pathLength',
|
||
|
patterncontentunits: 'patternContentUnits',
|
||
|
patterntransform: 'patternTransform',
|
||
|
patternunits: 'patternUnits',
|
||
|
pointsatx: 'pointsAtX',
|
||
|
pointsaty: 'pointsAtY',
|
||
|
pointsatz: 'pointsAtZ',
|
||
|
preservealpha: 'preserveAlpha',
|
||
|
preserveaspectratio: 'preserveAspectRatio',
|
||
|
primitiveunits: 'primitiveUnits',
|
||
|
refx: 'refX',
|
||
|
refy: 'refY',
|
||
|
repeatcount: 'repeatCount',
|
||
|
repeatdur: 'repeatDur',
|
||
|
requiredextensions: 'requiredExtensions',
|
||
|
requiredfeatures: 'requiredFeatures',
|
||
|
specularconstant: 'specularConstant',
|
||
|
specularexponent: 'specularExponent',
|
||
|
spreadmethod: 'spreadMethod',
|
||
|
startoffset: 'startOffset',
|
||
|
stddeviation: 'stdDeviation',
|
||
|
stitchtiles: 'stitchTiles',
|
||
|
surfacescale: 'surfaceScale',
|
||
|
systemlanguage: 'systemLanguage',
|
||
|
tablevalues: 'tableValues',
|
||
|
targetx: 'targetX',
|
||
|
targety: 'targetY',
|
||
|
textlength: 'textLength',
|
||
|
viewbox: 'viewBox',
|
||
|
viewtarget: 'viewTarget',
|
||
|
xchannelselector: 'xChannelSelector',
|
||
|
ychannelselector: 'yChannelSelector',
|
||
|
zoomandpan: 'zoomAndPan'
|
||
|
};
|
||
|
|
||
|
const XML_ATTRS_ADJUSTMENT_MAP = {
|
||
|
'xlink:actuate': { prefix: 'xlink', name: 'actuate', namespace: NS.XLINK },
|
||
|
'xlink:arcrole': { prefix: 'xlink', name: 'arcrole', namespace: NS.XLINK },
|
||
|
'xlink:href': { prefix: 'xlink', name: 'href', namespace: NS.XLINK },
|
||
|
'xlink:role': { prefix: 'xlink', name: 'role', namespace: NS.XLINK },
|
||
|
'xlink:show': { prefix: 'xlink', name: 'show', namespace: NS.XLINK },
|
||
|
'xlink:title': { prefix: 'xlink', name: 'title', namespace: NS.XLINK },
|
||
|
'xlink:type': { prefix: 'xlink', name: 'type', namespace: NS.XLINK },
|
||
|
'xml:base': { prefix: 'xml', name: 'base', namespace: NS.XML },
|
||
|
'xml:lang': { prefix: 'xml', name: 'lang', namespace: NS.XML },
|
||
|
'xml:space': { prefix: 'xml', name: 'space', namespace: NS.XML },
|
||
|
xmlns: { prefix: '', name: 'xmlns', namespace: NS.XMLNS },
|
||
|
'xmlns:xlink': { prefix: 'xmlns', name: 'xlink', namespace: NS.XMLNS }
|
||
|
};
|
||
|
|
||
|
//SVG tag names adjustment map
|
||
|
const SVG_TAG_NAMES_ADJUSTMENT_MAP = (exports.SVG_TAG_NAMES_ADJUSTMENT_MAP = {
|
||
|
altglyph: 'altGlyph',
|
||
|
altglyphdef: 'altGlyphDef',
|
||
|
altglyphitem: 'altGlyphItem',
|
||
|
animatecolor: 'animateColor',
|
||
|
animatemotion: 'animateMotion',
|
||
|
animatetransform: 'animateTransform',
|
||
|
clippath: 'clipPath',
|
||
|
feblend: 'feBlend',
|
||
|
fecolormatrix: 'feColorMatrix',
|
||
|
fecomponenttransfer: 'feComponentTransfer',
|
||
|
fecomposite: 'feComposite',
|
||
|
feconvolvematrix: 'feConvolveMatrix',
|
||
|
fediffuselighting: 'feDiffuseLighting',
|
||
|
fedisplacementmap: 'feDisplacementMap',
|
||
|
fedistantlight: 'feDistantLight',
|
||
|
feflood: 'feFlood',
|
||
|
fefunca: 'feFuncA',
|
||
|
fefuncb: 'feFuncB',
|
||
|
fefuncg: 'feFuncG',
|
||
|
fefuncr: 'feFuncR',
|
||
|
fegaussianblur: 'feGaussianBlur',
|
||
|
feimage: 'feImage',
|
||
|
femerge: 'feMerge',
|
||
|
femergenode: 'feMergeNode',
|
||
|
femorphology: 'feMorphology',
|
||
|
feoffset: 'feOffset',
|
||
|
fepointlight: 'fePointLight',
|
||
|
fespecularlighting: 'feSpecularLighting',
|
||
|
fespotlight: 'feSpotLight',
|
||
|
fetile: 'feTile',
|
||
|
feturbulence: 'feTurbulence',
|
||
|
foreignobject: 'foreignObject',
|
||
|
glyphref: 'glyphRef',
|
||
|
lineargradient: 'linearGradient',
|
||
|
radialgradient: 'radialGradient',
|
||
|
textpath: 'textPath'
|
||
|
});
|
||
|
|
||
|
//Tags that causes exit from foreign content
|
||
|
const EXITS_FOREIGN_CONTENT = {
|
||
|
[$.B]: true,
|
||
|
[$.BIG]: true,
|
||
|
[$.BLOCKQUOTE]: true,
|
||
|
[$.BODY]: true,
|
||
|
[$.BR]: true,
|
||
|
[$.CENTER]: true,
|
||
|
[$.CODE]: true,
|
||
|
[$.DD]: true,
|
||
|
[$.DIV]: true,
|
||
|
[$.DL]: true,
|
||
|
[$.DT]: true,
|
||
|
[$.EM]: true,
|
||
|
[$.EMBED]: true,
|
||
|
[$.H1]: true,
|
||
|
[$.H2]: true,
|
||
|
[$.H3]: true,
|
||
|
[$.H4]: true,
|
||
|
[$.H5]: true,
|
||
|
[$.H6]: true,
|
||
|
[$.HEAD]: true,
|
||
|
[$.HR]: true,
|
||
|
[$.I]: true,
|
||
|
[$.IMG]: true,
|
||
|
[$.LI]: true,
|
||
|
[$.LISTING]: true,
|
||
|
[$.MENU]: true,
|
||
|
[$.META]: true,
|
||
|
[$.NOBR]: true,
|
||
|
[$.OL]: true,
|
||
|
[$.P]: true,
|
||
|
[$.PRE]: true,
|
||
|
[$.RUBY]: true,
|
||
|
[$.S]: true,
|
||
|
[$.SMALL]: true,
|
||
|
[$.SPAN]: true,
|
||
|
[$.STRONG]: true,
|
||
|
[$.STRIKE]: true,
|
||
|
[$.SUB]: true,
|
||
|
[$.SUP]: true,
|
||
|
[$.TABLE]: true,
|
||
|
[$.TT]: true,
|
||
|
[$.U]: true,
|
||
|
[$.UL]: true,
|
||
|
[$.VAR]: true
|
||
|
};
|
||
|
|
||
|
//Check exit from foreign content
|
||
|
exports.causesExit = function(startTagToken) {
|
||
|
const tn = startTagToken.tagName;
|
||
|
const isFontWithAttrs =
|
||
|
tn === $.FONT &&
|
||
|
(Tokenizer.getTokenAttr(startTagToken, ATTRS.COLOR) !== null ||
|
||
|
Tokenizer.getTokenAttr(startTagToken, ATTRS.SIZE) !== null ||
|
||
|
Tokenizer.getTokenAttr(startTagToken, ATTRS.FACE) !== null);
|
||
|
|
||
|
return isFontWithAttrs ? true : EXITS_FOREIGN_CONTENT[tn];
|
||
|
};
|
||
|
|
||
|
//Token adjustments
|
||
|
exports.adjustTokenMathMLAttrs = function(token) {
|
||
|
for (let i = 0; i < token.attrs.length; i++) {
|
||
|
if (token.attrs[i].name === DEFINITION_URL_ATTR) {
|
||
|
token.attrs[i].name = ADJUSTED_DEFINITION_URL_ATTR;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.adjustTokenSVGAttrs = function(token) {
|
||
|
for (let i = 0; i < token.attrs.length; i++) {
|
||
|
const adjustedAttrName = SVG_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name];
|
||
|
|
||
|
if (adjustedAttrName) {
|
||
|
token.attrs[i].name = adjustedAttrName;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.adjustTokenXMLAttrs = function(token) {
|
||
|
for (let i = 0; i < token.attrs.length; i++) {
|
||
|
const adjustedAttrEntry = XML_ATTRS_ADJUSTMENT_MAP[token.attrs[i].name];
|
||
|
|
||
|
if (adjustedAttrEntry) {
|
||
|
token.attrs[i].prefix = adjustedAttrEntry.prefix;
|
||
|
token.attrs[i].name = adjustedAttrEntry.name;
|
||
|
token.attrs[i].namespace = adjustedAttrEntry.namespace;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.adjustTokenSVGTagName = function(token) {
|
||
|
const adjustedTagName = SVG_TAG_NAMES_ADJUSTMENT_MAP[token.tagName];
|
||
|
|
||
|
if (adjustedTagName) {
|
||
|
token.tagName = adjustedTagName;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//Integration points
|
||
|
function isMathMLTextIntegrationPoint(tn, ns) {
|
||
|
return ns === NS.MATHML && (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS || tn === $.MTEXT);
|
||
|
}
|
||
|
|
||
|
function isHtmlIntegrationPoint(tn, ns, attrs) {
|
||
|
if (ns === NS.MATHML && tn === $.ANNOTATION_XML) {
|
||
|
for (let i = 0; i < attrs.length; i++) {
|
||
|
if (attrs[i].name === ATTRS.ENCODING) {
|
||
|
const value = attrs[i].value.toLowerCase();
|
||
|
|
||
|
return value === MIME_TYPES.TEXT_HTML || value === MIME_TYPES.APPLICATION_XML;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ns === NS.SVG && (tn === $.FOREIGN_OBJECT || tn === $.DESC || tn === $.TITLE);
|
||
|
}
|
||
|
|
||
|
exports.isIntegrationPoint = function(tn, ns, attrs, foreignNS) {
|
||
|
if ((!foreignNS || foreignNS === NS.HTML) && isHtmlIntegrationPoint(tn, ns, attrs)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ((!foreignNS || foreignNS === NS.MATHML) && isMathMLTextIntegrationPoint(tn, ns)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
},{"../tokenizer":19,"./html":4}],4:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const NS = (exports.NAMESPACES = {
|
||
|
HTML: 'http://www.w3.org/1999/xhtml',
|
||
|
MATHML: 'http://www.w3.org/1998/Math/MathML',
|
||
|
SVG: 'http://www.w3.org/2000/svg',
|
||
|
XLINK: 'http://www.w3.org/1999/xlink',
|
||
|
XML: 'http://www.w3.org/XML/1998/namespace',
|
||
|
XMLNS: 'http://www.w3.org/2000/xmlns/'
|
||
|
});
|
||
|
|
||
|
exports.ATTRS = {
|
||
|
TYPE: 'type',
|
||
|
ACTION: 'action',
|
||
|
ENCODING: 'encoding',
|
||
|
PROMPT: 'prompt',
|
||
|
NAME: 'name',
|
||
|
COLOR: 'color',
|
||
|
FACE: 'face',
|
||
|
SIZE: 'size'
|
||
|
};
|
||
|
|
||
|
exports.DOCUMENT_MODE = {
|
||
|
NO_QUIRKS: 'no-quirks',
|
||
|
QUIRKS: 'quirks',
|
||
|
LIMITED_QUIRKS: 'limited-quirks'
|
||
|
};
|
||
|
|
||
|
const $ = (exports.TAG_NAMES = {
|
||
|
A: 'a',
|
||
|
ADDRESS: 'address',
|
||
|
ANNOTATION_XML: 'annotation-xml',
|
||
|
APPLET: 'applet',
|
||
|
AREA: 'area',
|
||
|
ARTICLE: 'article',
|
||
|
ASIDE: 'aside',
|
||
|
|
||
|
B: 'b',
|
||
|
BASE: 'base',
|
||
|
BASEFONT: 'basefont',
|
||
|
BGSOUND: 'bgsound',
|
||
|
BIG: 'big',
|
||
|
BLOCKQUOTE: 'blockquote',
|
||
|
BODY: 'body',
|
||
|
BR: 'br',
|
||
|
BUTTON: 'button',
|
||
|
|
||
|
CAPTION: 'caption',
|
||
|
CENTER: 'center',
|
||
|
CODE: 'code',
|
||
|
COL: 'col',
|
||
|
COLGROUP: 'colgroup',
|
||
|
|
||
|
DD: 'dd',
|
||
|
DESC: 'desc',
|
||
|
DETAILS: 'details',
|
||
|
DIALOG: 'dialog',
|
||
|
DIR: 'dir',
|
||
|
DIV: 'div',
|
||
|
DL: 'dl',
|
||
|
DT: 'dt',
|
||
|
|
||
|
EM: 'em',
|
||
|
EMBED: 'embed',
|
||
|
|
||
|
FIELDSET: 'fieldset',
|
||
|
FIGCAPTION: 'figcaption',
|
||
|
FIGURE: 'figure',
|
||
|
FONT: 'font',
|
||
|
FOOTER: 'footer',
|
||
|
FOREIGN_OBJECT: 'foreignObject',
|
||
|
FORM: 'form',
|
||
|
FRAME: 'frame',
|
||
|
FRAMESET: 'frameset',
|
||
|
|
||
|
H1: 'h1',
|
||
|
H2: 'h2',
|
||
|
H3: 'h3',
|
||
|
H4: 'h4',
|
||
|
H5: 'h5',
|
||
|
H6: 'h6',
|
||
|
HEAD: 'head',
|
||
|
HEADER: 'header',
|
||
|
HGROUP: 'hgroup',
|
||
|
HR: 'hr',
|
||
|
HTML: 'html',
|
||
|
|
||
|
I: 'i',
|
||
|
IMG: 'img',
|
||
|
IMAGE: 'image',
|
||
|
INPUT: 'input',
|
||
|
IFRAME: 'iframe',
|
||
|
|
||
|
KEYGEN: 'keygen',
|
||
|
|
||
|
LABEL: 'label',
|
||
|
LI: 'li',
|
||
|
LINK: 'link',
|
||
|
LISTING: 'listing',
|
||
|
|
||
|
MAIN: 'main',
|
||
|
MALIGNMARK: 'malignmark',
|
||
|
MARQUEE: 'marquee',
|
||
|
MATH: 'math',
|
||
|
MENU: 'menu',
|
||
|
META: 'meta',
|
||
|
MGLYPH: 'mglyph',
|
||
|
MI: 'mi',
|
||
|
MO: 'mo',
|
||
|
MN: 'mn',
|
||
|
MS: 'ms',
|
||
|
MTEXT: 'mtext',
|
||
|
|
||
|
NAV: 'nav',
|
||
|
NOBR: 'nobr',
|
||
|
NOFRAMES: 'noframes',
|
||
|
NOEMBED: 'noembed',
|
||
|
NOSCRIPT: 'noscript',
|
||
|
|
||
|
OBJECT: 'object',
|
||
|
OL: 'ol',
|
||
|
OPTGROUP: 'optgroup',
|
||
|
OPTION: 'option',
|
||
|
|
||
|
P: 'p',
|
||
|
PARAM: 'param',
|
||
|
PLAINTEXT: 'plaintext',
|
||
|
PRE: 'pre',
|
||
|
|
||
|
RB: 'rb',
|
||
|
RP: 'rp',
|
||
|
RT: 'rt',
|
||
|
RTC: 'rtc',
|
||
|
RUBY: 'ruby',
|
||
|
|
||
|
S: 's',
|
||
|
SCRIPT: 'script',
|
||
|
SECTION: 'section',
|
||
|
SELECT: 'select',
|
||
|
SOURCE: 'source',
|
||
|
SMALL: 'small',
|
||
|
SPAN: 'span',
|
||
|
STRIKE: 'strike',
|
||
|
STRONG: 'strong',
|
||
|
STYLE: 'style',
|
||
|
SUB: 'sub',
|
||
|
SUMMARY: 'summary',
|
||
|
SUP: 'sup',
|
||
|
|
||
|
TABLE: 'table',
|
||
|
TBODY: 'tbody',
|
||
|
TEMPLATE: 'template',
|
||
|
TEXTAREA: 'textarea',
|
||
|
TFOOT: 'tfoot',
|
||
|
TD: 'td',
|
||
|
TH: 'th',
|
||
|
THEAD: 'thead',
|
||
|
TITLE: 'title',
|
||
|
TR: 'tr',
|
||
|
TRACK: 'track',
|
||
|
TT: 'tt',
|
||
|
|
||
|
U: 'u',
|
||
|
UL: 'ul',
|
||
|
|
||
|
SVG: 'svg',
|
||
|
|
||
|
VAR: 'var',
|
||
|
|
||
|
WBR: 'wbr',
|
||
|
|
||
|
XMP: 'xmp'
|
||
|
});
|
||
|
|
||
|
exports.SPECIAL_ELEMENTS = {
|
||
|
[NS.HTML]: {
|
||
|
[$.ADDRESS]: true,
|
||
|
[$.APPLET]: true,
|
||
|
[$.AREA]: true,
|
||
|
[$.ARTICLE]: true,
|
||
|
[$.ASIDE]: true,
|
||
|
[$.BASE]: true,
|
||
|
[$.BASEFONT]: true,
|
||
|
[$.BGSOUND]: true,
|
||
|
[$.BLOCKQUOTE]: true,
|
||
|
[$.BODY]: true,
|
||
|
[$.BR]: true,
|
||
|
[$.BUTTON]: true,
|
||
|
[$.CAPTION]: true,
|
||
|
[$.CENTER]: true,
|
||
|
[$.COL]: true,
|
||
|
[$.COLGROUP]: true,
|
||
|
[$.DD]: true,
|
||
|
[$.DETAILS]: true,
|
||
|
[$.DIR]: true,
|
||
|
[$.DIV]: true,
|
||
|
[$.DL]: true,
|
||
|
[$.DT]: true,
|
||
|
[$.EMBED]: true,
|
||
|
[$.FIELDSET]: true,
|
||
|
[$.FIGCAPTION]: true,
|
||
|
[$.FIGURE]: true,
|
||
|
[$.FOOTER]: true,
|
||
|
[$.FORM]: true,
|
||
|
[$.FRAME]: true,
|
||
|
[$.FRAMESET]: true,
|
||
|
[$.H1]: true,
|
||
|
[$.H2]: true,
|
||
|
[$.H3]: true,
|
||
|
[$.H4]: true,
|
||
|
[$.H5]: true,
|
||
|
[$.H6]: true,
|
||
|
[$.HEAD]: true,
|
||
|
[$.HEADER]: true,
|
||
|
[$.HGROUP]: true,
|
||
|
[$.HR]: true,
|
||
|
[$.HTML]: true,
|
||
|
[$.IFRAME]: true,
|
||
|
[$.IMG]: true,
|
||
|
[$.INPUT]: true,
|
||
|
[$.LI]: true,
|
||
|
[$.LINK]: true,
|
||
|
[$.LISTING]: true,
|
||
|
[$.MAIN]: true,
|
||
|
[$.MARQUEE]: true,
|
||
|
[$.MENU]: true,
|
||
|
[$.META]: true,
|
||
|
[$.NAV]: true,
|
||
|
[$.NOEMBED]: true,
|
||
|
[$.NOFRAMES]: true,
|
||
|
[$.NOSCRIPT]: true,
|
||
|
[$.OBJECT]: true,
|
||
|
[$.OL]: true,
|
||
|
[$.P]: true,
|
||
|
[$.PARAM]: true,
|
||
|
[$.PLAINTEXT]: true,
|
||
|
[$.PRE]: true,
|
||
|
[$.SCRIPT]: true,
|
||
|
[$.SECTION]: true,
|
||
|
[$.SELECT]: true,
|
||
|
[$.SOURCE]: true,
|
||
|
[$.STYLE]: true,
|
||
|
[$.SUMMARY]: true,
|
||
|
[$.TABLE]: true,
|
||
|
[$.TBODY]: true,
|
||
|
[$.TD]: true,
|
||
|
[$.TEMPLATE]: true,
|
||
|
[$.TEXTAREA]: true,
|
||
|
[$.TFOOT]: true,
|
||
|
[$.TH]: true,
|
||
|
[$.THEAD]: true,
|
||
|
[$.TITLE]: true,
|
||
|
[$.TR]: true,
|
||
|
[$.TRACK]: true,
|
||
|
[$.UL]: true,
|
||
|
[$.WBR]: true,
|
||
|
[$.XMP]: true
|
||
|
},
|
||
|
[NS.MATHML]: {
|
||
|
[$.MI]: true,
|
||
|
[$.MO]: true,
|
||
|
[$.MN]: true,
|
||
|
[$.MS]: true,
|
||
|
[$.MTEXT]: true,
|
||
|
[$.ANNOTATION_XML]: true
|
||
|
},
|
||
|
[NS.SVG]: {
|
||
|
[$.TITLE]: true,
|
||
|
[$.FOREIGN_OBJECT]: true,
|
||
|
[$.DESC]: true
|
||
|
}
|
||
|
};
|
||
|
|
||
|
},{}],5:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const UNDEFINED_CODE_POINTS = [
|
||
|
0xfffe,
|
||
|
0xffff,
|
||
|
0x1fffe,
|
||
|
0x1ffff,
|
||
|
0x2fffe,
|
||
|
0x2ffff,
|
||
|
0x3fffe,
|
||
|
0x3ffff,
|
||
|
0x4fffe,
|
||
|
0x4ffff,
|
||
|
0x5fffe,
|
||
|
0x5ffff,
|
||
|
0x6fffe,
|
||
|
0x6ffff,
|
||
|
0x7fffe,
|
||
|
0x7ffff,
|
||
|
0x8fffe,
|
||
|
0x8ffff,
|
||
|
0x9fffe,
|
||
|
0x9ffff,
|
||
|
0xafffe,
|
||
|
0xaffff,
|
||
|
0xbfffe,
|
||
|
0xbffff,
|
||
|
0xcfffe,
|
||
|
0xcffff,
|
||
|
0xdfffe,
|
||
|
0xdffff,
|
||
|
0xefffe,
|
||
|
0xeffff,
|
||
|
0xffffe,
|
||
|
0xfffff,
|
||
|
0x10fffe,
|
||
|
0x10ffff
|
||
|
];
|
||
|
|
||
|
exports.REPLACEMENT_CHARACTER = '\uFFFD';
|
||
|
|
||
|
exports.CODE_POINTS = {
|
||
|
EOF: -1,
|
||
|
NULL: 0x00,
|
||
|
TABULATION: 0x09,
|
||
|
CARRIAGE_RETURN: 0x0d,
|
||
|
LINE_FEED: 0x0a,
|
||
|
FORM_FEED: 0x0c,
|
||
|
SPACE: 0x20,
|
||
|
EXCLAMATION_MARK: 0x21,
|
||
|
QUOTATION_MARK: 0x22,
|
||
|
NUMBER_SIGN: 0x23,
|
||
|
AMPERSAND: 0x26,
|
||
|
APOSTROPHE: 0x27,
|
||
|
HYPHEN_MINUS: 0x2d,
|
||
|
SOLIDUS: 0x2f,
|
||
|
DIGIT_0: 0x30,
|
||
|
DIGIT_9: 0x39,
|
||
|
SEMICOLON: 0x3b,
|
||
|
LESS_THAN_SIGN: 0x3c,
|
||
|
EQUALS_SIGN: 0x3d,
|
||
|
GREATER_THAN_SIGN: 0x3e,
|
||
|
QUESTION_MARK: 0x3f,
|
||
|
LATIN_CAPITAL_A: 0x41,
|
||
|
LATIN_CAPITAL_F: 0x46,
|
||
|
LATIN_CAPITAL_X: 0x58,
|
||
|
LATIN_CAPITAL_Z: 0x5a,
|
||
|
RIGHT_SQUARE_BRACKET: 0x5d,
|
||
|
GRAVE_ACCENT: 0x60,
|
||
|
LATIN_SMALL_A: 0x61,
|
||
|
LATIN_SMALL_F: 0x66,
|
||
|
LATIN_SMALL_X: 0x78,
|
||
|
LATIN_SMALL_Z: 0x7a,
|
||
|
REPLACEMENT_CHARACTER: 0xfffd
|
||
|
};
|
||
|
|
||
|
exports.CODE_POINT_SEQUENCES = {
|
||
|
DASH_DASH_STRING: [0x2d, 0x2d], //--
|
||
|
DOCTYPE_STRING: [0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45], //DOCTYPE
|
||
|
CDATA_START_STRING: [0x5b, 0x43, 0x44, 0x41, 0x54, 0x41, 0x5b], //[CDATA[
|
||
|
SCRIPT_STRING: [0x73, 0x63, 0x72, 0x69, 0x70, 0x74], //script
|
||
|
PUBLIC_STRING: [0x50, 0x55, 0x42, 0x4c, 0x49, 0x43], //PUBLIC
|
||
|
SYSTEM_STRING: [0x53, 0x59, 0x53, 0x54, 0x45, 0x4d] //SYSTEM
|
||
|
};
|
||
|
|
||
|
//Surrogates
|
||
|
exports.isSurrogate = function(cp) {
|
||
|
return cp >= 0xd800 && cp <= 0xdfff;
|
||
|
};
|
||
|
|
||
|
exports.isSurrogatePair = function(cp) {
|
||
|
return cp >= 0xdc00 && cp <= 0xdfff;
|
||
|
};
|
||
|
|
||
|
exports.getSurrogatePairCodePoint = function(cp1, cp2) {
|
||
|
return (cp1 - 0xd800) * 0x400 + 0x2400 + cp2;
|
||
|
};
|
||
|
|
||
|
//NOTE: excluding NULL and ASCII whitespace
|
||
|
exports.isControlCodePoint = function(cp) {
|
||
|
return (
|
||
|
(cp !== 0x20 && cp !== 0x0a && cp !== 0x0d && cp !== 0x09 && cp !== 0x0c && cp >= 0x01 && cp <= 0x1f) ||
|
||
|
(cp >= 0x7f && cp <= 0x9f)
|
||
|
);
|
||
|
};
|
||
|
|
||
|
exports.isUndefinedCodePoint = function(cp) {
|
||
|
return (cp >= 0xfdd0 && cp <= 0xfdef) || UNDEFINED_CODE_POINTS.indexOf(cp) > -1;
|
||
|
};
|
||
|
|
||
|
},{}],6:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
|
||
|
class ErrorReportingMixinBase extends Mixin {
|
||
|
constructor(host, opts) {
|
||
|
super(host);
|
||
|
|
||
|
this.posTracker = null;
|
||
|
this.onParseError = opts.onParseError;
|
||
|
}
|
||
|
|
||
|
_setErrorLocation(err) {
|
||
|
err.startLine = err.endLine = this.posTracker.line;
|
||
|
err.startCol = err.endCol = this.posTracker.col;
|
||
|
err.startOffset = err.endOffset = this.posTracker.offset;
|
||
|
}
|
||
|
|
||
|
_reportError(code) {
|
||
|
const err = {
|
||
|
code: code,
|
||
|
startLine: -1,
|
||
|
startCol: -1,
|
||
|
startOffset: -1,
|
||
|
endLine: -1,
|
||
|
endCol: -1,
|
||
|
endOffset: -1
|
||
|
};
|
||
|
|
||
|
this._setErrorLocation(err);
|
||
|
this.onParseError(err);
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods(mxn) {
|
||
|
return {
|
||
|
_err(code) {
|
||
|
mxn._reportError(code);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = ErrorReportingMixinBase;
|
||
|
|
||
|
},{"../../utils/mixin":24}],7:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const ErrorReportingMixinBase = require('./mixin-base');
|
||
|
const ErrorReportingTokenizerMixin = require('./tokenizer-mixin');
|
||
|
const LocationInfoTokenizerMixin = require('../location-info/tokenizer-mixin');
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
|
||
|
class ErrorReportingParserMixin extends ErrorReportingMixinBase {
|
||
|
constructor(parser, opts) {
|
||
|
super(parser, opts);
|
||
|
|
||
|
this.opts = opts;
|
||
|
this.ctLoc = null;
|
||
|
this.locBeforeToken = false;
|
||
|
}
|
||
|
|
||
|
_setErrorLocation(err) {
|
||
|
if (this.ctLoc) {
|
||
|
err.startLine = this.ctLoc.startLine;
|
||
|
err.startCol = this.ctLoc.startCol;
|
||
|
err.startOffset = this.ctLoc.startOffset;
|
||
|
|
||
|
err.endLine = this.locBeforeToken ? this.ctLoc.startLine : this.ctLoc.endLine;
|
||
|
err.endCol = this.locBeforeToken ? this.ctLoc.startCol : this.ctLoc.endCol;
|
||
|
err.endOffset = this.locBeforeToken ? this.ctLoc.startOffset : this.ctLoc.endOffset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods(mxn, orig) {
|
||
|
return {
|
||
|
_bootstrap(document, fragmentContext) {
|
||
|
orig._bootstrap.call(this, document, fragmentContext);
|
||
|
|
||
|
Mixin.install(this.tokenizer, ErrorReportingTokenizerMixin, mxn.opts);
|
||
|
Mixin.install(this.tokenizer, LocationInfoTokenizerMixin);
|
||
|
},
|
||
|
|
||
|
_processInputToken(token) {
|
||
|
mxn.ctLoc = token.location;
|
||
|
|
||
|
orig._processInputToken.call(this, token);
|
||
|
},
|
||
|
|
||
|
_err(code, options) {
|
||
|
mxn.locBeforeToken = options && options.beforeToken;
|
||
|
mxn._reportError(code);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = ErrorReportingParserMixin;
|
||
|
|
||
|
},{"../../utils/mixin":24,"../location-info/tokenizer-mixin":12,"./mixin-base":6,"./tokenizer-mixin":9}],8:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const ErrorReportingMixinBase = require('./mixin-base');
|
||
|
const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin');
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
|
||
|
class ErrorReportingPreprocessorMixin extends ErrorReportingMixinBase {
|
||
|
constructor(preprocessor, opts) {
|
||
|
super(preprocessor, opts);
|
||
|
|
||
|
this.posTracker = Mixin.install(preprocessor, PositionTrackingPreprocessorMixin);
|
||
|
this.lastErrOffset = -1;
|
||
|
}
|
||
|
|
||
|
_reportError(code) {
|
||
|
//NOTE: avoid reporting error twice on advance/retreat
|
||
|
if (this.lastErrOffset !== this.posTracker.offset) {
|
||
|
this.lastErrOffset = this.posTracker.offset;
|
||
|
super._reportError(code);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = ErrorReportingPreprocessorMixin;
|
||
|
|
||
|
},{"../../utils/mixin":24,"../position-tracking/preprocessor-mixin":13,"./mixin-base":6}],9:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const ErrorReportingMixinBase = require('./mixin-base');
|
||
|
const ErrorReportingPreprocessorMixin = require('./preprocessor-mixin');
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
|
||
|
class ErrorReportingTokenizerMixin extends ErrorReportingMixinBase {
|
||
|
constructor(tokenizer, opts) {
|
||
|
super(tokenizer, opts);
|
||
|
|
||
|
const preprocessorMixin = Mixin.install(tokenizer.preprocessor, ErrorReportingPreprocessorMixin, opts);
|
||
|
|
||
|
this.posTracker = preprocessorMixin.posTracker;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = ErrorReportingTokenizerMixin;
|
||
|
|
||
|
},{"../../utils/mixin":24,"./mixin-base":6,"./preprocessor-mixin":8}],10:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
|
||
|
class LocationInfoOpenElementStackMixin extends Mixin {
|
||
|
constructor(stack, opts) {
|
||
|
super(stack);
|
||
|
|
||
|
this.onItemPop = opts.onItemPop;
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods(mxn, orig) {
|
||
|
return {
|
||
|
pop() {
|
||
|
mxn.onItemPop(this.current);
|
||
|
orig.pop.call(this);
|
||
|
},
|
||
|
|
||
|
popAllUpToHtmlElement() {
|
||
|
for (let i = this.stackTop; i > 0; i--) {
|
||
|
mxn.onItemPop(this.items[i]);
|
||
|
}
|
||
|
|
||
|
orig.popAllUpToHtmlElement.call(this);
|
||
|
},
|
||
|
|
||
|
remove(element) {
|
||
|
mxn.onItemPop(this.current);
|
||
|
orig.remove.call(this, element);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = LocationInfoOpenElementStackMixin;
|
||
|
|
||
|
},{"../../utils/mixin":24}],11:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
const Tokenizer = require('../../tokenizer');
|
||
|
const LocationInfoTokenizerMixin = require('./tokenizer-mixin');
|
||
|
const LocationInfoOpenElementStackMixin = require('./open-element-stack-mixin');
|
||
|
const HTML = require('../../common/html');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = HTML.TAG_NAMES;
|
||
|
|
||
|
class LocationInfoParserMixin extends Mixin {
|
||
|
constructor(parser) {
|
||
|
super(parser);
|
||
|
|
||
|
this.parser = parser;
|
||
|
this.treeAdapter = this.parser.treeAdapter;
|
||
|
this.posTracker = null;
|
||
|
this.lastStartTagToken = null;
|
||
|
this.lastFosterParentingLocation = null;
|
||
|
this.currentToken = null;
|
||
|
}
|
||
|
|
||
|
_setStartLocation(element) {
|
||
|
let loc = null;
|
||
|
|
||
|
if (this.lastStartTagToken) {
|
||
|
loc = Object.assign({}, this.lastStartTagToken.location);
|
||
|
loc.startTag = this.lastStartTagToken.location;
|
||
|
}
|
||
|
|
||
|
this.treeAdapter.setNodeSourceCodeLocation(element, loc);
|
||
|
}
|
||
|
|
||
|
_setEndLocation(element, closingToken) {
|
||
|
const loc = this.treeAdapter.getNodeSourceCodeLocation(element);
|
||
|
|
||
|
if (loc) {
|
||
|
if (closingToken.location) {
|
||
|
const ctLoc = closingToken.location;
|
||
|
const tn = this.treeAdapter.getTagName(element);
|
||
|
|
||
|
// NOTE: For cases like <p> <p> </p> - First 'p' closes without a closing
|
||
|
// tag and for cases like <td> <p> </td> - 'p' closes without a closing tag.
|
||
|
const isClosingEndTag = closingToken.type === Tokenizer.END_TAG_TOKEN && tn === closingToken.tagName;
|
||
|
const endLoc = {};
|
||
|
if (isClosingEndTag) {
|
||
|
endLoc.endTag = Object.assign({}, ctLoc);
|
||
|
endLoc.endLine = ctLoc.endLine;
|
||
|
endLoc.endCol = ctLoc.endCol;
|
||
|
endLoc.endOffset = ctLoc.endOffset;
|
||
|
} else {
|
||
|
endLoc.endLine = ctLoc.startLine;
|
||
|
endLoc.endCol = ctLoc.startCol;
|
||
|
endLoc.endOffset = ctLoc.startOffset;
|
||
|
}
|
||
|
|
||
|
this.treeAdapter.updateNodeSourceCodeLocation(element, endLoc);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods(mxn, orig) {
|
||
|
return {
|
||
|
_bootstrap(document, fragmentContext) {
|
||
|
orig._bootstrap.call(this, document, fragmentContext);
|
||
|
|
||
|
mxn.lastStartTagToken = null;
|
||
|
mxn.lastFosterParentingLocation = null;
|
||
|
mxn.currentToken = null;
|
||
|
|
||
|
const tokenizerMixin = Mixin.install(this.tokenizer, LocationInfoTokenizerMixin);
|
||
|
|
||
|
mxn.posTracker = tokenizerMixin.posTracker;
|
||
|
|
||
|
Mixin.install(this.openElements, LocationInfoOpenElementStackMixin, {
|
||
|
onItemPop: function(element) {
|
||
|
mxn._setEndLocation(element, mxn.currentToken);
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
_runParsingLoop(scriptHandler) {
|
||
|
orig._runParsingLoop.call(this, scriptHandler);
|
||
|
|
||
|
// NOTE: generate location info for elements
|
||
|
// that remains on open element stack
|
||
|
for (let i = this.openElements.stackTop; i >= 0; i--) {
|
||
|
mxn._setEndLocation(this.openElements.items[i], mxn.currentToken);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
//Token processing
|
||
|
_processTokenInForeignContent(token) {
|
||
|
mxn.currentToken = token;
|
||
|
orig._processTokenInForeignContent.call(this, token);
|
||
|
},
|
||
|
|
||
|
_processToken(token) {
|
||
|
mxn.currentToken = token;
|
||
|
orig._processToken.call(this, token);
|
||
|
|
||
|
//NOTE: <body> and <html> are never popped from the stack, so we need to updated
|
||
|
//their end location explicitly.
|
||
|
const requireExplicitUpdate =
|
||
|
token.type === Tokenizer.END_TAG_TOKEN &&
|
||
|
(token.tagName === $.HTML || (token.tagName === $.BODY && this.openElements.hasInScope($.BODY)));
|
||
|
|
||
|
if (requireExplicitUpdate) {
|
||
|
for (let i = this.openElements.stackTop; i >= 0; i--) {
|
||
|
const element = this.openElements.items[i];
|
||
|
|
||
|
if (this.treeAdapter.getTagName(element) === token.tagName) {
|
||
|
mxn._setEndLocation(element, token);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
//Doctype
|
||
|
_setDocumentType(token) {
|
||
|
orig._setDocumentType.call(this, token);
|
||
|
|
||
|
const documentChildren = this.treeAdapter.getChildNodes(this.document);
|
||
|
const cnLength = documentChildren.length;
|
||
|
|
||
|
for (let i = 0; i < cnLength; i++) {
|
||
|
const node = documentChildren[i];
|
||
|
|
||
|
if (this.treeAdapter.isDocumentTypeNode(node)) {
|
||
|
this.treeAdapter.setNodeSourceCodeLocation(node, token.location);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
//Elements
|
||
|
_attachElementToTree(element) {
|
||
|
//NOTE: _attachElementToTree is called from _appendElement, _insertElement and _insertTemplate methods.
|
||
|
//So we will use token location stored in this methods for the element.
|
||
|
mxn._setStartLocation(element);
|
||
|
mxn.lastStartTagToken = null;
|
||
|
orig._attachElementToTree.call(this, element);
|
||
|
},
|
||
|
|
||
|
_appendElement(token, namespaceURI) {
|
||
|
mxn.lastStartTagToken = token;
|
||
|
orig._appendElement.call(this, token, namespaceURI);
|
||
|
},
|
||
|
|
||
|
_insertElement(token, namespaceURI) {
|
||
|
mxn.lastStartTagToken = token;
|
||
|
orig._insertElement.call(this, token, namespaceURI);
|
||
|
},
|
||
|
|
||
|
_insertTemplate(token) {
|
||
|
mxn.lastStartTagToken = token;
|
||
|
orig._insertTemplate.call(this, token);
|
||
|
|
||
|
const tmplContent = this.treeAdapter.getTemplateContent(this.openElements.current);
|
||
|
|
||
|
this.treeAdapter.setNodeSourceCodeLocation(tmplContent, null);
|
||
|
},
|
||
|
|
||
|
_insertFakeRootElement() {
|
||
|
orig._insertFakeRootElement.call(this);
|
||
|
this.treeAdapter.setNodeSourceCodeLocation(this.openElements.current, null);
|
||
|
},
|
||
|
|
||
|
//Comments
|
||
|
_appendCommentNode(token, parent) {
|
||
|
orig._appendCommentNode.call(this, token, parent);
|
||
|
|
||
|
const children = this.treeAdapter.getChildNodes(parent);
|
||
|
const commentNode = children[children.length - 1];
|
||
|
|
||
|
this.treeAdapter.setNodeSourceCodeLocation(commentNode, token.location);
|
||
|
},
|
||
|
|
||
|
//Text
|
||
|
_findFosterParentingLocation() {
|
||
|
//NOTE: store last foster parenting location, so we will be able to find inserted text
|
||
|
//in case of foster parenting
|
||
|
mxn.lastFosterParentingLocation = orig._findFosterParentingLocation.call(this);
|
||
|
|
||
|
return mxn.lastFosterParentingLocation;
|
||
|
},
|
||
|
|
||
|
_insertCharacters(token) {
|
||
|
orig._insertCharacters.call(this, token);
|
||
|
|
||
|
const hasFosterParent = this._shouldFosterParentOnInsertion();
|
||
|
|
||
|
const parent =
|
||
|
(hasFosterParent && mxn.lastFosterParentingLocation.parent) ||
|
||
|
this.openElements.currentTmplContent ||
|
||
|
this.openElements.current;
|
||
|
|
||
|
const siblings = this.treeAdapter.getChildNodes(parent);
|
||
|
|
||
|
const textNodeIdx =
|
||
|
hasFosterParent && mxn.lastFosterParentingLocation.beforeElement
|
||
|
? siblings.indexOf(mxn.lastFosterParentingLocation.beforeElement) - 1
|
||
|
: siblings.length - 1;
|
||
|
|
||
|
const textNode = siblings[textNodeIdx];
|
||
|
|
||
|
//NOTE: if we have location assigned by another token, then just update end position
|
||
|
const tnLoc = this.treeAdapter.getNodeSourceCodeLocation(textNode);
|
||
|
|
||
|
if (tnLoc) {
|
||
|
const { endLine, endCol, endOffset } = token.location;
|
||
|
this.treeAdapter.updateNodeSourceCodeLocation(textNode, { endLine, endCol, endOffset });
|
||
|
} else {
|
||
|
this.treeAdapter.setNodeSourceCodeLocation(textNode, token.location);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = LocationInfoParserMixin;
|
||
|
|
||
|
},{"../../common/html":4,"../../tokenizer":19,"../../utils/mixin":24,"./open-element-stack-mixin":10,"./tokenizer-mixin":12}],12:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
const Tokenizer = require('../../tokenizer');
|
||
|
const PositionTrackingPreprocessorMixin = require('../position-tracking/preprocessor-mixin');
|
||
|
|
||
|
class LocationInfoTokenizerMixin extends Mixin {
|
||
|
constructor(tokenizer) {
|
||
|
super(tokenizer);
|
||
|
|
||
|
this.tokenizer = tokenizer;
|
||
|
this.posTracker = Mixin.install(tokenizer.preprocessor, PositionTrackingPreprocessorMixin);
|
||
|
this.currentAttrLocation = null;
|
||
|
this.ctLoc = null;
|
||
|
}
|
||
|
|
||
|
_getCurrentLocation() {
|
||
|
return {
|
||
|
startLine: this.posTracker.line,
|
||
|
startCol: this.posTracker.col,
|
||
|
startOffset: this.posTracker.offset,
|
||
|
endLine: -1,
|
||
|
endCol: -1,
|
||
|
endOffset: -1
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_attachCurrentAttrLocationInfo() {
|
||
|
this.currentAttrLocation.endLine = this.posTracker.line;
|
||
|
this.currentAttrLocation.endCol = this.posTracker.col;
|
||
|
this.currentAttrLocation.endOffset = this.posTracker.offset;
|
||
|
|
||
|
const currentToken = this.tokenizer.currentToken;
|
||
|
const currentAttr = this.tokenizer.currentAttr;
|
||
|
|
||
|
if (!currentToken.location.attrs) {
|
||
|
currentToken.location.attrs = Object.create(null);
|
||
|
}
|
||
|
|
||
|
currentToken.location.attrs[currentAttr.name] = this.currentAttrLocation;
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods(mxn, orig) {
|
||
|
const methods = {
|
||
|
_createStartTagToken() {
|
||
|
orig._createStartTagToken.call(this);
|
||
|
this.currentToken.location = mxn.ctLoc;
|
||
|
},
|
||
|
|
||
|
_createEndTagToken() {
|
||
|
orig._createEndTagToken.call(this);
|
||
|
this.currentToken.location = mxn.ctLoc;
|
||
|
},
|
||
|
|
||
|
_createCommentToken() {
|
||
|
orig._createCommentToken.call(this);
|
||
|
this.currentToken.location = mxn.ctLoc;
|
||
|
},
|
||
|
|
||
|
_createDoctypeToken(initialName) {
|
||
|
orig._createDoctypeToken.call(this, initialName);
|
||
|
this.currentToken.location = mxn.ctLoc;
|
||
|
},
|
||
|
|
||
|
_createCharacterToken(type, ch) {
|
||
|
orig._createCharacterToken.call(this, type, ch);
|
||
|
this.currentCharacterToken.location = mxn.ctLoc;
|
||
|
},
|
||
|
|
||
|
_createEOFToken() {
|
||
|
orig._createEOFToken.call(this);
|
||
|
this.currentToken.location = mxn._getCurrentLocation();
|
||
|
},
|
||
|
|
||
|
_createAttr(attrNameFirstCh) {
|
||
|
orig._createAttr.call(this, attrNameFirstCh);
|
||
|
mxn.currentAttrLocation = mxn._getCurrentLocation();
|
||
|
},
|
||
|
|
||
|
_leaveAttrName(toState) {
|
||
|
orig._leaveAttrName.call(this, toState);
|
||
|
mxn._attachCurrentAttrLocationInfo();
|
||
|
},
|
||
|
|
||
|
_leaveAttrValue(toState) {
|
||
|
orig._leaveAttrValue.call(this, toState);
|
||
|
mxn._attachCurrentAttrLocationInfo();
|
||
|
},
|
||
|
|
||
|
_emitCurrentToken() {
|
||
|
const ctLoc = this.currentToken.location;
|
||
|
|
||
|
//NOTE: if we have pending character token make it's end location equal to the
|
||
|
//current token's start location.
|
||
|
if (this.currentCharacterToken) {
|
||
|
this.currentCharacterToken.location.endLine = ctLoc.startLine;
|
||
|
this.currentCharacterToken.location.endCol = ctLoc.startCol;
|
||
|
this.currentCharacterToken.location.endOffset = ctLoc.startOffset;
|
||
|
}
|
||
|
|
||
|
if (this.currentToken.type === Tokenizer.EOF_TOKEN) {
|
||
|
ctLoc.endLine = ctLoc.startLine;
|
||
|
ctLoc.endCol = ctLoc.startCol;
|
||
|
ctLoc.endOffset = ctLoc.startOffset;
|
||
|
} else {
|
||
|
ctLoc.endLine = mxn.posTracker.line;
|
||
|
ctLoc.endCol = mxn.posTracker.col + 1;
|
||
|
ctLoc.endOffset = mxn.posTracker.offset + 1;
|
||
|
}
|
||
|
|
||
|
orig._emitCurrentToken.call(this);
|
||
|
},
|
||
|
|
||
|
_emitCurrentCharacterToken() {
|
||
|
const ctLoc = this.currentCharacterToken && this.currentCharacterToken.location;
|
||
|
|
||
|
//NOTE: if we have character token and it's location wasn't set in the _emitCurrentToken(),
|
||
|
//then set it's location at the current preprocessor position.
|
||
|
//We don't need to increment preprocessor position, since character token
|
||
|
//emission is always forced by the start of the next character token here.
|
||
|
//So, we already have advanced position.
|
||
|
if (ctLoc && ctLoc.endOffset === -1) {
|
||
|
ctLoc.endLine = mxn.posTracker.line;
|
||
|
ctLoc.endCol = mxn.posTracker.col;
|
||
|
ctLoc.endOffset = mxn.posTracker.offset;
|
||
|
}
|
||
|
|
||
|
orig._emitCurrentCharacterToken.call(this);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//NOTE: patch initial states for each mode to obtain token start position
|
||
|
Object.keys(Tokenizer.MODE).forEach(modeName => {
|
||
|
const state = Tokenizer.MODE[modeName];
|
||
|
|
||
|
methods[state] = function(cp) {
|
||
|
mxn.ctLoc = mxn._getCurrentLocation();
|
||
|
orig[state].call(this, cp);
|
||
|
};
|
||
|
});
|
||
|
|
||
|
return methods;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = LocationInfoTokenizerMixin;
|
||
|
|
||
|
},{"../../tokenizer":19,"../../utils/mixin":24,"../position-tracking/preprocessor-mixin":13}],13:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Mixin = require('../../utils/mixin');
|
||
|
|
||
|
class PositionTrackingPreprocessorMixin extends Mixin {
|
||
|
constructor(preprocessor) {
|
||
|
super(preprocessor);
|
||
|
|
||
|
this.preprocessor = preprocessor;
|
||
|
this.isEol = false;
|
||
|
this.lineStartPos = 0;
|
||
|
this.droppedBufferSize = 0;
|
||
|
|
||
|
this.offset = 0;
|
||
|
this.col = 0;
|
||
|
this.line = 1;
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods(mxn, orig) {
|
||
|
return {
|
||
|
advance() {
|
||
|
const pos = this.pos + 1;
|
||
|
const ch = this.html[pos];
|
||
|
|
||
|
//NOTE: LF should be in the last column of the line
|
||
|
if (mxn.isEol) {
|
||
|
mxn.isEol = false;
|
||
|
mxn.line++;
|
||
|
mxn.lineStartPos = pos;
|
||
|
}
|
||
|
|
||
|
if (ch === '\n' || (ch === '\r' && this.html[pos + 1] !== '\n')) {
|
||
|
mxn.isEol = true;
|
||
|
}
|
||
|
|
||
|
mxn.col = pos - mxn.lineStartPos + 1;
|
||
|
mxn.offset = mxn.droppedBufferSize + pos;
|
||
|
|
||
|
return orig.advance.call(this);
|
||
|
},
|
||
|
|
||
|
retreat() {
|
||
|
orig.retreat.call(this);
|
||
|
|
||
|
mxn.isEol = false;
|
||
|
mxn.col = this.pos - mxn.lineStartPos + 1;
|
||
|
},
|
||
|
|
||
|
dropParsedChunk() {
|
||
|
const prevPos = this.pos;
|
||
|
|
||
|
orig.dropParsedChunk.call(this);
|
||
|
|
||
|
const reduction = prevPos - this.pos;
|
||
|
|
||
|
mxn.lineStartPos -= reduction;
|
||
|
mxn.droppedBufferSize += reduction;
|
||
|
mxn.offset = mxn.droppedBufferSize + this.pos;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = PositionTrackingPreprocessorMixin;
|
||
|
|
||
|
},{"../../utils/mixin":24}],14:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Parser = require('./parser');
|
||
|
const Serializer = require('./serializer');
|
||
|
|
||
|
// Shorthands
|
||
|
exports.parse = function parse(html, options) {
|
||
|
const parser = new Parser(options);
|
||
|
|
||
|
return parser.parse(html);
|
||
|
};
|
||
|
|
||
|
exports.parseFragment = function parseFragment(fragmentContext, html, options) {
|
||
|
if (typeof fragmentContext === 'string') {
|
||
|
options = html;
|
||
|
html = fragmentContext;
|
||
|
fragmentContext = null;
|
||
|
}
|
||
|
|
||
|
const parser = new Parser(options);
|
||
|
|
||
|
return parser.parseFragment(html, fragmentContext);
|
||
|
};
|
||
|
|
||
|
exports.serialize = function(node, options) {
|
||
|
const serializer = new Serializer(node, options);
|
||
|
|
||
|
return serializer.serialize();
|
||
|
};
|
||
|
|
||
|
},{"./parser":16,"./serializer":18}],15:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
//Const
|
||
|
const NOAH_ARK_CAPACITY = 3;
|
||
|
|
||
|
//List of formatting elements
|
||
|
class FormattingElementList {
|
||
|
constructor(treeAdapter) {
|
||
|
this.length = 0;
|
||
|
this.entries = [];
|
||
|
this.treeAdapter = treeAdapter;
|
||
|
this.bookmark = null;
|
||
|
}
|
||
|
|
||
|
//Noah Ark's condition
|
||
|
//OPTIMIZATION: at first we try to find possible candidates for exclusion using
|
||
|
//lightweight heuristics without thorough attributes check.
|
||
|
_getNoahArkConditionCandidates(newElement) {
|
||
|
const candidates = [];
|
||
|
|
||
|
if (this.length >= NOAH_ARK_CAPACITY) {
|
||
|
const neAttrsLength = this.treeAdapter.getAttrList(newElement).length;
|
||
|
const neTagName = this.treeAdapter.getTagName(newElement);
|
||
|
const neNamespaceURI = this.treeAdapter.getNamespaceURI(newElement);
|
||
|
|
||
|
for (let i = this.length - 1; i >= 0; i--) {
|
||
|
const entry = this.entries[i];
|
||
|
|
||
|
if (entry.type === FormattingElementList.MARKER_ENTRY) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const element = entry.element;
|
||
|
const elementAttrs = this.treeAdapter.getAttrList(element);
|
||
|
|
||
|
const isCandidate =
|
||
|
this.treeAdapter.getTagName(element) === neTagName &&
|
||
|
this.treeAdapter.getNamespaceURI(element) === neNamespaceURI &&
|
||
|
elementAttrs.length === neAttrsLength;
|
||
|
|
||
|
if (isCandidate) {
|
||
|
candidates.push({ idx: i, attrs: elementAttrs });
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return candidates.length < NOAH_ARK_CAPACITY ? [] : candidates;
|
||
|
}
|
||
|
|
||
|
_ensureNoahArkCondition(newElement) {
|
||
|
const candidates = this._getNoahArkConditionCandidates(newElement);
|
||
|
let cLength = candidates.length;
|
||
|
|
||
|
if (cLength) {
|
||
|
const neAttrs = this.treeAdapter.getAttrList(newElement);
|
||
|
const neAttrsLength = neAttrs.length;
|
||
|
const neAttrsMap = Object.create(null);
|
||
|
|
||
|
//NOTE: build attrs map for the new element so we can perform fast lookups
|
||
|
for (let i = 0; i < neAttrsLength; i++) {
|
||
|
const neAttr = neAttrs[i];
|
||
|
|
||
|
neAttrsMap[neAttr.name] = neAttr.value;
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < neAttrsLength; i++) {
|
||
|
for (let j = 0; j < cLength; j++) {
|
||
|
const cAttr = candidates[j].attrs[i];
|
||
|
|
||
|
if (neAttrsMap[cAttr.name] !== cAttr.value) {
|
||
|
candidates.splice(j, 1);
|
||
|
cLength--;
|
||
|
}
|
||
|
|
||
|
if (candidates.length < NOAH_ARK_CAPACITY) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//NOTE: remove bottommost candidates until Noah's Ark condition will not be met
|
||
|
for (let i = cLength - 1; i >= NOAH_ARK_CAPACITY - 1; i--) {
|
||
|
this.entries.splice(candidates[i].idx, 1);
|
||
|
this.length--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Mutations
|
||
|
insertMarker() {
|
||
|
this.entries.push({ type: FormattingElementList.MARKER_ENTRY });
|
||
|
this.length++;
|
||
|
}
|
||
|
|
||
|
pushElement(element, token) {
|
||
|
this._ensureNoahArkCondition(element);
|
||
|
|
||
|
this.entries.push({
|
||
|
type: FormattingElementList.ELEMENT_ENTRY,
|
||
|
element: element,
|
||
|
token: token
|
||
|
});
|
||
|
|
||
|
this.length++;
|
||
|
}
|
||
|
|
||
|
insertElementAfterBookmark(element, token) {
|
||
|
let bookmarkIdx = this.length - 1;
|
||
|
|
||
|
for (; bookmarkIdx >= 0; bookmarkIdx--) {
|
||
|
if (this.entries[bookmarkIdx] === this.bookmark) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.entries.splice(bookmarkIdx + 1, 0, {
|
||
|
type: FormattingElementList.ELEMENT_ENTRY,
|
||
|
element: element,
|
||
|
token: token
|
||
|
});
|
||
|
|
||
|
this.length++;
|
||
|
}
|
||
|
|
||
|
removeEntry(entry) {
|
||
|
for (let i = this.length - 1; i >= 0; i--) {
|
||
|
if (this.entries[i] === entry) {
|
||
|
this.entries.splice(i, 1);
|
||
|
this.length--;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clearToLastMarker() {
|
||
|
while (this.length) {
|
||
|
const entry = this.entries.pop();
|
||
|
|
||
|
this.length--;
|
||
|
|
||
|
if (entry.type === FormattingElementList.MARKER_ENTRY) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Search
|
||
|
getElementEntryInScopeWithTagName(tagName) {
|
||
|
for (let i = this.length - 1; i >= 0; i--) {
|
||
|
const entry = this.entries[i];
|
||
|
|
||
|
if (entry.type === FormattingElementList.MARKER_ENTRY) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (this.treeAdapter.getTagName(entry.element) === tagName) {
|
||
|
return entry;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
getElementEntry(element) {
|
||
|
for (let i = this.length - 1; i >= 0; i--) {
|
||
|
const entry = this.entries[i];
|
||
|
|
||
|
if (entry.type === FormattingElementList.ELEMENT_ENTRY && entry.element === element) {
|
||
|
return entry;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Entry types
|
||
|
FormattingElementList.MARKER_ENTRY = 'MARKER_ENTRY';
|
||
|
FormattingElementList.ELEMENT_ENTRY = 'ELEMENT_ENTRY';
|
||
|
|
||
|
module.exports = FormattingElementList;
|
||
|
|
||
|
},{}],16:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Tokenizer = require('../tokenizer');
|
||
|
const OpenElementStack = require('./open-element-stack');
|
||
|
const FormattingElementList = require('./formatting-element-list');
|
||
|
const LocationInfoParserMixin = require('../extensions/location-info/parser-mixin');
|
||
|
const ErrorReportingParserMixin = require('../extensions/error-reporting/parser-mixin');
|
||
|
const Mixin = require('../utils/mixin');
|
||
|
const defaultTreeAdapter = require('../tree-adapters/default');
|
||
|
const mergeOptions = require('../utils/merge-options');
|
||
|
const doctype = require('../common/doctype');
|
||
|
const foreignContent = require('../common/foreign-content');
|
||
|
const ERR = require('../common/error-codes');
|
||
|
const unicode = require('../common/unicode');
|
||
|
const HTML = require('../common/html');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = HTML.TAG_NAMES;
|
||
|
const NS = HTML.NAMESPACES;
|
||
|
const ATTRS = HTML.ATTRS;
|
||
|
|
||
|
const DEFAULT_OPTIONS = {
|
||
|
scriptingEnabled: true,
|
||
|
sourceCodeLocationInfo: false,
|
||
|
onParseError: null,
|
||
|
treeAdapter: defaultTreeAdapter
|
||
|
};
|
||
|
|
||
|
//Misc constants
|
||
|
const HIDDEN_INPUT_TYPE = 'hidden';
|
||
|
|
||
|
//Adoption agency loops iteration count
|
||
|
const AA_OUTER_LOOP_ITER = 8;
|
||
|
const AA_INNER_LOOP_ITER = 3;
|
||
|
|
||
|
//Insertion modes
|
||
|
const INITIAL_MODE = 'INITIAL_MODE';
|
||
|
const BEFORE_HTML_MODE = 'BEFORE_HTML_MODE';
|
||
|
const BEFORE_HEAD_MODE = 'BEFORE_HEAD_MODE';
|
||
|
const IN_HEAD_MODE = 'IN_HEAD_MODE';
|
||
|
const IN_HEAD_NO_SCRIPT_MODE = 'IN_HEAD_NO_SCRIPT_MODE';
|
||
|
const AFTER_HEAD_MODE = 'AFTER_HEAD_MODE';
|
||
|
const IN_BODY_MODE = 'IN_BODY_MODE';
|
||
|
const TEXT_MODE = 'TEXT_MODE';
|
||
|
const IN_TABLE_MODE = 'IN_TABLE_MODE';
|
||
|
const IN_TABLE_TEXT_MODE = 'IN_TABLE_TEXT_MODE';
|
||
|
const IN_CAPTION_MODE = 'IN_CAPTION_MODE';
|
||
|
const IN_COLUMN_GROUP_MODE = 'IN_COLUMN_GROUP_MODE';
|
||
|
const IN_TABLE_BODY_MODE = 'IN_TABLE_BODY_MODE';
|
||
|
const IN_ROW_MODE = 'IN_ROW_MODE';
|
||
|
const IN_CELL_MODE = 'IN_CELL_MODE';
|
||
|
const IN_SELECT_MODE = 'IN_SELECT_MODE';
|
||
|
const IN_SELECT_IN_TABLE_MODE = 'IN_SELECT_IN_TABLE_MODE';
|
||
|
const IN_TEMPLATE_MODE = 'IN_TEMPLATE_MODE';
|
||
|
const AFTER_BODY_MODE = 'AFTER_BODY_MODE';
|
||
|
const IN_FRAMESET_MODE = 'IN_FRAMESET_MODE';
|
||
|
const AFTER_FRAMESET_MODE = 'AFTER_FRAMESET_MODE';
|
||
|
const AFTER_AFTER_BODY_MODE = 'AFTER_AFTER_BODY_MODE';
|
||
|
const AFTER_AFTER_FRAMESET_MODE = 'AFTER_AFTER_FRAMESET_MODE';
|
||
|
|
||
|
//Insertion mode reset map
|
||
|
const INSERTION_MODE_RESET_MAP = {
|
||
|
[$.TR]: IN_ROW_MODE,
|
||
|
[$.TBODY]: IN_TABLE_BODY_MODE,
|
||
|
[$.THEAD]: IN_TABLE_BODY_MODE,
|
||
|
[$.TFOOT]: IN_TABLE_BODY_MODE,
|
||
|
[$.CAPTION]: IN_CAPTION_MODE,
|
||
|
[$.COLGROUP]: IN_COLUMN_GROUP_MODE,
|
||
|
[$.TABLE]: IN_TABLE_MODE,
|
||
|
[$.BODY]: IN_BODY_MODE,
|
||
|
[$.FRAMESET]: IN_FRAMESET_MODE
|
||
|
};
|
||
|
|
||
|
//Template insertion mode switch map
|
||
|
const TEMPLATE_INSERTION_MODE_SWITCH_MAP = {
|
||
|
[$.CAPTION]: IN_TABLE_MODE,
|
||
|
[$.COLGROUP]: IN_TABLE_MODE,
|
||
|
[$.TBODY]: IN_TABLE_MODE,
|
||
|
[$.TFOOT]: IN_TABLE_MODE,
|
||
|
[$.THEAD]: IN_TABLE_MODE,
|
||
|
[$.COL]: IN_COLUMN_GROUP_MODE,
|
||
|
[$.TR]: IN_TABLE_BODY_MODE,
|
||
|
[$.TD]: IN_ROW_MODE,
|
||
|
[$.TH]: IN_ROW_MODE
|
||
|
};
|
||
|
|
||
|
//Token handlers map for insertion modes
|
||
|
const TOKEN_HANDLERS = {
|
||
|
[INITIAL_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenInInitialMode,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenInInitialMode,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: doctypeInInitialMode,
|
||
|
[Tokenizer.START_TAG_TOKEN]: tokenInInitialMode,
|
||
|
[Tokenizer.END_TAG_TOKEN]: tokenInInitialMode,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenInInitialMode
|
||
|
},
|
||
|
[BEFORE_HTML_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenBeforeHtml,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHtml,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagBeforeHtml,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagBeforeHtml,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenBeforeHtml
|
||
|
},
|
||
|
[BEFORE_HEAD_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenBeforeHead,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHead,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagBeforeHead,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagBeforeHead,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenBeforeHead
|
||
|
},
|
||
|
[IN_HEAD_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenInHead,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHead,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInHead,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInHead,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenInHead
|
||
|
},
|
||
|
[IN_HEAD_NO_SCRIPT_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenInHeadNoScript,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHeadNoScript,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInHeadNoScript,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInHeadNoScript,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenInHeadNoScript
|
||
|
},
|
||
|
[AFTER_HEAD_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenAfterHead,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterHead,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagAfterHead,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagAfterHead,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenAfterHead
|
||
|
},
|
||
|
[IN_BODY_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInBody,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInBody,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInBody,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[TEXT_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInText,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInText
|
||
|
},
|
||
|
[IN_TABLE_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInTable,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInTable,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_TABLE_TEXT_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInTableText,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInTableText,
|
||
|
[Tokenizer.COMMENT_TOKEN]: tokenInTableText,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: tokenInTableText,
|
||
|
[Tokenizer.START_TAG_TOKEN]: tokenInTableText,
|
||
|
[Tokenizer.END_TAG_TOKEN]: tokenInTableText,
|
||
|
[Tokenizer.EOF_TOKEN]: tokenInTableText
|
||
|
},
|
||
|
[IN_CAPTION_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInBody,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInCaption,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInCaption,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_COLUMN_GROUP_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenInColumnGroup,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenInColumnGroup,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInColumnGroup,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInColumnGroup,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_TABLE_BODY_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInTableBody,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInTableBody,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_ROW_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInRow,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInRow,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_CELL_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInBody,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInCell,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInCell,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_SELECT_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInSelect,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInSelect,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_SELECT_IN_TABLE_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInSelectInTable,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInSelectInTable,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInBody
|
||
|
},
|
||
|
[IN_TEMPLATE_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: characterInBody,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInTemplate,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInTemplate,
|
||
|
[Tokenizer.EOF_TOKEN]: eofInTemplate
|
||
|
},
|
||
|
[AFTER_BODY_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenAfterBody,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterBody,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendCommentToRootHtmlElement,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagAfterBody,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagAfterBody,
|
||
|
[Tokenizer.EOF_TOKEN]: stopParsing
|
||
|
},
|
||
|
[IN_FRAMESET_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagInFrameset,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagInFrameset,
|
||
|
[Tokenizer.EOF_TOKEN]: stopParsing
|
||
|
},
|
||
|
[AFTER_FRAMESET_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendComment,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagAfterFrameset,
|
||
|
[Tokenizer.END_TAG_TOKEN]: endTagAfterFrameset,
|
||
|
[Tokenizer.EOF_TOKEN]: stopParsing
|
||
|
},
|
||
|
[AFTER_AFTER_BODY_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: tokenAfterAfterBody,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterAfterBody,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendCommentToDocument,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagAfterAfterBody,
|
||
|
[Tokenizer.END_TAG_TOKEN]: tokenAfterAfterBody,
|
||
|
[Tokenizer.EOF_TOKEN]: stopParsing
|
||
|
},
|
||
|
[AFTER_AFTER_FRAMESET_MODE]: {
|
||
|
[Tokenizer.CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
|
||
|
[Tokenizer.COMMENT_TOKEN]: appendCommentToDocument,
|
||
|
[Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.START_TAG_TOKEN]: startTagAfterAfterFrameset,
|
||
|
[Tokenizer.END_TAG_TOKEN]: ignoreToken,
|
||
|
[Tokenizer.EOF_TOKEN]: stopParsing
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//Parser
|
||
|
class Parser {
|
||
|
constructor(options) {
|
||
|
this.options = mergeOptions(DEFAULT_OPTIONS, options);
|
||
|
|
||
|
this.treeAdapter = this.options.treeAdapter;
|
||
|
this.pendingScript = null;
|
||
|
|
||
|
if (this.options.sourceCodeLocationInfo) {
|
||
|
Mixin.install(this, LocationInfoParserMixin);
|
||
|
}
|
||
|
|
||
|
if (this.options.onParseError) {
|
||
|
Mixin.install(this, ErrorReportingParserMixin, { onParseError: this.options.onParseError });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// API
|
||
|
parse(html) {
|
||
|
const document = this.treeAdapter.createDocument();
|
||
|
|
||
|
this._bootstrap(document, null);
|
||
|
this.tokenizer.write(html, true);
|
||
|
this._runParsingLoop(null);
|
||
|
|
||
|
return document;
|
||
|
}
|
||
|
|
||
|
parseFragment(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`.
|
||
|
const 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);
|
||
|
|
||
|
const rootElement = this.treeAdapter.getFirstChild(documentMock);
|
||
|
const fragment = this.treeAdapter.createDocumentFragment();
|
||
|
|
||
|
this._adoptNodes(rootElement, fragment);
|
||
|
|
||
|
return fragment;
|
||
|
}
|
||
|
|
||
|
//Bootstrap parser
|
||
|
_bootstrap(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;
|
||
|
}
|
||
|
|
||
|
//Errors
|
||
|
_err() {
|
||
|
// NOTE: err reporting is noop by default. Enabled by mixin.
|
||
|
}
|
||
|
|
||
|
//Parsing loop
|
||
|
_runParsingLoop(scriptHandler) {
|
||
|
while (!this.stopped) {
|
||
|
this._setupTokenizerCDATAMode();
|
||
|
|
||
|
const 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
runParsingLoopForCurrentChunk(writeCallback, scriptHandler) {
|
||
|
this._runParsingLoop(scriptHandler);
|
||
|
|
||
|
if (scriptHandler && this.pendingScript) {
|
||
|
const script = this.pendingScript;
|
||
|
|
||
|
this.pendingScript = null;
|
||
|
|
||
|
scriptHandler(script);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (writeCallback) {
|
||
|
writeCallback();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Text parsing
|
||
|
_setupTokenizerCDATAMode() {
|
||
|
const current = this._getAdjustedCurrentElement();
|
||
|
|
||
|
this.tokenizer.allowCDATA =
|
||
|
current &&
|
||
|
current !== this.document &&
|
||
|
this.treeAdapter.getNamespaceURI(current) !== NS.HTML &&
|
||
|
!this._isIntegrationPoint(current);
|
||
|
}
|
||
|
|
||
|
_switchToTextParsing(currentToken, nextTokenizerState) {
|
||
|
this._insertElement(currentToken, NS.HTML);
|
||
|
this.tokenizer.state = nextTokenizerState;
|
||
|
this.originalInsertionMode = this.insertionMode;
|
||
|
this.insertionMode = TEXT_MODE;
|
||
|
}
|
||
|
|
||
|
switchToPlaintextParsing() {
|
||
|
this.insertionMode = TEXT_MODE;
|
||
|
this.originalInsertionMode = IN_BODY_MODE;
|
||
|
this.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
|
||
|
}
|
||
|
|
||
|
//Fragment parsing
|
||
|
_getAdjustedCurrentElement() {
|
||
|
return this.openElements.stackTop === 0 && this.fragmentContext
|
||
|
? this.fragmentContext
|
||
|
: this.openElements.current;
|
||
|
}
|
||
|
|
||
|
_findFormInFragmentContext() {
|
||
|
let node = this.fragmentContext;
|
||
|
|
||
|
do {
|
||
|
if (this.treeAdapter.getTagName(node) === $.FORM) {
|
||
|
this.formElement = node;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
node = this.treeAdapter.getParentNode(node);
|
||
|
} while (node);
|
||
|
}
|
||
|
|
||
|
_initTokenizerForFragmentParsing() {
|
||
|
if (this.treeAdapter.getNamespaceURI(this.fragmentContext) === NS.HTML) {
|
||
|
const 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
|
||
|
_setDocumentType(token) {
|
||
|
const name = token.name || '';
|
||
|
const publicId = token.publicId || '';
|
||
|
const systemId = token.systemId || '';
|
||
|
|
||
|
this.treeAdapter.setDocumentType(this.document, name, publicId, systemId);
|
||
|
}
|
||
|
|
||
|
_attachElementToTree(element) {
|
||
|
if (this._shouldFosterParentOnInsertion()) {
|
||
|
this._fosterParentElement(element);
|
||
|
} else {
|
||
|
const parent = this.openElements.currentTmplContent || this.openElements.current;
|
||
|
|
||
|
this.treeAdapter.appendChild(parent, element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_appendElement(token, namespaceURI) {
|
||
|
const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
|
||
|
|
||
|
this._attachElementToTree(element);
|
||
|
}
|
||
|
|
||
|
_insertElement(token, namespaceURI) {
|
||
|
const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
|
||
|
|
||
|
this._attachElementToTree(element);
|
||
|
this.openElements.push(element);
|
||
|
}
|
||
|
|
||
|
_insertFakeElement(tagName) {
|
||
|
const element = this.treeAdapter.createElement(tagName, NS.HTML, []);
|
||
|
|
||
|
this._attachElementToTree(element);
|
||
|
this.openElements.push(element);
|
||
|
}
|
||
|
|
||
|
_insertTemplate(token) {
|
||
|
const tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs);
|
||
|
const content = this.treeAdapter.createDocumentFragment();
|
||
|
|
||
|
this.treeAdapter.setTemplateContent(tmpl, content);
|
||
|
this._attachElementToTree(tmpl);
|
||
|
this.openElements.push(tmpl);
|
||
|
}
|
||
|
|
||
|
_insertFakeRootElement() {
|
||
|
const element = this.treeAdapter.createElement($.HTML, NS.HTML, []);
|
||
|
|
||
|
this.treeAdapter.appendChild(this.openElements.current, element);
|
||
|
this.openElements.push(element);
|
||
|
}
|
||
|
|
||
|
_appendCommentNode(token, parent) {
|
||
|
const commentNode = this.treeAdapter.createCommentNode(token.data);
|
||
|
|
||
|
this.treeAdapter.appendChild(parent, commentNode);
|
||
|
}
|
||
|
|
||
|
_insertCharacters(token) {
|
||
|
if (this._shouldFosterParentOnInsertion()) {
|
||
|
this._fosterParentText(token.chars);
|
||
|
} else {
|
||
|
const parent = this.openElements.currentTmplContent || this.openElements.current;
|
||
|
|
||
|
this.treeAdapter.insertText(parent, token.chars);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_adoptNodes(donor, recipient) {
|
||
|
for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) {
|
||
|
this.treeAdapter.detachNode(child);
|
||
|
this.treeAdapter.appendChild(recipient, child);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Token processing
|
||
|
_shouldProcessTokenInForeignContent(token) {
|
||
|
const current = this._getAdjustedCurrentElement();
|
||
|
|
||
|
if (!current || current === this.document) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
const 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;
|
||
|
}
|
||
|
|
||
|
const isCharacterToken =
|
||
|
token.type === Tokenizer.CHARACTER_TOKEN ||
|
||
|
token.type === Tokenizer.NULL_CHARACTER_TOKEN ||
|
||
|
token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN;
|
||
|
|
||
|
const 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;
|
||
|
}
|
||
|
|
||
|
_processToken(token) {
|
||
|
TOKEN_HANDLERS[this.insertionMode][token.type](this, token);
|
||
|
}
|
||
|
|
||
|
_processTokenInBodyMode(token) {
|
||
|
TOKEN_HANDLERS[IN_BODY_MODE][token.type](this, token);
|
||
|
}
|
||
|
|
||
|
_processTokenInForeignContent(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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_processInputToken(token) {
|
||
|
if (this._shouldProcessTokenInForeignContent(token)) {
|
||
|
this._processTokenInForeignContent(token);
|
||
|
} else {
|
||
|
this._processToken(token);
|
||
|
}
|
||
|
|
||
|
if (token.type === Tokenizer.START_TAG_TOKEN && token.selfClosing && !token.ackSelfClosing) {
|
||
|
this._err(ERR.nonVoidHtmlElementStartTagWithTrailingSolidus);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Integration points
|
||
|
_isIntegrationPoint(element, foreignNS) {
|
||
|
const tn = this.treeAdapter.getTagName(element);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(element);
|
||
|
const attrs = this.treeAdapter.getAttrList(element);
|
||
|
|
||
|
return foreignContent.isIntegrationPoint(tn, ns, attrs, foreignNS);
|
||
|
}
|
||
|
|
||
|
//Active formatting elements reconstruction
|
||
|
_reconstructActiveFormattingElements() {
|
||
|
const listLength = this.activeFormattingElements.length;
|
||
|
|
||
|
if (listLength) {
|
||
|
let unopenIdx = listLength;
|
||
|
let 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 (let 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
|
||
|
_closeTableCell() {
|
||
|
this.openElements.generateImpliedEndTags();
|
||
|
this.openElements.popUntilTableCellPopped();
|
||
|
this.activeFormattingElements.clearToLastMarker();
|
||
|
this.insertionMode = IN_ROW_MODE;
|
||
|
}
|
||
|
|
||
|
_closePElement() {
|
||
|
this.openElements.generateImpliedEndTagsWithExclusion($.P);
|
||
|
this.openElements.popUntilTagNamePopped($.P);
|
||
|
}
|
||
|
|
||
|
//Insertion modes
|
||
|
_resetInsertionMode() {
|
||
|
for (let i = this.openElements.stackTop, last = false; i >= 0; i--) {
|
||
|
let element = this.openElements.items[i];
|
||
|
|
||
|
if (i === 0) {
|
||
|
last = true;
|
||
|
|
||
|
if (this.fragmentContext) {
|
||
|
element = this.fragmentContext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const tn = this.treeAdapter.getTagName(element);
|
||
|
const 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_resetInsertionModeForSelect(selectIdx) {
|
||
|
if (selectIdx > 0) {
|
||
|
for (let i = selectIdx - 1; i > 0; i--) {
|
||
|
const ancestor = this.openElements.items[i];
|
||
|
const 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;
|
||
|
}
|
||
|
|
||
|
_pushTmplInsertionMode(mode) {
|
||
|
this.tmplInsertionModeStack.push(mode);
|
||
|
this.tmplInsertionModeStackTop++;
|
||
|
this.currentTmplInsertionMode = mode;
|
||
|
}
|
||
|
|
||
|
_popTmplInsertionMode() {
|
||
|
this.tmplInsertionModeStack.pop();
|
||
|
this.tmplInsertionModeStackTop--;
|
||
|
this.currentTmplInsertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStackTop];
|
||
|
}
|
||
|
|
||
|
//Foster parenting
|
||
|
_isElementCausesFosterParenting(element) {
|
||
|
const tn = this.treeAdapter.getTagName(element);
|
||
|
|
||
|
return tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR;
|
||
|
}
|
||
|
|
||
|
_shouldFosterParentOnInsertion() {
|
||
|
return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.current);
|
||
|
}
|
||
|
|
||
|
_findFosterParentingLocation() {
|
||
|
const location = {
|
||
|
parent: null,
|
||
|
beforeElement: null
|
||
|
};
|
||
|
|
||
|
for (let i = this.openElements.stackTop; i >= 0; i--) {
|
||
|
const openElement = this.openElements.items[i];
|
||
|
const tn = this.treeAdapter.getTagName(openElement);
|
||
|
const 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;
|
||
|
}
|
||
|
|
||
|
_fosterParentElement(element) {
|
||
|
const location = this._findFosterParentingLocation();
|
||
|
|
||
|
if (location.beforeElement) {
|
||
|
this.treeAdapter.insertBefore(location.parent, element, location.beforeElement);
|
||
|
} else {
|
||
|
this.treeAdapter.appendChild(location.parent, element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_fosterParentText(chars) {
|
||
|
const location = this._findFosterParentingLocation();
|
||
|
|
||
|
if (location.beforeElement) {
|
||
|
this.treeAdapter.insertTextBefore(location.parent, chars, location.beforeElement);
|
||
|
} else {
|
||
|
this.treeAdapter.insertText(location.parent, chars);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Special elements
|
||
|
_isSpecialElement(element) {
|
||
|
const tn = this.treeAdapter.getTagName(element);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(element);
|
||
|
|
||
|
return HTML.SPECIAL_ELEMENTS[ns][tn];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = Parser;
|
||
|
|
||
|
//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) {
|
||
|
let 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) {
|
||
|
let furthestBlock = null;
|
||
|
|
||
|
for (let i = p.openElements.stackTop; i >= 0; i--) {
|
||
|
const 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) {
|
||
|
let lastElement = furthestBlock;
|
||
|
let nextElement = p.openElements.getCommonAncestor(furthestBlock);
|
||
|
|
||
|
for (let 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);
|
||
|
|
||
|
const elementEntry = p.activeFormattingElements.getElementEntry(element);
|
||
|
const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
|
||
|
const 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) {
|
||
|
const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
|
||
|
const 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 {
|
||
|
const tn = p.treeAdapter.getTagName(commonAncestor);
|
||
|
const 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) {
|
||
|
const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
|
||
|
const token = formattingElementEntry.token;
|
||
|
const 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) {
|
||
|
let formattingElementEntry;
|
||
|
|
||
|
for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
|
||
|
formattingElementEntry = aaObtainFormattingElementEntry(p, token, formattingElementEntry);
|
||
|
|
||
|
if (!formattingElementEntry) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry);
|
||
|
|
||
|
if (!furthestBlock) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
p.activeFormattingElements.bookmark = formattingElementEntry;
|
||
|
|
||
|
const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element);
|
||
|
const 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 misplacedDoctype(p) {
|
||
|
p._err(ERR.misplacedDoctype);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
// The "initial" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function doctypeInInitialMode(p, token) {
|
||
|
p._setDocumentType(token);
|
||
|
|
||
|
const mode = token.forceQuirks ? HTML.DOCUMENT_MODE.QUIRKS : doctype.getDocumentMode(token);
|
||
|
|
||
|
if (!doctype.isConforming(token)) {
|
||
|
p._err(ERR.nonConformingDoctype);
|
||
|
}
|
||
|
|
||
|
p.treeAdapter.setDocumentMode(p.document, mode);
|
||
|
|
||
|
p.insertionMode = BEFORE_HTML_MODE;
|
||
|
}
|
||
|
|
||
|
function tokenInInitialMode(p, token) {
|
||
|
p._err(ERR.missingDoctype, { beforeToken: true });
|
||
|
p.treeAdapter.setDocumentMode(p.document, HTML.DOCUMENT_MODE.QUIRKS);
|
||
|
p.insertionMode = BEFORE_HTML_MODE;
|
||
|
p._processToken(token);
|
||
|
}
|
||
|
|
||
|
// 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) {
|
||
|
const 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);
|
||
|
}
|
||
|
|
||
|
// The "before head" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagBeforeHead(p, token) {
|
||
|
const 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) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) {
|
||
|
tokenBeforeHead(p, token);
|
||
|
} else {
|
||
|
p._err(ERR.endTagWithoutMatchingOpenElement);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function tokenBeforeHead(p, token) {
|
||
|
p._insertFakeElement($.HEAD);
|
||
|
p.headElement = p.openElements.current;
|
||
|
p.insertionMode = IN_HEAD_MODE;
|
||
|
p._processToken(token);
|
||
|
}
|
||
|
|
||
|
// The "in head" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInHead(p, token) {
|
||
|
const 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);
|
||
|
token.ackSelfClosing = true;
|
||
|
} else if (tn === $.TITLE) {
|
||
|
p._switchToTextParsing(token, Tokenizer.MODE.RCDATA);
|
||
|
} else if (tn === $.NOSCRIPT) {
|
||
|
if (p.options.scriptingEnabled) {
|
||
|
p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
|
||
|
} else {
|
||
|
p._insertElement(token, NS.HTML);
|
||
|
p.insertionMode = IN_HEAD_NO_SCRIPT_MODE;
|
||
|
}
|
||
|
} else if (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) {
|
||
|
p._err(ERR.misplacedStartTagForHeadElement);
|
||
|
} else {
|
||
|
tokenInHead(p, token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function endTagInHead(p, token) {
|
||
|
const 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) {
|
||
|
if (p.openElements.tmplCount > 0) {
|
||
|
p.openElements.generateImpliedEndTagsThoroughly();
|
||
|
|
||
|
if (p.openElements.currentTagName !== $.TEMPLATE) {
|
||
|
p._err(ERR.closingOfElementWithOpenChildElements);
|
||
|
}
|
||
|
|
||
|
p.openElements.popUntilTagNamePopped($.TEMPLATE);
|
||
|
p.activeFormattingElements.clearToLastMarker();
|
||
|
p._popTmplInsertionMode();
|
||
|
p._resetInsertionMode();
|
||
|
} else {
|
||
|
p._err(ERR.endTagWithoutMatchingOpenElement);
|
||
|
}
|
||
|
} else {
|
||
|
p._err(ERR.endTagWithoutMatchingOpenElement);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function tokenInHead(p, token) {
|
||
|
p.openElements.pop();
|
||
|
p.insertionMode = AFTER_HEAD_MODE;
|
||
|
p._processToken(token);
|
||
|
}
|
||
|
|
||
|
// The "in head no script" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInHeadNoScript(p, token) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.HTML) {
|
||
|
startTagInBody(p, token);
|
||
|
} else if (
|
||
|
tn === $.BASEFONT ||
|
||
|
tn === $.BGSOUND ||
|
||
|
tn === $.HEAD ||
|
||
|
tn === $.LINK ||
|
||
|
tn === $.META ||
|
||
|
tn === $.NOFRAMES ||
|
||
|
tn === $.STYLE
|
||
|
) {
|
||
|
startTagInHead(p, token);
|
||
|
} else if (tn === $.NOSCRIPT) {
|
||
|
p._err(ERR.nestedNoscriptInHead);
|
||
|
} else {
|
||
|
tokenInHeadNoScript(p, token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function endTagInHeadNoScript(p, token) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.NOSCRIPT) {
|
||
|
p.openElements.pop();
|
||
|
p.insertionMode = IN_HEAD_MODE;
|
||
|
} else if (tn === $.BR) {
|
||
|
tokenInHeadNoScript(p, token);
|
||
|
} else {
|
||
|
p._err(ERR.endTagWithoutMatchingOpenElement);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function tokenInHeadNoScript(p, token) {
|
||
|
const errCode =
|
||
|
token.type === Tokenizer.EOF_TOKEN ? ERR.openElementsLeftAfterEof : ERR.disallowedContentInNoscriptInHead;
|
||
|
|
||
|
p._err(errCode);
|
||
|
p.openElements.pop();
|
||
|
p.insertionMode = IN_HEAD_MODE;
|
||
|
p._processToken(token);
|
||
|
}
|
||
|
|
||
|
// The "after head" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagAfterHead(p, token) {
|
||
|
const 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._err(ERR.abandonedHeadElementChild);
|
||
|
p.openElements.push(p.headElement);
|
||
|
startTagInHead(p, token);
|
||
|
p.openElements.remove(p.headElement);
|
||
|
} else if (tn === $.HEAD) {
|
||
|
p._err(ERR.misplacedStartTagForHeadElement);
|
||
|
} else {
|
||
|
tokenAfterHead(p, token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function endTagAfterHead(p, token) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.BODY || tn === $.HTML || tn === $.BR) {
|
||
|
tokenAfterHead(p, token);
|
||
|
} else if (tn === $.TEMPLATE) {
|
||
|
endTagInHead(p, token);
|
||
|
} else {
|
||
|
p._err(ERR.endTagWithoutMatchingOpenElement);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function tokenAfterHead(p, token) {
|
||
|
p._insertFakeElement($.BODY);
|
||
|
p.insertionMode = IN_BODY_MODE;
|
||
|
p._processToken(token);
|
||
|
}
|
||
|
|
||
|
// 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) {
|
||
|
const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
|
||
|
|
||
|
if (bodyElement && p.openElements.tmplCount === 0) {
|
||
|
p.framesetOk = false;
|
||
|
p.treeAdapter.adoptAttributes(bodyElement, token.attrs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function framesetStartTagInBody(p, token) {
|
||
|
const 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();
|
||
|
}
|
||
|
|
||
|
const 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) {
|
||
|
const 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;
|
||
|
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
for (let i = p.openElements.stackTop; i >= 0; i--) {
|
||
|
const element = p.openElements.items[i];
|
||
|
const elementTn = p.treeAdapter.getTagName(element);
|
||
|
let 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) {
|
||
|
const 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;
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
function inputStartTagInBody(p, token) {
|
||
|
p._reconstructActiveFormattingElements();
|
||
|
p._appendElement(token, NS.HTML);
|
||
|
|
||
|
const inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE);
|
||
|
|
||
|
if (!inputType || inputType.toLowerCase() !== HIDDEN_INPUT_TYPE) {
|
||
|
p.framesetOk = false;
|
||
|
}
|
||
|
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
function paramStartTagInBody(p, token) {
|
||
|
p._appendElement(token, NS.HTML);
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
function hrStartTagInBody(p, token) {
|
||
|
if (p.openElements.hasInButtonScope($.P)) {
|
||
|
p._closePElement();
|
||
|
}
|
||
|
|
||
|
p._appendElement(token, NS.HTML);
|
||
|
p.framesetOk = false;
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
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 menuStartTagInBody(p, token) {
|
||
|
if (p.openElements.hasInButtonScope($.P)) {
|
||
|
p._closePElement();
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
const 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 || tn === $.MENU) {
|
||
|
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 ||
|
||
|
tn === $.DIALOG
|
||
|
) {
|
||
|
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 === $.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) {
|
||
|
if (p.options.scriptingEnabled) {
|
||
|
noembedStartTagInBody(p, token);
|
||
|
} else {
|
||
|
genericStartTagInBody(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) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (p.openElements.hasInScope(tn)) {
|
||
|
p.openElements.generateImpliedEndTags();
|
||
|
p.openElements.popUntilTagNamePopped(tn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function formEndTagInBody(p) {
|
||
|
const inTemplate = p.openElements.tmplCount > 0;
|
||
|
const 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) {
|
||
|
const 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) {
|
||
|
const 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) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
for (let i = p.openElements.stackTop; i > 0; i--) {
|
||
|
const 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) {
|
||
|
const 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 || tn === $.PRE) {
|
||
|
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 ||
|
||
|
tn === $.DIALOG
|
||
|
) {
|
||
|
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 ||
|
||
|
tn === $.LISTING
|
||
|
) {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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._err(ERR.eofInElementThatCanContainOnlyText);
|
||
|
p.openElements.pop();
|
||
|
p.insertionMode = p.originalInsertionMode;
|
||
|
p._processToken(token);
|
||
|
}
|
||
|
|
||
|
// The "in table" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function characterInTable(p, token) {
|
||
|
const 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) {
|
||
|
const inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE);
|
||
|
|
||
|
if (inputType && inputType.toLowerCase() === HIDDEN_INPUT_TYPE) {
|
||
|
p._appendElement(token, NS.HTML);
|
||
|
} else {
|
||
|
tokenInTable(p, token);
|
||
|
}
|
||
|
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
const 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) {
|
||
|
const 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) {
|
||
|
const savedFosterParentingState = p.fosterParentingEnabled;
|
||
|
|
||
|
p.fosterParentingEnabled = true;
|
||
|
p._processTokenInBodyMode(token);
|
||
|
p.fosterParentingEnabled = savedFosterParentingState;
|
||
|
}
|
||
|
|
||
|
// 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) {
|
||
|
let 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);
|
||
|
}
|
||
|
|
||
|
// The "in caption" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInCaption(p, token) {
|
||
|
const 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) {
|
||
|
const 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "in column group" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInColumnGroup(p, token) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.HTML) {
|
||
|
startTagInBody(p, token);
|
||
|
} else if (tn === $.COL) {
|
||
|
p._appendElement(token, NS.HTML);
|
||
|
token.ackSelfClosing = true;
|
||
|
} else if (tn === $.TEMPLATE) {
|
||
|
startTagInHead(p, token);
|
||
|
} else {
|
||
|
tokenInColumnGroup(p, token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function endTagInColumnGroup(p, token) {
|
||
|
const 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "in table body" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInTableBody(p, token) {
|
||
|
const 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) {
|
||
|
const 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "in row" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInRow(p, token) {
|
||
|
const 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) {
|
||
|
const 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "in cell" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInCell(p, token) {
|
||
|
const 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) {
|
||
|
const 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "in select" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInSelect(p, token) {
|
||
|
const 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) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.OPTGROUP) {
|
||
|
const prevOpenElement = p.openElements.items[p.openElements.stackTop - 1];
|
||
|
const 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) {
|
||
|
const 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) {
|
||
|
const 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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "in template" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInTemplate(p, token) {
|
||
|
const 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 {
|
||
|
const 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
|
||
|
// The "in frameset" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagInFrameset(p, token) {
|
||
|
const 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);
|
||
|
token.ackSelfClosing = true;
|
||
|
} 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The "after frameset" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagAfterFrameset(p, token) {
|
||
|
const 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
|
||
|
// The "after after frameset" insertion mode
|
||
|
//------------------------------------------------------------------
|
||
|
function startTagAfterAfterFrameset(p, token) {
|
||
|
const tn = token.tagName;
|
||
|
|
||
|
if (tn === $.HTML) {
|
||
|
startTagInBody(p, token);
|
||
|
} else if (tn === $.NOFRAMES) {
|
||
|
startTagInHead(p, token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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 {
|
||
|
const current = p._getAdjustedCurrentElement();
|
||
|
const 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);
|
||
|
}
|
||
|
|
||
|
token.ackSelfClosing = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function endTagInForeignContent(p, token) {
|
||
|
for (let i = p.openElements.stackTop; i > 0; i--) {
|
||
|
const 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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
},{"../common/doctype":1,"../common/error-codes":2,"../common/foreign-content":3,"../common/html":4,"../common/unicode":5,"../extensions/error-reporting/parser-mixin":7,"../extensions/location-info/parser-mixin":11,"../tokenizer":19,"../tree-adapters/default":22,"../utils/merge-options":23,"../utils/mixin":24,"./formatting-element-list":15,"./open-element-stack":17}],17:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const HTML = require('../common/html');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = HTML.TAG_NAMES;
|
||
|
const NS = HTML.NAMESPACES;
|
||
|
|
||
|
//Element utils
|
||
|
|
||
|
//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
|
||
|
//It's faster than using dictionary.
|
||
|
function isImpliedEndTagRequired(tn) {
|
||
|
switch (tn.length) {
|
||
|
case 1:
|
||
|
return tn === $.P;
|
||
|
|
||
|
case 2:
|
||
|
return tn === $.RB || tn === $.RP || tn === $.RT || tn === $.DD || tn === $.DT || tn === $.LI;
|
||
|
|
||
|
case 3:
|
||
|
return tn === $.RTC;
|
||
|
|
||
|
case 6:
|
||
|
return tn === $.OPTION;
|
||
|
|
||
|
case 8:
|
||
|
return tn === $.OPTGROUP;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function isImpliedEndTagRequiredThoroughly(tn) {
|
||
|
switch (tn.length) {
|
||
|
case 1:
|
||
|
return tn === $.P;
|
||
|
|
||
|
case 2:
|
||
|
return (
|
||
|
tn === $.RB ||
|
||
|
tn === $.RP ||
|
||
|
tn === $.RT ||
|
||
|
tn === $.DD ||
|
||
|
tn === $.DT ||
|
||
|
tn === $.LI ||
|
||
|
tn === $.TD ||
|
||
|
tn === $.TH ||
|
||
|
tn === $.TR
|
||
|
);
|
||
|
|
||
|
case 3:
|
||
|
return tn === $.RTC;
|
||
|
|
||
|
case 5:
|
||
|
return tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD;
|
||
|
|
||
|
case 6:
|
||
|
return tn === $.OPTION;
|
||
|
|
||
|
case 7:
|
||
|
return tn === $.CAPTION;
|
||
|
|
||
|
case 8:
|
||
|
return tn === $.OPTGROUP || tn === $.COLGROUP;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function isScopingElement(tn, ns) {
|
||
|
switch (tn.length) {
|
||
|
case 2:
|
||
|
if (tn === $.TD || tn === $.TH) {
|
||
|
return ns === NS.HTML;
|
||
|
} else if (tn === $.MI || tn === $.MO || tn === $.MN || tn === $.MS) {
|
||
|
return ns === NS.MATHML;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
if (tn === $.HTML) {
|
||
|
return ns === NS.HTML;
|
||
|
} else if (tn === $.DESC) {
|
||
|
return ns === NS.SVG;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 5:
|
||
|
if (tn === $.TABLE) {
|
||
|
return ns === NS.HTML;
|
||
|
} else if (tn === $.MTEXT) {
|
||
|
return ns === NS.MATHML;
|
||
|
} else if (tn === $.TITLE) {
|
||
|
return ns === NS.SVG;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 6:
|
||
|
return (tn === $.APPLET || tn === $.OBJECT) && ns === NS.HTML;
|
||
|
|
||
|
case 7:
|
||
|
return (tn === $.CAPTION || tn === $.MARQUEE) && ns === NS.HTML;
|
||
|
|
||
|
case 8:
|
||
|
return tn === $.TEMPLATE && ns === NS.HTML;
|
||
|
|
||
|
case 13:
|
||
|
return tn === $.FOREIGN_OBJECT && ns === NS.SVG;
|
||
|
|
||
|
case 14:
|
||
|
return tn === $.ANNOTATION_XML && ns === NS.MATHML;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//Stack of open elements
|
||
|
class OpenElementStack {
|
||
|
constructor(document, treeAdapter) {
|
||
|
this.stackTop = -1;
|
||
|
this.items = [];
|
||
|
this.current = document;
|
||
|
this.currentTagName = null;
|
||
|
this.currentTmplContent = null;
|
||
|
this.tmplCount = 0;
|
||
|
this.treeAdapter = treeAdapter;
|
||
|
}
|
||
|
|
||
|
//Index of element
|
||
|
_indexOf(element) {
|
||
|
let idx = -1;
|
||
|
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
if (this.items[i] === element) {
|
||
|
idx = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return idx;
|
||
|
}
|
||
|
|
||
|
//Update current element
|
||
|
_isInTemplate() {
|
||
|
return this.currentTagName === $.TEMPLATE && this.treeAdapter.getNamespaceURI(this.current) === NS.HTML;
|
||
|
}
|
||
|
|
||
|
_updateCurrentElement() {
|
||
|
this.current = this.items[this.stackTop];
|
||
|
this.currentTagName = this.current && this.treeAdapter.getTagName(this.current);
|
||
|
|
||
|
this.currentTmplContent = this._isInTemplate() ? this.treeAdapter.getTemplateContent(this.current) : null;
|
||
|
}
|
||
|
|
||
|
//Mutations
|
||
|
push(element) {
|
||
|
this.items[++this.stackTop] = element;
|
||
|
this._updateCurrentElement();
|
||
|
|
||
|
if (this._isInTemplate()) {
|
||
|
this.tmplCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pop() {
|
||
|
this.stackTop--;
|
||
|
|
||
|
if (this.tmplCount > 0 && this._isInTemplate()) {
|
||
|
this.tmplCount--;
|
||
|
}
|
||
|
|
||
|
this._updateCurrentElement();
|
||
|
}
|
||
|
|
||
|
replace(oldElement, newElement) {
|
||
|
const idx = this._indexOf(oldElement);
|
||
|
|
||
|
this.items[idx] = newElement;
|
||
|
|
||
|
if (idx === this.stackTop) {
|
||
|
this._updateCurrentElement();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
insertAfter(referenceElement, newElement) {
|
||
|
const insertionIdx = this._indexOf(referenceElement) + 1;
|
||
|
|
||
|
this.items.splice(insertionIdx, 0, newElement);
|
||
|
|
||
|
if (insertionIdx === ++this.stackTop) {
|
||
|
this._updateCurrentElement();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
popUntilTagNamePopped(tagName) {
|
||
|
while (this.stackTop > -1) {
|
||
|
const tn = this.currentTagName;
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.current);
|
||
|
|
||
|
this.pop();
|
||
|
|
||
|
if (tn === tagName && ns === NS.HTML) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
popUntilElementPopped(element) {
|
||
|
while (this.stackTop > -1) {
|
||
|
const poppedElement = this.current;
|
||
|
|
||
|
this.pop();
|
||
|
|
||
|
if (poppedElement === element) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
popUntilNumberedHeaderPopped() {
|
||
|
while (this.stackTop > -1) {
|
||
|
const tn = this.currentTagName;
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.current);
|
||
|
|
||
|
this.pop();
|
||
|
|
||
|
if (
|
||
|
tn === $.H1 ||
|
||
|
tn === $.H2 ||
|
||
|
tn === $.H3 ||
|
||
|
tn === $.H4 ||
|
||
|
tn === $.H5 ||
|
||
|
(tn === $.H6 && ns === NS.HTML)
|
||
|
) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
popUntilTableCellPopped() {
|
||
|
while (this.stackTop > -1) {
|
||
|
const tn = this.currentTagName;
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.current);
|
||
|
|
||
|
this.pop();
|
||
|
|
||
|
if (tn === $.TD || (tn === $.TH && ns === NS.HTML)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
popAllUpToHtmlElement() {
|
||
|
//NOTE: here we assume that root <html> element is always first in the open element stack, so
|
||
|
//we perform this fast stack clean up.
|
||
|
this.stackTop = 0;
|
||
|
this._updateCurrentElement();
|
||
|
}
|
||
|
|
||
|
clearBackToTableContext() {
|
||
|
while (
|
||
|
(this.currentTagName !== $.TABLE && this.currentTagName !== $.TEMPLATE && this.currentTagName !== $.HTML) ||
|
||
|
this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
|
||
|
) {
|
||
|
this.pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clearBackToTableBodyContext() {
|
||
|
while (
|
||
|
(this.currentTagName !== $.TBODY &&
|
||
|
this.currentTagName !== $.TFOOT &&
|
||
|
this.currentTagName !== $.THEAD &&
|
||
|
this.currentTagName !== $.TEMPLATE &&
|
||
|
this.currentTagName !== $.HTML) ||
|
||
|
this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
|
||
|
) {
|
||
|
this.pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clearBackToTableRowContext() {
|
||
|
while (
|
||
|
(this.currentTagName !== $.TR && this.currentTagName !== $.TEMPLATE && this.currentTagName !== $.HTML) ||
|
||
|
this.treeAdapter.getNamespaceURI(this.current) !== NS.HTML
|
||
|
) {
|
||
|
this.pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
remove(element) {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
if (this.items[i] === element) {
|
||
|
this.items.splice(i, 1);
|
||
|
this.stackTop--;
|
||
|
this._updateCurrentElement();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Search
|
||
|
tryPeekProperlyNestedBodyElement() {
|
||
|
//Properly nested <body> element (should be second element in stack).
|
||
|
const element = this.items[1];
|
||
|
|
||
|
return element && this.treeAdapter.getTagName(element) === $.BODY ? element : null;
|
||
|
}
|
||
|
|
||
|
contains(element) {
|
||
|
return this._indexOf(element) > -1;
|
||
|
}
|
||
|
|
||
|
getCommonAncestor(element) {
|
||
|
let elementIdx = this._indexOf(element);
|
||
|
|
||
|
return --elementIdx >= 0 ? this.items[elementIdx] : null;
|
||
|
}
|
||
|
|
||
|
isRootHtmlElementCurrent() {
|
||
|
return this.stackTop === 0 && this.currentTagName === $.HTML;
|
||
|
}
|
||
|
|
||
|
//Element in scope
|
||
|
hasInScope(tagName) {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (tn === tagName && ns === NS.HTML) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (isScopingElement(tn, ns)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hasNumberedHeaderInScope() {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (
|
||
|
(tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) &&
|
||
|
ns === NS.HTML
|
||
|
) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (isScopingElement(tn, ns)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hasInListItemScope(tagName) {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (tn === tagName && ns === NS.HTML) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (((tn === $.UL || tn === $.OL) && ns === NS.HTML) || isScopingElement(tn, ns)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hasInButtonScope(tagName) {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (tn === tagName && ns === NS.HTML) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ((tn === $.BUTTON && ns === NS.HTML) || isScopingElement(tn, ns)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hasInTableScope(tagName) {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (ns !== NS.HTML) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (tn === tagName) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (tn === $.TABLE || tn === $.TEMPLATE || tn === $.HTML) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hasTableBodyContextInTableScope() {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (ns !== NS.HTML) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (tn === $.TBODY || tn === $.THEAD || tn === $.TFOOT) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (tn === $.TABLE || tn === $.HTML) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
hasInSelectScope(tagName) {
|
||
|
for (let i = this.stackTop; i >= 0; i--) {
|
||
|
const tn = this.treeAdapter.getTagName(this.items[i]);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(this.items[i]);
|
||
|
|
||
|
if (ns !== NS.HTML) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (tn === tagName) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (tn !== $.OPTION && tn !== $.OPTGROUP) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//Implied end tags
|
||
|
generateImpliedEndTags() {
|
||
|
while (isImpliedEndTagRequired(this.currentTagName)) {
|
||
|
this.pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
generateImpliedEndTagsThoroughly() {
|
||
|
while (isImpliedEndTagRequiredThoroughly(this.currentTagName)) {
|
||
|
this.pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
generateImpliedEndTagsWithExclusion(exclusionTagName) {
|
||
|
while (isImpliedEndTagRequired(this.currentTagName) && this.currentTagName !== exclusionTagName) {
|
||
|
this.pop();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = OpenElementStack;
|
||
|
|
||
|
},{"../common/html":4}],18:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const defaultTreeAdapter = require('../tree-adapters/default');
|
||
|
const mergeOptions = require('../utils/merge-options');
|
||
|
const doctype = require('../common/doctype');
|
||
|
const HTML = require('../common/html');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = HTML.TAG_NAMES;
|
||
|
const NS = HTML.NAMESPACES;
|
||
|
|
||
|
//Default serializer options
|
||
|
const DEFAULT_OPTIONS = {
|
||
|
treeAdapter: defaultTreeAdapter
|
||
|
};
|
||
|
|
||
|
//Escaping regexes
|
||
|
const AMP_REGEX = /&/g;
|
||
|
const NBSP_REGEX = /\u00a0/g;
|
||
|
const DOUBLE_QUOTE_REGEX = /"/g;
|
||
|
const LT_REGEX = /</g;
|
||
|
const GT_REGEX = />/g;
|
||
|
|
||
|
//Serializer
|
||
|
class Serializer {
|
||
|
constructor(node, options) {
|
||
|
this.options = mergeOptions(DEFAULT_OPTIONS, options);
|
||
|
this.treeAdapter = this.options.treeAdapter;
|
||
|
|
||
|
this.html = '';
|
||
|
this.startNode = node;
|
||
|
}
|
||
|
|
||
|
//API
|
||
|
serialize() {
|
||
|
this._serializeChildNodes(this.startNode);
|
||
|
|
||
|
return this.html;
|
||
|
}
|
||
|
|
||
|
//Internals
|
||
|
_serializeChildNodes(parentNode) {
|
||
|
const childNodes = this.treeAdapter.getChildNodes(parentNode);
|
||
|
|
||
|
if (childNodes) {
|
||
|
for (let i = 0, cnLength = childNodes.length; i < cnLength; i++) {
|
||
|
const currentNode = childNodes[i];
|
||
|
|
||
|
if (this.treeAdapter.isElementNode(currentNode)) {
|
||
|
this._serializeElement(currentNode);
|
||
|
} else if (this.treeAdapter.isTextNode(currentNode)) {
|
||
|
this._serializeTextNode(currentNode);
|
||
|
} else if (this.treeAdapter.isCommentNode(currentNode)) {
|
||
|
this._serializeCommentNode(currentNode);
|
||
|
} else if (this.treeAdapter.isDocumentTypeNode(currentNode)) {
|
||
|
this._serializeDocumentTypeNode(currentNode);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_serializeElement(node) {
|
||
|
const tn = this.treeAdapter.getTagName(node);
|
||
|
const ns = this.treeAdapter.getNamespaceURI(node);
|
||
|
|
||
|
this.html += '<' + tn;
|
||
|
this._serializeAttributes(node);
|
||
|
this.html += '>';
|
||
|
|
||
|
if (
|
||
|
tn !== $.AREA &&
|
||
|
tn !== $.BASE &&
|
||
|
tn !== $.BASEFONT &&
|
||
|
tn !== $.BGSOUND &&
|
||
|
tn !== $.BR &&
|
||
|
tn !== $.COL &&
|
||
|
tn !== $.EMBED &&
|
||
|
tn !== $.FRAME &&
|
||
|
tn !== $.HR &&
|
||
|
tn !== $.IMG &&
|
||
|
tn !== $.INPUT &&
|
||
|
tn !== $.KEYGEN &&
|
||
|
tn !== $.LINK &&
|
||
|
tn !== $.META &&
|
||
|
tn !== $.PARAM &&
|
||
|
tn !== $.SOURCE &&
|
||
|
tn !== $.TRACK &&
|
||
|
tn !== $.WBR
|
||
|
) {
|
||
|
const childNodesHolder =
|
||
|
tn === $.TEMPLATE && ns === NS.HTML ? this.treeAdapter.getTemplateContent(node) : node;
|
||
|
|
||
|
this._serializeChildNodes(childNodesHolder);
|
||
|
this.html += '</' + tn + '>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_serializeAttributes(node) {
|
||
|
const attrs = this.treeAdapter.getAttrList(node);
|
||
|
|
||
|
for (let i = 0, attrsLength = attrs.length; i < attrsLength; i++) {
|
||
|
const attr = attrs[i];
|
||
|
const value = Serializer.escapeString(attr.value, true);
|
||
|
|
||
|
this.html += ' ';
|
||
|
|
||
|
if (!attr.namespace) {
|
||
|
this.html += attr.name;
|
||
|
} else if (attr.namespace === NS.XML) {
|
||
|
this.html += 'xml:' + attr.name;
|
||
|
} else if (attr.namespace === NS.XMLNS) {
|
||
|
if (attr.name !== 'xmlns') {
|
||
|
this.html += 'xmlns:';
|
||
|
}
|
||
|
|
||
|
this.html += attr.name;
|
||
|
} else if (attr.namespace === NS.XLINK) {
|
||
|
this.html += 'xlink:' + attr.name;
|
||
|
} else {
|
||
|
this.html += attr.prefix + ':' + attr.name;
|
||
|
}
|
||
|
|
||
|
this.html += '="' + value + '"';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_serializeTextNode(node) {
|
||
|
const content = this.treeAdapter.getTextNodeContent(node);
|
||
|
const parent = this.treeAdapter.getParentNode(node);
|
||
|
let parentTn = void 0;
|
||
|
|
||
|
if (parent && this.treeAdapter.isElementNode(parent)) {
|
||
|
parentTn = this.treeAdapter.getTagName(parent);
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
parentTn === $.STYLE ||
|
||
|
parentTn === $.SCRIPT ||
|
||
|
parentTn === $.XMP ||
|
||
|
parentTn === $.IFRAME ||
|
||
|
parentTn === $.NOEMBED ||
|
||
|
parentTn === $.NOFRAMES ||
|
||
|
parentTn === $.PLAINTEXT ||
|
||
|
parentTn === $.NOSCRIPT
|
||
|
) {
|
||
|
this.html += content;
|
||
|
} else {
|
||
|
this.html += Serializer.escapeString(content, false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_serializeCommentNode(node) {
|
||
|
this.html += '<!--' + this.treeAdapter.getCommentNodeContent(node) + '-->';
|
||
|
}
|
||
|
|
||
|
_serializeDocumentTypeNode(node) {
|
||
|
const name = this.treeAdapter.getDocumentTypeNodeName(node);
|
||
|
|
||
|
this.html += '<' + doctype.serializeContent(name, null, null) + '>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// NOTE: used in tests and by rewriting stream
|
||
|
Serializer.escapeString = function(str, attrMode) {
|
||
|
str = str.replace(AMP_REGEX, '&').replace(NBSP_REGEX, ' ');
|
||
|
|
||
|
if (attrMode) {
|
||
|
str = str.replace(DOUBLE_QUOTE_REGEX, '"');
|
||
|
} else {
|
||
|
str = str.replace(LT_REGEX, '<').replace(GT_REGEX, '>');
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
};
|
||
|
|
||
|
module.exports = Serializer;
|
||
|
|
||
|
},{"../common/doctype":1,"../common/html":4,"../tree-adapters/default":22,"../utils/merge-options":23}],19:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const Preprocessor = require('./preprocessor');
|
||
|
const unicode = require('../common/unicode');
|
||
|
const neTree = require('./named-entity-data');
|
||
|
const ERR = require('../common/error-codes');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = unicode.CODE_POINTS;
|
||
|
const $$ = unicode.CODE_POINT_SEQUENCES;
|
||
|
|
||
|
//C1 Unicode control character reference replacements
|
||
|
const C1_CONTROLS_REFERENCE_REPLACEMENTS = {
|
||
|
0x80: 0x20ac,
|
||
|
0x82: 0x201a,
|
||
|
0x83: 0x0192,
|
||
|
0x84: 0x201e,
|
||
|
0x85: 0x2026,
|
||
|
0x86: 0x2020,
|
||
|
0x87: 0x2021,
|
||
|
0x88: 0x02c6,
|
||
|
0x89: 0x2030,
|
||
|
0x8a: 0x0160,
|
||
|
0x8b: 0x2039,
|
||
|
0x8c: 0x0152,
|
||
|
0x8e: 0x017d,
|
||
|
0x91: 0x2018,
|
||
|
0x92: 0x2019,
|
||
|
0x93: 0x201c,
|
||
|
0x94: 0x201d,
|
||
|
0x95: 0x2022,
|
||
|
0x96: 0x2013,
|
||
|
0x97: 0x2014,
|
||
|
0x98: 0x02dc,
|
||
|
0x99: 0x2122,
|
||
|
0x9a: 0x0161,
|
||
|
0x9b: 0x203a,
|
||
|
0x9c: 0x0153,
|
||
|
0x9e: 0x017e,
|
||
|
0x9f: 0x0178
|
||
|
};
|
||
|
|
||
|
// Named entity tree flags
|
||
|
const HAS_DATA_FLAG = 1 << 0;
|
||
|
const DATA_DUPLET_FLAG = 1 << 1;
|
||
|
const HAS_BRANCHES_FLAG = 1 << 2;
|
||
|
const MAX_BRANCH_MARKER_VALUE = HAS_DATA_FLAG | DATA_DUPLET_FLAG | HAS_BRANCHES_FLAG;
|
||
|
|
||
|
//States
|
||
|
const DATA_STATE = 'DATA_STATE';
|
||
|
const RCDATA_STATE = 'RCDATA_STATE';
|
||
|
const RAWTEXT_STATE = 'RAWTEXT_STATE';
|
||
|
const SCRIPT_DATA_STATE = 'SCRIPT_DATA_STATE';
|
||
|
const PLAINTEXT_STATE = 'PLAINTEXT_STATE';
|
||
|
const TAG_OPEN_STATE = 'TAG_OPEN_STATE';
|
||
|
const END_TAG_OPEN_STATE = 'END_TAG_OPEN_STATE';
|
||
|
const TAG_NAME_STATE = 'TAG_NAME_STATE';
|
||
|
const RCDATA_LESS_THAN_SIGN_STATE = 'RCDATA_LESS_THAN_SIGN_STATE';
|
||
|
const RCDATA_END_TAG_OPEN_STATE = 'RCDATA_END_TAG_OPEN_STATE';
|
||
|
const RCDATA_END_TAG_NAME_STATE = 'RCDATA_END_TAG_NAME_STATE';
|
||
|
const RAWTEXT_LESS_THAN_SIGN_STATE = 'RAWTEXT_LESS_THAN_SIGN_STATE';
|
||
|
const RAWTEXT_END_TAG_OPEN_STATE = 'RAWTEXT_END_TAG_OPEN_STATE';
|
||
|
const RAWTEXT_END_TAG_NAME_STATE = 'RAWTEXT_END_TAG_NAME_STATE';
|
||
|
const SCRIPT_DATA_LESS_THAN_SIGN_STATE = 'SCRIPT_DATA_LESS_THAN_SIGN_STATE';
|
||
|
const SCRIPT_DATA_END_TAG_OPEN_STATE = 'SCRIPT_DATA_END_TAG_OPEN_STATE';
|
||
|
const SCRIPT_DATA_END_TAG_NAME_STATE = 'SCRIPT_DATA_END_TAG_NAME_STATE';
|
||
|
const SCRIPT_DATA_ESCAPE_START_STATE = 'SCRIPT_DATA_ESCAPE_START_STATE';
|
||
|
const SCRIPT_DATA_ESCAPE_START_DASH_STATE = 'SCRIPT_DATA_ESCAPE_START_DASH_STATE';
|
||
|
const SCRIPT_DATA_ESCAPED_STATE = 'SCRIPT_DATA_ESCAPED_STATE';
|
||
|
const SCRIPT_DATA_ESCAPED_DASH_STATE = 'SCRIPT_DATA_ESCAPED_DASH_STATE';
|
||
|
const SCRIPT_DATA_ESCAPED_DASH_DASH_STATE = 'SCRIPT_DATA_ESCAPED_DASH_DASH_STATE';
|
||
|
const SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE = 'SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE';
|
||
|
const SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE = 'SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE';
|
||
|
const SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE = 'SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE';
|
||
|
const SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE';
|
||
|
const SCRIPT_DATA_DOUBLE_ESCAPED_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_STATE';
|
||
|
const SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE';
|
||
|
const SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE';
|
||
|
const SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE';
|
||
|
const SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE';
|
||
|
const BEFORE_ATTRIBUTE_NAME_STATE = 'BEFORE_ATTRIBUTE_NAME_STATE';
|
||
|
const ATTRIBUTE_NAME_STATE = 'ATTRIBUTE_NAME_STATE';
|
||
|
const AFTER_ATTRIBUTE_NAME_STATE = 'AFTER_ATTRIBUTE_NAME_STATE';
|
||
|
const BEFORE_ATTRIBUTE_VALUE_STATE = 'BEFORE_ATTRIBUTE_VALUE_STATE';
|
||
|
const ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE = 'ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE';
|
||
|
const ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE = 'ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE';
|
||
|
const ATTRIBUTE_VALUE_UNQUOTED_STATE = 'ATTRIBUTE_VALUE_UNQUOTED_STATE';
|
||
|
const AFTER_ATTRIBUTE_VALUE_QUOTED_STATE = 'AFTER_ATTRIBUTE_VALUE_QUOTED_STATE';
|
||
|
const SELF_CLOSING_START_TAG_STATE = 'SELF_CLOSING_START_TAG_STATE';
|
||
|
const BOGUS_COMMENT_STATE = 'BOGUS_COMMENT_STATE';
|
||
|
const MARKUP_DECLARATION_OPEN_STATE = 'MARKUP_DECLARATION_OPEN_STATE';
|
||
|
const COMMENT_START_STATE = 'COMMENT_START_STATE';
|
||
|
const COMMENT_START_DASH_STATE = 'COMMENT_START_DASH_STATE';
|
||
|
const COMMENT_STATE = 'COMMENT_STATE';
|
||
|
const COMMENT_LESS_THAN_SIGN_STATE = 'COMMENT_LESS_THAN_SIGN_STATE';
|
||
|
const COMMENT_LESS_THAN_SIGN_BANG_STATE = 'COMMENT_LESS_THAN_SIGN_BANG_STATE';
|
||
|
const COMMENT_LESS_THAN_SIGN_BANG_DASH_STATE = 'COMMENT_LESS_THAN_SIGN_BANG_DASH_STATE';
|
||
|
const COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH_STATE = 'COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH_STATE';
|
||
|
const COMMENT_END_DASH_STATE = 'COMMENT_END_DASH_STATE';
|
||
|
const COMMENT_END_STATE = 'COMMENT_END_STATE';
|
||
|
const COMMENT_END_BANG_STATE = 'COMMENT_END_BANG_STATE';
|
||
|
const DOCTYPE_STATE = 'DOCTYPE_STATE';
|
||
|
const BEFORE_DOCTYPE_NAME_STATE = 'BEFORE_DOCTYPE_NAME_STATE';
|
||
|
const DOCTYPE_NAME_STATE = 'DOCTYPE_NAME_STATE';
|
||
|
const AFTER_DOCTYPE_NAME_STATE = 'AFTER_DOCTYPE_NAME_STATE';
|
||
|
const AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE = 'AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE';
|
||
|
const BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE = 'BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE';
|
||
|
const DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE = 'DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE';
|
||
|
const DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE = 'DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE';
|
||
|
const AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE = 'AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE';
|
||
|
const BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE = 'BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE';
|
||
|
const AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE = 'AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE';
|
||
|
const BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE = 'BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE';
|
||
|
const DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE = 'DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE';
|
||
|
const DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE = 'DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE';
|
||
|
const AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE = 'AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE';
|
||
|
const BOGUS_DOCTYPE_STATE = 'BOGUS_DOCTYPE_STATE';
|
||
|
const CDATA_SECTION_STATE = 'CDATA_SECTION_STATE';
|
||
|
const CDATA_SECTION_BRACKET_STATE = 'CDATA_SECTION_BRACKET_STATE';
|
||
|
const CDATA_SECTION_END_STATE = 'CDATA_SECTION_END_STATE';
|
||
|
const CHARACTER_REFERENCE_STATE = 'CHARACTER_REFERENCE_STATE';
|
||
|
const NAMED_CHARACTER_REFERENCE_STATE = 'NAMED_CHARACTER_REFERENCE_STATE';
|
||
|
const AMBIGUOUS_AMPERSAND_STATE = 'AMBIGUOS_AMPERSAND_STATE';
|
||
|
const NUMERIC_CHARACTER_REFERENCE_STATE = 'NUMERIC_CHARACTER_REFERENCE_STATE';
|
||
|
const HEXADEMICAL_CHARACTER_REFERENCE_START_STATE = 'HEXADEMICAL_CHARACTER_REFERENCE_START_STATE';
|
||
|
const DECIMAL_CHARACTER_REFERENCE_START_STATE = 'DECIMAL_CHARACTER_REFERENCE_START_STATE';
|
||
|
const HEXADEMICAL_CHARACTER_REFERENCE_STATE = 'HEXADEMICAL_CHARACTER_REFERENCE_STATE';
|
||
|
const DECIMAL_CHARACTER_REFERENCE_STATE = 'DECIMAL_CHARACTER_REFERENCE_STATE';
|
||
|
const NUMERIC_CHARACTER_REFERENCE_END_STATE = 'NUMERIC_CHARACTER_REFERENCE_END_STATE';
|
||
|
|
||
|
//Utils
|
||
|
|
||
|
//OPTIMIZATION: these utility functions should not be moved out of this module. V8 Crankshaft will not inline
|
||
|
//this functions if they will be situated in another module due to context switch.
|
||
|
//Always perform inlining check before modifying this functions ('node --trace-inlining').
|
||
|
function isWhitespace(cp) {
|
||
|
return cp === $.SPACE || cp === $.LINE_FEED || cp === $.TABULATION || cp === $.FORM_FEED;
|
||
|
}
|
||
|
|
||
|
function isAsciiDigit(cp) {
|
||
|
return cp >= $.DIGIT_0 && cp <= $.DIGIT_9;
|
||
|
}
|
||
|
|
||
|
function isAsciiUpper(cp) {
|
||
|
return cp >= $.LATIN_CAPITAL_A && cp <= $.LATIN_CAPITAL_Z;
|
||
|
}
|
||
|
|
||
|
function isAsciiLower(cp) {
|
||
|
return cp >= $.LATIN_SMALL_A && cp <= $.LATIN_SMALL_Z;
|
||
|
}
|
||
|
|
||
|
function isAsciiLetter(cp) {
|
||
|
return isAsciiLower(cp) || isAsciiUpper(cp);
|
||
|
}
|
||
|
|
||
|
function isAsciiAlphaNumeric(cp) {
|
||
|
return isAsciiLetter(cp) || isAsciiDigit(cp);
|
||
|
}
|
||
|
|
||
|
function isAsciiUpperHexDigit(cp) {
|
||
|
return cp >= $.LATIN_CAPITAL_A && cp <= $.LATIN_CAPITAL_F;
|
||
|
}
|
||
|
|
||
|
function isAsciiLowerHexDigit(cp) {
|
||
|
return cp >= $.LATIN_SMALL_A && cp <= $.LATIN_SMALL_F;
|
||
|
}
|
||
|
|
||
|
function isAsciiHexDigit(cp) {
|
||
|
return isAsciiDigit(cp) || isAsciiUpperHexDigit(cp) || isAsciiLowerHexDigit(cp);
|
||
|
}
|
||
|
|
||
|
function toAsciiLowerCodePoint(cp) {
|
||
|
return cp + 0x0020;
|
||
|
}
|
||
|
|
||
|
//NOTE: String.fromCharCode() function can handle only characters from BMP subset.
|
||
|
//So, we need to workaround this manually.
|
||
|
//(see: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode#Getting_it_to_work_with_higher_values)
|
||
|
function toChar(cp) {
|
||
|
if (cp <= 0xffff) {
|
||
|
return String.fromCharCode(cp);
|
||
|
}
|
||
|
|
||
|
cp -= 0x10000;
|
||
|
return String.fromCharCode(((cp >>> 10) & 0x3ff) | 0xd800) + String.fromCharCode(0xdc00 | (cp & 0x3ff));
|
||
|
}
|
||
|
|
||
|
function toAsciiLowerChar(cp) {
|
||
|
return String.fromCharCode(toAsciiLowerCodePoint(cp));
|
||
|
}
|
||
|
|
||
|
function findNamedEntityTreeBranch(nodeIx, cp) {
|
||
|
const branchCount = neTree[++nodeIx];
|
||
|
let lo = ++nodeIx;
|
||
|
let hi = lo + branchCount - 1;
|
||
|
|
||
|
while (lo <= hi) {
|
||
|
const mid = (lo + hi) >>> 1;
|
||
|
const midCp = neTree[mid];
|
||
|
|
||
|
if (midCp < cp) {
|
||
|
lo = mid + 1;
|
||
|
} else if (midCp > cp) {
|
||
|
hi = mid - 1;
|
||
|
} else {
|
||
|
return neTree[mid + branchCount];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//Tokenizer
|
||
|
class Tokenizer {
|
||
|
constructor() {
|
||
|
this.preprocessor = new Preprocessor();
|
||
|
|
||
|
this.tokenQueue = [];
|
||
|
|
||
|
this.allowCDATA = false;
|
||
|
|
||
|
this.state = DATA_STATE;
|
||
|
this.returnState = '';
|
||
|
|
||
|
this.charRefCode = -1;
|
||
|
this.tempBuff = [];
|
||
|
this.lastStartTagName = '';
|
||
|
|
||
|
this.consumedAfterSnapshot = -1;
|
||
|
this.active = false;
|
||
|
|
||
|
this.currentCharacterToken = null;
|
||
|
this.currentToken = null;
|
||
|
this.currentAttr = null;
|
||
|
}
|
||
|
|
||
|
//Errors
|
||
|
_err() {
|
||
|
// NOTE: err reporting is noop by default. Enabled by mixin.
|
||
|
}
|
||
|
|
||
|
_errOnNextCodePoint(err) {
|
||
|
this._consume();
|
||
|
this._err(err);
|
||
|
this._unconsume();
|
||
|
}
|
||
|
|
||
|
//API
|
||
|
getNextToken() {
|
||
|
while (!this.tokenQueue.length && this.active) {
|
||
|
this.consumedAfterSnapshot = 0;
|
||
|
|
||
|
const cp = this._consume();
|
||
|
|
||
|
if (!this._ensureHibernation()) {
|
||
|
this[this.state](cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.tokenQueue.shift();
|
||
|
}
|
||
|
|
||
|
write(chunk, isLastChunk) {
|
||
|
this.active = true;
|
||
|
this.preprocessor.write(chunk, isLastChunk);
|
||
|
}
|
||
|
|
||
|
insertHtmlAtCurrentPos(chunk) {
|
||
|
this.active = true;
|
||
|
this.preprocessor.insertHtmlAtCurrentPos(chunk);
|
||
|
}
|
||
|
|
||
|
//Hibernation
|
||
|
_ensureHibernation() {
|
||
|
if (this.preprocessor.endOfChunkHit) {
|
||
|
for (; this.consumedAfterSnapshot > 0; this.consumedAfterSnapshot--) {
|
||
|
this.preprocessor.retreat();
|
||
|
}
|
||
|
|
||
|
this.active = false;
|
||
|
this.tokenQueue.push({ type: Tokenizer.HIBERNATION_TOKEN });
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//Consumption
|
||
|
_consume() {
|
||
|
this.consumedAfterSnapshot++;
|
||
|
return this.preprocessor.advance();
|
||
|
}
|
||
|
|
||
|
_unconsume() {
|
||
|
this.consumedAfterSnapshot--;
|
||
|
this.preprocessor.retreat();
|
||
|
}
|
||
|
|
||
|
_reconsumeInState(state) {
|
||
|
this.state = state;
|
||
|
this._unconsume();
|
||
|
}
|
||
|
|
||
|
_consumeSequenceIfMatch(pattern, startCp, caseSensitive) {
|
||
|
let consumedCount = 0;
|
||
|
let isMatch = true;
|
||
|
const patternLength = pattern.length;
|
||
|
let patternPos = 0;
|
||
|
let cp = startCp;
|
||
|
let patternCp = void 0;
|
||
|
|
||
|
for (; patternPos < patternLength; patternPos++) {
|
||
|
if (patternPos > 0) {
|
||
|
cp = this._consume();
|
||
|
consumedCount++;
|
||
|
}
|
||
|
|
||
|
if (cp === $.EOF) {
|
||
|
isMatch = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
patternCp = pattern[patternPos];
|
||
|
|
||
|
if (cp !== patternCp && (caseSensitive || cp !== toAsciiLowerCodePoint(patternCp))) {
|
||
|
isMatch = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!isMatch) {
|
||
|
while (consumedCount--) {
|
||
|
this._unconsume();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return isMatch;
|
||
|
}
|
||
|
|
||
|
//Temp buffer
|
||
|
_isTempBufferEqualToScriptString() {
|
||
|
if (this.tempBuff.length !== $$.SCRIPT_STRING.length) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < this.tempBuff.length; i++) {
|
||
|
if (this.tempBuff[i] !== $$.SCRIPT_STRING[i]) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//Token creation
|
||
|
_createStartTagToken() {
|
||
|
this.currentToken = {
|
||
|
type: Tokenizer.START_TAG_TOKEN,
|
||
|
tagName: '',
|
||
|
selfClosing: false,
|
||
|
ackSelfClosing: false,
|
||
|
attrs: []
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_createEndTagToken() {
|
||
|
this.currentToken = {
|
||
|
type: Tokenizer.END_TAG_TOKEN,
|
||
|
tagName: '',
|
||
|
selfClosing: false,
|
||
|
attrs: []
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_createCommentToken() {
|
||
|
this.currentToken = {
|
||
|
type: Tokenizer.COMMENT_TOKEN,
|
||
|
data: ''
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_createDoctypeToken(initialName) {
|
||
|
this.currentToken = {
|
||
|
type: Tokenizer.DOCTYPE_TOKEN,
|
||
|
name: initialName,
|
||
|
forceQuirks: false,
|
||
|
publicId: null,
|
||
|
systemId: null
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_createCharacterToken(type, ch) {
|
||
|
this.currentCharacterToken = {
|
||
|
type: type,
|
||
|
chars: ch
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_createEOFToken() {
|
||
|
this.currentToken = { type: Tokenizer.EOF_TOKEN };
|
||
|
}
|
||
|
|
||
|
//Tag attributes
|
||
|
_createAttr(attrNameFirstCh) {
|
||
|
this.currentAttr = {
|
||
|
name: attrNameFirstCh,
|
||
|
value: ''
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_leaveAttrName(toState) {
|
||
|
if (Tokenizer.getTokenAttr(this.currentToken, this.currentAttr.name) === null) {
|
||
|
this.currentToken.attrs.push(this.currentAttr);
|
||
|
} else {
|
||
|
this._err(ERR.duplicateAttribute);
|
||
|
}
|
||
|
|
||
|
this.state = toState;
|
||
|
}
|
||
|
|
||
|
_leaveAttrValue(toState) {
|
||
|
this.state = toState;
|
||
|
}
|
||
|
|
||
|
//Token emission
|
||
|
_emitCurrentToken() {
|
||
|
this._emitCurrentCharacterToken();
|
||
|
|
||
|
const ct = this.currentToken;
|
||
|
|
||
|
this.currentToken = null;
|
||
|
|
||
|
//NOTE: store emited start tag's tagName to determine is the following end tag token is appropriate.
|
||
|
if (ct.type === Tokenizer.START_TAG_TOKEN) {
|
||
|
this.lastStartTagName = ct.tagName;
|
||
|
} else if (ct.type === Tokenizer.END_TAG_TOKEN) {
|
||
|
if (ct.attrs.length > 0) {
|
||
|
this._err(ERR.endTagWithAttributes);
|
||
|
}
|
||
|
|
||
|
if (ct.selfClosing) {
|
||
|
this._err(ERR.endTagWithTrailingSolidus);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.tokenQueue.push(ct);
|
||
|
}
|
||
|
|
||
|
_emitCurrentCharacterToken() {
|
||
|
if (this.currentCharacterToken) {
|
||
|
this.tokenQueue.push(this.currentCharacterToken);
|
||
|
this.currentCharacterToken = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_emitEOFToken() {
|
||
|
this._createEOFToken();
|
||
|
this._emitCurrentToken();
|
||
|
}
|
||
|
|
||
|
//Characters emission
|
||
|
|
||
|
//OPTIMIZATION: specification uses only one type of character tokens (one token per character).
|
||
|
//This causes a huge memory overhead and a lot of unnecessary parser loops. parse5 uses 3 groups of characters.
|
||
|
//If we have a sequence of characters that belong to the same group, parser can process it
|
||
|
//as a single solid character token.
|
||
|
//So, there are 3 types of character tokens in parse5:
|
||
|
//1)NULL_CHARACTER_TOKEN - \u0000-character sequences (e.g. '\u0000\u0000\u0000')
|
||
|
//2)WHITESPACE_CHARACTER_TOKEN - any whitespace/new-line character sequences (e.g. '\n \r\t \f')
|
||
|
//3)CHARACTER_TOKEN - any character sequence which don't belong to groups 1 and 2 (e.g. 'abcdef1234@@#$%^')
|
||
|
_appendCharToCurrentCharacterToken(type, ch) {
|
||
|
if (this.currentCharacterToken && this.currentCharacterToken.type !== type) {
|
||
|
this._emitCurrentCharacterToken();
|
||
|
}
|
||
|
|
||
|
if (this.currentCharacterToken) {
|
||
|
this.currentCharacterToken.chars += ch;
|
||
|
} else {
|
||
|
this._createCharacterToken(type, ch);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_emitCodePoint(cp) {
|
||
|
let type = Tokenizer.CHARACTER_TOKEN;
|
||
|
|
||
|
if (isWhitespace(cp)) {
|
||
|
type = Tokenizer.WHITESPACE_CHARACTER_TOKEN;
|
||
|
} else if (cp === $.NULL) {
|
||
|
type = Tokenizer.NULL_CHARACTER_TOKEN;
|
||
|
}
|
||
|
|
||
|
this._appendCharToCurrentCharacterToken(type, toChar(cp));
|
||
|
}
|
||
|
|
||
|
_emitSeveralCodePoints(codePoints) {
|
||
|
for (let i = 0; i < codePoints.length; i++) {
|
||
|
this._emitCodePoint(codePoints[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//NOTE: used then we emit character explicitly. This is always a non-whitespace and a non-null character.
|
||
|
//So we can avoid additional checks here.
|
||
|
_emitChars(ch) {
|
||
|
this._appendCharToCurrentCharacterToken(Tokenizer.CHARACTER_TOKEN, ch);
|
||
|
}
|
||
|
|
||
|
// Character reference helpers
|
||
|
_matchNamedCharacterReference(startCp) {
|
||
|
let result = null;
|
||
|
let excess = 1;
|
||
|
let i = findNamedEntityTreeBranch(0, startCp);
|
||
|
|
||
|
this.tempBuff.push(startCp);
|
||
|
|
||
|
while (i > -1) {
|
||
|
const current = neTree[i];
|
||
|
const inNode = current < MAX_BRANCH_MARKER_VALUE;
|
||
|
const nodeWithData = inNode && current & HAS_DATA_FLAG;
|
||
|
|
||
|
if (nodeWithData) {
|
||
|
//NOTE: we use greedy search, so we continue lookup at this point
|
||
|
result = current & DATA_DUPLET_FLAG ? [neTree[++i], neTree[++i]] : [neTree[++i]];
|
||
|
excess = 0;
|
||
|
}
|
||
|
|
||
|
const cp = this._consume();
|
||
|
|
||
|
this.tempBuff.push(cp);
|
||
|
excess++;
|
||
|
|
||
|
if (cp === $.EOF) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (inNode) {
|
||
|
i = current & HAS_BRANCHES_FLAG ? findNamedEntityTreeBranch(i, cp) : -1;
|
||
|
} else {
|
||
|
i = cp === current ? ++i : -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (excess--) {
|
||
|
this.tempBuff.pop();
|
||
|
this._unconsume();
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
_isCharacterReferenceInAttribute() {
|
||
|
return (
|
||
|
this.returnState === ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ||
|
||
|
this.returnState === ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ||
|
||
|
this.returnState === ATTRIBUTE_VALUE_UNQUOTED_STATE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_isCharacterReferenceAttributeQuirk(withSemicolon) {
|
||
|
if (!withSemicolon && this._isCharacterReferenceInAttribute()) {
|
||
|
const nextCp = this._consume();
|
||
|
|
||
|
this._unconsume();
|
||
|
|
||
|
return nextCp === $.EQUALS_SIGN || isAsciiAlphaNumeric(nextCp);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_flushCodePointsConsumedAsCharacterReference() {
|
||
|
if (this._isCharacterReferenceInAttribute()) {
|
||
|
for (let i = 0; i < this.tempBuff.length; i++) {
|
||
|
this.currentAttr.value += toChar(this.tempBuff[i]);
|
||
|
}
|
||
|
} else {
|
||
|
this._emitSeveralCodePoints(this.tempBuff);
|
||
|
}
|
||
|
|
||
|
this.tempBuff = [];
|
||
|
}
|
||
|
|
||
|
// State machine
|
||
|
|
||
|
// Data state
|
||
|
//------------------------------------------------------------------
|
||
|
[DATA_STATE](cp) {
|
||
|
this.preprocessor.dropParsedChunk();
|
||
|
|
||
|
if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = TAG_OPEN_STATE;
|
||
|
} else if (cp === $.AMPERSAND) {
|
||
|
this.returnState = DATA_STATE;
|
||
|
this.state = CHARACTER_REFERENCE_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitCodePoint(cp);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RCDATA state
|
||
|
//------------------------------------------------------------------
|
||
|
[RCDATA_STATE](cp) {
|
||
|
this.preprocessor.dropParsedChunk();
|
||
|
|
||
|
if (cp === $.AMPERSAND) {
|
||
|
this.returnState = RCDATA_STATE;
|
||
|
this.state = CHARACTER_REFERENCE_STATE;
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = RCDATA_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RAWTEXT state
|
||
|
//------------------------------------------------------------------
|
||
|
[RAWTEXT_STATE](cp) {
|
||
|
this.preprocessor.dropParsedChunk();
|
||
|
|
||
|
if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = RAWTEXT_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_STATE](cp) {
|
||
|
this.preprocessor.dropParsedChunk();
|
||
|
|
||
|
if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// PLAINTEXT state
|
||
|
//------------------------------------------------------------------
|
||
|
[PLAINTEXT_STATE](cp) {
|
||
|
this.preprocessor.dropParsedChunk();
|
||
|
|
||
|
if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tag open state
|
||
|
//------------------------------------------------------------------
|
||
|
[TAG_OPEN_STATE](cp) {
|
||
|
if (cp === $.EXCLAMATION_MARK) {
|
||
|
this.state = MARKUP_DECLARATION_OPEN_STATE;
|
||
|
} else if (cp === $.SOLIDUS) {
|
||
|
this.state = END_TAG_OPEN_STATE;
|
||
|
} else if (isAsciiLetter(cp)) {
|
||
|
this._createStartTagToken();
|
||
|
this._reconsumeInState(TAG_NAME_STATE);
|
||
|
} else if (cp === $.QUESTION_MARK) {
|
||
|
this._err(ERR.unexpectedQuestionMarkInsteadOfTagName);
|
||
|
this._createCommentToken();
|
||
|
this._reconsumeInState(BOGUS_COMMENT_STATE);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofBeforeTagName);
|
||
|
this._emitChars('<');
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.invalidFirstCharacterOfTagName);
|
||
|
this._emitChars('<');
|
||
|
this._reconsumeInState(DATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// End tag open state
|
||
|
//------------------------------------------------------------------
|
||
|
[END_TAG_OPEN_STATE](cp) {
|
||
|
if (isAsciiLetter(cp)) {
|
||
|
this._createEndTagToken();
|
||
|
this._reconsumeInState(TAG_NAME_STATE);
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingEndTagName);
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofBeforeTagName);
|
||
|
this._emitChars('</');
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.invalidFirstCharacterOfTagName);
|
||
|
this._createCommentToken();
|
||
|
this._reconsumeInState(BOGUS_COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tag name state
|
||
|
//------------------------------------------------------------------
|
||
|
[TAG_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_ATTRIBUTE_NAME_STATE;
|
||
|
} else if (cp === $.SOLIDUS) {
|
||
|
this.state = SELF_CLOSING_START_TAG_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (isAsciiUpper(cp)) {
|
||
|
this.currentToken.tagName += toAsciiLowerChar(cp);
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.tagName += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.tagName += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RCDATA less-than sign state
|
||
|
//------------------------------------------------------------------
|
||
|
[RCDATA_LESS_THAN_SIGN_STATE](cp) {
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.tempBuff = [];
|
||
|
this.state = RCDATA_END_TAG_OPEN_STATE;
|
||
|
} else {
|
||
|
this._emitChars('<');
|
||
|
this._reconsumeInState(RCDATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RCDATA end tag open state
|
||
|
//------------------------------------------------------------------
|
||
|
[RCDATA_END_TAG_OPEN_STATE](cp) {
|
||
|
if (isAsciiLetter(cp)) {
|
||
|
this._createEndTagToken();
|
||
|
this._reconsumeInState(RCDATA_END_TAG_NAME_STATE);
|
||
|
} else {
|
||
|
this._emitChars('</');
|
||
|
this._reconsumeInState(RCDATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RCDATA end tag name state
|
||
|
//------------------------------------------------------------------
|
||
|
[RCDATA_END_TAG_NAME_STATE](cp) {
|
||
|
if (isAsciiUpper(cp)) {
|
||
|
this.currentToken.tagName += toAsciiLowerChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else if (isAsciiLower(cp)) {
|
||
|
this.currentToken.tagName += toChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else {
|
||
|
if (this.lastStartTagName === this.currentToken.tagName) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_ATTRIBUTE_NAME_STATE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.state = SELF_CLOSING_START_TAG_STATE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._emitChars('</');
|
||
|
this._emitSeveralCodePoints(this.tempBuff);
|
||
|
this._reconsumeInState(RCDATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RAWTEXT less-than sign state
|
||
|
//------------------------------------------------------------------
|
||
|
[RAWTEXT_LESS_THAN_SIGN_STATE](cp) {
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.tempBuff = [];
|
||
|
this.state = RAWTEXT_END_TAG_OPEN_STATE;
|
||
|
} else {
|
||
|
this._emitChars('<');
|
||
|
this._reconsumeInState(RAWTEXT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RAWTEXT end tag open state
|
||
|
//------------------------------------------------------------------
|
||
|
[RAWTEXT_END_TAG_OPEN_STATE](cp) {
|
||
|
if (isAsciiLetter(cp)) {
|
||
|
this._createEndTagToken();
|
||
|
this._reconsumeInState(RAWTEXT_END_TAG_NAME_STATE);
|
||
|
} else {
|
||
|
this._emitChars('</');
|
||
|
this._reconsumeInState(RAWTEXT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RAWTEXT end tag name state
|
||
|
//------------------------------------------------------------------
|
||
|
[RAWTEXT_END_TAG_NAME_STATE](cp) {
|
||
|
if (isAsciiUpper(cp)) {
|
||
|
this.currentToken.tagName += toAsciiLowerChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else if (isAsciiLower(cp)) {
|
||
|
this.currentToken.tagName += toChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else {
|
||
|
if (this.lastStartTagName === this.currentToken.tagName) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_ATTRIBUTE_NAME_STATE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.state = SELF_CLOSING_START_TAG_STATE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._emitChars('</');
|
||
|
this._emitSeveralCodePoints(this.tempBuff);
|
||
|
this._reconsumeInState(RAWTEXT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data less-than sign state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_LESS_THAN_SIGN_STATE](cp) {
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.tempBuff = [];
|
||
|
this.state = SCRIPT_DATA_END_TAG_OPEN_STATE;
|
||
|
} else if (cp === $.EXCLAMATION_MARK) {
|
||
|
this.state = SCRIPT_DATA_ESCAPE_START_STATE;
|
||
|
this._emitChars('<!');
|
||
|
} else {
|
||
|
this._emitChars('<');
|
||
|
this._reconsumeInState(SCRIPT_DATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data end tag open state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_END_TAG_OPEN_STATE](cp) {
|
||
|
if (isAsciiLetter(cp)) {
|
||
|
this._createEndTagToken();
|
||
|
this._reconsumeInState(SCRIPT_DATA_END_TAG_NAME_STATE);
|
||
|
} else {
|
||
|
this._emitChars('</');
|
||
|
this._reconsumeInState(SCRIPT_DATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data end tag name state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_END_TAG_NAME_STATE](cp) {
|
||
|
if (isAsciiUpper(cp)) {
|
||
|
this.currentToken.tagName += toAsciiLowerChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else if (isAsciiLower(cp)) {
|
||
|
this.currentToken.tagName += toChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else {
|
||
|
if (this.lastStartTagName === this.currentToken.tagName) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_ATTRIBUTE_NAME_STATE;
|
||
|
return;
|
||
|
} else if (cp === $.SOLIDUS) {
|
||
|
this.state = SELF_CLOSING_START_TAG_STATE;
|
||
|
return;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._emitChars('</');
|
||
|
this._emitSeveralCodePoints(this.tempBuff);
|
||
|
this._reconsumeInState(SCRIPT_DATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escape start state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPE_START_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = SCRIPT_DATA_ESCAPE_START_DASH_STATE;
|
||
|
this._emitChars('-');
|
||
|
} else {
|
||
|
this._reconsumeInState(SCRIPT_DATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escape start dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPE_START_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_DASH_DASH_STATE;
|
||
|
this._emitChars('-');
|
||
|
} else {
|
||
|
this._reconsumeInState(SCRIPT_DATA_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escaped state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPED_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_DASH_STATE;
|
||
|
this._emitChars('-');
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInScriptHtmlCommentLikeText);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escaped dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPED_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_DASH_DASH_STATE;
|
||
|
this._emitChars('-');
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.state = SCRIPT_DATA_ESCAPED_STATE;
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInScriptHtmlCommentLikeText);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_STATE;
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escaped dash dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPED_DASH_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this._emitChars('-');
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_STATE;
|
||
|
this._emitChars('>');
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.state = SCRIPT_DATA_ESCAPED_STATE;
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInScriptHtmlCommentLikeText);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.state = SCRIPT_DATA_ESCAPED_STATE;
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escaped less-than sign state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE](cp) {
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.tempBuff = [];
|
||
|
this.state = SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE;
|
||
|
} else if (isAsciiLetter(cp)) {
|
||
|
this.tempBuff = [];
|
||
|
this._emitChars('<');
|
||
|
this._reconsumeInState(SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE);
|
||
|
} else {
|
||
|
this._emitChars('<');
|
||
|
this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escaped end tag open state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE](cp) {
|
||
|
if (isAsciiLetter(cp)) {
|
||
|
this._createEndTagToken();
|
||
|
this._reconsumeInState(SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE);
|
||
|
} else {
|
||
|
this._emitChars('</');
|
||
|
this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data escaped end tag name state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE](cp) {
|
||
|
if (isAsciiUpper(cp)) {
|
||
|
this.currentToken.tagName += toAsciiLowerChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else if (isAsciiLower(cp)) {
|
||
|
this.currentToken.tagName += toChar(cp);
|
||
|
this.tempBuff.push(cp);
|
||
|
} else {
|
||
|
if (this.lastStartTagName === this.currentToken.tagName) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_ATTRIBUTE_NAME_STATE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.state = SELF_CLOSING_START_TAG_STATE;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._emitChars('</');
|
||
|
this._emitSeveralCodePoints(this.tempBuff);
|
||
|
this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data double escape start state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE](cp) {
|
||
|
if (isWhitespace(cp) || cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = this._isTempBufferEqualToScriptString()
|
||
|
? SCRIPT_DATA_DOUBLE_ESCAPED_STATE
|
||
|
: SCRIPT_DATA_ESCAPED_STATE;
|
||
|
this._emitCodePoint(cp);
|
||
|
} else if (isAsciiUpper(cp)) {
|
||
|
this.tempBuff.push(toAsciiLowerCodePoint(cp));
|
||
|
this._emitCodePoint(cp);
|
||
|
} else if (isAsciiLower(cp)) {
|
||
|
this.tempBuff.push(cp);
|
||
|
this._emitCodePoint(cp);
|
||
|
} else {
|
||
|
this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data double escaped state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_DOUBLE_ESCAPED_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE;
|
||
|
this._emitChars('-');
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE;
|
||
|
this._emitChars('<');
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInScriptHtmlCommentLikeText);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data double escaped dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE;
|
||
|
this._emitChars('-');
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE;
|
||
|
this._emitChars('<');
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInScriptHtmlCommentLikeText);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data double escaped dash dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this._emitChars('-');
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE;
|
||
|
this._emitChars('<');
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = SCRIPT_DATA_STATE;
|
||
|
this._emitChars('>');
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
|
||
|
this._emitChars(unicode.REPLACEMENT_CHARACTER);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInScriptHtmlCommentLikeText);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data double escaped less-than sign state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE](cp) {
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.tempBuff = [];
|
||
|
this.state = SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE;
|
||
|
this._emitChars('/');
|
||
|
} else {
|
||
|
this._reconsumeInState(SCRIPT_DATA_DOUBLE_ESCAPED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Script data double escape end state
|
||
|
//------------------------------------------------------------------
|
||
|
[SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE](cp) {
|
||
|
if (isWhitespace(cp) || cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = this._isTempBufferEqualToScriptString()
|
||
|
? SCRIPT_DATA_ESCAPED_STATE
|
||
|
: SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
|
||
|
|
||
|
this._emitCodePoint(cp);
|
||
|
} else if (isAsciiUpper(cp)) {
|
||
|
this.tempBuff.push(toAsciiLowerCodePoint(cp));
|
||
|
this._emitCodePoint(cp);
|
||
|
} else if (isAsciiLower(cp)) {
|
||
|
this.tempBuff.push(cp);
|
||
|
this._emitCodePoint(cp);
|
||
|
} else {
|
||
|
this._reconsumeInState(SCRIPT_DATA_DOUBLE_ESCAPED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Before attribute name state
|
||
|
//------------------------------------------------------------------
|
||
|
[BEFORE_ATTRIBUTE_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN || cp === $.EOF) {
|
||
|
this._reconsumeInState(AFTER_ATTRIBUTE_NAME_STATE);
|
||
|
} else if (cp === $.EQUALS_SIGN) {
|
||
|
this._err(ERR.unexpectedEqualsSignBeforeAttributeName);
|
||
|
this._createAttr('=');
|
||
|
this.state = ATTRIBUTE_NAME_STATE;
|
||
|
} else {
|
||
|
this._createAttr('');
|
||
|
this._reconsumeInState(ATTRIBUTE_NAME_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Attribute name state
|
||
|
//------------------------------------------------------------------
|
||
|
[ATTRIBUTE_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp) || cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN || cp === $.EOF) {
|
||
|
this._leaveAttrName(AFTER_ATTRIBUTE_NAME_STATE);
|
||
|
this._unconsume();
|
||
|
} else if (cp === $.EQUALS_SIGN) {
|
||
|
this._leaveAttrName(BEFORE_ATTRIBUTE_VALUE_STATE);
|
||
|
} else if (isAsciiUpper(cp)) {
|
||
|
this.currentAttr.name += toAsciiLowerChar(cp);
|
||
|
} else if (cp === $.QUOTATION_MARK || cp === $.APOSTROPHE || cp === $.LESS_THAN_SIGN) {
|
||
|
this._err(ERR.unexpectedCharacterInAttributeName);
|
||
|
this.currentAttr.name += toChar(cp);
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentAttr.name += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else {
|
||
|
this.currentAttr.name += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After attribute name state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_ATTRIBUTE_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.SOLIDUS) {
|
||
|
this.state = SELF_CLOSING_START_TAG_STATE;
|
||
|
} else if (cp === $.EQUALS_SIGN) {
|
||
|
this.state = BEFORE_ATTRIBUTE_VALUE_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._createAttr('');
|
||
|
this._reconsumeInState(ATTRIBUTE_NAME_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Before attribute value state
|
||
|
//------------------------------------------------------------------
|
||
|
[BEFORE_ATTRIBUTE_VALUE_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.QUOTATION_MARK) {
|
||
|
this.state = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this.state = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingAttributeValue);
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else {
|
||
|
this._reconsumeInState(ATTRIBUTE_VALUE_UNQUOTED_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Attribute value (double-quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE](cp) {
|
||
|
if (cp === $.QUOTATION_MARK) {
|
||
|
this.state = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
|
||
|
} else if (cp === $.AMPERSAND) {
|
||
|
this.returnState = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
|
||
|
this.state = CHARACTER_REFERENCE_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentAttr.value += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentAttr.value += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Attribute value (single-quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE](cp) {
|
||
|
if (cp === $.APOSTROPHE) {
|
||
|
this.state = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
|
||
|
} else if (cp === $.AMPERSAND) {
|
||
|
this.returnState = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
|
||
|
this.state = CHARACTER_REFERENCE_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentAttr.value += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentAttr.value += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Attribute value (unquoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[ATTRIBUTE_VALUE_UNQUOTED_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this._leaveAttrValue(BEFORE_ATTRIBUTE_NAME_STATE);
|
||
|
} else if (cp === $.AMPERSAND) {
|
||
|
this.returnState = ATTRIBUTE_VALUE_UNQUOTED_STATE;
|
||
|
this.state = CHARACTER_REFERENCE_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._leaveAttrValue(DATA_STATE);
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentAttr.value += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (
|
||
|
cp === $.QUOTATION_MARK ||
|
||
|
cp === $.APOSTROPHE ||
|
||
|
cp === $.LESS_THAN_SIGN ||
|
||
|
cp === $.EQUALS_SIGN ||
|
||
|
cp === $.GRAVE_ACCENT
|
||
|
) {
|
||
|
this._err(ERR.unexpectedCharacterInUnquotedAttributeValue);
|
||
|
this.currentAttr.value += toChar(cp);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentAttr.value += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After attribute value (quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_ATTRIBUTE_VALUE_QUOTED_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this._leaveAttrValue(BEFORE_ATTRIBUTE_NAME_STATE);
|
||
|
} else if (cp === $.SOLIDUS) {
|
||
|
this._leaveAttrValue(SELF_CLOSING_START_TAG_STATE);
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._leaveAttrValue(DATA_STATE);
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingWhitespaceBetweenAttributes);
|
||
|
this._reconsumeInState(BEFORE_ATTRIBUTE_NAME_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Self-closing start tag state
|
||
|
//------------------------------------------------------------------
|
||
|
[SELF_CLOSING_START_TAG_STATE](cp) {
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.currentToken.selfClosing = true;
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInTag);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.unexpectedSolidusInTag);
|
||
|
this._reconsumeInState(BEFORE_ATTRIBUTE_NAME_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bogus comment state
|
||
|
//------------------------------------------------------------------
|
||
|
[BOGUS_COMMENT_STATE](cp) {
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.data += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else {
|
||
|
this.currentToken.data += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Markup declaration open state
|
||
|
//------------------------------------------------------------------
|
||
|
[MARKUP_DECLARATION_OPEN_STATE](cp) {
|
||
|
if (this._consumeSequenceIfMatch($$.DASH_DASH_STRING, cp, true)) {
|
||
|
this._createCommentToken();
|
||
|
this.state = COMMENT_START_STATE;
|
||
|
} else if (this._consumeSequenceIfMatch($$.DOCTYPE_STRING, cp, false)) {
|
||
|
this.state = DOCTYPE_STATE;
|
||
|
} else if (this._consumeSequenceIfMatch($$.CDATA_START_STRING, cp, true)) {
|
||
|
if (this.allowCDATA) {
|
||
|
this.state = CDATA_SECTION_STATE;
|
||
|
} else {
|
||
|
this._err(ERR.cdataInHtmlContent);
|
||
|
this._createCommentToken();
|
||
|
this.currentToken.data = '[CDATA[';
|
||
|
this.state = BOGUS_COMMENT_STATE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//NOTE: sequence lookup can be abrupted by hibernation. In that case lookup
|
||
|
//results are no longer valid and we will need to start over.
|
||
|
else if (!this._ensureHibernation()) {
|
||
|
this._err(ERR.incorrectlyOpenedComment);
|
||
|
this._createCommentToken();
|
||
|
this._reconsumeInState(BOGUS_COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment start state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_START_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = COMMENT_START_DASH_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.abruptClosingOfEmptyComment);
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else {
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment start dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_START_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = COMMENT_END_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.abruptClosingOfEmptyComment);
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInComment);
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.data += '-';
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = COMMENT_END_DASH_STATE;
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.currentToken.data += '<';
|
||
|
this.state = COMMENT_LESS_THAN_SIGN_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.data += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInComment);
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.data += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment less-than sign state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_LESS_THAN_SIGN_STATE](cp) {
|
||
|
if (cp === $.EXCLAMATION_MARK) {
|
||
|
this.currentToken.data += '!';
|
||
|
this.state = COMMENT_LESS_THAN_SIGN_BANG_STATE;
|
||
|
} else if (cp === $.LESS_THAN_SIGN) {
|
||
|
this.currentToken.data += '!';
|
||
|
} else {
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment less-than sign bang state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_LESS_THAN_SIGN_BANG_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = COMMENT_LESS_THAN_SIGN_BANG_DASH_STATE;
|
||
|
} else {
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment less-than sign bang dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_LESS_THAN_SIGN_BANG_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH_STATE;
|
||
|
} else {
|
||
|
this._reconsumeInState(COMMENT_END_DASH_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment less-than sign bang dash dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH_STATE](cp) {
|
||
|
if (cp !== $.GREATER_THAN_SIGN && cp !== $.EOF) {
|
||
|
this._err(ERR.nestedComment);
|
||
|
}
|
||
|
|
||
|
this._reconsumeInState(COMMENT_END_STATE);
|
||
|
}
|
||
|
|
||
|
// Comment end dash state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_END_DASH_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.state = COMMENT_END_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInComment);
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.data += '-';
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment end state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_END_STATE](cp) {
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EXCLAMATION_MARK) {
|
||
|
this.state = COMMENT_END_BANG_STATE;
|
||
|
} else if (cp === $.HYPHEN_MINUS) {
|
||
|
this.currentToken.data += '-';
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInComment);
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.data += '--';
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment end bang state
|
||
|
//------------------------------------------------------------------
|
||
|
[COMMENT_END_BANG_STATE](cp) {
|
||
|
if (cp === $.HYPHEN_MINUS) {
|
||
|
this.currentToken.data += '--!';
|
||
|
this.state = COMMENT_END_DASH_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.incorrectlyClosedComment);
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInComment);
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.data += '--!';
|
||
|
this._reconsumeInState(COMMENT_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DOCTYPE state
|
||
|
//------------------------------------------------------------------
|
||
|
[DOCTYPE_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_DOCTYPE_NAME_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._reconsumeInState(BEFORE_DOCTYPE_NAME_STATE);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this._createDoctypeToken(null);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingWhitespaceBeforeDoctypeName);
|
||
|
this._reconsumeInState(BEFORE_DOCTYPE_NAME_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Before DOCTYPE name state
|
||
|
//------------------------------------------------------------------
|
||
|
[BEFORE_DOCTYPE_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (isAsciiUpper(cp)) {
|
||
|
this._createDoctypeToken(toAsciiLowerChar(cp));
|
||
|
this.state = DOCTYPE_NAME_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this._createDoctypeToken(unicode.REPLACEMENT_CHARACTER);
|
||
|
this.state = DOCTYPE_NAME_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingDoctypeName);
|
||
|
this._createDoctypeToken(null);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this._createDoctypeToken(null);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._createDoctypeToken(toChar(cp));
|
||
|
this.state = DOCTYPE_NAME_STATE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DOCTYPE name state
|
||
|
//------------------------------------------------------------------
|
||
|
[DOCTYPE_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = AFTER_DOCTYPE_NAME_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (isAsciiUpper(cp)) {
|
||
|
this.currentToken.name += toAsciiLowerChar(cp);
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.name += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.name += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After DOCTYPE name state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_DOCTYPE_NAME_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else if (this._consumeSequenceIfMatch($$.PUBLIC_STRING, cp, false)) {
|
||
|
this.state = AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE;
|
||
|
} else if (this._consumeSequenceIfMatch($$.SYSTEM_STRING, cp, false)) {
|
||
|
this.state = AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE;
|
||
|
}
|
||
|
//NOTE: sequence lookup can be abrupted by hibernation. In that case lookup
|
||
|
//results are no longer valid and we will need to start over.
|
||
|
else if (!this._ensureHibernation()) {
|
||
|
this._err(ERR.invalidCharacterSequenceAfterDoctypeName);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After DOCTYPE public keyword state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
|
||
|
} else if (cp === $.QUOTATION_MARK) {
|
||
|
this._err(ERR.missingWhitespaceAfterDoctypePublicKeyword);
|
||
|
this.currentToken.publicId = '';
|
||
|
this.state = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this._err(ERR.missingWhitespaceAfterDoctypePublicKeyword);
|
||
|
this.currentToken.publicId = '';
|
||
|
this.state = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingDoctypePublicIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingQuoteBeforeDoctypePublicIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Before DOCTYPE public identifier state
|
||
|
//------------------------------------------------------------------
|
||
|
[BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.QUOTATION_MARK) {
|
||
|
this.currentToken.publicId = '';
|
||
|
this.state = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this.currentToken.publicId = '';
|
||
|
this.state = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingDoctypePublicIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingQuoteBeforeDoctypePublicIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DOCTYPE public identifier (double-quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE](cp) {
|
||
|
if (cp === $.QUOTATION_MARK) {
|
||
|
this.state = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.publicId += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.abruptDoctypePublicIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.publicId += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DOCTYPE public identifier (single-quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE](cp) {
|
||
|
if (cp === $.APOSTROPHE) {
|
||
|
this.state = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.publicId += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.abruptDoctypePublicIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.publicId += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After DOCTYPE public identifier state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.QUOTATION_MARK) {
|
||
|
this._err(ERR.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers);
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this._err(ERR.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers);
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Between DOCTYPE public and system identifiers state
|
||
|
//------------------------------------------------------------------
|
||
|
[BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.QUOTATION_MARK) {
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After DOCTYPE system keyword state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
this.state = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
|
||
|
} else if (cp === $.QUOTATION_MARK) {
|
||
|
this._err(ERR.missingWhitespaceAfterDoctypeSystemKeyword);
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this._err(ERR.missingWhitespaceAfterDoctypeSystemKeyword);
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Before DOCTYPE system identifier state
|
||
|
//------------------------------------------------------------------
|
||
|
[BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.QUOTATION_MARK) {
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
|
||
|
} else if (cp === $.APOSTROPHE) {
|
||
|
this.currentToken.systemId = '';
|
||
|
this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.missingDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this.state = DATA_STATE;
|
||
|
this._emitCurrentToken();
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DOCTYPE system identifier (double-quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE](cp) {
|
||
|
if (cp === $.QUOTATION_MARK) {
|
||
|
this.state = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.systemId += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.abruptDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.systemId += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// DOCTYPE system identifier (single-quoted) state
|
||
|
//------------------------------------------------------------------
|
||
|
[DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE](cp) {
|
||
|
if (cp === $.APOSTROPHE) {
|
||
|
this.state = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
this.currentToken.systemId += unicode.REPLACEMENT_CHARACTER;
|
||
|
} else if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._err(ERR.abruptDoctypeSystemIdentifier);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this.currentToken.systemId += toChar(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// After DOCTYPE system identifier state
|
||
|
//------------------------------------------------------------------
|
||
|
[AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE](cp) {
|
||
|
if (isWhitespace(cp)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInDoctype);
|
||
|
this.currentToken.forceQuirks = true;
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._err(ERR.unexpectedCharacterAfterDoctypeSystemIdentifier);
|
||
|
this._reconsumeInState(BOGUS_DOCTYPE_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bogus DOCTYPE state
|
||
|
//------------------------------------------------------------------
|
||
|
[BOGUS_DOCTYPE_STATE](cp) {
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this._emitCurrentToken();
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.NULL) {
|
||
|
this._err(ERR.unexpectedNullCharacter);
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._emitCurrentToken();
|
||
|
this._emitEOFToken();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CDATA section state
|
||
|
//------------------------------------------------------------------
|
||
|
[CDATA_SECTION_STATE](cp) {
|
||
|
if (cp === $.RIGHT_SQUARE_BRACKET) {
|
||
|
this.state = CDATA_SECTION_BRACKET_STATE;
|
||
|
} else if (cp === $.EOF) {
|
||
|
this._err(ERR.eofInCdata);
|
||
|
this._emitEOFToken();
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CDATA section bracket state
|
||
|
//------------------------------------------------------------------
|
||
|
[CDATA_SECTION_BRACKET_STATE](cp) {
|
||
|
if (cp === $.RIGHT_SQUARE_BRACKET) {
|
||
|
this.state = CDATA_SECTION_END_STATE;
|
||
|
} else {
|
||
|
this._emitChars(']');
|
||
|
this._reconsumeInState(CDATA_SECTION_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CDATA section end state
|
||
|
//------------------------------------------------------------------
|
||
|
[CDATA_SECTION_END_STATE](cp) {
|
||
|
if (cp === $.GREATER_THAN_SIGN) {
|
||
|
this.state = DATA_STATE;
|
||
|
} else if (cp === $.RIGHT_SQUARE_BRACKET) {
|
||
|
this._emitChars(']');
|
||
|
} else {
|
||
|
this._emitChars(']]');
|
||
|
this._reconsumeInState(CDATA_SECTION_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Character reference state
|
||
|
//------------------------------------------------------------------
|
||
|
[CHARACTER_REFERENCE_STATE](cp) {
|
||
|
this.tempBuff = [$.AMPERSAND];
|
||
|
|
||
|
if (cp === $.NUMBER_SIGN) {
|
||
|
this.tempBuff.push(cp);
|
||
|
this.state = NUMERIC_CHARACTER_REFERENCE_STATE;
|
||
|
} else if (isAsciiAlphaNumeric(cp)) {
|
||
|
this._reconsumeInState(NAMED_CHARACTER_REFERENCE_STATE);
|
||
|
} else {
|
||
|
this._flushCodePointsConsumedAsCharacterReference();
|
||
|
this._reconsumeInState(this.returnState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Named character reference state
|
||
|
//------------------------------------------------------------------
|
||
|
[NAMED_CHARACTER_REFERENCE_STATE](cp) {
|
||
|
const matchResult = this._matchNamedCharacterReference(cp);
|
||
|
|
||
|
//NOTE: matching can be abrupted by hibernation. In that case match
|
||
|
//results are no longer valid and we will need to start over.
|
||
|
if (this._ensureHibernation()) {
|
||
|
this.tempBuff = [$.AMPERSAND];
|
||
|
} else if (matchResult) {
|
||
|
const withSemicolon = this.tempBuff[this.tempBuff.length - 1] === $.SEMICOLON;
|
||
|
|
||
|
if (!this._isCharacterReferenceAttributeQuirk(withSemicolon)) {
|
||
|
if (!withSemicolon) {
|
||
|
this._errOnNextCodePoint(ERR.missingSemicolonAfterCharacterReference);
|
||
|
}
|
||
|
|
||
|
this.tempBuff = matchResult;
|
||
|
}
|
||
|
|
||
|
this._flushCodePointsConsumedAsCharacterReference();
|
||
|
this.state = this.returnState;
|
||
|
} else {
|
||
|
this._flushCodePointsConsumedAsCharacterReference();
|
||
|
this.state = AMBIGUOUS_AMPERSAND_STATE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Ambiguos ampersand state
|
||
|
//------------------------------------------------------------------
|
||
|
[AMBIGUOUS_AMPERSAND_STATE](cp) {
|
||
|
if (isAsciiAlphaNumeric(cp)) {
|
||
|
if (this._isCharacterReferenceInAttribute()) {
|
||
|
this.currentAttr.value += toChar(cp);
|
||
|
} else {
|
||
|
this._emitCodePoint(cp);
|
||
|
}
|
||
|
} else {
|
||
|
if (cp === $.SEMICOLON) {
|
||
|
this._err(ERR.unknownNamedCharacterReference);
|
||
|
}
|
||
|
|
||
|
this._reconsumeInState(this.returnState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Numeric character reference state
|
||
|
//------------------------------------------------------------------
|
||
|
[NUMERIC_CHARACTER_REFERENCE_STATE](cp) {
|
||
|
this.charRefCode = 0;
|
||
|
|
||
|
if (cp === $.LATIN_SMALL_X || cp === $.LATIN_CAPITAL_X) {
|
||
|
this.tempBuff.push(cp);
|
||
|
this.state = HEXADEMICAL_CHARACTER_REFERENCE_START_STATE;
|
||
|
} else {
|
||
|
this._reconsumeInState(DECIMAL_CHARACTER_REFERENCE_START_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Hexademical character reference start state
|
||
|
//------------------------------------------------------------------
|
||
|
[HEXADEMICAL_CHARACTER_REFERENCE_START_STATE](cp) {
|
||
|
if (isAsciiHexDigit(cp)) {
|
||
|
this._reconsumeInState(HEXADEMICAL_CHARACTER_REFERENCE_STATE);
|
||
|
} else {
|
||
|
this._err(ERR.absenceOfDigitsInNumericCharacterReference);
|
||
|
this._flushCodePointsConsumedAsCharacterReference();
|
||
|
this._reconsumeInState(this.returnState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Decimal character reference start state
|
||
|
//------------------------------------------------------------------
|
||
|
[DECIMAL_CHARACTER_REFERENCE_START_STATE](cp) {
|
||
|
if (isAsciiDigit(cp)) {
|
||
|
this._reconsumeInState(DECIMAL_CHARACTER_REFERENCE_STATE);
|
||
|
} else {
|
||
|
this._err(ERR.absenceOfDigitsInNumericCharacterReference);
|
||
|
this._flushCodePointsConsumedAsCharacterReference();
|
||
|
this._reconsumeInState(this.returnState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Hexademical character reference state
|
||
|
//------------------------------------------------------------------
|
||
|
[HEXADEMICAL_CHARACTER_REFERENCE_STATE](cp) {
|
||
|
if (isAsciiUpperHexDigit(cp)) {
|
||
|
this.charRefCode = this.charRefCode * 16 + cp - 0x37;
|
||
|
} else if (isAsciiLowerHexDigit(cp)) {
|
||
|
this.charRefCode = this.charRefCode * 16 + cp - 0x57;
|
||
|
} else if (isAsciiDigit(cp)) {
|
||
|
this.charRefCode = this.charRefCode * 16 + cp - 0x30;
|
||
|
} else if (cp === $.SEMICOLON) {
|
||
|
this.state = NUMERIC_CHARACTER_REFERENCE_END_STATE;
|
||
|
} else {
|
||
|
this._err(ERR.missingSemicolonAfterCharacterReference);
|
||
|
this._reconsumeInState(NUMERIC_CHARACTER_REFERENCE_END_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Decimal character reference state
|
||
|
//------------------------------------------------------------------
|
||
|
[DECIMAL_CHARACTER_REFERENCE_STATE](cp) {
|
||
|
if (isAsciiDigit(cp)) {
|
||
|
this.charRefCode = this.charRefCode * 10 + cp - 0x30;
|
||
|
} else if (cp === $.SEMICOLON) {
|
||
|
this.state = NUMERIC_CHARACTER_REFERENCE_END_STATE;
|
||
|
} else {
|
||
|
this._err(ERR.missingSemicolonAfterCharacterReference);
|
||
|
this._reconsumeInState(NUMERIC_CHARACTER_REFERENCE_END_STATE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Numeric character reference end state
|
||
|
//------------------------------------------------------------------
|
||
|
[NUMERIC_CHARACTER_REFERENCE_END_STATE]() {
|
||
|
if (this.charRefCode === $.NULL) {
|
||
|
this._err(ERR.nullCharacterReference);
|
||
|
this.charRefCode = $.REPLACEMENT_CHARACTER;
|
||
|
} else if (this.charRefCode > 0x10ffff) {
|
||
|
this._err(ERR.characterReferenceOutsideUnicodeRange);
|
||
|
this.charRefCode = $.REPLACEMENT_CHARACTER;
|
||
|
} else if (unicode.isSurrogate(this.charRefCode)) {
|
||
|
this._err(ERR.surrogateCharacterReference);
|
||
|
this.charRefCode = $.REPLACEMENT_CHARACTER;
|
||
|
} else if (unicode.isUndefinedCodePoint(this.charRefCode)) {
|
||
|
this._err(ERR.noncharacterCharacterReference);
|
||
|
} else if (unicode.isControlCodePoint(this.charRefCode) || this.charRefCode === $.CARRIAGE_RETURN) {
|
||
|
this._err(ERR.controlCharacterReference);
|
||
|
|
||
|
const replacement = C1_CONTROLS_REFERENCE_REPLACEMENTS[this.charRefCode];
|
||
|
|
||
|
if (replacement) {
|
||
|
this.charRefCode = replacement;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.tempBuff = [this.charRefCode];
|
||
|
|
||
|
this._flushCodePointsConsumedAsCharacterReference();
|
||
|
this._reconsumeInState(this.returnState);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Token types
|
||
|
Tokenizer.CHARACTER_TOKEN = 'CHARACTER_TOKEN';
|
||
|
Tokenizer.NULL_CHARACTER_TOKEN = 'NULL_CHARACTER_TOKEN';
|
||
|
Tokenizer.WHITESPACE_CHARACTER_TOKEN = 'WHITESPACE_CHARACTER_TOKEN';
|
||
|
Tokenizer.START_TAG_TOKEN = 'START_TAG_TOKEN';
|
||
|
Tokenizer.END_TAG_TOKEN = 'END_TAG_TOKEN';
|
||
|
Tokenizer.COMMENT_TOKEN = 'COMMENT_TOKEN';
|
||
|
Tokenizer.DOCTYPE_TOKEN = 'DOCTYPE_TOKEN';
|
||
|
Tokenizer.EOF_TOKEN = 'EOF_TOKEN';
|
||
|
Tokenizer.HIBERNATION_TOKEN = 'HIBERNATION_TOKEN';
|
||
|
|
||
|
//Tokenizer initial states for different modes
|
||
|
Tokenizer.MODE = {
|
||
|
DATA: DATA_STATE,
|
||
|
RCDATA: RCDATA_STATE,
|
||
|
RAWTEXT: RAWTEXT_STATE,
|
||
|
SCRIPT_DATA: SCRIPT_DATA_STATE,
|
||
|
PLAINTEXT: PLAINTEXT_STATE
|
||
|
};
|
||
|
|
||
|
//Static
|
||
|
Tokenizer.getTokenAttr = function(token, attrName) {
|
||
|
for (let i = token.attrs.length - 1; i >= 0; i--) {
|
||
|
if (token.attrs[i].name === attrName) {
|
||
|
return token.attrs[i].value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
};
|
||
|
|
||
|
module.exports = Tokenizer;
|
||
|
|
||
|
},{"../common/error-codes":2,"../common/unicode":5,"./named-entity-data":20,"./preprocessor":21}],20:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
//NOTE: this file contains auto-generated array mapped radix tree that is used for the named entity references consumption
|
||
|
//(details: https://github.com/inikulin/parse5/tree/master/scripts/generate-named-entity-data/README.md)
|
||
|
module.exports = new Uint16Array([4,52,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,106,303,412,810,1432,1701,1796,1987,2114,2360,2420,2484,3170,3251,4140,4393,4575,4610,5106,5512,5728,6117,6274,6315,6345,6427,6516,7002,7910,8733,9323,9870,10170,10631,10893,11318,11386,11467,12773,13092,14474,14922,15448,15542,16419,17666,18166,18611,19004,19095,19298,19397,4,16,69,77,97,98,99,102,103,108,109,110,111,112,114,115,116,117,140,150,158,169,176,194,199,210,216,222,226,242,256,266,283,294,108,105,103,5,198,1,59,148,1,198,80,5,38,1,59,156,1,38,99,117,116,101,5,193,1,59,167,1,193,114,101,118,101,59,1,258,4,2,105,121,182,191,114,99,5,194,1,59,189,1,194,59,1,1040,114,59,3,55349,56580,114,97,118,101,5,192,1,59,208,1,192,112,104,97,59,1,913,97,99,114,59,1,256,100,59,1,10835,4,2,103,112,232,237,111,110,59,1,260,102,59,3,55349,56632,112,108,121,70,117,110,99,116,105,111,110,59,1,8289,105,110,103,5,197,1,59,264,1,197,4,2,99,115,272,277,114,59,3,55349,56476,105,103,110,59,1,8788,105,108,100,101,5,195,1,59,292,1,195,109,108,5,196,1,59,301,1,196,4,8,97,99,101,102,111,114,115,117,321,350,354,383,388,394,400,405,4,2,99,114,327,336,107,115,108,97,115,104,59,1,8726,4,2,118,119,342,345,59,1,10983,101,100,59,1,8966,121,59,1,1041,4,3,99,114,116,362,369,379,97,117,115,101,59,1,8757,110,111,117,108,108,105,115,59,1,8492,97,59,1,914,114,59,3,55349,56581,112,102,59,3,55349,56633,101,118,101,59,1,728,99,114,59,1,8492,109,112,101,113,59,1,8782,4,14,72,79,97,99,100,101,102,104,105,108,111,114,115,117,442,447,456,504,542,547,569,573,577,616,678,784,790,796,99,121,59,1,1063,80,89,5,169,1,59,454,1,169,4,3,99,112,121,464,470,497,117,116,101,59,1,262,4,2,59,105,476,478,1,8914,116,97,108,68,105,102,102,101,114,101,110,116,105,97,108,68,59,1,8517,108,101,121,115,59,1,8493,4,4,97,101,105,111,514,520,530,535,114,111,110,59,1,268,100,105,108,5,199,1,59,528,1,199,114,99,59,1,264,110,105,110,116,59,1,8752,111,116,59,1,266,4,2,100,110,553,560,105,108,108,97,59,1,184,116,101,114,68,111,116,59,1,183,114,59,1,8493,105,59,1,935,114,99,108,101,4,4,68,77,80,84,591,596,603,609,111,116,59,1,8857,105,110,117,115,59,1,8854,108,117,115,59,1,8853,105,109,101,115,59,1,8855,111,4,2,99,115,623,646,107,119,105,115,101,67,111,110,116,111,117,114,73,110,116,101,103,114,97,108,59,1,8754,101,67,117,114,108,121,4,2,68,81,658,671,111,117,98,108,101,81,117,111,116,101,59,1,8221,117,111,116,101,59,1,8217,4,4,108,110,112,117,688,701,736,753,111,110,4,2,59,101,696,698,1,8759,59,1,10868,4,3,103,105,116,709,717,722,114,117,101,110,116,59,1,8801,110,116,59,1,8751,111,117,114,73,110,116,101,103,114,97,108,59,1,8750,4,2,102,114,742,745,59,1,8450,111,100,117,99,116,59,1,8720,110,116,101,114,67,108,111,99,107,119,105,115,101,67,111,110,116,111,117,114,73,110,116,101,103,114,97,108,59,1,8755,111,115,115,59,1,10799,99,114,59,3,55349,56478,112,4,2,59,67,803,805,1,8915,97,112,59,1,8781,4,11,68,74,83,90,97,99,101,102,105,111,115,834,850,855,860,865,888,903,916,921,1011,1415,4,2,59,111,840,842,1,8517,116,114,97,104,100,59,1,10513,99,121,59,1,1026,99,121,59,1,1029,99,121,59,1,1039,4,3,103,114,115,873,879,883,103,101,114,59,1,8225,114,59,1,8609,104,118,59,1,10980,4,2,97,121,894,900,114,111,110,59,1,270,59,1,1044,108,4,2,59,116,910,912,1,8711,97,59,1,916,114,59,3,55349,56583,4,2,97,102,927,998,4,2,99,109,933,992,114,105,116,105,99,97,108,4,4,65,68,71,84,950,957,978,985,99,117,116,101,59,1,180,111,4,2,116,117,964,967,59,1,729,98,108,101,65,99,117,116,101,59,1,733,114,97,118,101,59,1,96,105,108,100,101,59,1,732,111,110,100,59,1,8900,102,101,114,101,110,116,105,97,108,68,59,1,8518,4,4,112,116,117,119,1021,1026,1048,1249,102,59,3,55349,56635,4,3,59,68,69,1034,1036,1041,1,168,111,116,59,1,8412,113,117,97,108,59,1,8784,98,108,101,4,6,67,68,76,82,85,86,1065,1082,1101,1189,1211,1236,111,110,116,111,117,114,73,110,116,101,103,114,97,108,59,1,8751,111,4,2,116,119,1089,1092,59,1,168,110,65,114,114,111,119,59,1,8659,4,2,101,111,1107,1141,102,116,4,3,65,82,84,1117,1124,1136,114,114,111,
|
||
|
},{}],21:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const unicode = require('../common/unicode');
|
||
|
const ERR = require('../common/error-codes');
|
||
|
|
||
|
//Aliases
|
||
|
const $ = unicode.CODE_POINTS;
|
||
|
|
||
|
//Const
|
||
|
const DEFAULT_BUFFER_WATERLINE = 1 << 16;
|
||
|
|
||
|
//Preprocessor
|
||
|
//NOTE: HTML input preprocessing
|
||
|
//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#preprocessing-the-input-stream)
|
||
|
class Preprocessor {
|
||
|
constructor() {
|
||
|
this.html = null;
|
||
|
|
||
|
this.pos = -1;
|
||
|
this.lastGapPos = -1;
|
||
|
this.lastCharPos = -1;
|
||
|
|
||
|
this.gapStack = [];
|
||
|
|
||
|
this.skipNextNewLine = false;
|
||
|
|
||
|
this.lastChunkWritten = false;
|
||
|
this.endOfChunkHit = false;
|
||
|
this.bufferWaterline = DEFAULT_BUFFER_WATERLINE;
|
||
|
}
|
||
|
|
||
|
_err() {
|
||
|
// NOTE: err reporting is noop by default. Enabled by mixin.
|
||
|
}
|
||
|
|
||
|
_addGap() {
|
||
|
this.gapStack.push(this.lastGapPos);
|
||
|
this.lastGapPos = this.pos;
|
||
|
}
|
||
|
|
||
|
_processSurrogate(cp) {
|
||
|
//NOTE: try to peek a surrogate pair
|
||
|
if (this.pos !== this.lastCharPos) {
|
||
|
const nextCp = this.html.charCodeAt(this.pos + 1);
|
||
|
|
||
|
if (unicode.isSurrogatePair(nextCp)) {
|
||
|
//NOTE: we have a surrogate pair. Peek pair character and recalculate code point.
|
||
|
this.pos++;
|
||
|
|
||
|
//NOTE: add gap that should be avoided during retreat
|
||
|
this._addGap();
|
||
|
|
||
|
return unicode.getSurrogatePairCodePoint(cp, nextCp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//NOTE: we are at the end of a chunk, therefore we can't infer surrogate pair yet.
|
||
|
else if (!this.lastChunkWritten) {
|
||
|
this.endOfChunkHit = true;
|
||
|
return $.EOF;
|
||
|
}
|
||
|
|
||
|
//NOTE: isolated surrogate
|
||
|
this._err(ERR.surrogateInInputStream);
|
||
|
|
||
|
return cp;
|
||
|
}
|
||
|
|
||
|
dropParsedChunk() {
|
||
|
if (this.pos > this.bufferWaterline) {
|
||
|
this.lastCharPos -= this.pos;
|
||
|
this.html = this.html.substring(this.pos);
|
||
|
this.pos = 0;
|
||
|
this.lastGapPos = -1;
|
||
|
this.gapStack = [];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
write(chunk, isLastChunk) {
|
||
|
if (this.html) {
|
||
|
this.html += chunk;
|
||
|
} else {
|
||
|
this.html = chunk;
|
||
|
}
|
||
|
|
||
|
this.lastCharPos = this.html.length - 1;
|
||
|
this.endOfChunkHit = false;
|
||
|
this.lastChunkWritten = isLastChunk;
|
||
|
}
|
||
|
|
||
|
insertHtmlAtCurrentPos(chunk) {
|
||
|
this.html = this.html.substring(0, this.pos + 1) + chunk + this.html.substring(this.pos + 1, this.html.length);
|
||
|
|
||
|
this.lastCharPos = this.html.length - 1;
|
||
|
this.endOfChunkHit = false;
|
||
|
}
|
||
|
|
||
|
advance() {
|
||
|
this.pos++;
|
||
|
|
||
|
if (this.pos > this.lastCharPos) {
|
||
|
this.endOfChunkHit = !this.lastChunkWritten;
|
||
|
return $.EOF;
|
||
|
}
|
||
|
|
||
|
let cp = this.html.charCodeAt(this.pos);
|
||
|
|
||
|
//NOTE: any U+000A LINE FEED (LF) characters that immediately follow a U+000D CARRIAGE RETURN (CR) character
|
||
|
//must be ignored.
|
||
|
if (this.skipNextNewLine && cp === $.LINE_FEED) {
|
||
|
this.skipNextNewLine = false;
|
||
|
this._addGap();
|
||
|
return this.advance();
|
||
|
}
|
||
|
|
||
|
//NOTE: all U+000D CARRIAGE RETURN (CR) characters must be converted to U+000A LINE FEED (LF) characters
|
||
|
if (cp === $.CARRIAGE_RETURN) {
|
||
|
this.skipNextNewLine = true;
|
||
|
return $.LINE_FEED;
|
||
|
}
|
||
|
|
||
|
this.skipNextNewLine = false;
|
||
|
|
||
|
if (unicode.isSurrogate(cp)) {
|
||
|
cp = this._processSurrogate(cp);
|
||
|
}
|
||
|
|
||
|
//OPTIMIZATION: first check if code point is in the common allowed
|
||
|
//range (ASCII alphanumeric, whitespaces, big chunk of BMP)
|
||
|
//before going into detailed performance cost validation.
|
||
|
const isCommonValidRange =
|
||
|
(cp > 0x1f && cp < 0x7f) || cp === $.LINE_FEED || cp === $.CARRIAGE_RETURN || (cp > 0x9f && cp < 0xfdd0);
|
||
|
|
||
|
if (!isCommonValidRange) {
|
||
|
this._checkForProblematicCharacters(cp);
|
||
|
}
|
||
|
|
||
|
return cp;
|
||
|
}
|
||
|
|
||
|
_checkForProblematicCharacters(cp) {
|
||
|
if (unicode.isControlCodePoint(cp)) {
|
||
|
this._err(ERR.controlCharacterInInputStream);
|
||
|
} else if (unicode.isUndefinedCodePoint(cp)) {
|
||
|
this._err(ERR.noncharacterInInputStream);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
retreat() {
|
||
|
if (this.pos === this.lastGapPos) {
|
||
|
this.lastGapPos = this.gapStack.pop();
|
||
|
this.pos--;
|
||
|
}
|
||
|
|
||
|
this.pos--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = Preprocessor;
|
||
|
|
||
|
},{"../common/error-codes":2,"../common/unicode":5}],22:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
const { DOCUMENT_MODE } = require('../common/html');
|
||
|
|
||
|
//Node construction
|
||
|
exports.createDocument = function() {
|
||
|
return {
|
||
|
nodeName: '#document',
|
||
|
mode: DOCUMENT_MODE.NO_QUIRKS,
|
||
|
childNodes: []
|
||
|
};
|
||
|
};
|
||
|
|
||
|
exports.createDocumentFragment = function() {
|
||
|
return {
|
||
|
nodeName: '#document-fragment',
|
||
|
childNodes: []
|
||
|
};
|
||
|
};
|
||
|
|
||
|
exports.createElement = function(tagName, namespaceURI, attrs) {
|
||
|
return {
|
||
|
nodeName: tagName,
|
||
|
tagName: tagName,
|
||
|
attrs: attrs,
|
||
|
namespaceURI: namespaceURI,
|
||
|
childNodes: [],
|
||
|
parentNode: null
|
||
|
};
|
||
|
};
|
||
|
|
||
|
exports.createCommentNode = function(data) {
|
||
|
return {
|
||
|
nodeName: '#comment',
|
||
|
data: data,
|
||
|
parentNode: null
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const createTextNode = function(value) {
|
||
|
return {
|
||
|
nodeName: '#text',
|
||
|
value: value,
|
||
|
parentNode: null
|
||
|
};
|
||
|
};
|
||
|
|
||
|
//Tree mutation
|
||
|
const appendChild = (exports.appendChild = function(parentNode, newNode) {
|
||
|
parentNode.childNodes.push(newNode);
|
||
|
newNode.parentNode = parentNode;
|
||
|
});
|
||
|
|
||
|
const insertBefore = (exports.insertBefore = function(parentNode, newNode, referenceNode) {
|
||
|
const insertionIdx = parentNode.childNodes.indexOf(referenceNode);
|
||
|
|
||
|
parentNode.childNodes.splice(insertionIdx, 0, newNode);
|
||
|
newNode.parentNode = parentNode;
|
||
|
});
|
||
|
|
||
|
exports.setTemplateContent = function(templateElement, contentElement) {
|
||
|
templateElement.content = contentElement;
|
||
|
};
|
||
|
|
||
|
exports.getTemplateContent = function(templateElement) {
|
||
|
return templateElement.content;
|
||
|
};
|
||
|
|
||
|
exports.setDocumentType = function(document, name, publicId, systemId) {
|
||
|
let doctypeNode = null;
|
||
|
|
||
|
for (let i = 0; i < document.childNodes.length; i++) {
|
||
|
if (document.childNodes[i].nodeName === '#documentType') {
|
||
|
doctypeNode = document.childNodes[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (doctypeNode) {
|
||
|
doctypeNode.name = name;
|
||
|
doctypeNode.publicId = publicId;
|
||
|
doctypeNode.systemId = systemId;
|
||
|
} else {
|
||
|
appendChild(document, {
|
||
|
nodeName: '#documentType',
|
||
|
name: name,
|
||
|
publicId: publicId,
|
||
|
systemId: systemId
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.setDocumentMode = function(document, mode) {
|
||
|
document.mode = mode;
|
||
|
};
|
||
|
|
||
|
exports.getDocumentMode = function(document) {
|
||
|
return document.mode;
|
||
|
};
|
||
|
|
||
|
exports.detachNode = function(node) {
|
||
|
if (node.parentNode) {
|
||
|
const idx = node.parentNode.childNodes.indexOf(node);
|
||
|
|
||
|
node.parentNode.childNodes.splice(idx, 1);
|
||
|
node.parentNode = null;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.insertText = function(parentNode, text) {
|
||
|
if (parentNode.childNodes.length) {
|
||
|
const prevNode = parentNode.childNodes[parentNode.childNodes.length - 1];
|
||
|
|
||
|
if (prevNode.nodeName === '#text') {
|
||
|
prevNode.value += text;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
appendChild(parentNode, createTextNode(text));
|
||
|
};
|
||
|
|
||
|
exports.insertTextBefore = function(parentNode, text, referenceNode) {
|
||
|
const prevNode = parentNode.childNodes[parentNode.childNodes.indexOf(referenceNode) - 1];
|
||
|
|
||
|
if (prevNode && prevNode.nodeName === '#text') {
|
||
|
prevNode.value += text;
|
||
|
} else {
|
||
|
insertBefore(parentNode, createTextNode(text), referenceNode);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
exports.adoptAttributes = function(recipient, attrs) {
|
||
|
const recipientAttrsMap = [];
|
||
|
|
||
|
for (let i = 0; i < recipient.attrs.length; i++) {
|
||
|
recipientAttrsMap.push(recipient.attrs[i].name);
|
||
|
}
|
||
|
|
||
|
for (let j = 0; j < attrs.length; j++) {
|
||
|
if (recipientAttrsMap.indexOf(attrs[j].name) === -1) {
|
||
|
recipient.attrs.push(attrs[j]);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//Tree traversing
|
||
|
exports.getFirstChild = function(node) {
|
||
|
return node.childNodes[0];
|
||
|
};
|
||
|
|
||
|
exports.getChildNodes = function(node) {
|
||
|
return node.childNodes;
|
||
|
};
|
||
|
|
||
|
exports.getParentNode = function(node) {
|
||
|
return node.parentNode;
|
||
|
};
|
||
|
|
||
|
exports.getAttrList = function(element) {
|
||
|
return element.attrs;
|
||
|
};
|
||
|
|
||
|
//Node data
|
||
|
exports.getTagName = function(element) {
|
||
|
return element.tagName;
|
||
|
};
|
||
|
|
||
|
exports.getNamespaceURI = function(element) {
|
||
|
return element.namespaceURI;
|
||
|
};
|
||
|
|
||
|
exports.getTextNodeContent = function(textNode) {
|
||
|
return textNode.value;
|
||
|
};
|
||
|
|
||
|
exports.getCommentNodeContent = function(commentNode) {
|
||
|
return commentNode.data;
|
||
|
};
|
||
|
|
||
|
exports.getDocumentTypeNodeName = function(doctypeNode) {
|
||
|
return doctypeNode.name;
|
||
|
};
|
||
|
|
||
|
exports.getDocumentTypeNodePublicId = function(doctypeNode) {
|
||
|
return doctypeNode.publicId;
|
||
|
};
|
||
|
|
||
|
exports.getDocumentTypeNodeSystemId = function(doctypeNode) {
|
||
|
return doctypeNode.systemId;
|
||
|
};
|
||
|
|
||
|
//Node types
|
||
|
exports.isTextNode = function(node) {
|
||
|
return node.nodeName === '#text';
|
||
|
};
|
||
|
|
||
|
exports.isCommentNode = function(node) {
|
||
|
return node.nodeName === '#comment';
|
||
|
};
|
||
|
|
||
|
exports.isDocumentTypeNode = function(node) {
|
||
|
return node.nodeName === '#documentType';
|
||
|
};
|
||
|
|
||
|
exports.isElementNode = function(node) {
|
||
|
return !!node.tagName;
|
||
|
};
|
||
|
|
||
|
// Source code location
|
||
|
exports.setNodeSourceCodeLocation = function(node, location) {
|
||
|
node.sourceCodeLocation = location;
|
||
|
};
|
||
|
|
||
|
exports.getNodeSourceCodeLocation = function(node) {
|
||
|
return node.sourceCodeLocation;
|
||
|
};
|
||
|
|
||
|
exports.updateNodeSourceCodeLocation = function(node, endLocation) {
|
||
|
node.sourceCodeLocation = Object.assign(node.sourceCodeLocation, endLocation);
|
||
|
};
|
||
|
|
||
|
},{"../common/html":4}],23:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
module.exports = function mergeOptions(defaults, options) {
|
||
|
options = options || Object.create(null);
|
||
|
|
||
|
return [defaults, options].reduce((merged, optObj) => {
|
||
|
Object.keys(optObj).forEach(key => {
|
||
|
merged[key] = optObj[key];
|
||
|
});
|
||
|
|
||
|
return merged;
|
||
|
}, Object.create(null));
|
||
|
};
|
||
|
|
||
|
},{}],24:[function(require,module,exports){
|
||
|
'use strict';
|
||
|
|
||
|
class Mixin {
|
||
|
constructor(host) {
|
||
|
const originalMethods = {};
|
||
|
const overriddenMethods = this._getOverriddenMethods(this, originalMethods);
|
||
|
|
||
|
for (const key of Object.keys(overriddenMethods)) {
|
||
|
if (typeof overriddenMethods[key] === 'function') {
|
||
|
originalMethods[key] = host[key];
|
||
|
host[key] = overriddenMethods[key];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_getOverriddenMethods() {
|
||
|
throw new Error('Not implemented');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Mixin.install = function(host, Ctor, opts) {
|
||
|
if (!host.__mixins) {
|
||
|
host.__mixins = [];
|
||
|
}
|
||
|
|
||
|
for (let i = 0; i < host.__mixins.length; i++) {
|
||
|
if (host.__mixins[i].constructor === Ctor) {
|
||
|
return host.__mixins[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const mixin = new Ctor(host, opts);
|
||
|
|
||
|
host.__mixins.push(mixin);
|
||
|
|
||
|
return mixin;
|
||
|
};
|
||
|
|
||
|
module.exports = Mixin;
|
||
|
|
||
|
},{}]},{},[14])(14)
|
||
|
});
|
||
|
|
||
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi9jb21tb24vZG9jdHlwZS5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2NvbW1vbi9lcnJvci1jb2Rlcy5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2NvbW1vbi9mb3JlaWduLWNvbnRlbnQuanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi9jb21tb24vaHRtbC5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2NvbW1vbi91bmljb2RlLmpzIiwibm9kZV9tb2R1bGVzL3BhcnNlNS9saWIvZXh0ZW5zaW9ucy9lcnJvci1yZXBvcnRpbmcvbWl4aW4tYmFzZS5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2V4dGVuc2lvbnMvZXJyb3ItcmVwb3J0aW5nL3BhcnNlci1taXhpbi5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2V4dGVuc2lvbnMvZXJyb3ItcmVwb3J0aW5nL3ByZXByb2Nlc3Nvci1taXhpbi5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2V4dGVuc2lvbnMvZXJyb3ItcmVwb3J0aW5nL3Rva2VuaXplci1taXhpbi5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2V4dGVuc2lvbnMvbG9jYXRpb24taW5mby9vcGVuLWVsZW1lbnQtc3RhY2stbWl4aW4uanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi9leHRlbnNpb25zL2xvY2F0aW9uLWluZm8vcGFyc2VyLW1peGluLmpzIiwibm9kZV9tb2R1bGVzL3BhcnNlNS9saWIvZXh0ZW5zaW9ucy9sb2NhdGlvbi1pbmZvL3Rva2VuaXplci1taXhpbi5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL2V4dGVuc2lvbnMvcG9zaXRpb24tdHJhY2tpbmcvcHJlcHJvY2Vzc29yLW1peGluLmpzIiwibm9kZV9tb2R1bGVzL3BhcnNlNS9saWIvaW5kZXguanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi9wYXJzZXIvZm9ybWF0dGluZy1lbGVtZW50LWxpc3QuanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi9wYXJzZXIvaW5kZXguanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi9wYXJzZXIvb3Blbi1lbGVtZW50LXN0YWNrLmpzIiwibm9kZV9tb2R1bGVzL3BhcnNlNS9saWIvc2VyaWFsaXplci9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL3Rva2VuaXplci9pbmRleC5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL3Rva2VuaXplci9uYW1lZC1lbnRpdHktZGF0YS5qcyIsIm5vZGVfbW9kdWxlcy9wYXJzZTUvbGliL3Rva2VuaXplci9wcmVwcm9jZXNzb3IuanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi90cmVlLWFkYXB0ZXJzL2RlZmF1bHQuanMiLCJub2RlX21vZHVsZXMvcGFyc2U1L2xpYi91dGlscy9tZXJnZS1vcHRpb25zLmpzIiwibm9kZV9tb2R1bGVzL3BhcnNlNS9saWIvdXRpbHMvbWl4aW4uanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUNsS0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUN
|