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