import { getImageUrl } from "./firebaseImages.js";
let modal = null;
let modalImage = null;
let teller = null;
let vorigeKnop = null;
let volgendeKnop = null;
let groep = []; // alle thumbnails van het album waarin je klikte
let index = 0; // welke foto nu getoond wordt
let isOpen = false;
// Eigen stijl, omdat jouw style.css van elke .btn 100% breed maakt.
const PIJL_STIJL = `
position:absolute; top:50%; transform:translateY(-50%); z-index:5;
width:44px; height:44px; border:0; border-radius:50%;
background:rgba(0,0,0,.55); color:#fff; font-size:2rem; line-height:1;
padding:0 0 4px 0; display:flex; align-items:center; justify-content:center;
cursor:pointer;
`;
// De modal wordt pas gemaakt bij de eerste klik. Pagina's hoeven er dus geen HTML voor te hebben.
function ensureModal() {
if (modal) return;
const element = document.createElement("div");
element.className = "modal fade";
element.tabIndex = -1;
element.setAttribute("aria-hidden", "true");
element.innerHTML = `
`;
document.body.appendChild(element);
modalImage = element.querySelector("img");
teller = element.querySelector(".badge");
vorigeKnop = element.querySelector(".lightbox-vorige");
volgendeKnop = element.querySelector(".lightbox-volgende");
modal = new bootstrap.Modal(element);
vorigeKnop.addEventListener("click", () => toon(index - 1));
volgendeKnop.addEventListener("click", () => toon(index + 1));
element.addEventListener("shown.bs.modal", () => { isOpen = true; });
element.addEventListener("hidden.bs.modal", () => {
isOpen = false;
modalImage.removeAttribute("src"); // volgende keer niet even de vorige foto tonen
});
// Pijltjestoetsen op het toetsenbord
document.addEventListener("keydown", event => {
if (!isOpen) return;
if (event.key === "ArrowLeft") toon(index - 1);
if (event.key === "ArrowRight") toon(index + 1);
});
// Vegen op gsm
const body = element.querySelector(".modal-body");
let startX = null;
body.addEventListener("touchstart", event => {
startX = event.changedTouches[0].clientX;
}, { passive: true });
body.addEventListener("touchend", event => {
if (startX === null) return;
const verschil = event.changedTouches[0].clientX - startX;
startX = null;
if (Math.abs(verschil) > 50) {
toon(index + (verschil < 0 ? 1 : -1));
}
});
}
/*
* Toont één foto in twee stappen:
* 1. de thumbnail meteen, uitvergroot. Is die op de pagina nog niet geladen
* (bv. omdat je er nog niet naartoe gescrold bent), dan halen we hem zelf op;
* 2. de grote versie op de achtergrond laden en ertussen wisselen zodra ze klaar is.
*/
function toonFoto(thumbnail) {
const { album, foto } = thumbnail.dataset;
const url = getImageUrl(album, foto, false);
const thumbUrl = getImageUrl(album, foto, true);
// Onthoud welke foto nu gevraagd is, zodat een trage oudere foto nooit een nieuwere overschrijft.
modalImage.dataset.requested = url;
modalImage.alt = thumbnail.alt;
modalImage.classList.remove("firebase-image-error");
const isHuidig = () => modalImage.dataset.requested === url;
let groteGetoond = false;
let thumbGetoond = false;
// Het formaat volgt uit de foto zelf, zodat thumbnail en grote foto dezelfde plek innemen.
const zetMaat = bron => {
const verhouding = bron.naturalWidth / bron.naturalHeight;
modalImage.style.aspectRatio = `${bron.naturalWidth} / ${bron.naturalHeight}`;
modalImage.style.width = `min(100%, ${(90 * verhouding).toFixed(2)}vh)`;
};
const toonThumb = bron => {
if (!isHuidig() || groteGetoond || !bron.naturalWidth) return;
thumbGetoond = true;
zetMaat(bron);
modalImage.src = thumbUrl;
};
const groot = new Image();
groot.onload = () => {
if (!isHuidig()) return;
groteGetoond = true;
zetMaat(groot);
modalImage.src = url;
};
groot.onerror = () => {
console.error(`Afbeelding kon niet geladen worden: ${url}`);
// Is er geen thumbnail om op terug te vallen, dan tonen we de foutstijl.
if (isHuidig() && !thumbGetoond) {
modalImage.classList.add("firebase-image-error");
modalImage.alt = "Afbeelding kon niet geladen worden";
}
};
groot.src = url;
// Al in de cache (bv. een vooraf opgehaalde buurfoto): meteen de grote tonen.
if (groot.complete && groot.naturalWidth > 0) {
groot.onload();
return;
}
if (thumbnail.complete && thumbnail.naturalWidth > 0) {
toonThumb(thumbnail); // thumbnail staat al op de pagina
} else {
const kleine = new Image(); // thumbnail nog niet geladen: zelf ophalen
kleine.onload = () => toonThumb(kleine);
kleine.src = thumbUrl;
}
}
// Toont foto nummer i van de groep. Na de laatste foto begin je weer bij de eerste.
function toon(nieuweIndex) {
index = (nieuweIndex + groep.length) % groep.length;
toonFoto(groep[index]);
teller.textContent = `${index + 1} / ${groep.length}`;
const metPijlen = groep.length > 1;
vorigeKnop.style.display = metPijlen ? "flex" : "none";
volgendeKnop.style.display = metPijlen ? "flex" : "none";
teller.style.display = metPijlen ? "" : "none";
// De volgende en vorige foto alvast ophalen, zodat bladeren snel gaat.
if (metPijlen) {
[index + 1, index - 1].forEach(i => {
const buur = groep[(i + groep.length) % groep.length];
new Image().src = getImageUrl(buur.dataset.album, buur.dataset.foto, false);
});
}
}
// Alle thumbnails van hetzelfde album, in de volgorde van de pagina.
function verzamelGroep(thumbnail) {
return Array.from(document.querySelectorAll("img[data-album][data-foto]"))
.filter(img =>
img.dataset.album === thumbnail.dataset.album &&
!img.classList.contains("firebase-image-error")
);
}
/**
* Zet de lightbox aan voor de hele pagina.
*
* Werkt met event delegation: elke opent bij een
* klik de normale versie, ook als die pas later aan de pagina wordt toegevoegd.
*/
export function initLightbox() {
document.addEventListener("click", event => {
const thumbnail = event.target.closest("img[data-album][data-foto]");
if (!thumbnail || thumbnail.classList.contains("firebase-image-error")) {
return;
}
ensureModal();
groep = verzamelGroep(thumbnail);
modalImage.removeAttribute("src");
modal.show();
toon(groep.indexOf(thumbnail));
});
}