PageSourceSearch

https://www.debatpublic.fr/modules/contrib/ckeditor5_youtube/js/vendor/lite-youtube.js?tlvim3

js debatpublic.fr collected 2026-10-02 09:51:56 UTC 14,528 bytes, 392 lines download raw bytes

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

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.