PageSourceSearch

https://zombits.io/wp-content/plugins/brandview/script.js?ver=1791169550

js zombits.io collected 2026-10-05 03:06:35 UTC 26,897 bytes, 666 lines download raw bytes

1(function () {
2    'use strict';
3
4    function initMarquee() {
5        document.querySelectorAll('.tf-title, .tf-desc').forEach(function (el) {
6            var inner = el.querySelector('.tf-title-inner, .tf-desc-inner');
7            if (!inner) return;
8
9            inner.style.animation = 'none';
10            inner.style.removeProperty('--tf-scroll');
11            void inner.offsetWidth;
12
13            var innerW = inner.getBoundingClientRect().width;
14            var elW = el.getBoundingClientRect().width;
15            if (innerW <= elW) return;
16
17            el.style.overflow = 'hidden';
18
19            var dist = innerW - elW;
20            var dur = Math.max(4, dist / 30);
21
22            inner.style.setProperty('--tf-scroll', '-' + dist + 'px');
23            inner.style.animation = 'tf-scroll ' + dur + 's ease-in-out infinite';
24            inner.style.animationDelay = '1s';
25        });
26    }
27
28    var style = document.createElement('style');
29    style.textContent = '@keyframes tf-scroll { 0% { transform: translateX(0); } 40% { transform: translateX(var(--tf-scroll)); } 60% { transform: translateX(var(--tf-scroll)); } 100% { transform: translateX(0); } }';
30    document.head.appendChild(style);
31
32    var AJAX_URL = typeof tfTrack !== 'undefined' && tfTrack.ajaxurl ? tfTrack.ajaxurl : '';
33
34    function initClickTracking() {
35        if (!AJAX_URL) return;
36        document.addEventListener('click', function (e) {
37            if (!(e.target instanceof Element)) return;
38            var link = e.target.closest('.tf-item, .tf-s3-item, .tf-catfish .logo-item');
39            if (!link) return;
40            var code = link.getAttribute('data-code') || '';
41            var domain = '';
42            try { domain = new URL(link.href).hostname; } catch (e2) {}
43
44            var data = new URLSearchParams();
45            data.append('action', 'tf_track_click');
46            data.append('domain', domain);
47            data.append('brand', code);
48            data.append('target_url', link.href);
49
50            fetch(AJAX_URL, { method: 'POST', body: data, keepalive: true });
51        });
52    }
53
54    function updateCtaVisibility() {
55        document.querySelectorAll('.tf-section').forEach(function (section) {
56            var w = section.clientWidth;
57            var isSlider = section.classList.contains('tf-theme-style3') || section.classList.contains('tf-theme-style4');
58            section.style.display = w < 280 ? 'none' : '';
59            if (isSlider) {
60                section.classList.toggle('tf-s3-small', section.style.display !== 'none' && w < 390);
61            } else {
62                section.classList.toggle('tf-cta-hidden', w < 360);
63            }
64        });
65    }
66
67    var roTimer;
68    function initSlider(section) {
69        var track = section.querySelector('.tf-s3-track');
70        var prev = section.querySelector('.tf-s3-prev');
71        var next = section.querySelector('.tf-s3-next');
72        var wrap = section.querySelector('.tf-s3-track-wrap');
73        if (!track || !prev || !next || !wrap) return;
74
75        var items = track.children;
76        if (items.length === 0) return;
77
78        fillTrack();
79        var currentPos = 0;
80        var autoTimer = null;
81
82        function fillTrack() {
83            var wrapW = wrap.getBoundingClientRect().width;
84            var gap = getGap();
85            var totalW = 0;
86            var originals = Array.from(items);
87            if (originals.length === 0) return;
88            for (var i = 0; i < originals.length; i++) {
89                totalW += originals[i].offsetWidth;
90                if (i < originals.length - 1) totalW += gap;
91            }
92            var minClones = 1;
93            var clonesAdded = 0;
94            while ((totalW < wrapW || clonesAdded < minClones) && clonesAdded < 20) {
95                for (var i = 0; i < originals.length && (totalW < wrapW || clonesAdded < minClones); i++) {
96                    var clone = originals[i].cloneNode(true);
97                    track.appendChild(clone);
98                    totalW += originals[i].offsetWidth + gap;
99                }
100                clonesAdded++;
101            }
102        }
103
104        function getItemWidth() {
105            if (items.length === 0) return 0;
106            return items[0].getBoundingClientRect().width;
107        }
108
109        function getGap() {
110            return parseFloat(getComputedStyle(track).gap) || 0;
111        }
112
113        function getItemsPerView() {
114            var wrapW = wrap.getBoundingClientRect().width;
115            var itemW = getItemWidth();
116            var gap = getGap();
117            var step = itemW + gap;
118            if (step <= 0) return 1;
119            return Math.max(1, Math.floor(wrapW / step));
120        }
121
122        function getMaxTranslate() {
123            var wrapW = wrap.getBoundingClientRect().width;
124            var gap = getGap();
125            var totalItemW = 0;
126            for (var i = 0; i < items.length; i++) {
127                totalItemW += items[i].getBoundingClientRect().width;
128            }
129            var totalGaps = (items.length - 1) * gap;
130            return Math.max(0, totalItemW + totalGaps - wrapW);
131        }
132
133        function getStep() {
134            var perView = getItemsPerView();
135            return perView >= 4 ? 2 : 1;
136        }
137
138        function update(instant) {
139            if (section.offsetParent === null) return;
140            var prevTransition = track.style.transition;
141            if (instant) {
142                track.style.transition = 'none';
143            }
144            var itemW = getItemWidth();
145            var gap = getGap();
146            var step = itemW + gap;
147            if (step <= 0) return;
148            var maxTx = getMaxTranslate();
149            var maxPos = maxTx > 0 ? Math.ceil(maxTx / step) : 0;
150            if (currentPos > maxPos) currentPos = 0;
151            if (currentPos < 0) currentPos = maxPos;
152            var tx = Math.min(currentPos * step, maxTx);
153            track.style.transform = 'translateX(-' + tx + 'px)';
154            if (instant) {
155                void track.offsetHeight;
156                track.style.transition = prevTransition;
157            }
158        }
159
160        function goNext() {
161            var s = getStep();
162            var maxTx = getMaxTranslate();
163            var step = getItemWidth() + getGap();
164            var maxPos = maxTx > 0 ? Math.ceil(maxTx / step) : 0;
165            if (currentPos >= maxPos) {
166                currentPos = 0;
167                update(true);
168            } else {
169                currentPos = Math.min(currentPos + s, maxPos);
170                update();
171            }
172        }
173
174        function goPrev() {
175            var s = getStep();
176            var maxTx = getMaxTranslate();
177            var step = getItemWidth() + getGap();
178            var maxPos = maxTx > 0 ? Math.ceil(maxTx / step) : 0;
179            if (currentPos <= 0) {
180                currentPos = maxPos;
181                update(true);
182            } else {
183                currentPos = Math.max(currentPos - s, 0);
184                update();
185            }
186        }
187
188        function startAuto() {
189            stopAuto();
190            autoTimer = setInterval(goNext, 3000);
191        }
192
193        function stopAuto() {
194            if (autoTimer) {
195                clearInterval(autoTimer);
196                autoTimer = null;
197            }
198        }
199
200        next.addEventListener('click', function () { stopAuto(); goNext(); });
201        prev.addEventListener('click', function () { stopAuto(); goPrev(); });
202
203        section.addEventListener('mouseenter', stopAuto);
204        section.addEventListener('mouseleave', startAuto);
205
206        var startX = 0, isDragging = false;
207        track.addEventListener('touchstart', function (e) {
208            stopAuto();
209            startX = e.touches[0].clientX;
210            isDragging = true;
211        }, { passive: true });
212
213        track.addEventListener('touchend', function (e) {
214            if (!isDragging) return;
215            isDragging = false;
216            var endX = e.changedTouches[0].clientX;
217            var diff = startX - endX;
218            if (Math.abs(diff) >= 50) {
219                var maxTx = getMaxTranslate();
220                var step = getItemWidth() + getGap();
221                var maxPos = maxTx > 0 ? Math.ceil(maxTx / step) : 0;
222                if (diff > 0) {
223                    if (currentPos >= maxPos) { currentPos = 0; update(true); }
224                    else { currentPos = Math.min(currentPos + getStep(), maxPos); update(); }
225                } else {
226                    if (currentPos <= 0) { currentPos = maxPos; update(true); }
227                    else { currentPos = Math.max(currentPos - getStep(), 0); update(); }
228                }
229            }
230            startAuto();
231        }, { passive: true });
232
233        update();
234        startAuto();
235        return { update: update, destroy: stopAuto };
236    }
237
238    var sliders = [];
239
240    // ========================================
241    // CATFISH BANNER
242    // ========================================
243    function initCatfish() {
244        document.querySelectorAll('.tf-catfish').forEach(function (root) {
245            var id = root.getAttribute('data-uid') || 'tf-catfish';
246            var banner = root.querySelector('.catfish-banner');
247            var bubble = root.querySelector('.promo-bubble');
248            var bubbleLine1 = root.querySelector('.bubble-line1');
249            var bubbleLine2 = root.querySelector('.bubble-line2');
250            var bubbleArrow = root.querySelector('.bubble-arrow');
251            var wrapper = root.querySelector('.logo-swiper-wrapper');
252            var sliderContainer = root.querySelector('.logo-slider-container');
253            var swiperEl = root.querySelector('.logo-swiper');
254            var hideBtn = root.querySelector('.hide-ad-btn');
255            var collapsed = root.querySelector('.collapsed-banner');
256            var countdownEl = root.querySelector('.countdown-value');
257            var showBtn = root.querySelector('.collapsed-content');
258            if (!banner || !wrapper || !hideBtn || !showBtn) return;
259
260            var countdownDuration = parseInt(root.getAttribute('data-countdown') || '180', 10);
261            var STORAGE_END = 'catfishBannerEndTime_' + id;
262            var STORAGE_HIDDEN = 'catfishBannerHidden_' + id;
263            var MOBILE_BREAKPOINT = 768;
264            var AUTO_SLIDE_INTERVAL = 5000;
265            var SLIDE_SPEED = 600;
266
267            var state = {
268                isMobile: window.innerWidth < MOBILE_BREAKPOINT,
269                isHidden: false,
270                swiperInstance: null,
271                currentSlideIndex: 0,
272                slideTimer: null,
273                bubbleTimer: null,
274                autoSlidePaused: false
275            };
276
277            function isMobile() {
278                return window.innerWidth < MOBILE_BREAKPOINT;
279            }
280
281            var logoCount = wrapper.querySelectorAll('.logo-item').length;
282            if (logoCount === 0) return;
283
284            if (state.isMobile) banner.classList.add('is-mobile');
285
286            // --- Swiper slider (logic from catfish-banner-plugin) ---
287            function getSlidesPerView() {
288                var available = sliderContainer ? sliderContainer.clientWidth : 1000;
289                var slideW = state.isMobile ? 60 : 80;
290                var space = state.isMobile ? 4 : 10;
291                return Math.max(1, Math.floor(available / (slideW + space)));
292            }
293
294            function setSwiperWidth() {
295                if (!swiperEl) return;
296                var slideW = state.isMobile ? 60 : 80;
297                var space = state.isMobile ? 4 : 10;
298                var perView = getSlidesPerView();
299                var exactWidth = (perView * slideW) + ((perView - 1) * space);
300                swiperEl.style.width = exactWidth + 'px';
301            }
302
303            function ensureSlideCoverage() {
304                if (logoCount <= 0) return;
305                var perView = getSlidesPerView();
306                var minSlides = Math.max(logoCount * 3, perView * 2);
307                var slides = wrapper.querySelectorAll('.swiper-slide:not(.swiper-slide-duplicate)');
308                if (slides.length >= minSlides) return;
309                var originals = Array.prototype.slice.call(slides);
310                var i = 0;
311                while (wrapper.querySelectorAll('.swiper-slide:not(.swiper-slide-duplicate)').length < minSlides) {
312                    wrapper.appendChild(originals[i % originals.length].cloneNode(true));
313                    i++;
314                }
315            }
316
317            function initSwiper() {
318                if (typeof Swiper === 'undefined') return;
319                var dup = wrapper.querySelectorAll('.swiper-slide-duplicate');
320                for (var d = 0; d < dup.length; d++) dup[d].parentNode.removeChild(dup[d]);
321                state.isMobile = isMobile();
322                if (state.isMobile) banner.classList.add('is-mobile');
323                else banner.classList.remove('is-mobile');
324
325                setSwiperWidth();
326                ensureSlideCoverage();
327                state.swiperInstance = new Swiper(swiperEl, {
328                    direction: 'horizontal',
329                    loop: true,
330                    loopedSlides: logoCount,
331                    loopAdditionalSlides: 0,
332                    freeMode: {
333                        enabled: true,
334                        momentum: true,
335                        momentumRatio: 0.5,
336                        momentumBounceRatio: 1
337                    },
338                    slidesPerView: getSlidesPerView(),
339                    slidesPerGroup: 1,
340                    spaceBetween: state.isMobile ? 4 : 10,
341                    simulateTouch: true,
342                    touchEventsTarget: 'container',
343                    touchRatio: 1,
344                    longSwipesRatio: 0.5,
345                    allowTouchMove: true,
346                    autoplay: false,
347                    speed: SLIDE_SPEED,
348                    centeredSlides: false,
349                    observer: true,
350                    observeParents: true,
351                    observeSlideChildren: true,
352                    watchOverflow: true,
353                    on: {
354                        init: function () {
355                            state.currentSlideIndex = 0;
356                            setSwiperWidth();
357                            if (window.innerWidth >= 2500) {
358                                this.wrapperEl.style.transform = 'translate3d(0px, 0px, 0px)';
359                            }
360                        },
361                        touchStart: function () {
362                            state.autoSlidePaused = true;
363                            stopAutoSlide();
364                        },
365                        touchEnd: function () {
366                            setTimeout(function () {
367                                state.autoSlidePaused = false;
368                                startAutoSlide();
369                            }, 1500);
370                        },
371                        resize: function (swiper) {
372                            setTimeout(function () {
373                                state.isMobile = isMobile();
374                                setSwiperWidth();
375                                swiper.params.slidesPerView = getSlidesPerView();
376                                swiper.params.slideWidth = state.isMobile ? 60 : 80;
377                                swiper.params.spaceBetween = state.isMobile ? 4 : 10;
378                                swiper.params.freeMode.enabled = true;
379                                swiper.params.freeMode.momentum = true;
380                                swiper.params.simulateTouch = true;
381                                swiper.params.allowTouchMove = true;
382                                swiper.update();
383                                startAutoSlide();
384                            }, 150);
385                        }
386                    }
387                });
388            }
389
390            function resetSlider() {
391                state.currentSlideIndex = 0;
392                if (state.swiperInstance) {
393                    state.swiperInstance.slideTo(logoCount, SLIDE_SPEED, false);
394                }
395            }
396
397            function startAutoSlide() {
398                if (state.isHidden) return;
399                if (state.slideTimer) return;
400                if (state.autoSlidePaused) return;
401                scheduleNextSlide();
402            }
403
404            function stopAutoSlide() {
405                if (state.slideTimer) {
406                    clearTimeout(state.slideTimer);
407                    state.slideTimer = null;
408                }
409                hideBubble();
410            }
411
412            function scheduleNextSlide() {
413                if (state.isHidden) return;
414                if (!state.swiperInstance) return;
415                state.slideTimer = setTimeout(function () {
416                    slideToNextAndShow();
417                }, AUTO_SLIDE_INTERVAL);
418            }
419
420            function slideToNextAndShow() {
421                if (state.isHidden) return;
422                var swiper = state.swiperInstance;
423                if (!swiper) {
424                    scheduleNextSlide();
425                    return;
426                }
427                var looped = logoCount;
428                var currentIndex = swiper.activeIndex;
429                var targetIndex;
430                if (currentIndex < looped) {
431                    targetIndex = currentIndex + 1;
432                    if (targetIndex >= looped) targetIndex = looped;
433                } else {
434                    targetIndex = currentIndex + 1;
435                    if (targetIndex >= looped * 2) targetIndex = looped;
436                }
437                state.currentSlideIndex = (state.currentSlideIndex + 1) % logoCount;
438                swiper.slideTo(targetIndex, SLIDE_SPEED, true);
439                scheduleNextSlide();
440            }
441
442            // --- Promo bubble ---
443            function setBubbleContent(el) {
444                if (!bubbleLine1 || !bubbleLine2) return;
445                bubbleLine1.textContent = el.getAttribute('data-line1') || '';
446                bubbleLine2.textContent = el.getAttribute('data-line2') || '';
447            }
448
449            function positionAndShowDesktopBubble(el) {
450                setBubbleContent(el);
451                var rect = el.getBoundingClientRect();
452                var viewportWidth = window.innerWidth;
453                var bubbleWidth = bubble.offsetWidth || 270;
454                var finalX = rect.left + (rect.width / 2) - (bubbleWidth / 2);
455                var arrowPercentage = 50;
456                if (finalX < 0) {
457                    finalX = 0;
458                    arrowPercentage = ((rect.left + rect.width / 2) / bubbleWidth) * 100;
459                }
460                var bubbleRightEdge = finalX + bubbleWidth;
461                if (bubbleRightEdge > viewportWidth) {
462                    finalX = viewportWidth - bubbleWidth;
463                    arrowPercentage = ((rect.left + rect.width / 2 - finalX) / bubbleWidth) * 100;
464                }
465                arrowPercentage = Math.max(15, Math.min(85, arrowPercentage));
466                bubble.style.left = finalX + 'px';
467                if (bubbleArrow) bubbleArrow.style.left = arrowPercentage + '%';
468                bubble.classList.add('visible');
469            }
470
471            function hideBubble() {
472                bubble.classList.remove('visible');
473            }
474
475            // --- Hover (desktop) ---
476            wrapper.addEventListener('mouseenter', function (e) {
477                if (isMobile()) return;
478                var el = e.target.closest('.logo-item');
479                if (el) {
480                    state.autoSlidePaused = true;
481                    stopAutoSlide();
482                    positionAndShowDesktopBubble(el);
483                }
484            }, true);
485
486            wrapper.addEventListener('mouseleave', function (e) {
487                if (isMobile()) return;
488                var el = e.target.closest('.logo-item');
489                if (el && !wrapper.contains(e.relatedTarget)) {
490                    hideBubble();
491                    state.autoSlidePaused = false;
492                    startAutoSlide();
493                }
494            }, true);
495
496            // --- Hide / show + countdown ---
497            function setStorage(key, val) {
498                try { localStorage.setItem(key, val); } catch (e) { }
499            }
500            function getStorage(key) {
501                try { return localStorage.getItem(key); } catch (e) { return null; }
502            }
503            function clearStorage() {
504                try {
505                    localStorage.removeItem(STORAGE_END);
506                    localStorage.removeItem(STORAGE_HIDDEN);
507                } catch (e) { }
508            }
509
510            function hideBanner() {
511                state.isHidden = true;
512                stopAutoSlide();
513                hideBubble();
514                resetSlider();
515                var endTime = Date.now() + (countdownDuration * 1000);
516                setStorage(STORAGE_END, endTime.toString());
517                setStorage(STORAGE_HIDDEN, 'true');
518                banner.classList.add('hidden');
519                collapsed.classList.add('visible');
520                startCountdown();
521            }
522
523            function showBanner() {
524                state.isHidden = false;
525                stopCountdown();
526                clearStorage();
527                collapsed.classList.remove('visible');
528                banner.classList.remove('hidden');
529                resetSlider();
530                startAutoSlide();
531            }
532
533            function startCountdown() {
534                stopCountdown();
535                var endTime;
536                var stored = getStorage(STORAGE_END);
537                if (stored) {
538                    endTime = parseInt(stored, 10);
539                } else {
540                    endTime = Date.now() + (countdownDuration * 1000);
541                    setStorage(STORAGE_END, endTime.toString());
542                }
543                updateCountdown(endTime);
544                state.bubbleTimer = setInterval(function () {
545                    updateCountdown(endTime);
546                }, 1000);
547            }
548
549            function updateCountdown(endTime) {
550                var remaining = Math.max(0, Math.ceil((endTime - Date.now()) / 1000));
551                if (countdownEl) countdownEl.textContent = remaining;
552                if (remaining <= 0) showBanner();
553            }
554
555            function stopCountdown() {
556                if (state.bubbleTimer) {
557                    clearInterval(state.bubbleTimer);
558                    state.bubbleTimer = null;
559                }
560            }
561
562            function restoreState() {
563                if (getStorage(STORAGE_HIDDEN) === 'true') {
564                    var stored = getStorage(STORAGE_END);
565                    if (stored && parseInt(stored, 10) > Date.now()) {
566                        state.isHidden = true;
567                        banner.classList.add('hidden');
568                        collapsed.classList.add('visible');
569                        startCountdown();
570                    } else {
571                        clearStorage();
572                    }
573                }
574            }
575
576            hideBtn.addEventListener('click', hideBanner);
577            showBtn.addEventListener('click', showBanner);
578
579            // --- Resize / orientation handling ---
580            var resizeTimer;
581            var isOrientationChange = false;
582            window.addEventListener('resize', function () {
583                clearTimeout(resizeTimer);
584                resizeTimer = setTimeout(function () {
585                    var wasMobile = state.isMobile;
586                    state.isMobile = isMobile();
587                    if (state.isMobile !== wasMobile) {
588                        state.autoSlidePaused = false;
589                    }
590                    if (state.isMobile && !wasMobile) {
591                        banner.classList.add('is-mobile');
592                        startAutoSlide();
593                    } else if (!state.isMobile && wasMobile) {
594                        banner.classList.remove('is-mobile');
595                        stopAutoSlide();
596                    }
597                    if (state.swiperInstance) {
598                        if (isOrientationChange) {
599                            state.swiperInstance.detachEvents();
600                            state.swiperInstance.destroy(false, true);
601                            initSwiper();
602                            if (wasMobile) startAutoSlide();
603                        } else {
604                            setSwiperWidth();
605                            if (wrapper.querySelectorAll('.swiper-slide:not(.swiper-slide-duplicate)').length < getSlidesPerView() * 2) {
606                                state.swiperInstance.detachEvents();
607                                state.swiperInstance.destroy(false, true);
608                                initSwiper();
609                            } else {
610                                state.swiperInstance.update();
611                                state.swiperInstance.updateSize();
612                                state.swiperInstance.updateSlides();
613                                state.swiperInstance.updateProgress();
614                                state.swiperInstance.updateSlidesClasses();
615                            }
616                        }
617                    }
618                    isOrientationChange = false;
619                }, isOrientationChange ? 300 : 100);
620            });
621
622            window.addEventListener('orientationchange', function () {
623                isOrientationChange = true;
624            });
625
626            initSwiper();
627            startAutoSlide();
628            restoreState();
629        });
630    }
631
632    function initAll() {
633        initMarquee();
634        initClickTracking();
635        updateCtaVisibility();
636        initCatfish();
637        var sections = document.querySelectorAll('.tf-section');
638        sections.forEach(function (section) {
639            if (section.classList.contains('tf-theme-style3') || section.classList.contains('tf-theme-style4')) {
640                var sl = initSlider(section);
641                if (sl) sliders.push(sl);
642            }
643        });
644    }
645
646    var ro = new ResizeObserver(function () {
647        clearTimeout(roTimer);
648        roTimer = setTimeout(function () {
649            initMarquee();
650            updateCtaVisibility();
651            sliders.forEach(function (s) { if (s.update) s.update(); });
652        }, 150);
653    });
654
655    if (document.readyState === 'loading') {
656        document.addEventListener('DOMContentLoaded', function () {
657            initAll();
658            var section = document.querySelector('.tf-section');
659            if (section) ro.observe(section);
660        });
661    } else {
662        initAll();
663        var section = document.querySelector('.tf-section');
664        if (section) ro.observe(section);
665    }
666})();

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.