text: "use strict";
  var __importDefault = (this && this.__importDefault) || function (mod) {
      return (mod && mod.__esModule) ? mod : { "default": mod };
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.Parser = void 0;
  var Tokenizer_1 = __importDefault(require("./Tokenizer"));
  var formTags = new Set([
      "input",
      "option",
      "optgroup",
      "select",
      "button",
      "datalist",
      "textarea",
  ]);
  var pTag = new Set(["p"]);
  var tableSectionTags = new Set(["thead", "tbody"]);
  var ddtTags = new Set(["dd", "dt"]);
  var rtpTags = new Set(["rt", "rp"]);
  var openImpliesClose = new Map([
      ["tr", new Set(["tr", "th", "td"])],
      ["th", new Set(["th"])],
      ["td", new Set(["thead", "th", "td"])],
      ["body", new Set(["head", "link", "script"])],
      ["li", 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", new Set(["option"])],
      ["optgroup", 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],
  ]);
  var voidElements = new Set([
      "area",
      "base",
      "basefont",
      "br",
      "col",
      "command",
      "embed",
      "frame",
      "hr",
      "img",
      "input",
      "isindex",
      "keygen",
      "link",
      "meta",
      "param",
      "source",
      "track",
      "wbr",
  ]);
  var foreignContextElements = new Set(["math", "svg"]);
  var htmlIntegrationElements = new Set([
      "mi",
      "mo",
      "mn",
      "ms",
      "mtext",
      "annotation-xml",
      "foreignobject",
      "desc",
      "title",
  ]);
  var reNameEnd = /\s|\//;
  var Parser = /** @class */ (function () {
      function Parser(cbs, options) {
          if (options === void 0) { options = {}; }
          var _a, _b, _c, _d, _e;
          this.options = options;
          /** The start index of the last event. */
          this.startIndex = 0;
          /** The end index of the last event. */
          this.endIndex = 0;
          /**
           * Store the start index of the current open tag,
           * so we can update the start index for attributes.
           */
          this.openTagStart = 0;
          this.tagname = "";
          this.attribname = "";
          this.attribvalue = "";
          this.attribs = null;
          this.stack = [];
          this.foreignContext = [];
          this.cbs = cbs !== null && cbs !== void 0 ? cbs : {};
          this.lowerCaseTagNames = (_a = options.lowerCaseTags) !== null && _a !== void 0 ? _a : !options.xmlMode;
          this.lowerCaseAttributeNames =
              (_b = options.lowerCaseAttributeNames) !== null && _b !== void 0 ? _b : !options.xmlMode;
          this.tokenizer = new ((_c = options.Tokenizer) !== null && _c !== void 0 ? _c : Tokenizer_1.default)(this.options, this);
          (_e = (_d = this.cbs).onparserinit) === null || _e === void 0 ? void 0 : _e.call(_d, this);
      }
      // Tokenizer event handlers
      /** @internal */
      Parser.prototype.ontext = function (data) {
          var _a, _b;
          var idx = this.tokenizer.getAbsoluteIndex();
          this.endIndex = idx - 1;
          (_b = (_a = this.cbs).ontext) === null || _b === void 0 ? void 0 : _b.call(_a, data);
          this.startIndex = idx;
      };
      Parser.prototype.isVoidElement = function (name) {
          return !this.options.xmlMode && voidElements.has(name);
      };
      /** @internal */
      Parser.prototype.onopentagname = function (name) {
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          if (this.lowerCaseTagNames) {
              name = name.toLowerCase();
          }
          this.emitOpenTag(name);
      };
      Parser.prototype.emitOpenTag = function (name) {
          var _a, _b, _c, _d;
          this.openTagStart = this.startIndex;
          this.tagname = name;
          var impliesClose = !this.options.xmlMode && openImpliesClose.get(name);
          if (impliesClose) {
              while (this.stack.length > 0 &&
                  impliesClose.has(this.stack[this.stack.length - 1])) {
                  var el = this.stack.pop();
                  (_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, el, true);
              }
          }
          if (!this.isVoidElement(name)) {
              this.stack.push(name);
              if (foreignContextElements.has(name)) {
                  this.foreignContext.push(true);
              }
              else if (htmlIntegrationElements.has(name)) {
                  this.foreignContext.push(false);
              }
          }
          (_d = (_c = this.cbs).onopentagname) === null || _d === void 0 ? void 0 : _d.call(_c, name);
          if (this.cbs.onopentag)
              this.attribs = {};
      };
      Parser.prototype.endOpenTag = function (isImplied) {
          var _a, _b;
          this.startIndex = this.openTagStart;
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          if (this.attribs) {
              (_b = (_a = this.cbs).onopentag) === null || _b === void 0 ? void 0 : _b.call(_a, 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 */
      Parser.prototype.onopentagend = function () {
          this.endOpenTag(false);
          // Set `startIndex` for next node
          this.startIndex = this.endIndex + 1;
      };
      /** @internal */
      Parser.prototype.onclosetag = function (name) {
          var _a, _b, _c, _d, _e, _f;
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          if (this.lowerCaseTagNames) {
              name = name.toLowerCase();
          }
          if (foreignContextElements.has(name) ||
              htmlIntegrationElements.has(name)) {
              this.foreignContext.pop();
          }
          if (!this.isVoidElement(name)) {
              var pos = this.stack.lastIndexOf(name);
              if (pos !== -1) {
                  if (this.cbs.onclosetag) {
                      var count = this.stack.length - pos;
                      while (count--) {
                          // We know the stack has sufficient elements.
                          this.cbs.onclosetag(this.stack.pop(), count !== 0);
                      }
                  }
                  else
                      this.stack.length = pos;
              }
              else if (!this.options.xmlMode && name === "p") {
                  this.emitOpenTag(name);
                  this.closeCurrentTag(true);
              }
          }
          else if (!this.options.xmlMode && name === "br") {
              // We can't go through `emitOpenTag` here, as `br` would be implicitly closed.
              (_b = (_a = this.cbs).onopentagname) === null || _b === void 0 ? void 0 : _b.call(_a, name);
              (_d = (_c = this.cbs).onopentag) === null || _d === void 0 ? void 0 : _d.call(_c, name, {}, true);
              (_f = (_e = this.cbs).onclosetag) === null || _f === void 0 ? void 0 : _f.call(_e, name, false);
          }
          // Set `startIndex` for next node
          this.startIndex = this.endIndex + 1;
      };
      /** @internal */
      Parser.prototype.onselfclosingtag = function () {
          if (this.options.xmlMode ||
              this.options.recognizeSelfClosing ||
              this.foreignContext[this.foreignContext.length - 1]) {
              this.closeCurrentTag(false);
          }
          else {
              // Ignore the fact that the tag is self-closing.
              this.onopentagend();
          }
      };
      Parser.prototype.closeCurrentTag = function (isOpenImplied) {
          var _a, _b;
          var name = this.tagname;
          this.endOpenTag(isOpenImplied);
          // Self-closing tags will be on the top of the stack
          if (this.stack[this.stack.length - 1] === name) {
              // If the opening tag isn't implied, the closing tag has to be implied.
              (_b = (_a = this.cbs).onclosetag) === null || _b === void 0 ? void 0 : _b.call(_a, name, !isOpenImplied);
              this.stack.pop();
          }
      };
      /** @internal */
      Parser.prototype.onattribname = function (name) {
          this.startIndex = this.tokenizer.getAbsoluteSectionStart();
          if (this.lowerCaseAttributeNames) {
              name = name.toLowerCase();
          }
          this.attribname = name;
      };
      /** @internal */
      Parser.prototype.onattribdata = function (value) {
          this.attribvalue += value;
      };
      /** @internal */
      Parser.prototype.onattribend = function (quote) {
          var _a, _b;
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          (_b = (_a = this.cbs).onattribute) === null || _b === void 0 ? void 0 : _b.call(_a, this.attribname, this.attribvalue, quote);
          if (this.attribs &&
              !Object.prototype.hasOwnProperty.call(this.attribs, this.attribname)) {
              this.attribs[this.attribname] = this.attribvalue;
          }
          this.attribname = "";
          this.attribvalue = "";
      };
      Parser.prototype.getInstructionName = function (value) {
          var idx = value.search(reNameEnd);
          var name = idx < 0 ? value : value.substr(0, idx);
          if (this.lowerCaseTagNames) {
              name = name.toLowerCase();
          }
          return name;
      };
      /** @internal */
      Parser.prototype.ondeclaration = function (value) {
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          if (this.cbs.onprocessinginstruction) {
              var name_1 = this.getInstructionName(value);
              this.cbs.onprocessinginstruction("!" + name_1, "!" + value);
          }
          // Set `startIndex` for next node
          this.startIndex = this.endIndex + 1;
      };
      /** @internal */
      Parser.prototype.onprocessinginstruction = function (value) {
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          if (this.cbs.onprocessinginstruction) {
              var name_2 = this.getInstructionName(value);
              this.cbs.onprocessinginstruction("?" + name_2, "?" + value);
          }
          // Set `startIndex` for next node
          this.startIndex = this.endIndex + 1;
      };
      /** @internal */
      Parser.prototype.oncomment = function (value) {
          var _a, _b, _c, _d;
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          (_b = (_a = this.cbs).oncomment) === null || _b === void 0 ? void 0 : _b.call(_a, value);
          (_d = (_c = this.cbs).oncommentend) === null || _d === void 0 ? void 0 : _d.call(_c);
          // Set `startIndex` for next node
          this.startIndex = this.endIndex + 1;
      };
      /** @internal */
      Parser.prototype.oncdata = function (value) {
          var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
          this.endIndex = this.tokenizer.getAbsoluteIndex();
          if (this.options.xmlMode || this.options.recognizeCDATA) {
              (_b = (_a = this.cbs).oncdatastart) === null || _b === void 0 ? void 0 : _b.call(_a);
              (_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);
          }
          // Set `startIndex` for next node
          this.startIndex = this.endIndex + 1;
      };
      /** @internal */
      Parser.prototype.onerror = function (err) {
          var _a, _b;
          (_b = (_a = this.cbs).onerror) === null || _b === void 0 ? void 0 : _b.call(_a, err);
      };
      /** @internal */
      Parser.prototype.onend = function () {
          var _a, _b;
          if (this.cbs.onclosetag) {
              // Set the end index for all remaining tags
              this.endIndex = this.startIndex;
              for (var i = this.stack.length; i > 0; this.cbs.onclosetag(this.stack[--i], true))
                  ;
          }
          (_b = (_a = this.cbs).onend) === null || _b === void 0 ? void 0 : _b.call(_a);
      };
      /**
       * Resets the parser to a blank state, ready to parse a new HTML document
       */
      Parser.prototype.reset = function () {
          var _a, _b, _c, _d;
          (_b = (_a = this.cbs).onreset) === null || _b === void 0 ? void 0 : _b.call(_a);
          this.tokenizer.reset();
          this.tagname = "";
          this.attribname = "";
          this.attribs = null;
          this.stack = [];
          this.startIndex = 0;
          this.endIndex = 0;
          (_d = (_c = this.cbs).onparserinit) === null || _d === void 0 ? void 0 : _d.call(_c, this);
      };
      /**
       * Resets the parser, then parses a complete document and
       * pushes it to the handler.
       *
       * @param data Document to parse.
       */
      Parser.prototype.parseComplete = function (data) {
          this.reset();
          this.end(data);
      };
      /**
       * Parses a chunk of data and calls the corresponding callbacks.
       *
       * @param chunk Chunk to parse.
       */
      Parser.prototype.write = function (chunk) {
          this.tokenizer.write(chunk);
      };
      /**
       * Parses the end of the buffer and clears the stack, calls onend.
       *
       * @param chunk Optional final chunk to parse.
       */
      Parser.prototype.end = function (chunk) {
          this.tokenizer.end(chunk);
      };
      /**
       * Pauses parsing. The parser won't emit events until `resume` is called.
       */
      Parser.prototype.pause = function () {
          this.tokenizer.pause();
      };
      /**
       * Resumes parsing after `pause` was called.
       */
      Parser.prototype.resume = function () {
          this.tokenizer.resume();
      };
      /**
       * Alias of `write`, for backwards compatibility.
       *
       * @param chunk Chunk to parse.
       * @deprecated
       */
      Parser.prototype.parseChunk = function (chunk) {
          this.write(chunk);
      };
      /**
       * Alias of `end`, for backwards compatibility.
       *
       * @param chunk Optional final chunk to parse.
       * @deprecated
       */
      Parser.prototype.done = function (chunk) {
          this.end(chunk);
      };
      return Parser;
  }());
  exports.Parser = Parser;
  