// Assume that currentGalleryImages is already declared in app.js. // Only declare currentGalleryIndex if it isn't defined already. if (typeof currentGalleryIndex === "undefined") { var currentGalleryIndex = -1; } function openGalleryModal(relUrl) { document.body.style.overflow = 'hidden'; // Disable background scrolling. currentGalleryIndex = currentGalleryImages.indexOf(relUrl); showGalleryImage(relUrl); document.getElementById('gallery-modal').style.display = 'flex'; } function showGalleryImage(relUrl) { const fullUrl = '/media/' + relUrl; const modal = document.getElementById('gallery-modal'); const currentImage = document.getElementById('gallery-modal-content'); const loader = document.getElementById('loader-container'); const closeBtn = document.getElementById('gallery-close'); // Set a timeout for showing the loader after 500ms. let loaderTimeout = setTimeout(() => { loader.style.display = 'flex'; }, 500); // Preload the new image. const tempImg = new Image(); tempImg.src = fullUrl; tempImg.onload = function () { clearTimeout(loaderTimeout); loader.style.display = 'none'; // Select all current images in the gallery modal const existingImages = document.querySelectorAll('#gallery-modal img'); // Create a new image element for the transition const newImage = document.createElement('img'); newImage.src = fullUrl; newImage.style.position = 'absolute'; newImage.style.top = '50%'; // Center vertically newImage.style.left = '50%'; // Center horizontally newImage.style.transform = 'translate(-50%, -50%)'; // Ensure centering newImage.style.maxWidth = '95vw'; // Prevent overflow on large screens newImage.style.maxHeight = '95vh'; // Prevent overflow on tall screens newImage.style.transition = 'opacity 0.5s ease-in-out'; newImage.style.opacity = 0; // Start fully transparent newImage.id = 'gallery-modal-content'; // Append new image on top of existing ones modal.appendChild(newImage); // Fade out all old images and fade in the new one setTimeout(() => { existingImages.forEach(img => { img.style.opacity = 0; // Apply fade-out }); newImage.style.opacity = 1; // Fade in the new image }, 10); // Wait for fade-out to finish, then remove all old images setTimeout(() => { existingImages.forEach(img => { if (img.parentNode) { img.parentNode.removeChild(img); // Remove old images } }); }, 510); // 500ms for fade-out + small buffer // Preload the next image preloadNextImage(); }; tempImg.onerror = function() { clearTimeout(loaderTimeout); loader.style.display = 'none'; console.error('Error loading image:', fullUrl); }; } function preloadNextImage() { if (currentGalleryIndex < currentGalleryImages.length - 1) { const nextImage = new Image(); nextImage.src = '/media/' + currentGalleryImages[currentGalleryIndex + 1]; } } function closeGalleryModal() { document.getElementById('gallery-modal').style.display = 'none'; // remove all images const existingImages = document.querySelectorAll('#gallery-modal img'); existingImages.forEach(img => { if (img.parentNode) { img.parentNode.removeChild(img); } }); // Restore scrolling. document.body.style.overflow = ''; } function handleGalleryImageClick(e) { if (e.target.classList.contains('gallery-next') || e.target.classList.contains('gallery-prev')) { return; // Prevent conflict } const imgRect = this.getBoundingClientRect(); if (e.clientX < imgRect.left + imgRect.width / 2) { if (currentGalleryIndex > 0) { currentGalleryIndex--; showGalleryImage(currentGalleryImages[currentGalleryIndex]); } } else { if (currentGalleryIndex < currentGalleryImages.length - 1) { currentGalleryIndex++; showGalleryImage(currentGalleryImages[currentGalleryIndex]); } } } function initGallerySwipe() { let touchStartX = 0; let touchEndX = 0; const galleryModal = document.getElementById('gallery-modal'); galleryModal.addEventListener('touchstart', function(e) { touchStartX = e.changedTouches[0].screenX; }, false); galleryModal.addEventListener('touchend', function(e) { touchEndX = e.changedTouches[0].screenX; handleSwipe(); }, false); function handleSwipe() { const deltaX = touchEndX - touchStartX; if (Math.abs(deltaX) > 50) { // Swipe threshold. if (deltaX > 0) { // Swipe right: previous image. if (currentGalleryIndex > 0) { currentGalleryIndex--; showGalleryImage(currentGalleryImages[currentGalleryIndex]); } } else { // Swipe left: next image. if (currentGalleryIndex < currentGalleryImages.length - 1) { currentGalleryIndex++; showGalleryImage(currentGalleryImages[currentGalleryIndex]); } } } } } function initGalleryControls() { const nextButton = document.querySelector('.gallery-next'); const prevButton = document.querySelector('.gallery-prev'); const closeButton = document.getElementById('gallery-close'); if (nextButton) { nextButton.addEventListener('click', function () { if (currentGalleryIndex < currentGalleryImages.length - 1) { currentGalleryIndex++; showGalleryImage(currentGalleryImages[currentGalleryIndex]); } }); } if (prevButton) { prevButton.addEventListener('click', function () { if (currentGalleryIndex > 0) { currentGalleryIndex--; showGalleryImage(currentGalleryImages[currentGalleryIndex]); } }); } if (closeButton) { closeButton.addEventListener('click', closeGalleryModal); } } function initGallery() { const galleryImage = document.getElementById('gallery-modal-content'); if (galleryImage) { galleryImage.addEventListener('click', handleGalleryImageClick); } initGallerySwipe(); initGalleryControls(); // Close modal when clicking outside the image. const galleryModal = document.getElementById('gallery-modal'); galleryModal.addEventListener('click', function(e) { if (e.target === galleryModal) { closeGalleryModal(); } }); } // Initialize the gallery once the DOM content is loaded. if (document.readyState === "loading") { document.addEventListener('DOMContentLoaded', initGallery); } else { initGallery(); }