{"version":3,"file":"shoppable-videos-section.min.js","mappings":"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","sources":["webpack://lange/../node_modules/@integrabeauty/assert/src/index.ts","webpack://lange/./lib/dom-ready.ts","webpack://lange/./sections/shoppable-videos/shoppable-video-player/index.ts","webpack://lange/./sections/shoppable-videos/index.ts"],"sourcesContent":["/**\n * Throws an assertion error if the given condition is falsy.\n *\n * @param expression any expression, although one that evaluates to true or false is preferred by\n * convention\n * @param message optional error text\n */\nexport function assert(expression: unknown, message?: string) {\n  if (!expression) {\n    throw new AssertionError(message);\n  }\n}\n\nexport class AssertionError extends Error {\n  constructor(message = 'Assertion error') {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n","/**\n * Releases the handle returned from the ready() function.\n *\n * This is idempotent. Additional calls are pointless but harmless.\n */\nexport type ReleaseHandle = () => void;\n\n/**\n * A subset of events that pertain to document ready state.\n */\ntype LoadingEventMap = Pick<DocumentEventMap, 'DOMContentLoaded' | 'load'>;\n\n/**\n * Calls the provided callback when the document is ready. This works both when the document is\n * already ready and when it is not, so callers do not have to care. This exists because it ensures\n * that each caller consistently expresses its dependency on document readiness, and to save a bit\n * of boilerplate.\n *\n * A document is considered ready when it has been parsed. This corresponds to the ready state of\n * 'interactive'. When the document has been fully loaded, the ready state is 'complete'. The\n * callback runs either way, so callers do not have to check which one they are in.\n *\n * Ready means the html is parsed and the DOM is there to query. It does not mean the page's other\n * scripts have run. Deferred scripts and module scripts run after the ready state turns\n * 'interactive' and before DOMContentLoaded fires, so a callback here can run before them. When a\n * caller needs some other script to have run already, this is the wrong tool, and it should wait on\n * whatever that script produces instead.\n *\n * That remedy has a catch of its own when the calling bundle is loaded with async. An async bundle\n * can execute after DOMContentLoaded has already fired, so a plain listener for that event never\n * runs and the work silently never happens. Nothing here can detect that, because by the time this\n * file executes the event is long gone. Catching it needs something registered at parse time,\n * before any bundle loads.\n *\n * There is a second trap right next to that one, and it applies only when waiting for\n * DOMContentLoaded. The ready state stays 'interactive' after DOMContentLoaded has fired, all the\n * way until 'complete', so the ready state cannot tell you whether DOMContentLoaded is still coming\n * or has already gone. Waiting for load is not exposed to this, because the ready state turns\n * 'complete' and load fires in the same task, so seeing 'complete' means load has fired too.\n *\n * When the document is already ready, the callback is invoked via setTimeout. This way, if the\n * callback throws, the error surfaces to the global error handler in a way that is similar to what\n * happens when an event listener throws.\n *\n * We might one day want to move this to an NPM library.\n *\n * Beware of a quirk here. setTimeout yields, and the busier the frontend, the longer it takes to\n * fire. Page load is exactly when the frontend is busiest. The timeout is a floor and not a\n * guarantee, so there is no real bound on how long that yield lasts. What this means is that using\n * this library can introduce more delay than you might expect. Compare code that runs on async load\n * and checks ready state and invokes immediately to code that runs after setTimeout. The former\n * runs in the same tick, the latter runs in \"some later tick\". That later tick can be surprisingly\n * much later. That can cause initial paint issues.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event\n * @see https://html.spec.whatwg.org/multipage/parsing.html#the-end\n */\nexport function ready(callback: (event?: Event) => void,\n  type: keyof LoadingEventMap = 'DOMContentLoaded') {\n  if (document.readyState === 'complete' || (type === 'DOMContentLoaded' &&\n    document.readyState === 'interactive')) {\n    const timer = setTimeout(callback, 0);\n    const handle: ReleaseHandle = () => clearTimeout(timer);\n    return handle;\n  } else {\n    addEventListener(type, callback);\n    const handle: ReleaseHandle = () => removeEventListener(type, callback);\n    return handle;\n  }\n}\n","import { assert } from '@integrabeauty/assert';\nimport type { CustomElement } from '@integrabeauty/custom-elements';\nimport { ready, type ReleaseHandle } from '../../../lib/dom-ready.js';\nimport html from './index.html';\nimport styles from './index.scss';\n\ninterface ShoppableVideo {\n  affiliate_display_name: string;\n\n  alt: string | null;\n  aspect_ratio: number;\n\n  /**\n   * url to the .vtt captions file\n   */\n  captions: string;\n\n  /**\n   * millisecond duration of the video\n   */\n  duration: number;\n\n  /**\n   * ID of the video media\n   */\n  id: number;\n\n  /**\n   * type of media file, should be 'video'\n   */\n  media_type: string;\n  metaobject_handle: string;\n  metaobject_id: number;\n  metaobject_type: string;\n  metaobject_url: string | null;\n  position: null;\n\n  /**\n   * URL of the poster image of the video\n   */\n  preview_image: string;\n  primary_variant: {\n    id: number;\n\n    /**\n     * URL of the variant image, or null when the section could not resolve one\n     */\n    image: string | null;\n    options: string;\n    product_id: number;\n  };\n  products: {\n    /**\n     * URL of product image, or null when the section could not resolve one\n     */\n    featured_image: string | null;\n    handle: string;\n    id: number;\n    subtitle: string;\n    title: string;\n  }[];\n  sources: {\n    /**\n     * file type\n     *\n     * @example 'mp4'\n     */\n    format: string;\n    height: number;\n    mime_type: string;\n    url: string;\n    width: number;\n  }[];\n\n  /**\n   * metaobject handles are a snake case composite of metaobject type and a special ID. This field\n   * isolates only the id and strips out the metaobject type. it's an alphanumeric value.\n   *\n   * @example 'r1o6yxen'\n   */\n  video_id: string;\n}\n\ninterface VideoPlayState {\n  [id: string]: 'paused' | 'playing' | 'promise-to-play';\n}\n\n/**\n * A fullscreen feed of short videos, in the style of Instagram/TikTok.\n *\n * Each video has a carousel of the products it features. The tap to view product details of one of\n * the products in the carousel is why this is called \"shoppable\" as that is low friction.\n *\n * It shows as a modal whose open state and current video are tied to the URL and browser history.\n * That lets a shared or advertised link open straight to a specific video, and lets back and\n * forward navigation open, close, or switch the player to match.\n *\n * The video list is read from a JSON script on the page.\n *\n * The shopper moves between videos by keyboard, mouse, or touchscreen.\n *\n * As a video is watched it emits progress events at a few view milestones, which other code\n * consumes for analytics.\n */\nclass ShoppableVideoPlayer extends HTMLElement implements CustomElement {\n  public static get observedAttributes() {\n    return [\n      'hidden'\n    ];\n  }\n\n  declare readonly dataset: {\n    /**\n     * Play the video with the specified id\n     */\n    videoId: string;\n  };\n\n  private declare videos: ShoppableVideo[];\n  private muted = false;\n  private paused = false;\n  private videoPlayState: VideoPlayState = {};\n  private watchedAnalyticsCache: { [id: string]: boolean; } = {};\n  private declare loaderTimer: ReturnType<typeof setTimeout>;\n  private declare scrollTimer: ReturnType<typeof setTimeout>;\n  private lastKnownScrollPosition = 0;\n  private lastKnownTouchPosition = 0;\n  private lastKnownProductScrollPosition = 0;\n\n  private videoWidth = 600;\n\n  private mouseDownOnProducts = false;\n\n  private onVideoEventBound = this.onVideoEvent.bind(this);\n  private onVideoClickBound = this.onVideoClick.bind(this);\n  private onPlayClickBound = this.onPlayClick.bind(this);\n  private onCaptionCueChangeBound = this.onCaptionCueChange.bind(this);\n  private onCaptionsControlClickBound = this.onCaptionsControlClick.bind(this);\n  private onCloseClickBound = this.onCloseClick.bind(this);\n  private onMuteClickBound = this.onMuteClick.bind(this);\n  private onPreviousClickBound = this.onPreviousClick.bind(this);\n  private onNextClickBound = this.onNextClick.bind(this);\n  private onWheelBound = this.onWheel.bind(this);\n  private onTouchStartBound = this.onTouchStart.bind(this);\n  private onTouchMoveBound = this.onTouchMove.bind(this);\n  private onMouseDownBound = this.onMouseDown.bind(this);\n  private onMouseMoveBound = this.onMouseMove.bind(this);\n  private onProductMoveBound = this.onProductMove.bind(this);\n  private onProductsWheelBound = this.onProductsWheel.bind(this);\n  private onPopStateBound = this.onPopState.bind(this);\n  private onReadyBound = this.onReady.bind(this);\n  private onWindowResizeBound = this.onWindowResize.bind(this);\n  private onScreenChangeBound = this.onScreenChange.bind(this);\n  private onKeyboardEventBound = this.onKeyboardEvent.bind(this);\n  private declare releaseReady: ReleaseHandle;\n\n  public connectedCallback() {\n    this.initializeShadowRoot();\n\n    addEventListener('popstate', this.onPopStateBound);\n\n    this.releaseReady = ready(this.onReadyBound);\n\n    this.addEventListener('keydown', this.onKeyboardEventBound);\n    this.addEventListener('keyup', this.onKeyboardEventBound);\n\n    addEventListener('resize', this.onWindowResizeBound);\n    addEventListener('change', this.onScreenChangeBound);\n\n    this.init();\n  }\n\n  public disconnectedCallback() {\n    if (this.releaseReady) {\n      this.releaseReady();\n    }\n\n    removeEventListener('popstate', this.onPopStateBound);\n\n    const playEl = this.shadowRoot.getElementById('play');\n    playEl?.removeEventListener('click', this.onPlayClickBound);\n\n    const closeEl = this.shadowRoot.getElementById('close');\n    closeEl?.removeEventListener('click', this.onCloseClickBound);\n\n    const muteEl = this.shadowRoot.getElementById('mute');\n    muteEl?.removeEventListener('click', this.onMuteClickBound);\n\n    const previousEl = this.shadowRoot.getElementById('previous');\n    previousEl?.removeEventListener('click', this.onPreviousClickBound);\n\n    const nextEl = this.shadowRoot.getElementById('next');\n    nextEl?.removeEventListener('click', this.onNextClickBound);\n\n    const videosEl = this.shadowRoot.querySelector<HTMLElement>('.videos');\n    videosEl?.removeEventListener('touchstart', this.onTouchStartBound);\n    videosEl?.removeEventListener('touchmove', this.onTouchMoveBound);\n    videosEl?.removeEventListener('mousedown', this.onMouseDownBound);\n    videosEl?.removeEventListener('mousemove', this.onMouseMoveBound);\n    videosEl?.removeEventListener('wheel', this.onWheelBound);\n\n    const captionsControlEl = this.shadowRoot.getElementById('captions-control');\n    captionsControlEl?.removeEventListener('click', this.onCaptionsControlClickBound);\n\n    this.removeEventListener('keydown', this.onKeyboardEventBound);\n    this.removeEventListener('keyup', this.onKeyboardEventBound);\n\n    removeEventListener('resize', this.onWindowResizeBound);\n    removeEventListener('change', this.onScreenChangeBound);\n\n    const videoEl = this.shadowRoot.querySelector<HTMLVideoElement>('.active video');\n    if (videoEl) {\n      this.removeVideoListeners(videoEl);\n    }\n\n    const productsEl = this.shadowRoot.querySelectorAll<HTMLElement>('.products.scrollable');\n    for (const productEl of productsEl) {\n      productEl.removeEventListener('wheel', this.onProductsWheelBound);\n    }\n  }\n\n  private initializeShadowRoot() {\n    if (!this.shadowRoot) {\n      this.attachShadow({ mode: 'open' });\n      this.shadowRoot.innerHTML = `<style>${styles}</style>${html}`;\n    }\n  }\n\n  private onKeyboardEvent(event: KeyboardEvent) {\n    if (event.type === 'keydown') {\n      return;\n    }\n\n    if (event.code === 'Escape') {\n      this.closePlayer();\n    } else if (['ArrowLeft', 'ArrowUp'].includes(event.code)) {\n      this.navigateToVideo(false);\n    } else if (['ArrowRight', 'ArrowDown'].includes(event.code)) {\n      this.navigateToVideo(true);\n    }\n  }\n\n  private showPlayer() {\n    const currentSection = this.closest('section');\n    if (currentSection instanceof HTMLElement) {\n      const carousel = currentSection.querySelector('carousel-element');\n\n      if (carousel instanceof HTMLElement) {\n        carousel.inert = true;\n      }\n    }\n\n    const event = new Event('modal-open-requested', { bubbles: true, composed: true });\n    this.dispatchEvent(event);\n\n    // put keyboard focus on the first interactive element\n    const closeButton = this.shadowRoot.querySelector<HTMLButtonElement>('#close');\n    closeButton.focus({ focusVisible: true });\n  }\n\n  public attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n    this.initializeShadowRoot();\n\n    if (this.isConnected && oldValue !== newValue) {\n      // We used to watch data-hidden here and treat any falsy newValue as un-hiding. That does not\n      // work with the native attribute, because setting it gives us an empty string, which is also\n      // falsy, so we would show the player when we meant to hide it. Read the property instead. It\n      // is already in sync with the attribute by the time we get here.\n\n      if (name === 'hidden' && !this.hidden) {\n        this.showPlayer();\n      }\n\n      if (!this.videos) {\n        this.init();\n      }\n\n      if (this.dataset.videoId) {\n        // close the player if there are no videos\n        if (this.videos.length === 0) {\n          this.closePlayer();\n          return;\n        }\n\n        let index = this.videos.findIndex(v => v.video_id === this.dataset.videoId);\n        if (index === -1) {\n          // If the video no longer exists (e.g., when a stale URL is shared or advertised),\n          // fallback to showing the first video\n          index = 0;\n        }\n\n        const videoId = this.videos[index].video_id;\n\n        this.activatePlayerAtIndex(index);\n        this.activateVideo(videoId);\n        this.updateVideoContainerWidth({ type: 'attributeChanged' });\n      }\n    }\n  }\n\n  private activatePlayerAtIndex(index: number) {\n    let prevIndex = index - 1;\n    if (prevIndex < 0) {\n      prevIndex = this.videos.length - 1;\n    }\n\n    let nextIndex = index + 1;\n    if (nextIndex >= this.videos.length) {\n      nextIndex = 0;\n    }\n\n    const videosEl = this.shadowRoot.querySelector('.videos');\n    const currentVideo = this.createVideoWrapperElement(this.videos[index], index);\n    videosEl.appendChild(currentVideo);\n\n    if (nextIndex !== index) {\n      const nextVideo = this.createVideoWrapperElement(this.videos[nextIndex], nextIndex);\n      videosEl.appendChild(nextVideo);\n    }\n\n    if ((prevIndex !== index) && (prevIndex !== nextIndex)) {\n      // we add previous in the end to allow current to load first\n      const previousVideo = this.createVideoWrapperElement(this.videos[prevIndex], prevIndex);\n      videosEl.insertBefore(previousVideo, currentVideo);\n    }\n  }\n\n  private showLoader() {\n    clearTimeout(this.loaderTimer);\n    this.loaderTimer = setTimeout(() => {\n      const loader = this.shadowRoot.getElementById('loader');\n      loader?.classList.remove('display-hide');\n    }, 300);\n  }\n\n  private hideLoader() {\n    clearTimeout(this.loaderTimer);\n\n    const loader = this.shadowRoot.getElementById('loader');\n    loader?.classList.add('display-hide');\n  }\n\n  private activateVideo(id: string) {\n    assert(id, 'activate video invalid id');\n\n    // Since the onScroll method will activate videos on each event emitted, we must first check if\n    // that video ID is already active before proceeding.\n    const activeEl = this.shadowRoot.querySelector<HTMLElement>('.video.active');\n    if (activeEl?.dataset.id === id) {\n      return;\n    }\n\n    // On load there is no initially active video, so we must confirm the element was found\n    if (activeEl) {\n      activeEl.classList.remove('active');\n      const videoEl = activeEl.querySelector('video');\n      videoEl.pause();\n      videoEl.textTracks[0].mode = 'disabled';\n      this.videoPlayState[videoEl.parentElement.dataset.id] = 'paused';\n      videoEl.currentTime = 0;\n      this.removeVideoListeners(videoEl);\n\n      const products = videoEl.parentElement.querySelector<HTMLElement>('.products');\n      if (products) {\n        products.inert = true;\n      }\n    }\n\n    const targetVideoDiv = this.shadowRoot.querySelector(`[data-id=\"${id}\"]`);\n    if (!targetVideoDiv) {\n      return;\n    }\n\n    targetVideoDiv.classList.add('active');\n\n    const targetProducts = targetVideoDiv.querySelector<HTMLElement>('.products');\n    if (targetProducts) {\n      targetProducts.inert = false;\n    }\n\n    const video = targetVideoDiv.querySelector('video');\n    this.addVideoListeners(video);\n    video.muted = this.muted;\n    video.textTracks[0].mode = 'showing';\n\n    if (this.dataset.videoId !== id) {\n      this.dataset.videoId = id;\n    }\n\n    updateUrlParamVideoId(id);\n    this.playVideo(video);\n  }\n\n  private navigateToVideo(forward: boolean) {\n    this.hideLoader();\n    const videos = this.shadowRoot.querySelector('.videos');\n    if (!videos) {\n      return;\n    }\n\n    const active = videos.querySelector<HTMLElement>('.video.active');\n    if (!active) {\n      return;\n    }\n\n    // Compute the index to which to navigate\n    let targetIndex = parseInt(active.dataset.index, 10) + (forward ? 1 : -1);\n\n    // Wrap around the index if out of bounds to achieve infinite looping.\n    if (forward) {\n      if (targetIndex >= this.videos.length) {\n        targetIndex = 0;\n      }\n    } else {\n      if (targetIndex < 0) {\n        targetIndex = this.videos.length - 1;\n      }\n    }\n\n    const target = videos.querySelector<HTMLElement>(`.video[data-index=\"${targetIndex}\"`);\n    this.activateVideo(target.dataset.id);\n\n    let addIndex = forward ? targetIndex + 1 : targetIndex - 1;\n    if (addIndex >= this.videos.length) {\n      addIndex = 0;\n    } else if (addIndex < 0) {\n      addIndex = this.videos.length - 1;\n    }\n\n    const toBeRemoved = forward ? videos.firstElementChild : videos.lastElementChild;\n    videos.removeChild(toBeRemoved);\n\n    let toBeAdded: Element;\n\n    if (toBeRemoved.classList.contains('active')) {\n      // if we have less than 4 videos, it can happen that the video to be removed is the active\n      // video; we need to add the active video back; it keeps the listeners and css classes\n      toBeAdded = toBeRemoved;\n    } else {\n      // create a new video element to be added\n      toBeAdded = this.createVideoWrapperElement(this.videos[addIndex], addIndex);\n    }\n\n    if (forward) {\n      videos.appendChild(toBeAdded);\n    } else {\n      videos.prepend(toBeAdded);\n    }\n  }\n\n  private onNextClick(_event: MouseEvent) {\n    this.navigateToVideo(true);\n  }\n\n  private onPreviousClick(_event: MouseEvent) {\n    this.navigateToVideo(false);\n  }\n\n  private onCaptionCueChange(_event: Event) {\n    const activeVideo = this.shadowRoot.querySelector<HTMLVideoElement>('.video.active video');\n    if (!activeVideo) {\n      return;\n    }\n\n    // Carefully obtain the cue. We have to be careful here because of poor browser support and\n    // strange situations where activeCues is undefined. We also have to hard cast the cue type from\n    // the base TextTrackCue type into the VTTCue subtype so as to gain access to the cue.text\n    // property. This hard cast is not safe.\n\n    let cue;\n    if (activeVideo.textTracks?.length > 0) {\n      const track = activeVideo.textTracks[0];\n      if (track?.activeCues?.length > 0) {\n        cue = <VTTCue>track.activeCues[0];\n      }\n    }\n\n    const captions = this.shadowRoot.querySelector('.video.active .captions');\n    captions.textContent = cue?.text || '';\n\n    if (cue?.text) {\n      captions.classList.remove('empty');\n    } else {\n      captions.classList.add('empty');\n    }\n  }\n\n  private onCaptionsControlClick(_event: MouseEvent) {\n    const captionsOffIcon = this.shadowRoot.getElementById('captions-off');\n    const captionsOnIcon = this.shadowRoot.getElementById('captions-on');\n    const videos = this.shadowRoot.querySelector('.videos');\n\n    // Toggle captions\n    if (videos.classList.contains('hide-captions')) {\n      captionsOffIcon.classList.add('display-hide');\n      captionsOnIcon.classList.remove('display-hide');\n      videos.classList.remove('hide-captions');\n      writeCaptionsEnabled(true);\n      this.setVideoStatus('Captions enabled');\n    } else {\n      captionsOffIcon.classList.remove('display-hide');\n      captionsOnIcon.classList.add('display-hide');\n      videos.classList.add('hide-captions');\n      writeCaptionsEnabled(false);\n      this.setVideoStatus('Captions disabled');\n    }\n  }\n\n  private onReady() {\n    if (!this.isConnected) {\n      return;\n    }\n\n    const playEl = this.shadowRoot.getElementById('play');\n    playEl.addEventListener('click', this.onPlayClickBound);\n\n    const closeEl = this.shadowRoot.getElementById('close');\n    closeEl.addEventListener('click', this.onCloseClickBound);\n\n    const muteEl = this.shadowRoot.getElementById('mute');\n    muteEl.addEventListener('click', this.onMuteClickBound);\n\n    const previousEl = this.shadowRoot.getElementById('previous');\n    previousEl.addEventListener('click', this.onPreviousClickBound);\n\n    const nextEl = this.shadowRoot.getElementById('next');\n    nextEl.addEventListener('click', this.onNextClickBound);\n\n    const videosEl = this.shadowRoot.querySelector<HTMLElement>('.videos');\n    videosEl.addEventListener('touchstart', this.onTouchStartBound, { passive: true });\n    videosEl.addEventListener('touchmove', this.onTouchMoveBound, { passive: false });\n    videosEl.addEventListener('mousedown', this.onMouseDownBound);\n    videosEl.addEventListener('mousemove', this.onMouseMoveBound);\n    videosEl.addEventListener('wheel', this.onWheelBound, { passive: false });\n\n    const captionsControlEl = this.shadowRoot.getElementById('captions-control');\n    captionsControlEl.addEventListener('click', this.onCaptionsControlClickBound);\n\n    const captionsEnabled = readCaptionsEnabled();\n    if (captionsEnabled) {\n      const captionsOffIcon = this.shadowRoot.getElementById('captions-off');\n      const captionsOnIcon = this.shadowRoot.getElementById('captions-on');\n      const videos = this.shadowRoot.querySelector('.videos');\n\n      captionsOffIcon.classList.add('display-hide');\n      captionsOnIcon.classList.remove('display-hide');\n      videos.classList.remove('hide-captions');\n    }\n\n    this.updateIconPositions();\n  }\n\n  private onScreenChange(event: Event) {\n    this.updateVideoContainerWidth(event);\n    this.updateIconPositions();\n  }\n\n  private onWindowResize(event: Event) {\n    this.updateVideoContainerWidth(event);\n    this.updateIconPositions();\n  }\n\n  private updateVideoContainerWidth(event: Pick<Event, 'type'>) {\n    const width = Math.floor(innerHeight * 0.56);\n    if (innerWidth < 600) {\n      this.videoWidth = innerWidth;\n    } else if (width < 600) {\n      this.videoWidth = width;\n    } else {\n      this.videoWidth = 600;\n    }\n\n    const containerEl = this.shadowRoot.querySelector<HTMLElement>('.container');\n    containerEl.style.minWidth = `${this.videoWidth}px`;\n\n    const progressEl = this.shadowRoot.querySelector<HTMLElement>('.progress-bar');\n    progressEl.style.maxWidth = `${this.videoWidth}px`;\n\n    const productEl = this.shadowRoot.querySelector<HTMLElement>('.active .product.only-card');\n    if (productEl) {\n      productEl.style.width = `${this.videoWidth - 32}px`;\n    } else {\n      if (event.type === 'resize' || event.type === 'change') {\n        // do not log, this is normal, e.g. when resizing with devtools open\n      }\n    }\n\n    const captionsEl = this.shadowRoot.querySelector<HTMLElement>('.active .captions');\n    if (captionsEl) {\n      captionsEl.style.width = `${this.videoWidth - 32}px`;\n    } else {\n      if (event.type === 'resize' || event.type === 'change') {\n        // do not log, this is normal, e.g. when resizing with devtools open\n      }\n    }\n\n    const box = containerEl.getBoundingClientRect();\n    progressEl.style.left = `${box.left}px`;\n  }\n\n  private updateIconPositions() {\n    const closeEl = this.shadowRoot.getElementById('close');\n    const menuEl = this.shadowRoot.querySelector<HTMLElement>('.menu');\n\n    if (innerWidth > 767) {\n      const offset = (innerWidth - this.videoWidth) / 2 - 60;\n\n      closeEl.style.left = `${offset}px`;\n      menuEl.style.right = `${offset}px`;\n    } else {\n      closeEl.style.left = '16px';\n      menuEl.style.right = '16px';\n    }\n  }\n\n  private onPopState(_event: Event) {\n    const urlParams = new URLSearchParams(location.search);\n    const videoId = urlParams.get('video_id');\n    if (videoId) {\n      this.dataset.videoId = videoId;\n      this.hidden = false;\n    } else if (!this.hidden) {\n      // Go through closePlayer rather than just hiding. Hiding on its own leaves the carousel inert\n      // and leaves the modal manager thinking a modal is still open, so the backdrop and the scroll\n      // lock stay up with nothing on screen to explain them.\n      this.closePlayer();\n    }\n  }\n\n  private onMouseDown(event: MouseEvent) {\n    const products = this.shadowRoot.querySelector<HTMLElement>('.active .products');\n    if (!products) {\n      return;\n    }\n\n    const bounds = products.getBoundingClientRect();\n\n    if (event.clientY > bounds.y && event.clientY < bounds.y + bounds.height &&\n      event.clientX > bounds.x && event.clientX < bounds.x + bounds.width) {\n      this.lastKnownTouchPosition = event.pageX - products.offsetLeft;\n      this.lastKnownProductScrollPosition = products.scrollLeft;\n      this.mouseDownOnProducts = true;\n\n      return;\n    }\n\n    this.mouseDownOnProducts = false;\n    this.lastKnownTouchPosition = event.clientY;\n  }\n\n  private onMouseMove(event: MouseEvent) {\n    if (event.buttons === 1 && this.mouseDownOnProducts) {\n      return;\n    }\n\n    if (this.mouseDownOnProducts && event.buttons !== 1) {\n      this.mouseDownOnProducts = false;\n    }\n\n    if (event.buttons !== 1 || (this.lastKnownTouchPosition === 0 && !this.mouseDownOnProducts)) {\n      return;\n    }\n\n    clearTimeout(this.scrollTimer);\n\n    this.scrollTimer = setTimeout(() => {\n      if (this.lastKnownTouchPosition < event.clientY) {\n        this.navigateToVideo(false);\n      } else if (this.lastKnownTouchPosition > event.clientY) {\n        this.navigateToVideo(true);\n      }\n\n      this.lastKnownScrollPosition = 0;\n      clearTimeout(this.scrollTimer);\n    }, 100);\n  }\n\n  private onProductMove(event: MouseEvent) {\n    if (event.buttons === 1 && this.mouseDownOnProducts) {\n      event.preventDefault();\n      const products = this.shadowRoot.querySelector<HTMLElement>('.active .products');\n      const x = event.pageX - products.offsetLeft;\n      const walk = (x - this.lastKnownTouchPosition) * 50;\n      const moveTo = this.lastKnownProductScrollPosition - walk;\n\n      products.scrollTo({\n        top: 0,\n        left: moveTo,\n        behavior: 'smooth'\n      });\n    }\n  }\n\n  private onTouchStart(event: TouchEvent) {\n    const products = this.shadowRoot.querySelector('.active .products');\n\n    // For unclear reasons this element is sometimes not found. In this case treat this as an in\n    // bounds touch and do nothing. This is not logged at the warning level because we do not care\n    // about error diagnostics for this error.\n    if (!products) {\n      console.log('active products element not found');\n      this.lastKnownTouchPosition = 0;\n      return;\n    }\n\n    const bounds = products.getBoundingClientRect();\n    if (event.touches[0].clientY > bounds.y &&\n      event.touches[0].clientY < bounds.y + bounds.height &&\n      event.touches[0].clientX > bounds.x &&\n      event.touches[0].clientX < bounds.x + bounds.width) {\n      this.lastKnownTouchPosition = 0;\n      return;\n    }\n\n    this.lastKnownTouchPosition = event.touches[0].clientY;\n  }\n\n  private onTouchMove(event: TouchEvent) {\n    if (this.lastKnownTouchPosition === 0) {\n      return;\n    }\n\n    clearTimeout(this.scrollTimer);\n\n    this.scrollTimer = setTimeout(() => {\n      if (this.lastKnownTouchPosition < event.touches[0].clientY) {\n        this.navigateToVideo(false);\n      } else if (this.lastKnownTouchPosition > event.touches[0].clientY) {\n        this.navigateToVideo(true);\n      }\n\n      this.lastKnownScrollPosition = 0;\n      clearTimeout(this.scrollTimer);\n    }, 100);\n  }\n\n  private isWheelEventInBounds(event: WheelEvent) {\n    const element = this.shadowRoot.querySelector('.active .products');\n    const rect = element.getBoundingClientRect();\n    return event.clientY > rect.y && event.clientY < rect.y + rect.height &&\n      event.clientX > rect.x && event.clientX < rect.x + rect.width;\n  }\n\n  private onWheel(event: WheelEvent) {\n    if (this.isWheelEventInBounds(event)) {\n      return;\n    }\n\n    event.preventDefault();\n    event.stopPropagation();\n\n    clearTimeout(this.scrollTimer);\n\n    if (!(event.target instanceof Element)) {\n      return;\n    }\n\n    if (this.lastKnownScrollPosition === 0) {\n      this.lastKnownScrollPosition = event.target.scrollTop;\n    }\n\n    this.scrollTimer = setTimeout(() => {\n      if (event.deltaY > 0) {\n        this.navigateToVideo(true);\n      } else if (event.deltaY < 0) {\n        this.navigateToVideo(false);\n      }\n\n      this.lastKnownScrollPosition = 0;\n      clearTimeout(this.scrollTimer);\n    }, 100);\n  }\n\n  private onProductsWheel(event: WheelEvent) {\n    if (!this.isWheelEventInBounds(event)) {\n      return;\n    }\n\n    event.preventDefault();\n    event.stopPropagation();\n\n    if (event.deltaY !== 0) {\n      const products = this.shadowRoot.querySelector('.active .products');\n      products.scrollLeft = products.scrollLeft - event.deltaY;\n    }\n  }\n\n  private playVideo(video: HTMLVideoElement) {\n    assert(video);\n\n    const playIcon = this.shadowRoot.getElementById('play');\n    if (this.paused) {\n      playIcon.classList.remove('display-hide');\n      return;\n    } else {\n      playIcon.classList.add('display-hide');\n    }\n\n    if (!video.paused) {\n      return;\n    }\n\n    if (this.hidden) {\n      return;\n    }\n\n    const id = video.parentElement.dataset.id;\n    const playState = this.videoPlayState[id];\n    if (playState && playState !== 'paused') {\n      return;\n    }\n\n    this.videoPlayState[id] = 'promise-to-play';\n\n    play(video)\n      .then(_ => {\n        this.videoPlayState[id] = 'playing';\n        this.setVideoStatus('Playing video ' + this.getVideoNumber(id));\n      })\n      .catch(error => {\n        this.videoPlayState[id] = video.paused || video.ended ?\n          'paused' :\n          undefined;\n\n        // Autoplay is best effort. Restrictive in-app browsers and low power mode refuse it even\n        // after a user gesture, and interrupted or unsupported sources reject too. None of it is\n        // actionable, so keep a debug breadcrumb and never warn.\n        console.debug(error);\n      });\n  }\n\n  /**\n   * Get the video number (1-based index) for the given video ID\n   *\n   * We do not have any titles or other identifiers for the videos, so we just use their position in\n   * the list when announcing which video is playing.\n   */\n  private getVideoNumber(id: string) {\n    const index = this.videos.findIndex(v => v.video_id === id);\n    return index + 1;\n  }\n\n  /**\n   * Sets the video status message for screen readers\n   */\n  private setVideoStatus(message: string) {\n    const statusEl = this.shadowRoot.getElementById('video-status');\n    if (statusEl) {\n      statusEl.textContent = message;\n    }\n  }\n\n  private onPlayClick(_event: MouseEvent) {\n    this.paused = false;\n    const video = this.shadowRoot.querySelector<HTMLVideoElement>('.active video');\n    this.playVideo(video);\n    const playIcon = this.shadowRoot.getElementById('play');\n    playIcon.classList.add('display-hide');\n  }\n\n  /**\n   * clicking on the video toggles play/pause\n   */\n  private onVideoClick(_event: MouseEvent) {\n    const video = this.shadowRoot.querySelector<HTMLVideoElement>('.active video');\n\n    if (video.paused) {\n      this.paused = false;\n      this.playVideo(video);\n    } else {\n      this.paused = true;\n      this.pauseVideo(video);\n    }\n  }\n\n  private pauseVideo(video: HTMLVideoElement) {\n    this.paused = true;\n    const videoId = video.parentElement.dataset.id;\n    video.pause();\n    this.videoPlayState[videoId] = 'paused';\n\n    this.setVideoStatus('Video paused');\n\n    const playIcon = this.shadowRoot.getElementById('play');\n    playIcon.classList.remove('display-hide');\n  }\n\n  private onTimeUpdate(event: Event) {\n    const video = event.target;\n\n    if (!(video instanceof HTMLVideoElement)) {\n      return;\n    }\n\n    if (!video.parentElement.classList.contains('active')) {\n      return;\n    }\n\n    const percentDuration = video.currentTime / video.duration * 100;\n    this.updateProgress(percentDuration);\n    this.trackViewMilestones(video.parentElement.dataset.id, percentDuration);\n  }\n\n  private updateProgress(percentDuration: number) {\n    const progress = this.shadowRoot.querySelector<HTMLElement>('.progress-value');\n    progress.style.width = percentDuration + '%';\n  }\n\n  /**\n   * Dispatches a viewed event the first time playback of a video passes each milestone. The 98\n   * percent milestone is cached under the key suffix 100.\n   */\n  private trackViewMilestones(id: string, percentDuration: number) {\n    const milestones = [\n      { key: `${id}_3`, reached: percentDuration > 3 },\n      { key: `${id}_50`, reached: percentDuration >= 50 },\n      { key: `${id}_100`, reached: percentDuration >= 98 }\n    ];\n\n    const videoObject = this.videos.find(video => video.video_id === id);\n\n    for (const milestone of milestones) {\n      if (this.watchedAnalyticsCache[milestone.key] || !milestone.reached) {\n        continue;\n      }\n\n      this.watchedAnalyticsCache[milestone.key] = true;\n\n      type Detail = WindowEventMap['shoppable-video-viewed']['detail'];\n      const viewEvent = new CustomEvent<Detail>('shoppable-video-viewed', {\n        bubbles: true,\n        composed: true,\n        detail: {\n          affiliate_display_name: videoObject.affiliate_display_name,\n          percent_viewed: percentDuration,\n          video_id: id\n        }\n      });\n      this.dispatchEvent(viewEvent);\n    }\n  }\n\n  private onMuteClick(_event: MouseEvent) {\n    const video = this.shadowRoot.querySelector<HTMLVideoElement>('.active video');\n    if (!video) {\n      return;\n    }\n\n    video.muted = !video.muted;\n    this.muted = video.muted;\n\n    const mute = this.shadowRoot.getElementById('mute');\n    if (video.muted) {\n      mute.querySelector('.volume-off').classList.remove('display-hide');\n      mute.querySelector('.volume-on').classList.add('display-hide');\n      this.setVideoStatus('Video muted');\n    } else {\n      mute.querySelector('.volume-off').classList.add('display-hide');\n      mute.querySelector('.volume-on').classList.remove('display-hide');\n      this.setVideoStatus('Video unmuted');\n    }\n  }\n\n  private onCloseClick(_event: MouseEvent) {\n    this.closePlayer();\n  }\n\n  private onVideoEvent(event: Event) {\n    if (event.type === 'play' || event.type === 'playing') {\n      this.hideLoader();\n      const playButton = this.shadowRoot.getElementById('play');\n      playButton.classList.add('display-hide');\n      return;\n    }\n\n    if (['waiting', 'error', 'stalled'].includes(event.type)) {\n      this.showLoader();\n    } else {\n      this.hideLoader();\n    }\n  }\n\n  private init() {\n    const mute = this.shadowRoot.getElementById('mute');\n    if (this.muted) {\n      mute.querySelector('.volume-off').classList.remove('display-hide');\n      mute.querySelector('.volume-on').classList.add('display-hide');\n    } else {\n      mute.querySelector('.volume-off').classList.add('display-hide');\n      mute.querySelector('.volume-on').classList.remove('display-hide');\n    }\n\n    // if already initialized do nothing\n    if (this.videos) {\n      return;\n    }\n\n    const scriptTag = document.getElementById('shoppable-video-data');\n    this.videos = <ShoppableVideo[]> JSON.parse(scriptTag.textContent);\n\n    for (const video of this.videos) {\n      this.videoPlayState[video.video_id] = 'paused';\n    }\n  }\n\n  private closePlayer() {\n    const playIcon = this.shadowRoot.getElementById('play');\n    playIcon.classList.add('display-hide');\n\n    const id = this.dataset.videoId;\n\n    this.hidden = true;\n    this.dataset.videoId = '';\n\n    const videosEl = this.shadowRoot.querySelector('.videos');\n    videosEl.innerHTML = '';\n    this.videoPlayState = {};\n\n    updateUrlParamVideoId();\n\n    type Detail = WindowEventMap['shoppable-video-player-closed']['detail'];\n    const event = new CustomEvent<Detail>('shoppable-video-player-closed', {\n      bubbles: true,\n      composed: true,\n      detail: { video_id: id }\n    });\n    this.dispatchEvent(event);\n\n    const currentSection = this.closest('section');\n    if (currentSection instanceof HTMLElement) {\n      const carousel = currentSection.querySelector('carousel-element');\n\n      if (carousel instanceof HTMLElement) {\n        carousel.inert = false;\n      }\n    }\n\n    const closeEvent = new Event('modal-close-requested', { bubbles: true, composed: true });\n    this.dispatchEvent(closeEvent);\n  }\n\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/video#events\n   */\n  private createVideoWrapperElement(video: ShoppableVideo, index: number) {\n    const videoWrapperEl = document.createElement('div');\n    videoWrapperEl.classList.add('video');\n    videoWrapperEl.dataset.id = video.video_id;\n    videoWrapperEl.dataset.index = index.toString();\n\n    const videoEl = this.createVideoElement(video);\n    videoWrapperEl.append(videoEl);\n\n    const productsEl = this.createProductsCarousel(video);\n    videoWrapperEl.append(productsEl);\n\n    const captionsDiv = document.createElement('div');\n    captionsDiv.classList.add('captions');\n    captionsDiv.classList.add('empty');\n\n    videoWrapperEl.append(captionsDiv);\n\n    return videoWrapperEl;\n  }\n\n  private addVideoListeners(videoEl: HTMLVideoElement) {\n    videoEl.addEventListener('click', this.onVideoClickBound);\n    videoEl.addEventListener('timeupdate', event => this.onTimeUpdate(event));\n    videoEl.addEventListener('canplay', this.onVideoEventBound);\n    videoEl.addEventListener('canplaythrough', this.onVideoEventBound);\n    videoEl.addEventListener('error', this.onVideoEventBound);\n    videoEl.addEventListener('loadeddata', this.onVideoEventBound);\n    videoEl.addEventListener('loadedmetadata', this.onVideoEventBound);\n    videoEl.addEventListener('loadstart', this.onVideoEventBound);\n    videoEl.addEventListener('play', this.onVideoEventBound);\n    videoEl.addEventListener('playing', this.onVideoEventBound);\n    videoEl.addEventListener('ratechange', this.onVideoEventBound);\n    videoEl.addEventListener('stalled', this.onVideoEventBound);\n    videoEl.addEventListener('suspend', this.onVideoEventBound);\n    videoEl.addEventListener('waiting', this.onVideoEventBound);\n  }\n\n  private removeVideoListeners(videoEl: HTMLVideoElement) {\n    videoEl.removeEventListener('click', this.onVideoClickBound);\n    videoEl.removeEventListener('timeupdate', event => this.onTimeUpdate(event));\n    videoEl.removeEventListener('canplay', this.onVideoEventBound);\n    videoEl.removeEventListener('canplaythrough', this.onVideoEventBound);\n    videoEl.removeEventListener('error', this.onVideoEventBound);\n    videoEl.removeEventListener('loadeddata', this.onVideoEventBound);\n    videoEl.removeEventListener('loadedmetadata', this.onVideoEventBound);\n    videoEl.removeEventListener('loadstart', this.onVideoEventBound);\n    videoEl.removeEventListener('play', this.onVideoEventBound);\n    videoEl.removeEventListener('playing', this.onVideoEventBound);\n    videoEl.removeEventListener('ratechange', this.onVideoEventBound);\n    videoEl.removeEventListener('stalled', this.onVideoEventBound);\n    videoEl.removeEventListener('suspend', this.onVideoEventBound);\n    videoEl.removeEventListener('waiting', this.onVideoEventBound);\n  }\n\n  private createVideoElement(video: ShoppableVideo) {\n    const videoEl = document.createElement('video');\n    videoEl.setAttribute('loop', 'loop');\n    videoEl.setAttribute('poster', video.preview_image);\n    videoEl.setAttribute('preload', 'auto');\n    videoEl.setAttribute('muted', '');\n    videoEl.setAttribute('playsinline', '');\n    videoEl.setAttribute('controlslist', 'nofullscreen');\n    videoEl.setAttribute('disablepictureinpicture', '');\n    videoEl.setAttribute('disableremoteplayback', '');\n\n    videoEl.controls = false;\n\n    const mp4Source = video.sources.find(source => source.mime_type === 'video/mp4');\n    if (mp4Source) {\n      videoEl.setAttribute('src', mp4Source.url);\n    }\n\n    const track = this.createTrackElement(video);\n    videoEl.append(track);\n\n    return videoEl;\n  }\n\n  private createTrackElement(video: ShoppableVideo) {\n    const track = document.createElement('track');\n    track.label = 'English';\n    track.kind = 'metadata';\n    track.srclang = 'en';\n    track.src = video.captions;\n    track.default = true;\n    track.addEventListener('cuechange', this.onCaptionCueChangeBound);\n    return track;\n  }\n\n  private createProductsCarousel(video: ShoppableVideo) {\n    const productsEl = document.createElement('div');\n    productsEl.classList.add('products');\n\n    // this will be cleared once the video becomes active\n    productsEl.inert = true;\n\n    if (video.products.length > 1) {\n      productsEl.classList.add('scrollable');\n      productsEl.addEventListener('wheel', this.onProductsWheelBound, { passive: false });\n    }\n\n    for (const product of video.products) {\n      const productEl = document.createElement('a');\n      productEl.classList.add('product');\n\n      // links and images can be dragged by default, which paints a translucent copy of the card\n      // while the user is trying to swipe the carousel sideways\n      productEl.draggable = false;\n\n      productEl.addEventListener('mousemove', this.onProductMoveBound);\n\n      const url = new URL(product.handle, location.origin);\n      if (video.primary_variant.product_id === product.id) {\n        url.searchParams.set('variant', video.primary_variant.id.toString());\n      }\n\n      productEl.href = url.toString();\n\n      let thumb = product.featured_image;\n      if (video.primary_variant.product_id === product.id) {\n        thumb = video.primary_variant.image;\n      }\n\n      const productImage = document.createElement('img');\n      // thumb is null when the section could not resolve a product image, so skip src rather than\n      // request /null\n      if (thumb) {\n        productImage.setAttribute('src', thumb);\n      }\n\n      productImage.setAttribute('alt', '');\n\n      // turning off drag on the link does not cover the image inside it\n      productImage.draggable = false;\n\n      productEl.append(productImage);\n\n      const titleEl = document.createElement('div');\n      titleEl.classList.add('title-subtitle');\n\n      let title = product.title;\n      if (video.primary_variant.product_id === product.id) {\n        title += ` ${video.primary_variant.options}`;\n      }\n\n      const productTitle = document.createElement('h2');\n      productTitle.innerHTML = title;\n\n      titleEl.appendChild(productTitle);\n\n      const productSubTitle = document.createElement('h3');\n      productSubTitle.textContent = product.subtitle;\n\n      titleEl.appendChild(productSubTitle);\n\n      productEl.append(titleEl);\n\n      productsEl.append(productEl);\n    }\n\n    return productsEl;\n  }\n}\n\n/**\n * Do not inline this function. It provides several benefits.\n */\nasync function play(video: HTMLVideoElement) {\n  return video.play();\n}\n\nfunction readCaptionsEnabled() {\n  try {\n    return localStorage.getItem('video_captions_enabled') === 'true';\n  } catch (error) {\n    // ignore\n  }\n}\n\nfunction writeCaptionsEnabled(enabled: boolean) {\n  try {\n    localStorage.setItem('video_captions_enabled', enabled ? 'true' : 'false');\n  } catch (error) {\n    // ignore\n  }\n}\n\n/**\n * Either set or clear the video parameter id. There are both use cases to support. For example,\n * when closing the player we want to revert back to the url without the parameter in the url.\n */\nfunction updateUrlParamVideoId(videoId?: string) {\n  const url = new URL(location.href);\n  const data: Record<any, any> = {};\n\n  if (videoId) {\n    url.searchParams.set('video_id', videoId);\n    data.video_id = videoId;\n  } else {\n    url.searchParams.delete('video_id');\n  }\n\n  history.replaceState(data, '', url);\n}\n\ntype ShoppableVideoViewedEvent = CustomEvent<{\n  affiliate_display_name?: string;\n  percent_viewed?: number;\n  video_id: string;\n}>;\n\ntype ShoppableVideoPlayerClosedEvent = CustomEvent<{\n  video_id: string;\n}>;\n\ndeclare global {\n  interface GlobalEventHandlersEventMap {\n    'shoppable-video-player-closed': ShoppableVideoPlayerClosedEvent;\n    'shoppable-video-viewed': ShoppableVideoViewedEvent;\n  }\n\n  interface HTMLElementTagNameMap {\n    'shoppable-video-player': ShoppableVideoPlayer;\n  }\n}\n\nif (!customElements.get('shoppable-video-player')) {\n  customElements.define('shoppable-video-player', ShoppableVideoPlayer);\n}\n","import './shoppable-video-player/index.js';\nimport { ready } from '../../lib/dom-ready.js';\n\nfunction updateVideoId() {\n  const player = document.querySelector('shoppable-video-player');\n\n  // The player element is rendered by the same template that loads this script, so it should always\n  // be present here. In practice we see rare nulls in the wild on iOS Safari, most likely a\n  // navigation or bfcache teardown race firing this deferred handler against a document whose\n  // section subtree is momentarily gone. Guard rather than crash.\n\n  if (!player) {\n    return;\n  }\n\n  const params = new URLSearchParams(location.search);\n  const videoId = params.get('video_id');\n  if (videoId) {\n    player.dataset.videoId = videoId;\n    player.hidden = false;\n  } else {\n    player.dataset.videoId = '';\n    player.hidden = true;\n  }\n}\n\nfunction onSlideClicked(event: GlobalEventHandlersEventMap['carousel-slide-clicked']) {\n  const button = event.detail?.slide.querySelector('button');\n  if (!button?.dataset.videoId) {\n    return;\n  }\n\n  const focusEvent = new Event('focus-stack-push-requested', {\n    bubbles: true,\n    composed: true\n  });\n  button.dispatchEvent(focusEvent);\n\n  const player = document.querySelector('shoppable-video-player');\n  if (player) {\n    player.dataset.videoId = button.dataset.videoId;\n    player.hidden = false;\n  }\n}\n\n/**\n * After the player is closed, moves the carousel back to the slide with the video that was playing.\n */\nfunction onPlayerClosed(event: GlobalEventHandlersEventMap['shoppable-video-player-closed']) {\n  const carousel = document.querySelector<HTMLElement>('#shoppable-videos-carousel');\n  if (!carousel) {\n    return;\n  }\n\n  const videoId = event.detail.video_id;\n  const slides = carousel.querySelectorAll('[data-carousel-slide]');\n  for (const [index, slide] of slides.entries()) {\n    if (slide.querySelector(`[data-video-id=\"${videoId}\"]`)) {\n      carousel.dataset.go = index.toString();\n      return;\n    }\n  }\n}\n\nfunction onReady() {\n  updateVideoId();\n\n  const carousel = document.querySelector<HTMLElement>('#shoppable-videos-carousel');\n  if (!carousel) {\n    return;\n  }\n\n  carousel.addEventListener('carousel-slide-clicked', onSlideClicked);\n  addEventListener('shoppable-video-player-closed', onPlayerClosed);\n}\n\nready(onReady);\n"],"names":["assert","expression","message","AssertionError","Error","constructor","super","this","name","ready","callback","type","document","readyState","timer","setTimeout","clearTimeout","addEventListener","removeEventListener","ShoppableVideoPlayer","HTMLElement","observedAttributes","muted","paused","videoPlayState","watchedAnalyticsCache","lastKnownScrollPosition","lastKnownTouchPosition","lastKnownProductScrollPosition","videoWidth","mouseDownOnProducts","onVideoEventBound","onVideoEvent","bind","onVideoClickBound","onVideoClick","onPlayClickBound","onPlayClick","onCaptionCueChangeBound","onCaptionCueChange","onCaptionsControlClickBound","onCaptionsControlClick","onCloseClickBound","onCloseClick","onMuteClickBound","onMuteClick","onPreviousClickBound","onPreviousClick","onNextClickBound","onNextClick","onWheelBound","onWheel","onTouchStartBound","onTouchStart","onTouchMoveBound","onTouchMove","onMouseDownBound","onMouseDown","onMouseMoveBound","onMouseMove","onProductMoveBound","onProductMove","onProductsWheelBound","onProductsWheel","onPopStateBound","onPopState","onReadyBound","onReady","onWindowResizeBound","onWindowResize","onScreenChangeBound","onScreenChange","onKeyboardEventBound","onKeyboardEvent","connectedCallback","initializeShadowRoot","releaseReady","init","disconnectedCallback","playEl","shadowRoot","getElementById","closeEl","muteEl","previousEl","nextEl","videosEl","querySelector","captionsControlEl","videoEl","removeVideoListeners","productsEl","querySelectorAll","productEl","attachShadow","mode","innerHTML","event","code","closePlayer","includes","navigateToVideo","showPlayer","currentSection","closest","carousel","inert","Event","bubbles","composed","dispatchEvent","focus","focusVisible","attributeChangedCallback","oldValue","newValue","isConnected","hidden","videos","dataset","videoId","length","index","findIndex","v","video_id","activatePlayerAtIndex","activateVideo","updateVideoContainerWidth","prevIndex","nextIndex","currentVideo","createVideoWrapperElement","appendChild","nextVideo","previousVideo","insertBefore","showLoader","loaderTimer","loader","classList","remove","hideLoader","add","id","activeEl","pause","textTracks","parentElement","currentTime","products","targetVideoDiv","targetProducts","video","addVideoListeners","updateUrlParamVideoId","playVideo","forward","active","targetIndex","parseInt","target","addIndex","toBeRemoved","firstElementChild","lastElementChild","toBeAdded","removeChild","contains","prepend","_event","_activeVideo$textTrac","_cue","_cue2","activeVideo","cue","_track$activeCues","track","activeCues","captions","textContent","text","captionsOffIcon","captionsOnIcon","writeCaptionsEnabled","setVideoStatus","passive","localStorage","getItem","error","readCaptionsEnabled","updateIconPositions","width","Math","floor","innerHeight","innerWidth","containerEl","style","minWidth","progressEl","maxWidth","captionsEl","box","getBoundingClientRect","left","menuEl","offset","right","URLSearchParams","location","search","get","bounds","clientY","y","height","clientX","x","pageX","offsetLeft","scrollLeft","buttons","scrollTimer","preventDefault","walk","moveTo","scrollTo","top","behavior","console","log","touches","isWheelEventInBounds","rect","stopPropagation","Element","scrollTop","deltaY","playIcon","playState","async","play","then","_","getVideoNumber","catch","ended","undefined","debug","statusEl","pauseVideo","onTimeUpdate","HTMLVideoElement","percentDuration","duration","updateProgress","trackViewMilestones","milestones","key","reached","videoObject","find","milestone","viewEvent","CustomEvent","detail","affiliate_display_name","percent_viewed","mute","scriptTag","JSON","parse","closeEvent","videoWrapperEl","createElement","toString","createVideoElement","append","createProductsCarousel","captionsDiv","setAttribute","preview_image","controls","mp4Source","sources","source","mime_type","url","createTrackElement","label","kind","srclang","src","default","product","draggable","URL","handle","origin","primary_variant","product_id","searchParams","set","href","thumb","featured_image","image","productImage","titleEl","title","options","productTitle","productSubTitle","subtitle","enabled","setItem","data","delete","history","replaceState","onSlideClicked","_event$detail","button","slide","focusEvent","player","onPlayerClosed","slides","entries","go","customElements","define","updateVideoId"],"sourceRoot":""}