1(() => { 2 const carousel = document.getElementById("tcarousel"); 3 if (!carousel) return; 4 5 const track = carousel.querySelector(".tcarousel__track"); 6 const cards = Array.from(track.querySelectorAll(".tcard")); 7 8 const fill = document.getElementById("tFill"); 9 const idxEl = document.getElementById("tIndex"); 10 const totalEl = document.getElementById("tTotal"); 11 const prevBtn = document.getElementById("tPrev"); 12 const nextBtn = document.getElementById("tNext"); 13 14 let currentIndex = 0; 15 let settleTimer = null; 16 let snapRestoreTimer = null; 17 18 function isOverflowMode() { 19 return window.innerWidth <= 768; 20 } 21 22 function leftAlignScrollLeft(cardEl) { 23 const trackRect = track.getBoundingClientRect(); 24 const cardRect = cardEl.getBoundingClientRect(); 25 return track.scrollLeft + (cardRect.left - trackRect.left); 26 } 27 28 function setEndSpacer() { 29 const spacer = track.querySelector(".carousel__spacer"); 30 if (!spacer || !cards.length) return; 31 32 if (isOverflowMode()) { 33 spacer.style.flexBasis = "16px"; 34 return; 35 } 36 37 spacer.style.flexBasis = "0px"; 38 const last = cards[cards.length - 1]; 39 const lastW = last.getBoundingClientRect().width; 40 const needed = Math.max(0, Math.ceil(track.clientWidth - lastW)); 41 spacer.style.flexBasis = needed + "px"; 42 } 43 44 function getMaxIndex() { 45 if (isOverflowMode()) { 46 return cards.length - 1; 47 } 48 49 const carouselWidth = carousel.clientWidth; 50 let perView = 1; 51 if (carouselWidth >= 1538) perView = 3; 52 else if (carouselWidth >= 1012) perView = 2; 53 54 return Math.max(0, cards.length - perView); 55 } 56 57 function nearestLeftMostIndex() { 58 const trackRect = track.getBoundingClientRect(); 59 const targetLeft = trackRect.left; 60 61 let best = 0; 62 let bestDist = Infinity; 63 64 for (let i = 0; i < cards.length; i++) { 65 const cardLeft = cards[i].getBoundingClientRect().left; 66 const dist = Math.abs(cardLeft - targetLeft); 67 if (dist < bestDist) { 68 bestDist = dist; 69 best = i; 70 } 71 } 72 return best; 73 } 74 75 function render(i) { 76 const maxIndex = getMaxIndex(); 77 currentIndex = Math.max(0, Math.min(i, maxIndex)); 78 79 idxEl.textContent = String(currentIndex + 1); 80 totalEl.textContent = String(maxIndex + 1); 81 fill.style.width = `${((currentIndex + 1) / (maxIndex + 1)) * 100}%`; 82 83 prevBtn.disabled = currentIndex === 0; 84 nextBtn.disabled = currentIndex === maxIndex; 85 } 86 87 function scrollToIndex(i) { 88 const maxIndex = getMaxIndex(); 89 const targetIndex = Math.max(0, Math.min(i, maxIndex)); 90 const targetLeft = leftAlignScrollLeft(cards[targetIndex]); 91 92 track.style.scrollSnapType = "none"; 93 clearTimeout(snapRestoreTimer); 94 95 render(targetIndex); 96 97 track.scrollTo({ 98 left: targetLeft, 99 behavior: "smooth" 100 }); 101 102 snapRestoreTimer = setTimeout(() => { 103 track.style.scrollSnapType = "x mandatory"; 104 render(nearestLeftMostIndex()); 105 }, 350); 106 } 107 108 prevBtn.addEventListener("click", () => { 109 scrollToIndex(currentIndex - 1); 110 }); 111 112 nextBtn.addEventListener("click", () => { 113 scrollToIndex(currentIndex + 1); 114 }); 115 116 track.addEventListener("scroll", () => { 117 clearTimeout(settleTimer); 118 settleTimer = setTimeout(() => { 119 render(nearestLeftMostIndex()); 120 }, 120); 121 }); 122 123 window.addEventListener("resize", () => { 124 setEndSpacer(); 125 track.style.scrollSnapType = "none"; 126 track.scrollTo({ 127 left: leftAlignScrollLeft(cards[Math.min(currentIndex, cards.length - 1)]) || 0, 128 behavior: "auto" 129 }); 130 track.style.scrollSnapType = "x mandatory"; 131 render(nearestLeftMostIndex()); 132 }); 133 134 window.addEventListener("load", () => { 135 setEndSpacer(); 136 track.scrollTo({ 137 left: leftAlignScrollLeft(cards[currentIndex]) || 0, 138 behavior: "auto" 139 }); 140 render(nearestLeftMostIndex()); 141 }); 142 143 setEndSpacer(); 144 render(0); 145 track.scrollTo({ 146 left: leftAlignScrollLeft(cards[0]) || 0, 147 behavior: "auto" 148 }); 149})();
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.