/** * Lightbox (Gallery Modal) - Modular component for image viewing * Supports galleries from both project metadata and scanned images */ let galleryImages = []; let scannedImages = []; let videoItems = []; let currentLightboxIndex = 0; let currentGallery = ''; /** * Open lightbox for project gallery images * @param {number} index - Index of image to display */ function openLightboxFromGallery(index) { currentGallery = 'gallery'; if (galleryImages.length === 0) { const items = document.querySelectorAll('.project-gallery .gallery-item'); galleryImages = Array.from(items).map(item => ({ url: item.dataset.src, alt: item.dataset.alt || '' })); } currentLightboxIndex = index; showLightbox(); } /** * Open lightbox for scanned images * @param {number} index - Index of image to display */ function openLightboxFromScanned(index) { currentGallery = 'scanned'; if (scannedImages.length === 0) { const items = document.querySelectorAll('.scanned-gallery .gallery-item'); scannedImages = Array.from(items).map(item => ({ url: item.dataset.src, alt: item.querySelector('img').alt || '' })); } currentLightboxIndex = index; showLightbox(); } /** * Open lightbox for videos (YouTube embed) * @param {number} index - Index of video in the gallery */ function openLightboxFromVideo(index) { currentGallery = 'video'; currentLightboxIndex = index; // Initialize video items array if empty if (videoItems.length === 0) { const items = document.querySelectorAll('.video-section .video-item'); videoItems = Array.from(items).map(item => ({ url: item.dataset.videoUrl, title: item.dataset.videoTitle })); } showLightboxVideo(); } /** * Display the video in lightbox */ function showLightboxVideo() { const video = videoItems[currentLightboxIndex]; const lightbox = document.getElementById('lightbox'); const content = document.querySelector('.lightbox-content'); // Clear any existing video const existingVideo = content.querySelector('.lightbox-video'); if (existingVideo) { existingVideo.remove(); } // Build embed URL let embedUrl = video.url; if (video.url.includes('youtube.com/shorts/')) { const vid = video.url.split('/shorts/')[1].split('?')[0]; embedUrl = `https://www.youtube.com/embed/${vid}`; } else if (video.url.includes('youtube.com/watch')) { const urlObj = new URL(video.url); const vid = urlObj.searchParams.get('v'); if (vid) { embedUrl = `https://www.youtube.com/embed/${vid}`; } } else if (!video.url.includes('http')) { // It's already a video ID embedUrl = `https://www.youtube.com/embed/${video.url}`; } const videoDiv = document.createElement('div'); videoDiv.className = 'lightbox-video'; videoDiv.innerHTML = ``; // Add video to lightbox-media div const mediaDiv = content.querySelector('.lightbox-media'); if (mediaDiv) { mediaDiv.insertBefore(videoDiv, mediaDiv.firstChild); } else { content.insertBefore(videoDiv, content.firstChild); } // Clear image elements document.getElementById('lightbox-img').src = ''; document.getElementById('lightbox-img').style.display = 'none'; document.getElementById('lightbox-caption').textContent = video.title || ''; document.getElementById('lightbox-counter').textContent = (currentLightboxIndex + 1) + ' / ' + videoItems.length; // Populate metadata sidebar for video document.getElementById('lightbox-description').textContent = video.title || 'No description available'; document.getElementById('lightbox-metadata').innerHTML = ` Source: Video
Platform: YouTube `; lightbox.classList.add('visible'); lightbox.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; } /** * Display the lightbox with current image */ function showLightbox() { const images = currentGallery === 'gallery' ? galleryImages : scannedImages; const img = images[currentLightboxIndex]; // Hide video element const videoEl = document.querySelector('.lightbox-video'); if (videoEl) videoEl.remove(); // Show image const imgEl = document.getElementById('lightbox-img'); imgEl.src = img.url; imgEl.style.display = 'block'; document.getElementById('lightbox-caption').textContent = img.alt || ''; document.getElementById('lightbox-counter').textContent = (currentLightboxIndex + 1) + ' / ' + images.length; // Populate metadata sidebar document.getElementById('lightbox-description').textContent = img.alt ? img.alt : 'No description available'; document.getElementById('lightbox-metadata').innerHTML = ` Source: ${currentGallery === 'gallery' ? 'Gallery' : 'Scanned Images'}
File: ${img.url.split('/').pop()} `; const lightbox = document.getElementById('lightbox'); lightbox.classList.add('visible'); lightbox.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; } /** * Close the lightbox */ function closeLightbox() { const lightbox = document.getElementById('lightbox'); lightbox.classList.remove('visible'); lightbox.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; // Remove video from lightbox when closing const video = document.querySelector('.lightbox-video'); if (video) { video.remove(); } // Show image element again const imgEl = document.getElementById('lightbox-img'); imgEl.style.display = 'block'; } /** * Show next image/video in gallery */ function nextImage() { if (currentGallery === 'video') { currentLightboxIndex = (currentLightboxIndex + 1) % videoItems.length; showLightboxVideo(); } else { const images = currentGallery === 'gallery' ? galleryImages : scannedImages; currentLightboxIndex = (currentLightboxIndex + 1) % images.length; showLightbox(); } } /** * Show previous image/video in gallery */ function prevImage() { if (currentGallery === 'video') { currentLightboxIndex = (currentLightboxIndex - 1 + videoItems.length) % videoItems.length; showLightboxVideo(); } else { const images = currentGallery === 'gallery' ? galleryImages : scannedImages; currentLightboxIndex = (currentLightboxIndex - 1 + images.length) % images.length; showLightbox(); } } /** * Handle keyboard navigation */ document.addEventListener('keydown', function(e) { const lightbox = document.getElementById('lightbox'); if (!lightbox || !lightbox.classList.contains('visible')) return; switch(e.key) { case 'Escape': closeLightbox(); break; case 'ArrowRight': nextImage(); break; case 'ArrowLeft': prevImage(); break; } }); /** * Handle click outside image to close */ document.addEventListener('click', function(e) { const lightbox = document.getElementById('lightbox'); if (e.target === lightbox) { closeLightbox(); } }); /** * Cleanup on gallery change (optional, for dynamic content) */ function resetLightboxData() { galleryImages = []; scannedImages = []; videoItems = []; }