{
  "version": 3,
  "sources": ["/cdn/shop/t/4/assets/media.js?fpv=20260818-2"],
  "sourcesContent": ["import { Component } from '@theme/component';\nimport { ThemeEvents, MediaStartedPlayingEvent } from '@theme/events';\nimport { DialogCloseEvent } from '@theme/dialog';\n\n/**\n * A deferred media element\n * @typedef {Object} Refs\n * @property {HTMLElement} deferredMediaPlayButton - The button to show the deferred media content\n * @property {HTMLElement} toggleMediaButton - The button to toggle the media\n *\n * @extends {Component<Refs>}\n */\nclass DeferredMedia extends Component {\n  /** @type {boolean} */\n  isPlaying = false;\n\n  #abortController = new AbortController();\n\n  /** @type {IntersectionObserver | undefined} */\n  #autoplayObserver;\n\n  connectedCallback() {\n    super.connectedCallback();\n    const signal = this.#abortController.signal;\n    // If we're to use deferred media for images, we will need to run this only when it's not an image type media\n    document.addEventListener(ThemeEvents.mediaStartedPlaying, this.pauseMedia.bind(this), { signal });\n    window.addEventListener(DialogCloseEvent.eventName, this.pauseMedia.bind(this), { signal });\n\n    if (this.hasAttribute('autoplay') && this.querySelector('template')) {\n      this.#observeForAutoplay();\n    }\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback();\n    this.#autoplayObserver?.disconnect();\n    this.#abortController.abort();\n  }\n\n  /**\n   * Loads ambient autoplay media only when it is close to the viewport.\n   * Mobile waits for user interaction, window load, and an idle period so\n   * video bytes do not compete with the LCP poster on a throttled connection.\n   */\n  #observeForAutoplay() {\n    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;\n\n    const loadWhenReady = () => {\n      this.#autoplayObserver?.disconnect();\n\n      const load = () => {\n        if (!this.isConnected || this.hasAttribute('data-media-loaded')) return;\n\n        this.loadContent(false);\n        this.isPlaying = true;\n        this.updatePlayPauseHint(true);\n      };\n\n      if (!window.matchMedia('(max-width: 749px)').matches) {\n        load();\n        return;\n      }\n\n      const loadWhenIdle = () => {\n        if ('requestIdleCallback' in window) {\n          window.requestIdleCallback(load, { timeout: 2500 });\n        } else {\n          window.setTimeout(load, 1);\n        }\n      };\n\n      const loadAfterInteraction = () => {\n        if (document.readyState === 'complete') {\n          loadWhenIdle();\n        } else {\n          window.addEventListener('load', loadWhenIdle, {\n            once: true,\n            signal: this.#abortController.signal,\n          });\n        }\n      };\n\n      // Keep ambient video out of the initial mobile payload. Tapping the\n      // poster still loads immediately through showDeferredMedia().\n      window.addEventListener('pointerdown', loadAfterInteraction, {\n        once: true,\n        passive: true,\n        signal: this.#abortController.signal,\n      });\n      window.addEventListener('keydown', loadAfterInteraction, {\n        once: true,\n        signal: this.#abortController.signal,\n      });\n      window.addEventListener('scroll', loadAfterInteraction, {\n        once: true,\n        passive: true,\n        signal: this.#abortController.signal,\n      });\n    };\n\n    if (!('IntersectionObserver' in window)) {\n      loadWhenReady();\n      return;\n    }\n\n    this.#autoplayObserver = new IntersectionObserver(\n      (entries) => {\n        if (entries.some((entry) => entry.isIntersecting)) loadWhenReady();\n      },\n      { rootMargin: '200px 0px', threshold: 0.01 }\n    );\n    this.#autoplayObserver.observe(this);\n  }\n\n  /**\n   * Updates the visual hint for play/pause state\n   * @param {boolean} isPlaying - Whether the video is currently playing\n   */\n  updatePlayPauseHint(isPlaying) {\n    const toggleMediaButton = this.refs.toggleMediaButton;\n    if (toggleMediaButton instanceof HTMLElement) {\n      toggleMediaButton.classList.remove('hidden');\n      const playIcon = toggleMediaButton.querySelector('.icon-play');\n      if (playIcon) playIcon.classList.toggle('hidden', isPlaying);\n      const pauseIcon = toggleMediaButton.querySelector('.icon-pause');\n      if (pauseIcon) pauseIcon.classList.toggle('hidden', !isPlaying);\n    }\n  }\n\n  /**\n   * Shows the deferred media content\n   */\n  showDeferredMedia = () => {\n    this.loadContent(true);\n    this.isPlaying = true;\n    this.updatePlayPauseHint(this.isPlaying);\n  };\n\n  /**\n   * Loads the content\n   * @param {boolean} [focus] - Whether to focus the content\n   */\n  loadContent(focus = true) {\n    if (this.getAttribute('data-media-loaded')) return;\n\n    this.dispatchEvent(new MediaStartedPlayingEvent(this));\n\n    const content = this.querySelector('template')?.content.firstElementChild?.cloneNode(true);\n\n    if (!content) return;\n\n    this.setAttribute('data-media-loaded', 'true');\n    this.appendChild(content);\n\n    if (focus && content instanceof HTMLElement) {\n      content.focus();\n    }\n\n    this.refs.deferredMediaPlayButton?.classList.add('deferred-media__playing');\n\n    if (content instanceof HTMLVideoElement && content.getAttribute('autoplay')) {\n      // force autoplay for safari\n      content.play();\n    }\n  }\n\n  /**\n   * Toggle play/pause state of the media\n   */\n  toggleMedia() {\n    if (this.isPlaying) {\n      this.pauseMedia();\n    } else {\n      this.playMedia();\n    }\n  }\n\n  playMedia() {\n    /** @type {HTMLIFrameElement | null} */\n    const iframe = this.querySelector('iframe[data-video-type]');\n    if (iframe) {\n      iframe.contentWindow?.postMessage(\n        iframe.dataset.videoType === 'youtube'\n          ? '{\"event\":\"command\",\"func\":\"playVideo\",\"args\":\"\"}'\n          : '{\"method\":\"play\"}',\n        '*'\n      );\n    } else {\n      this.querySelector('video')?.play();\n    }\n    this.isPlaying = true;\n    this.updatePlayPauseHint(this.isPlaying);\n  }\n\n  /**\n   * Pauses the media\n   */\n  pauseMedia() {\n    /** @type {HTMLIFrameElement | null} */\n    const iframe = this.querySelector('iframe[data-video-type]');\n\n    if (iframe) {\n      iframe.contentWindow?.postMessage(\n        iframe.dataset.videoType === 'youtube'\n          ? '{\"event\":\"command\",\"func\":\"' + 'pauseVideo' + '\",\"args\":\"\"}'\n          : '{\"method\":\"pause\"}',\n        '*'\n      );\n    } else {\n      this.querySelector('video')?.pause();\n    }\n    this.isPlaying = false;\n\n    // If we've already revealed the deferred media, we should toggle the play/pause hint\n    if (this.getAttribute('data-media-loaded')) {\n      this.updatePlayPauseHint(this.isPlaying);\n    }\n  }\n}\n\nif (!customElements.get('deferred-media')) {\n  customElements.define('deferred-media', DeferredMedia);\n}\n\n/**\n * A product model\n */\nclass ProductModel extends DeferredMedia {\n  #abortController = new AbortController();\n\n  loadContent() {\n    super.loadContent();\n\n    Shopify.loadFeatures([\n      {\n        name: 'model-viewer-ui',\n        version: '1.0',\n        onLoad: this.setupModelViewerUI.bind(this),\n      },\n    ]);\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback();\n    this.#abortController.abort();\n  }\n\n  pauseMedia() {\n    super.pauseMedia();\n    this.modelViewerUI?.pause();\n  }\n\n  playMedia() {\n    super.playMedia();\n    this.modelViewerUI?.play();\n  }\n\n  /**\n   * @param {Error[]} errors\n   */\n  async setupModelViewerUI(errors) {\n    if (errors) return;\n\n    if (!Shopify.ModelViewerUI) {\n      await this.#waitForModelViewerUI();\n    }\n\n    if (!Shopify.ModelViewerUI) return;\n\n    const element = this.querySelector('model-viewer');\n    if (!element) return;\n\n    const signal = this.#abortController.signal;\n\n    // Match the theme\u2019s graphite stage (poster images are baked on white).\n    element.style.setProperty('background-color', '#2e3238');\n    element.style.setProperty('--poster-color', '#2e3238');\n    element.setAttribute('environment-image', 'neutral');\n\n    this.modelViewerUI = new Shopify.ModelViewerUI(element);\n    if (!this.modelViewerUI) return;\n\n    this.playMedia();\n\n    // Track pointer events to detect taps\n    let pointerStartX = 0;\n    let pointerStartY = 0;\n\n    element.addEventListener(\n      'pointerdown',\n      (/** @type {PointerEvent} */ event) => {\n        pointerStartX = event.clientX;\n        pointerStartY = event.clientY;\n      },\n      { signal }\n    );\n\n    element.addEventListener(\n      'click',\n      (/** @type {PointerEvent} */ event) => {\n        const distanceX = Math.abs(event.clientX - pointerStartX);\n        const distanceY = Math.abs(event.clientY - pointerStartY);\n        const totalDistance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);\n\n        // Try to ensure that this is a tap, not a drag.\n        if (totalDistance < 10) {\n          // When the model is paused, it has its own button overlay for playing the model again.\n          // If we're receiving a click event, it means the model is playing, all we can do is pause it.\n          this.pauseMedia();\n        }\n      },\n      { signal }\n    );\n  }\n\n  /**\n   * Waits for Shopify.ModelViewerUI to be defined.\n   * This seems to be necessary for Safari since Shopify.ModelViewerUI is always undefined on the first try.\n   * @returns {Promise<void>}\n   */\n  async #waitForModelViewerUI() {\n    const maxAttempts = 10;\n    const interval = 50;\n\n    for (let i = 0; i < maxAttempts; i++) {\n      if (Shopify.ModelViewerUI) {\n        return;\n      }\n      await new Promise((resolve) => setTimeout(resolve, interval));\n    }\n  }\n}\n\nif (!customElements.get('product-model')) {\n  customElements.define('product-model', ProductModel);\n}\n"],
  "mappings": "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",
  "names": []
}
