class PresentationApp { constructor() { this.currentSlide = 1; this.totalSlides = 26; this.isFullscreen = false; this.autoAdvance = false; this.autoAdvanceInterval = null; this.touchStartX = 0; this.touchStartY = 0; this.init(); } init() { this.setupElements(); this.setupEventListeners(); this.updateDisplay(); this.preloadSlides(); } setupElements() { this.slidesContainer = document.getElementById('slidesContainer'); this.slides = document.querySelectorAll('.slide'); this.prevBtn = document.getElementById('prevBtn'); this.nextBtn = document.getElementById('nextBtn'); this.currentSlideSpan = document.getElementById('currentSlide'); this.totalSlidesSpan = document.getElementById('totalSlides'); this.progressFill = document.querySelector('.progress-fill'); this.fullscreenBtn = document.getElementById('fullscreenBtn'); this.overviewBtn = document.getElementById('overviewBtn'); // Update total slides display this.totalSlidesSpan.textContent = this.totalSlides; } setupEventListeners() { // Navigation buttons this.prevBtn.addEventListener('click', () => this.previousSlide()); this.nextBtn.addEventListener('click', () => this.nextSlide()); // Keyboard navigation document.addEventListener('keydown', (e) => { switch(e.key) { case 'ArrowRight': case ' ': case 'PageDown': e.preventDefault(); this.nextSlide(); break; case 'ArrowLeft': case 'PageUp': e.preventDefault(); this.previousSlide(); break; case 'Home': e.preventDefault(); this.goToSlide(1); break; case 'End': e.preventDefault(); this.goToSlide(this.totalSlides); break; case 'Escape': if (this.isFullscreen) { this.exitFullscreen(); } break; case 'f': case 'F': this.toggleFullscreen(); break; } }); // Click to advance this.slidesContainer.addEventListener('click', (e) => { // Don't advance if clicking on interactive elements if (e.target.tagName === 'BUTTON' || e.target.closest('button')) { return; } this.nextSlide(); }); // Touch/swipe support this.slidesContainer.addEventListener('touchstart', (e) => { this.touchStartX = e.touches[0].clientX; this.touchStartY = e.touches[0].clientY; }, { passive: true }); this.slidesContainer.addEventListener('touchend', (e) => { if (!this.touchStartX || !this.touchStartY) return; const touchEndX = e.changedTouches[0].clientX; const touchEndY = e.changedTouches[0].clientY; const deltaX = this.touchStartX - touchEndX; const deltaY = this.touchStartY - touchEndY; // Check if horizontal swipe is more significant than vertical if (Math.abs(deltaX) > Math.abs(deltaY)) { if (Math.abs(deltaX) > 50) { // Minimum swipe distance if (deltaX > 0) { this.nextSlide(); } else { this.previousSlide(); } } } this.touchStartX = 0; this.touchStartY = 0; }, { passive: true }); // Control buttons this.fullscreenBtn.addEventListener('click', () => this.toggleFullscreen()); this.overviewBtn.addEventListener('click', () => this.showOverview()); // Fullscreen change events document.addEventListener('fullscreenchange', () => { this.isFullscreen = !this.isFullscreen; this.updateFullscreenButton(); }); // Prevent context menu on right click document.addEventListener('contextmenu', (e) => { e.preventDefault(); }); // Handle window resize window.addEventListener('resize', () => { this.handleResize(); }); } preloadSlides() { // Add entrance animations to bullet points this.slides.forEach((slide, index) => { const bullets = slide.querySelectorAll('.bullet-list li'); bullets.forEach((bullet, bulletIndex) => { bullet.style.animationDelay = `${(bulletIndex + 1) * 0.1}s`; }); }); } nextSlide() { if (this.currentSlide < this.totalSlides) { this.goToSlide(this.currentSlide + 1); } } previousSlide() { if (this.currentSlide > 1) { this.goToSlide(this.currentSlide - 1); } } goToSlide(slideNumber) { if (slideNumber < 1 || slideNumber > this.totalSlides) return; // Remove active class from current slide const currentSlideElement = document.querySelector('.slide.active'); if (currentSlideElement) { currentSlideElement.classList.remove('active'); currentSlideElement.classList.add('prev'); } // Add active class to new slide const newSlideElement = document.querySelector(`[data-slide="${slideNumber}"]`); if (newSlideElement) { // Remove prev class from all slides this.slides.forEach(slide => slide.classList.remove('prev')); newSlideElement.classList.add('active'); // Trigger entrance animations this.triggerSlideAnimations(newSlideElement); } this.currentSlide = slideNumber; this.updateDisplay(); } triggerSlideAnimations(slideElement) { // Reset and trigger bullet point animations const bullets = slideElement.querySelectorAll('.bullet-list li'); bullets.forEach((bullet, index) => { bullet.style.animation = 'none'; bullet.offsetHeight; // Trigger reflow bullet.style.animation = `fadeInUp 0.6s ease-out ${(index + 1) * 0.1}s both`; }); // Trigger other element animations const animatedElements = slideElement.querySelectorAll('.aspect-card, .transform-item, .mode-card'); animatedElements.forEach((element, index) => { element.style.transform = 'translateY(20px)'; element.style.opacity = '0'; setTimeout(() => { element.style.transition = 'all 0.6s ease-out'; element.style.transform = 'translateY(0)'; element.style.opacity = '1'; }, (index + 1) * 100); }); } updateDisplay() { // Update slide counter this.currentSlideSpan.textContent = this.currentSlide; // Update progress bar const progress = (this.currentSlide / this.totalSlides) * 100; this.progressFill.style.width = `${progress}%`; // Update navigation buttons this.prevBtn.disabled = this.currentSlide === 1; this.nextBtn.disabled = this.currentSlide === this.totalSlides; // Update button opacity based on state this.prevBtn.style.opacity = this.currentSlide === 1 ? '0.5' : '1'; this.nextBtn.style.opacity = this.currentSlide === this.totalSlides ? '0.5' : '1'; } toggleFullscreen() { if (!this.isFullscreen) { this.enterFullscreen(); } else { this.exitFullscreen(); } } enterFullscreen() { const element = document.documentElement; if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.mozRequestFullScreen) { element.mozRequestFullScreen(); } else if (element.webkitRequestFullscreen) { element.webkitRequestFullscreen(); } else if (element.msRequestFullscreen) { element.msRequestFullscreen(); } } exitFullscreen() { if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitExitFullscreen) { document.webkitExitFullscreen(); } else if (document.msExitFullscreen) { document.msExitFullscreen(); } } updateFullscreenButton() { this.fullscreenBtn.textContent = this.isFullscreen ? '⛶' : '⛶'; this.fullscreenBtn.title = this.isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'; } showOverview() { // Create overview modal const modal = document.createElement('div'); modal.className = 'overview-modal'; modal.innerHTML = `

Slide Overview

${this.generateOverviewThumbnails()}
`; // Add modal styles const style = document.createElement('style'); style.textContent = ` .overview-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; } .overview-content { background: white; border-radius: 15px; padding: 30px; max-width: 1200px; max-height: 90vh; overflow-y: auto; width: 100%; } .overview-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 30px; border-bottom: 2px solid #3498db; padding-bottom: 15px; } .overview-header h2 { color: #2c3e50; margin: 0; } .close-overview { background: #e74c3c; color: white; border: none; width: 35px; height: 35px; border-radius: 50%; font-size: 20px; cursor: pointer; display: flex; align-items: center; justify-content: center; } .close-overview:hover { background: #c0392b; } .overview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 20px; } .overview-slide { background: #f8f9fa; border: 2px solid #e9ecef; border-radius: 10px; padding: 15px; cursor: pointer; transition: all 0.3s ease; text-align: center; } .overview-slide:hover { border-color: #3498db; transform: translateY(-5px); box-shadow: 0 10px 30px rgba(52, 152, 219, 0.3); } .overview-slide.current { border-color: #27ae60; background: rgba(39, 174, 96, 0.1); } .overview-slide-number { background: #3498db; color: white; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; font-weight: bold; margin: 0 auto 10px; } .overview-slide.current .overview-slide-number { background: #27ae60; } .overview-slide-title { font-size: 0.9rem; color: #2c3e50; font-weight: 600; line-height: 1.3; } `; document.head.appendChild(style); document.body.appendChild(modal); // Add event listeners modal.querySelector('.close-overview').addEventListener('click', () => { document.body.removeChild(modal); document.head.removeChild(style); }); modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); document.head.removeChild(style); } }); // Add slide navigation modal.querySelectorAll('.overview-slide').forEach((slide, index) => { slide.addEventListener('click', () => { this.goToSlide(index + 1); document.body.removeChild(modal); document.head.removeChild(style); }); }); } generateOverviewThumbnails() { const slideTitles = [ "Title Slide", "Abstract", "Introduction: AI in Creation", "Theological Foundations", "The Language of Imago Dei", "Teilhard's Digital Noosphere", "The Two Books and Digital Code", "AI as a Semantic System", "Semantic Grounding", "Artificial Consciousness", "Language Bridge", "Behavioral Transformations", "Research Changes", "Philosophical Reasoning", "AI-Assisted Theology", "Ethical Dimensions", "Church's Magisterium", "Ethics of Algorithms", "Theological Governance", "Conclusions", "Theology for Digital Age", "Gift and Responsibility", "Toward Digital Hope", "Questions and Answers", "Sample Q&A", "Continue the Dialogue" ]; return slideTitles.map((title, index) => { const slideNumber = index + 1; const currentClass = slideNumber === this.currentSlide ? 'current' : ''; return `
${slideNumber}
${title}
`; }).join(''); } handleResize() { // Adjust layout for different screen sizes const slideContents = document.querySelectorAll('.slide-content'); slideContents.forEach(content => { if (window.innerWidth < 768) { content.style.fontSize = '0.9rem'; } else { content.style.fontSize = ''; } }); } // Auto-advance functionality startAutoAdvance(intervalSeconds = 30) { this.stopAutoAdvance(); this.autoAdvance = true; this.autoAdvanceInterval = setInterval(() => { if (this.currentSlide < this.totalSlides) { this.nextSlide(); } else { this.stopAutoAdvance(); } }, intervalSeconds * 1000); } stopAutoAdvance() { if (this.autoAdvanceInterval) { clearInterval(this.autoAdvanceInterval); this.autoAdvanceInterval = null; } this.autoAdvance = false; } // Utility methods addCustomEventListeners() { // Add number key navigation document.addEventListener('keydown', (e) => { if (e.key >= '1' && e.key <= '9') { const slideNumber = parseInt(e.key); if (slideNumber <= this.totalSlides) { this.goToSlide(slideNumber); } } }); // Add mouse wheel navigation document.addEventListener('wheel', (e) => { if (Math.abs(e.deltaY) > 50) { if (e.deltaY > 0) { this.nextSlide(); } else { this.previousSlide(); } } }, { passive: true }); } // Initialize additional features initializeAdvancedFeatures() { this.addCustomEventListeners(); // Add presentation timer this.startTime = Date.now(); // Add slide visit tracking this.slideVisits = new Array(this.totalSlides).fill(0); this.slideVisits[0] = 1; // First slide visited // Update visit count when slide changes const originalGoToSlide = this.goToSlide.bind(this); this.goToSlide = function(slideNumber) { originalGoToSlide(slideNumber); if (slideNumber >= 1 && slideNumber <= this.totalSlides) { this.slideVisits[slideNumber - 1]++; } }; } // Get presentation statistics getPresentationStats() { const currentTime = Date.now(); const duration = Math.floor((currentTime - this.startTime) / 1000); const minutes = Math.floor(duration / 60); const seconds = duration % 60; return { duration: `${minutes}:${seconds.toString().padStart(2, '0')}`, currentSlide: this.currentSlide, totalSlides: this.totalSlides, progress: `${Math.round((this.currentSlide / this.totalSlides) * 100)}%`, slideVisits: this.slideVisits }; } } // Initialize the presentation when DOM is loaded document.addEventListener('DOMContentLoaded', () => { const presentation = new PresentationApp(); presentation.initializeAdvancedFeatures(); // Expose to global scope for debugging window.presentation = presentation; // Add some helpful console commands console.log('Presentation loaded! Available commands:'); console.log('- presentation.goToSlide(n) - Go to slide n'); console.log('- presentation.nextSlide() - Next slide'); console.log('- presentation.previousSlide() - Previous slide'); console.log('- presentation.toggleFullscreen() - Toggle fullscreen'); console.log('- presentation.showOverview() - Show slide overview'); console.log('- presentation.startAutoAdvance(seconds) - Start auto-advance'); console.log('- presentation.stopAutoAdvance() - Stop auto-advance'); console.log('- presentation.getPresentationStats() - Get presentation statistics'); }); // Add some additional utility functions window.addEventListener('load', () => { // Smooth scroll behavior for any internal links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth' }); } }); }); // Add loading animation completion setTimeout(() => { document.body.classList.add('loaded'); }, 100); }); // Export for potential module use if (typeof module !== 'undefined' && module.exports) { module.exports = PresentationApp; }