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