PageSourceSearch

https://www.rentalrates.ai/app/web/js/theme.js

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