{ "version": 3, "sources": ["../../assets/src/js/lite-yt-embed.js"], "sourcesContent": ["/**\n * A lightweight youtube embed. Still should feel the same to the user, just 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 (\uD83D\uDC4D\uD83D\uDC4D)\n * https://github.com/Daugilas/lazyYT\n * https://github.com/vb/lazyframe\n */\nclass LiteYTEmbed extends HTMLElement {\n connectedCallback() {\n this.videoId = this.getAttribute('videoid');\n\n let playBtnEl = this.querySelector('.lty-playbtn');\n // A label for the button takes priority over a [playlabel] attribute on the custom-element\n this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play';\n\n /**\n * Lo, the youtube placeholder image! (aka the thumbnail, poster image, etc)\n *\n * See https://github.com/paulirish/lite-youtube-embed/blob/master/youtube-thumbnail-urls.md\n *\n * TODO: Do the sddefault->hqdefault fallback\n * - When doing this, apply referrerpolicy (https://github.com/ampproject/amphtml/pull/3940)\n * TODO: Consider using webp if supported, falling back to jpg\n */\n if (!this.style.backgroundImage) {\n this.style.backgroundImage = `url(\"https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg\")`;\n }\n\n // Set up play button, and its visually hidden label\n if (!playBtnEl) {\n playBtnEl = document.createElement('button');\n playBtnEl.type = 'button';\n playBtnEl.classList.add('lty-playbtn');\n this.append(playBtnEl);\n }\n if (!playBtnEl.textContent) {\n const playBtnLabelEl = document.createElement('span');\n playBtnLabelEl.className = 'lyt-visually-hidden';\n playBtnLabelEl.textContent = this.playLabel;\n playBtnEl.append(playBtnLabelEl);\n }\n playBtnEl.removeAttribute('href');\n\n // On hover (or tap), warm up the TCP connections we're (likely) about to use.\n this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {once: true});\n\n // Once the user clicks, add the real iframe and drop our play button\n // TODO: In the future we could be like amp-youtube and silently swap in the iframe during idle time\n // We'd want to only do this for in-viewport or near-viewport ones: https://github.com/ampproject/amphtml/pull/5003\n this.addEventListener('click', this.addIframe);\n\n // Chrome & Edge desktop have no problem with the basic YouTube Embed with ?autoplay=1\n // However Safari desktop and most/all mobile browsers do not successfully track the user gesture of clicking through the creation/loading of the iframe,\n // so they don't autoplay automatically. Instead we must load an additional 2 sequential JS files (1KB + 165KB) (un-br) for the YT Player API\n // TODO: Try loading the the YT API in parallel with our iframe and then attaching/playing it. #82\n this.needsYTApiForAutoplay = navigator.vendor.includes('Apple') || navigator.userAgent.includes('Mobi');\n }\n\n /**\n * Add a to the head\n */\n static addPrefetch(kind, url, as) {\n const linkEl = document.createElement('link');\n linkEl.rel = kind;\n linkEl.href = url;\n if (as) {\n linkEl.as = as;\n }\n document.head.append(linkEl);\n }\n\n /**\n * Begin pre-connecting to warm up the iframe load\n * Since the embed's network requests load within its iframe,\n * preload/prefetch'ing them outside the iframe will only cause double-downloads.\n * So, the best we can do is warm up a few connections to origins that are in the critical path.\n *\n * Maybe `` would work, but it's unsupported: http://crbug.com/593267\n * But TBH, I don't think it'll happen soon with Site Isolation and split caches adding serious complexity.\n */\n static warmConnections() {\n if (LiteYTEmbed.preconnected) return;\n\n // The iframe document and most of its subresources come right off youtube.com\n LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com');\n // The botguard script is fetched off from google.com\n LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');\n\n // Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling.\n LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');\n LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');\n\n LiteYTEmbed.preconnected = true;\n }\n\n fetchYTPlayerApi() {\n if (window.YT || (window.YT && window.YT.Player)) return;\n\n this.ytApiPromise = new Promise((res, rej) => {\n var el = document.createElement('script');\n el.src = 'https://www.youtube.com/iframe_api';\n el.async = true;\n el.onload = _ => {\n YT.ready(res);\n };\n el.onerror = rej;\n this.append(el);\n });\n }\n\n async addYTPlayerIframe(params) {\n this.fetchYTPlayerApi();\n await this.ytApiPromise;\n\n const videoPlaceholderEl = document.createElement('div')\n this.append(videoPlaceholderEl);\n\n const paramsObj = Object.fromEntries(params.entries());\n\n new YT.Player(videoPlaceholderEl, {\n width: '100%',\n videoId: this.videoId,\n playerVars: paramsObj,\n events: {\n 'onReady': event => {\n event.target.playVideo();\n }\n }\n });\n }\n\n async addIframe(){\n if (this.classList.contains('lyt-activated')) return;\n this.classList.add('lyt-activated');\n\n const params = new URLSearchParams(this.getAttribute('params') || []);\n params.append('autoplay', '1');\n params.append('playsinline', '1');\n\n if (this.needsYTApiForAutoplay) {\n return this.addYTPlayerIframe(params);\n }\n\n const iframeEl = document.createElement('iframe');\n iframeEl.width = 560;\n iframeEl.height = 315;\n // No encoding necessary as [title] is safe. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#:~:text=Safe%20HTML%20Attributes%20include\n iframeEl.title = this.playLabel;\n iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture';\n iframeEl.allowFullscreen = true;\n // AFAIK, the encoding here isn't necessary for XSS, but we'll do it only because this is a URL\n // https://stackoverflow.com/q/64959723/89484\n iframeEl.src = `https://www.youtube-nocookie.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`;\n this.append(iframeEl);\n\n // Set focus for a11y\n iframeEl.focus();\n }\n}\n// Register custom element\ncustomElements.define('lite-youtube', LiteYTEmbed);\n"], "mappings": "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", "names": [] }