PageSourceSearch

https://saoketv.help/wp-content/plugins/gwd-config-banner/assets/js/gwd-banner.js?ver=1790324577

js saoketv.help collected 2026-10-09 21:29:16 UTC 17,488 bytes, 537 lines download raw bytes

1(() => {
2    class GWD_Banner_Renderer {
3        static normalizeKey(key) {
4            return String(key || "").toLowerCase().replace(/[^a-z0-9]+/g, "");
5        }
6
7        static matchesKey(key, aliases) {
8            const normalized = this.normalizeKey(key);
9            return aliases.some(alias => normalized.startsWith(this.normalizeKey(alias)));
10        }
11
12        static getValueByKeys(item, aliases) {
13            if (!item || typeof item !== "object") return "";
14
15            const normalizedAliases = aliases.map(alias => this.normalizeKey(alias));
16            const key = Object.keys(item).find(name => normalizedAliases.includes(this.normalizeKey(name)));
17
18            return key ? item[key] : "";
19        }
20
21        static findAdByKeys(data, aliases) {
22            const normalizedAliases = aliases.map(alias => this.normalizeKey(alias));
23            const key = Object.keys(data).find(name => normalizedAliases.includes(this.normalizeKey(name)));
24
25            return key ? data[key] : null;
26        }
27
28        static findAdsByPrefix(data, prefix) {
29            const normalizedPrefix = this.normalizeKey(prefix);
30
31            return Object.entries(data)
32                .filter(([key, value]) => this.normalizeKey(key).startsWith(normalizedPrefix) && value)
33                .map(([, value]) => value);
34        }
35
36        static appendAd(container, value) {
37            const html = this.getValueByKeys(value, ["html_code", "html code", "html-code", "html"]);
38
39            if (html) {
40                container.insertAdjacentHTML("beforeend", html);
41                return;
42            }
43
44            const zone = value && typeof value === "object"
45                ? this.getValueByKeys(value, ["zone", "zone_id", "zone id", "id", "data-z", "data_z", "data z"])
46                : value;
47
48            if (!zone) return;
49
50            const ins = document.createElement("ins");
51            ins.className = "banner_item-theme";
52            ins.setAttribute("data-z", zone);
53            ins.setAttribute("data-revive-id", "1718ffff6aff14155bf9e84ffb3a29ee");
54            container.appendChild(ins);
55        }
56
57        static renderVideoBottom(data, container) {
58            // khong can check container o day
59            container.innerHTML = "";
60
61            const overlay = document.createElement("div");
62            overlay.className = "gwd-banner-bottom__overlay";
63
64            const infeed = document.createElement("div");
65            infeed.className = "gwd-banner-bottom__infeed";
66
67            Object.entries(data).forEach(([key, value]) => {
68                if (this.matchesKey(key, ["bottom-overlay"])) {
69                    this.appendAd(overlay, value);
70                }
71
72                if (this.matchesKey(key, ["bottom-infeed-left","bottom-infeed-right"])) {
73                    this.appendAd(infeed, value);
74                }
75            });
76
77            if (overlay.children.length) {
78                container.appendChild(overlay);
79            }
80
81            if (infeed.children.length) {
82                container.appendChild(infeed);
83            }
84        }
85
86        static renderVideoTop(data, container) {
87            // khong can check container o day
88            container.innerHTML = "";
89            container.classList.add("gwd-top-strip-marquee");
90
91            const track = document.createElement("div");
92            track.className = "gwd-top-strip-marquee__track";
93
94            Object.entries(data).forEach(([key, value]) => {
95                if (this.matchesKey(key, ["top-strip"])) {
96                    this.appendAd(track, value);
97                }
98            });
99
100            if (!track.children.length) return;
101
102            Array.from(track.children).forEach((child) => {
103                track.appendChild(child.cloneNode(true));
104            });
105
106            container.appendChild(track);
107        }
108
109        static renderTopOverlay(data, container) {
110            // khong can check container o day
111            let overlay = container.querySelector(".gwd-banner-top__overlay");
112
113            if (!overlay) {
114                overlay = document.createElement("div");
115                overlay.className = "gwd-banner-top__overlay";
116                container.prepend(overlay);
117            }
118
119            overlay.innerHTML = "";
120
121            Object.entries(data).forEach(([key, value]) => {
122                if (this.matchesKey(key, ["top-overlay"])) {
123                    this.appendAd(overlay, value);
124                }
125            });
126        }
127
128        static renderGutterAfterHeader(data, referenceNode) {
129            if (!referenceNode) return;
130
131            let random = referenceNode.dataset.gutterRandom;
132
133            if (!random) {
134                random = Math.random().toString(36).slice(2, 8);
135                referenceNode.dataset.gutterRandom = random;
136            }
137
138            const gutterClass = `${random}_gutter_banner gutter-banner`;
139            const leftClass = `${random}_gutter_left gutter-left`;
140            const rightClass = `${random}_gutter_right gutter-right`;
141
142            let left = referenceNode.nextElementSibling?.classList.contains(leftClass)
143                ? referenceNode.nextElementSibling
144                : document.createElement("div");
145
146            let right = left.nextElementSibling?.classList.contains(rightClass)
147                ? left.nextElementSibling
148                : document.createElement("div");
149
150            if (!left.parentElement) {
151                left.className = `${gutterClass} ${leftClass}`;
152                referenceNode.after(left);
153            }
154
155            if (!right.parentElement) {
156                right.className = `${gutterClass} ${rightClass}`;
157                left.after(right);
158            }
159
160            left.innerHTML = "";
161            right.innerHTML = "";
162
163            Object.entries(data).forEach(([key, value]) => {
164                if (this.matchesKey(key, ["sidebar-left"])) {
165                    this.appendAd(left, value);
166                } else if (this.matchesKey(key, ["sidebar-right"])) {
167                    this.appendAd(right, value);
168                }
169            });
170        }
171
172        static renderBetButtons(data) {
173            const buttons = document.querySelectorAll(".gwd_btn_bet");
174            const ads = this.findAdsByPrefix(data, "betbutton-");
175
176            if (!buttons.length || !ads.length) return;
177
178            buttons.forEach(button => {
179                button.innerHTML = "";
180            });
181
182            ads.forEach((ad, index) => {
183                const button = buttons[index] || buttons[buttons.length - 1];
184
185                this.appendAd(button, ad);
186            });
187        }
188
189        static renderOddListLogo(data) {
190            const ads = this.findAdsByPrefix(data, "logoicon-").slice(0, 3);
191            const items = document.querySelectorAll(".table_oddList li");
192
193            if (!ads.length || !items.length) return;
194
195            ads.forEach((ad, index) => {
196                const item = items[index];
197                if (!item) return;
198
199                item.innerHTML = "";
200
201                this.appendAd(item, ad);
202            });
203        }
204
205
206        static renderChooseOddStream(data) {
207            const targets = document.querySelectorAll(".chose_odd_stream");
208            const ad = this.findAdsByPrefix(data, "logoicon-")[0];
209
210            if (!targets.length || !ad) return;
211
212            targets.forEach(target => {
213                target.innerHTML = "";
214
215                const wrapper = document.createElement("div");
216                wrapper.className = "gwd_choose_odd_stream_ad";
217
218                this.appendAd(wrapper, ad);
219
220                wrapper.insertAdjacentHTML(
221                    "beforeend",
222                    `
223                    <span class="gwd_choose_odd_stream_icon">
224                        <svg xmlns="http://www.w3.org/2000/svg" width="8" height="6" viewBox="0 0 8 6" fill="none">
225                            <path d="M7.525 0.5L8 1.04688L4 5.5L0 1.04688L0.475 0.5L4 4.40625L7.525 0.5Z" fill="#4FA747"></path>
226                        </svg>
227                    </span>
228                    `
229                );
230
231                target.appendChild(wrapper);
232            });
233        }
234
235        static countDivInsSiteLive() {
236            const config = window.GWD_CONFIG_BANNER?.data?.data || {};
237
238            const logoIcons = Object.entries(config)
239                .filter(([key]) => /^logoicon-\d+$/i.test(key))
240                .sort((a, b) => {
241                    const numA = parseInt(a[0].match(/\d+/)?.[0] || 0, 10);
242                    const numB = parseInt(b[0].match(/\d+/)?.[0] || 0, 10);
243                    return numA - numB;
244                })
245                .map(([, value]) => value);
246
247            if (!logoIcons.length) {
248                return;
249            }
250
251            function render() {
252                document.querySelectorAll('.gwd-logoicon-ins').forEach((el, index) => {
253                    // Không append nhiều lần
254                    if (el.querySelector('ins[data-revive-zoneid]')) {
255                        return;
256                    }
257
258                    const zoneId = logoIcons[index % logoIcons.length];
259
260                    el.insertAdjacentHTML(
261                        'beforeend',
262                        `
263                        <ins
264                            data-revive-zoneid="${zoneId}"
265                            data-revive-id="1718ffff6aff14155bf9e84ffb3a29ee">
266                        </ins>
267                        `
268                    );
269                });
270            }
271
272            // Xử lý các div đã có sẵn
273            render();
274
275            // Lắng nghe các div được thêm sau này
276            const observer = new MutationObserver(() => {
277                render();
278            });
279
280            observer.observe(document.body, {
281                childList: true,
282                subtree: true
283            });
284        }
285
286        static appendCloseAdsButton() {
287            const videoTarget = document.querySelector(".gwd-banner-bottom__infeed");
288
289            if (!videoTarget) {
290                return;
291            }
292
293            // Tránh render nhiều lần
294            if (videoTarget.querySelector(".gwd-close-banner-btn")) {
295                return;
296            }
297
298            const button = document.createElement("button");
299
300            button.className =
301                "gwd-close-banner-btn inter absolute right-0 -top-3.5 z-20 flex min-h-2 cursor-pointer items-center gap-1.5 bg-[#F6FF00] px-1 text-[10px] text-black transition-colors md:-top-4 md:text-xs";
302
303            button.type = "button";
304            button.setAttribute("aria-label", "Tắt quảng cáo");
305
306            button.innerHTML = `
307                <span class="whitespace-nowrap font-light">Tắt quảng cáo</span>
308                <span class="inline-flex items-center font-light leading-none text-black" aria-hidden="true">
309                    <span>[</span>
310                    <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
311                        <line x1="18" y1="6" x2="6" y2="18"></line>
312                        <line x1="6" y1="6" x2="18" y2="18"></line>
313                    </svg>
314                    <span>]</span>
315                </span>
316            `;
317
318            button.addEventListener("click", (event) => {
319                event.preventDefault();
320                event.stopPropagation();
321                hideBanner();
322            });
323
324            videoTarget.appendChild(button);
325        }
326
327        static init() {
328            const config = window.GWD_CONFIG_BANNER;
329            const options = window.GWD_CONFIG_OPTIONS || {};
330            const data = config?.data?.data;
331            if (!data) return;
332            const isLiveZone = [
333                "gwd-zone-live",
334                "gwd-zone-saoke"
335            ].some(className => document.body.classList.contains(className));
336
337            this.renderBetButtons(data);
338
339            this.renderChooseOddStream(data);
340
341            this.renderOddListLogo(data);
342            
343            // bottom banner
344            const videoTarget = document.querySelector(".gwd_banner_video_bottom");
345            if (videoTarget && isLiveZone) this.renderVideoBottom(data, videoTarget);
346
347            this.appendCloseAdsButton();
348
349            // top banner
350            const videoTopTarget = document.querySelector(".gwd_banner_top_marquee");
351            
352            if (videoTopTarget && isLiveZone) this.renderVideoTop(data, videoTopTarget);
353
354            const topOverlayTarget = document.querySelector(".gwd_banner_top_video_wrapper");
355            if (topOverlayTarget && isLiveZone) this.renderTopOverlay(data, topOverlayTarget);
356
357            // gutter banner
358            const gutterReference = document.querySelector(
359                "#header, #masthead, .site-header, header"
360            ) || document.body.firstElementChild;
361
362            if (gutterReference && options.gutter) {
363                this.renderGutterAfterHeader(data, gutterReference);
364            }            
365        }
366    }
367    
368    document.addEventListener("DOMContentLoaded", () => GWD_Banner_Renderer.init());
369    document.addEventListener("DOMContentLoaded", () => GWD_Banner_Renderer.countDivInsSiteLive());
370
371
372    let bannerTimer = null;
373let bannerShowTimer = null;
374let overlayTimer = null;
375let isBannerDown = false;
376
377const HIDE_DURATION = 30000;
378const SHOW_DURATION = 10000;
379const OVERLAY_DELAY = 1000;
380const OVERLAY_DURATION = 10000;
381
382function getBannerEls() {
383    return {
384        videoTarget: document.querySelector(".gwd_banner_video_bottom"),
385        videoTargetDown: document.querySelector(".gwd_controls_banner"),
386        videoControl: document.querySelector(".gwd_controls"),
387        overlay: document.querySelector(".gwd-banner-bottom__overlay"),
388        overlayImg: document.querySelector(".gwd-banner-bottom__overlay img"),
389    };
390}
391
392function clearBannerTimers() {
393    clearTimeout(bannerTimer);
394    clearTimeout(bannerShowTimer);
395    clearTimeout(overlayTimer);
396}
397
398function updateBannerPosition() {
399    const { videoTarget, videoTargetDown, videoControl } = getBannerEls();
400
401    if (!videoTarget || !videoTargetDown || !videoControl || !isBannerDown) {
402        return;
403    }
404
405    const height = videoTarget.offsetHeight;
406
407    videoControl.style.paddingBottom = "12px";
408
409    // Chỉ di chuyển nguyên cụm theo trục Y
410    videoTargetDown.style.transition = "transform .3s ease";
411    videoTargetDown.style.transform = `translate3d(0, ${height}px, 0)`;
412}
413
414function resetOverlayImg() {
415    const { overlayImg } = getBannerEls();
416
417    if (!overlayImg) {
418        return;
419    }
420
421    // Chỉ reset theo trục X
422    overlayImg.style.transition = "none";
423    overlayImg.style.transform = "translate3d(-100%, 0, 0)";
424}
425
426function startOverlayProgress() {
427    const { overlayImg } = getBannerEls();
428
429    if (!overlayImg) {
430        return;
431    }
432
433    resetOverlayImg();
434
435    overlayImg.offsetHeight;
436
437    // Chỉ chạy ngang từ trái sang phải
438    overlayImg.style.transition = `transform ${OVERLAY_DURATION}ms linear`;
439    overlayImg.style.transform = "translate3d(0, 0, 0)";
440}
441
442function hideBanner() {
443    const { videoTarget } = getBannerEls();
444
445    if (!videoTarget) {
446        return;
447    }
448
449    clearBannerTimers();
450
451    isBannerDown = true;
452    videoTarget.classList.add("gwd-down");
453
454    // Bước 1: nguyên cụm đi từ trên xuống
455    updateBannerPosition();
456
457    // Bước 2: sau 1s ảnh overlay mới chạy từ trái sang phải
458    overlayTimer = setTimeout(() => {
459        startOverlayProgress();
460    }, OVERLAY_DELAY);
461
462    // Sau 30s show lại
463    bannerTimer = setTimeout(() => {
464        showBannerBack();
465    }, HIDE_DURATION);
466}
467
468function showBannerBack() {
469    const { videoTargetDown, videoControl, overlay, overlayImg } = getBannerEls();
470
471    if (!videoTargetDown || !videoControl) {
472        return;
473    }
474
475    isBannerDown = false;
476
477    // Không remove gwd-down
478    videoTargetDown.style.transition = "transform .3s ease";
479    videoTargetDown.style.transform = "translate3d(0, -10px, 0)";
480
481    videoControl.style.paddingBottom = "";
482
483    if (overlay) {
484        overlay.style.transition = "";
485        overlay.style.transform = "";
486    }
487
488    if (overlayImg) {
489        overlayImg.style.transition = "none";
490        overlayImg.style.transform = "translate3d(0, 0, 0)";
491    }
492
493    clearTimeout(bannerTimer);
494    bannerTimer = null;
495
496    // Sau khi show 10s thì ẩn lại
497    bannerShowTimer = setTimeout(() => {
498        hideBanner();
499    }, SHOW_DURATION);
500}
501
502function recalcBannerPositionOnly() {
503    if (!isBannerDown) {
504        return;
505    }
506
507    requestAnimationFrame(() => {
508        requestAnimationFrame(updateBannerPosition);
509    });
510}
511
512document.addEventListener("click", (e) => {
513    const target = e.target instanceof Element ? e.target : e.target.parentElement;
514    const balloonClose = target?.closest("[data-gwd-balloon-close]");
515
516    if (balloonClose) {
517        e.preventDefault();
518        e.stopPropagation();
519
520        balloonClose.closest("[data-gwd-balloon]")?.classList.add("gwd-hide");
521        return;
522    }
523
524    if (target?.closest(".gwd-close-banner-btn")) {
525        e.preventDefault();
526        hideBanner();
527        return;
528    }
529
530    if (target?.closest(".gwd_fullscreen-btn")) {
531        recalcBannerPositionOnly();
532    }
533});
534
535window.addEventListener("resize", recalcBannerPositionOnly);
536
537})();

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.