1(function () { 2 const debounce = (fn, ms) => { 3 let t; 4 return (...args) => { 5 clearTimeout(t); 6 t = setTimeout(() => fn.apply(this, args), ms); 7 }; 8 }; 9 10 function perPage(root) { 11 const m = parseInt(root.dataset.perpageMobile || '2', 10); 12 const t = parseInt(root.dataset.perpageTablet || '2', 10); 13 const d = parseInt(root.dataset.perpageDesktop || '4', 10); 14 15 if (window.innerWidth <= 768) return m; // mobile 16 return d; // desktop 17 } 18 19 function initCatalogueCarousel(root) { 20 const wrapper = root.querySelector('.carousel-track-wrapper'); 21 const track = root.querySelector('.carousel-track'); 22 const cards = Array.from(root.querySelectorAll('.carousel-card')); 23 const prev = root.querySelector('.carousel-btn.prev'); 24 const next = root.querySelector('.carousel-btn.next'); 25 const dotsWrap = root.querySelector('.carousel-indicators'); 26 27 let currentIndex = 0; 28 let pages = 1; 29 30 31// carousel indicators 32 function buildDots() { 33 dotsWrap.innerHTML = ''; 34 35 for (let i = 0; i < pages; i++) { 36 const dot = document.createElement('span'); 37 dot.className = 'carousel-dot'; 38 39 dot.addEventListener('click', () => { 40 currentIndex = i * perPage(root); 41 update(); 42 }); 43 44 dotsWrap.appendChild(dot); 45 } 46 } 47 48 function measure() { 49 const visibleCount = perPage(root); 50 pages = Math.max(1, Math.ceil(cards.length / visibleCount)); 51 52 currentIndex = Math.min( 53 currentIndex, 54 Math.max(0, cards.length - visibleCount) 55 ); 56 57 buildDots(); 58 update(); 59 } 60 61 62 function update() { 63 const card = cards[0]; 64 if (!card) return; 65 66 const cardWidth = Math.round(card.getBoundingClientRect().width); 67 const visibleCount = perPage(root); 68 69 const maxIndex = Math.max(0, cards.length - visibleCount); 70 currentIndex = Math.min(currentIndex, maxIndex); 71 72 const translate = Math.round(currentIndex * cardWidth); 73 track.style.transform = `translate3d(-${translate}px, 0, 0)`; 74 75 // arrows 76 prev.classList.toggle('is-hidden', currentIndex === 0); 77 next.classList.toggle('is-hidden', currentIndex === maxIndex); 78 79 // dots (FIXED) 80 const activeDot = (currentIndex >= maxIndex) 81 ? pages - 1 82 : Math.floor(currentIndex / visibleCount); 83 84 dotsWrap.querySelectorAll('.carousel-dot') 85 .forEach((dot, i) => 86 dot.classList.toggle('active', i === activeDot) 87 ); 88 } 89 90 91 /* ---------- carousel control buttons---------- */ 92 93 prev.addEventListener('click', () => { 94 if (currentIndex > 0) { 95 currentIndex--; 96 update(); 97 } 98 }); 99 100 next.addEventListener('click', () => { 101 currentIndex++; 102 update(); 103 }); 104 105 window.addEventListener('resize', debounce(measure, 150)); 106 107 measure(); 108 } 109 110 document 111 .querySelectorAll('.catalogue-carousel') 112 .forEach(initCatalogueCarousel); 113})();
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.