PageSourceSearch

https://www.fulltravel.it/framework/assets/js/global.js?x69729

js fulltravel.it collected 2026-09-24 08:56:35 UTC 127,806 bytes, 3,488 lines download raw bytes

1/**  FOXIZ MAIN SCRIPT */
2
3/** Ensure RB_VIEWPORT_OBSERVER is available */
4if (typeof RB_VIEWPORT_OBSERVER === 'undefined') {
5    var RB_VIEWPORT_OBSERVER = {
6        observe: function () {
7            return null;
8        },
9        unobserve: function () {},
10        destroy: function () {},
11        refreshAll: function () {},
12        has: function () {
13            return false;
14        },
15        getActiveIds: function () {
16            return [];
17        },
18        destroyAll: function () {},
19    };
20}
21
22var FOXIZ_MAIN_SCRIPT = (function (Module, $) {
23    'use strict';
24
25    Module.initParams = function () {
26        this.yesStorage = this.isStorageAvailable();
27        this.themeSettings = typeof foxizParams !== 'undefined' ? foxizParams : {};
28        this.galleriesData = typeof foxizGalleriesData !== 'undefined' ? foxizGalleriesData : {};
29        this.ajaxURL = typeof foxizCoreParams !== 'undefined' ? foxizCoreParams.ajaxurl || '' : '';
30        this.ajaxData = {};
31        this.siteAccessFlag = false;
32        this._document = $(document);
33        this._body = $('body');
34        this._window = $(window);
35        this.html = $('html, body');
36        this.outerHTML = $('html');
37        this.iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
38        this.sticky = {};
39        this.eSticky = {};
40        this.YTPlayers = {};
41        this.articleData = [];
42        this.readIndicatorPercent = 0;
43        this.isProgress = false;
44        this.readIndicator = $('#reading-progress');
45        this.popupNewsletterEl = $('#rb-popup-newsletter');
46
47        this.personalizeUID =
48            typeof FOXIZ_CORE_SCRIPT !== 'undefined' && FOXIZ_CORE_SCRIPT.personalizeUID !== undefined
49                ? FOXIZ_CORE_SCRIPT.personalizeUID
50                : 'u0';
51
52        // Unified scroll manager
53        this._scrollHandlers = {};
54        this._scrollTicking = false;
55        this._scrollListenerActive = false;
56    };
57
58    /**
59     * Unified Scroll Manager - single listener for all scroll handlers
60     */
61    Module._onScroll = function () {
62        const self = this;
63        if (!self._scrollTicking) {
64            self._scrollTicking = true;
65            requestAnimationFrame(function () {
66                const handlers = self._scrollHandlers;
67                for (const id in handlers) {
68                    if (handlers[id]) handlers[id]();
69                }
70                self._scrollTicking = false;
71            });
72        }
73    };
74
75    Module.addScrollHandler = function (id, handler) {
76        this._scrollHandlers[id] = handler;
77        if (!this._scrollListenerActive) {
78            this._scrollListenerActive = true;
79            window.addEventListener('scroll', this._onScroll.bind(this), { passive: true });
80        }
81    };
82
83    Module.removeScrollHandler = function (id) {
84        delete this._scrollHandlers[id];
85    };
86
87    Module.init = function () {
88        this.initParams();
89        this.syncLayoutLike();
90        this.topSpacing();
91        this.siteAccessDetector();
92        this.headerDropdown();
93        this.mobileCollapse();
94        this.initSubMenuPos();
95        this.privacyTrigger();
96        this.popupNewsletter();
97        this.documentClick();
98        this.backTop();
99        this.readIndicatorInit();
100        this.fontResizer();
101        this.breakingNews();
102        this.sequentialGalleryDisplay();
103        this.sliders();
104        this.carousels();
105        this.liveSearch();
106        this.personalizeBlocks();
107        this.personalizeCategories();
108        this.likeToggle();
109        this.singleInfiniteLoadNext();
110        this.loginPopup();
111        this.popupTemplate();
112        this.loadYoutubeIframe();
113        this.browserResize();
114        this.footerSlideUp();
115        this.cartNotification();
116        this.newsNotification();
117        this.tocToggle();
118        this.delayLoad();
119        this.showPostComment();
120        this.replyReview();
121        this.paginationNextPrev();
122        this.paginationLoadMore();
123        this.paginationInfinite();
124        this.productQuantity();
125        this.readingCollect();
126        this.liveBlog();
127        this.neededReloadFuncs();
128    };
129
130    Module.neededReloadFuncs = function () {
131        this.hoverTipsy();
132        this.scrollToComment();
133        this.usersRating();
134        this.singleGallery();
135        this.floatingVideo();
136        this.videoPreview();
137        this.floatingVideoRemove();
138        this.scrollTableContent();
139        this.singleScrollRefresh();
140        this.playerAutoPlay();
141        this.hoverEffects();
142        this.highlightShares();
143        this.galleryLightbox();
144        this.singleFeaturedLightbox();
145        this.accordion();
146        this.resIframeClassic();
147        this.taxBasedAccordion();
148        this.scrollBarSlider();
149    };
150
151    Module.reInitAll = function () {
152        this._window.trigger('load');
153        this.syncLayoutLike();
154        if (typeof FOXIZ_PERSONALIZE !== 'undefined') {
155            FOXIZ_PERSONALIZE.syncPersonalizeLayout();
156        }
157        if (typeof RB_REACTION !== 'undefined') {
158            RB_REACTION.syncReactLayout();
159        }
160        this.neededReloadFuncs();
161        RB_VIEWPORT_OBSERVER.refreshAll();
162    };
163
164    /** sync layout & reload features */
165    Module.reloadBlockFunc = function () {
166        this._window.trigger('load');
167
168        if (typeof FOXIZ_PERSONALIZE !== 'undefined') {
169            FOXIZ_PERSONALIZE.syncLayoutBookmarks();
170        }
171        this.syncLayoutLike();
172        this.videoPreview();
173        this.hoverTipsy();
174        RB_VIEWPORT_OBSERVER.refreshAll();
175    };
176
177    Module.initElementor = function () {
178        if ('undefined' !== typeof initDarkMode && !FOXIZ_MAIN_SCRIPT.editorDarkModeInit) {
179            FOXIZ_MAIN_SCRIPT.editorDarkModeInit = true;
180            initDarkMode();
181        }
182        FOXIZ_MAIN_SCRIPT.breakingNews();
183        FOXIZ_MAIN_SCRIPT.carousels();
184        FOXIZ_MAIN_SCRIPT.sliders();
185    };
186
187    Module.isRTL = function () {
188        return this._body.hasClass('rtl');
189    };
190
191    Module.animationFrame = function (callback) {
192        const func =
193            window.requestAnimationFrame ||
194            window.mozRequestAnimationFrame ||
195            window.webkitRequestAnimationFrame ||
196            this.animationFrameFallback;
197        func.call(window, callback);
198    };
199
200    Module.animationFrameFallback = function (callback) {
201        window.setTimeout(callback, 1000 / 60);
202    };
203
204    /**
205     *
206     * @returns {boolean}
207     */
208    Module.isStorageAvailable = function () {
209        let storage;
210        try {
211            storage = window['localStorage'];
212            storage.setItem('__rbStorageSet', 'x');
213            storage.removeItem('__rbStorageSet');
214            return true;
215        } catch (e) {
216            return false;
217        }
218    };
219
220    /**
221     * set localStorage
222     * @param key
223     * @param data
224     */
225    Module.setStorage = function (key, data) {
226        this.yesStorage && localStorage.setItem(key, typeof data === 'string' ? data : JSON.stringify(data));
227    };
228
229    /**
230     * get localStorage
231     * @param key
232     * @param defaultValue
233     * @returns {any}
234     */
235    Module.getStorage = function (key, defaultValue) {
236        if (!this.yesStorage) return null;
237        const data = localStorage.getItem(key);
238        if (data === null) return defaultValue;
239        try {
240            return JSON.parse(data);
241        } catch (e) {
242            return data;
243        }
244    };
245
246    /**
247     * delelte localStorage
248     * @param key
249     */
250    Module.deleteStorage = function (key) {
251        this.yesStorage && localStorage.removeItem(key);
252    };
253
254    /**
255     *
256     * @param id
257     * @param value
258     * @param ttl
259     * @constructor
260     */
261    Module.SetTTLStorage = function (id, value, ttl) {
262        const data = {
263            value: value,
264            ttl: Date.now() + ttl * 3600000,
265        };
266
267        this.setStorage(id, data);
268    };
269
270    /**
271     *
272     * @param id
273     * @returns {null|*}
274     */
275    Module.getTTLStorage = function (id) {
276        const data = this.getStorage(id);
277        if (data && Date.now() <= data.ttl) {
278            return data.value;
279        }
280        this.deleteStorage(id);
281        return null;
282    };
283
284    /** on load */
285    Module.delayLoad = function () {
286        const self = this;
287        setTimeout(function () {
288            self.stickyNavBar();
289            self.stickyHeaderBuilder();
290        }, 100);
291    };
292
293    /** resize */
294    Module.browserResize = function () {
295        const self = this;
296
297        self._window.on('resize', function () {
298            self.topSpacing();
299            self.calcSubMenuPos();
300        });
301    };
302
303    /* ================================ HEADERS ================================ */
304    Module.hoverTipsy = function () {
305        if (!$.fn.rbTipsy) {
306            return false;
307        }
308
309        this._body.find('[data-copy]').rbTipsy({
310            title: 'data-copy',
311            fade: true,
312            opacity: 1,
313            trigger: 'hover',
314            gravity: 's',
315        });
316
317        if (window.innerWidth > 1024) {
318            this._body.find('#site-header [data-title]').rbTipsy({
319                title: 'data-title',
320                fade: true,
321                opacity: 1,
322                trigger: 'hover',
323                gravity: 'n',
324            });
325
326            this._body.find('.site-wrap [data-title]').rbTipsy({
327                title: 'data-title',
328                fade: true,
329                opacity: 1,
330                trigger: 'hover',
331                gravity: 's',
332            });
333        }
334    };
335
336    /** font resizer */
337    Module.fontResizer = function () {
338        const self = this;
339        let size = self.yesStorage ? sessionStorage.getItem('rubyResizerStep') : 1;
340
341        self._body.on('click', '.font-resizer-trigger', function (e) {
342            e.preventDefault();
343            e.stopPropagation();
344            size++;
345            if (3 < size) {
346                size = 1;
347                self._body.removeClass('medium-entry-size big-entry-size');
348            } else {
349                if (2 == size) {
350                    self._body.addClass('medium-entry-size').removeClass('big-entry-size');
351                } else {
352                    self._body.addClass('big-entry-size').removeClass('medium-entry-size');
353                }
354            }
355
356            self.yesStorage && sessionStorage.setItem('rubyResizerStep', size);
357        });
358    };
359
360    /** hover */
361    Module.hoverEffects = function () {
362        const selectors = $('.effect-fadeout');
363
364        if (selectors.length === 0) {
365            return;
366        }
367        selectors
368            .off('mouseenter mouseleave')
369            .on('mouseenter', function (e) {
370                e.stopPropagation();
371                const target = $(this);
372                if (!target.hasClass('activated')) {
373                    target.addClass('activated');
374                }
375            })
376            .on('mouseleave', function () {
377                $(this).removeClass('activated');
378            });
379    };
380
381    Module.videoPreview = function () {
382        let playPromise;
383
384        $('.preview-trigger')
385            .on('mouseenter', function () {
386                const target = $(this);
387                const wrap = target.find('.preview-video');
388                if (!wrap.hasClass('video-added')) {
389                    const video =
390                        '<video preload="auto" muted loop><source src="' +
391                        wrap.data('source') +
392                        '" type="' +
393                        wrap.data('type') +
394                        '"></video>';
395                    wrap.append(video).addClass('video-added');
396                }
397                target.addClass('show-preview');
398                wrap.css('z-index', 3);
399                const el = target.find('video')[0];
400                if (el) {
401                    playPromise = el.play();
402                }
403            })
404            .on('mouseleave', function () {
405                const target = $(this);
406                target.find('.preview-video').css('z-index', 1);
407                const el = target.find('video')[0];
408                if (el && playPromise !== undefined) {
409                    playPromise
410                        .then((_) => {
411                            el.pause();
412                        })
413                        .catch();
414                }
415            });
416    };
417
418    Module.playerAutoPlay = function () {
419        const self = this;
420        const items = $('.is-autoplay');
421        const nonResIframe = $('.entry-content > iframe');
422
423        if (items != null && items.length > 0) {
424            items.each(function (index) {
425                const el = $(this);
426                if (!el.hasClass('is-loaded')) {
427                    RB_VIEWPORT_OBSERVER.observe('iframe_' + index, {
428                        element: el,
429                        offset: '70%',
430                        once: true,
431                        handler: function () {
432                            const iframe = el.find('iframe');
433                            self.initAutoPlay(iframe);
434                            el.addClass('is-loaded');
435                        },
436                    });
437                }
438            });
439        }
440
441        if (nonResIframe != null && nonResIframe.length > 0) {
442            nonResIframe.each(function () {
443                const el = $(this);
444                if (!el.hasClass('is-loaded')) {
445                    const iURL = el.attr('src');
446                    if (
447                        iURL.indexOf('youtube.com') > 0 ||
448                        iURL.indexOf('youtu.be') > 0 ||
449                        iURL.indexOf('vimeo.com') > 0
450                    ) {
451                        el.wrap('<div class="rb-ires is-loaded"></div>');
452                    }
453                }
454            });
455        }
456    };
457
458    Module.initAutoPlay = function (item) {
459        if (item.length > 0 && undefined !== item[0]) {
460            const src = item[0].src;
461            if (src.indexOf('?') > -1) {
462                item[0].src += '&autoplay=1';
463            } else {
464                item[0].src += '?autoplay=1';
465            }
466        }
467    };
468
469    Module.tocToggle = function () {
470        this._document.on('click', '.toc-toggle', function (e) {
471            e.preventDefault();
472            e.stopPropagation();
473
474            const target = $(this);
475            const content = target.parents('.ruby-table-contents').find('.toc-content');
476            content.toggle(200);
477            target.toggleClass('activate');
478        });
479    };
480
481    /** Header JS functions */
482    Module.headerDropdown = function () {
483        const self = this;
484        $('.more-trigger').on('click', function (e) {
485            e.preventDefault();
486            e.stopPropagation();
487
488            /** re calc menu  */
489            self.calcSubMenuPos();
490
491            const target = $(this);
492            const holder = target.parents('.header-wrap').find('.more-section-outer');
493
494            if (!holder.hasClass('dropdown-activated')) {
495                self._body.find('.dropdown-activated').removeClass('dropdown-activated');
496                holder.addClass('dropdown-activated');
497            } else {
498                holder.removeClass('dropdown-activated');
499            }
500            if (target.hasClass('search-btn')) {
501                setTimeout(function () {
502                    holder.find('input[type="text"]').focus();
503                }, 150);
504            }
505
506            return false;
507        });
508
509        /** search trigger */
510        $('.search-trigger').on('click', function (e) {
511            e.preventDefault();
512            e.stopPropagation();
513            const holder = $(this).parent('.header-dropdown-outer');
514            if (!holder.hasClass('dropdown-activated')) {
515                self._body.find('.dropdown-activated').removeClass('dropdown-activated');
516                holder.addClass('dropdown-activated');
517                setTimeout(function () {
518                    holder.find('input[type="text"]').focus();
519                }, 150);
520            } else {
521                holder.removeClass('dropdown-activated');
522            }
523            return false;
524        });
525
526        /** header dropdown */
527        $('.dropdown-trigger').on('click', function (e) {
528            e.preventDefault();
529            e.stopPropagation();
530            const holder = $(this).parent('.header-dropdown-outer');
531            if (!holder.hasClass('dropdown-activated')) {
532                self._body.find('.dropdown-activated').removeClass('dropdown-activated');
533                holder.addClass('dropdown-activated');
534            } else {
535                holder.removeClass('dropdown-activated');
536            }
537        });
538    };
539
540    Module.topSpacing = function () {
541        const self = this;
542        if (self._body.hasClass('top-spacing')) {
543            const height = $('.top-site-ad').outerHeight();
544            $('.site-outer').css('margin-top', height);
545        }
546    };
547
548    /** outside click */
549    Module.documentClick = function () {
550        const self = this;
551        const wrapper = $('.more-section-outer, .header-dropdown-outer, .mobile-collapse, .mfp-wrap');
552        const inlineSearchForm = $('.is-form-layout');
553
554        document.addEventListener('click', function (e) {
555            if (!wrapper.is(e.target) && wrapper.has(e.target).length === 0) {
556                wrapper.removeClass('dropdown-activated');
557                self.outerHTML.removeClass('collapse-activated');
558            }
559
560            if (!inlineSearchForm.is(e.target) && inlineSearchForm.has(e.target).length === 0) {
561                inlineSearchForm.find('.live-search-response').fadeOut(500);
562            }
563        });
564    };
565
566    /** calc mega menu position */
567    Module.initSubMenuPos = function () {
568        const self = this;
569        let trigger = false;
570
571        /** add delay to ensure image loaded */
572        setTimeout(function () {
573            self.calcSubMenuPos();
574        }, 1000);
575
576        /** re calc when hovering */
577        $('.menu-has-child-mega').on('mouseenter', function () {
578            if (!trigger) {
579                self.calcSubMenuPos();
580            }
581            trigger = true;
582        });
583    };
584
585    Module.calcSubMenuPos = function () {
586        if (window.outerWidth < 1025) {
587            return false;
588        }
589
590        const self = this;
591        const megaParents = $('.menu-has-child-mega');
592        const headerWrapper = $('#site-header');
593
594        /** for mega wide */
595        if (megaParents.length > 0) {
596            megaParents.each(function () {
597                const item = $(this);
598                item.find('.mega-dropdown').css({
599                    width: self._body.width(),
600                    left: -item.offset().left,
601                });
602                item.addClass('mega-menu-loaded');
603            });
604        }
605
606        /** sub-menu left right direction */
607        if (headerWrapper.length > 0) {
608            let headerLeftOffset = headerWrapper.offset().left;
609            let headerWidth = headerWrapper.width();
610            let headerRightOffset = headerLeftOffset + headerWidth;
611
612            const flexDropdown = $('.flex-dropdown');
613
614            /** sub menu direction */
615            const subElements = $('ul.sub-menu');
616            if (subElements.length > 0) {
617                subElements.each(function () {
618                    const item = $(this);
619                    let itemLeftOffset = item.offset().left;
620                    let itemRightOffset = itemLeftOffset + item.width() + 100;
621                    if (itemRightOffset > headerRightOffset) {
622                        item.addClass('left-direction');
623                    }
624                });
625            }
626
627            /** calc dropdown flex width */
628            if (flexDropdown.length > 0) {
629                flexDropdown.each(function () {
630                    const item = $(this);
631                    const parentItem = item.parent();
632                    if (parentItem.hasClass('is-child-wide') || item.hasClass('mega-has-left')) {
633                        return;
634                    }
635                    const itemWidth = item.width();
636                    const itemHalfWidth = itemWidth / 2;
637                    const parentItemOffset = parentItem.offset().left;
638                    const parentHalfWidth = parentItem.width() / 2;
639                    const parentItemCenterOffset = parentItemOffset + parentHalfWidth;
640                    const rightSpacing = headerRightOffset - parentItemCenterOffset;
641                    const leftSpacing = parentItemCenterOffset - headerLeftOffset;
642
643                    if (itemWidth >= headerWidth) {
644                        item.css({
645                            width: headerWidth - 2,
646                            left: -parentItemOffset,
647                        });
648                    } else if (itemHalfWidth > rightSpacing) {
649                        item.css({
650                            right: -rightSpacing + parentHalfWidth + 1,
651                            left: 'auto',
652                        });
653                    } else if (itemHalfWidth > leftSpacing) {
654                        item.css({
655                            left: -leftSpacing + parentHalfWidth + 1,
656                            right: 'auto',
657                        });
658                    } else {
659                        item.css({
660                            right: 'auto',
661                            left: -itemHalfWidth + parentHalfWidth,
662                        });
663                    }
664                });
665            }
666        }
667    };
668
669    /**
670     *
671     * @returns {boolean}
672     */
673    Module.stickyNavBar = function () {
674        const self = this;
675
676        /** turn off sticky on editor mode */
677        if (self._body.hasClass('elementor-editor-active')) {
678            return false;
679        }
680
681        self.sticky.section = $('#sticky-holder');
682        self.sticky.outer = $('#navbar-outer');
683
684        if (
685            (!self._body.hasClass('is-mstick') && !self._body.hasClass('yes-tstick')) ||
686            self.sticky.outer.length < 1 ||
687            self.sticky.section.length < 1
688        ) {
689            return false;
690        }
691
692        self.sticky.smartSticky = !!self._body.hasClass('is-smart-sticky');
693        self.sticky.isSticky = false;
694        self.sticky.lastScroll = 0;
695
696        if (self._body.hasClass('yes-tstick')) {
697            self.sticky.isTitleSticky = true;
698        } else {
699            self.sticky.isTitleSticky = 0;
700        }
701
702        self.sticky.additionalOffset = 200;
703        if (window.innerWidth > 1024) {
704            if (self.sticky.isTitleSticky) {
705                self.sticky.additionalOffset = 450;
706            } else {
707                self.sticky.additionalOffset = 0;
708            }
709        }
710
711        if (self._body.hasClass('admin-bar')) {
712            self.sticky.adminBarSpacing = 32;
713        } else {
714            self.sticky.adminBarSpacing = 0;
715        }
716
717        self.sticky.topOffset = self.sticky.section.offset().top;
718        self.sticky.stickySectionHeight = self.sticky.section.outerHeight();
719
720        self.sticky.outer.css('min-height', self.sticky.outer.outerHeight());
721        self.sticky.activatePos =
722            self.sticky.topOffset + 1 + self.sticky.stickySectionHeight + self.sticky.additionalOffset;
723        self.sticky.deactivePos = self.sticky.topOffset - self.sticky.adminBarSpacing + self.sticky.additionalOffset;
724
725        // Use unified scroll manager
726        if (self.sticky.smartSticky) {
727            self.addScrollHandler('stickyNav', self.initSmartStickyNavBar.bind(self));
728        } else {
729            self.addScrollHandler('stickyNav', self.initStickyNavBar.bind(self));
730        }
731
732        self._window.on('unstickMenu', function () {
733            self.sticky.outer.css('min-height', self.sticky.outer.outerHeight());
734            self.sticky.stickySectionHeight = self.sticky.section.outerHeight();
735            self.sticky.topOffset = self.sticky.section.offset().top;
736            self.sticky.activatePos =
737                self.sticky.topOffset + 1 + self.sticky.stickySectionHeight + self.sticky.additionalOffset;
738            self.sticky.deactivePos =
739                self.sticky.topOffset - self.sticky.adminBarSpacing + self.sticky.additionalOffset;
740        });
741    };
742
743    Module.initStickyNavBar = function () {
744        const self = this;
745        const scroll = self._window.scrollTop();
746
747        if (!self.sticky.isSticky && scroll > self.sticky.activatePos) {
748            self.sticky.isSticky = true;
749            self._body.addClass('stick-animated sticky-on');
750            self.sticky.stickAnimatedTimeout = setTimeout(function () {
751                self._body.removeClass('stick-animated');
752            }, 200);
753        } else if (self.sticky.isSticky && scroll <= self.sticky.deactivePos) {
754            self.sticky.isSticky = false;
755            self._body.removeClass('sticky-on stick-animated');
756            self._window.trigger('unstickMenu');
757        }
758    };
759
760    Module.initSmartStickyNavBar = function () {
761        const self = this;
762        const scroll = self._window.scrollTop();
763
764        if (!self.sticky.isSticky && scroll > self.sticky.activatePos && scroll < self.sticky.lastScroll) {
765            self.sticky.isSticky = true;
766            self._body.addClass('stick-animated sticky-on');
767            self.sticky.stickAnimatedTimeout = setTimeout(function () {
768                self._body.removeClass('stick-animated');
769            }, 200);
770        } else if (self.sticky.isSticky && (scroll <= self.sticky.deactivePos || scroll > self.sticky.lastScroll)) {
771            self.sticky.isSticky = false;
772            self._body.removeClass('sticky-on stick-animated');
773            if (scroll <= self.sticky.deactivePos) {
774                self._window.trigger('unstickESection');
775            }
776        }
777        self.sticky.lastScroll = scroll;
778    };
779
780    /** header sticky template */
781    Module.stickyHeaderBuilder = function () {
782        const self = this;
783
784        /** turn off sticky on editor mode */
785        if (self._body.hasClass('elementor-editor-active')) {
786            return false;
787        }
788
789        let stickySection = $('.header-template .e-section-sticky').first();
790        const hasTitleSticky = $('body.single-post #s-title-sticky').first();
791
792        if (stickySection.length < 1) {
793            return false;
794        }
795
796        if (hasTitleSticky.length > 0) {
797            self._body.addClass('yes-tstick');
798            self.eSticky.isTitleSticky = true;
799        } else {
800            self.eSticky.isTitleSticky = 0;
801        }
802
803        self.eSticky.additionalOffset = 200;
804        if (window.innerWidth > 1024) {
805            if (self.eSticky.isTitleSticky) {
806                self.eSticky.additionalOffset = 450;
807            } else {
808                self.eSticky.additionalOffset = 0;
809            }
810        }
811
812        self.eSticky.smartSticky = !!stickySection.hasClass('is-smart-sticky');
813
814        /** mobile sticky for header template */
815        if (window.innerWidth <= 1024) {
816            stickySection.removeClass('e-section-sticky');
817            stickySection = $('#header-template-holder').addClass('e-section-sticky');
818        }
819
820        self.eSticky.section = stickySection;
821        self.eSticky.outer = stickySection.parent();
822
823        self.eSticky.adminBarSpacing = 0;
824        self.eSticky.isSticky = false;
825        self.eSticky.lastScroll = 0;
826        self.eSticky.stickySectionHeight = stickySection.outerHeight();
827        self.eSticky.topOffset = stickySection.offset().top;
828
829        /** set min height */
830        if (self._body.hasClass('admin-bar')) {
831            self.eSticky.adminBarSpacing = 32;
832        }
833        self.eSticky.outer.css('min-height', self.eSticky.outer.outerHeight());
834        self.eSticky.activatePos =
835            self.eSticky.topOffset + 1 + self.eSticky.stickySectionHeight + self.eSticky.additionalOffset;
836        self.eSticky.deactivePos =
837            self.eSticky.topOffset - self.eSticky.adminBarSpacing + self.eSticky.additionalOffset;
838
839        // Use unified scroll manager
840        if (self.eSticky.smartSticky) {
841            self.addScrollHandler('stickyESection', self.initSmartStickyESection.bind(self));
842        } else {
843            self.addScrollHandler('stickyESection', self.initStickyESection.bind(self));
844        }
845
846        /** re-calc height values */
847        self._window.on('unstickESection', function () {
848            self.eSticky.outer.css('min-height', self.eSticky.outer.outerHeight());
849            self.eSticky.stickySectionHeight = self.eSticky.section.outerHeight();
850            self.eSticky.topOffset = self.eSticky.section.offset().top;
851            self.eSticky.activatePos =
852                self.eSticky.topOffset + 1 + self.eSticky.stickySectionHeight + self.eSticky.additionalOffset;
853            self.eSticky.deactivePos =
854                self.eSticky.topOffset - self.eSticky.adminBarSpacing + self.eSticky.additionalOffset;
855        });
856    };
857
858    Module.initStickyESection = function () {
859        const self = this;
860        const scroll = self._window.scrollTop();
861        if (!self.eSticky.isSticky && scroll > self.eSticky.activatePos) {
862            self.eSticky.isSticky = true;
863            self._body.addClass('stick-animated sticky-on');
864            self.eSticky.stickAnimatedTimeout = setTimeout(function () {
865                self._body.removeClass('stick-animated');
866            }, 200);
867        } else if (self.eSticky.isSticky && scroll <= self.eSticky.deactivePos) {
868            self.eSticky.isSticky = false;
869            self._body.removeClass('sticky-on stick-animated');
870            self._window.trigger('unstickESection');
871        }
872    };
873
874    Module.initSmartStickyESection = function () {
875        const self = this;
876        const scroll = self._window.scrollTop();
877
878        if (!self.eSticky.isSticky && scroll > self.eSticky.activatePos && scroll < self.eSticky.lastScroll) {
879            self.eSticky.isSticky = true;
880            self._body.addClass('stick-animated sticky-on');
881            self.eSticky.stickAnimatedTimeout = setTimeout(function () {
882                self._body.removeClass('stick-animated');
883            }, 200);
884        } else if (self.eSticky.isSticky && (scroll <= self.eSticky.deactivePos || scroll > self.eSticky.lastScroll)) {
885            self.eSticky.isSticky = false;
886            self._body.removeClass('sticky-on stick-animated');
887            if (scroll <= self.eSticky.deactivePos) {
888                self._window.trigger('unstickESection');
889            }
890        }
891
892        self.eSticky.lastScroll = scroll;
893    };
894
895    /** mobileCollapse */
896    Module.mobileCollapse = function () {
897        const self = this;
898        const $mobileMenuTrigger = $('.mobile-menu-trigger');
899        const $outerHTML = self.outerHTML;
900        const $mobileSearchForm = $outerHTML.find('.mobile-search-form input[type="text"]');
901
902        $mobileMenuTrigger.on('click', function (e) {
903            e.preventDefault();
904            e.stopPropagation();
905            const target = $(this);
906
907            if (target.hasClass('mobile-search-icon')) {
908                setTimeout(function () {
909                    $mobileSearchForm.focus();
910                }, 100);
911            }
912
913            const isCollapseActivated = $outerHTML.hasClass('collapse-activated');
914            $outerHTML.toggleClass('collapse-activated', !isCollapseActivated);
915        });
916    };
917
918    /**
919     * privacy trigger
920     */
921    Module.privacyTrigger = function () {
922        const self = this;
923        $('#privacy-trigger').on('click', function (e) {
924            e.preventDefault();
925            e.stopPropagation();
926            self.setStorage('RubyPrivacyAllowed', '1');
927            $('#rb-privacy').slideUp(200, function () {
928                $(this).remove();
929            });
930            return false;
931        });
932    };
933
934    /** back top */
935    Module.backTop = function () {
936        if (this._body.hasClass('is-backtop')) {
937            $().UItoTop({
938                text: '<i class="rbi rbi-darrow-top"></i>',
939            });
940        }
941    };
942
943    /** login popup */
944    Module.loginPopup = function () {
945        const self = this;
946        const form = $('#rb-user-popup-form');
947        if (form.length < 1) {
948            return false;
949        }
950        this._document.on('click', '.login-toggle', (e) => {
951            e.preventDefault();
952            e.stopPropagation();
953            $.magnificPopup.open({
954                type: 'inline',
955                preloader: false,
956                removalDelay: 400,
957                showCloseBtn: true,
958                closeBtnInside: true,
959                closeOnBgClick: false,
960                items: {
961                    src: form,
962                    type: 'inline',
963                },
964                mainClass: 'rb-popup-center',
965                closeMarkup: '<span class="close-popup-btn mfp-close"><span class="close-icon"></span></span>',
966                fixedBgPos: true,
967                fixedContentPos: true,
968                callbacks: { open: () => self.reloadCaptchaPopups() },
969            });
970        });
971    };
972
973    /** popup template blocks */
974    Module.popupTemplate = function () {
975        const templateCache = {};
976        this._document.on('click', '.popup-trigger-btn', (e) => {
977            e.preventDefault();
978            e.stopPropagation();
979
980            const target = $(e.target);
981            const templateID = target.data('trigger') || '';
982            const position = target.data('position') || '';
983
984            if (!templateCache[templateID]) {
985                const template = $(`#tmpl-${templateID}`);
986                if (template.length === 0) {
987                    return;
988                }
989                templateCache[templateID] = template.html();
990            }
991            $.magnificPopup.open({
992                type: 'inline',
993                preloader: false,
994                removalDelay: 400,
995                showCloseBtn: true,
996                closeBtnInside: true,
997                closeOnBgClick: true,
998                allowHTMLInTemplate: true,
999                items: {
1000                    src: templateCache[templateID],
1001                    type: 'inline',
1002                },
1003                mainClass: `is-template-popup popup-${templateID} ${position}`,
1004                closeMarkup: '<span class="close-popup-btn mfp-close"><span class="close-icon"></span></span>',
1005                fixedBgPos: true,
1006                fixedContentPos: true,
1007                callbacks: { open: () => self.reloadCaptchaPopups() },
1008            });
1009        });
1010    };
1011
1012    /** newsletter */
1013    Module.popupNewsletter = function () {
1014        const self = this;
1015        if (self.popupNewsletterEl.length > 0) {
1016            const display = self.popupNewsletterEl.data('display');
1017            self.newsletterExpired = self.popupNewsletterEl.data('expired');
1018            self.newsletterDisplayOffset = self.popupNewsletterEl.data('offset');
1019            const delay = self.popupNewsletterEl.data('delay');
1020            const oldExpired = self.getStorage('RubyNewsletterExpired');
1021
1022            if (self.newsletterExpired === 0) {
1023                self.deleteStorage('RubyNewsletter');
1024            }
1025
1026            if (!oldExpired || self.newsletterExpired !== oldExpired) {
1027                self.setStorage('RubyNewsletterExpired', self.newsletterExpired);
1028                self.deleteStorage('RubyNewsletter');
1029            }
1030
1031            if (!self.getTTLStorage('RubyNewsletter')) {
1032                if (!display || 'scroll' === display) {
1033                    self.addScrollHandler('newsletter', self.scrollPopupNewsletter.bind(self));
1034                } else {
1035                    setTimeout(function () {
1036                        self.popupNewsletterInit();
1037                    }, delay);
1038                }
1039            }
1040        }
1041    };
1042
1043    Module.scrollPopupNewsletter = function () {
1044        const self = this;
1045        if (!self.newsletterPopupFlag && self._window.scrollTop() > self.newsletterDisplayOffset) {
1046            self.newsletterPopupFlag = true;
1047            self.removeScrollHandler('newsletter');
1048            self.popupNewsletterInit();
1049        }
1050    };
1051
1052    Module.popupNewsletterInit = function () {
1053        const self = this;
1054        if (self.siteAccessFlag) {
1055            return;
1056        }
1057
1058        if (!self.popupNewsletterEl.hasClass('is-pos-fixed')) {
1059            $.magnificPopup.open({
1060                type: 'inline',
1061                preloader: false,
1062                closeBtnInside: true,
1063                removalDelay: 400,
1064                showCloseBtn: true,
1065                closeOnBgClick: false,
1066                disableOn: 1024,
1067                items: {
1068                    src: '#rb-popup-newsletter',
1069                    type: 'inline',
1070                },
1071                mainClass: 'rb-popup-center',
1072                fixedBgPos: true,
1073                fixedContentPos: true,
1074                closeMarkup: '<span class="close-popup-btn mfp-close"><span class="close-icon"></span></span>',
1075                callbacks: {
1076                    open: () => self.reloadCaptchaPopups(),
1077                    close: function () {
1078                        if (self.siteAccessFlag) {
1079                            return;
1080                        }
1081                        self.SetTTLStorage('RubyNewsletter', 1, self.newsletterExpired * 24);
1082                    },
1083                },
1084            });
1085        } else {
1086            self.popupNewsletterEl.removeClass('is-hidden');
1087            setTimeout(() => {
1088                self.popupNewsletterEl.addClass('yes-show');
1089            }, 10);
1090            const closeBtn = self.popupNewsletterEl.find('.close-popup-btn');
1091            closeBtn.on('click', function () {
1092                self.SetTTLStorage('RubyNewsletter', 1, self.newsletterExpired * 24);
1093                self.popupNewsletterEl.removeClass('yes-show');
1094                setTimeout(() => {
1095                    self.popupNewsletterEl.addClass('is-hidden');
1096                }, 400);
1097            });
1098        }
1099    };
1100
1101    /** footer slide up */
1102    Module.footerSlideUp = function () {
1103        const target = $('#footer-slideup');
1104        if (target.length > 0) {
1105            const self = this;
1106            self.footerSlideUpExpired = target.data('expired');
1107            const delay = target.data('delay');
1108            const oldExpired = self.getStorage('footerSlideUpExpired');
1109            if (!oldExpired || self.footerSlideUpExpired != oldExpired) {
1110                self.setStorage('footerSlideUpExpired', self.footerSlideUpExpired);
1111                self.deleteStorage('footerSlideUp');
1112            }
1113            if (!self.getTTLStorage('footerSlideUp')) {
1114                setTimeout(function () {
1115                    self.footerSlideUpInit();
1116                }, delay);
1117            }
1118            /** show hide toggle */
1119            setTimeout(function () {
1120                self.footerSlideUpToggle();
1121            }, delay);
1122        }
1123    };
1124
1125    Module.footerSlideUpToggle = function () {
1126        const self = this;
1127        $('.slideup-toggle')
1128            .off('click')
1129            .on('click', function (e) {
1130                e.preventDefault();
1131                e.stopPropagation();
1132                self.footerSlideUpInit();
1133                return false;
1134            });
1135    };
1136
1137    Module.footerSlideUpInit = function () {
1138        if (this._body.hasClass('yes-f-slideup')) {
1139            this._body.removeClass('yes-f-slideup');
1140            this.SetTTLStorage('footerSlideUp', 1, this.footerSlideUpExpired * 24);
1141        } else {
1142            this._body.addClass('yes-f-slideup');
1143            this.deleteStorage('footerSlideUp');
1144        }
1145    };
1146
1147    /** youtube iframe */
1148    Module.loadYoutubeIframe = function () {
1149        const self = this;
1150        const blockPlaylist = $('.yt-playlist');
1151        if (blockPlaylist.length > 0) {
1152            const tag = document.createElement('script');
1153            tag.src = '//www.youtube.com/iframe_api';
1154            const firstScriptTag = document.getElementsByTagName('script')[0];
1155            firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
1156        }
1157
1158        window.onYouTubeIframeAPIReady = function () {
1159            $('.yt-playlist').each(function () {
1160                const target = $(this);
1161                const iframe = target.find('.yt-player');
1162                const videoID = target.data('id');
1163                const blockID = target.data('block');
1164                self.YTPlayers[blockID] = new YT.Player(iframe.get(0), {
1165                    height: '540',
1166                    width: '960',
1167                    videoId: videoID,
1168                    events: {
1169                        onReady: self.videoPlayToggle,
1170                        onStateChange: self.videoPlayToggle,
1171                    },
1172                });
1173            });
1174
1175            $('.plist-item').on('click', function (e) {
1176                e.preventDefault();
1177                e.stopPropagation();
1178
1179                const target = $(this);
1180                const wrapper = target.closest('.yt-playlist');
1181                const currentBlockID = wrapper.data('block');
1182                const videoID = target.data('id');
1183                const title = target.find('.plist-item-title').text();
1184                const meta = target.data('index');
1185
1186                Object.keys(self.YTPlayers).forEach(function (id) {
1187                    self.YTPlayers[id].pauseVideo();
1188                });
1189                self.YTPlayers[currentBlockID].loadVideoById({
1190                    videoId: videoID,
1191                });
1192
1193                wrapper.find('.yt-trigger').addClass('is-playing');
1194                wrapper.find('.play-title').hide().text(title).fadeIn(200);
1195                wrapper.find('.video-index').text(meta);
1196            });
1197        };
1198    };
1199
1200    Module.videoPlayToggle = function () {
1201        const players = FOXIZ_MAIN_SCRIPT.YTPlayers;
1202
1203        $('.yt-trigger').on('click', function (e) {
1204            e.preventDefault();
1205            e.stopPropagation();
1206
1207            const target = $(this);
1208            const currentBlockID = target.closest('.yt-playlist').data('block');
1209            const currentState = players[currentBlockID].getPlayerState();
1210            const isPlaying = [1, 3].includes(currentState);
1211            if (!isPlaying) {
1212                players[currentBlockID].playVideo();
1213                target.addClass('is-playing');
1214            } else {
1215                players[currentBlockID].pauseVideo();
1216                target.removeClass('is-playing');
1217            }
1218        });
1219    };
1220
1221    /** Comment scripts */
1222    Module.showPostComment = function () {
1223        const self = this;
1224
1225        this._document.on('click', '.smeta-sec .meta-comment', function (e) {
1226            e.stopPropagation();
1227            const commentBtn = $('.show-post-comment');
1228            if (commentBtn.length > 0) {
1229                self.html.animate({ scrollTop: commentBtn.offset().top }, 300);
1230                commentBtn.trigger('click');
1231            }
1232        });
1233
1234        this._document.on('click', '.show-post-comment', function (e) {
1235            e.preventDefault();
1236            e.stopPropagation();
1237            const target = $(this);
1238            const wrap = target.parent();
1239
1240            target.fadeOut(200, function () {
1241                target.remove();
1242                wrap.find('.is-invisible').removeClass('is-invisible');
1243                wrap.next('.comment-holder').removeClass('is-hidden');
1244            });
1245        });
1246    };
1247
1248    /** table scroll */
1249    Module.scrollTableContent = function () {
1250        const self = this;
1251        const behavior = self._body.hasClass('toc-smooth') ? 'smooth' : 'instant';
1252
1253        self._document.on('click', '.anchor-link', function (e) {
1254            e.stopPropagation();
1255
1256            const targetClass = $(this).data('index');
1257            if (!targetClass) return;
1258
1259            let $scrollElement = $(`.${targetClass}`);
1260
1261            // if target not found, search inside its parent .s-ct
1262            if (!$scrollElement.length) {
1263                const $parentContainer = $(this).closest('.s-ct');
1264                if ($parentContainer.length) {
1265                    $scrollElement = $parentContainer.find('.restrict-box, .continue-reading-btn').first();
1266                }
1267            }
1268
1269            if (!$scrollElement.length) return;
1270
1271            setTimeout(() => {
1272                window.scrollTo({
1273                    top: $scrollElement.offset().top,
1274                    behavior: behavior,
1275                });
1276            }, 10);
1277        });
1278    };
1279
1280    /** scroll to comment  */
1281    Module.scrollToComment = function () {
1282        const self = this;
1283        const hash = window.location.hash;
1284        if (hash === '#respond' || hash.startsWith('#comment')) {
1285            const commentBtn = $('.show-post-comment').first();
1286            if (commentBtn.length === 0) {
1287                return;
1288            }
1289            self.html.animate({ scrollTop: commentBtn.offset().top - 200 }, 400);
1290            commentBtn.trigger('click');
1291        }
1292    };
1293
1294    Module.replyReview = function () {
1295        this._document.on('click', '.comment-reply-link', function () {
1296            const target = $(this);
1297            const wrapper = target.parents('.rb-reviews-area');
1298            const cancelLink = $('#cancel-comment-reply-link');
1299            if (wrapper.length > 0) {
1300                wrapper.find('.rb-form-rating').addClass('is-hidden');
1301                cancelLink.on('click', function () {
1302                    wrapper.find('.rb-form-rating').removeClass('is-hidden');
1303                });
1304            }
1305        });
1306    };
1307
1308    /** user rating */
1309    Module.usersRating = function () {
1310        const self = this;
1311        const reviewsForm = self._body.find('.rb-reviews-form');
1312        if (reviewsForm.length > 0) {
1313            reviewsForm.each(function () {
1314                const reviewForm = $(this);
1315                if (!reviewForm.hasClass('is-loaded')) {
1316                    reviewForm.addClass('is-loaded');
1317                    const ratingForm = reviewForm.find('.rb-form-rating');
1318                    const selection = reviewForm.find('.rb-rating-selection');
1319                    const text = reviewForm.find('.rating-alert').html();
1320                    let ratingValue = null;
1321
1322                    selection.val('');
1323                    selection.hide();
1324                    selection.before(
1325                        '<div class="rb-review-stars">\
1326                                <span>\
1327                                    <a class="star" data-rating="1" href="#"><i class="rbi rbi-star-o"></i></a>\
1328                                    <a class="star" data-rating="2" href="#"><i class="rbi rbi-star-o"></i></a>\
1329                                    <a class="star" data-rating="3" href="#"><i class="rbi rbi-star-o"></i></a>\
1330                                    <a class="star" data-rating="4" href="#"><i class="rbi rbi-star-o"></i></a>\
1331                                    <a class="star" data-rating="5" href="#"><i class="rbi rbi-star-o"></i></a>\
1332                                </span>\
1333                            </div>'
1334                    );
1335
1336                    ratingForm.on('click', 'a.star', function (e) {
1337                        e.preventDefault();
1338                        e.stopPropagation();
1339                        const star = $(this);
1340                        ratingValue = star.data('rating');
1341                        star.siblings('a').removeClass('active');
1342                        star.addClass('active');
1343                        ratingForm.addClass('selected');
1344                    });
1345
1346                    reviewForm.on('click', '#respond #submit', function () {
1347                        selection.val(ratingValue);
1348                        if (!selection.val()) {
1349                            window.alert(text);
1350                            return false;
1351                        }
1352                    });
1353                }
1354            });
1355        }
1356    };
1357
1358    /**
1359     *
1360     * @returns {boolean}
1361     */
1362    Module.readIndicatorInit = function () {
1363        const self = this;
1364        if (!self._body.hasClass('single') || self.readIndicator.length < 1) {
1365            return false;
1366        }
1367
1368        let content = $('.entry-content').first();
1369        if (!content.length) return false;
1370
1371        self.indicatorTop = content.offset().top;
1372        self.indicatorHeight = content.outerHeight(true) - self._window.height();
1373        /** delay for load images */
1374        setTimeout(function () {
1375            self.indicatorTop = content.offset().top;
1376            self.indicatorHeight = content.outerHeight(true) - self._window.height();
1377        }, 1000);
1378
1379        self.addScrollHandler('readIndicator', self.readIndicatorCalc.bind(self));
1380    };
1381
1382    Module.readIndicatorCalc = function () {
1383        const self = this;
1384        const scroll = self._window.scrollTop();
1385        self.readIndicatorPercent = Math.min(((scroll - self.indicatorTop) / self.indicatorHeight) * 100, 100);
1386        if (self.readIndicatorPercent <= 100) {
1387            self.readIndicator.css('width', self.readIndicatorPercent + '%');
1388        }
1389    };
1390
1391    /** breaking news */
1392    Module.breakingNews = function () {
1393        const self = this;
1394        const breakingNews = $('.breaking-news-slider');
1395
1396        if (breakingNews.length < 1) {
1397            return false;
1398        }
1399
1400        breakingNews.each(function () {
1401            const el = $(this);
1402            let params = {
1403                slidesPerView: 1,
1404                loop: true,
1405            };
1406            if (el.data('play')) {
1407                params.autoplay = {
1408                    delay: el.data('speed') || self.themeSettings.sliderSpeed,
1409                    stopOnLastSlide: false,
1410                    disableOnInteraction: self.isElementorEditor ? true : window.outerWidth < 1025,
1411                    pauseOnMouseEnter: true,
1412                };
1413            }
1414            params.navigation = {
1415                nextEl: el.find('.breaking-news-next')[0],
1416                prevEl: el.find('.breaking-news-prev')[0],
1417            };
1418            new RBSwiper(this, params);
1419        });
1420    };
1421
1422    /** overlay slider */
1423    Module.sliders = function () {
1424        const self = this;
1425        const sliders = $('.post-slider');
1426        if (sliders.length < 1) {
1427            return false;
1428        }
1429
1430        sliders.each(function () {
1431            const slider = $(this);
1432            let params = {
1433                grabCursor: true,
1434                allowTouchMove: true,
1435                effect: self.themeSettings.sliderEffect,
1436                loop: true,
1437            };
1438            if (slider.data('play')) {
1439                params.autoplay = {
1440                    delay: slider.data('speed') || self.themeSettings.sliderSpeed,
1441                    stopOnLastSlide: true,
1442                    disableOnInteraction: self.isElementorEditor ? true : window.outerWidth < 1025,
1443                    pauseOnMouseEnter: true,
1444                };
1445            }
1446
1447            params.pagination = {
1448                el: slider.find('.slider-pagination')[0],
1449                clickable: true,
1450            };
1451
1452            params.navigation = {
1453                nextEl: slider.find('.slider-next')[0],
1454                prevEl: slider.find('.slider-prev')[0],
1455            };
1456            new RBSwiper(this, params);
1457        });
1458    };
1459
1460    /** carousel blocks */
1461    Module.carousels = function () {
1462        const self = this;
1463        const carousels = $('.post-carousel');
1464        if (carousels.length < 1) {
1465            return false;
1466        }
1467        carousels.each(function () {
1468            const carousel = $(this);
1469            let params = {
1470                grabCursor: true,
1471                allowTouchMove: true,
1472                freeMode: false,
1473                loop: true,
1474            };
1475            params.slidesPerView = carousel.data('mcol');
1476            params.spaceBetween = carousel.data('mgap');
1477            params.centeredSlides = carousel.data('centered');
1478
1479            params.navigation = {
1480                nextEl: carousel.find('.slider-next')[0],
1481                prevEl: carousel.find('.slider-prev')[0],
1482            };
1483            if (carousel.find('.slider-pagination')[0]) {
1484                params.pagination = {
1485                    el: carousel.find('.slider-pagination')[0],
1486                    type: 'bullets',
1487                    clickable: true,
1488                };
1489            }
1490
1491            if (carousel.data('play')) {
1492                params.autoplay = {
1493                    delay: carousel.data('speed') || self.themeSettings.sliderSpeed,
1494                    stopOnLastSlide: false,
1495                    disableOnInteraction: self.isElementorEditor ? true : window.outerWidth < 1025,
1496                    pauseOnMouseEnter: true,
1497                };
1498            }
1499
1500            if (carousel.data('fmode')) {
1501                params.freeMode = true;
1502            }
1503            params.breakpoints = {
1504                768: {
1505                    slidesPerView: carousel.data('tcol'),
1506                    spaceBetween: carousel.data('tgap'),
1507                },
1508                1025: {
1509                    slidesPerView: carousel.data('col'),
1510                    spaceBetween: carousel.data('gap'),
1511                },
1512                1500: {
1513                    slidesPerView: carousel.data('wcol'),
1514                    spaceBetween: carousel.data('gap'),
1515                },
1516            };
1517            params.on = {
1518                afterInit: function (swiper) {
1519                    const wrap = $(swiper.$wrapperEl);
1520                    $(swiper.$wrapperEl).find('.p-box').css('height', wrap.height());
1521                },
1522                resize: function (swiper) {
1523                    const wrap = $(swiper.$wrapperEl);
1524                    $(swiper.$wrapperEl).find('.p-box').css('height', wrap.height());
1525                },
1526            };
1527
1528            new RBSwiper(this, params);
1529        });
1530    };
1531
1532    /* ================================ SINGLE GALLERY ================================ */
1533    Module.singleGallery = function () {
1534        const self = this;
1535        const gallerySections = self._body.find('.featured-gallery-wrap');
1536        if (gallerySections.length === 0) {
1537            return;
1538        }
1539
1540        gallerySections.each(function () {
1541            const section = $(this);
1542            if (!section.hasClass('is-loaded')) {
1543                const index = section.data('gallery');
1544                const sliderEl = section.find('.gallery-slider').attr('id');
1545                const sliderNavEl = section.find('.gallery-slider-nav').attr('id');
1546                const carouselEl = section.find('.gallery-carousel').attr('id');
1547                const coverflowEL = section.find('.gallery-coverflow').attr('id');
1548
1549                if (sliderEl && sliderNavEl) {
1550                    const navEl = $('#' + sliderNavEl);
1551                    const navSlidesCount = navEl.find('.swiper-slide').length;
1552                    const shouldLoopNav = navSlidesCount >= 6;
1553                    const viewSlides = Math.min(navSlidesCount, 6);
1554
1555                    if (!shouldLoopNav) {
1556                        navEl.css('max-width', `${navSlidesCount * 125}px`);
1557                    }
1558
1559                    const galleryNav = new RBSwiper('#' + sliderNavEl, {
1560                        spaceBetween: 15,
1561                        slidesPerView: viewSlides,
1562                        freeMode: self.themeSettings.sliderFMode,
1563                        grabCursor: true,
1564                        loop: shouldLoopNav,
1565                        watchSlidesVisibility: shouldLoopNav,
1566                        watchSlidesProgress: true,
1567                        on: {
1568                            init: function () {
1569                                $(this.$wrapperEl).removeClass('pre-load');
1570                            },
1571                        },
1572                    });
1573
1574                    const gallerySlider = new RBSwiper('#' + sliderEl, {
1575                        spaceBetween: 0,
1576                        grabCursor: true,
1577                        loop: true,
1578                        pagination: {
1579                            el: '.swiper-pagination-' + index,
1580                            type: 'progressbar',
1581                            clickable: true,
1582                        },
1583                        on: {
1584                            init: function () {
1585                                section.addClass('is-loaded');
1586                            },
1587                        },
1588                        thumbs: {
1589                            swiper: galleryNav,
1590                        },
1591                    });
1592
1593                    gallerySlider.on('slideChange', function () {
1594                        const index = this.activeIndex;
1595                        if (index) {
1596                            const label = $(this.$el).next().find('.current-slider-count');
1597                            label.fadeOut(0, function () {
1598                                $(this)
1599                                    .html(index <= label.data('total') ? index : 1)
1600                                    .fadeIn(50);
1601                            });
1602                        }
1603                    });
1604                }
1605
1606                if ('undefined' !== typeof carouselEl) {
1607                    new RBSwiper('#' + carouselEl, {
1608                        spaceBetween: 20,
1609                        slidesPerView: 'auto',
1610                        freeMode: self.themeSettings.sliderFMode,
1611                        loop: false,
1612                        grabCursor: true,
1613                        scrollbar: {
1614                            el: '.swiper-scrollbar-' + index,
1615                            hide: true,
1616                        },
1617                        on: {
1618                            init: function () {
1619                                $(this.$wrapperEl).removeClass('pre-load');
1620                                section.addClass('is-loaded');
1621                            },
1622                        },
1623                    });
1624                }
1625
1626                if ('undefined' !== typeof coverflowEL) {
1627                    new RBSwiper('#' + coverflowEL, {
1628                        effect: 'coverflow',
1629                        grabCursor: true,
1630                        centeredSlides: true,
1631                        slidesPerView: 1.2,
1632                        coverflowEffect: {
1633                            rotate: 50,
1634                            stretch: 0,
1635                            depth: 250,
1636                            modifier: 1,
1637                            slideShadows: true,
1638                        },
1639                        breakpoints: {
1640                            768: {
1641                                slidesPerView: 3,
1642                            },
1643                        },
1644                        on: {
1645                            init: function () {
1646                                $(this.$wrapperEl).removeClass('pre-load');
1647                                section.addClass('is-loaded');
1648                            },
1649                        },
1650                        pagination: {
1651                            el: '.swiper-pagination-' + index,
1652                            clickable: true,
1653                        },
1654                    });
1655                }
1656            }
1657        });
1658    };
1659
1660    Module.scrollBarSlider = function () {
1661        const self = this;
1662        const items = $('.gb-slider-scrollbar');
1663        if (!items.length) return;
1664
1665        items.each(function (index, itemEl) {
1666            if ($(itemEl).hasClass('is-loaded')) return;
1667
1668            const autoplay = $(itemEl).data('autoplay') === 1;
1669            const caption = $(itemEl).closest('.gb-image-slider').find('.gb-image-caption');
1670            const scrollbarEl = $(itemEl).find('.swiper-scrollbar')[0];
1671
1672            $(itemEl).find('.swiper-wrapper').children().addClass('swiper-slide');
1673
1674            const updateCaption = (swiper) => {
1675                const captionText = $(swiper.slides[swiper.activeIndex]).find('.wp-element-caption').html() || '';
1676                caption.fadeOut(150, () => caption.html(captionText).fadeIn(200));
1677            };
1678
1679            new RBSwiper(itemEl, {
1680                slidesPerView: 1,
1681                loop: false,
1682                grabCursor: true,
1683                autoplay: autoplay
1684                    ? {
1685                          delay: self.themeSettings.sliderSpeed || 5000,
1686                          disableOnInteraction: self.isElementorEditor ? true : window.outerWidth < 1025,
1687                      }
1688                    : false,
1689                scrollbar: {
1690                    el: scrollbarEl,
1691                    hide: false,
1692                },
1693                on: {
1694                    init() {
1695                        $(itemEl).removeClass('pre-load').addClass('is-loaded');
1696                        updateCaption(this);
1697                    },
1698                    slideChange() {
1699                        updateCaption(this);
1700                    },
1701                },
1702            });
1703        });
1704    };
1705
1706    Module.highlightShares = function () {
1707        const self = this;
1708
1709        if (
1710            !self.themeSettings.highlightShares ||
1711            (window.matchMedia && window.matchMedia('(pointer: coarse)').matches)
1712        ) {
1713            return;
1714        }
1715
1716        const config = {
1717            selectableElements: ['.is-highlight-shares'],
1718            twitterUsername: self.themeSettings.twitterName,
1719            facebook: !!self.themeSettings.highlightShareFacebook,
1720            twitter: !!self.themeSettings.highlightShareTwitter,
1721        };
1722
1723        const extra = self.themeSettings.highlightShareReddit
1724            ? [
1725                  {
1726                      icon: '<i class="rbi rbi-reddit"></i>',
1727                      url: 'https://reddit.com/submit?url=PAGE_URL&title=TEXT_SELECTION',
1728                  },
1729              ]
1730            : [];
1731
1732        Sharect.config(config).appendCustomShareButtons(extra).init();
1733    };
1734
1735    /**
1736     *
1737     * @returns {boolean}
1738     */
1739    // Module-level floating video state
1740    Module._floatingVideo = {
1741        active: false,
1742        el: null,
1743        $el: null,
1744        isFloating: false,
1745        scrollHandler: null
1746    };
1747
1748    Module.floatingVideo = function () {
1749        const self = this;
1750        const fv = self._floatingVideo;
1751
1752        // Already active, skip
1753        if (fv.active) return;
1754
1755        const floating = $('.floating-video').not('.is-loaded');
1756        if (floating.length === 0 || window.outerWidth < 1025) {
1757            return false;
1758        }
1759
1760        // Mark as loaded and store references
1761        floating.addClass('is-loaded');
1762        fv.active = true;
1763        fv.el = floating[0];
1764        fv.$el = floating;
1765        fv.isFloating = false;
1766
1767        // Create scroll handler for floating video
1768        fv.scrollHandler = function () {
1769            if (!fv.el || !fv.active) return;
1770            const rect = fv.el.getBoundingClientRect();
1771            const shouldFloat = rect.bottom < 0;
1772
1773            if (shouldFloat && !fv.isFloating) {
1774                fv.isFloating = true;
1775                fv.$el.addClass('floating-activated');
1776                if (!fv.$el.find('.floating-close').length) {
1777                    fv.$el.find('.float-holder')
1778                        .prepend('<a class="floating-close close-popup-btn" href="#"><span class="close-icon"></span></a>');
1779                }
1780            } else if (!shouldFloat && fv.isFloating) {
1781                fv.isFloating = false;
1782                fv.$el.removeClass('floating-activated');
1783            }
1784        };
1785
1786        self.addScrollHandler('floatingVideo', fv.scrollHandler);
1787    };
1788
1789    Module.floatingVideoRemove = function () {
1790        const self = this;
1791        self._body.on('click', '.floating-close', function (e) {
1792            e.preventDefault();
1793            e.stopPropagation();
1794
1795            const fv = self._floatingVideo;
1796            if (fv.$el) {
1797                fv.$el.removeClass('floating-activated');
1798            }
1799            self.removeScrollHandler('floatingVideo');
1800            // Reset state
1801            fv.active = false;
1802            fv.el = null;
1803            fv.$el = null;
1804            fv.isFloating = false;
1805            fv.scrollHandler = null;
1806        });
1807    };
1808
1809    Module.siteAccessDetector = function () {
1810        const self = this;
1811        const method = self.themeSettings?.adDetectorMethod;
1812
1813        if (!method || self.siteAccessFlag || self.crwDetect()) {
1814            return;
1815        }
1816
1817        setTimeout(() => {
1818            if (self.siteAccessFlag) return;
1819
1820            switch (parseInt(method)) {
1821                case 1:
1822                    self.baitElementDetector();
1823                    break;
1824                case 2:
1825                    self.externalScriptDetector();
1826                    break;
1827                case 3:
1828                    self.combinedDetector();
1829                    break;
1830            }
1831        }, 1500);
1832    };
1833
1834    Module.baitElementDetector = function () {
1835        const self = this;
1836        const bait = self.createBaitElement();
1837        document.body.appendChild(bait);
1838
1839        setTimeout(() => {
1840            const isBlocked = self.checkBaitElement(bait);
1841            bait.remove();
1842
1843            if (isBlocked) {
1844                self.siteAccessFlag = true;
1845                $.magnificPopup.close();
1846                setTimeout(() => {
1847                    self.siteAccessNotification();
1848                }, 200);
1849            }
1850        }, 100);
1851    };
1852
1853    Module.externalScriptDetector = function () {
1854        const self = this;
1855        const testUrl = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
1856        const script = document.createElement('script');
1857        script.src = testUrl;
1858        script.async = true;
1859
1860        let completed = false;
1861
1862        const complete = (blocked) => {
1863            if (completed) return;
1864            completed = true;
1865            script.remove();
1866
1867            if (blocked) {
1868                self.siteAccessFlag = true;
1869                $.magnificPopup.close();
1870                setTimeout(() => {
1871                    self.siteAccessNotification();
1872                }, 200);
1873            }
1874        };
1875
1876        script.onerror = () => complete(true);
1877        script.onload = () => complete(false);
1878        setTimeout(() => complete(true), 5000);
1879
1880        document.head.appendChild(script);
1881    };
1882
1883    Module.combinedDetector = function () {
1884        const self = this;
1885        const bait = self.createBaitElement();
1886        document.body.appendChild(bait);
1887
1888        setTimeout(() => {
1889            const baitBlocked = self.checkBaitElement(bait);
1890            bait.remove();
1891
1892            if (baitBlocked) {
1893                self.siteAccessFlag = true;
1894                $.magnificPopup.close();
1895                setTimeout(() => {
1896                    self.siteAccessNotification();
1897                }, 200);
1898                return;
1899            }
1900
1901            self.externalScriptDetector();
1902        }, 100);
1903    };
1904
1905    Module.createBaitElement = function () {
1906        const container = document.createElement('div');
1907        container.id = 'rb-adcheck';
1908        container.setAttribute('aria-hidden', 'true');
1909        container.style.cssText = 'position:absolute;bottom:0;left:0;z-index:-1;pointer-events:none;overflow:hidden;';
1910
1911        const inner = document.createElement('div');
1912        inner.id = 'google_ads_iframe_test';
1913        inner.className = 'adsbygoogle ad-slot adbanner ads-banner banner_ad ad-placeholder pub_300x250 textads adsbox';
1914        inner.setAttribute('data-ad-slot', 'test');
1915        inner.style.cssText = 'width:300px;height:250px;background:transparent;';
1916
1917        container.appendChild(inner);
1918        return container;
1919    };
1920
1921    Module.checkBaitElement = function (container) {
1922        const inner = container.querySelector('.adsbygoogle');
1923
1924        if (!inner || !document.body.contains(inner)) {
1925            return true;
1926        }
1927
1928        const style = window.getComputedStyle(inner);
1929        const rect = inner.getBoundingClientRect();
1930
1931        return (
1932            rect.height < 1 ||
1933            rect.width < 1 ||
1934            style.display === 'none' ||
1935            style.visibility === 'hidden' ||
1936            parseFloat(style.opacity) === 0 ||
1937            inner.offsetParent === null
1938        );
1939    };
1940
1941    Module.siteAccessNotification = function () {
1942        $.magnificPopup.open({
1943            type: 'inline',
1944            preloader: false,
1945            showCloseBtn: false,
1946            closeBtnInside: false,
1947            enableEscapeKey: false,
1948            closeOnBgClick: false,
1949            removalDelay: 9999999,
1950            items: {
1951                src: $('#tmpl-rb-site-access').html(),
1952                type: 'inline',
1953            },
1954            mainClass: 'rb-popup-center site-access-popup',
1955            fixedBgPos: true,
1956            fixedContentPos: true,
1957        });
1958
1959        window.addEventListener('contextmenu', (event) => event.preventDefault());
1960    };
1961
1962    /** build gallery lightbox */
1963    Module.galleryLightbox = function () {
1964        const self = this;
1965        if (!self.galleriesData) {
1966            return;
1967        }
1968
1969        $('.gallery-popup-trigger').on('click', function (e) {
1970            e.preventDefault();
1971            e.stopPropagation();
1972
1973            const target = $(this);
1974            const slideIndex = target.data('index');
1975
1976            $.magnificPopup.open({
1977                type: 'inline',
1978                mainClass: 'rb-gallery-popup rb-popup-center',
1979                closeMarkup:
1980                    '<button title="%title%" class="mfp-close mfp-button"><i class="rbi rbi-close"></i></button>',
1981                closeOnBgClick: false,
1982                removalDelay: 400,
1983                showCloseBtn: true,
1984                fixedBgPos: true,
1985                fixedContentPos: true,
1986                preloader: false,
1987                gallery: {
1988                    enabled: true,
1989                    preload: [0, 2],
1990                    arrowMarkup:
1991                        '<button title="%title%" type="button" class="mfp-arrow mfp-button mfp-arrow-%dir%"><i class="rbi rbi-%dir%"></i></button>',
1992                    tCounter: '<span>%curr% / %total%</span>',
1993                },
1994                callbacks: {
1995                    open: function () {
1996                        let itemsArray = Object.values(self.galleriesData);
1997                        let targetIndex = itemsArray.findIndex((item) => item.key === slideIndex);
1998                        if (targetIndex !== -1) {
1999                            $.magnificPopup.instance.goTo(targetIndex);
2000                        }
2001                    },
2002                    change: function () {
2003                        let imgContainer = $('.gallery-popup-content');
2004                        let loader = imgContainer.find('.rb-loader');
2005                        let img = imgContainer.find('img');
2006                        loader.show();
2007
2008                        if (!img.length) {
2009                            loader.hide();
2010                            return;
2011                        }
2012
2013                        let loaderTimeout = setTimeout(() => {
2014                            loader.fadeIn(200);
2015                        }, 300); // Show loader only if image takes longer  300ms
2016
2017                        img.on('load', function () {
2018                            clearTimeout(loaderTimeout); // Prevent loader from showing if image loads within 300ms
2019                            loader.hide();
2020                        }).on('error', function () {
2021                            clearTimeout(loaderTimeout);
2022                            loader.hide();
2023                        });
2024                    },
2025                    afterClose: function () {
2026                        $.magnificPopup.instance.popupsCache = {};
2027                    },
2028                    buildControls: function () {
2029                        if (this.arrowLeft && this.arrowRight) {
2030                            this.contentContainer
2031                                .find('.gallery-popup-nav')
2032                                .append(this.arrowLeft.add(this.arrowRight));
2033                        }
2034                    },
2035                },
2036                inline: {
2037                    markup:
2038                        '<div class="gallery-popup-header light-scheme">' +
2039                        '<div class="mfp-counter"></div><div class="mfp-title h5"></div>' +
2040                        '<div class="popup-header-right"><div class="mfp-close"></div></div>' +
2041                        '</div><div class="gallery-popup-nav"></div>' +
2042                        '<div class="gallery-popup-content"><i class="rb-loader"></i>' +
2043                        '<div class="gallery-popup-image mfp-image"></div>' +
2044                        '<div class="gallery-popup-entry light-scheme">' +
2045                        '<div class="h4 mfp-excerpt"></div>' +
2046                        '<div class="description-text mfp-description"></div>' +
2047                        '</div>' +
2048                        '</div>',
2049                },
2050                items: Object.values(self.galleriesData),
2051            });
2052
2053            return false;
2054        });
2055    };
2056
2057    /** single featured lightbox */
2058    Module.singleFeaturedLightbox = function () {
2059        $('.featured-lightbox-trigger').on('click', function (e) {
2060            e.preventDefault();
2061            e.stopPropagation();
2062
2063            const trigger = $(this);
2064            const source = '<img src="' + trigger.data('source') + '" alt="' + $(this).find('img').attr('alt') + '">';
2065            const caption = trigger.data('caption');
2066            const attribution = trigger.data('attribution');
2067
2068            $.magnificPopup.open({
2069                mainClass: 'rb-popup-center popup-no-overflow',
2070                closeOnBgClick: true,
2071                removalDelay: 400,
2072                showCloseBtn: true,
2073                fixedBgPos: true,
2074                fixedContentPos: true,
2075                preloader: false,
2076                closeMarkup:
2077                    '<button title="%title%" class="mfp-close rb-popup-close"><i class="rbi rbi-close"></i></button>',
2078                callbacks: {
2079                    afterClose: function () {
2080                        $.magnificPopup.instance.popupsCache = {};
2081                    },
2082                },
2083                inline: {
2084                    markup:
2085                        '<div class="featured-popup-image"><div class="mfp-source"></div>' +
2086                        '<div class="mfp-close rb-popup-close"></div></div>' +
2087                        '<div class="gallery-popup-entry light-scheme">' +
2088                        '<div class="h4 mfp-excerpt"></div>' +
2089                        '<div class="description-text mfp-description"></div>' +
2090                        '</div>',
2091                },
2092                items: {
2093                    source: source,
2094                    excerpt: caption,
2095                    description: attribution,
2096                },
2097            });
2098        });
2099    };
2100
2101    /** Ajax pagination */
2102    Module.paginationNextPrev = function () {
2103        const self = this;
2104        self._body.on('click', '.pagination-trigger', function (e) {
2105            e.preventDefault();
2106            e.stopPropagation();
2107            const paginationTrigger = $(this);
2108            if (paginationTrigger.hasClass('is-disable')) {
2109                return;
2110            }
2111
2112            const block = paginationTrigger.parents('.block-wrap');
2113            const uuid = block.attr('id');
2114
2115            if (!self.ajaxData[uuid]) {
2116                self.ajaxData[uuid] = self.getBlockSettings(uuid);
2117            }
2118            if (self.ajaxData[uuid] && self.ajaxData[uuid].processing) {
2119                return;
2120            }
2121            self.ajaxData[uuid].processing = true;
2122            const type = paginationTrigger.data('type');
2123            self.ajaxStartAnimation(block, 'replace');
2124            self.ajaxReplaceLoad(block, uuid, type);
2125        });
2126    };
2127
2128    Module.ajaxReplaceLoad = function (block, uuid, type) {
2129        const self = this;
2130
2131        if (!self.ajaxData[uuid].paged) {
2132            self.ajaxData[uuid].paged = 1;
2133        }
2134        if ('prev' === type) {
2135            self.ajaxData[uuid].page_next = parseInt(self.ajaxData[uuid].paged) - 1;
2136        } else {
2137            self.ajaxData[uuid].page_next = parseInt(self.ajaxData[uuid].paged) + 1;
2138        }
2139
2140        const cacheID = self.cacheData.getCacheID(uuid, self.ajaxData[uuid].page_next);
2141
2142        /** use cache */
2143        if (self.cacheData.exist(cacheID)) {
2144            const cache = self.cacheData.get(cacheID);
2145            if ('undefined' !== typeof cache.paged) {
2146                self.ajaxData[uuid].paged = cache.paged;
2147            }
2148            setTimeout(function () {
2149                self.ajaxRenderHTML(block, uuid, cache, 'replace');
2150            }, 200);
2151        } else {
2152            /** POST AJAX */
2153            $.ajax({
2154                type: 'GET',
2155                url: self.ajaxURL,
2156                data: {
2157                    action: 'rblivep',
2158                    data: self.ajaxData[uuid],
2159                },
2160                success: function (response) {
2161                    response = JSON.parse(JSON.stringify(response));
2162                    if ('undefined' !== typeof response.paged) {
2163                        self.ajaxData[uuid].paged = response.paged;
2164                    }
2165                    self.cacheData.set(cacheID, response);
2166                    self.ajaxRenderHTML(block, uuid, response, 'replace');
2167                },
2168            });
2169        }
2170    };
2171
2172    Module.paginationLoadMore = function () {
2173        const self = this;
2174        self._body.on('click', '.loadmore-trigger', function (e) {
2175            e.preventDefault();
2176            e.stopPropagation();
2177
2178            const paginationTrigger = $(this);
2179            if (paginationTrigger.hasClass('is-disable')) {
2180                return;
2181            }
2182
2183            const block = paginationTrigger.parents('.block-wrap');
2184            const uuid = block.attr('id');
2185
2186            if (!self.ajaxData[uuid]) {
2187                self.ajaxData[uuid] = self.getBlockSettings(uuid);
2188            }
2189            if (self.ajaxData[uuid] && self.ajaxData[uuid].processing) {
2190                return;
2191            }
2192            self.ajaxData[uuid].processing = true;
2193            self.ajaxStartAnimation(block, 'append');
2194            self.ajaxAppendLoad(block, uuid);
2195        });
2196    };
2197
2198    Module.paginationInfinite = function () {
2199        const self = this;
2200
2201        const infiniteElements = $('.pagination-infinite');
2202        if (infiniteElements.length > 0) {
2203            infiniteElements.each(function () {
2204                const paginationTrigger = $(this);
2205                if (!paginationTrigger.hasClass('is-disable')) {
2206                    const block = paginationTrigger.parents('.block-wrap');
2207                    if (
2208                        (block.hasClass('is-hoz-scroll') ||
2209                            block.hasClass('is-mhoz-scroll') ||
2210                            block.hasClass('is-thoz-scroll')) &&
2211                        window.outerWidth < 1025
2212                    ) {
2213                        paginationTrigger.addClass('is-disable');
2214                        return;
2215                    }
2216                    const uuid = block.attr('id');
2217                    const observerID = 'infinite_' + uuid;
2218                    if (!self.ajaxData[uuid]) {
2219                        self.ajaxData[uuid] = self.getBlockSettings(uuid);
2220                    }
2221                    RB_VIEWPORT_OBSERVER.observe(observerID, {
2222                        element: paginationTrigger,
2223                        offset: '132%',
2224                        continuous: true,
2225                        handler: function (direction) {
2226                            if (self.ajaxData[uuid] && self.ajaxData[uuid].processing) {
2227                                return;
2228                            }
2229                            if ('down' === direction) {
2230                                self.ajaxData[uuid].processing = true;
2231                                self.ajaxStartAnimation(block, 'append');
2232                                self.ajaxAppendLoad(block, uuid);
2233                            }
2234                        },
2235                    });
2236                }
2237            });
2238        }
2239    };
2240
2241    Module.ajaxAppendLoad = function (block, uuid) {
2242        const self = this;
2243        if (!self.ajaxData[uuid].paged) {
2244            self.ajaxData[uuid].paged = 1;
2245        }
2246        if (self.ajaxData[uuid].paged >= self.ajaxData[uuid].page_max) {
2247            return;
2248        }
2249        self.ajaxData[uuid].page_next = parseInt(self.ajaxData[uuid].paged) + 1;
2250        $.ajax({
2251            type: 'GET',
2252            url: self.ajaxURL,
2253            data: {
2254                action: 'rblivep',
2255                data: self.ajaxData[uuid],
2256            },
2257            success: function (response) {
2258                response = JSON.parse(JSON.stringify(response));
2259                if ('undefined' !== typeof response.paged) {
2260                    self.ajaxData[uuid].paged = response.paged;
2261                }
2262                if ('undefined' !== typeof response.notice) {
2263                    response.content = response.content + response.notice;
2264                }
2265                self.ajaxRenderHTML(block, uuid, response, 'append');
2266            },
2267        });
2268    };
2269
2270    Module.liveSearch = function () {
2271        const liveSearch = $('.live-search-form');
2272        if (liveSearch.length === 0) {
2273            return;
2274        }
2275
2276        const self = this;
2277
2278        liveSearch.each(function () {
2279            const liveSearchEl = $(this);
2280            const input = liveSearchEl.find('input[type="text"]');
2281            const responseWrap = liveSearchEl.find('.live-search-response');
2282            const animation = liveSearchEl.find('.live-search-animation');
2283            const limit = liveSearchEl.data('limit');
2284            const search = liveSearchEl.data('search');
2285            const follow = liveSearchEl.data('follow');
2286            const tax = liveSearchEl.data('tax');
2287            const dsource = liveSearchEl.data('dsource');
2288            const ptype = liveSearchEl.data('ptype');
2289
2290            input.attr('autocomplete', 'off');
2291
2292            input.on('focus', function () {
2293                const param = $(this).val();
2294                if (param && !responseWrap.is(':empty')) {
2295                    responseWrap.css('height', 'auto').fadeIn(200);
2296                }
2297            });
2298
2299            const delay = (function () {
2300                let timer = 0;
2301                return function (callback, ms) {
2302                    clearTimeout(timer);
2303                    timer = setTimeout(callback, ms);
2304                };
2305            })();
2306
2307            input.keyup(function () {
2308                const param = $(this).val();
2309                delay(function () {
2310                    if (param) {
2311                        liveSearchEl.addClass('search-loading');
2312                        setTimeout(function () {
2313                            animation.fadeIn(100);
2314                        }, 120);
2315
2316                        $.ajax({
2317                            type: 'GET',
2318                            url: self.ajaxURL,
2319                            data: {
2320                                action: 'rbsearch',
2321                                s: param,
2322                                limit: limit,
2323                                search: search,
2324                                follow: follow,
2325                                tax: tax,
2326                                dsource: dsource,
2327                                ptype: ptype,
2328                            },
2329                            success: function (data) {
2330                                data = $.parseJSON(JSON.stringify(data));
2331                                animation.fadeOut(200);
2332                                setTimeout(function () {
2333                                    liveSearchEl.removeClass('search-loading');
2334                                }, 200);
2335                                responseWrap.hide().empty().css('height', responseWrap.height());
2336                                responseWrap.html(data);
2337                                if ('category' === search && follow && typeof FOXIZ_PERSONALIZE !== 'undefined') {
2338                                    FOXIZ_PERSONALIZE.syncLayoutCategories();
2339                                }
2340                                responseWrap.css('height', 'auto').fadeIn(200);
2341                            },
2342                        });
2343                    } else {
2344                        responseWrap.fadeOut(200, function () {
2345                            responseWrap.empty().css('height', 'auto');
2346                        });
2347                    }
2348                }, 300);
2349            });
2350        });
2351    };
2352
2353    /** personalized block */
2354    Module.personalizeBlocks = function () {
2355        const self = this;
2356        const elements = $('.is-ajax-block');
2357        if (elements.length > 0) {
2358            const blockRequests = elements.map(function () {
2359                const block = $(this);
2360                const uuid = block.attr('id');
2361                if (!self.ajaxData[uuid]) {
2362                    self.ajaxData[uuid] = self.getBlockSettings(uuid);
2363                }
2364
2365                if (
2366                    self.ajaxData[uuid].content_source &&
2367                    self.ajaxData[uuid].content_source === 'recommended' &&
2368                    typeof foxizQueriedIDs !== 'undefined' &&
2369                    foxizQueriedIDs.data
2370                ) {
2371                    self.ajaxData[uuid].post_not_in = foxizQueriedIDs.data;
2372                }
2373
2374                self.ajaxData[uuid].uID = self.getStorage('RBUUID', '0');
2375
2376                return $.ajax({
2377                    type: 'GET',
2378                    url: self.ajaxURL,
2379                    data: {
2380                        action: 'rbpersonalizeb',
2381                        data: self.ajaxData[uuid],
2382                    },
2383                });
2384            });
2385
2386            Promise.all(blockRequests).then((responses) => {
2387                responses.forEach((response, index) => {
2388                    const block = $(elements[index]);
2389                    block.html(response).fadeIn(200);
2390                    block.dequeue();
2391                });
2392                self.reloadBlockFunc();
2393            });
2394        }
2395    };
2396
2397    /** personalized categories */
2398    Module.personalizeCategories = function () {
2399        const self = this;
2400        const elements = $('.is-ajax-categories');
2401        if (elements.length > 0) {
2402            const categoryRequests = elements.map(function () {
2403                const block = $(this);
2404                const uuid = block.attr('id');
2405                if (!self.ajaxData[uuid]) {
2406                    self.ajaxData[uuid] = self.getBlockSettings(uuid);
2407                }
2408                return $.ajax({
2409                    type: 'GET',
2410                    url: self.ajaxURL,
2411                    data: {
2412                        action: 'rbpersonalizecat',
2413                        data: self.ajaxData[uuid],
2414                    },
2415                });
2416            });
2417
2418            Promise.all(categoryRequests).then((responses) => {
2419                responses.forEach((response, index) => {
2420                    const block = $(elements[index]);
2421                    block.html(response).fadeIn(200);
2422                    block.dequeue();
2423                });
2424
2425                if (typeof FOXIZ_PERSONALIZE !== 'undefined') {
2426                    FOXIZ_PERSONALIZE.syncLayoutCategories();
2427                }
2428                self.reloadBlockFunc();
2429            });
2430        }
2431    };
2432
2433    Module.readingCollect = function () {
2434        const self = this;
2435        if (self.themeSettings.yesReadingHis === undefined) {
2436            return;
2437        }
2438        $.ajax({
2439            type: 'GET',
2440            url: self.ajaxURL,
2441            data: {
2442                action: 'rbcollect',
2443                id: self.themeSettings.yesReadingHis,
2444            },
2445        });
2446    };
2447
2448    /** header notification */
2449    Module.newsNotification = function () {
2450        const notificationWrapper = $('.rb-notification');
2451        if (notificationWrapper.length < 1) {
2452            return false;
2453        }
2454
2455        const self = this;
2456        const storageKey = this.personalizeUID + '-notification';
2457        const dot = $('.notification-info');
2458
2459        // Get reload interval (now in minutes, default 15)
2460        let reload = notificationWrapper.data('interval');
2461        if (!reload || isNaN(reload) || reload <= 0) {
2462            reload = 15;
2463        }
2464
2465        // Helper: count NEW post IDs (in current but not in viewed)
2466        function countNewPosts(currentIds, viewedIds) {
2467            if (!currentIds) return 0;
2468            if (!viewedIds) return currentIds.split(',').length;
2469
2470            const current = currentIds.split(',');
2471            const viewed = viewedIds.split(',');
2472            let count = 0;
2473            for (let i = 0; i < current.length; i++) {
2474                if (viewed.indexOf(current[i]) === -1) {
2475                    count++;
2476                }
2477            }
2478            return count;
2479        }
2480
2481        // Get cached data (contains: content, count, ids, viewedIds, ttl)
2482        const cached = self.getStorage(storageKey);
2483
2484        // Show cached content immediately for instant UX
2485        if (cached && cached.content) {
2486            notificationWrapper.append(cached.content);
2487            // Show count of NEW posts only (not total)
2488            const newCount = countNewPosts(cached.ids, cached.viewedIds);
2489            if (newCount > 0) {
2490                dot.append(newCount);
2491                dot.css('opacity', 1);
2492            }
2493        }
2494
2495        // Hide dot when user opens notification dropdown
2496        $('.notification-trigger').on('click', function () {
2497            dot.css('opacity', 0).empty();
2498            // Mark current IDs as viewed
2499            const current = self.getStorage(storageKey);
2500            if (current && current.ids) {
2501                current.viewedIds = current.ids;
2502                self.setStorage(storageKey, current);
2503            }
2504        });
2505
2506        // Check if refresh needed (expired or no cache)
2507        const isExpired = !cached || !cached.ttl || Date.now() > cached.ttl;
2508
2509        if (isExpired) {
2510            $.ajax({
2511                type: 'GET',
2512                url: self.ajaxURL,
2513                data: { action: 'rbnotification' },
2514                success: function (response) {
2515                    response = $.parseJSON(JSON.stringify(response));
2516
2517                    const newIds = response.ids || '';
2518                    const oldIds = cached ? cached.ids || '' : '';
2519                    const viewedIds = cached ? cached.viewedIds || '' : '';
2520
2521                    // Store new data with TTL in minutes (preserve viewedIds)
2522                    self.setStorage(storageKey, {
2523                        content: response.content,
2524                        count: response.count,
2525                        ids: newIds,
2526                        viewedIds: viewedIds,
2527                        ttl: Date.now() + reload * 60000,
2528                    });
2529
2530                    // Update DOM if: no local cache (first load) OR post IDs changed
2531                    if (!cached || newIds !== oldIds) {
2532                        notificationWrapper.empty().append(response.content);
2533                        dot.empty();
2534                        // Show count of NEW posts only (or total if first load)
2535                        const newCount = countNewPosts(newIds, viewedIds);
2536                        if (newCount > 0) {
2537                            dot.append(newCount);
2538                            dot.css('opacity', 1);
2539                        }
2540                    }
2541                },
2542            });
2543        }
2544    };
2545
2546    /** register cache object */
2547    Module.cacheData = {
2548        data: {},
2549        get: function (id) {
2550            return this.data[id];
2551        },
2552
2553        set: function (id, data) {
2554            this.remove(id);
2555            this.data[id] = data;
2556        },
2557
2558        remove: function (id) {
2559            delete this.data[id];
2560        },
2561
2562        getCacheID: function (blockID, currentPage) {
2563            return JSON.stringify('RB_' + blockID + '_' + currentPage);
2564        },
2565
2566        exist: function (id) {
2567            return this.data.hasOwnProperty(id) && this.data[id] !== null;
2568        },
2569    };
2570
2571    /**
2572     * ajax start animation
2573     * @param block
2574     * @param action
2575     */
2576    Module.ajaxStartAnimation = function (block, action) {
2577        const inner = block.find('.block-inner');
2578        block.find('.pagination-trigger').addClass('is-disable');
2579        inner.stop();
2580
2581        if ('replace' === action) {
2582            inner.css('min-height', inner.outerHeight());
2583            inner.fadeTo('200', 0.05);
2584            inner.after('<i class="rb-loader loader-absolute"></i>');
2585        } else {
2586            block.find('.loadmore-trigger').addClass('loading');
2587            block.find('.rb-loader').css({ display: 'block' }).delay(200).animate({ opacity: 1 }, 200);
2588        }
2589    };
2590
2591    /**
2592     * render ajax
2593     * @param block
2594     * @param uuid
2595     * @param response
2596     * @param action
2597     */
2598    Module.ajaxRenderHTML = function (block, uuid, response, action) {
2599        const self = this;
2600
2601        block.delay(50).queue(function () {
2602            const uuid = block.attr('id');
2603            const inner = block.find('.block-inner');
2604            block.find('.pagination-trigger').removeClass('is-disable');
2605            inner.stop();
2606
2607            if ('replace' === action) {
2608                inner.html(response.content);
2609                block.find('.rb-loader').animate({ opacity: 0 }, 200, function () {
2610                    $(this).remove();
2611                });
2612                inner.css('min-height', '');
2613                inner.fadeTo(200, 1);
2614            } else {
2615                const content = $(response.content);
2616                inner.append(content);
2617                content.addClass('is-invisible');
2618                content.addClass('opacity-animate');
2619
2620                block.find('.rb-loader').animate({ opacity: 0 }, 200, function () {
2621                    $(this).css({ display: 'none' });
2622                });
2623                setTimeout(function () {
2624                    content.removeClass('is-invisible');
2625                }, 200);
2626                block.find('.loadmore-trigger').removeClass('loading');
2627            }
2628
2629            /** reload */
2630            self.ajaxTriggerState(block, uuid);
2631            self.ajaxData[uuid].processing = false;
2632
2633            // Refresh viewport observer for infinite scroll after content change
2634            var observerID = 'infinite_' + uuid;
2635            if (RB_VIEWPORT_OBSERVER.has(observerID)) {
2636                RB_VIEWPORT_OBSERVER.refresh(observerID);
2637            }
2638
2639            block.dequeue();
2640            self.reloadBlockFunc();
2641        });
2642    };
2643
2644    /**
2645     * set
2646     * @param block
2647     * @param uuid
2648     */
2649    Module.ajaxTriggerState = function (block, uuid) {
2650        const self = this;
2651        block.find('.pagination-trigger').removeClass('is-disable');
2652        if (self.ajaxData[uuid].paged < 2) {
2653            block.find('[data-type="prev"]').addClass('is-disable');
2654        } else if (self.ajaxData[uuid].paged >= self.ajaxData[uuid].page_max) {
2655            block.find('[data-type="next"]').addClass('is-disable');
2656            block.find('.loadmore-trigger').addClass('is-disable').hide();
2657            block.find('.pagination-infinite').addClass('is-disable').hide();
2658        }
2659    };
2660
2661    Module.getBlockSettings = function (uuid) {
2662        const settings = typeof window[uuid] !== 'undefined' ? window[uuid] : undefined;
2663        return this.cleanNull(settings);
2664    };
2665
2666    Module.cleanNull = function (data) {
2667        if (typeof data === 'string') {
2668            return data;
2669        }
2670
2671        if (data && typeof data === 'object') {
2672            Object.keys(data).forEach((key) => {
2673                if (data[key] === '' || data[key] === null) {
2674                    delete data[key];
2675                }
2676            });
2677        }
2678
2679        return data;
2680    };
2681
2682    /* SINGLE INFINITE */
2683    Module.singleInfiniteLoadNext = function () {
2684        const infiniteWrapper = $('#single-post-infinite');
2685        const self = this;
2686
2687        if (!infiniteWrapper.length || self.disabledLoadNext()) {
2688            return;
2689        }
2690
2691        self.singleLoadNextCounter = 1;
2692        self.singleLoadNextLimit = self.themeSettings?.singleLoadNextLimit
2693            ? parseInt(self.themeSettings.singleLoadNextLimit, 10)
2694            : 20;
2695
2696        const infiniteLoadPoint = $('#single-infinite-point');
2697        const animationIcon = infiniteLoadPoint.find('.rb-loader');
2698        const rootURL = new URL(window.location.href);
2699        const rootGetParams = rootURL.searchParams;
2700
2701        RB_VIEWPORT_OBSERVER.observe('ajaxSingleNextPosts', {
2702            element: infiniteLoadPoint,
2703            offset: '138%',
2704            continuous: true,
2705            handler: function (direction) {
2706                if (
2707                    self.ajaxData.singleProcessing ||
2708                    direction === 'up' ||
2709                    self.singleLoadNextCounter > self.singleLoadNextLimit
2710                ) {
2711                    return;
2712                }
2713                const nextPostURL = new URL(infiniteWrapper.data('nextposturl'));
2714                nextPostURL.searchParams.set('rbsnp', '1');
2715                if (rootGetParams) {
2716                    rootGetParams.forEach((value, key) => {
2717                        if (key !== 'rbsnp' && 'p' !== key) {
2718                            nextPostURL.searchParams.set(key, value);
2719                        }
2720                    });
2721                }
2722                self.ajaxData.singleProcessing = true;
2723                animationIcon.css('display', 'block').animate({ opacity: 1 }, 200);
2724
2725                $.ajax({
2726                    type: 'GET',
2727                    url: nextPostURL.toString(),
2728                    dataType: 'html',
2729                    success: function (response) {
2730                        response = $('<div id="temp-dom"></div>')
2731                            .append($.parseHTML(response))
2732                            .find('.single-post-outer');
2733                        const nextPostURL = response.data('nextposturl');
2734
2735                        if (nextPostURL) {
2736                            infiniteWrapper.data('nextposturl', nextPostURL);
2737                        } else {
2738                            infiniteWrapper.removeAttr('id');
2739                            infiniteLoadPoint.remove();
2740                            RB_VIEWPORT_OBSERVER.unobserve('ajaxSingleNextPosts');
2741                        }
2742
2743                        animationIcon.animate({ opacity: 0 }, 200, function () {
2744                            $(this).css('display', 'none');
2745                        });
2746                        infiniteWrapper.append(response);
2747                        self.ajaxData.singleProcessing = false;
2748                        self.singleLoadNextCounter++;
2749
2750                        // Refresh observer for next trigger
2751                        if (RB_VIEWPORT_OBSERVER.has('ajaxSingleNextPosts')) {
2752                            RB_VIEWPORT_OBSERVER.refresh('ajaxSingleNextPosts');
2753                        }
2754
2755                        setTimeout(function () {
2756                            self.reInitAll();
2757                            if (typeof FOXIZ_CORE_SCRIPT !== 'undefined') {
2758                                FOXIZ_CORE_SCRIPT.loadGoogleAds(response);
2759                                FOXIZ_CORE_SCRIPT.loadInstagram(response);
2760                                FOXIZ_CORE_SCRIPT.loadTwttr();
2761                                FOXIZ_CORE_SCRIPT.loadTikTok && FOXIZ_CORE_SCRIPT.loadTikTok(response);
2762                                FOXIZ_CORE_SCRIPT.loadThreads && FOXIZ_CORE_SCRIPT.loadThreads(response);
2763                                FOXIZ_CORE_SCRIPT.loadBluesky && FOXIZ_CORE_SCRIPT.loadBluesky(response);
2764                            }
2765                        }, 1);
2766                    },
2767                    error: function () {
2768                        animationIcon.animate({ opacity: 0 }, 200, function () {
2769                            $(this).css('display', 'none');
2770                        });
2771                        self.ajaxData.singleProcessing = false;
2772                    },
2773                });
2774            },
2775        });
2776    };
2777
2778    Module.singleScrollRefresh = function () {
2779        const infiniteWrapper = $('#single-post-infinite');
2780        if (!infiniteWrapper.length) {
2781            return;
2782        }
2783        const self = this;
2784        self.articleData = [];
2785        const articleOuter = infiniteWrapper.find('.single-post-outer');
2786
2787        if (articleOuter.length > 0) {
2788            self.inviewPostID = articleOuter.eq(0).data('postid');
2789
2790            articleOuter.each(function () {
2791                const article = $(this);
2792                const itemData = {
2793                    postID: article.data('postid'),
2794                    postURL: article.data('postlink'),
2795                    postTitle: article.find('h1.s-title').text(),
2796                    shareList: article.find('.sticky-share-list-buffer').html(),
2797                    top: article.offset().top,
2798                    bottom: article.offset().top + article.outerHeight(true),
2799                };
2800
2801                if (self.readIndicator.length > 0) {
2802                    const content = article.find('.rbct').eq(0);
2803                    itemData.indicatorTop = content.offset().top;
2804                    itemData.indicatorHeight = content.outerHeight(true) - self._window.height();
2805                }
2806                self.articleData.push(itemData);
2807            });
2808
2809            // Only add scroll listener once
2810            if (!self._singleScrollListenerAdded) {
2811                self._singleScrollListenerAdded = true;
2812                self.addScrollHandler('singleScroll', self.scrollToUpdateArticle.bind(self));
2813            }
2814        }
2815    };
2816
2817    /** scrollToUpdateArticle */
2818    Module.scrollToUpdateArticle = function () {
2819        const self = this;
2820        const scroll = self._window.scrollTop();
2821
2822        self.articleData.every((article) => {
2823            if (scroll > article.top + 5 && scroll < article.bottom - 5) {
2824                if (article.indicatorTop) {
2825                    self.readIndicatorPercent = Math.min(
2826                        ((scroll - article.indicatorTop) / article.indicatorHeight) * 100,
2827                        100
2828                    );
2829                    if (self.readIndicatorPercent <= 100) {
2830                        self.readIndicator.css('width', `${self.readIndicatorPercent}%`);
2831                    }
2832                }
2833
2834                if (article.postID !== self.inviewPostID) {
2835                    self.inviewPostID = article.postID;
2836
2837                    const articleEl = $('[data-postid="' + article.postID + '"]');
2838
2839                    // Always update URL and title
2840                    if (article.postURL) {
2841                        history.replaceState({ postId: article.postID }, article.postTitle, article.postURL);
2842                        self.updateCanonicalURL(article.postURL);
2843                    }
2844                    document.title = article.postTitle;
2845
2846                    $('.single-post-outer').removeClass('activated');
2847                    articleEl.addClass('activated');
2848                    $('#s-title-sticky .sticky-title').hide().html(article.postTitle).fadeIn(300);
2849                    $('#s-title-sticky .sticky-share-list').html(article.shareList);
2850                    self._body.find('.floating-video').removeClass('floating-activated');
2851
2852                    // Only trigger analytics if NOT in teaser mode (full content visible)
2853                    const hasContinueReading = articleEl.hasClass('has-continue-reading');
2854                    const continueBtn = articleEl.find('.continue-reading');
2855                    const isTeaserMode = hasContinueReading && continueBtn.length > 0 && continueBtn.is(':visible');
2856
2857                    if (!isTeaserMode && typeof FOXIZ_CORE_SCRIPT !== 'undefined') {
2858                        FOXIZ_CORE_SCRIPT.updateGA(article);
2859                    }
2860                }
2861                return false;
2862            }
2863
2864            return true;
2865        });
2866    };
2867
2868    /**
2869     * Update canonical URL for SEO
2870     * @param {string} url
2871     */
2872    Module.updateCanonicalURL = function (url) {
2873        if (!url) return;
2874
2875        let canonical = document.querySelector('link[rel="canonical"]');
2876        if (canonical) {
2877            canonical.href = url;
2878        }
2879
2880        // Update Open Graph URL
2881        let ogUrl = document.querySelector('meta[property="og:url"]');
2882        if (ogUrl) {
2883            ogUrl.content = url;
2884        }
2885    };
2886
2887    /**
2888     * @returns {boolean}
2889     */
2890    Module.crwDetect = function () {
2891        const botPatterns = [
2892            /alexa|altavista|ask jeeves|attentio|baiduspider|bingbot|chtml generic|crawler|fastmobilecrawl|feedfetcher-google|firefly|froogle|gigabot|googlebot|googlebot-mobile|heritrix|httrack|ia_archiver|irlbot|iescholar|infoseek|jumpbot|linkcheck|lycos|mediapartners|mediobot|motionbot|msnbot|mshots|openbot|pss-webkit-request|pythumbnail|scooter|slurp|snapbot|spider|taptubot|technoratisnoop|teoma|twiceler|yahooseeker|yahooysmcm|yammybot|ahrefsbot|pingdom.com_bot|kraken|yandexbot|twitterbot|tweetmemebot|openhosebot|queryseekerspider|linkdexbot|grokkit-crawler|livelapbot|germcrawler|domaintunocrawler|grapeshotcrawler|cloudflare-alwaysonline/i,
2893        ];
2894
2895        const userAgent = navigator.userAgent;
2896        return botPatterns.some((pattern) => pattern.test(userAgent));
2897    };
2898
2899    /**
2900     *
2901     * @returns {boolean}
2902     */
2903    Module.disabledLoadNext = function () {
2904        if (this.themeSettings.crwLoadNext) {
2905            return false;
2906        }
2907
2908        return this.crwDetect();
2909    };
2910
2911    /** productQuantity */
2912    Module.productQuantity = function () {
2913        this._document.on('click', '.quantity .quantity-btn', function (e) {
2914            e.preventDefault();
2915            e.stopPropagation();
2916
2917            const button = $(this);
2918            const input = button.parent().find('input');
2919
2920            let step = 1;
2921            let min = 1;
2922            let max = 9999;
2923            let value_old = parseInt(input.val());
2924            let value_new = parseInt(input.val());
2925
2926            if (input.attr('step')) {
2927                step = parseInt(input.attr('step'));
2928            }
2929
2930            if (input.attr('min')) {
2931                min = parseInt(input.attr('min'));
2932            }
2933
2934            if (input.attr('max')) {
2935                max = parseInt(input.attr('max'));
2936            }
2937
2938            if (button.hasClass('up')) {
2939                if (value_old < max) {
2940                    value_new = value_old + step;
2941                } else {
2942                    value_new = 1;
2943                }
2944            } else if (button.hasClass('down')) {
2945                if (value_old > min) {
2946                    value_new = value_old - step;
2947                } else {
2948                    value_new = 0;
2949                }
2950            }
2951
2952            if (!input.attr('disabled')) {
2953                input.val(value_new).change();
2954            }
2955        });
2956    };
2957
2958    Module.cartNotification = function () {
2959        const self = this;
2960        const element = $('#add-cart-popup');
2961
2962        self._body.on('added_to_cart', function () {
2963            element.fadeIn(200);
2964            self.cartNotificationTimeOut = setTimeout(function () {
2965                element.fadeOut(200);
2966            }, 5000);
2967            self.hoveringNotification(element);
2968        });
2969    };
2970
2971    Module.hoveringNotification = function (element) {
2972        const self = this;
2973
2974        element.on('mouseenter', function () {
2975            clearTimeout(self.cartNotificationTimeOut);
2976        });
2977
2978        element.on('mouseleave', function () {
2979            setTimeout(function () {
2980                element.fadeOut(200);
2981            }, 1500);
2982        });
2983    };
2984
2985    /** like/dislike */
2986    Module.likeToggle = function () {
2987        const self = this;
2988        let reaction = '';
2989        self._body.on('click', '.like-trigger, .dislike-trigger', function (e) {
2990            e.preventDefault();
2991            e.stopPropagation();
2992
2993            const target = $(this);
2994            const wrap = target.parent('[data-like]');
2995            const pid = wrap.data('like');
2996
2997            if (!pid || self.isProgress) return;
2998
2999            self.isProgress = true;
3000            const storageKey = self.getLikeKey(pid);
3001            const isLike = target.hasClass('like-trigger');
3002            const isDislike = target.hasClass('dislike-trigger');
3003            const sibEl = target.siblings();
3004            const likeCountEl = wrap.find('.like-count');
3005            const dislikeCountEl = wrap.find('.dislike-count');
3006            let likeCountText = likeCountEl.text();
3007            let dislikeCountText = dislikeCountEl.text();
3008
3009            if (isLike) {
3010                if (!target.hasClass('triggered')) {
3011                    reaction = 'like';
3012                    if (!/[kKmM]/.test(likeCountText)) {
3013                        likeCountText = parseInt(likeCountText);
3014                        if (isNaN(likeCountText)) {
3015                            likeCountText = 0;
3016                        }
3017                        likeCountEl.text(likeCountText + 1);
3018                    }
3019                    target.addClass('triggered');
3020
3021                    if (sibEl.hasClass('triggered')) {
3022                        sibEl.removeClass('triggered');
3023                        if (!/[kKmM]/.test(dislikeCountText)) {
3024                            dislikeCountText = parseInt(dislikeCountText) - 1;
3025                            if (isNaN(dislikeCountText) || dislikeCountText < 1) {
3026                                dislikeCountText = '';
3027                            }
3028                            dislikeCountEl.text(dislikeCountText);
3029                        }
3030                    }
3031                    self.setStorage(storageKey, 'like');
3032                } else {
3033                    reaction = 'rmlike';
3034                    if (!/[kKmM]/.test(likeCountText)) {
3035                        likeCountText = parseInt(likeCountText) - 1;
3036                        if (isNaN(likeCountText) || likeCountText < 1) {
3037                            likeCountText = '';
3038                        }
3039                        likeCountEl.text(likeCountText);
3040                    }
3041                    target.removeClass('triggered');
3042                    self.deleteStorage(storageKey);
3043                }
3044            } else if (isDislike) {
3045                if (!target.hasClass('triggered')) {
3046                    reaction = 'dislike';
3047                    if (!/[kKmM]/.test(dislikeCountText)) {
3048                        dislikeCountText = parseInt(dislikeCountText);
3049                        if (isNaN(dislikeCountText)) {
3050                            dislikeCountText = 0;
3051                        }
3052                        dislikeCountEl.text(dislikeCountText + 1);
3053                    }
3054                    target.addClass('triggered');
3055                    if (sibEl.hasClass('triggered')) {
3056                        sibEl.removeClass('triggered');
3057                        if (!/[kKmM]/.test(likeCountText)) {
3058                            likeCountText = parseInt(likeCountText) - 1;
3059                            if (isNaN(likeCountText) || likeCountText < 1) {
3060                                likeCountText = '';
3061                            }
3062                            likeCountEl.text(likeCountText);
3063                        }
3064                    }
3065                    self.setStorage(storageKey, 'dislike');
3066                } else {
3067                    reaction = 'rmdislike';
3068                    if (!/[kKmM]/.test(dislikeCountText)) {
3069                        likeCountText = parseInt(likeCountText) - 1;
3070                        if (isNaN(likeCountText) || likeCountText < 1) {
3071                            likeCountText = '';
3072                        }
3073                        dislikeCountEl.text(likeCountText);
3074                    }
3075                    target.removeClass('triggered');
3076                    self.deleteStorage(storageKey);
3077                }
3078            }
3079
3080            $.ajax({
3081                type: 'GET',
3082                url: self.ajaxURL,
3083                data: {
3084                    uuid: self.personalizeUID,
3085                    action: 'rbvoting',
3086                    value: reaction,
3087                    pid: pid,
3088                },
3089                complete: () => {
3090                    self.isProgress = false;
3091                },
3092            });
3093        });
3094    };
3095
3096    /** sync layout */
3097    Module.syncLayoutLike = function () {
3098        this.isProgress = true;
3099        const likeElements = document.querySelectorAll('[data-like]:not(.loaded)');
3100        const jsCount = this._body.hasClass('is-jscount');
3101        let count, countEl;
3102
3103        for (const el of likeElements) {
3104            el.classList.add('loaded');
3105            const key = this.getLikeKey(el.getAttribute('data-like'));
3106            const triggered = this.getStorage(key);
3107
3108            if (!triggered) continue;
3109
3110            const likeEl = el.querySelector('.el-like');
3111            const dislikeEl = el.querySelector('.el-dislike');
3112
3113            if (!likeEl || !dislikeEl) continue;
3114
3115            if (triggered === 'like') {
3116                likeEl.classList.add('triggered');
3117                countEl = jsCount ? likeEl.querySelector('.like-count') : null;
3118                if (countEl) {
3119                    count = parseInt(countEl.textContent.trim()) || 0;
3120                    countEl.textContent = count + 1;
3121                }
3122            } else if (triggered === 'dislike') {
3123                dislikeEl.classList.add('triggered');
3124                countEl = jsCount ? dislikeEl.querySelector('.dislike-count') : null;
3125                if (countEl) {
3126                    count = parseInt(countEl.textContent.trim()) || 0;
3127                    countEl.textContent = count + 1;
3128                }
3129            }
3130        }
3131
3132        this.isProgress = false;
3133    };
3134
3135    /** like key */
3136    Module.getLikeKey = function (id) {
3137        return this.personalizeUID + '-like-' + id;
3138    };
3139
3140    /** live blog */
3141    Module.liveBlog = function () {
3142        const self = this;
3143
3144        const liveEntry = $('.rb-live-entry');
3145
3146        if (liveEntry.length === 0) {
3147            return false;
3148        }
3149
3150        let intervalId;
3151        let delayTimeoutId;
3152        const interval = Math.min(1800, Math.max(15, this.themeSettings.liveInterval || 1800));
3153        const liveSwitcher = $('#live-interval-switcher');
3154        const storedSetting = this.getStorage('liveIntervalCheckbox');
3155
3156        // Start the regular interval (no initial delay)
3157        const startInterval = () => {
3158            clearInterval(intervalId);
3159            intervalId = setInterval(() => this.updateLiveBlog(liveEntry), interval * 1000);
3160        };
3161
3162        // Stop all timers
3163        const stopAll = () => {
3164            clearInterval(intervalId);
3165            clearTimeout(delayTimeoutId);
3166        };
3167
3168        // Handle manual toggle with 5s delay to avoid rapid triggering
3169        const handleToggle = () => {
3170            stopAll();
3171
3172            if (liveSwitcher.prop('checked')) {
3173                // Delay 3s before first load, then continue with regular interval
3174                delayTimeoutId = setTimeout(() => {
3175                    this.updateLiveBlog(liveEntry);
3176                    startInterval();
3177                }, 3000);
3178                self.setStorage('liveIntervalCheckbox', 'yes');
3179            } else {
3180                self.setStorage('liveIntervalCheckbox', 'no');
3181            }
3182        };
3183
3184        // Initialize from stored setting
3185        if (storedSetting !== null) {
3186            liveSwitcher.prop('checked', storedSetting === 'yes');
3187        } else {
3188            self.setStorage('liveIntervalCheckbox', 'yes');
3189        }
3190
3191        // On page load: start interval immediately if checked (no delay)
3192        if (liveSwitcher.prop('checked')) {
3193            startInterval();
3194        }
3195
3196        // On manual toggle: use 5s delay
3197        liveSwitcher.on('change', handleToggle);
3198    };
3199
3200    Module.updateLiveBlog = function (liveEntry) {
3201        const self = this;
3202        const liveURL = new URL(window.location.href);
3203        liveURL.searchParams.set('rblive', '1');
3204        $.ajax({
3205            type: 'GET',
3206            url: liveURL.toString(),
3207            dataType: 'html',
3208            success: function (response) {
3209                response = $('<div id="temp-dom"></div>').append($.parseHTML(response)).find('#rb-live-content');
3210                liveEntry.html(response.html());
3211                const liveCountElement = liveEntry.prev().find('.live-count');
3212                if (liveCountElement.length) {
3213                    liveCountElement.text(response.data('total'));
3214                }
3215                setTimeout(function () {
3216                    self.reInitAll();
3217                    if (typeof FOXIZ_CORE_SCRIPT !== 'undefined') {
3218                        FOXIZ_CORE_SCRIPT.loadGoogleAds(response);
3219                        FOXIZ_CORE_SCRIPT.loadInstagram(response);
3220                        FOXIZ_CORE_SCRIPT.loadTwttr();
3221                    }
3222                }, 1);
3223            },
3224        });
3225    };
3226
3227    Module.accordion = function () {
3228        $('.gb-accordion').each(function () {
3229            const accordion = $(this);
3230            if (accordion.hasClass('yesLoaded')) return;
3231
3232            accordion.addClass('yesLoaded');
3233            const accordionItems = accordion.find('.gb-accordion-item');
3234
3235            if (accordion.hasClass('yes-open')) {
3236                accordionItems.first().addClass('active').find('.accordion-item-content').css('display', 'block');
3237            }
3238
3239            accordion.on('click', '.accordion-item-header', function (e) {
3240                e.preventDefault();
3241
3242                const header = $(this);
3243                const item = header.closest('.gb-accordion-item');
3244                const content = item.find('.accordion-item-content');
3245
3246                if (item.hasClass('active')) {
3247                    item.removeClass('active');
3248                    content.stop().slideUp(200);
3249                } else {
3250                    const activeItem = accordionItems.filter('.active');
3251                    activeItem.removeClass('active').find('.accordion-item-content').stop().slideUp(200);
3252                    item.addClass('active');
3253                    content.stop().slideDown(200);
3254                }
3255            });
3256        });
3257    };
3258
3259    Module.resIframeClassic = function () {
3260        if (!document.body.classList.contains('res-iframe-classic')) return;
3261        const iframes = document.querySelectorAll('.rbct iframe');
3262        iframes.forEach((iframe) => {
3263            const parent = iframe.parentElement;
3264            const grandParent = parent.parentElement;
3265            const greatGrandParent = grandParent.parentElement;
3266
3267            if (
3268                [parent, grandParent, greatGrandParent].some((element) => {
3269                    const classNames = Array.from(element.classList);
3270                    return classNames.some((className) => className.indexOf('wp-block') !== -1);
3271                })
3272            )
3273                return;
3274
3275            if (!iframe.parentElement.classList.contains('rb-video-ires')) {
3276                if (
3277                    iframe.src.includes('youtube.com') ||
3278                    iframe.src.includes('youtu.be') ||
3279                    iframe.src.includes('vimeo.com') ||
3280                    iframe.src.includes('dailymotion.com')
3281                ) {
3282                    iframe.parentElement.classList.add('rb-video-ires');
3283                }
3284            }
3285        });
3286    };
3287
3288    Module.taxBasedAccordion = function () {
3289        const self = this;
3290        $('.block-tax-accordion').each(function () {
3291            const accordion = $(this);
3292            if (accordion.hasClass('is-loaded')) {
3293                return;
3294            }
3295            accordion.addClass('is-loaded');
3296            const accordionItems = accordion.find('.tax-accordion-item');
3297            const lastOpenedTab = self.getStorage(accordion.attr('id') + '_lastOpenedTab', null);
3298
3299            if (lastOpenedTab) {
3300                const tabToOpen = accordion.find('[data-tab="' + lastOpenedTab + '"] .tax-accordion-sub');
3301                if (tabToOpen.length !== 0) {
3302                    tabToOpen.show();
3303                }
3304            }
3305
3306            accordionItems.each(function () {
3307                const item = $(this);
3308                const trigger = item.find('.tax-accordion-trigger');
3309                const subMenu = item.find('.tax-accordion-sub');
3310
3311                trigger.click(function () {
3312                    if (!subMenu.is(':visible')) {
3313                        accordion.find('.tax-accordion-sub').not(subMenu).slideUp();
3314                    }
3315                    subMenu.slideToggle(function () {
3316                        if (subMenu.is(':visible')) {
3317                            self.setStorage(accordion.attr('id') + '_lastOpenedTab', item.attr('data-tab'));
3318                        } else {
3319                            self.deleteStorage(accordion.attr('id') + '_lastOpenedTab');
3320                        }
3321                    });
3322                });
3323            });
3324        });
3325    };
3326
3327    Module.sequentialGalleryDisplay = function () {
3328        const galleries = Array.from(document.querySelectorAll('.gallery-animated'));
3329        galleries.forEach((gallery, galleryIndex) => {
3330            const images = Array.from(gallery.querySelectorAll('.e-gallery-item'));
3331            RB_VIEWPORT_OBSERVER.observe('gallery_' + galleryIndex, {
3332                element: gallery,
3333                offset: '100%',
3334                once: true,
3335                handler: function (direction) {
3336                    if (direction === 'down') {
3337                        images.forEach((image, index) => {
3338                            requestAnimationFrame(() => {
3339                                setTimeout(() => {
3340                                    image.classList.add('gallery-visible');
3341                                }, index * 120);
3342                            });
3343                        });
3344                    }
3345                },
3346            });
3347        });
3348    };
3349
3350    Module.reloadCaptchaPopups = function (delay = 50) {
3351        setTimeout(() => {
3352            const $content = $($.magnificPopup?.instance?.content || []);
3353            if (!$content.length) return;
3354
3355            const removeDuplicateCaptchas = (form) => {
3356                const seen = new Set();
3357                $(form)
3358                    .find(
3359                        '.g-recaptcha, .h-captcha, .cf-turnstile, .turnstile-widget, .frc-captcha, [data-arkose-public-key], .arkose-enforcement, .geetest_holder, .geetest_panel'
3360                    )
3361                    .each(function () {
3362                        const cls = this.className;
3363                        const type = [
3364                            'g-recaptcha',
3365                            'h-captcha',
3366                            'cf-turnstile',
3367                            'turnstile-widget',
3368                            'frc-captcha',
3369                            'arkose',
3370                            'geetest',
3371                        ].find(
3372                            (t) =>
3373                                cls.includes(t) ||
3374                                $(this).data('arkose-public-key') ||
3375                                $(this).hasClass('arkose-enforcement')
3376                        );
3377                        if (!type) return;
3378                        const key = `${type}`;
3379                        if (seen.has(key)) {
3380                            $(this).remove();
3381                        } else {
3382                            seen.add(key);
3383                        }
3384                    });
3385            };
3386
3387            try {
3388                // Remove duplicates per form
3389                $content.find('form').each(function () {
3390                    removeDuplicateCaptchas(this);
3391                });
3392
3393                // Google reCAPTCHA
3394                if (typeof grecaptcha !== 'undefined' && grecaptcha?.reset) {
3395                    $content.find('.g-recaptcha').each(function () {
3396                        const wid = this.dataset.widgetId;
3397                        if (wid) {
3398                            try {
3399                                grecaptcha.reset(Number(wid));
3400                            } catch {}
3401                        }
3402                    });
3403                }
3404
3405                // Cloudflare Turnstile
3406                if (typeof turnstile !== 'undefined' && turnstile?.reset) {
3407                    $content.find('.cf-turnstile, .turnstile-widget').each(function () {
3408                        const iframe = this.querySelector('iframe');
3409                        const wid = iframe?.dataset?.wid;
3410                        if (wid) {
3411                            try {
3412                                turnstile.reset(wid);
3413                            } catch {}
3414                        }
3415                    });
3416                }
3417
3418                // hCaptcha
3419                if (typeof hcaptcha !== 'undefined' && hcaptcha?.reset) {
3420                    $content.find('.h-captcha').each(function () {
3421                        const wid = this.dataset.widgetId;
3422                        if (wid) {
3423                            try {
3424                                hcaptcha.reset(Number(wid));
3425                            } catch {}
3426                        }
3427                    });
3428                }
3429
3430                // FriendlyCaptcha
3431                if (typeof friendlycaptcha !== 'undefined' && friendlycaptcha?.widget) {
3432                    $content.find('.frc-captcha').each(function () {
3433                        const stored = $(this).data('frc-widget');
3434                        if (stored?.reset) {
3435                            try {
3436                                stored.reset();
3437                            } catch {}
3438                        }
3439                    });
3440                }
3441
3442                // Arkose Labs
3443                if (window?.arkose?.reset || typeof ECArkose !== 'undefined') {
3444                    const $arkoseEls = $content.find('[data-arkose-public-key], .arkose-enforcement');
3445                    if ($arkoseEls.length) {
3446                        try {
3447                            window?.arkose?.reset?.();
3448                            ECArkose?.reset?.();
3449                        } catch {}
3450                    }
3451                }
3452
3453                // GeeTest
3454                if (typeof initGeetest !== 'undefined' || typeof GeetInit !== 'undefined') {
3455                    $content.find('.geetest_holder, .geetest_panel').each(function () {
3456                        const inst = this.geetestInstance;
3457                        if (inst?.reset) {
3458                            try {
3459                                inst.reset();
3460                            } catch {}
3461                        }
3462                    });
3463                }
3464            } catch (err) {}
3465        }, delay);
3466    };
3467
3468    return Module;
3469})(FOXIZ_MAIN_SCRIPT || {}, jQuery);
3470
3471/** init */
3472jQuery(document).ready(function ($) {
3473    FOXIZ_MAIN_SCRIPT.init();
3474});
3475
3476/** Elementor editor */
3477jQuery(window).on('elementor/frontend/init', function () {
3478    if (
3479        typeof elementorFrontend !== 'undefined' &&
3480        typeof elementorFrontend.isEditMode === 'function' &&
3481        elementorFrontend.isEditMode()
3482    ) {
3483        FOXIZ_MAIN_SCRIPT.isElementorEditor = true;
3484        FOXIZ_MAIN_SCRIPT.editorDarkModeInit = false;
3485
3486        elementorFrontend.hooks.addAction('frontend/element_ready/widget', FOXIZ_MAIN_SCRIPT.initElementor);
3487    }
3488});

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.