'use strict';

require('../core/settings.js');
require('../color.js');
require('../constants.js');
require('../extensions.js');
require('../math/const.js');
var Rectangle = require('../math/shapes/Rectangle.js');
require('../math/groupD8.js');
require('../math/Transform.js');
require('../runner.js');
var settings = require('../settings/settings.js');
require('../ticker/settings.js');
require('../ticker/const.js');
require('../ticker/Ticker.js');
require('../ticker/TickerPlugin.js');
require('../utils/settings.js');
require('../utils/is_mobile/index.js');
require('../utils/color/premultiply.js');
var sign = require('../utils/data/sign.js');
require('../utils/media/BoundingBox.js');
require('../core/autoDetectRenderer.js');
require('../core/background/BackgroundSystem.js');
require('../core/batch/BatchRenderer.js');
require('../core/shader/Program.js');
require('../core/batch/BatchSystem.js');
require('../core/context/ContextSystem.js');
require('../core/filters/Filter.js');
require('../core/filters/FilterSystem.js');
require('../core/textures/BaseTexture.js');
require('../core/framebuffer/FramebufferSystem.js');
require('../core/framebuffer/MultisampleSystem.js');
require('../core/geometry/BufferSystem.js');
require('../core/geometry/GeometrySystem.js');
require('../core/mask/MaskSystem.js');
require('../core/mask/ScissorSystem.js');
require('../core/mask/StencilSystem.js');
require('../core/plugin/PluginSystem.js');
require('../core/projection/ProjectionSystem.js');
require('../core/render/ObjectRendererSystem.js');
require('../core/Renderer.js');
require('../core/renderTexture/GenerateTextureSystem.js');
var Texture = require('../core/textures/Texture.js');
require('../core/renderTexture/RenderTexturePool.js');
require('../core/renderTexture/RenderTextureSystem.js');
require('../core/shader/ShaderSystem.js');
require('../core/startup/StartupSystem.js');
require('../core/state/StateSystem.js');
require('../core/textures/TextureGCSystem.js');
require('../core/textures/TextureSystem.js');
require('../core/transformFeedback/TransformFeedbackSystem.js');
require('../core/view/ViewSystem.js');
require('../core/textures/resources/index.js');
var sprite = require('../sprite.js');
require('../text/const.js');
require('../text/Text.js');
require('../text/TextMetrics.js');
var TextStyle = require('../text/TextStyle.js');
var HTMLTextStyle = require('./HTMLTextStyle.js');

class HTMLText extends sprite.Sprite {
  /**
   * @param {string} [text] - Text contents
   * @param {PIXI.HTMLTextStyle|PIXI.TextStyle|PIXI.ITextStyle} [style] - Style setting to use.
   *        Strongly recommend using an HTMLTextStyle object. Providing a PIXI.TextStyle
   *        will convert the TextStyle to an HTMLTextStyle and will no longer be linked.
   */
  constructor(text = "", style = {}) {
    super(Texture.Texture.EMPTY);
    this._text = null;
    this._style = null;
    this._autoResolution = true;
    this.localStyleID = -1;
    this.dirty = false;
    this.ownsStyle = false;
    this.loadID = 0;
    const image = new Image();
    const texture = Texture.Texture.from(image, {
      scaleMode: settings.settings.SCALE_MODE,
      resourceOptions: {
        autoLoad: false
      }
    });
    texture.orig = new Rectangle.Rectangle();
    texture.trim = new Rectangle.Rectangle();
    this.texture = texture;
    const nssvg = "http://www.w3.org/2000/svg";
    const nsxhtml = "http://www.w3.org/1999/xhtml";
    const svgRoot = document.createElementNS(nssvg, "svg");
    const foreignObject = document.createElementNS(nssvg, "foreignObject");
    const domElement = document.createElementNS(nsxhtml, "div");
    const styleElement = document.createElementNS(nsxhtml, "style");
    foreignObject.setAttribute("width", "10000");
    foreignObject.setAttribute("height", "10000");
    foreignObject.style.overflow = "hidden";
    svgRoot.appendChild(foreignObject);
    this.maxWidth = HTMLText.defaultMaxWidth;
    this.maxHeight = HTMLText.defaultMaxHeight;
    this._domElement = domElement;
    this._styleElement = styleElement;
    this._svgRoot = svgRoot;
    this._foreignObject = foreignObject;
    this._foreignObject.appendChild(styleElement);
    this._foreignObject.appendChild(domElement);
    this._image = image;
    this._loadImage = new Image();
    this._autoResolution = HTMLText.defaultAutoResolution;
    this._resolution = HTMLText.defaultResolution ?? settings.settings.RESOLUTION;
    this.text = text;
    this.style = style;
  }
  /**
   * Calculate the size of the output text without actually drawing it.
   * This includes the `padding` in the `style` object.
   * This can be used as a fast-pass to do things like text-fitting.
   * @param {object} [overrides] - Overrides for the text, style, and resolution.
   * @param {string} [overrides.text] - The text to measure, if not specified, the current text is used.
   * @param {PIXI.HTMLTextStyle} [overrides.style] - The style to measure, if not specified, the current style is used.
   * @param {number} [overrides.resolution] - The resolution to measure, if not specified, the current resolution is used.
   * @returns {PIXI.ISize} Width and height of the measured text.
   */
  measureText(overrides) {
    const { text, style, resolution } = Object.assign({
      text: this._text,
      style: this._style,
      resolution: this._resolution
    }, overrides);
    Object.assign(this._domElement, {
      innerHTML: text,
      style: style.toCSS(resolution)
    });
    this._styleElement.textContent = style.toGlobalCSS();
    document.body.appendChild(this._svgRoot);
    const contentBounds = this._domElement.getBoundingClientRect();
    this._svgRoot.remove();
    const contentWidth = Math.min(this.maxWidth, Math.ceil(contentBounds.width));
    const contentHeight = Math.min(this.maxHeight, Math.ceil(contentBounds.height));
    this._svgRoot.setAttribute("width", contentWidth.toString());
    this._svgRoot.setAttribute("height", contentHeight.toString());
    if (text !== this._text) {
      this._domElement.innerHTML = this._text;
    }
    if (style !== this._style) {
      Object.assign(this._domElement, { style: this._style?.toCSS(resolution) });
      this._styleElement.textContent = this._style?.toGlobalCSS();
    }
    return {
      width: contentWidth + style.padding * 2,
      height: contentHeight + style.padding * 2
    };
  }
  /**
   * Manually refresh the text.
   * @public
   * @param {boolean} respectDirty - Whether to abort updating the
   *        text if the Text isn't dirty and the function is called.
   */
  async updateText(respectDirty = true) {
    const { style, _image: image, _loadImage: loadImage } = this;
    if (this.localStyleID !== style.styleID) {
      this.dirty = true;
      this.localStyleID = style.styleID;
    }
    if (!this.dirty && respectDirty) {
      return;
    }
    const { width, height } = this.measureText();
    const loadID = this.loadID;
    this.dirty = false;
    image.width = loadImage.width = Math.ceil(Math.max(1, width));
    image.height = loadImage.height = Math.ceil(Math.max(1, height));
    await new Promise((resolve) => {
      loadImage.onload = async () => {
        if (loadID !== this.loadID) {
          resolve();
          return;
        }
        await style.onBeforeDraw();
        image.src = loadImage.src;
        loadImage.onload = null;
        loadImage.src = "";
        this.updateTexture();
        resolve();
      };
      const svgURL = new XMLSerializer().serializeToString(this._svgRoot);
      loadImage.src = `data:image/svg+xml;charset=utf8,${encodeURIComponent(svgURL)}`;
    });
  }
  /** The raw image element that is rendered under-the-hood. */
  get source() {
    return this._image;
  }
  /**
   * Update the texture resource.
   * @private
   */
  updateTexture() {
    const { style, texture, _image: image, resolution } = this;
    const { padding } = style;
    const { baseTexture } = texture;
    texture.trim.width = texture._frame.width = image.width / resolution;
    texture.trim.height = texture._frame.height = image.height / resolution;
    texture.trim.x = -padding;
    texture.trim.y = -padding;
    texture.orig.width = texture._frame.width - padding * 2;
    texture.orig.height = texture._frame.height - padding * 2;
    this._onTextureUpdate();
    baseTexture.setRealSize(image.width, image.height, resolution);
    this.dirty = false;
  }
  /**
   * Renders the object using the WebGL renderer
   * @param {PIXI.Renderer} renderer - The renderer
   * @private
   */
  _render(renderer) {
    if (this._autoResolution && this._resolution !== renderer.resolution) {
      this._resolution = renderer.resolution;
      this.dirty = true;
    }
    this.updateText(true);
    super._render(renderer);
  }
  /**
   * Renders the object using the Canvas Renderer.
   * @private
   * @param {PIXI.CanvasRenderer} renderer - The renderer
   */
  _renderCanvas(renderer) {
    if (this._autoResolution && this._resolution !== renderer.resolution) {
      this._resolution = renderer.resolution;
      this.dirty = true;
    }
    this.updateText(true);
    super._renderCanvas(renderer);
  }
  /**
   * Get the local bounds.
   * @param {PIXI.Rectangle} rect - Input rectangle.
   * @returns {PIXI.Rectangle} Local bounds
   */
  getLocalBounds(rect) {
    this.updateText(true);
    return super.getLocalBounds(rect);
  }
  _calculateBounds() {
    this.updateText(true);
    this.calculateVertices();
    this._bounds.addQuad(this.vertexData);
  }
  /**
   * Handle dirty style changes
   * @private
   */
  _onStyleChange() {
    this.dirty = true;
  }
  /**
   * Destroy this Text object. Don't use after calling.
   * @param {boolean|object} options - Same as Sprite destroy options.
   */
  destroy(options) {
    if (typeof options === "boolean") {
      options = { children: options };
    }
    options = Object.assign({}, HTMLText.defaultDestroyOptions, options);
    super.destroy(options);
    const forceClear = null;
    if (this.ownsStyle) {
      this._style?.cleanFonts();
    }
    this._style = forceClear;
    this._svgRoot?.remove();
    this._svgRoot = forceClear;
    this._domElement?.remove();
    this._domElement = forceClear;
    this._foreignObject?.remove();
    this._foreignObject = forceClear;
    this._styleElement?.remove();
    this._styleElement = forceClear;
    this._loadImage.src = "";
    this._loadImage.onload = null;
    this._loadImage = forceClear;
    this._image.src = "";
    this._image = forceClear;
  }
  /**
   * Get the width in pixels.
   * @member {number}
   */
  get width() {
    this.updateText(true);
    return Math.abs(this.scale.x) * this._image.width / this.resolution;
  }
  set width(value) {
    this.updateText(true);
    const s = sign.sign(this.scale.x) || 1;
    this.scale.x = s * value / this._image.width / this.resolution;
    this._width = value;
  }
  /**
   * Get the height in pixels.
   * @member {number}
   */
  get height() {
    this.updateText(true);
    return Math.abs(this.scale.y) * this._image.height / this.resolution;
  }
  set height(value) {
    this.updateText(true);
    const s = sign.sign(this.scale.y) || 1;
    this.scale.y = s * value / this._image.height / this.resolution;
    this._height = value;
  }
  /** The base style to render with text. */
  get style() {
    return this._style;
  }
  set style(style) {
    if (this._style === style) {
      return;
    }
    style = style || {};
    if (style instanceof HTMLTextStyle.HTMLTextStyle) {
      this.ownsStyle = false;
      this._style = style;
    } else if (style instanceof TextStyle.TextStyle) {
      console.warn("[HTMLText] Cloning TextStyle, if this is not what you want, use HTMLTextStyle");
      this.ownsStyle = true;
      this._style = HTMLTextStyle.HTMLTextStyle.from(style);
    } else {
      this.ownsStyle = true;
      this._style = new HTMLTextStyle.HTMLTextStyle(style);
    }
    this.localStyleID = -1;
    this.dirty = true;
  }
  /**
   * Contents of text. This can be HTML text and include tags.
   * @example
   * const text = new HTMLText('This is a <em>styled</em> text!');
   * @member {string}
   */
  get text() {
    return this._text;
  }
  set text(text) {
    text = String(text === "" || text === null || text === void 0 ? " " : text);
    text = this.sanitiseText(text);
    if (this._text === text) {
      return;
    }
    this._text = text;
    this.dirty = true;
  }
  /**
   * The resolution / device pixel ratio of the canvas.
   * This is set to automatically match the renderer resolution by default, but can be overridden by setting manually.
   * @member {number}
   * @default 1
   */
  get resolution() {
    return this._resolution;
  }
  set resolution(value) {
    this._autoResolution = false;
    if (this._resolution === value) {
      return;
    }
    this._resolution = value;
    this.dirty = true;
  }
  /**
   * Sanitise text - replace `<br>` with `<br/>`, `&nbsp;` with `&#160;`
   * @param text
   * @see https://www.sitepoint.com/community/t/xhtml-1-0-transitional-xml-parsing-error-entity-nbsp-not-defined/3392/3
   */
  sanitiseText(text) {
    return text.replace(/<br>/gi, "<br/>").replace(/<hr>/gi, "<hr/>").replace(/&nbsp;/gi, "&#160;");
  }
}
HTMLText.defaultDestroyOptions = {
  texture: true,
  children: false,
  baseTexture: true
};
HTMLText.defaultMaxWidth = 2024;
HTMLText.defaultMaxHeight = 2024;
HTMLText.defaultAutoResolution = true;

exports.HTMLText = HTMLText;
//# sourceMappingURL=HTMLText.js.map
