PageSourceSearch

https://www.seven49.net/template/vendor/iframemanager/iframemanager.js?v=639258453281727278

js seven49.net collected 2026-09-24 22:49:24 UTC 30,840 bytes, 947 lines download raw bytes

1/*!
2 * iframemanager v1.2.5
3 * Author Orest Bida
4 * Released under the MIT License
5 */
6(() => {
7    'use strict';
8
9    /**
10     * @typedef {Object} servicePropObj
11     * @property {string} _id
12     * @property {string} _title
13     * @property {string} _thumbnail
14     * @property {string} _params
15     * @property {HTMLDivElement} _div
16     * @property {HTMLDivElement} _innerContainer
17     * @property {HTMLDivElement} _placeholderDiv
18     * @property {HTMLDivElement} _initialPlaceholderClone
19     * @property {HTMLIFrameElement} _iframe
20     * @property {HTMLDivElement} _backgroundDiv
21     * @property {boolean} _hasIframe
22     * @property {boolean} _hasNotice
23     * @property {boolean} _showNotice
24     * @property {boolean} _dataWidget
25     * @property {boolean} _dataPlaceholderVisible
26     * @property {Object.<string, string>} _iframeAttributes
27     */
28
29    /**
30     * @typedef {HTMLIFrameElement} IframeProp
31     */
32
33    /**
34     * @typedef {Object} CookieStructure
35     * @property {string} name
36     * @property {string} path
37     * @property {string} domain
38     * @property {string} sameSite
39     * @property {number} expiration
40     */
41
42    /**
43     * @typedef {Object} Language
44     * @property {string} notice
45     * @property {string} loadBtn
46     * @property {string} loadAllBtn
47     */
48
49    /**
50     * @typedef {Object} ServiceConfig
51     * @property {string} embedUrl
52     * @property {string | () => string} [thumbnailUrl]
53     * @property {IframeProp} [iframe]
54     * @property {CookieStructure} cookie
55     * @property {Object.<string, Language>} languages
56     * @property {Function} [onAccept]
57     * @property {Function} [onReject]
58     */
59
60    const API_EVENT_SOURCE = 'api';
61    const CLICK_EVENT_SOURCE = 'click';
62    const DATA_ID_PLACEHOLDER = '{data-id}';
63    const ACCEPT_ACTION = 'accept';
64    const REJECT_ACTION = 'reject';
65
66    const HIDE_NOTICE_CLASS = 'c-h-n';
67    const HIDE_LOADER_CLASS = 'c-h-b';
68    const SHOW_PLACEHOLDER_CLASS = 'show-ph';
69
70    let
71
72        /**
73         * @type {Window}
74         */
75        win,
76
77        /**
78         * @type {Document}
79         */
80        doc,
81
82        config,
83
84        /**
85         * @type {Object.<string, servicePropObj[]>}
86         */
87        allServiceProps = {},
88
89        /**
90         * @type {Object.<string, IntersectionObserver>}
91         */
92        serviceObservers = {},
93
94        /**
95         * @type {Object.<string, boolean>}
96         */
97        stopServiceObserver = {},
98
99        currLang = '',
100
101        /**
102         * @type {Object.<string, ServiceConfig>}
103         */
104        services = {},
105
106        /**
107         * @type {string[]}
108         */
109        serviceNames,
110
111        /**
112         * @type {Map<string, boolean>}
113         */
114        servicesState = new Map(),
115
116        /**
117         * @type {'api' | 'click'}
118         */
119        currentEventSource = API_EVENT_SOURCE,
120
121        onChangeCallback;
122
123    /**
124     * Prevent direct use of the following
125     * props. in the `iframe` element to avoid
126     * potential issues
127     */
128    const disallowedProps = ['onload', 'onerror', 'src'];
129
130    const isFunction = el => typeof el === 'function';
131    const isString = el => typeof el === 'string';
132    const getBrowserLang = () => navigator.language.slice(0, 2).toLowerCase();
133
134    /**
135     * Create and return HTMLElement based on specified type
136     * @param {string} type
137     */
138    const createNode = (type) => doc.createElement(type);
139
140    /**
141     * @returns {HTMLDivElement}
142     */
143    const createDiv = () => createNode('div');
144
145    /**
146     * @returns {HTMLButtonElement}
147     */
148    const createButton = () => {
149        const btn = createNode('button');
150        btn.type = 'button';
151        return btn;
152    };
153
154    /**
155     * @param {HTMLElement} el
156     * @param {string} className
157     */
158    const setClassName = (el, className) => el.className = className;
159
160    /**
161     * @param {HTMLElement} el
162     * @param {string} className
163     * @returns
164     */
165    const addClass = (el, className) => el.classList.add(className);
166
167    /**
168     * @param {HTMLElement} parent
169     * @param {HTMLElement} child
170     */
171    const appendChild = (parent, child) => parent.appendChild(child);
172
173    /**
174     * @returns {string[]}
175     */
176    const getKeys = obj => obj && Object.keys(obj) || [];
177
178    /**
179     * @param {HTMLIFrameElement} iframe
180     * @param {Object.<string, string>} attrs
181     */
182    const setIframeAttributes = (iframe, attrs) => {
183        for(const key in attrs)
184            setAttribute(iframe, key, attrs[key]);
185    };
186
187    /**
188     * @param {HTMLDivElement} serviceDiv
189     * @returns {servicePropObj}
190     */
191    const getDivProps = (serviceDiv) => {
192
193        const dataset = serviceDiv.dataset;
194        const iframeAttrs = {};
195        const iframeAttrSelector = 'data-iframe-';
196
197        const iframeAttrNames = serviceDiv.getAttributeNames()
198            .filter(attr => attr.slice(0, 12) === iframeAttrSelector)
199            .map(attr => attr.slice(12));
200
201        const placeholderDiv = serviceDiv.querySelector('[data-placeholder]');
202        const dataVisible = placeholderDiv && placeholderDiv.hasAttribute('data-visible');
203        dataVisible && placeholderDiv.removeAttribute('data-visible');
204        const placeholderClone = placeholderDiv && placeholderDiv.cloneNode(true);
205
206        /**
207         * Get all "data-iframe-* attributes
208         */
209        for(const attrName of iframeAttrNames)
210            iframeAttrs[attrName] = serviceDiv.getAttribute(iframeAttrSelector + attrName);
211
212        return {
213            _id: dataset.id,
214            _title: dataset.title,
215            _thumbnail: dataset.thumbnail,
216            _params: dataset.params,
217            _div: serviceDiv,
218            _innerContainer: null,
219            _placeholderDiv: placeholderDiv,
220            _initialPlaceholderClone: placeholderClone,
221            _backgroundDiv: null,
222            _hasIframe: false,
223            _hasNotice: false,
224            _showNotice: true,
225            _dataWidget: 'widget' in dataset,
226            _dataPlaceholderVisible: dataVisible,
227            _iframeAttributes: iframeAttrs
228        };
229    };
230
231    /**
232     * @param {string} serviceName
233     * @param {string} thumbnailUrl
234     */
235    const lazyLoadThumbnails = (serviceName, thumbnailUrl) => {
236
237        const serviceProps = allServiceProps[serviceName];
238
239        if ('IntersectionObserver' in win) {
240            const thumbnailObserver = new IntersectionObserver((entries) => {
241                for(const entry of entries){
242                    if(entry.isIntersecting){
243                        // index relative to the current service array
244                        loadThumbnail(thumbnailUrl, serviceProps[entry.target.dataset.index]);
245                        thumbnailObserver.unobserve(entry.target);
246                    }
247                }
248            });
249
250            for(const serviceProp of serviceProps)
251                thumbnailObserver.observe(serviceProp._div);
252        }
253    };
254
255
256    /**
257     * @param {string} url
258     * @param {servicePropObj} serviceProp
259     */
260    const loadThumbnail = (url, serviceProp) => {
261
262        const loadBackgroundImage = (src) => {
263            serviceProp._backgroundDiv.style.backgroundImage = `url('${src}')`;
264
265            const img = new Image();
266            img.onload = () => addClass(serviceProp._backgroundDiv, 'loaded');
267            img.src = src;
268        };
269
270        // Set custom thumbnail if provided
271        if(isString(serviceProp._thumbnail)){
272            serviceProp._thumbnail !== '' && loadBackgroundImage(serviceProp._thumbnail);
273        }else{
274
275            if(isFunction(url)){
276                url(serviceProp._id, (src) => loadBackgroundImage(src));
277
278            }else if(isString(url)){
279                const src = url.replace(DATA_ID_PLACEHOLDER, serviceProp._id);
280                loadBackgroundImage(src);
281            }
282        }
283
284    };
285
286    /**
287     * Create iframe and append it into the specified div
288     * @param {servicePropObj} serviceProp
289     * @param {ServiceConfig} serviceConfig
290     */
291    const createIframe = (serviceProp, serviceConfig) => {
292
293        // Create iframe only if doesn't already have one
294        if(serviceProp._hasIframe)
295            return;
296
297        serviceProp._hasIframe = true;
298
299        if(serviceProp._placeholderDiv){
300            const newFreshPlaceholder = serviceProp._initialPlaceholderClone.cloneNode(true);
301            serviceProp._placeholderDiv.replaceWith(newFreshPlaceholder);
302            serviceProp._placeholderDiv = newFreshPlaceholder;
303        }
304
305        const iframeProps = serviceConfig.iframe;
306
307        if(isFunction(serviceConfig.onAccept)){
308
309            // Let the onAccept method create the iframe
310            serviceConfig.onAccept(serviceProp._div, (iframe) => {
311
312                if(!(iframe instanceof HTMLIFrameElement))
313                    return false;
314
315                /**
316                 * Add global internal attributes
317                 */
318                setIframeAttributes(iframe, iframeProps);
319
320                /**
321                 * Add all data-attr-* attributes (iframe specific)
322                 */
323                setIframeAttributes(iframe, serviceProp._iframeAttributes);
324
325                serviceProp._iframe = iframe;
326                serviceProp._hasIframe = true;
327
328                // Hide loading circle
329                addClass(serviceProp._div, HIDE_LOADER_CLASS);
330
331                // Show placeholder
332                (!serviceProp._dataPlaceholderVisible || serviceProp._dataWidget)
333                    && addClass(serviceProp._div, SHOW_PLACEHOLDER_CLASS);
334            });
335
336            return;
337        }
338
339        serviceProp._iframe = createNode('iframe');
340
341        const iframeConfig = serviceConfig.iframe;
342
343        /**
344         * @type {string}
345         */
346        const iframeParams = serviceProp._params || iframeConfig && iframeConfig.params;
347
348        // Replace data-id with valid resource id
349        const embedUrl = serviceConfig.embedUrl || '';
350        let src = embedUrl.replace(DATA_ID_PLACEHOLDER, serviceProp._id);
351
352        serviceProp._title && (serviceProp._iframe.title = serviceProp._title);
353
354        // Add parameters to src
355        if(iframeParams && isString(iframeParams)){
356            src += iframeParams.slice(0, 1) === '?'
357                ? iframeParams
358                : `?${iframeParams}`;
359        }
360
361        // When iframe is loaded => hide background image
362        serviceProp._iframe.onload = () => {
363            addClass(serviceProp._div, HIDE_LOADER_CLASS);
364            serviceProp._iframe.onload = undefined;
365
366            isFunction(iframeProps && iframeProps.onload)
367                && iframeProps.onload(serviceProp._id, serviceProp._iframe);
368        };
369
370        /**
371         * Add global internal attributes
372         */
373        setIframeAttributes(serviceProp._iframe, iframeProps);
374
375        /**
376         * Add all data-attr-* attributes (iframe specific)
377         */
378        setIframeAttributes(serviceProp._iframe, serviceProp._iframeAttributes);
379
380        serviceProp._iframe.src = src;
381
382        appendChild(serviceProp._innerContainer, serviceProp._iframe);
383    };
384
385    /**
386     * @param {HTMLElement} el
387     * @param {string} attrKey
388     * @param {string} attrValue
389     */
390    const setAttribute = (el, attrKey, attrValue) => {
391        if(!disallowedProps.includes(attrKey))
392            el.setAttribute(attrKey, attrValue);
393    };
394
395    /**
396     * Remove iframe HTMLElement from div
397     * @param {servicePropObj} serviceProp
398     */
399    const removeIframe = (serviceProp) => {
400        serviceProp._iframe && serviceProp._iframe.remove();
401        serviceProp._hasIframe = false;
402    };
403
404    /**
405     * Add necessary classes to hide notice
406     * @param {servicePropObj} serviceProp
407     */
408    const hideNotice = (serviceProp) => {
409        addClass(serviceProp._div, HIDE_NOTICE_CLASS);
410        serviceProp._showNotice = false;
411    };
412
413    /**
414     * Add necessary classes to show notice
415     * @param {servicePropObj} serviceProp
416     */
417    const showNotice = (serviceProp) => {
418        serviceProp._div.classList.remove(
419            HIDE_NOTICE_CLASS,
420            HIDE_LOADER_CLASS,
421            SHOW_PLACEHOLDER_CLASS
422        );
423        serviceProp._showNotice = true;
424    };
425
426    /**
427     * Get cookie by name
428     * @param {string} a cookie name
429     * @returns {string} cookie value
430     */
431    const getCookie = (a) => {
432        a = doc.cookie.match(`(^|;)\\s*${a}\\s*=\\s*([^;]+)`);
433
434        return a ? a.pop() : '';
435    };
436
437    /**
438     * Set cookie based on given object
439     * @param {CookieStructure} cookie
440     */
441    const setCookie = (cookie) => {
442
443        const { hostname, protocol } = location;
444        const name = cookie.name;
445        const value = '1';
446        const date = new Date();
447        const path = cookie.path || '/';
448        const expiration = (cookie.expiration || 182) * 86400000;
449        const sameSite = cookie.sameSite || 'Lax';
450        const domain = cookie.domain || hostname;
451
452        date.setTime(date.getTime() + expiration);
453
454        let cookieStr = name + '='
455            + value
456            + (expiration !== 0 ? `; Expires=${date.toUTCString()}` : '')
457            + `; Path=${path}`
458            + `; SameSite=${sameSite}`;
459
460        // assures cookie works with localhost (=> don't specify domain if on localhost)
461        if(domain.indexOf('.') > -1)
462            cookieStr += `; Domain=${domain}`;
463
464        if(protocol === 'https:')
465            cookieStr += '; Secure';
466
467        doc.cookie = cookieStr;
468    };
469
470    /**
471     * Delete cookie by name & path
472     * @param {CookieStructure} cookie
473     */
474    const eraseCookie = (cookie) => {
475        const name = cookie.name;
476        const path = cookie.path || '/';
477        const domain = cookie.domain || location.hostname;
478        const expires = 'Thu, 01 Jan 1970 00:00:01 GMT';
479
480        doc.cookie = `${name}=; Path=${path}; Domain=${domain}; Expires=${expires};`;
481    };
482
483    /**
484     * Create all notices relative to the specified service
485     * @param {string} serviceName
486     * @param {ServiceConfig} serviceConfig
487     * @param {boolean} hidden
488     */
489    const createAllNotices = (serviceName, serviceConfig, hidden) => {
490
491        // get number of iframes of current service
492        const serviceProps = allServiceProps[serviceName];
493        const languages = serviceConfig.languages;
494
495        serviceProps.forEach(serviceProp => {
496
497            if(!serviceProp._hasNotice && languages){
498                const lang = languages[currLang];
499                const loadBtnText = lang && lang.loadBtn;
500                const noticeText = lang && lang.notice;
501                const loadAllBtnText = lang && lang.loadAllBtn;
502
503                const fragment = doc.createElement('div');
504                const notice = createDiv();
505                const span = createDiv();
506                const innerDiv = createDiv();
507                const buttons = createDiv();
508
509                setClassName(fragment, 'cll');
510                serviceProp._innerContainer = fragment;
511
512                const showVideo = () => {
513                    hideNotice(serviceProp);
514                    createIframe(serviceProp, serviceConfig);
515                };
516
517                if(loadBtnText){
518                    const load_button = createButton();
519                    load_button.textContent = loadBtnText;
520                    setClassName(load_button, 'c-l-b');
521
522                    load_button.addEventListener(CLICK_EVENT_SOURCE, showVideo);
523                    appendChild(buttons, load_button);
524                }
525
526                if(loadAllBtnText){
527                    const load_all_button = createButton();
528                    load_all_button.textContent = loadAllBtnText;
529                    setClassName(load_all_button, loadBtnText ? 'c-la-b' : 'c-l-b');
530
531                    load_all_button.addEventListener(CLICK_EVENT_SOURCE, () => {
532                        showVideo();
533
534                        currentEventSource = CLICK_EVENT_SOURCE;
535                        api.acceptService(serviceName);
536                    });
537
538                    appendChild(buttons, load_all_button);
539                }
540
541                const notice_text = createDiv();
542                const ytVideoBackground = createDiv();
543                const loaderBg = createDiv();
544                const ytVideoBackgroundInner = createDiv();
545                const notice_text_container = createDiv();
546
547                setClassName(notice_text, 'cc-text');
548                setClassName(ytVideoBackgroundInner, 'c-bg-i');
549
550                serviceProp._backgroundDiv = ytVideoBackgroundInner;
551                setClassName(loaderBg, 'c-ld');
552
553                if(!isString(serviceProp._thumbnail) || serviceProp._thumbnail !== ''){
554                    setClassName(ytVideoBackground, 'c-bg');
555                }
556
557                const iframeTitle = serviceProp._title;
558                const fragment_2 = doc.createDocumentFragment();
559
560                if(iframeTitle) {
561                    const title_span = createNode('span');
562                    setClassName(title_span, 'c-tl');
563                    title_span.insertAdjacentHTML('beforeend', iframeTitle);
564                    appendChild(fragment_2, title_span);
565                }
566
567                appendChild(notice_text, fragment_2);
568                notice && notice_text.insertAdjacentHTML('beforeend', noticeText || '');
569                appendChild(span, notice_text);
570
571                setClassName(notice_text_container, 'c-t-cn');
572                setClassName(span, 'c-n-t');
573                setClassName(innerDiv, 'c-n-c');
574                setClassName(notice, 'c-nt');
575                setClassName(buttons,  'c-n-a');
576
577                appendChild(notice_text_container, span);
578
579                if(loadBtnText || loadAllBtnText)
580                    appendChild(notice_text_container, buttons);
581
582                appendChild(innerDiv, notice_text_container);
583                appendChild(notice, innerDiv);
584
585                appendChild(ytVideoBackground, ytVideoBackgroundInner);
586                appendChild(fragment, notice);
587                (serviceConfig.thumbnailUrl || serviceProp._thumbnail) && appendChild(fragment, ytVideoBackground);
588                appendChild(fragment, loaderBg);
589
590                hidden && addClass(serviceProp._div, HIDE_NOTICE_CLASS);
591
592                // Avoid reflow with fragment (only 1 appendChild)
593                serviceProp._div.prepend(fragment);
594                serviceProp._hasNotice = true;
595
596                setTimeout(()=> addClass(serviceProp._div, 'c-an'), 20);
597            }
598        });
599    };
600
601    /**
602     * Hide notices for the specified service
603     * and then create iframes.
604     *
605     * @param {string} serviceName
606     * @param {ServiceConfig} serviceConfig
607     */
608    const hideAllNotices = (serviceName, serviceConfig) => {
609
610        // get number of iframes of current service
611        const serviceProps = allServiceProps[serviceName];
612
613        const observeServiceDiv = (div, serviceName) => {
614
615            if(!serviceObservers[serviceName]) {
616                serviceObservers[serviceName] = new IntersectionObserver((entries) => {
617
618                    if(stopServiceObserver[serviceName]){
619                        serviceObservers[serviceName].disconnect();
620                        return;
621                    }
622
623                    for(let i=0; i<entries.length; ++i){
624                        if(entries[i].isIntersecting){
625                            ((i) => {
626                                /**
627                                 * @type {HTMLDivElement}
628                                 */
629                                const target = entries[i].target;
630                                const dataIndex = target.dataset.index;
631                                createIframe(serviceProps[dataIndex], serviceConfig);
632
633                                setTimeout(() => {
634                                    hideNotice(serviceProps[dataIndex]);
635                                }, i*50);
636
637                                serviceObservers[serviceName].unobserve(target);
638                            })(i);
639                        }
640                    }
641
642                });
643            }
644
645            serviceObservers[serviceName].observe(div);
646        };
647
648        serviceProps.forEach((serviceProp) => {
649            if(!serviceProp._hasIframe)
650                observeServiceDiv(serviceProp._div, serviceName);
651        });
652    };
653
654
655    /**
656     * Show notices for the specified service
657     * and remove iframes.
658     *
659     * @param {string} serviceName
660     * @param {ServiceConfig}
660 serviceConfig
661     */
662    const showAllNotices = (serviceName, serviceConfig) => {
663
664        const serviceProps = allServiceProps[serviceName];
665
666        for(let i=0; i<serviceProps.length; i++){
667            ((i) => {
668
669                /**
670                 * Create iframe if it doesn't exist
671                 */
672                if(serviceProps[i]._hasIframe){
673                    if(isFunction(serviceConfig.onReject)){
674                        serviceConfig.onReject(serviceProps[i]._iframe, serviceProps[i]._div, () => showNotice(serviceProps[i]));
675                        serviceProps[i]._hasIframe = false;
676                    } else {
677                        removeIframe(serviceProps[i]);
678                    }
679                }
680
681                showNotice(serviceProps[i]);
682            })(i);
683        }
684    };
685
686    /**
687     * Validate language (make sure it exists)
688     *
689     * @param {string} lang
690     * @param {Object.<string, Object>} allLanguages
691     * @returns {string} language
692     */
693    const getValidatedLanguage = (lang, allLanguages) => {
694        if(lang in allLanguages){
695            return lang;
696        }else if(getKeys(allLanguages).length > 0){
697            return currLang in allLanguages
698                ? currLang
699                : getKeys(allLanguages)[0];
700        }
701    };
702
703    /**
704     * @param {string} serviceName
705     * @param {ServiceConfig} serviceConfig
706     */
707    const acceptHelper = (serviceName, serviceConfig) => {
708        const { cookie } = serviceConfig;
709
710        if(!getCookie(cookie.name))
711            setCookie(cookie);
712
713        hideAllNotices(serviceName, serviceConfig);
714    };
715
716    /**
717     * @param {string} serviceName
718     * @param {ServiceConfig} serviceConfig
719     */
720    const rejectHelper = (serviceName, serviceConfig) => {
721        const { cookie } = serviceConfig;
722
723        if(getCookie(cookie.name))
724            eraseCookie(cookie);
725
726        showAllNotices(serviceName, serviceConfig);
727    };
728
729    /**
730     * @param {string} serviceName
731     * @param {'accept' | 'reject'} action
732     * @param {string[]} changedServices
733     */
734    const fireOnChangeCallback = (serviceName, action, changedServices) => {
735        isFunction(onChangeCallback) && onChangeCallback({
736            eventSource: {
737                type: currentEventSource,
738                service: serviceName,
739                action
740            },
741            changedServices
742        });
743    };
744
745    const api = {
746
747        /**
748         * @param {string} serviceName
749         */
750        acceptService: (serviceName) => {
751            const changedServices = [];
752
753            if(serviceName === 'all'){
754
755                for(const name of serviceNames){
756                    stopServiceObserver[name] = false;
757
758                    if(!servicesState.get(name)){
759                        servicesState.set(name, true);
760                        acceptHelper(name, services[name]);
761                        changedServices.push(name);
762                    }
763                }
764
765                changedServices.length > 0 && fireOnChangeCallback(serviceName, ACCEPT_ACTION, changedServices);
766
767            }else if(serviceNames.includes(serviceName)){
768                stopServiceObserver[serviceName] = false;
769
770                if(!servicesState.get(serviceName)){
771                    servicesState.set(serviceName, true);
772                    acceptHelper(serviceName, services[serviceName]);
773                    changedServices.push(serviceName);
774                    fireOnChangeCallback(serviceName, ACCEPT_ACTION, changedServices);
775                }
776            }
777
778            currentEventSource = API_EVENT_SOURCE;
779        },
780
781        /**
782         * @param {string} serviceName
783         */
784        rejectService: (serviceName) => {
785
786            const changedServices = [];
787
788            if(serviceName === 'all'){
789
790                for(const name of serviceNames){
791                    stopServiceObserver[name] = true;
792                    rejectHelper(name, services[name]);
793
794                    if(servicesState.get(name)){
795                        servicesState.set(name, false);
796                        changedServices.push(name);
797                    }
798                }
799
800                changedServices.length >
800 0 && fireOnChangeCallback(serviceName, REJECT_ACTION, changedServices);
801
802            }else if(serviceNames.includes(serviceName)){
803                stopServiceObserver[serviceName] = true;
804                rejectHelper(serviceName, services[serviceName]);
805
806                if(servicesState.get(serviceName)){
807                    servicesState.set(serviceName, false);
808                    changedServices.push(serviceName);
809
810                    fireOnChangeCallback(serviceName, REJECT_ACTION, changedServices);
811                }
812            }
813        },
814
815        /**
816         * Check if a property/element is defined,
817         * if it's not then check again; repeat until maxTimeout reached.
818         *
819         * Useful when trying to use API from external scripts,
820         * or when you need to make sure a dom element exists
821         * (e.g. dynamically generated iframe).
822         *
823         * @param {object} config
824         * @param {any} [config.parent]
825         * @param {string} [config.childProperty]
826         * @param {string} [config.childSelector]
827         * @param {number} [config.timeout]
828         * @param {number} [config.maxTimeout]
829         * @returns {Promise<boolean>}
830         */
831        childExists: async ({parent=win, childProperty, childSelector='iframe', timeout=1000, maxTimeout=15000}) => {
832
833            let nTimeouts = 1;
834
835            const child = childProperty
836                ? () => parent[childProperty]
837                : () => parent.querySelector(childSelector);
838
839            return new Promise(resolve => {
840                const checkChild = () => {
841                    if (child() || nTimeouts++ * timeout > maxTimeout)
842                        return resolve(child() !== undefined);
843                    else
844                        setTimeout(checkChild, timeout);
845                };
846
847                checkChild();
848            });
849        },
850
851        getState: () => ({
852            services: new Map(servicesState),
853            acceptedServices: [...servicesState]
854                .filter(([, value]) => !!value)
855                .map(([name]) => name)
856        }),
857
858        getConfig: () => config,
859
860        run: (_config) => {
861
862            doc = document;
863            win = window;
864            config = _config;
865
866            /**
867             * Object with all services config.
868             */
869            services = config.services;
870
871            onChangeCallback = config.onChange;
872
873            /**
874             * Array containing the names of all services
875             */
876            serviceNames = getKeys(services);
877
878            if(serviceNames.length === 0)
879                return;
880
881            // Set curr lang
882            currLang = config.currLang;
883            const languages = services[serviceNames[0]].languages;
884
885            if(config.autoLang === true){
886                currLang = getValidatedLanguage(getBrowserLang(), languages);
887            }else if(isString(config.currLang)){
888                currLang = getValidatedLanguage(config.currLang, languages);
889            }
890
891            // for each service
892            for(const serviceName of serviceNames){
893
894                const currService = services[serviceName];
895
896                /**
897                 * Use service's name as cookie name,
898                 * if no cookie.name is specified
899                 * @type {CookieStructure}
900                 */
901                const cookieObj = (currService.cookie = currService.cookie || {});
902                const cookieName = (cookieObj.name = cookieObj.name || `im_${serviceName}`);
903
904                const cookieExists = getCookie(cookieName);
905                servicesState.set(serviceName, !!cookieExists);
906
907                // add new empty array of serviceProps (with current service name as property)
908                allServiceProps[serviceName] = [];
909
910                /**
911                 * @type {NodeListOf<HTMLDivElement>}
912                 */
913                const foundDivs = doc.querySelectorAll(`div[data-service="${serviceName}"]`);
914
915                const nDivs = foundDivs.length;
916
917                // if no iframes found =>
917 go to next service
918                if(nDivs === 0){
919                    continue;
920                }
921
922                // add each iframe to array of iframes of the current service
923                for(let j=0; j<nDivs; j++){
924                    foundDivs[j].dataset.index = j;
925                    allServiceProps[serviceName].push(getDivProps(foundDivs[j]));
926                }
927
928                // if cookie is not set => show notice
929                if(cookieExists){
930                    createAllNotices(serviceName, currService, true);
931                    hideAllNotices(serviceName, currService);
932                }else{
933                    createAllNotices(serviceName, currService, false);
934                }
935
936                lazyLoadThumbnails(serviceName, currService.thumbnailUrl);
937            }
938        }
939    };
940
941    const fnName = 'iframemanager';
942
943    if(typeof window !== 'undefined' && !isFunction(window[fnName])){
944        window[fnName] = () => api;
945    }
946
947})();

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.