// =============================== // MODALE FOTO/VIDEO — Navigazione + Info Panel + Preload // =============================== const modal = document.getElementById("modal"); const modalClose = document.getElementById("modalClose"); const modalPrev = document.getElementById("modalPrev"); const modalNext = document.getElementById("modalNext"); window.currentPhoto = null; window.modalList = []; window.modalIndex = 0; // =============================== // INFO PANEL — Stato + Toggle // =============================== let infoOpen = false; function getInfoPanel() { return document.getElementById("infoPanel"); } function isInfoOpen() { return infoOpen; } function openInfo(photo) { try { if (typeof window.openInfoPanel === "function") { window.openInfoPanel(photo); } else if (typeof window.toggleInfoPanel === "function") { window.toggleInfoPanel(photo); } } catch {} infoOpen = true; const panel = getInfoPanel(); panel?.classList.add("open"); panel?.setAttribute("aria-hidden", "false"); panel?.setAttribute("data-open", "1"); document.getElementById("modalInfoBtn")?.classList.add("active"); } function closeInfo() { try { if (typeof window.closeInfoPanel === "function") { window.closeInfoPanel(); } else if (typeof window.toggleInfoPanel === "function") { window.toggleInfoPanel(); } } catch {} infoOpen = false; const panel = getInfoPanel(); panel?.classList.remove("open"); panel?.setAttribute("aria-hidden", "true"); panel?.setAttribute("data-open", "0"); document.getElementById("modalInfoBtn")?.classList.remove("active"); } function toggleInfo(photo) { if (infoOpen) closeInfo(); else openInfo(photo); } // =============================== // MIME / MEDIA HELPERS // =============================== function isProbablyVideo(photo, srcOriginal) { const mime = String(photo?.mime_type || "").toLowerCase(); if (mime.startsWith("video/")) return true; return /\.(mp4|m4v|webm|mov|qt|avi|mkv)$/i.test(String(srcOriginal || "")); } function guessVideoMime(photo, srcOriginal) { const t = String(photo?.mime_type || "").toLowerCase(); if (t && t !== "application/octet-stream") return t; const src = String(srcOriginal || ""); if (/\.(mp4|m4v)$/i.test(src)) return "video/mp4"; if (/\.(webm)$/i.test(src)) return "video/webm"; if (/\.(mov|qt)$/i.test(src)) return "video/quicktime"; if (/\.(avi)$/i.test(src)) return "video/x-msvideo"; if (/\.(mkv)$/i.test(src)) return "video/x-matroska"; return ""; } function createVideoElement(srcOriginal, srcPreview, photo) { const video = document.createElement("video"); video.controls = true; video.playsInline = true; video.setAttribute("webkit-playsinline", ""); video.preload = "metadata"; video.poster = srcPreview || ""; video.style.maxWidth = "100%"; video.style.maxHeight = "100%"; video.style.objectFit = "contain"; const source = document.createElement("source"); source.src = srcOriginal; const type = guessVideoMime(photo, srcOriginal); if (type) source.type = type; video.appendChild(source); video.addEventListener("loadedmetadata", () => { try { video.currentTime = 0.001; } catch {} }); video.addEventListener("error", () => { const code = video.error?.code; const msg = document.createElement("div"); msg.style.padding = "12px"; msg.style.color = "#fff"; msg.style.background = "rgba(0,0,0,0.6)"; msg.style.borderRadius = "8px"; msg.innerHTML = ` Impossibile riprodurre questo video nel browser. ${code === 4 ? "Formato/codec non supportato (es. HEVC/H.265)." : "Errore durante il caricamento."}

`; document.getElementById("modalMediaContainer")?.appendChild(msg); }); video.addEventListener("click", e => e.stopPropagation()); return video; } function createImageElement(srcOriginal, srcPreview) { const img = document.createElement("img"); img.src = srcPreview || srcOriginal || ""; img.style.maxWidth = "100%"; img.style.maxHeight = "100%"; img.style.objectFit = "contain"; if (srcPreview && srcOriginal && srcPreview !== srcOriginal) { const full = new Image(); full.src = srcOriginal; full.onload = () => { img.src = srcOriginal; }; } return img; } // =============================== // URL ASSOLUTI // =============================== function mediaUrlsFromPhoto(photo) { if (!photo) return { original: "", preview: "" }; if (window.PATH_FULL) { return { original: photo.path, preview: photo.thub2 || photo.thub1 || photo.path }; } const original = toAbsoluteUrl( photo.path, photo.name, "original", photo.cartella ); const preview = toAbsoluteUrl( photo.thub2 || photo.thub1 || photo.path, photo.name, "thumbs", photo.cartella ); return { original, preview }; } // =============================== // PRELOAD ±N // =============================== function preloadNeighbors(N = 3) { const list = window.modalList || []; const idx = window.modalIndex || 0; for (let offset = 1; offset <= N; offset++) { const iPrev = idx - offset; const iNext = idx + offset; [iPrev, iNext].forEach(i => { const p = list[i]; if (!p) return; const { original, preview } = mediaUrlsFromPhoto(p); const isVideo = isProbablyVideo(p, original); const src = isVideo ? (preview || original) : original; if (!src) return; const img = new Image(); img.src = src; }); } } // =============================== // SET CONTENUTO MODAL // =============================== function setModalContent(photo) { const container = document.getElementById("modalMediaContainer"); container.innerHTML = ""; window.currentPhoto = photo; const { original, preview } = mediaUrlsFromPhoto(photo); const isVideo = isProbablyVideo(photo, original); if (isVideo) { container.appendChild(createVideoElement(original, preview, photo)); } else { container.appendChild(createImageElement(original, preview)); } const infoBtn = document.createElement("button"); infoBtn.id = "modalInfoBtn"; infoBtn.className = "modal-info-btn"; infoBtn.type = "button"; infoBtn.setAttribute("aria-label", "Dettagli"); infoBtn.textContent = "ℹ️"; infoBtn.addEventListener("click", e => { e.stopPropagation(); toggleInfo(photo); }); container.appendChild(infoBtn); } // =============================== // OPEN / CLOSE MODAL // =============================== function openModal(photo) { window.closeBottomSheet?.(); setModalContent(photo); modal.classList.add("open"); modal.setAttribute("aria-hidden", "false"); document.body.style.overflow = "hidden"; } function closeModal() { if (isInfoOpen()) closeInfo(); const v = document.querySelector("#modal video"); if (v) { try { v.pause(); } catch {} v.removeAttribute("src"); while (v.firstChild) v.removeChild(v.firstChild); try { v.load(); } catch {} } document.getElementById("modalMediaContainer").innerHTML = ""; modal.classList.remove("open"); modal.setAttribute("aria-hidden", "true"); document.body.style.overflow = ""; modalPrev?.classList.add("hidden"); modalNext?.classList.add("hidden"); } modalClose?.addEventListener("click", e => { e.stopPropagation(); closeModal(); }); modal.addEventListener("click", e => { if (e.target === modal) closeModal(); }); // =============================== // NAVIGAZIONE // =============================== function openAt(i) { const list = window.modalList || []; if (!list[i]) return; window.modalIndex = i; const photo = list[i]; if (isInfoOpen()) openInfo(photo); openModal(photo); preloadNeighbors(3); updateArrows(); } window.openModalFromList = function(list, index) { window.modalList = Array.isArray(list) ? list : []; window.modalIndex = Math.max(0, Math.min(index || 0, window.modalList.length - 1)); openAt(window.modalIndex); }; function showPrev() { if (window.modalIndex > 0) openAt(window.modalIndex - 1); } function showNext() { if (window.modalIndex < (window.modalList.length - 1)) openAt(window.modalIndex + 1); } document.addEventListener("keydown", e => { if (!modal.classList.contains("open")) return; if (e.key === "ArrowLeft") { e.preventDefault(); showPrev(); } if (e.key === "ArrowRight") { e.preventDefault(); showNext(); } }); modal.addEventListener("click", e => { if (!modal.classList.contains("open")) return; if (e.target.closest(".modal-info-btn, .modal-close, .modal-nav-btn")) return; if (e.target === modal) return; const rect = modal.getBoundingClientRect(); const x = e.clientX - rect.left; const side = x / rect.width; if (side < 0.25) showPrev(); else if (side > 0.75) showNext(); }); // =============================== // FRECCE // =============================== function updateArrows() { if (!modalPrev || !modalNext) return; const len = (window.modalList || []).length; const i = window.modalIndex || 0; const show = len > 1; modalPrev.classList.toggle("hidden", !show); modalNext.classList.toggle("hidden", !show); modalPrev.classList.toggle("disabled", i <= 0); modalNext.classList.toggle("disabled", i >= len - 1); } modalPrev?.addEventListener("click", e => { e.stopPropagation(); showPrev(); updateArrows(); }); modalNext?.addEventListener("click", e => { e.stopPropagation(); showNext(); updateArrows(); }); // EXPORT window.openModal = openModal; window.closeModal = closeModal;