{"version":3,"sources":["jsdelivr-header.js","/npm/@justinribeiro/lite-youtube@1.9.0/lite-youtube.ts"],"names":[],"mappings":"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","file":"/npm/@justinribeiro/lite-youtube@1.9.0/lite-youtube.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@justinribeiro/lite-youtube@1.9.0/lite-youtube.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","/**\n *\n * The shadowDom / Intersection Observer version of Paul's concept:\n * https://github.com/paulirish/lite-youtube-embed\n *\n * A lightweight YouTube embed. Still should feel the same to the user, just\n * MUCH faster to initialize and paint.\n *\n * Thx to these as the inspiration\n *   https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html\n *   https://autoplay-youtube-player.glitch.me/\n *\n * Once built it, I also found these (👍👍):\n *   https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube\n *   https://github.com/Daugilas/lazyYT https://github.com/vb/lazyframe\n */\nexport class LiteYTEmbed extends HTMLElement {\n  shadowRoot!: ShadowRoot;\n  private domRefFrame!: HTMLDivElement;\n  private domRefImg!: {\n    fallback: HTMLImageElement;\n    webp: HTMLSourceElement;\n    jpeg: HTMLSourceElement;\n  };\n  private domRefPlayButton!: HTMLButtonElement;\n  private static isPreconnected = false;\n  private isIframeLoaded = false;\n  private isPlaylistThumbnailLoaded = false;\n\n  constructor() {\n    super();\n    this.setupDom();\n  }\n\n  static get observedAttributes(): string[] {\n    return ['videoid', 'playlistid', 'videoplay', 'videotitle'];\n  }\n\n  connectedCallback(): void {\n    this.addEventListener(\n      'pointerover',\n      () => LiteYTEmbed.warmConnections(this),\n      {\n        once: true,\n      },\n    );\n\n    this.addEventListener('click', () => this.addIframe());\n  }\n\n  get videoId(): string {\n    return encodeURIComponent(this.getAttribute('videoid') || '');\n  }\n\n  set videoId(id: string) {\n    this.setAttribute('videoid', id);\n  }\n\n  get playlistId(): string {\n    return encodeURIComponent(this.getAttribute('playlistid') || '');\n  }\n\n  set playlistId(id: string) {\n    this.setAttribute('playlistid', id);\n  }\n\n  get videoTitle(): string {\n    return this.getAttribute('videotitle') || 'Video';\n  }\n\n  set videoTitle(title: string) {\n    this.setAttribute('videotitle', title);\n  }\n\n  get videoPlay(): string {\n    return this.getAttribute('videoplay') || 'Play';\n  }\n\n  set videoPlay(name: string) {\n    this.setAttribute('videoplay', name);\n  }\n\n  get videoStartAt(): string {\n    return this.getAttribute('videoStartAt') || '0';\n  }\n\n  get autoLoad(): boolean {\n    return this.hasAttribute('autoload');\n  }\n\n  get autoPause(): boolean {\n    return this.hasAttribute('autopause');\n  }\n\n  get noCookie(): boolean {\n    return this.hasAttribute('nocookie');\n  }\n\n  get posterQuality(): string {\n    return this.getAttribute('posterquality') || 'hqdefault';\n  }\n\n  get posterLoading(): HTMLImageElement['loading'] {\n    return (\n      (this.getAttribute('posterloading') as HTMLImageElement['loading']) ||\n      'lazy'\n    );\n  }\n\n  get params(): string {\n    return `start=${this.videoStartAt}&${this.getAttribute('params')}`;\n  }\n\n  set params(opts: string) {\n    this.setAttribute('params', opts);\n  }\n\n  set posterQuality(opts: string) {\n    this.setAttribute('posterquality', opts);\n  }\n\n  get disableNoscript(): boolean {\n    return this.hasAttribute('disablenoscript');\n  }\n\n  /**\n   * Define our shadowDOM for the component\n   */\n  private setupDom(): void {\n    const shadowDom = this.attachShadow({ mode: 'open' });\n    let nonce = '';\n    if (window.liteYouTubeNonce) {\n      nonce = `nonce=\"${window.liteYouTubeNonce}\"`;\n    }\n    shadowDom.innerHTML = `\n      <style ${nonce}>\n        :host {\n          --aspect-ratio: var(--lite-youtube-aspect-ratio, 16 / 9);\n          --aspect-ratio-short: var(--lite-youtube-aspect-ratio-short, 9 / 16);\n          --frame-shadow-visible: var(--lite-youtube-frame-shadow-visible, yes);\n          contain: content;\n          display: block;\n          position: relative;\n          width: 100%;\n          aspect-ratio: var(--aspect-ratio);\n        }\n\n        @media (max-width: 40em) {\n          :host([short]) {\n            aspect-ratio: var(--aspect-ratio-short);\n          }\n        }\n\n        #frame, #fallbackPlaceholder, iframe {\n          position: absolute;\n          width: 100%;\n          height: 100%;\n          left: 0;\n          top: 0;\n        }\n\n        #frame {\n          cursor: pointer;\n        }\n\n        #fallbackPlaceholder, slot[name=image]::slotted(*) {\n          object-fit: cover;\n          width: 100%;\n          height: 100%;\n        }\n\n        @container style(--frame-shadow-visible: yes) {\n          #frame::before {\n            content: '';\n            display: block;\n            position: absolute;\n            top: 0;\n            background-image: linear-gradient(180deg, #111 -20%, transparent 90%);\n            height: 60px;\n            width: 100%;\n            z-index: 1;\n          }\n        }\n\n        #playButton {\n          width: 68px;\n          height: 48px;\n          background-color: transparent;\n          background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 68 48\"><path d=\"M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z\" fill=\"red\"/><path d=\"M45 24 27 14v20\" fill=\"white\"/></svg>');\n          z-index: 1;\n          border: 0;\n          border-radius: inherit;\n        }\n\n        #playButton:before {\n          content: '';\n          border-style: solid;\n          border-width: 11px 0 11px 19px;\n          border-color: transparent transparent transparent #fff;\n        }\n\n        #playButton,\n        #playButton:before {\n          position: absolute;\n          top: 50%;\n          left: 50%;\n          transform: translate3d(-50%, -50%, 0);\n          cursor: inherit;\n        }\n\n        /* Post-click styles */\n        .activated {\n          cursor: unset;\n        }\n\n        #frame.activated::before,\n        #frame.activated > #playButton {\n          display: none;\n        }\n      </style>\n      <div id=\"frame\">\n        <picture>\n          <slot name=\"image\">\n            <source id=\"webpPlaceholder\" type=\"image/webp\">\n            <source id=\"jpegPlaceholder\" type=\"image/jpeg\">\n            <img id=\"fallbackPlaceholder\" referrerpolicy=\"origin\" loading=\"lazy\">\n          </slot>\n        </picture>\n        <button id=\"playButton\" part=\"playButton\"></button>\n      </div>\n    `;\n    this.domRefFrame = shadowDom.querySelector<HTMLDivElement>('#frame')!;\n    this.domRefImg = {\n      fallback: shadowDom.querySelector('#fallbackPlaceholder')!,\n      webp: shadowDom.querySelector('#webpPlaceholder')!,\n      jpeg: shadowDom.querySelector('#jpegPlaceholder')!,\n    };\n    this.domRefPlayButton = shadowDom.querySelector('#playButton')!;\n  }\n\n  /**\n   * Parse our attributes and fire up some placeholders\n   */\n  private setupComponent(): void {\n    // If the named slot is not empty, then we save the network requests and\n    // don't fire up the selector; we use assignedNodes() since we're using\n    // default slot elements for the picture\n    const hasImgSlot: HTMLSlotElement =\n      this.shadowRoot.querySelector('slot[name=image]')!;\n    if (hasImgSlot.assignedNodes().length === 0) {\n      this.initImagePlaceholder();\n    }\n\n    this.domRefPlayButton.setAttribute(\n      'aria-label',\n      `${this.videoPlay}: ${this.videoTitle}`,\n    );\n    this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`);\n\n    if (this.autoLoad || this.isYouTubeShort() || this.autoPause) {\n      this.initIntersectionObserver();\n    }\n\n    if (!this.disableNoscript) {\n      this.injectSearchNoScript();\n    }\n  }\n\n  /**\n   * Lifecycle method that we use to listen for attribute changes to period\n   * @param {*} name\n   * @param {*} oldVal\n   * @param {*} newVal\n   */\n  attributeChangedCallback(\n    name: string,\n    oldVal: unknown,\n    newVal: unknown,\n  ): void {\n    if (oldVal !== newVal) {\n      // Reset playlist thumbnail flag if playlistid changes to a different non-null value\n      if (name === 'playlistid' && oldVal !== null && oldVal !== newVal) {\n        this.isPlaylistThumbnailLoaded = false;\n      }\n\n      this.setupComponent();\n\n      // if we have a previous iframe, remove it and the activated class\n      if (this.domRefFrame.classList.contains('activated')) {\n        this.domRefFrame.classList.remove('activated');\n        this.shadowRoot.querySelector('iframe')!.remove();\n        this.isIframeLoaded = false;\n      }\n    }\n  }\n\n  // h/t @paulirish et al\n  // https://github.com/paulirish/lite-youtube-embed/issues/105\n  // differs in that we inject into the lightdom above any other nodes so our\n  // slots or fallbacks still work\n  private injectSearchNoScript(): void {\n    const eleNoScript = document.createElement('noscript');\n    this.prepend(eleNoScript);\n    eleNoScript.innerHTML = this.generateIframe();\n  }\n\n  private generateIframe(isIntersectionObserver = false): string {\n    let autoplay = isIntersectionObserver ? 0 : 1;\n    // autopause needs the postMessage() in the iframe, so you have to enable\n    // the jsapi\n    let autoPause = this.autoPause ? '&enablejsapi=1' : '';\n    const wantsNoCookie = this.noCookie ? '-nocookie' : '';\n    let embedTarget;\n    if (this.playlistId) {\n      embedTarget = `?listType=playlist&list=${this.playlistId}&`;\n    } else {\n      embedTarget = `${this.videoId}?`;\n    }\n\n    // Oh wait, you're a YouTube short, so let's try to make you more workable\n    if (this.isYouTubeShort()) {\n      this.params = `loop=1&mute=1&modestbranding=1&playsinline=1&rel=0&enablejsapi=1&playlist=${this.videoId}`;\n      autoplay = 1;\n    }\n\n    return `\n<iframe credentialless frameborder=\"0\" title=\"${this.videoTitle}\"\n  referrerpolicy=\"strict-origin-when-cross-origin\"\n  allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen\n  src=\"https://www.youtube${wantsNoCookie}.com/embed/${embedTarget}autoplay=${autoplay}${autoPause}&${this.params}\"\n></iframe>`;\n  }\n\n  /**\n   * Inject the iframe into the component body\n   * @param {boolean} isIntersectionObserver\n   */\n  private addIframe(isIntersectionObserver = false): void {\n    if (!this.isIframeLoaded) {\n      // Don't autoplay the intersection observer injection, it's weird\n      const iframeHTML = this.generateIframe(isIntersectionObserver);\n\n      this.domRefFrame.insertAdjacentHTML('beforeend', iframeHTML);\n      this.domRefFrame.classList.add('activated');\n      this.isIframeLoaded = true;\n      this.attemptShortAutoPlay();\n      this.dispatchEvent(\n        new CustomEvent('liteYoutubeIframeLoaded', {\n          detail: {\n            videoId: this.videoId,\n          },\n          bubbles: true,\n          cancelable: true,\n        }),\n      );\n    }\n  }\n\n  /**\n   * Setup the placeholder image for the component\n   */\n  private initImagePlaceholder(): void {\n    if (this.playlistId && !this.videoId) {\n      this.loadPlaylistThumbnail();\n    } else {\n      this.testPosterImage();\n    }\n\n    this.domRefImg.fallback.setAttribute(\n      'aria-label',\n      `${this.videoPlay}: ${this.videoTitle}`,\n    );\n    this.domRefImg?.fallback?.setAttribute(\n      'alt',\n      `${this.videoPlay}: ${this.videoTitle}`,\n    );\n  }\n\n  /**\n   * Load playlist thumbnail using YouTube oEmbed API\n   */\n  private async loadPlaylistThumbnail(): Promise<void> {\n    if (this.isPlaylistThumbnailLoaded) {\n      return;\n    }\n    this.isPlaylistThumbnailLoaded = true;\n\n    try {\n      const oEmbedUrl = `https://www.youtube.com/oembed?url=https://www.youtube.com/playlist?list=${this.playlistId}&format=json`;\n      const response = await fetch(oEmbedUrl);\n\n      if (!response.ok) {\n        throw new Error(`Failed to fetch playlist thumbnail: ${response.status}`);\n      }\n\n      const data = await response.json();\n\n      if (data.thumbnail_url) {\n        // Extract video ID from thumbnail URL to use with existing image loading logic\n        const thumbnailUrl = data.thumbnail_url;\n        const videoIdMatch = thumbnailUrl.match(/\\/vi\\/([^\\/]+)\\//);\n\n        if (videoIdMatch) {\n          const extractedVideoId = videoIdMatch[1];\n          this.loadThumbnailImages(extractedVideoId);\n        } else {\n          // Fallback: use the direct thumbnail URL\n          this.domRefImg.fallback.src = thumbnailUrl;\n          this.domRefImg.fallback.loading = this.posterLoading;\n        }\n      }\n    } catch (error) {\n      console.warn('Failed to load playlist thumbnail:', error);\n      // Fallback to a default placeholder or empty state\n    }\n  }\n\n  /**\n   * Load thumbnail images for a given video ID\n   */\n  private loadThumbnailImages(videoId: string): void {\n    const posterUrlWebp = `https://i.ytimg.com/vi_webp/${videoId}/${this.posterQuality}.webp`;\n    this.domRefImg.webp.srcset = posterUrlWebp;\n\n    const posterUrlJpeg = `https://i.ytimg.com/vi/${videoId}/${this.posterQuality}.jpg`;\n    this.domRefImg.jpeg.srcset = posterUrlJpeg;\n    this.domRefImg.fallback.src = posterUrlJpeg;\n    this.domRefImg.fallback.loading = this.posterLoading;\n  }\n\n  /**\n   * Slightly varied approach for our shadowDOM, but identical lookup approach to\n   * paulirish's https://github.com/paulirish/lite-youtube-embed\n   *\n   * Note, this won't run if the named slot=image is defined\n   */\n  private async testPosterImage(): Promise<void> {\n    setTimeout(() => {\n      const webpUrl = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`;\n      const img = new Image();\n      img.fetchPriority = 'low'; // low priority to reduce network contention\n      img.referrerPolicy = 'origin'; // Not 100% sure it's needed, but https://github.com/ampproject/amphtml/pull/3940\n      img.src = webpUrl;\n      img.onload = async e => {\n        const target = e.target as HTMLImageElement;\n        // A pretty ugly hack since onerror won't fire on YouTube image 404.\n        // This is (probably) due to Youtube's style of returning data even with\n        // a 404 status. That data is a 120x90 placeholder image.\n        const noPoster =\n          target?.naturalHeight == 90 && target?.naturalWidth == 120;\n\n        // Diverge: this differs from Paul's in that I have a specific opinion\n        // about the fallback, given that we allow <slot> overriding and that\n        // having tested this against a lot of different cases, the safest\n        // fallback with respect to a missing poster appears to be the hqdefault\n        // even in cases where the maxresdefault for a JPG (which I find\n        // _doesn't_ actually always exist for the JPG case either as reported\n        // by some folks)\n        if (noPoster) {\n          this.posterQuality = 'hqdefault';\n        }\n\n        this.loadThumbnailImages(this.videoId);\n      };\n    }, 100);\n  }\n\n  /**\n   * Setup the Intersection Observer to load the iframe when scrolled into view\n   */\n  private initIntersectionObserver(): void {\n    const options = {\n      root: null,\n      rootMargin: '0px',\n      threshold: 0,\n    };\n\n    const observer = new IntersectionObserver((entries, observer) => {\n      entries.forEach(entry => {\n        if (entry.isIntersecting && !this.isIframeLoaded) {\n          LiteYTEmbed.warmConnections(this);\n          this.addIframe(true);\n          observer.unobserve(this);\n        }\n      });\n    }, options);\n\n    observer.observe(this);\n\n    // this needs the iframe loaded, so it has to run post the IO load at the\n    // least otherwise things will break\n    if (this.autoPause) {\n      const windowPause = new IntersectionObserver(\n        (e, o) => {\n          e.forEach(entry => {\n            if (entry.intersectionRatio !== 1) {\n              this.shadowRoot\n                .querySelector('iframe')\n                ?.contentWindow?.postMessage(\n                  '{\"event\":\"command\",\"func\":\"pauseVideo\",\"args\":\"\"}',\n                  '*',\n                );\n            }\n          });\n        },\n        { threshold: 1 },\n      );\n      windowPause.observe(this);\n    }\n  }\n\n  /**\n   * This is a terrible hack to attempt to get YouTube Short-like autoplay on\n   * mobile viewports. It's this way because:\n   * 1. YouTube's Iframe embed does not offer determinism when loading\n   * 2. Attempting to use onYouTubeIframeAPIReady() does not work in 99% of\n   *    cases\n   * 3. You can _technically_ load the Frame API library and do more advanced\n   *    things, but I don't want to burn the thread of the wire with its\n   *    shenanigans since this an edge case.\n   * @private\n   */\n  private attemptShortAutoPlay() {\n    if (this.isYouTubeShort()) {\n      setTimeout(() => {\n        this.shadowRoot\n          .querySelector('iframe')\n          ?.contentWindow?.postMessage(\n            '{\"event\":\"command\",\"func\":\"' + 'playVideo' + '\",\"args\":\"\"}',\n            '*',\n          );\n        // for youtube video recording demo\n      }, 2000);\n    }\n  }\n\n  /**\n   * A hacky attr check and viewport peek to see if we're going to try to enable\n   * a more friendly YouTube Short style loading\n   * @returns boolean\n   */\n  private isYouTubeShort(): boolean {\n    return (\n      this.getAttribute('short') === '' &&\n      window.matchMedia('(max-width: 40em)').matches\n    );\n  }\n\n  /**\n   * Add a <link rel={preload | preconnect} ...> to the head\n   * @param {string} kind\n   * @param {string} url\n   * @param {string} as\n   */\n  private static addPrefetch(kind: string, url: string): void {\n    const linkElem = document.createElement('link');\n    linkElem.rel = kind;\n    linkElem.href = url;\n    linkElem.crossOrigin = 'true';\n    document.head.append(linkElem);\n  }\n\n  /**\n   * Begin preconnecting to warm up the iframe load Since the embed's network\n   * requests load within its iframe, preload/prefetch'ing them outside the\n   * iframe will only cause double-downloads. So, the best we can do is warm up\n   * a few connections to origins that are in the critical path.\n   *\n   * Maybe `<link rel=preload as=document>` would work, but it's unsupported:\n   * http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site\n   * Isolation and split caches adding serious complexity.\n   */\n  private static warmConnections(context: LiteYTEmbed): void {\n    if (LiteYTEmbed.isPreconnected || window.liteYouTubeIsPreconnected) return;\n\n    // we don't know which image type to preload, so warm the connection\n    LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/');\n\n    // Host that YT uses to serve JS needed by player, per amp-youtube\n    LiteYTEmbed.addPrefetch('preconnect', 'https://s.ytimg.com');\n\n    if (!context.noCookie) {\n      // The iframe document and most of its subresources come right off\n      // youtube.com\n      LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube.com');\n\n      // The botguard script is fetched off from google.com\n      LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');\n\n      // TODO: Not certain if these ad related domains are in the critical path.\n      // Could verify with domain-specific throttling.\n      LiteYTEmbed.addPrefetch(\n        'preconnect',\n        'https://googleads.g.doubleclick.net',\n      );\n      LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');\n    } else {\n      LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com');\n    }\n\n    LiteYTEmbed.isPreconnected = true;\n\n    // multiple embeds in the same page don't check for each other\n    window.liteYouTubeIsPreconnected = true;\n  }\n}\n// Register custom element\ncustomElements.define('lite-youtube', LiteYTEmbed);\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'lite-youtube': LiteYTEmbed;\n  }\n  interface Window {\n    liteYouTubeNonce: string;\n    liteYouTubeIsPreconnected: boolean;\n  }\n}\n"]}