// Presentation JavaScript functionality class Presentation { constructor() { this.currentSlide = 0; this.totalSlides = 15; this.slides = []; this.isFullscreen = false; this.init(); } init() { // Wait for DOM to be fully loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.setupPresentation()); } else { this.setupPresentation(); } } setupPresentation() { this.slides = document.querySelectorAll('.slide'); console.log('Found slides:', this.slides.length); if (this.slides.length === 0) { console.error('No slides found!'); return; } this.totalSlides = this.slides.length; this.bindEvents(); this.showSlide(0); this.updateProgress(); this.updateCounter(); this.updateNavigationButtons(); } bindEvents() { // Button controls const prevBtn = document.getElementById('prev-slide'); const nextBtn = document.getElementById('next-slide'); const fullscreenBtn = document.getElementById('fullscreen-toggle'); if (prevBtn) prevBtn.addEventListener('click', () => this.previousSlide()); if (nextBtn) nextBtn.addEventListener('click', () => this.nextSlide()); if (fullscreenBtn) fullscreenBtn.addEventListener('click', () => this.toggleFullscreen()); // Keyboard controls document.addEventListener('keydown', (e) => this.handleKeydown(e)); // Fullscreen change events document.addEventListener('fullscreenchange', () => this.handleFullscreenChange()); document.addEventListener('webkitfullscreenchange', () => this.handleFullscreenChange()); document.addEventListener('mozfullscreenchange', () => this.handleFullscreenChange()); document.addEventListener('msfullscreenchange', () => this.handleFullscreenChange()); // Touch/swipe events for mobile this.addTouchEvents(); console.log('Event listeners bound successfully'); } handleKeydown(e) { switch(e.key) { case 'ArrowRight': case ' ': case 'Enter': e.preventDefault(); this.nextSlide(); break; case 'ArrowLeft': e.preventDefault(); this.previousSlide(); break; case 'Home': e.preventDefault(); this.goToSlide(0); break; case 'End': e.preventDefault(); this.goToSlide(this.totalSlides - 1); break; case 'f': case 'F': if (!e.ctrlKey && !e.metaKey) { e.preventDefault(); this.toggleFullscreen(); } break; case 'Escape': if (this.isFullscreen) { this.exitFullscreen(); } break; } } addTouchEvents() { let startX = 0; let startY = 0; const container = document.querySelector('.presentation-container'); if (!container) return; container.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; startY = e.touches[0].clientY; }, { passive: true }); container.addEventListener('touchend', (e) => { if (!startX || !startY) return; const endX = e.changedTouches[0].clientX; const endY = e.changedTouches[0].clientY; const deltaX = startX - endX; const deltaY = startY - endY; // Only trigger if horizontal swipe is dominant and significant if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) { if (deltaX > 0) { this.nextSlide(); } else { this.previousSlide(); } } startX = 0; startY = 0; }, { passive: true }); } nextSlide() { if (this.currentSlide < this.totalSlides - 1) { this.goToSlide(this.currentSlide + 1); } } previousSlide() { if (this.currentSlide > 0) { this.goToSlide(this.currentSlide - 1); } } goToSlide(slideIndex) { if (slideIndex >= 0 && slideIndex < this.totalSlides) { console.log(`Going to slide ${slideIndex + 1}`); // Hide current slide this.hideAllSlides(); // Update current slide index this.currentSlide = slideIndex; // Show new slide this.showSlide(slideIndex); // Update UI elements this.updateProgress(); this.updateCounter(); this.updateNavigationButtons(); // Handle slide-specific logic this.handleSlideSpecifics(slideIndex); } } hideAllSlides() { this.slides.forEach(slide => { slide.classList.remove('active', 'animate-in'); }); } showSlide(index) { const slide = this.slides[index]; if (slide) { // Remove active class from all slides first this.hideAllSlides(); // Add active class to current slide slide.classList.add('active'); // Add animation after a brief delay setTimeout(() => { slide.classList.add('animate-in'); this.animateSlideContent(slide); }, 50); } } animateSlideContent(slide) { const elements = slide.querySelectorAll('.bullet-points li, .value-prop, .flow-step, .metric-category, .tech-component, .framework-item, .phase-card, .roi-metric, .challenge-category, .advantage, .timeline-item'); elements.forEach((element, index) => { element.style.opacity = '0'; element.style.transform = 'translateY(20px)'; element.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; setTimeout(() => { element.style.opacity = '1'; element.style.transform = 'translateY(0)'; }, 100 + (index * 100)); }); } updateProgress() { const progressBar = document.querySelector('.progress-indicator'); if (progressBar) { const progress = ((this.currentSlide + 1) / this.totalSlides) * 100; progressBar.style.width = `${progress}%`; } } updateCounter() { const currentSlideEl = document.getElementById('current-slide'); const totalSlidesEl = document.getElementById('total-slides'); if (currentSlideEl) currentSlideEl.textContent = this.currentSlide + 1; if (totalSlidesEl) totalSlidesEl.textContent = this.totalSlides; } updateNavigationButtons() { const prevBtn = document.getElementById('prev-slide'); const nextBtn = document.getElementById('next-slide'); if (prevBtn) { prevBtn.disabled = this.currentSlide === 0; } if (nextBtn) { nextBtn.disabled = this.currentSlide === this.totalSlides - 1; // Update button text for last slide if (this.currentSlide === this.totalSlides - 1) { nextBtn.innerHTML = ` `; nextBtn.setAttribute('aria-label', 'Finish presentation'); } else { nextBtn.innerHTML = ` `; nextBtn.setAttribute('aria-label', 'Next slide'); } } } toggleFullscreen() { if (!this.isFullscreen) { this.enterFullscreen(); } else { this.exitFullscreen(); } } enterFullscreen() { const container = document.querySelector('.presentation-container'); if (container.requestFullscreen) { container.requestFullscreen(); } else if (container.webkitRequestFullscreen) { container.webkitRequestFullscreen(); } else if (container.mozRequestFullScreen) { container.mozRequestFullScreen(); } else if (container.msRequestFullscreen) { container.msRequestFullscreen(); } } exitFullscreen() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } handleFullscreenChange() { const isFullscreen = !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement); this.isFullscreen = isFullscreen; this.updateFullscreenIcon(); const container = document.querySelector('.presentation-container'); if (container) { if (isFullscreen) { container.classList.add('fullscreen'); } else { container.classList.remove('fullscreen'); } } } updateFullscreenIcon() { const icon = document.getElementById('fullscreen-icon'); if (icon) { if (this.isFullscreen) { icon.innerHTML = ` `; } else { icon.innerHTML = ` `; } } } // Method to handle special slide interactions initSlideInteractions() { // Add click handlers for call-to-action buttons const ctaButtons = document.querySelectorAll('.call-to-action .btn'); ctaButtons.forEach(button => { button.addEventListener('click', (e) => { e.preventDefault(); this.handleCTAClick(button.textContent.trim()); }); }); // Add hover effects for interactive elements this.addHoverEffects(); } handleCTAClick(buttonText) { if (buttonText.includes('Schedule')) { this.showNotification('Meeting request sent! Check your calendar for confirmation.'); } else if (buttonText.includes('Download')) { this.showNotification('Strategy document download initiated.'); } } showNotification(message) { // Create notification element const notification = document.createElement('div'); notification.className = 'notification'; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background-color: var(--color-success); color: var(--color-btn-primary-text); padding: 16px 24px; border-radius: 8px; font-size: 14px; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 1001; opacity: 0; transform: translateX(100%); transition: all 0.3s ease; `; notification.textContent = message; document.body.appendChild(notification); // Animate in setTimeout(() => { notification.style.opacity = '1'; notification.style.transform = 'translateX(0)'; }, 100); // Remove after 3 seconds setTimeout(() => { notification.style.opacity = '0'; notification.style.transform = 'translateX(100%)'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, 3000); } addHoverEffects() { // Add hover effects to interactive elements const interactiveElements = document.querySelectorAll('.value-prop, .tech-component, .framework-item, .advantage, .phase-card'); interactiveElements.forEach(element => { element.addEventListener('mouseenter', () => { element.style.transform = 'translateY(-5px)'; element.style.transition = 'transform 0.3s ease, box-shadow 0.3s ease'; element.style.boxShadow = 'var(--shadow-lg)'; }); element.addEventListener('mouseleave', () => { element.style.transform = 'translateY(0)'; element.style.boxShadow = ''; }); }); } // Method to handle slide-specific logic handleSlideSpecifics(slideIndex) { switch(slideIndex) { case 0: // Title slide setTimeout(() => this.animateTitleSlide(), 500); break; case 5: // Customer journey flow setTimeout(() => this.animateFlowDiagram(), 500); break; case 11: // Business impact setTimeout(() => this.animateROIMetrics(), 500); break; case 14: // Conclusion setTimeout(() => this.initSlideInteractions(), 500); break; } } animateTitleSlide() { const highlights = document.querySelectorAll('.highlight'); highlights.forEach((highlight, index) => { highlight.style.opacity = '0'; highlight.style.transform = 'scale(0.8)'; highlight.style.transition = 'all 0.5s ease'; setTimeout(() => { highlight.style.opacity = '1'; highlight.style.transform = 'scale(1)'; }, index * 200); }); } animateFlowDiagram() { const steps = document.querySelectorAll('.flow-step'); steps.forEach((step, index) => { step.style.opacity = '0'; step.style.transform = 'translateX(-50px)'; step.style.transition = 'all 0.5s ease'; setTimeout(() => { step.style.opacity = '1'; step.style.transform = 'translateX(0)'; }, index * 300); }); } animateROIMetrics() { const metrics = document.querySelectorAll('.roi-metric h3'); metrics.forEach((metric, index) => { const finalValue = metric.textContent; const isPercentage = finalValue.includes('%'); const numericValue = parseInt(finalValue.replace(/[^\d]/g, '')); if (numericValue > 0) { metric.textContent = isPercentage ? '0%' : '0'; setTimeout(() => { this.animateNumber(metric, finalValue, numericValue, isPercentage); }, index * 200); } }); } animateNumber(element, finalValue, numericValue, isPercentage) { let currentValue = 0; const increment = Math.ceil(numericValue / 30); const timer = setInterval(() => { currentValue += increment; if (currentValue >= numericValue) { element.textContent = finalValue; clearInterval(timer); } else { element.textContent = isPercentage ? `${currentValue}%` : `${currentValue}`; } }, 50); } } // Initialize presentation when DOM is loaded document.addEventListener('DOMContentLoaded', () => { console.log('DOM loaded, initializing presentation...'); window.presentation = new Presentation(); }); // Fallback initialization for cases where DOMContentLoaded already fired if (document.readyState !== 'loading') { console.log('DOM already loaded, initializing presentation...'); window.presentation = new Presentation(); } // Add global error handling window.addEventListener('error', (e) => { console.error('Presentation error:', e.error); }); // Add performance optimization window.addEventListener('load', () => { console.log('Window loaded, optimizing...'); // Preload images const images = document.querySelectorAll('img[src]'); images.forEach(img => { const link = document.createElement('link'); link.rel = 'prefetch'; link.href = img.src; document.head.appendChild(link); }); }); // Add visibility change handling for presentations document.addEventListener('visibilitychange', () => { if (document.hidden) { // Pause any ongoing animations when tab is not visible document.querySelectorAll('.animate-in').forEach(el => { if (el.style.animationPlayState !== undefined) { el.style.animationPlayState = 'paused'; } }); } else { // Resume animations when tab becomes visible document.querySelectorAll('.animate-in').forEach(el => { if (el.style.animationPlayState !== undefined) { el.style.animationPlayState = 'running'; } }); } }); // Export for potential external use window.PresentationApp = Presentation;