// ===============================
// 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;