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.