PageSourceSearch

https://www.artezahori.com/wp-content/plugins/divi-essential/assets/js/popup-pro.js

js artezahori.com collected 2026-10-02 23:03:41 UTC 28,688 bytes, 770 lines download raw bytes

1// Custom Popup Implementation - No Magnific Popup Required
2(function ($) {
3  // Popup settings arrive via data-* attributes, which the browser has already
4  // decoded — so the server's escaping is gone by the time these values are
5  // spliced back into markup below. Escape again at the sink.
6  function escapeAttr(value) {
7    return String(value == null ? "" : value)
8      .replace(/&/g, "&")
9      .replace(/"/g, """)
10      .replace(/</g, "&lt;")
11      .replace(/>/g, "&gt;");
12  }
13
14  // These two land in a class attribute, where only class-name characters are
15  // ever legitimate; anything else is dropped rather than escaped.
16  function sanitizeClassToken(value) {
17    return String(value == null ? "" : value).replace(/[^\w\s-]/g, "");
18  }
19
20  // Custom popup implementation object
21  const DnxteCustomPopup = {
22    // Current active popup (for backward compatibility)
23    currentPopup: null,
24    // Track all active popups
25    activePopups: [],
26
27    // Cache of detached popup templates keyed by their source selector.
28    templates: {},
29
30    // Initialize the popup
31    open: function (config) {
32      // The popup markup is server-rendered once inside the hidden #dnxte_popup
33      // holder and cloned into the visible wrapper on open. Cloning the LIVE
34      // holder each time left two elements in the DOM sharing the same ids (the
35      // popup container id plus any custom module ids the user set, e.g.
36      // #popupcoutdown on a countdown) — invalid HTML that makes
37      // getElementById()/#id lookups resolve to the hidden copy. On first use,
38      // cache a detached template (ids intact, captured after Divi has
39      // initialized the holder so clone behavior is unchanged) and remove the
40      // live-holder copy, so only the visible clone carries the ids.
41      const src = config.items.src;
42      let popupContent;
43      if (typeof src === "string") {
44        if (!this.templates[src]) {
45          const liveSource = $(src);
46          if (!liveSource.length) return;
47          this.templates[src] = liveSource.clone();
48          liveSource.remove();
49        }
50        popupContent = this.templates[src];
51      } else {
52        // Non-string src (already a jQuery/DOM node): use as-is, no dedup needed.
53        popupContent = $(src);
54      }
55      if (!popupContent || !popupContent.length) return;
56
57      // Create popup wrapper (creates both background and wrapper)
58      const popupWrapper = this.createPopupWrapper(config);
59      const popupContainer = this.createPopupContainer(config);
60
61      // Clone content and make it visible
62      const clonedContent = popupContent.clone();
63      clonedContent.css('display', 'block');
64
65      // Divi 5 lazy-loads video iframes (src moved to data-src, restored by an
66      // IntersectionObserver when scrolled into view). That observer can never
67      // fire for popup content: the holder is display:none, then removed, and
68      // each open injects a fresh clone the observer has never seen. Opening
69      // the popup IS the visibility signal, so restore the real src here.
70      clonedContent.find('iframe[data-src], source[data-src]').each(function () {
71        this.setAttribute('src', this.getAttribute('data-src'));
72        this.removeAttribute('data-src');
73      });
74
75      // Apply popup position classes
76      this.applyPopupPosition(popupContainer, clonedContent);
77
78      popupContainer.prepend(clonedContent);
79
80      // Add close button if needed - for inline popups, add to wrapper not content
81      if (config.closeMarkup && !config.closeBtnInside) {
82        popupWrapper.prepend(config.closeMarkup);
83      } else if (config.closeMarkup && config.closeBtnInside) {
84        // how to check already exit button or not
85        if (popupContainer.children().last().find('.dnxte_popup_close').length < 1) {
86          popupContainer.children().last().prepend(config.closeMarkup);
87        }
88      }
89
90      // Append container to wrapper's dnxte_popup_container
91      popupWrapper.find('.dnxte_popup_container').append(popupContainer);
92
93      // Store current popup reference (maintain backward compatibility)
94      this.currentPopup = popupWrapper;
95      // Also add to activePopups array
96      this.activePopups.push(popupWrapper);
97
98      // Re-initialize countdown timers in the popup
99      this.initializeCountdownTimers(clonedContent);
100
101      // Trigger beforeOpen callback
102      if (config.callbacks && config.callbacks.beforeOpen) {
103        config.callbacks.beforeOpen();
104      }
105
106      // Bind events  
107      this.bindEvents(popupWrapper, config);
108
109      // Prevent body scroll if needed
110      if (config.fixedContentPos) {
111        $('body').addClass('dnxte-popup-open');
112      }
113
114      // Re-initialize Divi element links on the cloned popup content
115      // Cloned elements don't carry jQuery event handlers, so Divi's
116      // element-link click bindings (from diviElementLinkData) are lost.
117      // if (typeof window.et_pb_init_link === 'function') {
118      //   window.et_pb_init_link();
119      // }
120    },
121
122    close: function (specificPopup = null) {
123      const popup = specificPopup || this?.currentPopup;
124      if (!popup) return;
125
126      const bgOverlay = popup?.data('bg-overlay');
127      const config = popup?.data('popup-config');
128
129      // Trigger beforeClose callback
130      if (config && config.callbacks && config.callbacks.beforeClose) {
131        config.callbacks.beforeClose();
132      }
133
134      // Add closing animation classes
135      popup?.addClass('dnxte_popup-removing');
136      if (bgOverlay) {
137        bgOverlay?.addClass('dnxte_popup-removing');
138      }
139
140      // Remove after animation
141      setTimeout(() => {
142        popup?.remove();
143        if (bgOverlay) {
144          bgOverlay.remove();
145        }
146
147        // Remove from activePopups array
148        const index = this.activePopups.indexOf(popup);
149        if (index > -1) {
150          this.activePopups.splice(index, 1);
151        }
152
153        // Update currentPopup reference
154        if (this.currentPopup === popup) {
155          this.currentPopup = this.activePopups.length > 0 ? this.activePopups[this.activePopups.length - 1] : null;
156        }
157
158        // Only remove body class if no popups are active
159        if (this.activePopups.length === 0) {
160          $('body').removeClass('dnxte-popup-open');
161        }
162
163        // Trigger afterClose callback
164        if (config && config.callbacks && config.callbacks.afterClose) {
165          config.callbacks.afterClose();
166        }
167      }, config ? config.removalDelay || 1000 : 1000);
168    },
169
170    createPopupWrapper: function (config) {
171
172      // Create background overlay with exact magnific popup classes
173      const bgOverlay = $('<div>', {
174        class: `dnxte_popup_overlay ${config.mainClass || ''} animate__animated`,
175      });
176
177      // Create popup wrapper with exact magnific popup classes  
178      const wrapper = $('<div>', {
179        class: `dnxte_popup_wrap ${config.mainClass || ''}`,
180      });
181
182      // Create container
183      const container = $('<div>', {
184        class: 'dnxte_popup_container'
185      });
186      wrapper.append(bgOverlay);
187      wrapper.append(container);
188
189      // Add both background and wrapper to body
190      $('body').prepend(wrapper);
191      //document.body.insertAdjacentHTML("afterbegin", wrapper);
192
193      wrapper.data('popup-config', config);
194      wrapper.data('bg-overlay', bgOverlay);
195      return wrapper;
196    },
197
198    createPopupContainer: function (config) {
199      return $('<div>', {
200        class: 'dnxte_popup-content'
201      });
202    },
203
204
205
206    applyPopupPosition: function (container, content) {
207      // Check if it's full width layout
208      const isFullWidth = content.hasClass('dnxte_popup_pro_layout_full_width');
209
210      if (isFullWidth) {
211        // For full width, make content fill the entire viewport
212        container.css({
213          width: '100%',
214          height: '100%',
215          maxWidth: '100vw',
216          maxHeight: '100vh'
217        });
218        content.css({
219          width: '100%',
220          height: '100%'
221        });
222      } else {
223        // Extract position classes from content
224        const className = content.attr('class') || '';
225        const positionClasses = className.match(/dnxte_popup_pro_position_[\w-]+/);
226        if (positionClasses) {
227          const positionClass = positionClasses[0];
228          const position = positionClass.replace('dnxte_popup_pro_position_', '').replace(/-/g, '_');
229
230          // Apply position-specific styling to container
231          switch (position) {
232            case 'top_left':
233              container.parent().css({ justifyContent: 'flex-start', alignItems: 'flex-start' });
234              break;
235            case 'top_center':
236              container.parent().css({ justifyContent: 'center', alignItems: 'flex-start' });
237              break;
238            case 'top_right':
239              container.parent().css({ justifyContent: 'flex-end', alignItems: 'flex-start' });
240              break;
241            case 'center_left':
242              container.parent().css({ justifyContent: 'flex-start', alignItems: 'center' });
243              break;
244            case 'center_center':
245              container.parent().css({ justifyContent: 'center', alignItems: 'center' });
246              break;
247            case 'center_right':
248              container.parent().css({ justifyContent: 'flex-end', alignItems: 'center' });
249              break;
250            case 'bottom_left':
251              container.parent().css({ justifyContent: 'flex-start', alignItems: 'flex-end' });
252              content.css({ left: '', right: '', top: '', bottom: '', transform: '' });
253              break;
254            case 'bottom_center':
255              container.parent().css({ justifyContent: 'center', alignItems: 'flex-end' });
256              break;
257            case 'bottom_right':
258              container.parent().css({ justifyContent: 'flex-end', alignItems: 'flex-end' });
259              break;
260          }
261        }
262      }
263    },
264
265    // Initialize countdown timers in popup content
266    initializeCountdownTimers: function (popupContent) {
267      const self = this;
268
269      // Find all countdown timer elements in the popup
270      const countdownTimers = popupContent.find('.et_pb_countdown_timer');
271
272      if (countdownTimers.length > 0) {
273
274        countdownTimers.each(function () {
275          const $timer = $(this);
276
277          // Clear any existing intervals
278          const existingInterval = $timer.data('interval');
279          if (existingInterval) {
280            clearInterval(existingInterval);
281          }
282
283          // Try Divi's native countdown initialization first
284          if (typeof window.et_pb_countdown_timer_init === 'function') {
285            try {
286              window.et_pb_countdown_timer_init($timer);
287            } catch (e) {
288              self.fallbackCountdownInit($timer);
289            }
290          } else if (typeof window.et_countdown_timer === 'function') {
291            try {
292              // Use et_countdown_timer function
293              window.et_countdown_timer($timer);
294
295              // Set up interval for continuous updates
296              const interval = setInterval(function () {
297                window.et_countdown_timer($timer);
298              }, 1000);
299
300              $timer.data('interval', interval);
301            } catch (e) {
302              self.fallbackCountdownInit($timer);
303            }
304          } else {
305            // Use fallback initialization
306            self.fallbackCountdownInit($timer);
307          }
308        });
309      }
310    },
311
312    // Fallback countdown initialization
313    fallbackCountdownInit: function ($timer) {
314      const endTimestamp = $timer.data('end-timestamp');
315
316      if (!endTimestamp) {
317        return;
318      }
319
320
321      const updateCountdown = function () {
322        const now = Math.floor(Date.now() / 1000);
323        const timeLeft = endTimestamp - now;
324
325        if (timeLeft <= 0) {
326          // Countdown finished
327          $timer.find('.value').text('00');
328          return;
329        }
330
331        const days = Math.floor(timeLeft / (60 * 60 * 24));
332        const hours = Math.floor((timeLeft % (60 * 60 * 24)) / (60 * 60));
333        const minutes = Math.floor((timeLeft % (60 * 60)) / 60);
334        const seconds = timeLeft % 60;
335
336        // Update display
337        $timer.find('.days .value').text(String(days).padStart(3, '0'));
338        $timer.find('.hours .value').text(String(hours).padStart(2, '0'));
339        $timer.find('.minutes .value').text(String(minutes).padStart(2, '0'));
340        $timer.find('.seconds .value').text(String(seconds).padStart(2, '0'));
341      };
342
343      // Initial update
344      updateCountdown();
345
346      // Set up interval
347      const interval = setInterval(updateCountdown, 1000);
348      $timer.data('interval', interval);
349    },
350
351    bindEvents: function (wrapper, config) {
352      const self = this;
353      const bgOverlay = wrapper.data('bg-overlay');
354
355      // Close on overlay click - bind to both wrapper and background
356      if (config.closeOnBgClick) {
357        // Bind to wrapper container click
358        wrapper.on('click', function (e) {
359          if (e.target === this || $(e.target).hasClass('dnxte_popup_container')) {
360            self.close(wrapper);
361          }
362        });
363
364        // Bind to background overlay click
365        if (bgOverlay) {
366          bgOverlay.on('click', function (e) {
367            self.close(wrapper);
368          });
369        }
370      }
371      // Close button click — route to close() so the closing animation
372      // (class swap in beforeClose) plays for the full removalDelay before
373      // the popup is removed. Previously this handler set a 500ms inline
374      // animation but removed the element at 200ms, cutting the animation off.
375      const idMatch = config.closeMarkup ? config.closeMarkup.match(/id="popup_close_(\d+)"/) : null;
376      const id = idMatch ? idMatch[1] : null;
377      let closeButton = wrapper.find(`#popup_close_${id}`);
378
379      closeButton.on('click', function (e) {
380        e.preventDefault();
381        self.close(wrapper);
382      });
383
384      // Custom close trigger — any element inside the popup matching the
385      // user-provided selector closes the popup (works alongside the default
386      // close button). A bare value (no . or #) matches either an id or a class.
387      if (config.closeTriggerSelector) {
388        let closeSel = String(config.closeTriggerSelector).trim();
389        if (closeSel) {
390          if (!closeSel.startsWith('.') && !closeSel.startsWith('#')) {
391            closeSel = '#' + closeSel + ', .' + closeSel;
392          }
393          wrapper.on('click', closeSel, function (e) {
394            e.preventDefault();
395            self.close(wrapper);
396          });
397        }
398      }
399
400      // ESC key - close the most recently opened popup
401      $(document).on('keydown.dnxte-popup', function (e) {
402        if (e.keyCode === 27 && self.activePopups.length > 0) {
403          // Close the most recent popup
404          const mostRecentPopup = self.activePopups[self.activePopups.length - 1];
405          self.close(mostRecentPopup);
406        }
407      });
408
409
410
411      // Close popup by clicking back button ========
412      let allPopups = $(".dnxte_popup_wrap");
413      allPopups.each(function () {
414        let ppID = $(this).find('.dnxte_popup_content').data("popup-id");
415        const ppOptions = ppID?.data()?.popup;
416        const clickOnBack = ppOptions?.close_clicking_back_button;
417
418
419        if ('true' === clickOnBack) {
420          window.history.pushState(null, null, "");
421          window.history.pushState(null, null, "");
422          window.history.pushState(null, null, "");
423          $(window).on("popstate", function (e) {
424            e.preventDefault();
425
426            const ppWrapper = $(`.dnxte_popup_custom_${ppID}`);
427            const ppContent = ppWrapper.find('.dnxte_popup_content');
428            if (ppWrapper.length > 0) {
429              ppContent.css('animation', `500ms ease 0s 1 normal none running dnxte_popup_pro_${ppOptions?.closing_animation}`);
430              setTimeout(function () {
431                ppWrapper.remove();
432
433                // Remove from activePopups array
434                const index = self.activePopups.indexOf(ppWrapper);
435                if (index > -1) {
436                  self.activePopups.splice(index, 1);
437                }
438
439                // Update currentPopup reference
440                if (self.currentPopup === ppWrapper) {
441                  self.currentPopup = self.activePopups.length > 0 ? self.activePopups[self.activePopups.length - 1] : null;
442                }
443
444                // Only remove body class if no popups are active
445                if (self.activePopups.length === 0) {
446                  $('body').removeClass('dnxte-popup-open');
447                }
448              }, 500);
449            }
450
451          });
452        }
453      });
454
455    }
456  };
457
458  // Replace magnificPopup with our custom implementation
459  if (!window.jQuery.magnificPopup) {
460    $.magnificPopup = DnxteCustomPopup;
461  }
462
463  $(document).ready(function ($) {
464    if ("undefined" == typeof popup_frontend) return false;
465
466    var popup_json = JSON.parse(popup_frontend?.dnxte_popup_show);
467    popupIds = popup_json?.popup_ids;
468
469    // Helper function to set cookie when popup is triggered
470    function setPopupCookie(popupId) {
471      const popupShow = JSON.parse(popup_frontend.dnxte_popup_show);
472      const periodicity = popupShow[`popup_perodicity${popupId}`];
473
474      if (periodicity !== "every_time" && !document.cookie.includes(`dxnteHours${popupId}=`)) {
475        var expires = new Date();
476        expires.setTime(expires.getTime() + popupShow[`periodicity_cookie${popupId}`]);
477        document.cookie = `dxnteHours${popupId}=true; expires=` + expires.toUTCString() + "; path=/;";
478      }
479    }
480
481    // Helper function to check if popup should be shown based on cookie
482    function canShowPopup(popupId) {
483      const popupShow = JSON.parse(popup_frontend.dnxte_popup_show);
484      const periodicity = popupShow[`popup_perodicity${popupId}`];
485
486      if (periodicity === "every_time") {
487        return true;
488      }
489
490      return !document.cookie.includes(`dxnteHours${popupId}=`);
491    }
492
493    popupIds.forEach((id) => {
494      const data = JSON.parse(popup_json[`json_data${id}`]);
495      const content = $(`.dnxte_popup_content_${id}`);
496      const close_btn_tooltip = content.data("close-btn-tooltip") ? `data-tooltip="${escapeAttr(content.data("close-btn-tooltip"))}"` : '';
497      const tooltipClass = content.data("close-btn-tooltip") ? ` close-btn-tooltip` : '';
498
499      if ("true" === data?.remove_link) {
500        $(`a#popup_${id}`).removeAttr("href");
501      }
502
503
504      let preventScrolling =
505        "true" === data.prevent_page_scrolling ? `dnxte-prevent-scrolling` : "";
506
507      const clickable_under_overlay =
508        "false" === data.close_overlay_click
509          ? data.clickable_under_overlay
510          : false;
511
512      const popupConfig = {
513        items: {
514          src: `.dnxte_popup_content_${id}`,
515          // src: content,
516        },
517        callbacks: {
518          beforeClose: function () {
519            $(`.dnxte_popup_content.dnxte_popup_content_${id}`)
520              .removeClass(`dnxte_popup_pro_${data.opening_animation}`)
521              .addClass(`dnxte_popup_pro_${data.closing_animation}`);
522          },
523          afterClose: function () {
524            $(`.dnxte_popup_content.dnxte_popup_content_${id}`)
525              .removeClass(`dnxte_popup_pro_${data.closing_animation}`)
526              .addClass(`dnxte_popup_pro_${data.opening_animation}`);
527          },
528        },
529        type: "inline",
530        closeBtnInside: !JSON.parse(data?.close_btn_outside?.toLowerCase()),
531        closeOnBgClick: JSON.parse(data.close_overlay_click.toLowerCase()),
532        closeTriggerSelector: data.close_trigger_selector,
533        fixedContentPos: JSON.parse(data.prevent_page_scrolling.toLowerCase()),
534        mainClass: `dnxte_popup_custom ${clickable_under_overlay} ${preventScrolling} dnxte_popup_custom_${id}`, // mainClass: `dnxte_popup_custom ${data.clickable_under_overlay} dnxte_popup_custom_${id}`,
535        removalDelay: 1500,
536        midClick: true,
537        closeMarkup:
538          "false" === data.hide_close_button
539            ? `<button type="button" ${close_btn_tooltip} data-popup-id="${escapeAttr(id)}" id="popup_close_${escapeAttr(id)}" class="custom-tooltip dnxte_popup-close ${sanitizeClassToken(data.closing_css_selector)} dnxte_popup_pro_${sanitizeClassToken(data.close_btn_position)}_close ${tooltipClass}">&#215;</button>`
540            : ``,
541      };
542
543      if ("on" == data.dnxte_disable_desktop && 981 < $(window).width()) {
544        return;
545      }
546      if (
547        "on" == data.dnxte_disable_tablet &&
548        768 <= $(window).width() &&
549        981 >= $(window).width()
550      ) {
551        return;
552      }
553      if ("on" == data.dnxte_disable_phone && 768 > $(window).width()) {
554        return;
555      }
556
557      var dnxte_popup_show = JSON.parse(popup_frontend.dnxte_popup_show);
558
559      let startTime = 0;
560
561      if ("" !== data?.trigger_delay_start) {
562        startTime = +data?.trigger_delay_start * 1000;
563      }
564      const endTime = startTime + data?.trigger_delay_end * 1000;
565
566      // Check if popup should be shown based on periodicity - but don't set cookie yet
567      if ("trigger_on_none" !== data?.trigger_event) {
568        // Only check if popup should show, don't set cookie until popup is actually triggered
569        if (!dnxte_popup_show[`popup_show${id}`]) return false;
570
571        // Remove cookie if periodicity is "every_time"
572        if ("every_time" == dnxte_popup_show[`popup_perodicity${id}`]) {
573          if (document.cookie.includes(`dxnteHours${id}=`)) {
574            document.cookie = `dxnteHours${id}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
575          }
576        }
577      }
578      var trigger_event = JSON.parse(popup_frontend.trigger_event);
579
580      if ("trigger_on_none" === data.trigger_event) {
581        $(`#popup_${id}`).on("click", function (event) {
582          event.preventDefault();
583
584          if ($(`body > .dnxte_popup_content_${id}`).length === 0 && canShowPopup(id)) {
585            DnxteCustomPopup.open(popupConfig);
586            setPopupCookie(id);
587          }
588        });
589
590        /* custom css selector */
591        if ("" == dnxte_popup_show[`custom_selector${id}`]) return false;        
592        
593        let custom_selector = dnxte_popup_show[`custom_selector${id}`];
594        if (!custom_selector.startsWith('.')) {
595          custom_selector = '.' + custom_selector;
596        }
597
598        $(custom_selector).each(function () {
599          $(this).on("click", function (event) {            
600              event.preventDefault();
601              if (canShowPopup(id)) {
602                DnxteCustomPopup.open(popupConfig);
603                setPopupCookie(id);
604              }
605            },
606          );
607        });
608      } else if ("trigger_on_load" === data.trigger_event) {
609        // Check if popup can be shown based on periodicity before triggering
610        if (canShowPopup(id)) {
611          if (0 == startTime) {
612            DnxteCustomPopup.open(popupConfig);
613            setPopupCookie(id);
614          } else {
615            const popupOpenTimer = setTimeout(() => {
616              DnxteCustomPopup.open(popupConfig);
617              setPopupCookie(id);
618              clearTimeout(popupOpenTimer);
619            }, startTime);
620          }
621          if (startTime < endTime) {
622            const popupCloseTimer = setTimeout(() => {
623              DnxteCustomPopup.close();
624              clearTimeout(popupCloseTimer);
625            }, endTime);
626          }
627        }
628      } else if ("trigger_on_scroll" === data.trigger_event) {
629        let scroll_event = trigger_event[id];
630        var recentScroll = false;
631        $(window).on("scroll", function () {
632          // Only proceed if popup can be shown based on periodicity
633          if (!recentScroll && canShowPopup(id)) {
634            const scrollPosition = window.scrollY + window.innerHeight;
635            const totalHeight = document.documentElement.scrollHeight;
636            let isPopupShown = false;
637
638            if (scroll_event.onscroll_offset_units != "px") {
639              const scrollPercentage = (scrollPosition / totalHeight) * 100;
640              if (scrollPercentage >= scroll_event.onscroll_offset) {
641                isPopupShown = true;
642                recentScroll = true;
643              }
644            } else if (scroll_event.onscroll_offset_units == "px") {
645              if (window.scrollY >= scroll_event.onscroll_offset) {
646                isPopupShown = true;
647                recentScroll = true;
648              }
649            }
650
651            if (isPopupShown) {
652              DnxteCustomPopup.open(popupConfig);
653              setPopupCookie(id);
654            }
655          }
656        });
657      } else if ("trigger_on_exit" === data.trigger_event) {
658        let exitPopupShown = false;
659        let exitIntentCooldown = false;
660
661        document.addEventListener("mousemove", function exitIntentHandler(e) {
662          // Check if popup can be shown based on periodicity
663          if (!canShowPopup(id)) return;
664
665          // For "every_time" periodicity, allow multiple triggers but with cooldown
666          const popupShow = JSON.parse(popup_frontend.dnxte_popup_show);
667          const periodicity = popupShow[`popup_perodicity${id}`];
668
669          // If not "every_time", only show once per session
670          if (periodicity !== "every_time" && exitPopupShown) return;
671
672          // Prevent rapid firing with cooldown
673          if (exitIntentCooldown) return;
674
675          // Get current scroll position
676          var scroll = window.pageYOffset || document.documentElement.scrollTop;
677
678          // Trigger on exit intent (mouse moving to top of viewport)
679          if (e.pageY - scroll < 7) {
680            // Check if this specific exit popup is already showing
681            const isThisPopupActive = $(`.dnxte_popup_custom_${id}`).length > 0;
682
683            if (!isThisPopupActive) {
684              DnxteCustomPopup.open(popupConfig);
685              setPopupCookie(id);
686              exitPopupShown = true;
687              exitIntentCooldown = true;
688
689              // Set cooldown to prevent rapid firing (3 seconds)
690              setTimeout(() => {
691                exitIntentCooldown = false;
692                // Reset exitPopupShown for "every_time" periodicity
693                if (periodicity === "every_time") {
694                  exitPopupShown = false;
695                }
696              }, 3000);
697
698              // Only remove event listener for non-"every_time" periodicity
699              if (periodicity !== "every_time") {
700                document.removeEventListener("mousemove", exitIntentHandler);
701              }
702            }
703          }
704        });
705      } else if ("trigger_on_inactivity" === data.trigger_event) {
706        let inactivity_event = trigger_event[id];
707
708        // Only set up inactivity timeout if popup can be shown
709        if (canShowPopup(id)) {
710          setIdleTimeout(
711            inactivity_event.oninactivity_delays * 1000,
712            function () {
713              if (canShowPopup(id)) { // Double check before showing
714                DnxteCustomPopup.open(popupConfig);
715                setPopupCookie(id);
716              }
717            },
718            function () {
719              return false;
720            },
721          );
722        }
723
724        function setIdleTimeout(millis, onIdle, onUnidle) {
725          var timeout = 0;
726          startTimer();
727
728          function startTimer() {
729            timeout = setTimeout(onExpires, millis);
730            document.addEventListener("mousemove", onActivity);
731            document.addEventListener("keydown", onActivity);
732            document.addEventListener("touchstart", onActivity);
733          }
734
735          function onExpires() {
736            timeout = 0;
737            onIdle();
738          }
739
740          function onActivity() {
741            if (timeout) clearTimeout(timeout);
742            else onUnidle();
743            //since the mouse is moving, we turn off our event hooks for 1 second
744            document.removeEventListener("mousemove", onActivity);
745            document.removeEventListener("keydown", onActivity);
746            document.removeEventListener("touchstart", onActivity);
747            setTimeout(startTimer, 1000);
748          }
749        }
750      }
751      return false;
752    });
753  });
754
755  $.initModules = function () {
756    const element = document.querySelector(".et_pb_section");
757    if (element) {
758      const parent = element.parentElement;
759      if (parent) {
760        element.style.width = `${parent.offsetWidth}px`;
761      }
762    }
763    if (typeof et_pb_init_global_functions === "function") {
764      et_pb_init_global_functions();
765    }
766    if (typeof et_pb_init_modules === "function") {
767      et_pb_init_modules();
768    }
769  };
770})(jQuery);

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.