1(function () { 2 if (window.__MAIN_JS_INITIALIZED) return; 3 window.__MAIN_JS_INITIALIZED = true; 4 5 const LOTTIE_SCRIPT_URL = "https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"; 6 const lottieScriptSelector = `script[src="${LOTTIE_SCRIPT_URL}"]`; 7 8 function loadLottieScript() { 9 if (window.customElements?.get("lottie-player")) return Promise.resolve(); 10 if (window.__lottieScriptPromise) return window.__lottieScriptPromise; 11 12 const existing = document.querySelector(lottieScriptSelector); 13 14 window.__lottieScriptPromise = new Promise((resolve, reject) => { 15 const script = existing || document.createElement("script"); 16 script.defer = true; 17 script.src = LOTTIE_SCRIPT_URL; 18 19 script.addEventListener("load", () => { 20 script.dataset.loaded = "true"; 21 resolve(); 22 }, { once: true }); 23 24 script.addEventListener("error", () => reject(new Error("Failed to load Lottie player script")), { once: true }); 25 26 if (!existing) { 27 document.head.appendChild(script); 28 } else if (existing.dataset.loaded === "true") { 29 resolve(); 30 } 31 }); 32 33 return window.__lottieScriptPromise; 34} 35 36 function initLazyLoad() { 37 const observer = new IntersectionObserver((entries) => { 38 entries.forEach(entry => { 39 if (!entry.isIntersecting) return; 40 41 const el = entry.target; 42 observer.unobserve(el); 43 44 if (el.tagName === 'VIDEO') return el.play(); 45 46 const src = el.dataset.src || el.dataset.lottieSrc || el.getAttribute('src'); 47 if (!src) return; 48 49 loadLottieScript().then(() => { 50 // Force re-creation by cloning after script loads 51 const clone = el.cloneNode(true); 52 clone.setAttribute('src', src); 53 el.replaceWith(clone); 54 55 customElements.whenDefined('lottie-player').then(() => { 56 // Small delay to ensure upgrade completes 57 requestAnimationFrame(() => { 58 if (typeof clone.play === 'function') { 59 clone.play(); 60 } 61 }); 62 }); 63 }); 64 }); 65 }, { rootMargin: '-100px' }); 66 67 document.querySelectorAll('lottie-player, video.lazy-load').forEach(el => observer.observe(el)); 68} 69 70 71// WP Header Mnu Submenu Toggle 72document.querySelectorAll('#menu-header li.menu-item-has-children > a').forEach(el => { 73 el.insertAdjacentHTML('beforeend', '<span class="toggle-sub"></span>'); 74}); 75 76document.addEventListener('click', function(e) { 77 if (e.target.classList.contains('toggle-sub')) { 78 e.preventDefault(); 79 const link = e.target.parentElement; 80 const menu = link.nextElementSibling; 81 82 e.target.classList.toggle('active'); 83 link.classList.toggle('active'); 84 if (menu) menu.classList.toggle('active'); 85 } 86}); 87 88// Partner Portal Coming Soon Tooltip 89document.querySelectorAll('a[href="#portal-soon"]').forEach(link => { 90 link.addEventListener('click', function(e) { 91 e.preventDefault(); 92 93 // Remove existing tooltip if any 94 const existing = document.querySelector('.inline-tip-box'); 95 if (existing) existing.remove(); 96 97 // Create tooltip 98 const tooltip = document.createElement('div'); 99 tooltip.className = 'inline-tip-box'; 100 tooltip.innerHTML = `<p><strong>Partner Portal Coming Soon</strong><br>We're putting the finishing touches on the partners portal experience.<br>Please check back soon, or contact us to get started in the meantime <a href="mailto:[email protected]">[email protected]</a></p>`; 101 102 // Append tooltip inside the link 103 this.appendChild(tooltip); 104 105 // Close on click outside 106 setTimeout(() => { 107 document.addEventListener('click', function closeTooltip(event) { 108 if (!tooltip.contains(event.target) && event.target !== link) { 109 tooltip.remove(); 110 document.removeEventListener('click', closeTooltip); 111 } 112 }); 113 }, 10); 114 }); 115}); 116 117// Function to update clear button visibility 118function updateClearButton(dropdown) { 119 const clearBtn = dropdown.querySelector(".filter-clear"); 120 const hasChecked = dropdown.querySelector('input[type="checkbox"]:checked'); 121 if (clearBtn) clearBtn.style.display = hasChecked ? "block" : "none"; 122} 123 124document.addEventListener("DOMContentLoaded", () => { 125 126 //initLottieLazyLoad(); 127 initLazyLoad(); 128 129 // Sticky Header 130 const header = document.querySelector(".site-header"); 131 if (header) { 132 const getHeaderHeight = () => header.offsetHeight || 0; 133 const toggleHeaderScrollClass = () => { 134 header.classList.toggle("is-scrolled", window.scrollY > getHeaderHeight()); 135 }; 136 window.addEventListener("scroll", toggleHeaderScrollClass); 137 toggleHeaderScrollClass(); 138 } 139 140 // Burger Menu 141 const burger = document.querySelector(".burger"); 142 const menu = document.querySelector(".header__nav"); 143 if (burger && menu) { 144 burger.addEventListener("click", () => { 145 burger.classList.toggle("is-open"); 146 menu.classList.toggle("is-open"); 147 }); 148 } 149 150 // Sticky Tab Nav 151 // Sticky Tab Nav (parent-bounded) 152 const tabnav = document.querySelector(".tab-navigation"); 153 154 if (tabnav) { 155 const parent = tabnav.closest(".industries-main"); // ð parent container 156 const offsetTop = tabnav.offsetTop; 157 const startOffset = 130; // when sticky starts 158 const topSpacing = 53; // fixed top position 159 160 const onScroll = () => { 161 const scrollY = window.scrollY; 162 163 const parentTop = parent.offsetTop; 164 const parentBottom = parentTop + parent.offsetHeight; 165 const tabHeight = tabnav.offsetHeight; 166 167 const stickyStart = offsetTop - startOffset;
168 const stickyEnd = parentBottom - tabHeight - topSpacing; 169 170 // ð¹ BEFORE parent 171 if (scrollY < stickyStart) { 172 tabnav.style.position = "relative"; 173 tabnav.style.top = "0"; 174 tabnav.style.width = ""; 175 } 176 177 // ð¹ STICKY inside parent 178 else if (scrollY >= stickyStart && scrollY < stickyEnd) { 179 tabnav.style.position = "fixed"; 180 tabnav.style.top = `${topSpacing}px`; 181 tabnav.style.width = `${parent.offsetWidth}px`; 182 tabnav.style.zIndex = "999"; 183 } 184 185 // ð¹ AFTER parent (stop / hide) 186 else { 187 tabnav.style.position = "absolute"; 188 tabnav.style.top = `${parent.offsetHeight - tabHeight}px`; 189 tabnav.style.width = `${parent.offsetWidth}px`; 190 } 191 }; 192 193 window.addEventListener("scroll", onScroll); 194 window.addEventListener("resize", onScroll); 195 onScroll(); 196 } 197 198 document.querySelectorAll('.rayz-close').forEach(btn => { 199 btn.addEventListener('click', () => { 200 btn.closest('.modal-rayzone')?.classList.remove('active'); 201 }); 202 }); 203 204 document.querySelectorAll('.open-modal').forEach(btn => { 205 btn.addEventListener('click', (e) => { 206 e.preventDefault(); 207 const targetId = btn.getAttribute('href').replace('#', ''); 208 const modal = document.getElementById(targetId); 209 if (modal) { 210 modal.classList.add('active'); 211 } 212 }); 213 }); 214 215 /*const sections = document.querySelectorAll(".industries-item"); 216 const tabs = document.querySelectorAll(".tab-link"); 217 218 const activateTab = (id) => {
219 tabs.forEach(tab => { 220 const isActive = tab.getAttribute("href") === `#${id}`; 221 tab.classList.toggle("active", isActive); 222 223 const li = tab.closest("li"); 224 if (li) li.classList.toggle("active", isActive); 225 }); 226 }; 227 228 const OFFSET = 180; 229 const EARLY = 60; 230 231 const onScrollSpy = () => { 232 const scrollPos = window.scrollY + OFFSET; 233 234 sections.forEach(section => { 235 const top = section.offsetTop + OFFSET; // ð magic here 236 const height = section.offsetHeight; 237 238 if (scrollPos >= top && scrollPos < top + height) { 239 activateTab(section.id); 240 } 241 }); 242 }; 243 244 window.addEventListener("scroll", onScrollSpy); 245 window.addEventListener("load", onScrollSpy); 246*/ 247// Tabs 248 249 250// Clients Carousel 251const list = document.querySelector(".clients__list"); 252if (list) { 253const items = Array.from(list.children); 254for (let i = 0; i < 2; i++) { 255items.forEach((item) => list.appendChild(item.cloneNode(true))); 256} 257 258Promise.all( 259Array.from(list.querySelectorAll("img")).map((img) => 260img.complete ? Promise.resolve() : new Promise((res) => (img.onload = res)) 261) 262).then(() => { 263const originalItems = Array.from(list.children).slice(0, items.length); 264const totalWidth = originalItems.reduce((total, item) => { 265return total + item.getBoundingClientRect().width + 64; 266}, 0); 267list.style.setProperty("--scroll-distance", `-${totalWidth * 2}px`); 268list.style.setProperty("--scroll-duration", `${(totalWidth * 2) / 50}s`); 269list.classList.add("scrolling"); 270}); 271} 272 273// Carousel Controls 274document.querySelectorAll(".carousel__buttons").forEach((group) => { 275 const targetId = group.dataset.target; 276 const carousel = document.getElementById(targetId); 277 if (!carousel) return; 278 279 const track = carousel.querySelector(".card__track"); 280 let cards = Array.from(track.children); 281 282 const GAP = 32; 283 const CARD_WIDTH = cards[0].offsetWidth + GAP; 284 285 // âââââââââââââââââââââââââââââââââââââ 286 // 1. Clone slides (before + after) 287 // âââââââââââââââââââââââââââââââââââââ 288 if (!track.dataset.loop) { 289 const before = cards.map(c => c.cloneNode(true)); 290 const after = cards.map(c => c.cloneNode(true)); 291 292 before.forEach(clone => track.insertBefore(clone, track.firstChild)); 293 after.forEach(clone => track.appendChild(clone)); 294 295 track.dataset.loop = "true"; 296 cards = Array.from(track.children); 297 } 298 299 const ORIGINAL_COUNT = cards.length / 3; 300 const START_OFFSET = ORIGINAL_COUNT * CARD_WIDTH; 301 302 // âââââââââââââââââââââââââââââââââââââ 303 // 2. Start centered (real slides) 304 // âââââââââââââââââââââââââââââââââââââ 305 track.scrollLeft = START_OFFSET; 306 307 // âââââââââââââââââââââââââââââââââââââ 308 // 3. Seamless boundary correction 309 // âââââââââââââââââââââââââââââââââââââ 310 function jumpWithoutAnimation(newLeft) { 311 track.style.scrollBehavior = "auto"; 312 track.scrollLeft = newLeft; 313 track.offsetHeight; // force reflow 314 track.style.scrollBehavior = "smooth"; 315 } 316 317 track.addEventListener("scroll", () => { 318 if (track.scrollLeft <= CARD_WIDTH) { 319 jumpWithoutAnimation(track.scrollLeft + ORIGINAL_COUNT * CARD_WIDTH); 320 } 321 322 if (track.scrollLeft >= START_OFFSET * 2) { 323 jumpWithoutAnimation(track.scrollLeft - ORIGINAL_COUNT * CARD_WIDTH); 324 } 325 }); 326 327 // âââââââââââââââââââââââââââââââââââââ 328 // 4. Controls 329 // âââââââââââââââââââââââââââââââââââââ 330 group.querySelector(".carousel__next").addEventListener("click", () => { 331 track.scrollBy({ left: CARD_WIDTH, behavior: "smooth" }); 332 }); 333 334 group.querySelector(".carousel__prev").addEventListener("click", () => { 335 track.scrollBy({ left: -CARD_WIDTH, behavior: "smooth" }); 336 }); 337}); 338 339// Filter Dropdowns 340 341// Initialize on page load 342document.querySelectorAll(".filter-dropdown").forEach(updateClearButton); 343 344document.querySelectorAll(".filter-dropdown").forEach((dropdown) => { 345const header = dropdown.querySelector(".filter-dropdown__header"); 346if (!header) return; 347 348header.addEventListener("click", () => { 349const isOpen = dropdown.classList.contains("open"); 350document.querySelectorAll(".filter-dropdown").forEach((d) => d.classList.remove("open")); 351if (!isOpen) dropdown.classList.add("open"); 352}); 353}); 354 355document.addEventListener("click", (e) => { 356if (!e.target.closest(".filter-dropdown")) { 357document.querySelectorAll(".filter-dropdown").forEach((d) => d.classList.remove("open")); 358} 359}); 360 361document.querySelectorAll('.filter-dropdown input[type="checkbox"]').forEach((cb) => { 362cb.addEventListener("change", () => { 363const dropdown = cb.closest(".filter-dropdown");
364const group = dropdown?.dataset.filter; 365if (!group) return; 366const selected = [...dropdown.querySelectorAll("input:checked")].map((i) => i.value); 367localStorage.setItem(`filter_${group}`, JSON.stringify(selected)); 368 369updateClearButton(dropdown); 370}); 371}); 372 373// Trigger event filter reload when checkboxes change (for events page) 374const eventGrid = document.getElementById('event-grid'); 375if (eventGrid) { 376document.querySelectorAll('.filter-dropdown[data-filter^="event_"]').forEach(dropdown => { 377dropdown.addEventListener('change', () => { 378// This will trigger the event filtering logic in event-archive.js 379const event = new CustomEvent('filtersChanged'); 380document.dispatchEvent(event); 381}); 382}); 383} 384 385document.querySelectorAll(".filter-clear").forEach((clearBtn) => { 386clearBtn.addEventListener("click", (e) => { 387e.stopPropagation(); 388const dropdown = clearBtn.closest(".filter-dropdown"); 389 390// Uncheck all checkboxes AND trigger change on each 391dropdown.querySelectorAll('input[type="checkbox"]').forEach(cb => { 392if (cb.checked) { 393cb.checked = false; 394cb.dispatchEvent(new Event('change', { bubbles: true })); 395} 396}); 397 398localStorage.removeItem(`filter_${dropdown.dataset.filter}`); 399updateClearButton(dropdown); 400}); 401}); 402 403document.querySelectorAll(".filter-dropdown").forEach((dropdown) => { 404const group = dropdown.dataset.filter; 405if (!group) return; 406 407const storageKey = `filter_${group}`; 408const preselected = [...dropdown.querySelectorAll('input[type="checkbox"]:checked')].map((cb) => cb.value); 409const saved = preselected.length ? preselected : JSON.parse(localStorage.getItem(storageKey) || "[]"); 410 411if (preselected.length) { 412localStorage.setItem(storageKey, JSON.stringify(preselected)); 413} 414 415dropdown.querySelectorAll('input[type="checkbox"]').forEach((cb) => { 416cb.checked = saved.includes(cb.value); 417}); 418 419// Add this line here to update UI after loading saved filters 420updateClearButton(dropdown); 421 422}); 423 424//Top Stories Slider 425document.querySelectorAll(".slider-buttons").forEach((group) => { 426const slider = document.getElementById(group.dataset.target); 427if (!slider) return; 428 429const track = slider.querySelector(".slider-track"); 430const slideWidth = track.children[0]?.offsetWidth; 431if (!slideWidth) return; 432 433const nextBtn = group.querySelector(".slider-next"); 434const prevBtn = group.querySelector(".slider-prev"); 435 436const update = () => { 437prevBtn.disabled = track.scrollLeft <= 1; 438nextBtn.disabled = track.scrollLeft + track.offsetWidth >= track.scrollWidth - 1; 439}; 440 441const autoNext = () => track.scrollLeft = track.scrollLeft + track.offsetWidth >= track.scrollWidth - 1 ? 0 : track.scrollLeft + slideWidth; 442 443nextBtn.addEventListener("click", () => track.scrollLeft += slideWidth); 444prevBtn.addEventListener("click", () => track.scrollLeft -= slideWidth); 445track.addEventListener("scroll", update); 446update(); 447 448const autoplay = slider.dataset.autoplay; 449if (autoplay) { 450let timer = setInterval(autoNext, parseInt(autoplay)); 451const restart = () => (clearInterval(timer), timer = setInterval(autoNext, parseInt(autoplay))); 452 453slider.addEventListener("mouseenter", () => clearInterval(timer)); 454slider.addEventListener("mouseleave", restart); 455nextBtn.addEventListener("click", restart); 456prevBtn.addEventListener("click", restart); 457} 458}); 459 460 461// Ajax Post Load (BLOG PAGE ONLY) 462const loader = document.getElementById('post-loader'); 463const loadBtn = document.querySelector('.load-post'); 464 465if (loader && loadBtn) { 466const loadAction = document.querySelector('.post-load-action'); 467const filterGroups = document.querySelectorAll('.filter-dropdown'); 468const spinner = loadBtn.querySelector('.loader'); 469 470const loadPosts = (page, append = true) => { 471loadBtn.disabled = true; 472spinner.style.display = 'inline-block'; 473 474const formData = new FormData(); 475formData.append('action', 'load_posts'); 476formData.append('page', page); 477if (postAjax && postAjax.nonce) { 478formData.append('security', postAjax.nonce); 479} 480 481filterGroups.forEach(group => { 482const filterType = group.dataset.filter; 483group.querySelectorAll('input[type="checkbox"]:checked').forEach(cb => { 484formData.append(filterType + '[]', cb.value); 485}); 486}); 487 488fetch(postAjax.ajaxurl, { method: 'POST', body: formData }) 489.then(res => res.json()) 490.then(data => { 491if (append) { 492loader.insertAdjacentHTML('beforeend', data.html); 493} else { 494loader.innerHTML = data.html || '<p class="no-posts">No posts found.</p>'; 495} 496 497loadBtn.dataset.page = page; 498loadBtn.dataset.max = data.max_pages; 499loadAction.style.display = 500page >= data.max_pages || data.found_posts === 0 ? 'none' : 'flex'; 501}) 502.finally(() => { 503loadBtn.disabled = false;
504spinner.style.display = 'none'; 505}); 506}; 507 508loadBtn.addEventListener('click', () => { 509loadPosts(parseInt(loadBtn.dataset.page) + 1); 510}); 511 512filterGroups.forEach(group => { 513group.addEventListener('change', () => { 514loadBtn.dataset.page = 1; 515loadPosts(1, false); 516}); 517}); 518} 519 520// Filter mObile 521document.querySelectorAll('.filter-mob').forEach(btn => { 522btn.addEventListener('click', function() { 523const filterGroup = this.parentElement.querySelector('.post-filter-group'); 524if (filterGroup) { 525filterGroup.classList.toggle('active'); 526} 527}); 528}); 529 530/*// Toggle tab-links on mobile filter action click 531document.querySelectorAll('.mob-filter-action').forEach(btn => { 532btn.addEventListener('click', function () { 533console.log('click done'); 534const tabLinks = this.parentElement.querySelector('.tab-links'); 535if (tabLinks) { 536tabLinks.classList.toggle('active'); 537} 538}); 539});*/ 540 541document.addEventListener('click', function (e) { 542const link = e.target.closest('.tab-links a'); 543if (!link) return; 544 545const li = link.closest('li'); 546const menu = li.parentElement; 547 548// Remove active from siblings 549menu.querySelectorAll('li.active').forEach(item => { 550item.classList.remove('active'); 551}); 552 553// Add active to clicked one 554li.classList.add('active'); 555}); 556 557//Counter About Page 558const observer = new IntersectionObserver((entries) => { 559entries.forEach((entry) => { 560if (entry.isIntersecting) { 561const el = entry.target; 562const target = +el.getAttribute('data-target'); 563const duration = 2000; // Total time in milliseconds 564const frameRate = 1000 / 60; // Approx 60 frames per second 565const totalSteps = duration / frameRate; 566const increment = target / totalSteps; 567 568let currentCount = 0; 569 570const interval = setInterval(() => { 571currentCount += increment; 572if (currentCount >= target) { 573el.innerText = target; 574clearInterval(interval); 575} else { 576el.innerText = Math.floor(currentCount); 577} 578}, frameRate); 579 580observer.unobserve(el); 581} 582}); 583}, { threshold: 1 }); 584 585document.querySelectorAll('.numb-counter').forEach(n => { 586const target = n.innerText.trim(); 587n.setAttribute('data-target', target); 588n.style.display = 'inline-block'; 589n.style.minWidth = n.offsetWidth + 'px'; 590n.innerText = '0'; 591observer.observe(n); 592}); 593 594 595// NOTE: the #subscribe-form submit handler that used to live here was 596// removed â it was a second, independent handler competing with the 597// jQuery Validate + AJAX handler in footer.php. Having both bound to the 598// same form's submit event meant every click fired two validations and 599// two AJAX requests, which is what caused the double-submit / blank-state 600// bug on rapid clicks. The form is now handled exclusively in footer.php. 601 602}); 603})(); 604
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.