text: /* Copyright 2021 Mozilla Foundation
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  class XfaLayer {
    static setupStorage(html, id, element, storage, intent) {
      const storedData = storage.getValue(id, { value: null });
      switch (element.name) {
        case "textarea":
          if (storedData.value !== null) {
            html.textContent = storedData.value;
          }
          if (intent === "print") {
            break;
          }
          html.addEventListener("input", event => {
            storage.setValue(id, { value: event.target.value });
          });
          break;
        case "input":
          if (
            element.attributes.type === "radio" ||
            element.attributes.type === "checkbox"
          ) {
            if (storedData.value === element.attributes.exportedValue) {
              html.setAttribute("checked", true);
            }
            if (intent === "print") {
              break;
            }
            html.addEventListener("change", event => {
              storage.setValue(id, { value: event.target.getAttribute("xfaOn") });
            });
          } else {
            if (storedData.value !== null) {
              html.setAttribute("value", storedData.value);
            }
            if (intent === "print") {
              break;
            }
            html.addEventListener("input", event => {
              storage.setValue(id, { value: event.target.value });
            });
          }
          break;
        case "select":
          if (storedData.value !== null) {
            for (const option of element.children) {
              if (option.attributes.value === storedData.value) {
                option.attributes.selected = true;
              }
            }
          }
          html.addEventListener("input", event => {
            const options = event.target.options;
            const value =
              options.selectedIndex === -1
                ? ""
                : options[options.selectedIndex].value;
            storage.setValue(id, { value });
          });
          break;
      }
    }
  
    static setAttributes(html, element, storage, intent) {
      const { attributes } = element;
      if (attributes.type === "radio") {
        // Avoid to have a radio group when printing with the same as one
        // already displayed.
        attributes.name = `${attributes.name}-${intent}`;
      }
      for (const [key, value] of Object.entries(attributes)) {
        if (value === null || value === undefined || key === "dataId") {
          continue;
        }
  
        if (key !== "style") {
          if (key === "textContent") {
            html.textContent = value;
          } else if (key === "class") {
            html.setAttribute(key, value.join(" "));
          } else {
            html.setAttribute(key, value);
          }
        } else {
          Object.assign(html.style, value);
        }
      }
  
      // Set the value after the others to be sure overwrite
      // any other values.
      if (storage && attributes.dataId) {
        this.setupStorage(html, attributes.dataId, element, storage);
      }
    }
  
    static render(parameters) {
      const storage = parameters.annotationStorage;
      const root = parameters.xfa;
      const intent = parameters.intent || "display";
      const rootHtml = document.createElement(root.name);
      if (root.attributes) {
        this.setAttributes(rootHtml, root);
      }
      const stack = [[root, -1, rootHtml]];
  
      const rootDiv = parameters.div;
      rootDiv.appendChild(rootHtml);
      const transform = `matrix(${parameters.viewport.transform.join(",")})`;
      rootDiv.style.transform = transform;
  
      // Set defaults.
      rootDiv.setAttribute("class", "xfaLayer xfaFont");
  
      while (stack.length > 0) {
        const [parent, i, html] = stack[stack.length - 1];
        if (i + 1 === parent.children.length) {
          stack.pop();
          continue;
        }
  
        const child = parent.children[++stack[stack.length - 1][1]];
        if (child === null) {
          continue;
        }
  
        const { name } = child;
        if (name === "#text") {
          html.appendChild(document.createTextNode(child.value));
          continue;
        }
  
        let childHtml;
        if (child?.attributes?.xmlns) {
          childHtml = document.createElementNS(child.attributes.xmlns, name);
        } else {
          childHtml = document.createElement(name);
        }
  
        html.appendChild(childHtml);
        if (child.attributes) {
          this.setAttributes(childHtml, child, storage, intent);
        }
  
        if (child.children && child.children.length > 0) {
          stack.push([child, -1, childHtml]);
        } else if (child.value) {
          childHtml.appendChild(document.createTextNode(child.value));
        }
      }
  
      for (const el of rootDiv.querySelectorAll(
        ".xfaDisabled input, .xfaDisabled textarea"
      )) {
        el.setAttribute("disabled", true);
      }
      for (const el of rootDiv.querySelectorAll(
        ".xfaReadOnly input, .xfaReadOnly textarea"
      )) {
        el.setAttribute("readOnly", true);
      }
    }
  
    /**
     * Update the xfa layer.
     *
     * @public
     * @param {XfaLayerParameters} parameters
     * @memberof XfaLayer
     */
    static update(parameters) {
      const transform = `matrix(${parameters.viewport.transform.join(",")})`;
      parameters.div.style.transform = transform;
      parameters.div.hidden = false;
    }
  }
  
  export { XfaLayer };
  