/** * Stay Fresh in AI — Presentation Engine * Pure JavaScript — No Dependencies */ (function () { 'use strict'; // ─── State ─── let currentSlide = 1; const totalSlides = 10; let isAnimating = false; // ─── DOM Elements ─── const progressFill = document.getElementById('progressFill'); const currentSlideEl = document.getElementById('currentSlide'); const totalSlidesEl = document.getElementById('totalSlides'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); const slides = document.querySelectorAll('.slide'); // ─── Init ─── function init() { totalSlidesEl.textContent = totalSlides; updateUI(1); bindEvents(); handleHash(); } // ─── Event Binding ─── function bindEvents() { prevBtn.addEventListener('click', goToPrev); nextBtn.addEventListener('click', goToNext); document.addEventListener('keydown', handleKeyDown); // Touch swipe let touchStartX = 0; document.addEventListener('touchstart', e => { touchStartX = e.changedTouches[0].screenX; }, { passive: true }); document.addEventListener('touchend', e => { const diff = touchStartX - e.changedTouches[0].screenX; if (Math.abs(diff) > 50) { diff > 0 ? goToNext() : goToPrev(); } }, { passive: true }); // Mouse wheel (throttled) let wheelLock = false; document.addEventListener('wheel', e => { if (wheelLock) return; wheelLock = true; setTimeout(() => { wheelLock = false; }, 800); e.deltaY > 0 ? goToNext() : goToPrev(); }, { passive: true }); } // ─── Keyboard Handler ─── function handleKeyDown(e) { switch (e.key) { case 'ArrowRight': case ' ': case 'Enter': e.preventDefault(); goToNext(); break; case 'ArrowLeft': case 'Backspace': e.preventDefault(); goToPrev(); break; case 'Home': e.preventDefault(); goToSlide(1); break; case 'End': e.preventDefault(); goToSlide(totalSlides); break; } // Number keys 1–9 for quick jump const num = parseInt(e.key); if (!isNaN(num) && num >= 1 && num <= Math.min(totalSlides, 9)) { goToSlide(num); } } // ─── Navigation ─── function goToNext() { if (currentSlide < totalSlides && !isAnimating) goToSlide(currentSlide + 1); } function goToPrev() { if (currentSlide > 1 && !isAnimating) goToSlide(currentSlide - 1); } function goToSlide(n) { if (n === currentSlide || n < 1 || n > totalSlides || isAnimating) return; isAnimating = true; currentSlide = n; slides.forEach((slide, i) => { const num = i + 1; slide.classList.remove('active', 'prev'); if (num === currentSlide) { slide.classList.add('active'); triggerAnimations(slide); } else if (num < currentSlide) { slide.classList.add('prev'); } }); updateUI(currentSlide); setTimeout(() => { isAnimating = false; }, 500); } // ─── UI Update ─── function updateUI(n) { currentSlideEl.textContent = n; progressFill.style.width = `${(n / totalSlides) * 100}%`; prevBtn.style.opacity = n === 1 ? '0.3' : '1'; prevBtn.style.pointerEvents = n === 1 ? 'none' : 'auto'; nextBtn.style.opacity = n === totalSlides ? '0.3' : '1'; nextBtn.style.pointerEvents = n === totalSlides ? 'none' : 'auto'; history.replaceState(null, null, `#slide-${n}`); } // ─── Re-trigger CSS animations when a slide becomes active ─── function triggerAnimations(slide) { const els = slide.querySelectorAll('.agenda-item, [style*="--delay"]'); els.forEach(el => { el.style.animation = 'none'; void el.offsetHeight; // reflow el.style.animation = null; }); } // ─── URL Hash Support ─── function handleHash() { const hash = window.location.hash; if (hash && hash.startsWith('#slide-')) { const n = parseInt(hash.replace('#slide-', '')); if (n >= 1 && n <= totalSlides) goToSlide(n); } } window.addEventListener('hashchange', handleHash); // ─── Public API ─── window.PresentationAPI = { next: goToNext, prev: goToPrev, goTo: goToSlide, getCurrentSlide: () => currentSlide, getTotalSlides: () => totalSlides }; // ─── Boot ─── if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();