PageSourceSearch

https://rayzone.com/wp-content/themes/rayzone_wptheme/asset/js/main.js?ver=1787631348

js rayzone.com collected 2026-10-04 16:17:12 UTC 18,966 bytes, 604 lines download raw bytes

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.