1var initSliderTimeline = async () => { 2 document.querySelectorAll('.timeline').forEach(timeline => { 3 setupTimeline(timeline); 4 }); 5}; 6 7var setupTimeline = function (timeline) { 8 const buttons = timeline.querySelectorAll('button[data-target]'); 9 const root = timeline.parentElement; 10 const wrapper = root.querySelector('.timeline-target'); 11 const allItems = root.querySelectorAll('.timeline-item'); 12 const btnNext = root.querySelector('.timeline-next'); 13 const btnPrev = root.querySelector('.timeline-prev'); 14 15 let currentIndex = [...buttons].findIndex(b => b.classList.contains('active')); 16 if (currentIndex < 0) currentIndex = 0; 17 18 buttons.forEach((b, i) => b.classList.toggle('active', i === currentIndex)); 19 20 allItems.forEach((it, i) => { 21 it.classList.toggle('hidden', i !== currentIndex); 22 it.classList.remove('absolute', 'inset-0', 'opacity-0', 'translate-x-full', '-translate-x-full'); 23 it.classList.add('translate-x-0', 'w-full'); 24 }); 25 26 const updateHeight = () => { 27 const h = allItems[currentIndex].offsetHeight; 28 wrapper.style.height = h + 'px'; 29 }; 30 31 updateHeight(); 32 33 window.addEventListener('resize', () => { 34 clearTimeout(wrapper._rz); 35 wrapper._rz = setTimeout(updateHeight, 120); 36 }); 37 38 const goTo = (nextIndex) => { 39 if (nextIndex === currentIndex || nextIndex < 0 || nextIndex >= allItems.length) return; 40 41 const dir = nextIndex > currentIndex ? 1 : -1; 42 const curr = allItems[currentIndex]; 43 const next = allItems[nextIndex]; 44 45 buttons.forEach((b, i) => b.classList.toggle('active', i === nextIndex)); 46 47 next.classList.remove('hidden'); 48 next.classList.add('absolute', 'inset-0', 'w-full', 'opacity-0'); 49 next.classList.remove('translate-x-0', '-translate-x-full', 'translate-x-full'); 50 next.classList.add(dir > 0 ? 'translate-x-full' : '-translate-x-full'); 51 52 curr.classList.add('absolute', 'inset-0', 'w-full'); 53 curr.classList.remove('translate-x-0', '-translate-x-full', 'translate-x-full'); 54 curr.classList.add('translate-x-0'); 55 56 next.offsetHeight; 57 58 curr.classList.add(dir > 0 ? '-translate-x-full' : 'translate-x-full', 'opacity-0', 'pointer-events-none'); 59 next.classList.remove('opacity-0', 'translate-x-full', '-translate-x-full'); 60 next.classList.add('translate-x-0'); 61 62 const onDone = () => { 63 curr.classList.add('hidden'); 64 curr.classList.remove( 65 'absolute', 'inset-0', 'w-full', 66 'translate-x-0', '-translate-x-full', 'translate-x-full', 67 'opacity-0', 'pointer-events-none' 68 ); 69 70 next.classList.remove('absolute', 'inset-0', 'opacity-0', 'translate-x-full', '-translate-x-full'); 71 next.classList.add('translate-x-0', 'w-full'); 72 73 currentIndex = nextIndex; 74 updateHeight(); 75 updateNav(); 76 }; 77 78 curr.addEventListener('transitionend', onDone, { once: true }); 79 }; 80 81 buttons.forEach((button, index) => { 82 button.addEventListener('click', () => goTo(index)); 83 }); 84 85 function updateNav() { 86 if (btnPrev) btnPrev.classList.toggle('hidden', currentIndex === 0); 87 if (btnNext) btnNext.classList.toggle('hidden', currentIndex === allItems.length - 1); 88 } 89 90 updateNav(); 91 92 const AUTOPLAY_DELAY = 5000; 93 let autoplayTimer = null; 94 95 const startAutoplay = () => { 96 stopAutoplay(); 97 autoplayTimer = setInterval(() => { 98 const next = currentIndex + 1 < allItems.length ? currentIndex + 1 : 0; 99 goTo(next); 100 }, AUTOPLAY_DELAY); 101 }; 102 103 const stopAutoplay = () => { 104 if (autoplayTimer) { 105 clearInterval(autoplayTimer); 106 autoplayTimer = null; 107 } 108 }; 109 110 startAutoplay(); 111 112 if (btnNext) btnNext.addEventListener('click', () => goTo(currentIndex + 1)); 113 if (btnPrev) btnPrev.addEventListener('click', () => goTo(currentIndex - 1)); 114};
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.