PageSourceSearch

https://calacstemis.ca/assets/js/theme.js

js calacstemis.ca collected 2026-10-04 02:25:12 UTC 31,797 bytes, 915 lines download raw bytes

1'use strict';
2var theme = {
3  /**
4   * Theme's components/functions list
5   * Comment out or delete the unnecessary component.
6   * Some components have dependencies (plugins).
7   * Do not forget to remove dependency from src/js/vendor/ and recompile.
8   */
9  init: function () {
10    theme.stickyHeader();
11    theme.subMenu();
12    theme.offCanvas();
13    theme.isotope();
14    theme.onepageHeaderOffset();
15    theme.spyScroll();
16    theme.anchorSmoothScroll();
17    theme.svgInject();
18    theme.backgroundImage();
19    theme.backgroundImageMobile();
20    theme.imageHoverOverlay();
21    theme.rellax();
22    theme.scrollCue();
23    theme.swiperSlider();
24    theme.lightbox();
25    theme.plyr();
26    theme.progressBar();
27    theme.loader();
28    theme.pageProgress();
29    theme.counterUp();
30    theme.bsTooltips();
31    theme.bsPopovers();
32    theme.bsModal();
33    theme.iTooltip();
34    theme.forms();
35    theme.passVisibility();
36    theme.pricingSwitcher();
37    theme.textRotator();
38    theme.codeSnippet();
39  },
40  /**
41   * Sticky Header
42   * Enables sticky behavior on navbar on page scroll
43   * Requires assets/js/vendor/headhesive.min.js
44  */
45  stickyHeader: () => {
46    var navbar = document.querySelector(".navbar");
47    if (navbar == null) return;
48    var options = {
49      offset: 350,
50      offsetSide: 'top',
51      classes: {
52        clone: 'navbar-clone fixed',
53        stick: 'navbar-stick',
54        unstick: 'navbar-unstick',
55      },
56      onStick: function() {
57        var navbarClonedClass = this.clonedElem.classList;
58        if (navbarClonedClass.contains('transparent') && navbarClonedClass.contains('navbar-dark')) {
59          this.clonedElem.className = this.clonedElem.className.replace("navbar-dark","navbar-light");
60        }
61      }
62    };
63    var banner = new Headhesive('.navbar', options);
64  },
65  /**
66   * Sub Menus
67   * Enables multilevel dropdown
68   */
69  subMenu: () => {
70    (function($bs) {
71      const CLASS_NAME = 'has-child-dropdown-show';
72      $bs.Dropdown.prototype.toggle = function(_original) {
73          return function() {
74              document.querySelectorAll('.' + CLASS_NAME).forEach(function(e) {
75                  e.classList.remove(CLASS_NAME);
76              });
77              let dd = this._element.closest('.dropdown').parentNode.closest('.dropdown');
78              for (; dd && dd !== document; dd = dd.parentNode.closest('.dropdown')) {
79                  dd.classList.add(CLASS_NAME);
80              }
81              return _original.call(this);
82          }
83      }($bs.Dropdown.prototype.toggle);
84      document.querySelectorAll('.dropdown').forEach(function(dd) {
85          dd.addEventListener('hide.bs.dropdown', function(e) {
86              if (this.classList.contains(CLASS_NAME)) {
87                  this.classList.remove(CLASS_NAME);
88                  e.preventDefault();
89              }
90              e.stopPropagation();
91          });
92      });
93    })(bootstrap);
94  },
95  /**
96   * Offcanvas
97   * Enables offcanvas-nav, closes offcanvas on anchor clicks, focuses on input in search offcanvas
98   */
99  offCanvas: () => {
100    var navbar = document.querySelector(".navbar");
101    if (navbar == null) return;
102    const navOffCanvasBtn = document.querySelectorAll(".offcanvas-nav-btn");
103    const navOffCanvas = document.querySelector('.navbar:not(.navbar-clone) .offcanvas-nav');
104    const bsOffCanvas = new bootstrap.Offcanvas(navOffCanvas, {scroll: true});
105    const scrollLink = document.querySelectorAll('.onepage .navbar li a.scroll');
106    const searchOffcanvas = document.getElementById('offcanvas-search');
107    navOffCanvasBtn.forEach(e => {
108      e.addEventListener('click', event => {
109        bsOffCanvas.show();
110      })
111    });
112    scrollLink.forEach(e => {
113      e.addEventListener('click', event => {
114        bsOffCanvas.hide();
115      })
116    });
117    if(searchOffcanvas != null) {
118      searchOffcanvas.addEventListener('shown.bs.offcanvas', function () {
119        document.getElementById("search-form").focus();
120      });
121    }
122  },
123  /**
124   * Isotope
125   * Enables isotope grid layout and filtering
126   * Requires assets/js/vendor/isotope.pkgd.min.js
127   * Requires assets/js/vendor/imagesloaded.pkgd.min.js
128   */
129  isotope: () => {
130    var grids = document.querySelectorAll('.itemgrid');
131    if(grids != null) {
132      grids.forEach(g => {
133        var itemgrid = g.querySelector('.isotope');
134        var filtersElem = g.querySelector('.isotope-filter');
135        var buttonGroups = g.querySelectorAll('.isotope-filter');
136        var iso = new Isotope(itemgrid, {
137          itemSelector: '.item',
138          layoutMode: 'masonry',
139          masonry: {
140            columnWidth: itemgrid.offsetWidth / 12
141          },
142          percentPosition: true,
143          transitionDuration: '0.7s'
144        });
145        imagesLoaded(itemgrid).on("progress", function() {
146          iso.layout({
147            masonry: {
148              columnWidth: itemgrid.offsetWidth / 12
149            }
150          })
151        }),
152        window.addEventListener("resize", function() {
153          iso.arrange({
154            masonry: {
155              columnWidth: itemgrid.offsetWidth / 12
156            }
157          });
158        }, true);
159        if(filtersElem != null) {
160          filtersElem.addEventListener('click', function(event) {
161            if(!matchesSelector(event.target, '.filter-item')) {
162              return;
163            }
164            var filterValue = event.target.getAttribute('data-filter');
165            iso.arrange({
166              filter: filterValue
167            });
168          });
169          for(var i = 0, len = buttonGroups.length; i < len; i++) {
170            var buttonGroup = buttonGroups[i];
171            buttonGroup.addEventListener('click', function(event) {
172              if(!matchesSelector(event.target, '.filter-item')) {
173                return;
174              }
175              buttonGroup.querySelector('.active').classList.remove('active');
176              event.target.classList.add('active');
177            });
178          }
179        }
180      });
181    }
182  },
183  /**
184   * Onepage Header Offset
185   * Adds an offset value to anchor point equal to sticky header height on a onepage
186   */
187  onepageHeaderOffset: () => {
188    var navbar = document.querySelector(".navbar");
189    if (navbar == null) return;
190    const header_height = document.querySelector(".navbar").offsetHeight;
191    const shrinked_header_height = 75;
192    const sections = document.querySelectorAll(".onepage section");
193    sections.forEach(section => {
194      section.style.paddingTop = shrinked_header_height + 'px';
195      section.style.marginTop = '-' + shrinked_header_height + 'px';
196    });
197    const first_section = document.querySelector(".onepage section:first-of-type");
198    if(first_section != null) {
199      first_section.style.paddingTop = header_height + 'px';
200      first_section.style.marginTop = '-' + header_height + 'px';
201    }
202  },
203  /**
204   * Spy Scroll
205   * Highlights the active nav link while scrolling through sections
206   */
207  spyScroll: () => {
208    let section = document.querySelectorAll('section[id]');
209    let navLinks = document.querySelectorAll('.nav-link.scroll');
210    window.onscroll = () => {
211      section.forEach(sec => {
212        let top = window.scrollY; //returns the number of pixels that the document is currently scrolled vertically.
213        let offset = sec.offsetTop - 0; //returns the distance of the outer border of the current element relative to the inner border of the top of the offsetParent, the closest positioned ancestor element
214        let height = sec.offsetHeight; //returns the height of an element, including vertical padding and borders, as an integer
215        let id = sec.getAttribute('id'); //gets the value of an attribute of an element
216        if (top >= offset && top < offset + height) {
217          navLinks.forEach(links => {
218            links.classList.remove('active');
219            document.querySelector(`.nav-link.scroll[href*=${id}]`).classList.add('active');
220            //[att*=val] Represents an element with the att attribute whose value contains at least one instance of the substring "val". If "val" is the empty string then the selector does not represent anything.
221          });
222        }
223      });
224    }
225  },
226  /**
227   * Anchor Smooth Scroll
228   * Adds smooth scroll animation to links with .scroll class
229   * Requires assets/js/vendor/smoothscroll.js
230   */
231  anchorSmoothScroll: () => {
232    const links = document.querySelectorAll(".scroll");
233    for(const link of links) {
234      link.addEventListener("click", clickHandler);
235    }
236    function clickHandler(e) {
237      e.preventDefault();
238      this.blur();
239      const href = this.getAttribute("href");
240      const offsetTop = document.querySelector(href).offsetTop;
241      scroll({
242        top: offsetTop,
243        behavior: "smooth"
244      });
245    }
246  },
247  /**
248   * SVGInject
249   * Replaces an img element with an inline SVG so you can apply colors to your SVGs
250   * Requires assets/js/vendor/svg-inject.min.js
251   */
252  svgInject: () => {
253    SVGInject.setOptions({
254      onFail: function(img, svg) {
255        img.classList.remove('svg-inject');
256      }
257    });
258    document.addEventListener('DOMContentLoaded', function() {
259      SVGInject(document.querySelectorAll('img.svg-inject'), {
260        useCache: true
261      });
262    });
263  },
264  /**
265   * Background Image
266   * Adds a background image link via data attribute "data-image-src"
267   */
268  backgroundImage: () => {
269    var bg = document.querySelectorAll(".bg-image");
270    for(var i = 0; i < bg.length; i++) {
271      var url = bg[i].getAttribute('data-image-src');
272      bg[i].style.backgroundImage = "url('" + url + "')";
273    }
274  },
275  /**
276   * Background Image Mobile
277   * Adds .mobile class to background images on mobile devices for styling purposes
278   */
279  backgroundImageMobile: () => {
280    var isMobile = (navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/webOS/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/BlackBerry/i)) ? true : false;
281    if(isMobile) {
282      document.querySelectorAll(".image-wrapper").forEach(e => {
283        e.classList.add("mobile")
284      })
285    }
286  },
287  /**
288   * Image Hover Overlay
289   * Adds span.bg inside .overlay for simpler markup and styling purposes
290   */
291  imageHoverOverlay: () => {
292    var overlay = document.querySelectorAll('.overlay > a, .overlay > span');
293    for(var i = 0; i < overlay.length; i++) {
294      var overlay_bg = document.createElement('span');
295      overlay_bg.className = "bg";
296      overlay[i].appendChild(overlay_bg);
297    }
298  },
299  /**
300   * Rellax.js
301   * Adds parallax animation to shapes and elements
302   * Requires assets/js/vendor/rellax.min.js
303   */
304  rellax: () => {
305    if(document.querySelector(".rellax") != null) {
306      window.onload = function() {
307        var rellax = new Rellax('.rellax', {
308          speed: 2,
309          center: true,
310          breakpoints: [576, 992, 1201]
311        });
312        var projects_overflow = document.querySelectorAll('.projects-overflow');
313        imagesLoaded(projects_overflow, function() {
314          rellax.refresh();
315        });
316      }
317    }
318  },
319  /**
320   * scrollCue.js
321   * Enables showing elements by scrolling
322   * Requires assets/js/vendor/scrollCue.min.js
323   */
324  scrollCue: () => {
325    scrollCue.init({
326      interval: -400,
327      duration: 700,
328      percentage: 0.8
329    });
330    scrollCue.update();
331  },
332  /**
333   * Swiper Slider
334   * Enables carousels and sliders
335   * Requires assets/js/vendor/swiper-bundle.min.js
336   */
337  swiperSlider: function() {
338    var carousel = document.querySelectorAll('.swiper-container');
339    for(var i = 0; i < carousel.length; i++) {
340      var slider1 = carousel[i];
341      slider1.classList.add('swiper-container-' + i);
342      var controls = document.createElement('div');
343      controls.className = "swiper-controls";
344      var pagi = document.createElement('div');
345      pagi.className = "swiper-pagination";
346      var navi = document.createElement('div');
347      navi.className = "swiper-navigation";
348      var prev = document.createElement('div');
349      prev.className = "swiper-button swiper-button-prev";
350      var next = document.createElement('div');
351      next.className = "swiper-button swiper-button-next";
352      slider1.appendChild(controls);
353      controls.appendChild(navi);
354      navi.appendChild(prev);
355      navi.appendChild(next);
356      controls.appendChild(pagi);
357      var sliderEffect = slider1.getAttribute('data-effect') ? slider1.getAttribute('data-effect') : 'slide';
358      if (slider1.getAttribute('data-items-auto') === 'true') {
359        var slidesPerViewInit = "auto";
360        var breakpointsInit = null;
361      } else {
362        var sliderItems = slider1.getAttribute('data-items') ? slider1.getAttribute('data-items') : 3; // items in all devices
363        var sliderItemsXs = slider1.getAttribute('data-items-xs') ? slider1.getAttribute('data-items-xs') : 1; // start - 575
364        var sliderItemsSm = slider1.getAttribute('data-items-sm') ? slider1.getAttribute('data-items-sm') : Number(sliderItemsXs); // 576 - 767
365        var sliderItemsMd = slider1.getAttribute('data-items-md') ? slider1.getAttribute('data-items-md') : Number(sliderItemsSm); // 768 - 991
366        var sliderItemsLg = slider1.getAttribute('data-items-lg') ? slider1.getAttribute('data-items-lg') : Number(sliderItemsMd); // 992 - 1199
367        var sliderItemsXl = slider1.getAttribute('data-items-xl') ? slider1.getAttribute('data-items-xl') 
367: Number(sliderItemsLg); // 1200 - end
368        var sliderItemsXxl = slider1.getAttribute('data-items-xxl') ? slider1.getAttribute('data-items-xxl') : Number(sliderItemsXl); // 1500 - end
369        var slidesPerViewInit = sliderItems;
370        var breakpointsInit = {
371          0: {
372            slidesPerView: Number(sliderItemsXs)
373          },
374          576: {
375            slidesPerView: Number(sliderItemsSm)
376          },
377          768: {
378            slidesPerView: Number(sliderItemsMd)
379          },
380          992: {
381            slidesPerView: Number(sliderItemsLg)
382          },
383          1200: {
384            slidesPerView: Number(sliderItemsXl)
385          },
386          1400: {
387            slidesPerView: Number(sliderItemsXxl)
388          }
389        }
390      }
391      var sliderSpeed = slider1.getAttribute('data-speed') ? slider1.getAttribute('data-speed') : 500;
392      var sliderAutoPlay = slider1.getAttribute('data-autoplay') !== 'false';
393      var sliderAutoPlayTime = slider1.getAttribute('data-autoplaytime') ? slider1.getAttribute('data-autoplaytime') : 5000;
394      var sliderAutoHeight = slider1.getAttribute('data-autoheight') === 'true';
395      var sliderResizeUpdate = slider1.getAttribute('data-resizeupdate') !== 'false';
396      var sliderAllowTouchMove = slider1.getAttribute('data-drag') !== 'false';
397      var sliderReverseDirection = slider1.getAttribute('data-reverse') === 'true';
398      var sliderMargin = slider1.getAttribute('data-margin') ? slider1.getAttribute('data-margin') : 30;
399      var sliderLoop = slider1.getAttribute('data-loop') === 'true';
400      var sliderCentered = slider1.getAttribute('data-centered') === 'true';
401      var swiper = slider1.querySelector('.swiper:not(.swiper-thumbs)');
402      var swiperTh = slider1.querySelector('.swiper-thumbs');
403      var sliderTh = new Swiper(swiperTh, {
404        slidesPerView: 5,
405        spaceBetween: 10,
406        loop: false,
407        threshold: 2,
408        slideToClickedSlide: true
409      });
410      if (slider1.getAttribute('data-thumbs') === 'true') {
411        var thumbsInit = sliderTh;
412        var swiperMain = document.createElement('div');
413        swiperMain.className = "swiper-main";
414        swiper.parentNode.insertBefore(swiperMain, swiper);
415        swiperMain.appendChild(swiper);
416        slider1.removeChild(controls);
417        swiperMain.appendChild(controls);
418      } else {
419        var thumbsInit = null;
420      }
421      var slider = new Swiper(swiper, {
422        on: {
423          beforeInit: function() {
424            if(slider1.getAttribute('data-nav') !== 'true' && slider1.getAttribute('data-dots') !== 'true') {
425              controls.remove();
426            }
427            if(slider1.getAttribute('data-dots') !== 'true') {
428              pagi.remove();
429            }
430            if(slider1.getAttribute('data-nav') !== 'true') {
431              navi.remove();
432            }
433          },
434          init: function() {
435            if(slider1.getAttribute('data-autoplay') !== 'true') {
436              this.autoplay.stop();
437            }
438            this.update();
439          }
440        },
441        autoplay: {
442          delay: sliderAutoPlayTime,
443          disableOnInteraction: false,
444          reverseDirection: sliderReverseDirection,
445          pauseOnMouseEnter: false
446        },
447        allowTouchMove: sliderAllowTouchMove,
448        speed: parseInt(sliderSpeed),
449        slidesPerView: slidesPerViewInit,
450        loop: sliderLoop,
451        centeredSlides: sliderCentered,
452        spaceBetween: Number(sliderMargin),
453        effect: sliderEffect,
454        autoHeight: sliderAutoHeight,
455        grabCursor: true,
456        resizeObserver: false,
457        updateOnWindowResize: sliderResizeUpdate,
458        breakpoints: breakpointsInit,
459        pagination: {
460          el: carousel[i].querySelector('.swiper-pagination'),
461          clickable: true
462        },
463        navigation: {
464          prevEl: slider1.querySelector('.swiper-button-prev'),
465          nextEl: slider1.querySelector('.swiper-button-next'),
466        },
467        thumbs: {
468          swiper: thumbsInit,
469        },
470      });
471    }
472  },
473  /**
474   * GLightbox
475   * Enables lightbox functionality
476   * Requires assets/js/vendor/glightbox.js
477   */
478  lightbox: () => {
479    const lightbox = GLightbox({
480      selector: '*[data-glightbox]',
481      touchNavigation: true,
482      loop: false,
483      zoomable: false,
484      autoplayVideos: true,
485      moreLength: 0,
486      slideExtraAttributes: {
487        poster: ''
488      },
489      plyr: {
490        css: '',
491        js: '',
492        config: {
493          ratio: '',
494          fullscreen: {
495            enabled: false,
496            iosNative: false
497          },
498          youtube: {
499            noCookie: true,
500            rel: 0,
501            showinfo: 0,
502            iv_load_policy: 3
503          },
504          vimeo: {
505            byline: false,
506            portrait: false,
507            title: false,
508            transparent: false
509          }
510        }
511      },
512    });
513  },
514  /**
515   * Plyr
516   * Enables media player
517   * Requires assets/js/vendor/plyr.js
518   */
519  plyr: () => {
520    var players = Plyr.setup('.player', {
521      loadSprite: true,
522      fullscreen: { enabled: true, iosNative: true },
523      muted: false
524    });
525  },
526  /**
527   * Progressbar
528   * Enables animated progressbars
529   * Requires assets/js/vendor/progressbar.min.js
530   * Requires assets/js/vendor/noframework.waypoints.min.js
531   */
532  progressBar: () => {
533    const pline = document.querySelectorAll(".progressbar.line");
534    const pcircle = document.querySelectorAll(".progressbar.semi-circle");
535    pline.forEach(e => {
536      var line = new ProgressBar.Line(e, {
537        strokeWidth: 6,
538        trailWidth: 6,
539        duration: 3000,
540        easing: 'easeInOut',
541        text: {
542          style: {
543            color: 'inherit',
544            position: 'absolute',
545            right: '0',
546            top: '-30px',
547            padding: 0,
548            margin: 0,
549            transform: null
550          },
551          autoStyleContainer: false
552        },
553        step: (state, line) => {
554          line.setText(Math.round(line.value() * 100) + ' %');
555        }
556      });
557      var value = e.getAttribute('data-value') / 100;
558      new Waypoint({
559        element: e,
560        handler: function() {
561          line.animate(value);
562        },
563        offset: 'bottom-in-view',
564      })
565    });
566    pcircle.forEach(e => {
567      var circle = new ProgressBar.SemiCircle(e, {
568        strokeWidth: 6,
569        trailWidth: 6,
570        duration: 2000,
571        easing: 'easeInOut',
572        step: (state, circle) => {
573          circle.setText(Math.round(circle.value() * 100));
574        }
575      });
576      var value = e.getAttribute('data-value') / 100;
577      new Waypoint({
578        element: e,
579        handler: function() {
580          circle.animate(value);
581        },
582        offset: 'bottom-in-view',
583      })
584    });
585  },
586  /**
587   * Loader
588   * 
589   */
590  loader: () => {
591    var preloader = document.querySelector('.page-loader');
592    if(preloader != null) {
593      document.body.onload = function(){
594        setTimeout(function() {
595          if( !preloader.classList.contains('done') )
596          {
597            preloader.classList.add('done');
598          }
599        }, 1000)
600      }
601    }
602  },
603  /**
604   * Page Progress
605   * Shows page progress on the bottom right corner of pages
606   */
607  pageProgress: () => {
608    var progressWrap = document.querySelector('.progress-wrap');
609    if(progressWrap != null) {
610      var progressPath = document.querySelector('.progress-wrap path');
611      var pathLength = progressPath.getTotalLength();
612      var offset = 50;
613      progressPath.style.transition = progressPath.style.WebkitTransition = 'none';
614      progressPath.style.strokeDasharray = pathLength + ' ' + pathLength;
615      progressPath.style.strokeDashoffset = pathLength;
616      progressPath.getBoundingClientRect();
617      progressPath.style.transition = progressPath.style.WebkitTransition = 'stroke-dashoffset 10ms linear';
618      window.addEventListener("scroll", function(event) {
619        var scroll = document.body.scrollTop || document.documentElement.scrollTop;
620        var height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
621        var progress = pathLength - (scroll * pathLength / height);
622        progressPath.style.strokeDashoffset = progress;
623        var scrollElementPos = document.body.scrollTop || document.documentElement.scrollTop;
624        if(scrollElementPos >= offset) {
625          progressWrap.classList.add("active-progress")
626        } else {
627          progressWrap.classList.remove("active-progress")
628        }
629      });
630      progressWrap.addEventListener('click', function(e) {
631        e.preventDefault();
632        window.scroll({
633          top: 0, 
634          left: 0,
635          behavior: 'smooth'
636        });
637      });
638    }
639  },
640  /**
641   * Counter Up
642   * Counts up to a targeted number when the number becomes visible
643   * Requires assets/js/vendor/counterup.min.js
644   * Requires assets/js/vendor/noframework.waypoints.min.js
645   */
646  counterUp: () => {
647    var counterUp = window.counterUp["default"];
648    const counters = document.querySelectorAll(".counter");
649    counters.forEach(el => {
650      new Waypoint({
651        element: el,
652        handler: function() {
653          counterUp(el, {
654            duration: 1000,
655            delay: 50
656          })
657          this.destroy()
658        },
659        offset: 'bottom-in-view',
660      })
661    });
662  },
663  /**
664   * Bootstrap Tooltips
665   * Enables Bootstrap tooltips
666   * Requires Poppers library
667   */
668  bsTooltips: () => {
669    var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
670    var tooltipList = tooltipTriggerList.map(function(tooltipTriggerEl) {
671      return new bootstrap.Tooltip(tooltipTriggerEl, {
672        trigger: 'hover'
673      })
674    });
675    var tooltipTriggerWhite = [].slice.call(document.querySelectorAll('[data-bs-toggle="white-tooltip"]'))
676    var tooltipWhite = tooltipTriggerWhite.map(function(tooltipTriggerEl) {
677      return new bootstrap.Tooltip(tooltipTriggerEl, {
678        customClass: 'white-tooltip',
679        trigger: 'hover',
680        placement: 'left'
681      })
682    })
683  },
684  /**
685   * Bootstrap Popovers
686   * Enables Bootstrap popovers
687   * Requires Poppers library
688   */
689  bsPopovers: () => {
690    var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
691    var popoverList = popoverTriggerList.map(function(popoverTriggerEl) {
692      return new bootstrap.Popover(popoverTriggerEl)
693    })
694  },
695  /**
696   * Bootstrap Modal
697   * Enables Bootstrap modal popup
698   */
699  bsModal: () => {
700    if(document.querySelector(".modal-popup") != null) {
701      var myModalPopup = new bootstrap.Modal(document.querySelector('.modal-popup'));
702      setTimeout(function() {
703        myModalPopup.show();
704      }, 200);
705    }
706    // Fixes jumping of page progress caused by modal
707    var innerWidth = window.innerWidth;
708    var clientWidth = document.body.clientWidth;
709    var scrollSize = innerWidth - clientWidth;
710    var myModalEl = document.querySelectorAll('.modal');
711    var navbarFixed = document.querySelector('.navbar.fixed');
712    var pageProgress = document.querySelector('.progress-wrap');
713    function setPadding() {
714      if(navbarFixed != null) {
715        navbarFixed.style.paddingRight = scrollSize + 'px';
716      }
717      if(pageProgress != null) {
718        pageProgress.style.marginRight = scrollSize + 'px';
719      }
720    }
721    function removePadding() {
722      if(navbarFixed != null) {
723        navbarFixed.style.paddingRight = '';
724      }
725      if(pageProgress != null) {
726       pageProgress.style.marginRight = '';
727      }
728    }
729    myModalEl.forEach(myModalEl => {
730      myModalEl.addEventListener('show.bs.modal', function(e) {
731        setPadding();
732      })
733      myModalEl.addEventListener('hidden.bs.modal', function(e) {
734        removePadding();
735      })
736    });
737  },
738  /**
739   * iTooltip
740   * Enables custom tooltip style for image hover docs/elements/hover.html
741   * Requires assets/js/vendor/itooltip.min.js
742   */
743  iTooltip: () => {
744    var tooltip = new iTooltip('.itooltip')
745    tooltip.init({
746      className: 'itooltip-inner',
747      indentX: 15,
748      indentY: 15,
749      positionX: 'right',
750      positionY: 'bottom'
751    })
752  },
753  /**
754   * Form Validation and Contact Form submit
755   * Bootstrap validation - Only sends messages if form has class ".contact-form" and is validated and shows success/fail messages
756   */
757  forms: () => {
758    (function() {
759      "use strict";
760      window.addEventListener("load", function() {
761        var forms = document.querySelectorAll(".needs-validation");
762        var inputRecaptcha = document.querySelector("input[data-recaptcha]"); 
763        window.verifyRecaptchaCallback = function (response) {
764          inputRecaptcha.value = response; 
765          inputRecaptcha.dispatchEvent(new Event("change"));
766        }
767        window.expiredRecaptchaCallback = function () {
768          var inputRecaptcha = document.querySelector("input[data-recaptcha]"); 
769          inputRecaptcha.value = ""; 
770          inputRecaptcha.dispatchEvent(new Event("change"));
771        }
772        var validation = Array.prototype.filter.call(forms, function(form) {
773          form.addEventListener("submit", function(event) {
774            if(form.checkValidity() === false) {
775              event.preventDefault();
776              event.stopPropagation();
777            }
778            form.classList.add("was-validated");
779            if(form.checkValidity() === true) {
780              event.preventDefault();
781              form.classList.remove("was-validated");
782              // Send message only if the form has class .contact-form
783              var isContactForm = form.classList.contains('contact-form');
784              if(isContactForm) {
785                var data = new FormData(form);
786                var alertClass = 'alert-danger';
787                fetch("assets/php/contact.php", {
788                  method: "post",
789                  body: data
790                }).then((data) => {
791                  if(data.ok) {
792                    alertClass = 'alert-success';
793                  }
794                  return data.text();
795                }).then((txt) => {
796                  var alertBox = '<div class="alert ' + alertClass + ' alert-dismissible fade show"><button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>' + txt + '</div>';
797                  if(alertClass && txt) {
798                    form.querySelector(".messages").insertAdjacentHTML('beforeend', alertBox);
799                    form.reset();
800                    grecaptcha.reset();
801                  }
802                }).catch((err) => {
803                  console.log(err);
804                });
805              }
806            }
807          }, false);
808        });
809      }, false);
810    })();
811  },
812  /**
813   * Password Visibility Toggle
814   * Toggles password visibility in password input
815   */
816  passVisibility: () => {
817    let pass = document.querySelectorAll('.password-field');
818    for (let i = 0; i < pass.length; i++) {
819      let passInput = pass[i].querySelector('.form-control');
820      let passToggle = pass[i].querySelector('.password-toggle > i');
821      passToggle.addEventListener('click', (e) => {
822        if (passInput.type === "password") {
823          passInput.type = "text";
824          passToggle.classList.remove('uil-eye');
825          passToggle.classList.add('uil-eye-slash');
826        } else {
827          passInput.type = "password";
828          passToggle.classList.remove('uil-eye-slash'); 
829          passToggle.classList.add('uil-eye');
830        } 
831      }, false);
832    }
833  },
834  /**
835   * Pricing Switcher
836   * Enables monthly/yearly switcher seen on pricing tables
837   */
838  pricingSwitcher: () => {
839    if(document.querySelector(".pricing-switchers") != null) {
840      const wrapper = document.querySelectorAll(".pricing-wrapper");
841      wrapper.forEach(wrap => {
842        const switchers = wrap.querySelector(".pricing-switchers");
843        const switcher = wrap.querySelectorAll(".pricing-switcher");
844        const price = wrap.querySelectorAll(".price");
845        switchers.addEventListener("click", (e) => {
846          switcher.forEach(s => {
847            s.classList.toggle("pricing-switcher-active");
848          });
849          price.forEach(p => {
850            p.classList.remove("price-hidden");
851            p.classList.toggle("price-show");
852            p.classList.toggle("price-hide");
853          });
854        });
855      });
856    }
857  },
858  /**
859   * ReplaceMe.js
860   * Enables text rotator
861   * Requires assets/js/vendor/replaceme.min.js
862   */
863  textRotator: () => {
864    if(document.querySelector(".rotator-zoom") != null) {
865      var replace = new ReplaceMe(document.querySelector('.rotator-zoom'), {
866        animation: 'animate__animated animate__zoomIn',
867        speed: 2500,
868        separator: ',',
869        clickChange: false,
870        loopCount: 'infinite'
871      });
872    }
873    if(document.querySelector(".rotator-fade") != null) {
874      var replace = new ReplaceMe(document.querySelector('.rotator-fade'), {
875        animation: 'animate__animated animate__fadeInDown',
876        speed: 2500,
877        separator: ',',
878        clickChange: false,
879        loopCount: 'infinite'
880      });
881    }
882  },
883  /**
884   * Clipboard.js
885   * Enables clipboard on docs
886   * Requires assets/js/vendor/clipboard.min.js
887   */
888  codeSnippet: () => {
889    var btnHtml = '<button type="button" class="btn btn-sm btn-white rounded-pill btn-clipboard">Copy</button>'
890    document.querySelectorAll('.code-wrapper-inner').forEach(function(element) {
891      element.insertAdjacentHTML('beforebegin', btnHtml)
892    })
893    var clipboard = new ClipboardJS('.btn-clipboard', {
894      target: function(trigger) {
895        return trigger.nextElementSibling
896      }
897    })
898    clipboard.on('success', event => {
899      event.trigger.textContent = 'Copied!';
900      event.clearSelection();
901      setTimeout(function () {
902        event.trigger.textContent = 'Copy';
903      }, 2000);
904    });
905    var copyIconCode = new ClipboardJS('.btn-copy-icon');
906    copyIconCode.on('success', function(event) {
907      event.clearSelection();
908      event.trigger.textContent = 'Copied!';
909      window.setTimeout(function() {
910        event.trigger.textContent = 'Copy';
911      }, 2300);
912    });
913  },
914}
915theme.init();

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.