text: "use strict";
  const DOMException = require("domexception");
  const HTMLElementImpl = require("./HTMLElement-impl").implementation;
  const { HTML_NS } = require("../helpers/namespaces");
  const { domSymbolTree } = require("../helpers/internal-constants");
  const { firstChildWithHTMLLocalName, childrenByHTMLLocalName } = require("../helpers/traversal");
  const HTMLCollection = require("../generated/HTMLCollection");
  const NODE_TYPE = require("../node-type");
  
  function tHeadInsertionPoint(table) {
    const iterator = domSymbolTree.childrenIterator(table);
    for (const child of iterator) {
      if (child.nodeType !== NODE_TYPE.ELEMENT_NODE) {
        continue;
      }
  
      if (child._namespaceURI !== HTML_NS || (child._localName !== "caption" && child._localName !== "colgroup")) {
        return child;
      }
    }
  
    return null;
  }
  
  class HTMLTableElementImpl extends HTMLElementImpl {
    get caption() {
      return firstChildWithHTMLLocalName(this, "caption");
    }
  
    set caption(value) {
      const currentCaption = this.caption;
      if (currentCaption !== null) {
        this.removeChild(currentCaption);
      }
  
      if (value !== null) {
        const insertionPoint = this.firstChild;
        this.insertBefore(value, insertionPoint);
      }
      return value;
    }
  
    get tHead() {
      return firstChildWithHTMLLocalName(this, "thead");
    }
  
    set tHead(value) {
      if (value !== null && value._localName !== "thead") {
        throw new DOMException("Cannot set a non-thead element as a table header", "HierarchyRequestError");
      }
  
      const currentHead = this.tHead;
      if (currentHead !== null) {
        this.removeChild(currentHead);
      }
  
      if (value !== null) {
        const insertionPoint = tHeadInsertionPoint(this);
        this.insertBefore(value, insertionPoint);
      }
    }
  
    get tFoot() {
      return firstChildWithHTMLLocalName(this, "tfoot");
    }
  
    set tFoot(value) {
      if (value !== null && value._localName !== "tfoot") {
        throw new DOMException("Cannot set a non-tfoot element as a table footer", "HierarchyRequestError");
      }
  
      const currentFoot = this.tFoot;
      if (currentFoot !== null) {
        this.removeChild(currentFoot);
      }
  
      if (value !== null) {
        this.appendChild(value);
      }
    }
  
    get rows() {
      if (!this._rows) {
        this._rows = HTMLCollection.createImpl([], {
          element: this,
          query: () => {
            const headerRows = [];
            const bodyRows = [];
            const footerRows = [];
  
            const iterator = domSymbolTree.childrenIterator(this);
            for (const child of iterator) {
              if (child.nodeType !== NODE_TYPE.ELEMENT_NODE || child._namespaceURI !== HTML_NS) {
                continue;
              }
  
              if (child._localName === "thead") {
                headerRows.push(...childrenByHTMLLocalName(child, "tr"));
              } else if (child._localName === "tbody") {
                bodyRows.push(...childrenByHTMLLocalName(child, "tr"));
              } else if (child._localName === "tfoot") {
                footerRows.push(...childrenByHTMLLocalName(child, "tr"));
              } else if (child._localName === "tr") {
                bodyRows.push(child);
              }
            }
  
            return [...headerRows, ...bodyRows, ...footerRows];
          }
        });
      }
      return this._rows;
    }
  
    get tBodies() {
      if (!this._tBodies) {
        this._tBodies = HTMLCollection.createImpl([], {
          element: this,
          query: () => childrenByHTMLLocalName(this, "tbody")
        });
      }
      return this._tBodies;
    }
  
    createTBody() {
      const el = this._ownerDocument.createElement("TBODY");
  
      const tbodies = childrenByHTMLLocalName(this, "tbody");
      const insertionPoint = tbodies[tbodies.length - 1];
  
      if (insertionPoint) {
        this.insertBefore(el, insertionPoint.nextSibling);
      } else {
        this.appendChild(el);
      }
      return el;
    }
  
    createTHead() {
      let el = this.tHead;
      if (!el) {
        el = this.tHead = this._ownerDocument.createElement("THEAD");
      }
      return el;
    }
  
    deleteTHead() {
      this.tHead = null;
    }
  
    createTFoot() {
      let el = this.tFoot;
      if (!el) {
        el = this.tFoot = this._ownerDocument.createElement("TFOOT");
      }
      return el;
    }
  
    deleteTFoot() {
      this.tFoot = null;
    }
  
    createCaption() {
      let el = this.caption;
      if (!el) {
        el = this.caption = this._ownerDocument.createElement("CAPTION");
      }
      return el;
    }
  
    deleteCaption() {
      const c = this.caption;
      if (c) {
        c.parentNode.removeChild(c);
      }
    }
  
    insertRow(index) {
      if (index < -1 || index > this.rows.length) {
        throw new DOMException("Cannot insert a row at an index that is less than -1 or greater than the number of " +
          "existing rows", "IndexSizeError");
      }
  
      const tr = this._ownerDocument.createElement("tr");
  
      if (this.rows.length === 0 && this.tBodies.length === 0) {
        const tBody = this._ownerDocument.createElement("tbody");
        tBody.appendChild(tr);
        this.appendChild(tBody);
      } else if (this.rows.length === 0) {
        const tBody = this.tBodies.item(this.tBodies.length - 1);
        tBody.appendChild(tr);
      } else if (index === -1 || index === this.rows.length) {
        const tSection = this.rows.item(this.rows.length - 1).parentNode;
        tSection.appendChild(tr);
      } else {
        const beforeTR = this.rows.item(index);
        const tSection = beforeTR.parentNode;
        tSection.insertBefore(tr, beforeTR);
      }
  
      return tr;
    }
  
    deleteRow(index) {
      const rowLength = this.rows.length;
      if (index < -1 || index >= rowLength) {
        throw new DOMException(`Cannot delete a row at index ${index}, where no row exists`, "IndexSizeError");
      }
  
      if (index === -1) {
        if (rowLength === 0) {
          return;
        }
  
        index = rowLength - 1;
      }
  
      const tr = this.rows.item(index);
      tr.parentNode.removeChild(tr);
    }
  }
  
  module.exports = {
    implementation: HTMLTableElementImpl
  };
  