"use strict";
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
const reactiveElement = require("../../../../../@lit_reactive-element@1.6.3/node_modules/@lit/reactive-element/reactive-element.js");
require("../../../../../lit-html@2.8.0/node_modules/lit-html/lit-html.js");
require("../../../../../lit-element@3.3.3/node_modules/lit-element/lit-element.js");
const property = require("../../../../../@lit_reactive-element@1.6.3/node_modules/@lit/reactive-element/decorators/property.js");
require("../../../../../@lit_reactive-element@1.6.3/node_modules/@lit/reactive-element/decorators/query-assigned-elements.js");
const constants = require("./constants.js");
const environment = require("./features/environment.js");
const template = require("./template.js");
const CachingGLTFLoader = require("./three-components/CachingGLTFLoader.js");
const ModelScene = require("./three-components/ModelScene.js");
const Renderer = require("./three-components/Renderer.js");
const utilities = require("./utilities.js");
const progressTracker = require("./utilities/progress-tracker.js");
/* @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * 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.
 */
var __decorate = globalThis && globalThis.__decorate || function(decorators, target, key, desc) {
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
    r = Reflect.decorate(decorators, target, key, desc);
  else
    for (var i = decorators.length - 1; i >= 0; i--)
      if (d = decorators[i])
        r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
const CLEAR_MODEL_TIMEOUT_MS = 10;
const FALLBACK_SIZE_UPDATE_THRESHOLD_MS = 50;
const ANNOUNCE_MODEL_VISIBILITY_DEBOUNCE_THRESHOLD = 0;
const UNSIZED_MEDIA_WIDTH = 300;
const UNSIZED_MEDIA_HEIGHT = 150;
const blobCanvas = document.createElement("canvas");
const $fallbackResizeHandler = Symbol("fallbackResizeHandler");
const $defaultAriaLabel = Symbol("defaultAriaLabel");
const $resizeObserver = Symbol("resizeObserver");
const $clearModelTimeout = Symbol("clearModelTimeout");
const $onContextLost = Symbol("onContextLost");
const $loaded = Symbol("loaded");
const $status = Symbol("status");
const $onFocus = Symbol("onFocus");
const $onBlur = Symbol("onBlur");
const $updateSize = Symbol("updateSize");
const $intersectionObserver = Symbol("intersectionObserver");
const $isElementInViewport = Symbol("isElementInViewport");
const $announceModelVisibility = Symbol("announceModelVisibility");
const $ariaLabel = Symbol("ariaLabel");
const $altDefaulted = Symbol("altDefaulted");
const $statusElement = Symbol("statusElement");
const $updateStatus = Symbol("updateStatus");
const $loadedTime = Symbol("loadedTime");
const $updateSource = Symbol("updateSource");
const $markLoaded = Symbol("markLoaded");
const $container = Symbol("container");
const $userInputElement = Symbol("input");
const $canvas = Symbol("canvas");
const $scene = Symbol("scene");
const $needsRender = Symbol("needsRender");
const $tick = Symbol("tick");
const $onModelLoad = Symbol("onModelLoad");
const $onResize = Symbol("onResize");
const $renderer = Symbol("renderer");
const $progressTracker = Symbol("progressTracker");
const $getLoaded = Symbol("getLoaded");
const $getModelIsVisible = Symbol("getModelIsVisible");
const $shouldAttemptPreload = Symbol("shouldAttemptPreload");
const toVector3D = (v) => {
  return {
    x: v.x,
    y: v.y,
    z: v.z,
    toString() {
      return `${this.x}m ${this.y}m ${this.z}m`;
    }
  };
};
const toVector2D = (v) => {
  return {
    u: v.x,
    v: v.y,
    toString() {
      return `${this.u} ${this.v}`;
    }
  };
};
class ModelViewerElementBase extends reactiveElement.ReactiveElement {
  /**
   * Creates a new ModelViewerElement.
   */
  constructor() {
    super();
    this.alt = null;
    this.src = null;
    this.withCredentials = false;
    this.generateSchema = false;
    this[_a] = false;
    this[_b] = false;
    this[_c] = 0;
    this[_d] = "";
    this[_e] = null;
    this[_f] = utilities.debounce(() => {
      const boundingRect = this.getBoundingClientRect();
      this[$updateSize](boundingRect);
    }, FALLBACK_SIZE_UPDATE_THRESHOLD_MS);
    this[_g] = utilities.debounce((oldVisibility) => {
      const newVisibility = this.modelIsVisible;
      if (newVisibility !== oldVisibility) {
        this.dispatchEvent(new CustomEvent("model-visibility", { detail: { visible: newVisibility } }));
      }
    }, ANNOUNCE_MODEL_VISIBILITY_DEBOUNCE_THRESHOLD);
    this[_h] = null;
    this[_j] = null;
    this[_k] = new progressTracker.ProgressTracker();
    this[_l] = () => {
      this[$statusElement].textContent = this[$status];
    };
    this[_m] = () => {
      this[$statusElement].textContent = "";
    };
    this[_o] = (event) => {
      this.dispatchEvent(new CustomEvent("error", { detail: { type: "webglcontextlost", sourceError: event.sourceEvent } }));
    };
    this.attachShadow({ mode: "open" });
    const shadowRoot = this.shadowRoot;
    template.makeTemplate(shadowRoot);
    this[$container] = shadowRoot.querySelector(".container");
    this[$userInputElement] = shadowRoot.querySelector(".userInput");
    this[$canvas] = shadowRoot.querySelector("canvas");
    this[$statusElement] = shadowRoot.querySelector("#status");
    this[$defaultAriaLabel] = this[$userInputElement].getAttribute("aria-label");
    let width, height;
    if (this.isConnected) {
      const rect = this.getBoundingClientRect();
      width = rect.width;
      height = rect.height;
    } else {
      width = UNSIZED_MEDIA_WIDTH;
      height = UNSIZED_MEDIA_HEIGHT;
    }
    this[$scene] = new ModelScene.ModelScene({ canvas: this[$canvas], element: this, width, height });
    Promise.resolve().then(() => {
      this[$updateSize](this.getBoundingClientRect());
    });
    if (constants.HAS_RESIZE_OBSERVER) {
      this[$resizeObserver] = new ResizeObserver((entries) => {
        if (this[$renderer].isPresenting) {
          return;
        }
        for (let entry of entries) {
          if (entry.target === this) {
            this[$updateSize](entry.contentRect);
          }
        }
      });
    }
    if (constants.HAS_INTERSECTION_OBSERVER) {
      this[$intersectionObserver] = new IntersectionObserver((entries) => {
        for (let entry of entries) {
          if (entry.target === this) {
            const oldVisibility = this.modelIsVisible;
            this[$isElementInViewport] = entry.isIntersecting;
            this[$announceModelVisibility](oldVisibility);
            if (this[$isElementInViewport] && !this.loaded) {
              this[$updateSource]();
            }
          }
        }
      }, {
        root: null,
        // We used to have margin here, but it was causing animated models below
        // the fold to steal the frame budget. Weirder still, it would also
        // cause input events to be swallowed, sometimes for seconds on the
        // model above the fold, but only when the animated model was completely
        // below. Setting this margin to zero fixed it.
        rootMargin: "0px",
        // With zero threshold, an element adjacent to but not intersecting the
        // viewport will be reported as intersecting, which will cause
        // unnecessary rendering. Any slight positive threshold alleviates this.
        threshold: 1e-5
      });
    } else {
      this[$isElementInViewport] = true;
    }
  }
  static get is() {
    return "model-viewer";
  }
  /** @export */
  static set modelCacheSize(value) {
    CachingGLTFLoader.CachingGLTFLoader[CachingGLTFLoader.$evictionPolicy].evictionThreshold = value;
  }
  /** @export */
  static get modelCacheSize() {
    return CachingGLTFLoader.CachingGLTFLoader[CachingGLTFLoader.$evictionPolicy].evictionThreshold;
  }
  /** @export */
  static set minimumRenderScale(value) {
    if (value > 1) {
      console.warn("<model-viewer> minimumRenderScale has been clamped to a maximum value of 1.");
    }
    if (value <= 0) {
      console.warn("<model-viewer> minimumRenderScale has been clamped to a minimum value of 0.25.");
    }
    Renderer.Renderer.singleton.minScale = value;
  }
  /** @export */
  static get minimumRenderScale() {
    return Renderer.Renderer.singleton.minScale;
  }
  /** @export */
  get loaded() {
    return this[$getLoaded]();
  }
  get [(_a = $isElementInViewport, _b = $loaded, _c = $loadedTime, _d = $status, _e = $clearModelTimeout, _f = $fallbackResizeHandler, _g = $announceModelVisibility, _h = $resizeObserver, _j = $intersectionObserver, _k = $progressTracker, $renderer)]() {
    return Renderer.Renderer.singleton;
  }
  /** @export */
  get modelIsVisible() {
    return this[$getModelIsVisible]();
  }
  connectedCallback() {
    super.connectedCallback && super.connectedCallback();
    if (constants.HAS_RESIZE_OBSERVER) {
      this[$resizeObserver].observe(this);
    } else {
      self.addEventListener("resize", this[$fallbackResizeHandler]);
    }
    if (constants.HAS_INTERSECTION_OBSERVER) {
      this[$intersectionObserver].observe(this);
    }
    this.addEventListener("focus", this[$onFocus]);
    this.addEventListener("blur", this[$onBlur]);
    const renderer = this[$renderer];
    renderer.addEventListener("contextlost", this[$onContextLost]);
    renderer.registerScene(this[$scene]);
    if (this[$clearModelTimeout] != null) {
      self.clearTimeout(this[$clearModelTimeout]);
      this[$clearModelTimeout] = null;
      this.requestUpdate("src", null);
    }
  }
  disconnectedCallback() {
    super.disconnectedCallback && super.disconnectedCallback();
    if (constants.HAS_RESIZE_OBSERVER) {
      this[$resizeObserver].unobserve(this);
    } else {
      self.removeEventListener("resize", this[$fallbackResizeHandler]);
    }
    if (constants.HAS_INTERSECTION_OBSERVER) {
      this[$intersectionObserver].unobserve(this);
    }
    this.removeEventListener("focus", this[$onFocus]);
    this.removeEventListener("blur", this[$onBlur]);
    const renderer = this[$renderer];
    renderer.removeEventListener("contextlost", this[$onContextLost]);
    renderer.unregisterScene(this[$scene]);
    this[$clearModelTimeout] = self.setTimeout(() => {
      this[$scene].dispose();
      this[$clearModelTimeout] = null;
    }, CLEAR_MODEL_TIMEOUT_MS);
  }
  updated(changedProperties) {
    super.updated(changedProperties);
    if (changedProperties.has("src")) {
      if (this.src == null) {
        this[$loaded] = false;
        this[$loadedTime] = 0;
        this[$scene].reset();
      } else if (this.src !== this[$scene].url) {
        this[$loaded] = false;
        this[$loadedTime] = 0;
        this[$updateSource]();
      }
    }
    if (changedProperties.has("alt")) {
      this[$userInputElement].setAttribute("aria-label", this[$ariaLabel]);
    }
    if (changedProperties.has("withCredentials")) {
      CachingGLTFLoader.CachingGLTFLoader.withCredentials = this.withCredentials;
      this[$renderer].textureUtils.withCredentials = this.withCredentials;
    }
    if (changedProperties.has("generateSchema")) {
      if (this.generateSchema) {
        this[$scene].updateSchema(this.src);
      } else {
        this[$scene].updateSchema(null);
      }
    }
  }
  /** @export */
  toDataURL(type, encoderOptions) {
    return this[$renderer].displayCanvas(this[$scene]).toDataURL(type, encoderOptions);
  }
  /** @export */
  async toBlob(options) {
    const mimeType = options ? options.mimeType : void 0;
    const qualityArgument = options ? options.qualityArgument : void 0;
    const useIdealAspect = options ? options.idealAspect : void 0;
    const { width, height, idealAspect, aspect } = this[$scene];
    const { dpr, scaleFactor } = this[$renderer];
    let outputWidth = width * scaleFactor * dpr;
    let outputHeight = height * scaleFactor * dpr;
    let offsetX = 0;
    let offsetY = 0;
    if (useIdealAspect === true) {
      if (idealAspect > aspect) {
        const oldHeight = outputHeight;
        outputHeight = Math.round(outputWidth / idealAspect);
        offsetY = (oldHeight - outputHeight) / 2;
      } else {
        const oldWidth = outputWidth;
        outputWidth = Math.round(outputHeight * idealAspect);
        offsetX = (oldWidth - outputWidth) / 2;
      }
    }
    blobCanvas.width = outputWidth;
    blobCanvas.height = outputHeight;
    try {
      return new Promise(async (resolve, reject) => {
        blobCanvas.getContext("2d").drawImage(this[$renderer].displayCanvas(this[$scene]), offsetX, offsetY, outputWidth, outputHeight, 0, 0, outputWidth, outputHeight);
        blobCanvas.toBlob((blob) => {
          if (!blob) {
            return reject(new Error("Unable to retrieve canvas blob"));
          }
          resolve(blob);
        }, mimeType, qualityArgument);
      });
    } finally {
      this[$updateSize]({ width, height });
    }
  }
  /**
   * Registers a new EffectComposer as the main rendering pipeline,
   * instead of the default ThreeJs renderer.
   * This method also calls setRenderer, setMainScene, and setMainCamera on
   * your effectComposer.
   * @param effectComposer An EffectComposer from `pmndrs/postprocessing`
   */
  registerEffectComposer(effectComposer) {
    effectComposer.setRenderer(this[$renderer].threeRenderer);
    effectComposer.setMainCamera(this[$scene].getCamera());
    effectComposer.setMainScene(this[$scene]);
    this[$scene].effectRenderer = effectComposer;
  }
  /**
   * Removes the registered EffectComposer
   */
  unregisterEffectComposer() {
    this[$scene].effectRenderer = null;
  }
  registerRenderer(renderer) {
    this[$scene].externalRenderer = renderer;
  }
  unregisterRenderer() {
    this[$scene].externalRenderer = null;
  }
  get [$ariaLabel]() {
    return this[$altDefaulted];
  }
  get [$altDefaulted]() {
    return this.alt == null || this.alt === "null" ? this[$defaultAriaLabel] : this.alt;
  }
  // NOTE(cdata): Although this may seem extremely redundant, it is required in
  // order to support overloading when TypeScript is compiled to ES5
  // @see https://github.com/Polymer/lit-element/pull/745
  // @see https://github.com/microsoft/TypeScript/issues/338
  [$getLoaded]() {
    return this[$loaded];
  }
  // @see [$getLoaded]
  [$getModelIsVisible]() {
    return this.loaded && this[$isElementInViewport];
  }
  [$shouldAttemptPreload]() {
    return !!this.src && this[$isElementInViewport];
  }
  /**
   * Called on initialization and when the resize observer fires.
   */
  [$updateSize]({ width, height }) {
    if (width === 0 || height === 0) {
      return;
    }
    this[$container].style.width = `${width}px`;
    this[$container].style.height = `${height}px`;
    this[$onResize]({ width, height });
  }
  [$tick](time, delta) {
    var _p;
    (_p = this[$scene].effectRenderer) === null || _p === void 0 ? void 0 : _p.beforeRender(time, delta);
  }
  [$markLoaded]() {
    if (this[$loaded]) {
      return;
    }
    this[$loaded] = true;
    this[$loadedTime] = performance.now();
  }
  [$needsRender]() {
    this[$scene].queueRender();
  }
  [$onModelLoad]() {
  }
  [$updateStatus](status) {
    this[$status] = status;
    const rootNode = this.getRootNode();
    if (rootNode != null && rootNode.activeElement === this && this[$statusElement].textContent != status) {
      this[$statusElement].textContent = status;
    }
  }
  [(_l = $onFocus, _m = $onBlur, $onResize)](e) {
    this[$scene].setSize(e.width, e.height);
  }
  /**
   * Parses the element for an appropriate source URL and
   * sets the views to use the new model based.
   */
  async [(_o = $onContextLost, $updateSource)]() {
    const scene = this[$scene];
    if (this.loaded || !this[$shouldAttemptPreload]() || this.src === scene.url) {
      return;
    }
    if (this.generateSchema) {
      scene.updateSchema(this.src);
    }
    this[$updateStatus]("Loading");
    scene.stopAnimation();
    const updateSourceProgress = this[$progressTracker].beginActivity();
    const source = this.src;
    try {
      const srcUpdated = scene.setSource(source, (progress) => updateSourceProgress(utilities.clamp(progress, 0, 1) * 0.95));
      const envUpdated = this[environment.$updateEnvironment]();
      await Promise.all([srcUpdated, envUpdated]);
      this[$markLoaded]();
      this[$onModelLoad]();
      this.updateComplete.then(() => {
        this.dispatchEvent(new CustomEvent("before-render"));
      });
      await new Promise((resolve) => {
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            this.dispatchEvent(new CustomEvent("load", { detail: { url: source } }));
            resolve();
          });
        });
      });
    } catch (error) {
      this.dispatchEvent(new CustomEvent("error", { detail: { type: "loadfailure", sourceError: error } }));
    } finally {
      updateSourceProgress(1);
    }
  }
}
__decorate([
  property.property({ type: String })
], ModelViewerElementBase.prototype, "alt", void 0);
__decorate([
  property.property({ type: String })
], ModelViewerElementBase.prototype, "src", void 0);
__decorate([
  property.property({ type: Boolean, attribute: "with-credentials" })
], ModelViewerElementBase.prototype, "withCredentials", void 0);
__decorate([
  property.property({ type: Boolean, attribute: "generate-schema" })
], ModelViewerElementBase.prototype, "generateSchema", void 0);
exports.$altDefaulted = $altDefaulted;
exports.$announceModelVisibility = $announceModelVisibility;
exports.$ariaLabel = $ariaLabel;
exports.$canvas = $canvas;
exports.$container = $container;
exports.$getLoaded = $getLoaded;
exports.$getModelIsVisible = $getModelIsVisible;
exports.$intersectionObserver = $intersectionObserver;
exports.$isElementInViewport = $isElementInViewport;
exports.$loadedTime = $loadedTime;
exports.$markLoaded = $markLoaded;
exports.$needsRender = $needsRender;
exports.$onModelLoad = $onModelLoad;
exports.$onResize = $onResize;
exports.$progressTracker = $progressTracker;
exports.$renderer = $renderer;
exports.$scene = $scene;
exports.$shouldAttemptPreload = $shouldAttemptPreload;
exports.$statusElement = $statusElement;
exports.$tick = $tick;
exports.$updateSize = $updateSize;
exports.$updateSource = $updateSource;
exports.$updateStatus = $updateStatus;
exports.$userInputElement = $userInputElement;
exports.blobCanvas = blobCanvas;
exports.default = ModelViewerElementBase;
exports.toVector2D = toVector2D;
exports.toVector3D = toVector3D;
//# sourceMappingURL=model-viewer-base.js.map
