1(function ($) { 2 "use strict"; 3 4 /* ---------------------------------------- 5 GLOBAL HELPERS 6 ---------------------------------------- */ 7 const hasEl = (selector) => document.querySelector(selector); 8 9 const initGSAP = () => { 10 if (typeof gsap !== "undefined" && typeof ScrollTrigger !== "undefined") { 11 gsap.registerPlugin(ScrollTrigger); 12 return true; 13 } 14 return false; 15 }; 16 17 /* ---------------------------------------- 18 ELEMENTOR INIT 19 ---------------------------------------- */ 20 $(window).on("elementor/frontend/init", function () { 21 elementorFrontend.hooks.addAction( 22 "frontend/element_ready/global", 23 function () { 24 25 /* 1. HOME â Timeline scroll + click */ 26 const section = document.querySelector(".timeline-container"); 27 if (section) { 28 const items = section.querySelectorAll(".timeline-item"); 29 if (!items.length) return; 30 31 let maxProgress = 0; 32 const activate = (i) => { 33 items.forEach((el, index) => 34 el.classList.toggle("active", index === i) 35 ); 36 }; 37 activate(0); 38 39 const onScroll = () => { 40 const rect = section.getBoundingClientRect(); 41 const winH = window.innerHeight; 42 const startPoint = winH * 0.4; 43 44 if (rect.top > startPoint || rect.bottom < 200) return; 45 46 let progress = Math.max(0, startPoint - rect.top); 47 48 const lastItem = items[items.length - 1]; 49 maxProgress = lastItem.offsetTop + 40; 50 progress = Math.min(progress, maxProgress); 51 52 const segment = maxProgress / (items.length - 1); 53 const index = Math.min( 54 Math.floor(progress / segment), 55 items.length - 1 56 ); 57 58 activate(index); 59 section.style.setProperty("--after-height", `${progress}px`); 60 }; 61 62 window.addEventListener("scroll", onScroll, { passive: true }); 63 64 items.forEach((item, index) => { 65 item.addEventListener("click", () => { 66 const segment = maxProgress / (items.length - 1); 67 section.style.setProperty( 68 "--after-height", 69 `${segment * index}px` 70 ); 71 activate(index); 72 }); 73 }); 74 } 75 76 /* 2. HOME â Text color scroll animation */ 77 if (hasEl(".about-cont p") && initGSAP()) { 78 const paragraph = document.querySelector(".about-cont p"); 79 if (!paragraph.classList.contains("gsap-processed")) { 80 paragraph.classList.add("gsap-processed"); 81 82 const words = paragraph.innerText.split(" "); 83 paragraph.innerHTML = words 84 .map((w) => `<span class="word">${w}</span>`) 85 .join(" "); 86 87 gsap.fromTo( 88 ".about-cont p .word", 89 { color: "#B6B6B6" }, 90 { 91 color: "#030467", 92 stagger: 0.1, 93 ease: "none", 94 scrollTrigger: { 95 trigger: ".about-cont", 96 start: "top 90%", 97 end: "bottom 50%", 98 scrub: true, 99 }, 100 } 101 ); 102 } 103 } 104 105 /* 3. ABOUT â Tabs progress indicator */ 106 if (hasEl(".story-tab")) { 107 const updateProgress = () => { 108 const tabs = $(".story-tab .e-n-tab-title"); 109 const active = tabs.filter("[aria-selected='true']").index(); 110 if (active < 0) return; 111 112 const step = 100 / tabs.length; 113 $(".story-tab .e-n-tabs-heading") 114 .css("--progress", `${(active + 1) * step}%`); 115 }; 116 117 updateProgress(); 118 $(".story-tab .e-n-tab-title").on("click", () => 119 setTimeout(updateProgress, 50) 120 ); 121 } 122 123 /* 4. LOGO SLIDER â Middle active slide */ 124 const initLogoSlider = () => { 125 const swiperEl = document.querySelector(".slider-logo .swiper"); 126 const swiper = swiperEl?.swiper; 127 if (!swiper) return setTimeout(initLogoSlider, 200); 128 129 const setMiddle = () => { 130 const slides = swiper.slides; 131 if (!slides?.length) return; 132 133 const perView = 134 Number(swiper.params.slidesPerView) || 135 Math.round(swiper.el.clientWidth / slides[0].clientWidth) || 136 1; 137 138 let idx = 139 (swiper.activeIndex + Math.floor(perView / 2)) % 140 slides.length; 141 142 $(slides).removeClass("middle-active"); 143 $(slides[idx]).addClass("middle-active"); 144 }; 145 146 swiper.on("init slideChange transitionEnd resize", setMiddle); 147 setTimeout(() => {
148 try { swiper.update(); } catch (e) {} 149 setMiddle(); 150 }, 250); 151 }; 152 153 if (hasEl(".slider-logo .swiper")) { 154 initLogoSlider(); 155 } 156 157 } 158 ); 159 }); 160 161 /* ---------------------------------------- 162 5. ENGAGE EXPERTS â Custom arrows 163 ---------------------------------------- */ 164 $(".success-stories-prev a, .success-stories-next a") 165 .attr("href", "javascript:void(0)"); 166 167 $(".success-stories-prev").on("click", () => 168 $(".elementor-swiper-button-prev").trigger("click") 169 ); 170 171 $(".success-stories-next").on("click", () => 172 $(".elementor-swiper-button-next").trigger("click") 173 ); 174 175 /* ---------------------------------------- 176 6. ELEMENTOR POPUP â Gravity Forms Phone Number Field Validation 177 ---------------------------------------- */ 178 jQuery(document).on('elementor/popup/show', function () { 179 180 jQuery(document).off('input.gfphone').on('input.gfphone', '#input_3_4', function () { 181 this.value = this.value.replace(/[^0-9]/g, ''); 182 }); 183 184}); 185 186 187 188 189})(jQuery);
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.