PageSourceSearch

https://lifespan.io/wp-content/themes/fundify/js/lsxc-25-alliance-carousel-v109.js?ver=1.0.9

js lifespan.io collected 2026-10-02 04:59:58 UTC 9,943 bytes, 390 lines download raw bytes

1/**
2 * LSXC-25 / LSXC-28 — Portable continuous Alliance logo strip (template 143291).
3 * Cursor agent change — 2026-07-24
4 * Ticket/context: LSXC-28 — suppress logo link activation after drag
5 * See: .cursor/plans/lsxc-28-alliance-slider-drag-click.plan.md (chat LSXC-28)
6 *
7 * Continuous auto-scroll + click/drag for every embed of template 143291.
8 * v1.0.9 — drag-vs-click guard: only pure clicks open logo URLs.
9 */
10(function () {
11	'use strict';
12
13	var VERSION = '1.0.9';
14	var SELECTOR =
15		'.elementor-143291 .eael-logo-carousel, .elementor-143291 .swiper-container-6b52d00, .elementor-143291 .swiper';
16	var SPEED_PX_PER_SEC = 40;
17	var DRAG_CLICK_THRESHOLD_PX = 5;
18	var reduceMotion =
19		window.matchMedia &&
20		window.matchMedia('(prefers-reduced-motion: reduce)').matches;
21
22	function qsa(ctx, sel) {
23		return Array.prototype.slice.call((ctx || document).querySelectorAll(sel));
24	}
25
26	function findHosts(ctx) {
27		var root = ctx && ctx.nodeType ? ctx : document;
28		var nodes = qsa(root, SELECTOR);
29		if (root !== document && root.matches && root.matches(SELECTOR)) {
30			nodes.unshift(root);
31		}
32		return nodes.filter(function (el) {
33			return el.closest && el.closest('.elementor-143291');
34		});
35	}
36
37	function wrapOffset(offset, loop) {
38		if (!loop || loop <= 0) {
39			return offset;
40		}
41		while (offset <= -loop) {
42			offset += loop;
43		}
44		while (offset > 0) {
45			offset -= loop;
46		}
47		return offset;
48	}
49
50	function destroySwiper(el) {
51		if (el.swiper && typeof el.swiper.destroy === 'function') {
52			try {
53				el.swiper.destroy(true, true);
54			} catch (e) {
55				/* ignore */
56			}
57		}
58		el.classList.remove(
59			'swiper-initialized',
60			'swiper-horizontal',
61			'swiper-pointer-events',
62			'swiper-autoheight',
63			'swiper-backface-hidden'
64		);
65	}
66
67	/**
68	 * Prefer real slides (not Swiper clones). Fall back to unique-by-src.
69	 */
70	function collectOriginalSlides(track) {
71		var children = Array.prototype.slice.call(track.children);
72		var withImg = children.filter(function (slide) {
73			return (
74				slide.classList &&
75				slide.classList.contains('swiper-slide') &&
76				slide.querySelector('img')
77			);
78		});
79		var real = withImg.filter(function (slide) {
80			return !slide.classList.contains('swiper-slide-duplicate');
81		});
82		var source = real.length >= 5 ? real : withImg;
83		var seen = Object.create(null);
84		var originals = [];
85		source.forEach(function (slide) {
86			var img = slide.querySelector('img');
87			var key =
88				(img &&
89					(img.getAttribute('src') ||
90						img.getAttribute('data-src') ||
91						img.currentSrc)) ||
92				'';
93			key = String(key).split('?')[0];
94			if (!key || seen[key]) {
95				return;
96			}
97			seen[key] = true;
98			var clean = slide.cloneNode(true);
99			clean.className = 'swiper-slide';
100			clean.removeAttribute('style');
101			clean.removeAttribute('data-swiper-slide-index');
102			originals.push(clean);
103		});
104		return originals;
105	}
106
107	function rebuildCleanTrack(track) {
108		var originals = collectOriginalSlides(track);
109		if (!originals.length) {
110			return 0;
111		}
112		track.innerHTML = '';
113		originals.forEach(function (s) {
114			track.appendChild(s);
115		});
116		originals.forEach(function (s) {
117			track.appendChild(s.cloneNode(true));
118		});
119		track.setAttribute('data-lsxc25-dup', '1');
120		track.setAttribute('data-lsxc25-count', String(originals.length));
121		return originals.length;
122	}
123
124	function setSlideColor(slide, on) {
125		var img = slide && slide.querySelector('img');
126		if (!img) {
127			return;
128		}
129		if (on) {
130			slide.classList.add('is-lsxc25-color');
131			img.style.setProperty('filter', 'grayscale(0)', 'important');
132			img.style.setProperty('-webkit-filter', 'grayscale(0)', 'important');
133		} else {
134			slide.classList.remove('is-lsxc25-color');
135			img.style.removeProperty('filter');
136			img.style.removeProperty('-webkit-filter');
137		}
138	}
139
140	function enhanceHost(el) {
141		if (!el || (el.getAttribute('data-lsxc25-ver') === VERSION && el._lsxc25 && !el._lsxc25.needsRepair)) {
142			return;
143		}
144
145		if (el._lsxc25 && el._lsxc25.raf) {
146			window.cancelAnimationFrame(el._lsxc25.raf);
147			el._lsxc25.dead = true;
148		}
149
150		destroySwiper(el);
151
152		var track = null;
153		for (var i = 0; i < el.children.length; i++) {
154			if (el.children[i].classList && el.children[i].classList.contains('swiper-wrapper')) {
155				track = el.children[i];
156				break;
157			}
158		}
159		if (!track) {
160			track = el.querySelector('.swiper-wrapper');
161		}
162		if (!track) {
163			return;
164		}
165
166		el.classList.add('lsxc25-marquee-host');
167		track.classList.add('lsxc25-marquee-track');
168
169		var count = rebuildCleanTrack(track);
170		if (!count) {
171			return;
172		}
173
174		el.setAttribute('data-lsxc25-ver', VERSION);
175		el.setAttribute('data-lsxc25-marquee', '1');
176
177		if (reduceMotion) {
178			el.classList.add('lsxc25-reduced-motion');
179			return;
180		}
181
182		var state = {
183			offset: 0,
184			dragging: false,
185			dragStartX: 0,
186			dragStartOffset: 0,
187			suppressClick: false,
188			lastTs: 0,
189			hovering: false,
190			track: track,
191			raf: 0,
192			dead: false,
193			logoCount: count,
194			needsRepair: false
195		};
196		el._lsxc25 = state;
197
198		function loopWidth() {
199			return Math.max(1, state.track.scrollWidth / 2);
200		}
201
202		function apply(doWrap) {
203			var x = state.offset;
204			if (doWrap) {
205				x = wrapOffset(x, loopWidth());
206				state.offset = x;
207			}
208			state.track.style.transform = 'translate3d(' + x + 'px, 0, 0)';
209		}
210
211		function tick(ts) {
212			if (state.dead) {
213				return;
214			}
215			// If EAEL/Swiper sneaks back in, tear it down and rebuild once.
216			if (el.swiper || state.track.querySelector('.swiper-slide-duplicate')) {
217				destroySwiper(el);
218				if (state.track.querySelector('.swiper-slide-duplicate')) {
219					var n = rebuildCleanTrack(state.track);
220					if (n) {
221						state.logoCount = n;
222						state.offset = wrapOffset(state.offset, loopWidth());
223					}
224				}
225			}
226			if (!state.lastTs) {
227				state.lastTs = ts;
228			}
229			var dt = Math.min(64, ts - state.lastTs) / 1000;
230			state.lastTs = ts;
231			if (!state.dragging && !state.hovering) {
232				state.offset -= SPEED_PX_PER_SEC * dt;
233				apply(true);
234			}
235			state.raf = window.requestAnimationFrame(tick);
236		}
237
238		function onMove(e) {
239			if (!state.dragging) {
240				return;
241			}
242			var dx = e.clientX - state.dragStartX;
243			if (Math.abs(dx) >= DRAG_CLICK_THRESHOLD_PX) {
244				state.suppressClick = true;
245			}
246			state.offset = state.dragStartOffset + dx;
247			apply(false);
248			if (e.cancelable) {
249				e.preventDefault();
250			}
251		}
252
253		function onUp() {
254			if (!state.dragging) {
255				return;
256			}
257			state.dragging = false;
258			el.classList.remove('is-dragging');
259			window.removeEventListener('pointermove', onMove, true);
260			window.removeEventListener('mousemove', onMove, true);
261			window.removeEventListener('pointerup', onUp, true);
262			window.removeEventListener('mouseup', onUp, true);
263			window.removeEventListener('pointercancel', onUp, true);
264			apply(true);
265		}
266
267		function onDown(e) {
268			if (e.button != null && e.button !== 0) {
269				return;
270			}
271			if (state.dragging) {
272				return;
273			}
274			state.dragging = true;
275			state.suppressClick = false;
276			state.dragStartX = e.clientX;
277			state.dragStartOffset = state.offset;
278			el.classList.add('is-dragging');
279			window.addEventListener('pointermove', onMove, true);
280			window.addEventListener('mousemove', onMove, true);
281			window.addEventListener('pointerup', onUp, true);
282			window.addEventListener('mouseup', onUp, true);
283			window.addEventListener('pointercancel', onUp, true);
284			if (e.cancelable) {
285				e.preventDefault();
286			}
287		}
288
289		function onClickCapture(e) {
290			if (!state.suppressClick) {
291				return;
292			}
293			e.preventDefault();
294			e.stopPropagation();
295			if (typeof e.stopImmediatePropagation === 'function') {
296				e.stopImmediatePropagation();
297			}
298			state.suppressClick = false;
299		}
300
301		track.addEventListener('mouseover', function (e) {
302			var slide = e.target.closest ? e.target.closest('.swiper-slide') : null;
303			if (!slide || !track.contains(slide)) {
304				return;
305			}
306			qsa(track, '.swiper-slide.is-lsxc25-color').forEach(function (s) {
307				if (s !== slide) {
308					setSlideColor(s, false);
309				}
310			});
311			setSlideColor(slide, true);
312		});
313		track.addEventListener('mouseleave', function () {
314			qsa(track, '.swiper-slide.is-lsxc25-color').forEach(function (s) {
315				setSlideColor(s, false);
316			});
317		});
318
319		// Bind host listeners once (re-enhance after version bump reuses them).
320		if (!el.getAttribute('data-lsxc25-bound')) {
321			el.setAttribute('data-lsxc25-bound', '1');
322			el.addEventListener('pointerdown', onDown);
323			el.addEventListener('mousedown', onDown);
324			el.addEventListener('click', onClickCapture, true);
325			el.addEventListener('mouseenter', function () {
326				if (el._lsxc25) {
327					el._lsxc25.hovering = true;
328				}
329			});
330			el.addEventListener('mouseleave', function () {
331				if (el._lsxc25) {
332					el._lsxc25.hovering = false;
333				}
334			});
335			el.addEventListener('dragstart', function (e) {
336				e.preventDefault();
337			});
338		}
339
340		window.requestAnimationFrame(function () {
341			apply(true);
342			state.raf = window.requestAnimationFrame(tick);
343		});
344	}
345
346	function boot(ctx) {
347		findHosts(ctx).forEach(enhanceHost);
348	}
349
350	function onReady(fn) {
351		if (document.readyState === 'loading') {
352			document.addEventListener('DOMContentLoaded', fn);
353		} else {
354			fn();
355		}
356	}
357
358	onReady(function () {
359		boot(document);
360		[400, 1200, 2500].forEach(function (ms) {
361			setTimeout(function () {
362				boot(document);
363			}, ms);
364		});
365	});
366
367	if (window.jQuery) {
368		window.jQuery(window).on('elementor/frontend/init', function () {
369			if (!window.elementorFrontend || !elementorFrontend.hooks) {
370				return;
371			}
372			elementorFrontend.hooks.addAction(
373				'frontend/element_ready/eael-logo-carousel.default',
374				function ($scope) {
375					setTimeout(function () {
376						boot($scope && $scope[0] ? $scope[0] : document);
377					}, 50);
378				}
379			);
380			elementorFrontend.hooks.addAction(
381				'frontend/element_ready/template.default',
382				function ($scope) {
383					setTimeout(function () {
384						boot($scope && $scope[0] ? $scope[0] : document);
385					}, 50);
386				}
387			);
388		});
389	}
390})();

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.