     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 += `</${name}>`;
        }
    }
    /**
     * 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