tags, also decode entities. */
stateInSpecialTag(c) {
if (this.sequenceIndex === this.currentSequence.length) {
if (c === CharCodes.Gt || isWhitespace(c)) {
const endOfText = this.index - this.currentSequence.length;
if (this.sectionStart < endOfText) {
const actualIndex = this.index;
this.index = endOfText;
this.cbs.ontext(this.sectionStart, endOfText);
this.index = actualIndex;
}
this.isSpecial = false;
this.sectionStart = endOfText + 2;
this.stateInClosingTagName(c);
return;
}
this.sequenceIndex = 0;
}
if ((c | 32) === this.currentSequence[this.sequenceIndex]) {
this.sequenceIndex += 1;
} else if (this.sequenceIndex === 0) {
if (this.currentSequence === Sequences.TitleEnd) {
if (this.decodeEntities && c === CharCodes.Amp) {
this.startEntity();
}
} else if (this.fastForwardTo(CharCodes.Lt)) {
this.sequenceIndex = 1;
}
} else {
this.sequenceIndex = Number(c === CharCodes.Lt);
}
}
stateCDATASequence(c) {
if (c === Sequences.Cdata[this.sequenceIndex]) {
if (++this.sequenceIndex === Sequences.Cdata.length) {
this.state = State.InCommentLike;
this.currentSequence = Sequences.CdataEnd;
this.sequenceIndex = 0;
this.sectionStart = this.index + 1;
}
} else {
this.sequenceIndex = 0;
this.state = State.InDeclaration;
this.stateInDeclaration(c);
}
}
/**
* When we wait for one specific character, we can speed things up
* by skipping through the buffer until we find it.
*
* @returns Whether the character was found.
*/
fastForwardTo(c) {
while (++this.index < this.buffer.length + this.offset) {
if (this.buffer.charCodeAt(this.index - this.offset) === c) {
return true;
}
}
this.index = this.buffer.length + this.offset - 1;
return false;
}
/**
* Comments and CDATA end with `-->` and `]]>`.
*
* Their common qualities are:
* - Their end sequences have a distinct character they start with.
* - That character is then repeated, so we have to check multiple repeats.
* - All characters but the start character of the sequence can be skipped.
*/
stateInCommentLike(c) {
if (c === this.currentSequence[this.sequenceIndex]) {
if (++this.sequenceIndex === this.currentSequence.length) {
if (this.currentSequence === Sequences.CdataEnd) {
this.cbs.oncdata(this.sectionStart, this.index, 2);
} else {
this.cbs.oncomment(this.sectionStart, this.index, 2);
}
this.sequenceIndex = 0;
this.sectionStart = this.index + 1;
this.state = State.Text;
}
} else if (this.sequenceIndex === 0) {
if (this.fastForwardTo(this.currentSequence[0])) {
this.sequenceIndex = 1;
}
} else if (c !== this.currentSequence[this.sequenceIndex - 1]) {
this.sequenceIndex = 0;
}
}
/**
* HTML only allows ASCII alpha characters (a-z and A-Z) at the beginning of a tag name.
*
* XML allows a lot more characters here (@see https://www.w3.org/TR/REC-xml/#NT-NameStartChar).
* We allow anything that wouldn't end the tag.
*/
isTagStartChar(c) {
return this.xmlMode ? !isEndOfTagSection(c) : isASCIIAlpha(c);
}
startSpecial(sequence, offset) {
this.isSpecial = true;
this.currentSequence = sequence;
this.sequenceIndex = offset;
this.state = State.SpecialStartSequence;
}
stateBeforeTagName(c) {
if (c === CharCodes.ExclamationMark) {
this.state = State.BeforeDeclaration;
this.sectionStart = this.index + 1;
} else if (c === CharCodes.Questionmark) {
this.state = State.InProcessingInstruction;
this.sectionStart = this.index + 1;
} else if (this.isTagStartChar(c)) {
const lower = c | 32;
this.sectionStart = this.index;
if (this.xmlMode) {
this.state = State.InTagName;
} else if (lower === Sequences.ScriptEnd[2]) {
this.state = State.BeforeSpecialS;
} else if (lower === Sequences.TitleEnd[2]) {
this.state = State.BeforeSpecialT;
} else {
this.state = State.InTagName;
}
} else if (c === CharCodes.Slash) {
this.state = State.BeforeClosingTagName;
} else {
this.state = State.Text;
this.stateText(c);
}
}
stateInTagName(c) {
if (isEndOfTagSection(c)) {
this.cbs.onopentagname(this.sectionStart, this.index);
this.sectionStart = -1;
this.state = State.BeforeAttributeName;
this.stateBeforeAttributeName(c);
}
}
stateBeforeClosingTagName(c) {
if (isWhitespace(c)) ;
else if (c === CharCodes.Gt) {
this.state = State.Text;
} else {
this.state = this.isTagStartChar(c) ? State.InClosingTagName : State.InSpecialComment;
this.sectionStart = this.index;
}
}
stateInClosingTagName(c) {
if (c === CharCodes.Gt || isWhitespace(c)) {
this.cbs.onclosetag(this.sectionStart, this.index);
this.sectionStart = -1;
this.state = State.AfterClosingTagName;
this.stateAfterClosingTagName(c);
}
}
stateAfterClosingTagName(c) {
if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {
this.state = State.Text;
this.sectionStart = this.index + 1;
}
}
stateBeforeAttributeName(c) {
if (c === CharCodes.Gt) {
this.cbs.onopentagend(this.index);
if (this.isSpecial) {
this.state = State.InSpecialTag;
this.sequenceIndex = 0;
} else {
this.state = State.Text;
}
this.sectionStart = this.index + 1;
} else if (c === CharCodes.Slash) {
this.state = State.InSelfClosingTag;
} else if (!isWhitespace(c)) {
this.state = State.InAttributeName;
this.sectionStart = this.index;
}
}
stateInSelfClosingTag(c) {
if (c === CharCodes.Gt) {
this.cbs.onselfclosingtag(this.index);
this.state = State.Text;
this.sectionStart = this.index + 1;
this.isSpecial = false;
} else if (!isWhitespace(c)) {
this.state = State.BeforeAttributeName;
this.stateBeforeAttributeName(c);
}
}
stateInAttributeName(c) {
if (c === CharCodes.Eq || isEndOfTagSection(c)) {
this.cbs.onattribname(this.sectionStart, this.index);
this.sectionStart = this.index;
this.state = State.AfterAttributeName;
this.stateAfterAttributeName(c);
}
}
stateAfterAttributeName(c) {
if (c === CharCodes.Eq) {
this.state = State.BeforeAttributeValue;
} else if (c === CharCodes.Slash || c === CharCodes.Gt) {
this.cbs.onattribend(QuoteType.NoValue, this.sectionStart);
this.sectionStart = -1;
this.state = State.BeforeAttributeName;
this.stateBeforeAttributeName(c);
} else if (!isWhitespace(c)) {
this.cbs.onattribend(QuoteType.NoValue, this.sectionStart);
this.state = State.InAttributeName;
this.sectionStart = this.index;
}
}
stateBeforeAttributeValue(c) {
if (c === CharCodes.DoubleQuote) {
this.state = State.InAttributeValueDq;
this.sectionStart = this.index + 1;
} else if (c === CharCodes.SingleQuote) {
this.state = State.InAttributeValueSq;
this.sectionStart = this.index + 1;
} else if (!isWhitespace(c)) {
this.sectionStart = this.index;
this.state = State.InAttributeValueNq;
this.stateInAttributeValueNoQuotes(c);
}
}
handleInAttributeValue(c, quote) {
if (c === quote || !this.decodeEntities && this.fastForwardTo(quote)) {
this.cbs.onattribdata(this.sectionStart, this.index);
this.sectionStart = -1;
this.cbs.onattribend(quote === CharCodes.DoubleQuote ? QuoteType.Double : QuoteType.Single, this.index + 1);
this.state = State.BeforeAttributeName;
} else if (this.decodeEntities && c === CharCodes.Amp) {
this.startEntity();
}
}
stateInAttributeValueDoubleQuotes(c) {
this.handleInAttributeValue(c, CharCodes.DoubleQuote);
}
stateInAttributeValueSingleQuotes(c) {
this.handleInAttributeValue(c, CharCodes.SingleQuote);
}
stateInAttributeValueNoQuotes(c) {
if (isWhitespace(c) || c === CharCodes.Gt) {
this.cbs.onattribdata(this.sectionStart, this.index);
this.sectionStart = -1;
this.cbs.onattribend(QuoteType.Unquoted, this.index);
this.state = State.BeforeAttributeName;
this.stateBeforeAttributeName(c);
} else if (this.decodeEntities && c === CharCodes.Amp) {
this.startEntity();
}
}
stateBeforeDeclaration(c) {
if (c === CharCodes.OpeningSquareBracket) {
this.state = State.CDATASequence;
this.sequenceIndex = 0;
} else {
this.state = c === CharCodes.Dash ? State.BeforeComment : State.InDeclaration;
}
}
stateInDeclaration(c) {
if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {
this.cbs.ondeclaration(this.sectionStart, this.index);
this.state = State.Text;
this.sectionStart = this.index + 1;
}
}
stateInProcessingInstruction(c) {
if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {
this.cbs.onprocessinginstruction(this.sectionStart, this.index);
this.state = State.Text;
this.sectionStart = this.index + 1;
}
}
stateBeforeComment(c) {
if (c === CharCodes.Dash) {
this.state = State.InCommentLike;
this.currentSequence = Sequences.CommentEnd;
this.sequenceIndex = 2;
this.sectionStart = this.index + 1;
} else {
this.state = State.InDeclaration;
}
}
stateInSpecialComment(c) {
if (c === CharCodes.Gt || this.fastForwardTo(CharCodes.Gt)) {
this.cbs.oncomment(this.sectionStart, this.index, 0);
this.state = State.Text;
this.sectionStart = this.index + 1;
}
}
stateBeforeSpecialS(c) {
const lower = c | 32;
if (lower === Sequences.ScriptEnd[3]) {
this.startSpecial(Sequences.ScriptEnd, 4);
} else if (lower === Sequences.StyleEnd[3]) {
this.startSpecial(Sequences.StyleEnd, 4);
} else {
this.state = State.InTagName;
this.stateInTagName(c);
}
}
stateBeforeSpecialT(c) {
const lower = c | 32;
if (lower === Sequences.TitleEnd[3]) {
this.startSpecial(Sequences.TitleEnd, 4);
} else if (lower === Sequences.TextareaEnd[3]) {
this.startSpecial(Sequences.TextareaEnd, 4);
} else {
this.state = State.InTagName;
this.stateInTagName(c);
}
}
startEntity() {
this.baseState = this.state;
this.state = State.InEntity;
this.entityStart = this.index;
this.entityDecoder.startEntity(this.xmlMode ? DecodingMode.Strict : this.baseState === State.Text || this.baseState === State.InSpecialTag ? DecodingMode.Legacy : DecodingMode.Attribute);
}
stateInEntity() {
const length = this.entityDecoder.write(this.buffer, this.index - this.offset);
if (length >= 0) {
this.state = this.baseState;
if (length === 0) {
this.index = this.entityStart;
}
} else {
this.index = this.offset + this.buffer.length - 1;
}
}
/**
* Remove data that has already been consumed from the buffer.
*/
cleanup() {
if (this.running && this.sectionStart !== this.index) {
if (this.state === State.Text || this.state === State.InSpecialTag && this.sequenceIndex === 0) {
this.cbs.ontext(this.sectionStart, this.index);
this.sectionStart = this.index;
} else if (this.state === State.InAttributeValueDq || this.state === State.InAttributeValueSq || this.state === State.InAttributeValueNq) {
this.cbs.onattribdata(this.sectionStart, this.index);
this.sectionStart = this.index;
}
}
}
shouldContinue() {
return this.index < this.buffer.length + this.offset && this.running;
}
/**
* Iterates through the buffer, calling the function corresponding to the current state.
*
* States that are more likely to be hit are higher up, as a performance improvement.
*/
parse() {
while (this.shouldContinue()) {
const c = this.buffer.charCodeAt(this.index - this.offset);
switch (this.state) {
case State.Text: {
this.stateText(c);
break;
}
case State.SpecialStartSequence: {
this.stateSpecialStartSequence(c);
break;
}
case State.InSpecialTag: {
this.stateInSpecialTag(c);
break;
}
case State.CDATASequence: {
this.stateCDATASequence(c);
break;
}
case State.InAttributeValueDq: {
this.stateInAttributeValueDoubleQuotes(c);
break;
}
case State.InAttributeName: {
this.stateInAttributeName(c);
break;
}
case State.InCommentLike: {
this.stateInCommentLike(c);
break;
}
case State.InSpecialComment: {
this.stateInSpecialComment(c);
break;
}
case State.BeforeAttributeName: {
this.stateBeforeAttributeName(c);
break;
}
case State.InTagName: {
this.stateInTagName(c);
break;
}
case State.InClosingTagName: {
this.stateInClosingTagName(c);
break;
}
case State.BeforeTagName: {
this.stateBeforeTagName(c);
break;
}
case State.AfterAttributeName: {
this.stateAfterAttributeName(c);
break;
}
case State.InAttributeValueSq: {
this.stateInAttributeValueSingleQuotes(c);
break;
}
case State.BeforeAttributeValue: {
this.stateBeforeAttributeValue(c);
break;
}
case State.BeforeClosingTagName: {
this.stateBeforeClosingTagName(c);
break;
}
case State.AfterClosingTagName: {
this.stateAfterClosingTagName(c);
break;
}
case State.BeforeSpecialS: {
this.stateBeforeSpecialS(c);
break;
}
case State.BeforeSpecialT: {
this.stateBeforeSpecialT(c);
break;
}
case State.InAttributeValueNq: {
this.stateInAttributeValueNoQuotes(c);
break;
}
case State.InSelfClosingTag: {
this.stateInSelfClosingTag(c);
break;
}
case State.InDeclaration: {
this.stateInDeclaration(c);
break;
}
case State.BeforeDeclaration: {
this.stateBeforeDeclaration(c);
break;
}
case State.BeforeComment: {
this.stateBeforeComment(c);
break;
}
case State.InProcessingInstruction: {
this.stateInProcessingInstruction(c);
break;
}
case State.InEntity: {
this.stateInEntity();
break;
}
}
this.index++;
}
this.cleanup();
}
finish() {
if (this.state === State.InEntity) {
this.entityDecoder.end();
this.state = this.baseState;
}
this.handleTrailingData();
this.cbs.onend();
}
/** Handle any trailing data. */
handleTrailingData() {
const endIndex = this.buffer.length + this.offset;
if (this.sectionStart >= endIndex) {
return;
}
if (this.state === State.InCommentLike) {
if (this.currentSequence === Sequences.CdataEnd) {
this.cbs.oncdata(this.sectionStart, endIndex, 0);
} else {
this.cbs.oncomment(this.sectionStart, endIndex, 0);
}
} else if (this.state === State.InTagName || this.state === State.BeforeAttributeName || this.state === State.BeforeAttributeValue || this.state === State.AfterAttributeName || this.state === State.InAttributeName || this.state === State.InAttributeValueSq || this.state === State.InAttributeValueDq || this.state === State.InAttributeValueNq || this.state === State.InClosingTagName) ;
else {
this.cbs.ontext(this.sectionStart, endIndex);
}
}
emitCodePoint(cp, consumed) {
if (this.baseState !== State.Text && this.baseState !== State.InSpecialTag) {
if (this.sectionStart < this.entityStart) {
this.cbs.onattribdata(this.sectionStart, this.entityStart);
}
this.sectionStart = this.entityStart + consumed;
this.index = this.sectionStart - 1;
this.cbs.onattribentity(cp);
} else {
if (this.sectionStart < this.entityStart) {
this.cbs.ontext(this.sectionStart, this.entityStart);
}
this.sectionStart = this.entityStart + consumed;
this.index = this.sectionStart - 1;
this.cbs.ontextentity(cp, this.sectionStart);
}
}
}
const formTags = /* @__PURE__ */ new Set([
"input",
"option",
"optgroup",
"select",
"button",
"datalist",
"textarea"
]);
const pTag = /* @__PURE__ */ new Set(["p"]);
const tableSectionTags = /* @__PURE__ */ new Set(["thead", "tbody"]);
const ddtTags = /* @__PURE__ */ new Set(["dd", "dt"]);
const rtpTags = /* @__PURE__ */ new Set(["rt", "rp"]);
const openImpliesClose = /* @__PURE__ */ new Map([
["tr", /* @__PURE__ */ new Set(["tr", "th", "td"])],
["th", /* @__PURE__ */ new Set(["th"])],
["td", /* @__PURE__ */ new Set(["thead", "th", "td"])],
["body", /* @__PURE__ */ new Set(["head", "link", "script"])],
["li", /* @__PURE__ */ new Set(["li"])],
["p", pTag],
["h1", pTag],
["h2", pTag],
["h3", pTag],
["h4", pTag],
["h5", pTag],
["h6", pTag],
["select", formTags],
["input", formTags],
["output", formTags],
["button", formTags],
["datalist", formTags],
["textarea", formTags],
["option", /* @__PURE__ */ new Set(["option"])],
["optgroup", /* @__PURE__ */ new Set(["optgroup", "option"])],
["dd", ddtTags],
["dt", ddtTags],
["address", pTag],
["article", pTag],
["aside", pTag],
["blockquote", pTag],
["details", pTag],
["div", pTag],
["dl", pTag],
["fieldset", pTag],
["figcaption", pTag],
["figure", pTag],
["footer", pTag],
["form", pTag],
["header", pTag],
["hr", pTag],
["main", pTag],
["nav", pTag],
["ol", pTag],
["pre", pTag],
["section", pTag],
["table", pTag],
["ul", pTag],
["rt", rtpTags],
["rp", rtpTags],
["tbody", tableSectionTags],
["tfoot", tableSectionTags]
]);
const voidElements = /* @__PURE__ */ new Set([
"area",
"base",
"basefont",
"br",
"col",
"command",
"embed",
"frame",
"hr",
"img",
"input",
"isindex",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]);
const foreignContextElements = /* @__PURE__ */ new Set(["math", "svg"]);
const htmlIntegrationElements = /* @__PURE__ */ new Set([
"mi",
"mo",
"mn",
"ms",
"mtext",
"annotation-xml",
"foreignobject",
"desc",
"title"
]);
const reNameEnd = /\s|\//;
class Parser {
constructor(cbs, options = {}) {
var _a2, _b, _c, _d, _e, _f;
this.options = options;
this.startIndex = 0;
this.endIndex = 0;
this.openTagStart = 0;
this.tagname = "";
this.attribname = "";
this.attribvalue = "";
this.attribs = null;
this.stack = [];
this.buffers = [];
this.bufferOffset = 0;
this.writeIndex = 0;
this.ended = false;
this.cbs = cbs !== null && cbs !== void 0 ? cbs : {};
this.htmlMode = !this.options.xmlMode;
this.lowerCaseTagNames = (_a2 = options.lowerCaseTags) !== null && _a2 !== void 0 ? _a2 : this.htmlMode;
this.lowerCaseAttributeNames = (_b = options.lowerCaseAttributeNames) !== null && _b !== void 0 ? _b : this.htmlMode;
this.recognizeSelfClosing = (_c = options.recognizeSelfClosing) !== null && _c !== void 0 ? _c : !this.htmlMode;
this.tokenizer = new ((_d = options.Tokenizer) !== null && _d !== void 0 ? _d : Tokenizer)(this.options, this);
this.foreignContext = [!this.htmlMode];
(_f = (_e = this.cbs).onparserinit) === null || _f === void 0 ? void 0 : _f.call(_e, this);
}
// Tokenizer event handlers
/** @internal */
ontext(start, endIndex) {
var _a2, _b;
const data2 = this.getSlice(start, endIndex);
this.endIndex = endIndex - 1;
(_b = (_a2 = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a2, data2);
this.startIndex = endIndex;
}
/** @internal */
ontextentity(cp, endIndex) {
var _a2, _b;
this.endIndex = endIndex - 1;
(_b = (_a2 = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a2, fromCodePoint(cp));
this.startIndex = endIndex;
}
/**
* Checks if the current tag is a void element. Override this if you want
* to specify your own additional void elements.
*/
isVoidElement(name) {
return this.htmlMode && voidElements.has(name);
}
/** @internal */
onopentagname(start, endIndex) {
this.endIndex = endIndex;
let name = this.getSlice(start, endIndex);
if (this.lowerCaseTagNames) {
name = name.toLowerCase();
}
this.emitOpenTag(name);
}
emitOpenTag(name) {
var _a2, _b, _c, _d;
this.openTagStart = this.startIndex;
this.tagname = name;
const impliesClose = this.htmlMode && openImpliesClose.get(name);
if (impliesClose) {
while (this.stack.length > 0 && impliesClose.has(this.stack[0])) {
const element = this.stack.shift();
(_b = (_a2 = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a2, element, true);
}
}
if (!this.isVoidElement(name)) {
this.stack.unshift(name);
if (this.htmlMode) {
if (foreignContextElements.has(name)) {
this.foreignContext.unshift(true);
} else if (htmlIntegrationElements.has(name)) {
this.foreignContext.unshift(false);
}
}
}
(_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, name);
if (this.cbs.onopentag)
this.attribs = {};
}
endOpenTag(isImplied) {
var _a2, _b;
this.startIndex = this.openTagStart;
if (this.attribs) {
(_b = (_a2 = this.cbs).onopentag) === null || _b === void 0 ? void 0 : _b.call(_a2, this.tagname, this.attribs, isImplied);
this.attribs = null;
}
if (this.cbs.onclosetag && this.isVoidElement(this.tagname)) {
this.cbs.onclosetag(this.tagname, true);
}
this.tagname = "";
}
/** @internal */
onopentagend(endIndex) {
this.endIndex = endIndex;
this.endOpenTag(false);
this.startIndex = endIndex + 1;
}
/** @internal */
onclosetag(start, endIndex) {
var _a2, _b, _c, _d, _e, _f, _g, _h;
this.endIndex = endIndex;
let name = this.getSlice(start, endIndex);
if (this.lowerCaseTagNames) {
name = name.toLowerCase();
}
if (this.htmlMode && (foreignContextElements.has(name) || htmlIntegrationElements.has(name))) {
this.foreignContext.shift();
}
if (!this.isVoidElement(name)) {
const pos = this.stack.indexOf(name);
if (pos !== -1) {
for (let index2 = 0; index2 <= pos; index2++) {
const element = this.stack.shift();
(_b = (_a2 = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a2, element, index2 !== pos);
}
} else if (this.htmlMode && name === "p") {
this.emitOpenTag("p");
this.closeCurrentTag(true);
}
} else if (this.htmlMode && name === "br") {
(_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, "br");
(_f = (_e = this.cbs).onopentag) === null || _f === void 0 ? void 0 : _f.call(_e, "br", {}, true);
(_h = (_g = this.cbs).onclosetag) === null || _h === void 0 ? void 0 : _h.call(_g, "br", false);
}
this.startIndex = endIndex + 1;
}
/** @internal */
onselfclosingtag(endIndex) {
this.endIndex = endIndex;
if (this.recognizeSelfClosing || this.foreignContext[0]) {
this.closeCurrentTag(false);
this.startIndex = endIndex + 1;
} else {
this.onopentagend(endIndex);
}
}
closeCurrentTag(isOpenImplied) {
var _a2, _b;
const name = this.tagname;
this.endOpenTag(isOpenImplied);
if (this.stack[0] === name) {
(_b = (_a2 = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a2, name, !isOpenImplied);
this.stack.shift();
}
}
/** @internal */
onattribname(start, endIndex) {
this.startIndex = start;
const name = this.getSlice(start, endIndex);
this.attribname = this.lowerCaseAttributeNames ? name.toLowerCase() : name;
}
/** @internal */
onattribdata(start, endIndex) {
this.attribvalue += this.getSlice(start, endIndex);
}
/** @internal */
onattribentity(cp) {
this.attribvalue += fromCodePoint(cp);
}
/** @internal */
onattribend(quote, endIndex) {
var _a2, _b;
this.endIndex = endIndex;
(_b = (_a2 = this.cbs).onattribute) === null || _b === void 0 ? void 0 : _b.call(_a2, this.attribname, this.attribvalue, quote === QuoteType.Double ? '"' : quote === QuoteType.Single ? "'" : quote === QuoteType.NoValue ? void 0 : null);
if (this.attribs && !Object.prototype.hasOwnProperty.call(this.attribs, this.attribname)) {
this.attribs[this.attribname] = this.attribvalue;
}
this.attribvalue = "";
}
getInstructionName(value) {
const index2 = value.search(reNameEnd);
let name = index2 < 0 ? value : value.substr(0, index2);
if (this.lowerCaseTagNames) {
name = name.toLowerCase();
}
return name;
}
/** @internal */
ondeclaration(start, endIndex) {
this.endIndex = endIndex;
const value = this.getSlice(start, endIndex);
if (this.cbs.onprocessinginstruction) {
const name = this.getInstructionName(value);
this.cbs.onprocessinginstruction(`!${name}`, `!${value}`);
}
this.startIndex = endIndex + 1;
}
/** @internal */
onprocessinginstruction(start, endIndex) {
this.endIndex = endIndex;
const value = this.getSlice(start, endIndex);
if (this.cbs.onprocessinginstruction) {
const name = this.getInstructionName(value);
this.cbs.onprocessinginstruction(`?${name}`, `?${value}`);
}
this.startIndex = endIndex + 1;
}
/** @internal */
oncomment(start, endIndex, offset) {
var _a2, _b, _c, _d;
this.endIndex = endIndex;
(_b = (_a2 = this.cbs).oncomment) === null || _b === void 0 ? void 0 : _b.call(_a2, this.getSlice(start, endIndex - offset));
(_d = (_c = this.cbs).oncommentend) === null || _d === void 0 ? void 0 : _d.call(_c);
this.startIndex = endIndex + 1;
}
/** @internal */
oncdata(start, endIndex, offset) {
var _a2, _b, _c, _d, _e, _f, _g, _h, _j, _k;
this.endIndex = endIndex;
const value = this.getSlice(start, endIndex - offset);
if (!this.htmlMode || this.options.recognizeCDATA) {
(_b = (_a2 = this.cbs).oncdatastart) === null || _b === void 0 ? void 0 : _b.call(_a2);
(_d = (_c = this.cbs).ontext) === null || _d === void 0 ? void 0 : _d.call(_c, value);
(_f = (_e = this.cbs).oncdataend) === null || _f === void 0 ? void 0 : _f.call(_e);
} else {
(_h = (_g = this.cbs).oncomment) === null || _h === void 0 ? void 0 : _h.call(_g, `[CDATA[${value}]]`);
(_k = (_j = this.cbs).oncommentend) === null || _k === void 0 ? void 0 : _k.call(_j);
}
this.startIndex = endIndex + 1;
}
/** @internal */
onend() {
var _a2, _b;
if (this.cbs.onclosetag) {
this.endIndex = this.startIndex;
for (let index2 = 0; index2 < this.stack.length; index2++) {
this.cbs.onclosetag(this.stack[index2], true);
}
}
(_b = (_a2 = this.cbs).onend) === null || _b === void 0 ? void 0 : _b.call(_a2);
}
/**
* Resets the parser to a blank state, ready to parse a new HTML document
*/
reset() {
var _a2, _b, _c, _d;
(_b = (_a2 = this.cbs).onreset) === null || _b === void 0 ? void 0 : _b.call(_a2);
this.tokenizer.reset();
this.tagname = "";
this.attribname = "";
this.attribs = null;
this.stack.length = 0;
this.startIndex = 0;
this.endIndex = 0;
(_d = (_c = this.cbs).onparserinit) === null || _d === void 0 ? void 0 : _d.call(_c, this);
this.buffers.length = 0;
this.foreignContext.length = 0;
this.foreignContext.unshift(!this.htmlMode);
this.bufferOffset = 0;
this.writeIndex = 0;
this.ended = false;
}
/**
* Resets the parser, then parses a complete document and
* pushes it to the handler.
*
* @param data Document to parse.
*/
parseComplete(data2) {
this.reset();
this.end(data2);
}
getSlice(start, end2) {
while (start - this.bufferOffset >= this.buffers[0].length) {
this.shiftBuffer();
}
let slice2 = this.buffers[0].slice(start - this.bufferOffset, end2 - this.bufferOffset);
while (end2 - this.bufferOffset > this.buffers[0].length) {
this.shiftBuffer();
slice2 += this.buffers[0].slice(0, end2 - this.bufferOffset);
}
return slice2;
}
shiftBuffer() {
this.bufferOffset += this.buffers[0].length;
this.writeIndex--;
this.buffers.shift();
}
/**
* Parses a chunk of data and calls the corresponding callbacks.
*
* @param chunk Chunk to parse.
*/
write(chunk) {
var _a2, _b;
if (this.ended) {
(_b = (_a2 = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a2, new Error(".write() after done!"));
return;
}
this.buffers.push(chunk);
if (this.tokenizer.running) {
this.tokenizer.write(chunk);
this.writeIndex++;
}
}
/**
* Parses the end of the buffer and clears the stack, calls onend.
*
* @param chunk Optional final chunk to parse.
*/
end(chunk) {
var _a2, _b;
if (this.ended) {
(_b = (_a2 = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a2, new Error(".end() after done!"));
return;
}
if (chunk)
this.write(chunk);
this.ended = true;
this.tokenizer.end();
}
/**
* Pauses parsing. The parser won't emit events until `resume` is called.
*/
pause() {
this.tokenizer.pause();
}
/**
* Resumes parsing after `pause` was called.
*/
resume() {
this.tokenizer.resume();
while (this.tokenizer.running && this.writeIndex < this.buffers.length) {
this.tokenizer.write(this.buffers[this.writeIndex++]);
}
if (this.ended)
this.tokenizer.end();
}
/**
* Alias of `write`, for backwards compatibility.
*
* @param chunk Chunk to parse.
* @deprecated
*/
parseChunk(chunk) {
this.write(chunk);
}
/**
* Alias of `end`, for backwards compatibility.
*
* @param chunk Optional final chunk to parse.
* @deprecated
*/
done(chunk) {
this.end(chunk);
}
}
function parseDocument(data2, options) {
const handler = new DomHandler(void 0, options);
new Parser(handler, options).end(data2);
return handler.root;
}
const load = getLoad(getParse(parseDocument), render$1);
var Levels = /* @__PURE__ */ ((Levels2) => {
Levels2[Levels2["None"] = 0] = "None";
Levels2[Levels2["H1"] = 1] = "H1";
Levels2[Levels2["H2"] = 2] = "H2";
Levels2[Levels2["H3"] = 3] = "H3";
Levels2[Levels2["H4"] = 4] = "H4";
Levels2[Levels2["H5"] = 5] = "H5";
Levels2[Levels2["H6"] = 6] = "H6";
Levels2[Levels2["Block"] = 7] = "Block";
Levels2[Levels2["List"] = 8] = "List";
Levels2[Levels2["ListItem"] = 9] = "ListItem";
return Levels2;
})(Levels || {});
const defaultSelectorRules = {
"div,p": ({ $node }) => ({
queue: $node.children()
}),
"h1,h2,h3,h4,h5,h6": ({ $node, getContent }) => ({
...getContent($node.contents())
}),
"ul,ol": ({ $node }) => ({
queue: $node.children(),
nesting: true
}),
li: ({ $node, getContent }) => {
const queue = $node.children().filter("ul,ol");
let content;
if ($node.contents().first().is("div,p")) {
content = getContent($node.children().first());
} else {
let $contents = $node.contents();
const i = $contents.index(queue);
if (i >= 0) $contents = $contents.slice(0, i);
content = getContent($contents);
}
return {
queue,
nesting: true,
...content
};
},
"table,pre,p>img:only-child": ({ $node, getContent }) => ({
...getContent($node)
})
};
const defaultOptions = {
selector: "h1,h2,h3,h4,h5,h6,ul,ol,li,table,pre,p>img:only-child",
selectorRules: defaultSelectorRules
};
const MARKMAP_COMMENT_PREFIX = "markmap: ";
const SELECTOR_HEADING = /^h[1-6]$/;
const SELECTOR_LIST = /^[uo]l$/;
const SELECTOR_LIST_ITEM = /^li$/;
function getLevel(tagName) {
if (SELECTOR_HEADING.test(tagName)) return +tagName[1];
if (SELECTOR_LIST.test(tagName)) return 8;
if (SELECTOR_LIST_ITEM.test(tagName)) return 9;
return 7;
}
function parseHtml(html2, opts) {
const options = {
...defaultOptions,
...opts
};
const $ = load(html2);
let $root = $("body");
if (!$root.length) $root = $.root();
let id = 0;
const rootNode = {
id,
tag: "",
html: "",
level: 0,
parent: 0,
childrenLevel: 0,
children: []
};
const headingStack = [];
let skippingHeading = 0;
checkNodes($root.children());
return rootNode;
function addChild(props) {
var _a2;
const { parent: parent2 } = props;
const node = {
id: ++id,
tag: props.tagName,
level: props.level,
html: props.html,
childrenLevel: 0,
children: props.nesting ? [] : void 0,
parent: parent2.id
};
if ((_a2 = props.comments) == null ? void 0 : _a2.length) {
node.comments = props.comments;
}
if (Object.keys(props.data || {}).length) {
node.data = props.data;
}
if (parent2.children) {
if (parent2.childrenLevel === 0 || parent2.childrenLevel > node.level) {
parent2.children = [];
parent2.childrenLevel = node.level;
}
if (parent2.childrenLevel === node.level) {
parent2.children.push(node);
}
}
return node;
}
function getCurrentHeading(level) {
let heading;
while ((heading = headingStack[headingStack.length - 1]) && heading.level >= level) {
headingStack.pop();
}
return heading || rootNode;
}
function getContent($node) {
var _a2;
const result = extractMagicComments($node);
const html22 = (_a2 = $.html(result.$node)) == null ? void 0 : _a2.trimEnd();
return { comments: result.comments, html: html22 };
}
function extractMagicComments($node) {
const comments = [];
$node = $node.filter((_, child) => {
if (child.type === "comment") {
const data2 = child.data.trim();
if (data2.startsWith(MARKMAP_COMMENT_PREFIX)) {
comments.push(data2.slice(MARKMAP_COMMENT_PREFIX.length).trim());
return false;
}
}
return true;
});
return { $node, comments };
}
function checkNodes($els, node) {
$els.each((_, child) => {
var _a2;
const $child = $(child);
const rule = (_a2 = Object.entries(options.selectorRules).find(
([selector]) => $child.is(selector)
)) == null ? void 0 : _a2[1];
const result = rule == null ? void 0 : rule({ $node: $child, $, getContent });
if ((result == null ? void 0 : result.queue) && !result.nesting) {
checkNodes(result.queue, node);
return;
}
const level = getLevel(child.tagName);
if (!result) {
if (level <= 6) {
skippingHeading = level;
}
return;
}
if (skippingHeading > 0 && level > skippingHeading) return;
if (!$child.is(options.selector)) return;
skippingHeading = 0;
const isHeading = level <= 6;
let data2 = {
// If the child is an inline element and expected to be a separate node,
// data from the closest `` should be included, e.g. `
![]()
`
...$child.closest("p").data(),
...$child.data()
};
let html22 = result.html || "";
if ($child.is("ol>li") && (node == null ? void 0 : node.children)) {
const start = +($child.parent().attr("start") || 1);
const listIndex = start + node.children.length;
html22 = `${listIndex}. ${html22}`;
data2 = {
...data2,
listIndex
};
}
const childNode = addChild({
parent: node || getCurrentHeading(level),
nesting: !!result.queue || isHeading,
tagName: child.tagName,
level,
html: html22,
comments: result.comments,
data: data2
});
if (isHeading) headingStack.push(childNode);
if (result.queue) checkNodes(result.queue, childNode);
});
}
}
function convertNode(htmlRoot) {
return walkTree(htmlRoot, (htmlNode, next2) => {
const node = {
content: htmlNode.html,
children: next2() || []
};
if (htmlNode.data) {
node.payload = {
tag: htmlNode.tag,
...htmlNode.data
};
}
if (htmlNode.comments) {
if (htmlNode.comments.includes("foldAll")) {
node.payload = { ...node.payload, fold: 2 };
} else if (htmlNode.comments.includes("fold")) {
node.payload = { ...node.payload, fold: 1 };
}
}
return node;
});
}
function buildTree(html2, opts) {
const htmlRoot = parseHtml(html2, opts);
return convertNode(htmlRoot);
}
export {
Levels,
buildTree,
convertNode,
defaultOptions,
parseHtml
};