PageSourceSearch

https://guthyjacksonfoundation.org/wp-content/plugins/enhanced-emb…/lite-youtube/lite-youtube.js?ver=1.8.2

js guthyjacksonfoundation.org collected 2026-10-02 05:44:37 UTC 12,585 bytes, 343 lines download raw bytes

vendor: 11,281 bytes, lines 1-313
1export class LiteYTEmbed extends HTMLElement {
2    constructor() {
3        super();
4        this.isIframeLoaded = false;
5        this.setupDom();
6    }
7    static get observedAttributes() {
8        return ['videoid', 'playlistid', 'videoplay', 'videotitle'];
9    }
10    connectedCallback() {
11        this.addEventListener('pointerover', () => LiteYTEmbed.warmConnections(this), {
12            once: true,
13        });
14        this.addEventListener('click', () => this.addIframe());
15    }
16    get videoId() {
17        return encodeURIComponent(this.getAttribute('videoid') || '');
18    }
19    set videoId(id) {
20        this.setAttribute('videoid', id);
21    }
22    get playlistId() {
23        return encodeURIComponent(this.getAttribute('playlistid') || '');
24    }
25    set playlistId(id) {
26        this.setAttribute('playlistid', id);
27    }
28    get videoTitle() {
29        return this.getAttribute('videotitle') || 'Video';
30    }
31    set videoTitle(title) {
32        this.setAttribute('videotitle', title);
33    }
34    get videoPlay() {
35        return this.getAttribute('videoplay') || 'Play';
36    }
37    set videoPlay(name) {
38        this.setAttribute('videoplay', name);
39    }
40    get videoStartAt() {
41        return this.getAttribute('videoStartAt') || '0';
42    }
43    get autoLoad() {
44        return this.hasAttribute('autoload');
45    }
46    get autoPause() {
47        return this.hasAttribute('autopause');
48    }
49    get noCookie() {
50        return this.hasAttribute('nocookie');
51    }
52    get posterQuality() {
53        return this.getAttribute('posterquality') || 'hqdefault';
54    }
55    get posterLoading() {
56        return (this.getAttribute('posterloading') ||
57            'lazy');
58    }
59    get params() {
60        return `start=${this.videoStartAt}&${this.getAttribute('params')}`;
61    }
62    set params(opts) {
63        this.setAttribute('params', opts);
64    }
65    set posterQuality(opts) {
66        this.setAttribute('posterquality', opts);
67    }
68    get disableNoscript() {
69        return this.hasAttribute('disablenoscript');
70    }
71    setupDom() {
72        const shadowDom = this.attachShadow({ mode: 'open' });
73        let nonce = '';
74        if (window.liteYouTubeNonce) {
75            nonce = `nonce="${window.liteYouTubeNonce}"`;
76        }
77        shadowDom.innerHTML = `
78      <style ${nonce}>
79        :host {
80          --aspect-ratio: var(--lite-youtube-aspect-ratio, 16 / 9);
81          --aspect-ratio-short: var(--lite-youtube-aspect-ratio-short, 9 / 16);
82          --frame-shadow-visible: var(--lite-youtube-frame-shadow-visible, yes);
83          contain: content;
84          display: block;
85          position: relative;
86          width: 100%;
87          aspect-ratio: var(--aspect-ratio);
88        }
89
90        @media (max-width: 40em) {
91          :host([short]) {
92            aspect-ratio: var(--aspect-ratio-short);
93          }
94        }
95
96        #frame, #fallbackPlaceholder, iframe {
97          position: absolute;
98          width: 100%;
99          height: 100%;
100          left: 0;
101          top: 0;
102        }
103
104        #frame {
105          cursor: pointer;
106        }
107
108        #fallbackPlaceholder, slot[name=image]::slotted(*) {
109          object-fit: cover;
110          width: 100%;
111        }
112
113        @container style(--frame-shadow-visible: yes) {
114          #frame::before {
115            content: '';
116            display: block;
117            position: absolute;
118            top: 0;
119            background-image: linear-gradient(180deg, #111 -20%, transparent 90%);
120            height: 60px;
121            width: 100%;
122            z-index: 1;
123          }
124        }
125
126        #playButton {
127          width: 68px;
128          height: 48px;
129          background-color: transparent;
130          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>');
131          z-index: 1;
132          border: 0;
133          border-radius: inherit;
134        }
135
136        #playButton:before {
137          content: '';
138          border-style: solid;
139          border-width: 11px 0 11px 19px;
140          border-color: transparent transparent transparent #fff;
141        }
142
143        #playButton,
144        #playButton:before {
145          position: absolute;
146          top: 50%;
147          left: 50%;
148          transform: translate3d(-50%, -50%, 0);
149          cursor: inherit;
150        }
151
152        /* Post-click styles */
153        .activated {
154          cursor: unset;
155        }
156
157        #frame.activated::before,
158        #frame.activated > #playButton {
159          display: none;
160        }
161      </style>
162      <div id="frame">
163        <picture>
164          <slot name="image">
165            <source id="webpPlaceholder" type="image/webp">
166            <source id="jpegPlaceholder" type="image/jpeg">
167            <img id="fallbackPlaceholder" referrerpolicy="origin" loading="lazy">
168          </slot>
169        </picture>
170        <button id="playButton" part="playButton"></button>
171      </div>
172    `;
173        this.domRefFrame = shadowDom.querySelector('#frame');
174        this.domRefImg = {
175            fallback: shadowDom.querySelector('#fallbackPlaceholder'),
176            webp: shadowDom.querySelector('#webpPlaceholder'),
177            jpeg: shadowDom.querySelector('#jpegPlaceholder'),
178        };
179        this.domRefPlayButton = shadowDom.querySelector('#playButton');
180    }
181    setupComponent() {
182        const hasImgSlot = this.shadowRoot.querySelector('slot[name=image]');
183        if (hasImgSlot.assignedNodes().length === 0) {
184            this.initImagePlaceholder();
185        }
186        this.domRefPlayButton.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
187        this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`);
188        if (this.autoLoad || this.isYouTubeShort() || this.autoPause) {
189            this.initIntersectionObserver();
190        }
191        if (!this.disableNoscript) {
192            this.injectSearchNoScript();
193        }
194    }
195    attributeChangedCallback(name, oldVal, newVal) {
196        if (oldVal !== newVal) {
197            this.setupComponent();
198            if (this.domRefFrame.classList.contains('activated')) {
199                this.domRefFrame.classList.remove('activated');
200                this.shadowRoot.querySelector('iframe').remove();
201                this.isIframeLoaded = false;
202            }
203        }
204    }
205    injectSearchNoScript() {
206        const eleNoScript = document.createElement('noscript');
207        this.prepend(eleNoScript);
208        eleNoScript.innerHTML = this.generateIframe();
209    }
210    generateIframe(isIntersectionObserver = false) {
211        let autoplay = isIntersectionObserver ? 0 : 1;
212        let autoPause = this.autoPause ? '&enablejsapi=1' : '';
213        const wantsNoCookie = this.noCookie ? '-nocookie' : '';
214        let embedTarget;
215        if (this.playlistId) {
216            embedTarget = `?listType=playlist&list=${this.playlistId}&`;
217        }
218        else {
219            embedTarget = `${this.videoId}?`;
220        }
221        if (this.isYouTubeShort()) {
222            this.params = `loop=1&mute=1&modestbranding=1&playsinline=1&rel=0&enablejsapi=1&playlist=${this.videoId}`;
223            autoplay = 1;
224        }
225        return `
226<iframe credentialless frameborder="0" title="${this.videoTitle}"
227  allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
228  src="https://www.youtube${wantsNoCookie}.com/embed/${embedTarget}autoplay=${autoplay}${autoPause}&${this.params}"
229></iframe>`;
230    }
231    addIframe(isIntersectionObserver = false) {
232        if (!this.isIframeLoaded) {
233            const iframeHTML = this.generateIframe(isIntersectionObserver);
234            this.domRefFrame.insertAdjacentHTML('beforeend', iframeHTML);
235            this.domRefFrame.classList.add('activated');
236            this.isIframeLoaded = true;
237            this.attemptShortAutoPlay();
238            this.dispatchEvent(new CustomEvent('liteYoutubeIframeLoaded', {
239                detail: {
240                    videoId: this.videoId,
241                },
242                bubbles: true,
243                cancelable: true,
244            }));
245        }
246    }
247    initImagePlaceholder() {
248        this.testPosterImage();
249        this.domRefImg.fallback.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
250        this.domRefImg?.fallback?.setAttribute('alt', `${this.videoPlay}: ${this.videoTitle}`);
251    }
252    async testPosterImage() {
253        setTimeout(() => {
254            const webpUrl = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`;
255            const img = new Image();
256            img.fetchPriority = 'low';
257            img.referrerPolicy = 'origin';
258            img.src = webpUrl;
259            img.onload = async (e) => {
260                const target = e.target;
261                const noPoster = target?.naturalHeight == 90 && target?.naturalWidth == 120;
262                if (noPoster) {
263                    this.posterQuality = 'hqdefault';
264                }
265                const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`;
266                this.domRefImg.webp.srcset = posterUrlWebp;
267                const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/${this.posterQuality}.jpg`;
268                this.domRefImg.fallback.loading = this.posterLoading;
269                this.domRefImg.jpeg.srcset = posterUrlJpeg;
270                this.domRefImg.fallback.src = posterUrlJpeg;
271                this.domRefImg.fallback.loading = this.posterLoading;
272            };
273        }, 100);
274    }
275    initIntersectionObserver() {
276        const options = {
277            root: null,
278            rootMargin: '0px',
279            threshold: 0,
280        };
281        const observer = new IntersectionObserver((entries, observer) => {
282            entries.forEach(entry => {
283                if (entry.isIntersecting && !this.isIframeLoaded) {
284                    LiteYTEmbed.warmConnections(this);
285                    this.addIframe(true);
286                    observer.unobserve(this);
287                }
288            });
289        }, options);
290        observer.observe(this);
291        if (this.autoPause) {
292            const windowPause = new IntersectionObserver((e, o) => {
293                e.forEach(entry => {
294                    if (entry.intersectionRatio !== 1) {
295                        this.shadowRoot
296                            .querySelector('iframe')
297                            ?.contentWindow?.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
298                    }
299                });
300            }, { threshold: 1 });
301            windowPause.observe(this);
302        }
303    }
304    attemptShortAutoPlay() {
305        if (this.isYouTubeShort()) {
306            setTimeout(() => {
307                this.shadowRoot
308                    .querySelector('iframe')
309                    ?.contentWindow?.postMessage('{"event":"command","func":"' + 'playVideo' + '","args":""}', '*');
310            }, 2000);
311        }
312    }
313    isYouTubeShort() {
314        return (this.getAttribute('short') === '' &&
315            window.matchMedia('(max-width: 40em)').matches);
316    }
317    static addPrefetch(kind, url) {
318        const linkElem = document.createElement('link');
319        linkElem.rel = kind;
320        linkElem.href = url;
321        linkElem.crossOrigin = 'true';
322        document.head.append(linkElem);
323    }
324    static warmConnections(context) {
325        if (LiteYTEmbed.isPreconnected || window.liteYouTubeIsPreconnected)
326            return;
327        LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/');
328        LiteYTEmbed.addPrefetch('preconnect', 'https://s.ytimg.com');
329        if (!context.noCookie) {
330            LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube.com');
331            LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');
332            LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');
333            LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');
334        }
335        else {
336            LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com');
337        }
338        LiteYTEmbed.isPreconnected = true;
339        window.liteYouTubeIsPreconnected = true;
340    }
341}
342LiteYTEmbed.isPreconnected = false;
343customElements.define('lite-youtube', LiteYTEmbed);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.