html-dom-parser
Version:
HTML to DOM parser.
1 lines • 77.4 kB
Source Map (JSON)
{"version":3,"file":"Tokenizer.mjs","sources":["../../../../node_modules/htmlparser2/dist/Tokenizer.js"],"sourcesContent":["import { DecodingMode, EntityDecoder, htmlDecodeTree, xmlDecodeTree, } from \"entities/decode\";\nvar CharCodes;\n(function (CharCodes) {\n CharCodes[CharCodes[\"Tab\"] = 9] = \"Tab\";\n CharCodes[CharCodes[\"NewLine\"] = 10] = \"NewLine\";\n CharCodes[CharCodes[\"FormFeed\"] = 12] = \"FormFeed\";\n CharCodes[CharCodes[\"CarriageReturn\"] = 13] = \"CarriageReturn\";\n CharCodes[CharCodes[\"Space\"] = 32] = \"Space\";\n CharCodes[CharCodes[\"ExclamationMark\"] = 33] = \"ExclamationMark\";\n CharCodes[CharCodes[\"Number\"] = 35] = \"Number\";\n CharCodes[CharCodes[\"Amp\"] = 38] = \"Amp\";\n CharCodes[CharCodes[\"SingleQuote\"] = 39] = \"SingleQuote\";\n CharCodes[CharCodes[\"DoubleQuote\"] = 34] = \"DoubleQuote\";\n CharCodes[CharCodes[\"Dash\"] = 45] = \"Dash\";\n CharCodes[CharCodes[\"Slash\"] = 47] = \"Slash\";\n CharCodes[CharCodes[\"Zero\"] = 48] = \"Zero\";\n CharCodes[CharCodes[\"Nine\"] = 57] = \"Nine\";\n CharCodes[CharCodes[\"Semi\"] = 59] = \"Semi\";\n CharCodes[CharCodes[\"Lt\"] = 60] = \"Lt\";\n CharCodes[CharCodes[\"Eq\"] = 61] = \"Eq\";\n CharCodes[CharCodes[\"Gt\"] = 62] = \"Gt\";\n CharCodes[CharCodes[\"Questionmark\"] = 63] = \"Questionmark\";\n CharCodes[CharCodes[\"UpperA\"] = 65] = \"UpperA\";\n CharCodes[CharCodes[\"LowerA\"] = 97] = \"LowerA\";\n CharCodes[CharCodes[\"UpperF\"] = 70] = \"UpperF\";\n CharCodes[CharCodes[\"LowerF\"] = 102] = \"LowerF\";\n CharCodes[CharCodes[\"UpperZ\"] = 90] = \"UpperZ\";\n CharCodes[CharCodes[\"LowerZ\"] = 122] = \"LowerZ\";\n CharCodes[CharCodes[\"LowerX\"] = 120] = \"LowerX\";\n CharCodes[CharCodes[\"OpeningSquareBracket\"] = 91] = \"OpeningSquareBracket\";\n})(CharCodes || (CharCodes = {}));\n/** All the states the tokenizer can be in. */\nvar State;\n(function (State) {\n State[State[\"Text\"] = 1] = \"Text\";\n State[State[\"BeforeTagName\"] = 2] = \"BeforeTagName\";\n State[State[\"InTagName\"] = 3] = \"InTagName\";\n State[State[\"InSelfClosingTag\"] = 4] = \"InSelfClosingTag\";\n State[State[\"BeforeClosingTagName\"] = 5] = \"BeforeClosingTagName\";\n State[State[\"InClosingTagName\"] = 6] = \"InClosingTagName\";\n State[State[\"AfterClosingTagName\"] = 7] = \"AfterClosingTagName\";\n // Attributes\n State[State[\"BeforeAttributeName\"] = 8] = \"BeforeAttributeName\";\n State[State[\"InAttributeName\"] = 9] = \"InAttributeName\";\n State[State[\"AfterAttributeName\"] = 10] = \"AfterAttributeName\";\n State[State[\"BeforeAttributeValue\"] = 11] = \"BeforeAttributeValue\";\n State[State[\"InAttributeValueDq\"] = 12] = \"InAttributeValueDq\";\n State[State[\"InAttributeValueSq\"] = 13] = \"InAttributeValueSq\";\n State[State[\"InAttributeValueNq\"] = 14] = \"InAttributeValueNq\";\n // Declarations\n State[State[\"BeforeDeclaration\"] = 15] = \"BeforeDeclaration\";\n State[State[\"InDeclaration\"] = 16] = \"InDeclaration\";\n // Processing instructions\n State[State[\"InProcessingInstruction\"] = 17] = \"InProcessingInstruction\";\n // Comments & CDATA\n State[State[\"BeforeComment\"] = 18] = \"BeforeComment\";\n State[State[\"CDATASequence\"] = 19] = \"CDATASequence\";\n State[State[\"DeclarationSequence\"] = 20] = \"DeclarationSequence\";\n State[State[\"InSpecialComment\"] = 21] = \"InSpecialComment\";\n State[State[\"InCommentLike\"] = 22] = \"InCommentLike\";\n // Special tags\n State[State[\"SpecialStartSequence\"] = 23] = \"SpecialStartSequence\";\n State[State[\"InSpecialTag\"] = 24] = \"InSpecialTag\";\n State[State[\"InPlainText\"] = 25] = \"InPlainText\";\n State[State[\"InEntity\"] = 26] = \"InEntity\";\n})(State || (State = {}));\nfunction isWhitespace(c) {\n return (c === CharCodes.Space ||\n c === CharCodes.NewLine ||\n c === CharCodes.Tab ||\n c === CharCodes.FormFeed ||\n c === CharCodes.CarriageReturn);\n}\nfunction isEndOfTagSection(c) {\n return c === CharCodes.Slash || c === CharCodes.Gt || isWhitespace(c);\n}\nfunction isASCIIAlpha(c) {\n return ((c >= CharCodes.LowerA && c <= CharCodes.LowerZ) ||\n (c >= CharCodes.UpperA && c <= CharCodes.UpperZ));\n}\n/**\n * Quote style used for parsed attributes.\n */\nexport var QuoteType;\n(function (QuoteType) {\n QuoteType[QuoteType[\"NoValue\"] = 0] = \"NoValue\";\n QuoteType[QuoteType[\"Unquoted\"] = 1] = \"Unquoted\";\n QuoteType[QuoteType[\"Single\"] = 2] = \"Single\";\n QuoteType[QuoteType[\"Double\"] = 3] = \"Double\";\n})(QuoteType || (QuoteType = {}));\n/**\n * Sequences used to match longer strings.\n *\n * We don't have `Script`, `Style`, or `Title` here. Instead, we re-use the *End\n * sequences with an increased offset.\n */\nconst Sequences = {\n Empty: new Uint8Array(0),\n Cdata: new Uint8Array([0x43, 0x44, 0x41, 0x54, 0x41, 0x5b]), // CDATA[\n CdataEnd: new Uint8Array([0x5d, 0x5d, 0x3e]), // ]]>\n CommentEnd: new Uint8Array([0x2d, 0x2d, 0x21, 0x3e]), // `--!>`\n Doctype: new Uint8Array([0x64, 0x6f, 0x63, 0x74, 0x79, 0x70, 0x65]), // `doctype`\n IframeEnd: new Uint8Array([0x3c, 0x2f, 0x69, 0x66, 0x72, 0x61, 0x6d, 0x65]), // `</iframe`\n NoembedEnd: new Uint8Array([\n 0x3c, 0x2f, 0x6e, 0x6f, 0x65, 0x6d, 0x62, 0x65, 0x64,\n ]), // `</noembed`\n NoframesEnd: new Uint8Array([\n 0x3c, 0x2f, 0x6e, 0x6f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x73,\n ]), // `</noframes`\n Plaintext: new Uint8Array([\n 0x3c, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74,\n ]), // `</plaintext`\n ScriptEnd: new Uint8Array([0x3c, 0x2f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74]), // `</script`\n StyleEnd: new Uint8Array([0x3c, 0x2f, 0x73, 0x74, 0x79, 0x6c, 0x65]), // `</style`\n TitleEnd: new Uint8Array([0x3c, 0x2f, 0x74, 0x69, 0x74, 0x6c, 0x65]), // `</title`\n TextareaEnd: new Uint8Array([\n 0x3c, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x61, 0x72, 0x65, 0x61,\n ]), // `</textarea`\n XmpEnd: new Uint8Array([0x3c, 0x2f, 0x78, 0x6d, 0x70]), // `</xmp`\n};\n/**\n * Maps the first lowercase character of an HTML tag name to the sequence\n * used for special-tag detection. All sequences share a common layout\n * where index 2 is the first tag-name character, so matching always\n * continues from offset 3.\n */\nconst specialStartSequences = new Map([\n [Sequences.IframeEnd[2], Sequences.IframeEnd],\n [Sequences.NoembedEnd[2], Sequences.NoembedEnd],\n [Sequences.Plaintext[2], Sequences.Plaintext],\n [Sequences.ScriptEnd[2], Sequences.ScriptEnd],\n [Sequences.TitleEnd[2], Sequences.TitleEnd],\n [Sequences.XmpEnd[2], Sequences.XmpEnd],\n]);\n/**\n * Tokenizer implementation used by `Parser`.\n */\nexport default class Tokenizer {\n cbs;\n /** The current state the tokenizer is in. */\n state = State.Text;\n /** The read buffer. */\n buffer = \"\";\n /** The beginning of the section that is currently being read. */\n sectionStart = 0;\n /** The index within the buffer that we are currently looking at. */\n index = 0;\n /** The start of the last entity. */\n entityStart = 0;\n /** Some behavior, eg. when decoding entities, is done while we are in another state. This keeps track of the other state type. */\n baseState = State.Text;\n /** For special parsing behavior inside of script and style tags. */\n isSpecial = false;\n /** Indicates whether the tokenizer has been paused. */\n running = true;\n /** The offset of the current buffer. */\n offset = 0;\n xmlMode;\n decodeEntities;\n recognizeSelfClosing;\n entityDecoder;\n constructor({ xmlMode = false, decodeEntities = true, recognizeSelfClosing = xmlMode, }, cbs) {\n this.cbs = cbs;\n this.xmlMode = xmlMode;\n this.decodeEntities = decodeEntities;\n this.recognizeSelfClosing = recognizeSelfClosing;\n this.entityDecoder = new EntityDecoder(xmlMode ? xmlDecodeTree : htmlDecodeTree, (cp, consumed) => this.emitCodePoint(cp, consumed));\n }\n reset() {\n this.state = State.Text;\n this.buffer = \"\";\n this.sectionStart = 0;\n this.index = 0;\n this.baseState = State.Text;\n this.isSpecial = false;\n this.currentSequence = Sequences.Empty;\n this.sequenceIndex = 0;\n this.running = true;\n this.offset = 0;\n }\n write(chunk) {\n this.offset += this.buffer.length;\n this.buffer = chunk;\n this.parse();\n }\n end() {\n if (this.running)\n this.finish();\n }\n pause() {\n this.running = false;\n }\n resume() {\n this.running = true;\n if (this.index < this.buffer.length + this.offset) {\n this.parse();\n }\n }\n stateText(c) {\n if (c === CharCodes.Lt ||\n (!this.decodeEntities && this.fastForwardTo(CharCodes.Lt))) {\n if (this.index > this.sectionStart) {\n this.cbs.ontext(this.sectionStart, this.index);\n }\n this.state = State.BeforeTagName;\n this.sectionStart = this.index;\n }\n else if (this.decodeEntities && c === CharCodes.Amp) {\n this.startEntity();\n }\n }\n currentSequence = Sequences.Empty;\n sequenceIndex = 0;\n enterTagBody() {\n if (this.currentSequence === Sequences.Plaintext) {\n this.currentSequence = Sequences.Empty;\n this.state = State.InPlainText;\n }\n else if (this.isSpecial) {\n this.state = State.InSpecialTag;\n this.sequenceIndex = 0;\n }\n else {\n this.state = State.Text;\n }\n }\n /**\n * Match the opening tag name against an HTML text-only tag sequence.\n *\n * Some tags share an initial prefix (`script`/`style`, `title`/`textarea`,\n * `noembed`/`noframes`), so we may switch to an alternate sequence at the\n * first distinguishing byte. On a successful full match we fall back to\n * the normal tag-name state; a later `>` will enter raw-text, RCDATA, or\n * plaintext mode based on `currentSequence` / `isSpecial`.\n * @param c Current character code point.\n */\n stateSpecialStartSequence(c) {\n const lower = c | 0x20;\n // Still matching — check for an alternate sequence at branch points.\n if (this.sequenceIndex < this.currentSequence.length) {\n if (lower === this.currentSequence[this.sequenceIndex]) {\n this.sequenceIndex++;\n return;\n }\n if (this.sequenceIndex === 3) {\n if (this.currentSequence === Sequences.ScriptEnd &&\n lower === Sequences.StyleEnd[3]) {\n this.currentSequence = Sequences.StyleEnd;\n this.sequenceIndex = 4;\n return;\n }\n if (this.currentSequence === Sequences.TitleEnd &&\n lower === Sequences.TextareaEnd[3]) {\n this.currentSequence = Sequences.TextareaEnd;\n this.sequenceIndex = 4;\n return;\n }\n }\n else if (this.sequenceIndex === 4 &&\n this.currentSequence === Sequences.NoembedEnd &&\n lower === Sequences.NoframesEnd[4]) {\n this.currentSequence = Sequences.NoframesEnd;\n this.sequenceIndex = 5;\n return;\n }\n }\n else if (isEndOfTagSection(c)) {\n // Full match on a valid tag boundary — keep the sequence.\n this.sequenceIndex = 0;\n this.state = State.InTagName;\n this.stateInTagName(c);\n return;\n }\n // No match — abandon special-tag detection.\n this.isSpecial = false;\n this.currentSequence = Sequences.Empty;\n this.sequenceIndex = 0;\n this.state = State.InTagName;\n this.stateInTagName(c);\n }\n stateCDATASequence(c) {\n if (c === Sequences.Cdata[this.sequenceIndex]) {\n if (++this.sequenceIndex === Sequences.Cdata.length) {\n this.state = State.InCommentLike;\n this.currentSequence = Sequences.CdataEnd;\n this.sequenceIndex = 0;\n this.sectionStart = this.index + 1;\n }\n }\n else {\n this.sequenceIndex = 0;\n if (this.xmlMode) {\n this.state = State.InDeclaration;\n this.stateInDeclaration(c); // Reconsume the character\n }\n else {\n this.state = State.InSpecialComment;\n this.stateInSpecialComment(c); // Reconsume the character\n }\n }\n }\n /**\n * When we wait for one specific character, we can speed things up\n * by skipping through the buffer until we find it.\n * @param c Current character code point.\n * @returns Whether the character was found.\n */\n fastForwardTo(c) {\n while (++this.index < this.buffer.length + this.offset) {\n if (this.buffer.charCodeAt(this.index - this.offset) === c) {\n return true;\n }\n }\n /*\n * We increment the index at the end of the `parse` loop,\n * so set it to `buffer.length - 1` here.\n *\n * TODO: Refactor `parse` to increment index before calling states.\n */\n this.index = this.buffer.length + this.offset - 1;\n return false;\n }\n /**\n * Emit a comment token and return to the text state.\n * @param offset Number of characters in the end sequence that have already been matched.\n */\n emitComment(offset) {\n this.cbs.oncomment(this.sectionStart, this.index, offset);\n this.sequenceIndex = 0;\n this.sectionStart = this.index + 1;\n this.state = State.Text;\n }\n /**\n * Comments and CDATA end with `-->` and `]]>`.\n *\n * Their common qualities are:\n * - Their end sequences have a distinct character they start with.\n * - That character is then repeated, so we have to check multiple repeats.\n * - All characters but the start character of the sequence can be skipped.\n * @param c Current character code point.\n */\n stateInCommentLike(c) {\n if (!this.xmlMode &&\n this.currentSequence === Sequences.CommentEnd &&\n this.sequenceIndex <= 1 &&\n /*\n * We're still at the very start of the comment: the only\n * characters consumed since `<!--` are the dashes that\n * advanced sequenceIndex (0 for `<!-->`, 1 for `<!--->`).\n */\n this.index === this.sectionStart + this.sequenceIndex &&\n c === CharCodes.Gt) {\n // Abruptly closed empty HTML comment.\n this.emitComment(this.sequenceIndex);\n }\n else if (this.currentSequence === Sequences.CommentEnd &&\n this.sequenceIndex === 2 &&\n c === CharCodes.Gt) {\n // `!` is optional here, so the same sequence also accepts `-->`.\n this.emitComment(2);\n }\n else if (this.currentSequence === Sequences.CommentEnd &&\n this.sequenceIndex === this.currentSequence.length - 1 &&\n c !== CharCodes.Gt) {\n this.sequenceIndex = Number(c === CharCodes.Dash);\n }\n else if (c === this.currentSequence[this.sequenceIndex]) {\n if (++this.sequenceIndex === this.currentSequence.length) {\n if (this.currentSequence === Sequences.CdataEnd) {\n this.cbs.oncdata(this.sectionStart, this.index, 2);\n }\n else {\n this.cbs.oncomment(this.sectionStart, this.index, 3);\n }\n this.sequenceIndex = 0;\n this.sectionStart = this.index + 1;\n this.state = State.Text;\n }\n }\n else if (this.sequenceIndex === 0) {\n // Fast-forward to the first character of the sequence\n if (this.fastForwardTo(this.currentSequence[0])) {\n this.sequenceIndex = 1;\n }\n }\n else if (c !== this.currentSequence[this.sequenceIndex - 1]) {\n // Allow long sequences, eg. --->, ]]]>\n this.sequenceIndex = 0;\n }\n }\n /**\n * HTML only allows ASCII alpha characters (a-z and A-Z) at the beginning of a tag name.\n *\n * XML allows a lot more characters here (@see https://www.w3.org/TR/REC-xml/#NT-NameStartChar).\n * We allow anything that wouldn't end the tag.\n * @param c Current character code point.\n */\n isTagStartChar(c) {\n return this.xmlMode ? !isEndOfTagSection(c) : isASCIIAlpha(c);\n }\n /**\n * Scan raw-text / RCDATA content for the matching end tag.\n *\n * For RCDATA tags (`<title>`, `<textarea>`) entities are decoded inline.\n * For raw-text tags (`<script>`, `<style>`, etc.) we fast-forward to `<`.\n * @param c Current character code point.\n */\n stateInSpecialTag(c) {\n if (this.sequenceIndex === this.currentSequence.length) {\n if (isEndOfTagSection(c)) {\n const endOfText = this.index - this.currentSequence.length;\n if (this.sectionStart < endOfText) {\n // Spoof the index so that reported locations match up.\n const actualIndex = this.index;\n this.index = endOfText;\n this.cbs.ontext(this.sectionStart, endOfText);\n this.index = actualIndex;\n }\n this.isSpecial = false;\n this.sectionStart = endOfText + 2; // Skip over the `</`\n this.stateInClosingTagName(c);\n return; // We are done; skip the rest of the function.\n }\n this.sequenceIndex = 0;\n }\n if ((c | 0x20) === this.currentSequence[this.sequenceIndex]) {\n this.sequenceIndex += 1;\n }\n else if (this.sequenceIndex === 0) {\n if (this.currentSequence === Sequences.TitleEnd ||\n this.currentSequence === Sequences.TextareaEnd) {\n // RCDATA tags have to parse entities while still looking for their end tag.\n if (this.decodeEntities && c === CharCodes.Amp) {\n this.startEntity();\n }\n }\n else if (this.fastForwardTo(CharCodes.Lt)) {\n // Outside of RCDATA tags, we can fast-forward.\n this.sequenceIndex = 1;\n }\n }\n else {\n // If we see a `<`, set the sequence index to 1; useful for eg. `<</script>`.\n this.sequenceIndex = Number(c === CharCodes.Lt);\n }\n }\n stateBeforeTagName(c) {\n if (c === CharCodes.ExclamationMark) {\n this.state = State.BeforeDeclaration;\n this.sectionStart = this.index + 1;\n }\n else if (c === CharCodes.Questionmark) {\n if (this.xmlMode) {\n this.state = State.InProcessingInstruction;\n this.sequenceIndex = 0;\n this.sectionStart = this.index + 1;\n }\n else {\n this.state = State.InSpecialComment;\n this.sectionStart = this.index;\n }\n }\n else if (this.isTagStartChar(c)) {\n this.sectionStart = this.index;\n const special = this.xmlMode || this.cbs.isInForeignContext?.()\n ? undefined\n : specialStartSequences.get(c | 0x20);\n if (special === undefined) {\n this.state = State.InTagName;\n }\n else {\n this.isSpecial = true;\n this.currentSequence = special;\n this.sequenceIndex = 3;\n this.state = State.SpecialStartSequence;\n }\n }\n else if (c === CharCodes.Slash) {\n this.state = State.BeforeClosingTagName;\n }\n else {\n this.state = State.Text;\n this.stateText(c);\n }\n }\n stateInTagName(c) {\n if (isEndOfTagSection(c)) {\n this.cbs.onopentagname(this.sectionStart, this.index);\n this.sectionStart = -1;\n this.state = State.BeforeAttributeName;\n this.stateBeforeAttributeName(c);\n }\n }\n stateBeforeClosingTagName(c) {\n if (isWhitespace(c)) {\n if (this.xmlMode) {\n // Ignore\n }\n else {\n this.state = State.InSpecialComment;\n this.sectionStart = this.index;\n }\n }\n else if (c === CharCodes.Gt) {\n this.state = State.Text;\n if (!this.xmlMode) {\n this.sectionStart = this.index + 1;\n }\n }\n else {\n this.state = this.isTagStartChar(c)\n ? State.InClosingTagName\n : State.InSpecialComment;\n this.sectionStart = this.index;\n }\n }\n stateInClosingTagName(c) {\n if (isEndOfTagSection(c)) {\n this.cbs.onclosetag(this.sectionStart, this.index);\n this.sectionStart = -1;\n this.state = State.AfterClosingTagName;\n this.stateAfterClosingTagName(c);\n }\n }\n stateAfterClosingTagName(c) {\n // Skip everything until \">\"\n if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n }\n stateBeforeAttributeName(c) {\n if (c === CharCodes.Gt) {\n this.cbs.onopentagend(this.index);\n this.enterTagBody();\n this.sectionStart = this.index + 1;\n }\n else if (c === CharCodes.Slash) {\n this.state = State.InSelfClosingTag;\n }\n else if (!isWhitespace(c)) {\n this.state = State.InAttributeName;\n this.sectionStart = this.index;\n }\n }\n /**\n * Handle `/` before `>` in an opening tag.\n *\n * In HTML mode, text-only tags ignore the self-closing flag and still enter\n * their raw-text/RCDATA/plaintext state unless self-closing tags are being\n * recognized. In XML mode, or for ordinary tags, the tokenizer returns to\n * regular text parsing after emitting the self-closing callback.\n * @param c Current character code point.\n */\n stateInSelfClosingTag(c) {\n if (c === CharCodes.Gt) {\n this.cbs.onselfclosingtag(this.index);\n this.sectionStart = this.index + 1;\n if (!this.recognizeSelfClosing) {\n this.enterTagBody();\n return;\n }\n this.state = State.Text;\n this.isSpecial = false; // Reset special state, in case of self-closing special tags\n this.currentSequence = Sequences.Empty;\n }\n else if (!isWhitespace(c)) {\n this.state = State.BeforeAttributeName;\n this.stateBeforeAttributeName(c);\n }\n }\n stateInAttributeName(c) {\n if (c === CharCodes.Eq || isEndOfTagSection(c)) {\n this.cbs.onattribname(this.sectionStart, this.index);\n this.sectionStart = this.index;\n this.state = State.AfterAttributeName;\n this.stateAfterAttributeName(c);\n }\n }\n stateAfterAttributeName(c) {\n if (c === CharCodes.Eq) {\n this.state = State.BeforeAttributeValue;\n }\n else if (c === CharCodes.Slash || c === CharCodes.Gt) {\n this.cbs.onattribend(QuoteType.NoValue, this.sectionStart);\n this.sectionStart = -1;\n this.state = State.BeforeAttributeName;\n this.stateBeforeAttributeName(c);\n }\n else if (!isWhitespace(c)) {\n this.cbs.onattribend(QuoteType.NoValue, this.sectionStart);\n this.state = State.InAttributeName;\n this.sectionStart = this.index;\n }\n }\n stateBeforeAttributeValue(c) {\n if (c === CharCodes.DoubleQuote) {\n this.state = State.InAttributeValueDq;\n this.sectionStart = this.index + 1;\n }\n else if (c === CharCodes.SingleQuote) {\n this.state = State.InAttributeValueSq;\n this.sectionStart = this.index + 1;\n }\n else if (!isWhitespace(c)) {\n this.sectionStart = this.index;\n this.state = State.InAttributeValueNq;\n this.stateInAttributeValueNoQuotes(c); // Reconsume token\n }\n }\n handleInAttributeValue(c, quote) {\n if (c === quote ||\n (!this.decodeEntities && this.fastForwardTo(quote))) {\n this.cbs.onattribdata(this.sectionStart, this.index);\n this.sectionStart = -1;\n this.cbs.onattribend(quote === CharCodes.DoubleQuote\n ? QuoteType.Double\n : QuoteType.Single, this.index + 1);\n this.state = State.BeforeAttributeName;\n }\n else if (this.decodeEntities && c === CharCodes.Amp) {\n this.startEntity();\n }\n }\n stateInAttributeValueDoubleQuotes(c) {\n this.handleInAttributeValue(c, CharCodes.DoubleQuote);\n }\n stateInAttributeValueSingleQuotes(c) {\n this.handleInAttributeValue(c, CharCodes.SingleQuote);\n }\n stateInAttributeValueNoQuotes(c) {\n if (isWhitespace(c) || c === CharCodes.Gt) {\n this.cbs.onattribdata(this.sectionStart, this.index);\n this.sectionStart = -1;\n this.cbs.onattribend(QuoteType.Unquoted, this.index);\n this.state = State.BeforeAttributeName;\n this.stateBeforeAttributeName(c);\n }\n else if (this.decodeEntities && c === CharCodes.Amp) {\n this.startEntity();\n }\n }\n /**\n * Distinguish between CDATA, declarations, HTML comments, and HTML bogus\n * comments after `<!`.\n *\n * In HTML mode, only real comments and doctypes stay on declaration paths;\n * everything else becomes a bogus comment terminated by the next `>`.\n * @param c Current character code point.\n */\n stateBeforeDeclaration(c) {\n if (c === CharCodes.OpeningSquareBracket) {\n this.state = State.CDATASequence;\n this.sequenceIndex = 0;\n }\n else if (this.xmlMode) {\n this.state =\n c === CharCodes.Dash\n ? State.BeforeComment\n : State.InDeclaration;\n }\n else if ((c | 0x20) === Sequences.Doctype[0]) {\n this.state = State.DeclarationSequence;\n this.currentSequence = Sequences.Doctype;\n this.sequenceIndex = 1;\n }\n else if (c === CharCodes.Gt) {\n this.cbs.oncomment(this.sectionStart, this.index, 0);\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n else if (c === CharCodes.Dash) {\n this.state = State.BeforeComment;\n }\n else {\n this.state = State.InSpecialComment;\n }\n }\n /**\n * Continue matching `doctype` after `<!d`.\n *\n * A full `doctype` match stays on the declaration path; any other name falls\n * back to an HTML bogus comment, which matches browser behavior for\n * non-doctype `<!...>` constructs.\n * @param c Current character code point.\n */\n stateDeclarationSequence(c) {\n if (this.sequenceIndex === this.currentSequence.length) {\n this.state = State.InDeclaration;\n this.stateInDeclaration(c);\n }\n else if ((c | 0x20) === this.currentSequence[this.sequenceIndex]) {\n this.sequenceIndex += 1;\n }\n else if (c === CharCodes.Gt) {\n this.cbs.oncomment(this.sectionStart, this.index, 0);\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n else {\n this.state = State.InSpecialComment;\n }\n }\n stateInDeclaration(c) {\n if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {\n this.cbs.ondeclaration(this.sectionStart, this.index);\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n }\n /**\n * XML processing instructions (`<?...?>`).\n *\n * In HTML mode `<?` is routed to `InSpecialComment` instead, so this\n * state is only reachable in XML mode.\n * @param c Current character code point.\n */\n stateInProcessingInstruction(c) {\n if (c === CharCodes.Questionmark) {\n // Remember that we just consumed `?`, so the next `>` closes the PI.\n this.sequenceIndex = 1;\n }\n else if (c === CharCodes.Gt && this.sequenceIndex === 1) {\n this.cbs.onprocessinginstruction(this.sectionStart, this.index - 1);\n this.sequenceIndex = 0;\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n else {\n // Keep scanning for the next `?`, which can start a closing `?>`.\n this.sequenceIndex = Number(this.fastForwardTo(CharCodes.Questionmark));\n }\n }\n stateBeforeComment(c) {\n if (c === CharCodes.Dash) {\n this.state = State.InCommentLike;\n this.currentSequence = Sequences.CommentEnd;\n this.sequenceIndex = 0;\n this.sectionStart = this.index + 1;\n }\n else if (this.xmlMode) {\n this.state = State.InDeclaration;\n }\n else if (c === CharCodes.Gt) {\n this.cbs.oncomment(this.sectionStart, this.index, 0);\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n else {\n this.state = State.InSpecialComment;\n }\n }\n stateInSpecialComment(c) {\n if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {\n this.cbs.oncomment(this.sectionStart, this.index, 0);\n this.state = State.Text;\n this.sectionStart = this.index + 1;\n }\n }\n startEntity() {\n this.baseState = this.state;\n this.state = State.InEntity;\n this.entityStart = this.index;\n this.entityDecoder.startEntity(this.xmlMode\n ? DecodingMode.Strict\n : this.baseState === State.Text ||\n this.baseState === State.InSpecialTag\n ? DecodingMode.Legacy\n : DecodingMode.Attribute);\n }\n stateInEntity() {\n const indexInBuffer = this.index - this.offset;\n const length = this.entityDecoder.write(this.buffer, indexInBuffer);\n // If `length` is positive, we are done with the entity.\n if (length >= 0) {\n this.state = this.baseState;\n if (length === 0) {\n this.index -= 1;\n }\n }\n else {\n if (indexInBuffer < this.buffer.length &&\n this.buffer.charCodeAt(indexInBuffer) === CharCodes.Amp) {\n this.state = this.baseState;\n this.index -= 1;\n return;\n }\n // Mark buffer as consumed.\n this.index = this.offset + this.buffer.length - 1;\n }\n }\n /**\n * Remove data that has already been consumed from the buffer.\n */\n cleanup() {\n // If we are inside of text or attributes, emit what we already have.\n if (this.running && this.sectionStart !== this.index) {\n if (this.state === State.Text ||\n this.state === State.InPlainText ||\n (this.state === State.InSpecialTag && this.sequenceIndex === 0)) {\n this.cbs.ontext(this.sectionStart, this.index);\n this.sectionStart = this.index;\n }\n else if (this.state === State.InAttributeValueDq ||\n this.state === State.InAttributeValueSq ||\n this.state === State.InAttributeValueNq) {\n this.cbs.onattribdata(this.sectionStart, this.index);\n this.sectionStart = this.index;\n }\n }\n }\n shouldContinue() {\n return this.index < this.buffer.length + this.offset && this.running;\n }\n /**\n * Iterates through the buffer, calling the function corresponding to the current state.\n *\n * States that are more likely to be hit are higher up, as a performance improvement.\n */\n parse() {\n while (this.shouldContinue()) {\n const c = this.buffer.charCodeAt(this.index - this.offset);\n switch (this.state) {\n case State.Text: {\n this.stateText(c);\n break;\n }\n case State.InPlainText: {\n // Skip to end of buffer; cleanup() emits the text.\n this.index = this.buffer.length + this.offset - 1;\n break;\n }\n case State.SpecialStartSequence: {\n this.stateSpecialStartSequence(c);\n break;\n }\n case State.InSpecialTag: {\n this.stateInSpecialTag(c);\n break;\n }\n case State.CDATASequence: {\n this.stateCDATASequence(c);\n break;\n }\n case State.DeclarationSequence: {\n this.stateDeclarationSequence(c);\n break;\n }\n case State.InAttributeValueDq: {\n this.stateInAttributeValueDoubleQuotes(c);\n break;\n }\n case State.InAttributeName: {\n this.stateInAttributeName(c);\n break;\n }\n case State.InCommentLike: {\n this.stateInCommentLike(c);\n break;\n }\n case State.InSpecialComment: {\n this.stateInSpecialComment(c);\n break;\n }\n case State.BeforeAttributeName: {\n this.stateBeforeAttributeName(c);\n break;\n }\n case State.InTagName: {\n this.stateInTagName(c);\n break;\n }\n case State.InClosingTagName: {\n this.stateInClosingTagName(c);\n break;\n }\n case State.BeforeTagName: {\n this.stateBeforeTagName(c);\n break;\n }\n case State.AfterAttributeName: {\n this.stateAfterAttributeName(c);\n break;\n }\n case State.InAttributeValueSq: {\n this.stateInAttributeValueSingleQuotes(c);\n break;\n }\n case State.BeforeAttributeValue: {\n this.stateBeforeAttributeValue(c);\n break;\n }\n case State.BeforeClosingTagName: {\n this.stateBeforeClosingTagName(c);\n break;\n }\n case State.AfterClosingTagName: {\n this.stateAfterClosingTagName(c);\n break;\n }\n case State.InAttributeValueNq: {\n this.stateInAttributeValueNoQuotes(c);\n break;\n }\n case State.InSelfClosingTag: {\n this.stateInSelfClosingTag(c);\n break;\n }\n case State.InDeclaration: {\n this.stateInDeclaration(c);\n break;\n }\n case State.BeforeDeclaration: {\n this.stateBeforeDeclaration(c);\n break;\n }\n case State.BeforeComment: {\n this.stateBeforeComment(c);\n break;\n }\n case State.InProcessingInstruction: {\n this.stateInProcessingInstruction(c);\n break;\n }\n case State.InEntity: {\n this.stateInEntity();\n break;\n }\n }\n this.index++;\n }\n this.cleanup();\n }\n finish() {\n if (this.state === State.InEntity) {\n this.entityDecoder.end();\n this.state = this.baseState;\n }\n this.handleTrailingData();\n this.cbs.onend();\n }\n handleTrailingCommentLikeData(endIndex) {\n if (this.state !== State.InCommentLike) {\n return false;\n }\n if (this.currentSequence === Sequences.CdataEnd) {\n if (this.xmlMode) {\n if (this.sectionStart < endIndex) {\n this.cbs.oncdata(this.sectionStart, endIndex, 0);\n }\n }\n else {\n /* In HTML mode, unclosed CDATA is a bogus comment. */\n const cdataStart = this.sectionStart - Sequences.Cdata.length - 1;\n this.cbs.oncomment(cdataStart, endIndex, 0);\n }\n }\n else {\n const offset = this.xmlMode\n ? 0\n : Math.min(this.sequenceIndex, Sequences.CommentEnd.length - 1);\n this.cbs.oncomment(this.sectionStart, endIndex, offset);\n }\n return true;\n }\n handleTrailingMarkupDeclaration(endIndex) {\n if (this.xmlMode) {\n switch (this.state) {\n case State.InSpecialComment:\n case State.BeforeComment:\n case State.CDATASequence:\n case State.DeclarationSequence:\n case State.InDeclaration: {\n this.cbs.ontext(this.sectionStart, endIndex);\n return true;\n }\n default: {\n return false;\n }\n }\n }\n switch (this.state) {\n case State.BeforeDeclaration:\n case State.InSpecialComment:\n case State.BeforeComment:\n case State.CDATASequence: {\n this.cbs.oncomment(this.sectionStart, endIndex, 0);\n return true;\n }\n case State.DeclarationSequence: {\n if (this.sequenceIndex !== Sequences.Doctype.length) {\n this.cbs.oncomment(this.sectionStart, endIndex, 0);\n }\n return true;\n }\n case State.InDeclaration: {\n return true;\n }\n default: {\n return false;\n }\n }\n }\n /** Handle any trailing data. */\n handleTrailingData() {\n const endIndex = this.buffer.length + this.offset;\n if (this.handleTrailingCommentLikeData(endIndex) ||\n this.handleTrailingMarkupDeclaration(endIndex)) {\n return;\n }\n // If there is no remaining data, we are done.\n if (this.sectionStart >= endIndex) {\n return;\n }\n switch (this.state) {\n case State.InTagName:\n case State.BeforeAttributeName:\n case State.BeforeAttributeValue:\n case State.AfterAttributeName:\n case State.InAttributeName:\n case State.InAttributeValueSq:\n case State.InAttributeValueDq:\n case State.InAttributeValueNq:\n case State.InClosingTagName: {\n /*\n * If we are currently in an opening or closing tag, us not calling the\n * respective callback signals that the tag should be ignored.\n */\n break;\n }\n default: {\n this.cbs.ontext(this.sectionStart, endIndex);\n }\n }\n }\n emitCodePoint(cp, consumed) {\n if (this.baseState !== State.Text &&\n this.baseState !== State.InSpecialTag) {\n if (this.sectionStart < this.entityStart) {\n this.cbs.onattribdata(this.sectionStart, this.entityStart);\n }\n this.sectionStart = this.entityStart + consumed;\n this.index = this.sectionStart - 1;\n this.cbs.onattribentity(cp);\n }\n else {\n if (this.sectionStart < this.entityStart) {\n this.cbs.ontext(this.sectionStart, this.entityStart);\n }\n this.sectionStart = this.entityStart + consumed;\n this.index = this.sectionStart - 1;\n this.cbs.ontextentity(cp, this.sectionStart);\n }\n }\n}\n//# sourceMappingURL=Tokenizer.js.map"],"names":[],"mappings":"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