/**
* 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 = [];
}