let { i: start } = this; const { chunk, textHandler: handler } = this; let nonSpace = false; // eslint-disable-next-line no-labels, no-restricted-syntax outRootLoop: // eslint-disable-next-line no-constant-condition while (true) { const code = this.getCode(); switch (code) { case LESS: { this.state = S_OPEN_WAKA; if (handler !== undefined) { const { text } = this; const slice = chunk.slice(start, this.prevI); if (text.length !== 0) { handler(text + slice); this.text = ""; } else if (slice.length !== 0) { handler(slice); } } // eslint-disable-next-line no-labels break outRootLoop; } case AMP: this.state = S_ENTITY; this.entityReturnState = S_TEXT; if (handler !== undefined) { this.text += chunk.slice(start, this.prevI); } nonSpace = true; // eslint-disable-next-line no-labels break outRootLoop; case NL_LIKE: if (handler !== undefined) { this.text += `${chunk.slice(start, this.prevI)}\n`; } start = this.i; break; case EOC: if (handler !== undefined) { this.text += chunk.slice(start); } // eslint-disable-next-line no-labels break outRootLoop; default: if (!isS(code)) { nonSpace = true; } } } if (!nonSpace) { return; } // We use the reportedTextBeforeRoot and reportedTextAfterRoot flags // to avoid reporting errors for every single character that is out of // place. if (!this.sawRoot && !this.reportedTextBeforeRoot) { this.fail("text data outside of root node."); this.reportedTextBeforeRoot = true; } if (this.closedRoot && !this.reportedTextAfterRoot) { this.fail("text data outside of root node."); this.reportedTextAfterRoot = true; } } pushAttribNS(name, value) { var _a; const { prefix, local } = this.qname(name); const attr = { name, prefix, local, value }; this.attribList.push(attr); (_a = this.attributeHandler) === null || _a === void 0 ? void 0 : _a.call(this, attr); if (prefix === "xmlns") { const trimmed = value.trim(); if (this.currentXMLVersion === "1.0" && trimmed === "") { this.fail("invalid attempt to undefine prefix in XML 1.0"); } this.topNS[local] = trimmed; nsPairCheck(this, local, trimmed); } else if (name === "xmlns") { const trimmed = value.trim(); this.topNS[""] = trimmed; nsPairCheck(this, "", trimmed); } } pushAttribPlain(name, value) { var _a; const attr = { name, value }; this.attribList.push(attr); (_a = this.attributeHandler) === null || _a === void 0 ? void 0 : _a.call(this, attr); } /** * End parsing. This performs final well-formedness checks and resets the * parser to a clean state. * * @returns this */ end() { var _a, _b; if (!this.sawRoot) { this.fail("document must contain a root element."); } const { tags } = this; while (tags.length > 0) { const tag = tags.pop(); this.fail(`unclosed tag: ${tag.name}`); } if ((this.state !== S_BEGIN) && (this.state !== S_TEXT)) { this.fail("unexpected end."); } const { text } = this; if (text.length !== 0) { (_a = this.textHandler) === null || _a === void 0 ? void 0 : _a.call(this, text); this.text = ""; } this._closed = true; (_b = this.endHandler) === null || _b === void 0 ? void 0 : _b.call(this); this._init(); return this; } /** * Resolve a namespace prefix. * * @param prefix The prefix to resolve. * * @returns The namespace URI or ``undefined`` if the prefix is not defined. */ resolve(prefix) { var _a, _b; let uri = this.topNS[prefix]; if (uri !== undefined) { return uri; } const { tags } = this; for (let index = tags.length - 1; index >= 0; index--) { uri = tags[index].ns[prefix]; if (uri !== undefined) { return uri; } } uri = this.ns[prefix]; if (uri !== undefined) { return uri; } return (_b = (_a = this.opt).resolvePrefix) === null || _b === void 0 ? void 0 : _b.call(_a, prefix); } /** * Parse a qname into its prefix and local name parts. * * @param name The name to parse * * @returns */ qname(name) { // This is faster than using name.split(":"). const colon = name.indexOf(":"); if (colon === -1) { return { prefix: "", local: name }; } const local = name.slice(colon + 1); const prefix = name.slice(0, colon); if (prefix === "" || local === "" || local.includes(":")) { this.fail(`malformed name: ${name}.`); } return { prefix, local }; } processAttribsNS() { var _a; const { attribList } = this; const tag = this.tag; { // add namespace info to tag const { prefix, local } = this.qname(tag.name); tag.prefix = prefix; tag.local = local; const uri = tag.uri = (_a = this.resolve(prefix)) !== null && _a !== void 0 ? _a : ""; if (prefix !== "") { if (prefix === "xmlns") { this.fail("tags may not have \"xmlns\" as prefix."); } if (uri === "") { this.fail(`unbound namespace prefix: ${JSON.stringify(prefix)}.`); tag.uri = prefix; } } } if (attribList.length === 0) { return; } const { attributes } = tag; const seen = new Set(); // Note: do not apply default ns to attributes: // http://www.w3.org/TR/REC-xml-names/#defaulting for (const attr of attribList) { const { name, prefix, local } = attr; let uri; let eqname; if (prefix === "") { uri = name === "xmlns" ? XMLNS_NAMESPACE : ""; eqname = name; } else { uri = this.resolve(prefix); // if there's any attributes with an undefined namespace, // then fail on them now. if (uri === undefined) { this.fail(`unbound namespace prefix: ${JSON.stringify(prefix)}.`); uri = prefix; } eqname = `{${uri}}${local}`; } if (seen.has(eqname)) { this.fail(`duplicate attribute: ${eqname}.`); } seen.add(eqname); attr.uri = uri; attributes[name] = attr; } this.attribList = []; } processAttribsPlain() { const { attribList } = this; // eslint-disable-next-line prefer-destructuring const attributes = this.tag.attributes; for (const { name, value } of attribList) { if (attributes[name] !== undefined) { this.fail(`duplicate attribute: ${name}.`); } attributes[name] = value; } this.attribList = []; } /** * Handle a complete open tag. This parser code calls this once it has seen * the whole tag. This method checks for well-formeness and then emits * ``onopentag``. */ openTag() { var _a; this.processAttribs(); const { tags } = this; const tag = this.tag; tag.isSelfClosing = false; // There cannot be any pending text here due to the onopentagstart that was // necessarily emitted before we get here. So we do not check text. (_a = this.openTagHandler) === null || _a === void 0 ? void 0 : _a.call(this, tag); tags.push(tag); this.state = S_TEXT; this.name = ""; } /** * Handle a complete self-closing tag. This parser code calls this once it has * seen the whole tag. This method checks for well-formeness and then emits * ``onopentag`` and ``onclosetag``. */ openSelfClosingTag() { var _a, _b, _c; this.processAttribs(); const { tags } = this; const tag = this.tag; tag.isSelfClosing = true; // There cannot be any pending text here due to the onopentagstart that was // necessarily emitted before we get here. So we do not check text. (_a = this.openTagHandler) === null || _a === void 0 ? void 0 : _a.call(this, tag); (_b = this.closeTagHandler) === null || _b === void 0 ? void 0 : _b.call(this, tag); const top = this.tag = (_c = tags[tags.length - 1]) !== null && _c !== void 0 ? _c : null; if (top === null) { this.closedRoot = true; } this.state = S_TEXT; this.name = ""; } /** * Handle a complete close tag. This parser code calls this once it has seen * the whole tag. This method checks for well-formeness and then emits * ``onclosetag``. */ closeTag() { const { tags, name } = this; // Our state after this will be S_TEXT, no matter what, and we can clear // tagName now. this.state = S_TEXT; this.name = ""; if (name === "") { this.fail("weird empty close tag."); this.text += ""; return; } const handler = this.closeTagHandler; let l = tags.length; while (l-- > 0) { const tag = this.tag = tags.pop(); this.topNS = tag.ns; handler === null || handler === void 0 ? void 0 : handler(tag); if (tag.name === name) { break; } this.fail("unexpected close tag."); } if (l === 0) { this.closedRoot = true; } else if (l < 0) { this.fail(`unmatched closing tag: ${name}.`); this.text += ``; } } /** * Resolves an entity. Makes any necessary well-formedness checks. * * @param entity The entity to resolve. * * @returns The parsed entity. */ parseEntity(entity) { // startsWith would be significantly slower for this test. if (entity[0] !== "#") { const defined = this.ENTITIES[entity]; if (defined !== undefined) { return defined; } this.fail(this.isName(entity) ? "undefined entity." : "disallowed character in entity name."); return `&${entity};`; } let num = NaN; if (entity[1] === "x" && /^#x[0-9a-f]+$/i.test(entity)) { num = parseInt(entity.slice(2), 16); } else if (/^#[0-9]+$/.test(entity)) { num = parseInt(entity.slice(1), 10); } // The character reference is required to match the CHAR production. if (!this.isChar(num)) { this.fail("malformed character entity."); return `&${entity};`; } return String.fromCodePoint(num); } } exports.SaxesParser = SaxesParser; //# sourceMappingURL=saxes.js.map