PageSourceSearch

https://onlineclassfix.com/wp-content/themes/portfolioxpress/assets/js/script.js?ver=1.0.0

js onlineclassfix.com collected 2026-10-08 00:19:46 UTC 35,141 bytes, 1,166 lines download raw bytes

1"use strict";
2
3var portfolioxpress = portfolioxpress || {};
4
5//  Nodelist forEach polyfill
6if (window.NodeList && !NodeList.prototype.forEach) {
7  NodeList.prototype.forEach = function (callback, thisArg) {
8    thisArg = thisArg || window;
9    for (let i = 0; i < this.length; i++) {
10      callback.call(thisArg, this[i], i, this);
11    }
12  };
13}
14
15/* Handle Accessiblity for Menu Items
16 **-----------------------------------------------------*/
17portfolioxpress.traverseMenu = {
18  init: function () {
19    let topNavigation = document.querySelector(".portfolioxpress-top-nav");
20    let primaryNavigation = document.getElementById("site-navigation");
21
22    // For top menu navigation
23    if (topNavigation) {
24      this.traverse(topNavigation);
25    }
26    // For primary menu navigation
27    if (primaryNavigation) {
28      this.traverse(primaryNavigation);
29    }
30  },
31
32  traverse: function (navigation) {
33    let menu = navigation.getElementsByTagName("ul")[0];
34    if ("undefined" !== typeof menu) {
35      if (!menu.classList.contains("nav-menu")) {
36        menu.classList.add("nav-menu");
37      }
38      // Get all the link elements within the menu.
39      let links = menu.getElementsByTagName("a");
40
41      // Get all the link elements with children within the menu.
42      let linksWithChildren = menu.querySelectorAll(
43        ".menu-item-has-children > a, .page_item_has_children > a"
44      );
45
46      // Toggle focus each time a menu link is focused or blurred.
47      for (let link of links) {
48        link.addEventListener("focus", this.toggleFocus, true);
49        link.addEventListener("blur", this.toggleFocus, true);
50      }
51
52      // Toggle focus each time a menu link with children receive a touch event.
53      for (let link of linksWithChildren) {
54        link.addEventListener("touchstart", this.toggleFocus, false);
55      }
56    }
57  },
58
59  toggleFocus: function (event) {
60    if (event.type === "focus" || event.type === "blur") {
61      let self = this;
62      // Move up through the ancestors of the current link until we hit .nav-menu.
63      while (!self.classList.contains("nav-menu")) {
64        // On li elements toggle the class .focus.
65        if ("li" === self.tagName.toLowerCase()) {
66          self.classList.toggle("focus");
67        }
68        self = self.parentNode;
69      }
70    }
71
72    if (event.type === "touchstart") {
73      let menuItem = this.parentNode;
74      event.preventDefault();
75      for (let link of menuItem.parentNode.children) {
76        if (menuItem !== link) {
77          link.classList.remove("focus");
78        }
79      }
80      menuItem.classList.toggle("focus");
81    }
82  },
83};
84
85/* Handle Focus for Dialog Accessiblity
86 **-----------------------------------------------------*/
87portfolioxpress.handleFocus = {
88  init: function () {
89    this.keepFocusInModal();
90  },
91
92  keepFocusInModal: function () {
93    let searchModal = document.querySelector(".theme-search-panel");
94    let canvasMenuModal = document.querySelector(".theme-offcanvas-panel-menu");
95    let canvasWidgetModal = document.querySelector(
96      ".theme-offcanvas-panel-widget"
97    );
98
99    document.addEventListener("keydown", function (event) {
100      // Check for if tab key is pressed
101      let KEYCODE_TAB = 9;
102      let isTabPressed = event.key === "Tab" || event.keyCode === KEYCODE_TAB;
103      if (!isTabPressed) {
104        return;
105      }
106
107      let modal;
108
109      if (document.body.classList.contains("portfolioxpress-search-canvas-open")) {
110        modal = searchModal;
111      }
112
113      if (document.body.classList.contains("portfolioxpress-offcanvas-menu-open")) {
114        modal = canvasMenuModal;
115      }
116
117      if (document.body.classList.contains("portfolioxpress-offcanvas-widget-open")) {
118        modal = canvasWidgetModal;
119      }
120
121      if (modal) {
122        let focusableEls = modal.querySelectorAll(
123          'a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="search"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
124        );
125
126        let firstFocusableEl = focusableEls[0];
127        let lastFocusableEl = focusableEls[focusableEls.length - 1];
128
129        // if shift key pressed for shift + tab combination
130        if (event.shiftKey) {
131          if (document.activeElement === firstFocusableEl) {
132            lastFocusableEl.focus(); // add focus for the last focusable element
133            event.preventDefault();
134          }
135        } else {
136          // if tab key is pressed
137          if (document.activeElement === lastFocusableEl) {
138            // if focused has reached to last focusable element then focus first focusable element after pressing tab
139            firstFocusableEl.focus(); // add focus for the first focusable element
140            event.preventDefault();
141          }
142        }
143      }
144    });
145  },
146};
147
148/* Preloader
149 **-----------------------------------------------------*/
150portfolioxpress.fadeOutPreloader = {
151  init: function () {
152    let preloader = document.querySelector("#theme-preloader-initialize");
153
154    if (preloader) {
155      let fadeOut = setInterval(function () {
156        preloader.style.transition = "0.2s";
157
158        if (!preloader.style.opacity) {
159          preloader.style.opacity = 1;
160        }
161        if (preloader.style.opacity > 0) {
162          preloader.style.opacity -= 0.2;
163        } else {
164          preloader.style.display = "none";
165          clearInterval(fadeOut);
166        }
167      }, 100);
168    }
169  },
170};
171
172/* Scroll to top
173 **-----------------------------------------------------*/
174portfolioxpress.scrollToTop = {
175  init: function () {
176    let scrollToTopBtn = document.getElementById("theme-scroll-to-start");
177    let rootElement = document.documentElement;
178
179    if (scrollToTopBtn) {
180      // Scroll to top on click
181      this.goToTop(scrollToTopBtn, rootElement);
182
183      // Render scroll to top button
184      this.scrollToTopPosition(scrollToTopBtn, rootElement);
185    }
186  },
187
188  goToTop: function (scrollToTopBtn, rootElement) {
189    scrollToTopBtn.addEventListener("click", function (elem) {
190      elem.preventDefault();
191      rootElement.scrollTo({
192        top: 0,
193        behavior: "smooth",
194      });
195    });
196  },
197
198  scrollToTopPosition: function (scrollToTopBtn, rootElement) {
199    window.addEventListener("scroll", function (event) {
200      let scrollTotal = rootElement.scrollHeight - rootElement.clientHeight;
201      // Show on certain window height
202      if (rootElement.scrollTop / scrollTotal > 0.99) {
203        scrollToTopBtn.classList.add("visible");
204      } else {
205        scrollToTopBtn.classList.remove("visible");
206      }
207    });
208  },
209};
210
211/* Display Clock
212 **-----------------------------------------------------*/
213portfolioxpress.currentClock = {
214  init: function () {
215    if (document.getElementsByClassName("theme-display-clock").length > 0) {
216      setInterval(function () {
217        let currentTime = new Date();
218        let hours = currentTime.getHours();
219        let minutes = currentTime.getMinutes();
220        let seconds = currentTime.getSeconds();
221        let ampm = hours >= 12 ? "PM" : "AM";
222        hours = hours % 12;
223        hours = hours ? hours : 12;
224        minutes = minutes < 10 ? "0" + minutes : minutes;
225        seconds = seconds < 10 ? "0" + seconds : seconds;
226        let timeString =
227          '<span class="theme-clock-unit clock-unit-hours">' +
228          hours +
229          "</span>" +
230          ":" +
231          '<span class="theme-clock-unit clock-unit-minute">' +
232          minutes +
233          "</span>" +
234          ":" +
235          '<span class="theme-clock-unit clock-unit-seconds">' +
236          seconds +
237          "</span>" +
238          " " +
239          '<span class="theme-clock-unit clock-unit-format">' +
240          ampm +
241          "</span>";
242        document.getElementsByClassName("theme-display-clock")[0].innerHTML =
243          timeString;
244      }, 1000);
245    }
246  },
247};
248/* Off Canvas
249 **-----------------------------------------------------*/
250portfolioxpress.offCanvasModal = {
251  init: function () {
252    let offCanvasBtn = document.getElementById("theme-toggle-offcanvas-button");
253    let closeOffCanvas = document.getElementById("theme-offcanvas-close");
254    let overlayDiv = document.querySelector("#page.site");
255
256    if (offCanvasBtn) {
257      let focusElement = document.querySelector("#theme-offcanvas-close");
258
259      // Handle canvas modal when opened
260      this.onOpen(offCanvasBtn, focusElement);
261
262      // Handle canvas modal when closed
263      this.onClose(offCanvasBtn, closeOffCanvas, focusElement);
264
265      // When open, close if visitor clicks on the wrapping element of the modal.
266      this.outsideModal(offCanvasBtn, overlayDiv, focusElement);
267
268      // Close on escape key press.
269      this.closeOnEscape(offCanvasBtn, focusElement);
270    }
271  },
272
273  onOpen: function (offCanvasBtn, focusElement) {
274    offCanvasBtn.addEventListener("click", function (event) {
275      event.preventDefault();
276      document.body.classList.add("portfolioxpress-offcanvas-menu-open");
277
278      offCanvasBtn.setAttribute("aria-expanded", true);
279
280      // Add focus after a timeout to take effect on hidden element to make the "all" transition work
281      setTimeout(function () {
282        focusElement.focus();
283      }, 500);
284    });
285  },
286
287  onClose: function (offCanvasBtn, closeOffCanvas, focusElement) {
288    closeOffCanvas.addEventListener("click", function (event) {
289      event.preventDefault();
290      document.body.classList.remove("portfolioxpress-offcanvas-menu-open");
291      offCanvasBtn.setAttribute("aria-expanded", false);
292      focusElement.blur();
293      offCanvasBtn.focus();
294    });
295  },
296
297  outsideModal: function (offCanvasBtn, overlayDiv, focusElement) {
298    document.addEventListener("click", function (event) {
299      if (document.body.classList.contains("portfolioxpress-offcanvas-menu-open")) {
300        if (event.target == overlayDiv) {
301          document.body.classList.remove("portfolioxpress-offcanvas-menu-open");
302          offCanvasBtn.setAttribute("aria-expanded", false);
303          focusElement.blur();
304          offCanvasBtn.focus();
305          console.log(offCanvasBtn);
306        }
307      }
308    });
309  },
310
311  closeOnEscape: function (offCanvasBtn, focusElement) {
312    document.addEventListener("keydown", function (event) {
313      if (document.body.classList.contains("portfolioxpress-offcanvas-menu-open")) {
314        if (event.key === "Escape") {
315          event.preventDefault();
316          document.body.classList.remove("portfolioxpress-offcanvas-menu-open");
317          offCanvasBtn.setAttribute("aria-expanded", false);
318          focusElement.blur();
319          offCanvasBtn.focus();
320        }
321      }
322    });
323  },
324};
325
326/* Off Canvas Dropdown menu item
327 **-----------------------------------------------------*/
328portfolioxpress.offCanvasDropdown = {
329  init: function () {
330    let subMenuToggles = document.querySelectorAll(".sub-menu-toggle");
331    subMenuToggles.forEach(function (toggle) {
332      toggle.addEventListener("click", function () {
333        let duration = toggle.getAttribute("data-toggle-duration") || 350;
334        toggle.classList.toggle("active");
335        toggle.setAttribute(
336          "aria-expanded",
337          toggle.getAttribute("aria-expanded") === "true" ? "false" : "true"
338        );
339        let currentClass = toggle.getAttribute("data-toggle-target");
340        let currentTarget = document.querySelector(currentClass);
341        currentTarget.classList.toggle("active");
342        currentTarget.style.transition = `height ${duration}ms ease`;
343        if (currentTarget.style.display === "block") {
344          currentTarget.style.height = currentTarget.offsetHeight + "px";
345          setTimeout(() => {
346            currentTarget.style.height = "0";
347          }, 20);
348          setTimeout(() => {
349            currentTarget.style.display = "none";
350          }, duration);
351        } else {
352          currentTarget.style.display = "block";
353          currentTarget.style.height = "0";
354          setTimeout(() => {
355            currentTarget.style.height = "auto";
356          }, duration);
357        }
358      });
359    });
360  },
361};
362
363/* Off-Canvas Widget
364 **-----------------------------------------------------*/
365portfolioxpress.offCanvasWidget = {
366  init: function () {
367    let offCanvasWidgetBtn = document.getElementById(
368      "theme-offcanvas-widget-button"
369    );
370    let closeOffCanvasWidget = document.getElementById(
371      "theme-offcanvas-widget-close"
372    );
373    let overlayWidgetDiv = document.querySelector("#page.site");
374
375    if (offCanvasWidgetBtn) {
376      let focusElement = document.querySelector(
377        "#theme-offcanvas-widget-close"
378      );
379
380      // Handle canvas widget when opened
381      this.onOpen(offCanvasWidgetBtn, focusElement);
382
383      // Handle canvas widget when closed
384      this.onClose(offCanvasWidgetBtn, closeOffCanvasWidget, focusElement);
385
386      // When open, close if visitor clicks on the wrapping element of the widget.
387      this.outsideWidget(offCanvasWidgetBtn, overlayWidgetDiv, focusElement);
388
389      // Close on escape key press.
390      this.closeOnEscape(offCanvasWidgetBtn, focusElement);
391    }
392  },
393
394  onOpen: function (offCanvasWidgetBtn, focusElement) {
395    offCanvasWidgetBtn.addEventListener("click", function (event) {
396      event.preventDefault();
397      document.body.classList.add("portfolioxpress-offcanvas-widget-open");
398
399      offCanvasWidgetBtn.setAttribute("aria-expanded", true);
400
401      // Add focus after a timeout to take effect on hidden element to make the "all" transition work
402      setTimeout(function () {
403        focusElement.focus();
404      }, 500);
405    });
406  },
407
408  onClose: function (offCanvasWidgetBtn, closeOffCanvasWidget, focusElement) {
409    closeOffCanvasWidget.addEventListener("click", function (event) {
410      event.preventDefault();
411      document.body.classList.remove("portfolioxpress-offcanvas-widget-open");
412      offCanvasWidgetBtn.setAttribute("aria-expanded", false);
413      focusElement.blur();
414      offCanvasWidgetBtn.focus();
415    });
416  },
417
418  outsideWidget: function (offCanvasWidgetBtn, overlayWidgetDiv, focusElement) {
419    document.addEventListener("click", function (event) {
420      if (document.body.classList.contains("portfolioxpress-offcanvas-widget-open")) {
421        if (event.target == overlayWidgetDiv) {
422          document.body.classList.remove("portfolioxpress-offcanvas-widget-open");
423          offCanvasWidgetBtn.setAttribute("aria-expanded", false);
424          focusElement.blur();
425          offCanvasWidgetBtn.focus();
426        }
427      }
428    });
429  },
430
431  closeOnEscape: function (offCanvasWidgetBtn, focusElement) {
432    document.addEventListener("keydown", function (event) {
433      if (document.body.classList.contains("portfolioxpress-offcanvas-widget-open")) {
434        if (event.key === "Escape") {
435          event.preventDefault();
436          document.body.classList.remove("portfolioxpress-offcanvas-widget-open");
437          offCanvasWidgetBtn.setAttribute("aria-expanded", false);
438          focusElement.blur();
439          offCanvasWidgetBtn.focus();
440        }
441      }
442    });
443  },
444};
445
446/* Search Canvas
447 **-----------------------------------------------------*/
448portfolioxpress.searchCanvasModal = {
449  init: function () {
450    let searchCanvasBtn = document.getElementById("theme-toggle-search-button");
451    let closeSearchCanvas = document.getElementById(
452      "portfolioxpress-search-canvas-close"
453    );
454    let overlayDiv = document.querySelector("#page.site");
455
456    if (searchCanvasBtn) {
457      let focusElement = document.querySelector(
458        ".theme-search-panel .search-field"
459      );
460
461      // Handle cover modals when they're opened
462      this.onOpen(searchCanvasBtn, focusElement);
463
464      // Handle cover modals when they're closed
465      this.onClose(searchCanvasBtn, closeSearchCanvas, focusElement);
466
467      // When open, close if visitor clicks on the outside the modal.
468      this.outsideModal(searchCanvasBtn, overlayDiv, focusElement);
469
470      // Close on escape key press.
471      this.closeOnEscape(searchCanvasBtn, focusElement);
472    }
473  },
474
475  onOpen: function (searchCanvasBtn, focusElement) {
476    searchCanvasBtn.addEventListener("click", function (event) {
477      event.preventDefault();
478      document.body.classList.add("portfolioxpress-search-canvas-open");
479
480      searchCanvasBtn.setAttribute("aria-expanded", true);
481
482      // Add focus after a timeout to take effect on hidden element to make the "all" transition work
483      setTimeout(function () {
484        focusElement.focus();
485      }, 500);
486    });
487  },
488
489  onClose: function (searchCanvasBtn, closeSearchCanvas, focusElement) {
490    closeSearchCanvas.addEventListener("click", function (event) {
491      event.preventDefault();
492      document.body.classList.remove("portfolioxpress-search-canvas-open");
493      searchCanvasBtn.setAttribute("aria-expanded", false);
494      focusElement.blur();
495      searchCanvasBtn.focus();
496    });
497  },
498
499  outsideModal: function (searchCanvasBtn, overlayDiv, focusElement) {
500    document.addEventListener("click", function (event) {
501      if (document.body.classList.contains("portfolioxpress-search-canvas-open")) {
502        if (event.target == overlayDiv) {
503          document.body.classList.remove("portfolioxpress-search-canvas-open");
504          searchCanvasBtn.setAttribute("aria-expanded", false);
505          focusElement.blur();
506          searchCanvasBtn.focus();
507        }
508      }
509    });
510  },
511
512  closeOnEscape: function (searchCanvasBtn, focusElement) {
513    document.addEventListener("keydown", function (event) {
514      if (document.body.classList.contains("portfolioxpress-search-canvas-open")) {
515        if (event.key === "Escape") {
516          event.preventDefault();
517          document.body.classList.remove("portfolioxpress-search-canvas-open");
518          searchCanvasBtn.setAttribute("aria-expanded", false);
519          focusElement.blur();
520          searchCanvasBtn.focus();
521        }
522      }
523    });
524  },
525};
526
527/* Background Image
528 **-----------------------------------------------------*/
529portfolioxpress.setBackgroundImage = {
530  init: function () {
531    let bgImageContainer = document.querySelectorAll(".portfolioxpress-bg-image");
532    if (bgImageContainer) {
533      bgImageContainer.forEach(function (item) {
534        let image = item.querySelector("img");
535        if (image) {
536          let imageSrc = image.getAttribute("src");
537          if (imageSrc) {
538            item.style.backgroundImage = "url(" + imageSrc + ")";
539            if (item.classList.contains("portfolioxpress-header-image")) {
540              image.style.visibility = "hidden";
541            } else {
542              image.style.display = "none";
543            }
544          }
545        }
546      });
547    }
548
549    let pageSections = document.querySelectorAll(".data-bg");
550    pageSections.forEach(function (section) {
551      let background = section.getAttribute("data-background");
552      if (background) {
553        section.style.backgroundImage = "url(" + background + ")";
554      }
555    });
556  },
557};
558
559/* Progress Bar
560 **-----------------------------------------------------*/
561portfolioxpress.progressBar = {
562  init: function () {
563    let progressBarDiv = document.getElementById("portfolioxpress-progress-bar");
564
565    if (progressBarDiv) {
566      let body = document.body;
567      let rootElement = document.documentElement;
568
569      window.addEventListener("scroll", function (event) {
570        let winScroll = body.scrollTop || rootElement.scrollTop;
571        let height = rootElement.scrollHeight - rootElement.clientHeight;
572        let scrolled = (winScroll / height) * 100;
573        progressBarDiv.style.width = scrolled + "%";
574      });
575    }
576  },
577};
578
579/* Tab Widget
580 **-----------------------------------------------------*/
581portfolioxpress.tabWidget = {
582  init: function () {
583    const widgetContainers = document.querySelectorAll(".theme-widget-tab");
584    widgetContainers.forEach((container) => {
585      const tabs = container.querySelectorAll(
586        ".tab-header-list .widget-tab-presentation"
587      );
588      const tabPanes = container.querySelectorAll(
589        ".widget-tab-content .tab-content-panel"
590      );
591
592      tabs.forEach((tab) => {
593        tab.addEventListener("click", function (event) {
594          const tabid = this.getAttribute("tab-data");
595          tabs.forEach((tab) => tab.classList.remove("active"));
596          tabPanes.forEach((tabPane) => tabPane.classList.remove("active"));
597          this.classList.add("active");
598          container.querySelector(`.content-${tabid}`).classList.add("active");
599        });
600      });
601    });
602  },
603};
604
605/* Swiper slides
606 **-----------------------------------------------------*/
607
608var swiper = new Swiper(".banner-slider-pagnav", {
609  spaceBetween: 10,
610  autoplay: {
611    delay: 8000,
612    disableOnInteraction: false
613  },
614  freeMode: true,
615  slidesPerView: 2,
616  speed: 900,
617  loop: true,
618  watchSlidesProgress: true,
619  breakpoints: {
620    576: {
621      slidesPerView: 2,
622    },
623    1199: {
624      slidesPerView: 3,
625      centeredSlides: true,
626      centeredSlidesBounds: true
627    }
628  }
629});
630var swiper2 = new Swiper(".theme-banner-slider", {
631  spaceBetween: 10,
632  autoplay: {
633    delay: 8000,
634    disableOnInteraction: false
635  },
636  speed: 900,
637  navigation: {
638    nextEl: ".swiper-button-next",
639    prevEl: ".swiper-button-prev",
640  },
641  thumbs: {
642    swiper: swiper,
643  },
644  pagination: {
645    el: ".swiper-pagination",
646  },
647});
648
649var swiper = new Swiper(".testimonial-content-container", {
650  effect: "coverflow",
651  centeredSlides: true,
652  autoplay: {
653    delay: 6000,
654    disableOnInteraction: false
655  },
656  speed: 700,
657  loop: true,
658  slidesPerView: "1",
659  coverflowEffect: {
660    rotate: 0,
661    stretch: 30,
662    depth: 100,
663    modifier: 5,
664    slideShadows: true
665  },
666  breakpoints: {
667    1025: {
668      slidesPerView: "2",
669    }
670  },
671  pagination: {
672    el: ".swiper-pagination",
673    clickable: true
674  },
675});
676
677var swiper = new Swiper(".service-image-group", {
678  slidesPerView: 1,
679  spaceBetween: 16,
680  loop: true,
681  speed: 700,
682  autoplay: {
683    delay: 2800,
684    disableOnInteraction: false,
685  },
686  breakpoints: {
687    576: {
688      slidesPerView: 2,
689    },
690    1199: {
691      slidesPerView: 3,
692    },
693    1400: {
694      slidesPerView: 4,
695    }
696  }
697});
698
699var swiper = new Swiper(".portfolioxpress-popular-container", {
700  effect: "coverflow",
701  grabCursor: true,
702  centeredSlides: true,
703  slidesPerView: 1,
704  coverflowEffect: {
705    rotate: 0,
706    stretch: 0,
707    depth: 100,
708    modifier: 2,
709    slideShadows: true
710  },
711  spaceBetween: 50,
712  loop: true,
713  pagination: {
714    el: ".swiper-pagination",
715    clickable: true
716  },
717  breakpoints: {
718    768: {
719      slidesPerView: 2,
720    },
721    991: {
722      slidesPerView: 3,
723    },
724    1400: {
725      slidesPerView: 4,
726    }
727  },
728});
729
730var swiper = new Swiper(".companies-worked-with", {
731  slidesPerView: 1,
732  spaceBetween: 16,
733  loop: true,
734  speed: 900,
735  autoplay: {
736    delay: 6000,
737    disableOnInteraction: false,
738  },
739  breakpoints: {
740    576: {
741      slidesPerView: 2,
742    },
743    1199: {
744      slidesPerView: 3,
745    }
746  }
747});
748
749/* Popup ModelBox
750 **-----------------------------------------------------*/
751portfolioxpress.lightBox = {
752  init: function () {
753    let light = document.querySelector("[data-glightbox]");
754    if (light) {
755      GLightbox({
756        selector: "[data-glightbox]",
757        openEffect: "zoom",
758        closeEffect: "fade",
759      });
760    }
761  },
762};
763
764/* Sticky Footer
765 **-----------------------------------------------------*/
766portfolioxpress.stickyFooter = {
767  init: function () {
768    const footer = document.querySelector('[data-sticky-footer=true]');
769    const spacer = document.querySelector('.sticky-footer-spacer');
770
771    if (!footer || !spacer) return;
772
773    const footerHeight = footer.offsetHeight;
774
775    function updateSpacerHeight() {
776      spacer.style.height = `${footerHeight}px`;
777    }
778
779    window.addEventListener('load', updateSpacerHeight);
780    window.addEventListener('resize', updateSpacerHeight);
781
782    function handleScroll() {
783      const scrollPosition = window.pageYOffset;
784      const spacerTopOffset = spacer.offsetTop;
785      const shouldStickyFooter = scrollPosition >= spacerTopOffset - window.innerHeight;
786      footer.classList.toggle('has-footer-stuck', shouldStickyFooter);
787    }
788
789    document.body.classList.add('has-sticky-footer');
790    window.addEventListener('scroll', handleScroll);
791  },
792};
793
794/* simple Tooltip
795 **-----------------------------------------------------*/
796portfolioxpress.simpleTooltip = {
797  init: function () {
798    const buttons = document.querySelectorAll(".theme-button-tooltip");
799
800    buttons.forEach((button) => {
801      const tooltipText = button.querySelector(
802        ".screen-reader-text"
803      ).textContent;
804
805      button.addEventListener("mouseover", () => {
806        const tooltipElement = document.createElement("span");
807        tooltipElement.classList.add("tooltiptext");
808        tooltipElement.innerText = tooltipText;
809        button.appendChild(tooltipElement);
810        button.classList.add("tooltip");
811      });
812
813      button.addEventListener("mouseout", () => {
814        const tooltipElement = button.querySelector(".tooltiptext");
815        button.removeChild(tooltipElement);
816        button.classList.remove("tooltip");
817      });
818    });
819  },
820};
821
822/* Welcome Screen Advertisement
823 **-----------------------------------------------------*/
824
825portfolioxpress.fullpageAdvertisement = {
826  init() {
827    const adCountDown = document.querySelector(".welcome-screen-countdown");
828    const headerAds = document.querySelector(".welcome-screen-banner");
829    const adBtn = document.querySelector(".welcome-screen-skip");
830
831    if (!headerAds) return;
832
833    let counter = 6;
834    let startCount = null;
835
836    function startScroll() {
837      headerAds.classList.add("welcome-screen-vanished");
838    }
839
840    adBtn.addEventListener("click", () => {
841      clearInterval(startCount);
842      startScroll();
843    });
844
845    startCount = setInterval(() => {
846      counter--;
847      adCountDown.textContent = `${counter}sec`;
848
849      if (counter === 0) {
850        clearInterval(startCount);
851        startScroll();
852      }
853    }, 1000);
854  },
855};
856
857/* Custom Cursor
858 **-----------------------------------------------------*/
859let cursorObj;
860portfolioxpress.customCursor = {
861  init: function () {
862    cursorObj = this;
863    this.customCursor();
864  },
865  isVariableDefined: function (el) {
866    return typeof !!el && el != "undefined" && el != null;
867  },
868  select: function (selectors) {
869    return document.querySelector(selectors);
870  },
871  selectAll: function (selectors) {
872    return document.querySelectorAll(selectors);
873  },
874  customCursor: function () {
875    let c = cursorObj.select(".cursor-dot");
876    if (cursorObj.isVariableDefined(c)) {
877      let cursor = {
878        delay: 8,
879        _x: 0,
880        _y: 0,
881        endX: window.innerWidth / 2,
882        endY: window.innerHeight / 2,
883        cursorVisible: true,
884        cursorEnlarged: false,
885        $dot: cursorObj.select(".cursor-dot"),
886        $outline: cursorObj.select(".cursor-dot-outline"),
887
888        init: function () {
889          // Set up element sizes
890          this.dotSize = this.$dot.offsetWidth;
891          this.outlineSize = this.$outline.offsetWidth;
892
893          this.setupEventListeners();
894          this.animateDotOutline();
895        },
896
897        updateCursor: function (e) {
898          let self = this;
899
900          // Show the cursor
901          self.cursorVisible = true;
902          self.toggleCursorVisibility();
903
904          // Position the dot
905          self.endX = e.clientX;
906          self.endY = e.clientY;
907          self.$dot.style.top = self.endY + "px";
908          self.$dot.style.left = self.endX + "px";
909        },
910
911        setupEventListeners: function () {
912          let self = this;
913
914          // Reposition cursor on window load
915          window.addEventListener("load", (event) => {
916            self.cursorEnlarged = false;
917            self.toggleCursorSize();
918          });
919
920          // Anchor hovering
921          cursorObj.selectAll("a, button").forEach(function (el) {
922            el.addEventListener("mouseover", function () {
923              self.cursorEnlarged = true;
924              self.toggleCursorSize();
925            });
926            el.addEventListener("mouseout", function () {
927              self.cursorEnlarged = false;
928              self.toggleCursorSize();
929            });
930          });
931
932          // Click events
933          document.addEventListener("mousedown", function () {
934            self.cursorEnlarged = true;
935            self.toggleCursorSize();
936          });
937          document.addEventListener("mouseup", function () {
938            self.cursorEnlarged = false;
939            self.toggleCursorSize();
940          });
941
942          document.addEventListener("mousemove", function (e) {
943            // Show the cursor
944            self.cursorVisible = true;
945            self.toggleCursorVisibility();
946
947            // Position the dot
948            self.endX = e.clientX;
949            self.endY = e.clientY;
950            self.$dot.style.top = self.endY + "px";
951            self.$dot.style.left = self.endX + "px";
952          });
953
954          // Hide/show cursor
955          document.addEventListener("mouseenter", function (e) {
956            self.cursorVisible = true;
957            self.toggleCursorVisibility();
958            self.$dot.style.opacity = 1;
959            self.$outline.style.opacity = 1;
960          });
961
962          document.addEventListener("mouseleave", function (e) {
963            self.cursorVisible = true;
964            self.toggleCursorVisibility();
965            self.$dot.style.opacity = 0;
966            self.$outline.style.opacity = 0;
967          });
968        },
969
970        animateDotOutline: function () {
971          let self = this;
972
973          self._x += (self.endX - self._x) / self.delay;
974          self._y += (self.endY - self._y) / self.delay;
975          self.$outline.style.top = self._y + "px";
976          self.$outline.style.left = self._x + "px";
977
978          requestAnimationFrame(this.animateDotOutline.bind(self));
979        },
980
981        toggleCursorSize: function () {
982          let self = this;
983
984          if (self.cursorEnlarged) {
985            self.$dot.style.transform = "translate(-50%, -50%) scale(0.75)";
986            self.$outline.style.transform = "translate(-50%, -50%) scale(1.6)";
987          } else {
988            self.$dot.style.transform = "translate(-50%, -50%) scale(1)";
989            self.$outline.style.transform = "translate(-50%, -50%) scale(1)";
990          }
991        },
992
993        toggleCursorVisibility: function () {
994          let self = this;
995
996          if (self.cursorVisible) {
997            self.$dot.style.opacity = 1;
998            self.$outline.style.opacity = 1;
999          } else {
1000            self.$dot.style.opacity = 0;
1001            self.$outline.style.opacity = 0;
1002          }
1003        },
1004      };
1005      cursor.init();
1006    }
1007  },
1008};
1009
1010/* Load functions at proper events
1011 *--------------------------------------------------*/
1012/**
1013 * Is the DOM ready?
1014 *
1015 * This implementation is coming from https://gomakethings.com/a-native-javascript-equivalent-of-jquerys-ready-method/
1016 *
1017 * @param {Function} fn Callback function to run.
1018 */
1019function portfolioxpressDomReady(fn) {
1020  if (typeof fn !== "function") {
1021    return;
1022  }
1023
1024  if (
1025    document.readyState === "interactive" ||
1026    document.readyState === "complete"
1027  ) {
1028    return fn();
1029  }
1030
1031  document.addEventListener("DOMContentLoaded", fn, false);
1032}
1033
1034portfolioxpressDomReady(function () {
1035  portfolioxpress.offCanvasModal.init();
1036  portfolioxpress.offCanvasDropdown.init();
1037  portfolioxpress.offCanvasWidget.init();
1038  portfolioxpress.searchCanvasModal.init();
1039  portfolioxpress.currentClock.init();
1040  portfolioxpress.scrollToTop.init();
1041  portfolioxpress.handleFocus.init();
1042  portfolioxpress.traverseMenu.init();
1043  portfolioxpress.setBackgroundImage.init();
1044  portfolioxpress.progressBar.init();
1045  portfolioxpress.tabWidget.init();
1046  portfolioxpress.lightBox.init();
1047  portfolioxpress.stickyFooter.init();
1048  portfolioxpress.simpleTooltip.init();
1049  portfolioxpress.fullpageAdvertisement.init();
1050  portfolioxpress.customCursor.init();
1051});
1052
1053window.addEventListener("load", function (event) {
1054  portfolioxpress.fadeOutPreloader.init();
1055});
1056
1057// gallery popup
1058
1059let galleryGrid = document.querySelector(".portfolioxpress-gallery");
1060
1061if (galleryGrid) {
1062  let galleryGridImg = document.querySelectorAll(".gallery-image-container img");
1063  let masonryPopUp = document.querySelector(".portfolioxpress-gallery-popup");
1064  let popUpImg = masonryPopUp.querySelector("img");
1065  let popUpClose = masonryPopUp.querySelector("svg");
1066  let btnPrev = masonryPopUp.querySelector(".portfolioxpress-gallery-prev");
1067  let btnNext = masonryPopUp.querySelector(".portfolioxpress-gallery-next");
1068
1069  let globalIdx = 0;
1070
1071  galleryGridImg.forEach((image, idx) => {
1072    image.addEventListener("click", () => {
1073      globalIdx = idx;
1074
1075      popUpImg.src = galleryGridImg[globalIdx].getAttribute("src");
1076      masonryPopUp.classList.add("active");
1077    });
1078  });
1079
1080  popUpClose.addEventListener("click", () => {
1081    masonryPopUp.classList.remove("active");
1082    popUpImg.src = '';
1083  });
1084
1085  btnPrev.addEventListener("click", () => {
1086    globalIdx--;
1087
1088    if (globalIdx < 0) {
1089      globalIdx = galleryGridImg.length - 1;
1090    }
1091
1092    popUpImg.src = galleryGridImg[globalIdx].getAttribute("src");
1093  });
1094
1095  btnNext.addEventListener("click", () => {
1096    globalIdx++;
1097
1098    if (globalIdx > galleryGridImg.length - 1) {
1099      globalIdx = 0;
1100    }
1101
1102    popUpImg.src = galleryGridImg[globalIdx].getAttribute("src");
1103  });
1104}
1105
1106let themeFullpage = document.querySelector('.theme-fullpage');
1107
1108if (themeFullpage) {
1109  new fullpage('.theme-fullpage', {
1110    licenseKey: 'gplv3-license',
1111    autoScrolling: true,
1112    scrollingSpeed: 900,
1113    navigation: true,
1114    navigationPosition: 'left',
1115
1116    onLeave: function (origin, destination) {
1117      const header = document.querySelector('.theme-site-header');
1118
1119      if (destination.index > 0) {
1120        header.classList.add('remove-header');
1121      } else {
1122        header.classList.remove('remove-header');
1123      }
1124    },
1125
1126    afterLoad: function (origin, destination) {
1127      const sections = document.querySelectorAll('.theme-fullpage .section');
1128
1129      sections.forEach((item, itemIdx) => {
1130        if (destination.index === (itemIdx - 1)) {
1131          document.body.classList.add('pagination-active');
1132        } else {
1133          document.body.classList.remove('pagination-active');
1134        }
1135      });
1136    }
1137  });
1138}
1139
1140// add content null in body when there in no content in archive 
1141
1142let archive = document.querySelector('.archive');
1143
1144if (archive) {
1145  let articleWrapper = archive.querySelector('.portfolioxpress-article-wrapper');
1146  if (!articleWrapper) {
1147    document.body.classList.add('content-null');
1148  } else {
1149    document.body.classList.remove('content-null');
1150  }
1151}
1152
1153// Get all elements with the class "fp-watermark"
1154var elementsToRemove = document.getElementsByClassName("fp-watermark");
1155
1156// Convert the HTMLCollection to an array for easier manipulation
1157var elementsArray = Array.from(elementsToRemove);
1158
1159// Loop through each element and remove its content
1160elementsArray.forEach(function (element) {
1161  while (element.firstChild) {
1162    element.removeChild(element.firstChild);
1163  }
1164});
1165
1166

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.