326 lines
8.8 KiB
JavaScript
326 lines
8.8 KiB
JavaScript
const CONSENT_PREFIX = "havelseiten:external:";
|
||
|
||
function storageGet(key) {
|
||
try {
|
||
return localStorage.getItem(key);
|
||
} catch (error) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function storageSet(key, value) {
|
||
try {
|
||
localStorage.setItem(key, value);
|
||
} catch (error) {
|
||
// If storage is unavailable, the current in-page load still works.
|
||
}
|
||
}
|
||
|
||
function consentKey(button) {
|
||
if (button.dataset.consentKey) {
|
||
return `${CONSENT_PREFIX}${button.dataset.consentKey}`;
|
||
}
|
||
|
||
if (button.dataset.mapAddress) {
|
||
return `${CONSENT_PREFIX}map:${button.dataset.mapAddress}`;
|
||
}
|
||
|
||
if (button.dataset.embedSrc) {
|
||
return `${CONSENT_PREFIX}embed:${button.dataset.embedSrc}`;
|
||
}
|
||
|
||
return "";
|
||
}
|
||
|
||
function mapEmbedUrl(lat, lon) {
|
||
const latitude = Number(lat);
|
||
const longitude = Number(lon);
|
||
const bbox = [
|
||
longitude - 0.01,
|
||
latitude - 0.006,
|
||
longitude + 0.01,
|
||
latitude + 0.006
|
||
].join(",");
|
||
|
||
return (
|
||
"https://www.openstreetmap.org/export/embed.html"
|
||
+ `?bbox=${encodeURIComponent(bbox)}`
|
||
+ "&layer=mapnik"
|
||
+ `&marker=${encodeURIComponent(`${latitude},${longitude}`)}`
|
||
);
|
||
}
|
||
|
||
async function geocodeAddress(address) {
|
||
const url = (
|
||
"https://nominatim.openstreetmap.org/search"
|
||
+ `?format=json&limit=1&q=${encodeURIComponent(address)}`
|
||
);
|
||
const response = await fetch(url, {
|
||
headers: {
|
||
Accept: "application/json"
|
||
}
|
||
});
|
||
const results = await response.json();
|
||
|
||
if (!results.length) {
|
||
throw new Error("Adresse nicht gefunden");
|
||
}
|
||
|
||
return results[0];
|
||
}
|
||
|
||
async function loadEmbed(button, remember = true) {
|
||
const iframe = document.createElement("iframe");
|
||
const key = consentKey(button);
|
||
|
||
button.disabled = true;
|
||
button.textContent = "Wird geladen...";
|
||
|
||
if (button.dataset.mapAddress) {
|
||
const cachedMapUrl = storageGet(`${key}:src`);
|
||
|
||
if (cachedMapUrl) {
|
||
iframe.src = cachedMapUrl;
|
||
} else {
|
||
try {
|
||
const place = await geocodeAddress(button.dataset.mapAddress);
|
||
iframe.src = mapEmbedUrl(place.lat, place.lon);
|
||
storageSet(`${key}:src`, iframe.src);
|
||
} catch (error) {
|
||
button.disabled = false;
|
||
button.textContent = "Karte konnte nicht geladen werden";
|
||
return;
|
||
}
|
||
}
|
||
} else {
|
||
iframe.src = button.dataset.embedSrc;
|
||
}
|
||
|
||
iframe.className = button.dataset.embedClass || "";
|
||
iframe.title = button.dataset.embedTitle || "Externer Inhalt";
|
||
iframe.loading = "lazy";
|
||
iframe.allow = button.dataset.embedAllow || "";
|
||
iframe.allowFullscreen = true;
|
||
|
||
if (remember && key) {
|
||
storageSet(key, "1");
|
||
}
|
||
|
||
button.closest(".external-placeholder, .location-placeholder").replaceWith(iframe);
|
||
}
|
||
|
||
function galleryItems() {
|
||
return [...document.querySelectorAll(".gallery-grid a")].map((link) => ({
|
||
alt: link.querySelector("img")?.alt || "",
|
||
height: link.dataset.pswpHeight,
|
||
src: link.href,
|
||
width: link.dataset.pswpWidth
|
||
}));
|
||
}
|
||
|
||
function createLightbox() {
|
||
const lightbox = document.createElement("div");
|
||
lightbox.className = "site-lightbox";
|
||
lightbox.hidden = true;
|
||
lightbox.innerHTML = `
|
||
<button class="site-lightbox-close" type="button" aria-label="Schliessen">×</button>
|
||
<button class="site-lightbox-prev" type="button" aria-label="Vorheriges Bild">‹</button>
|
||
<figure>
|
||
<img alt="">
|
||
<figcaption></figcaption>
|
||
</figure>
|
||
<button class="site-lightbox-next" type="button" aria-label="Naechstes Bild">›</button>
|
||
`;
|
||
document.body.append(lightbox);
|
||
|
||
return lightbox;
|
||
}
|
||
|
||
function initGallery() {
|
||
const items = galleryItems();
|
||
|
||
if (!items.length) {
|
||
return;
|
||
}
|
||
|
||
const lightbox = createLightbox();
|
||
const image = lightbox.querySelector("img");
|
||
const caption = lightbox.querySelector("figcaption");
|
||
let currentIndex = 0;
|
||
let touchStartX = 0;
|
||
|
||
function show(index) {
|
||
currentIndex = (index + items.length) % items.length;
|
||
const item = items[currentIndex];
|
||
image.src = item.src;
|
||
image.alt = item.alt;
|
||
caption.textContent = item.alt;
|
||
caption.hidden = !item.alt;
|
||
lightbox.hidden = false;
|
||
document.body.classList.add("lightbox-open");
|
||
}
|
||
|
||
function close() {
|
||
lightbox.hidden = true;
|
||
image.removeAttribute("src");
|
||
document.body.classList.remove("lightbox-open");
|
||
}
|
||
|
||
function next() {
|
||
show(currentIndex + 1);
|
||
}
|
||
|
||
function prev() {
|
||
show(currentIndex - 1);
|
||
}
|
||
|
||
document.querySelectorAll(".gallery-grid a").forEach((link, index) => {
|
||
link.addEventListener("click", (event) => {
|
||
event.preventDefault();
|
||
show(index);
|
||
});
|
||
});
|
||
|
||
lightbox.querySelector(".site-lightbox-close").addEventListener("click", close);
|
||
lightbox.querySelector(".site-lightbox-next").addEventListener("click", next);
|
||
lightbox.querySelector(".site-lightbox-prev").addEventListener("click", prev);
|
||
|
||
lightbox.addEventListener("click", (event) => {
|
||
if (event.target === lightbox) {
|
||
close();
|
||
}
|
||
});
|
||
|
||
lightbox.addEventListener("touchstart", (event) => {
|
||
touchStartX = event.changedTouches[0].clientX;
|
||
}, { passive: true });
|
||
|
||
lightbox.addEventListener("touchend", (event) => {
|
||
const delta = event.changedTouches[0].clientX - touchStartX;
|
||
|
||
if (Math.abs(delta) < 40) {
|
||
return;
|
||
}
|
||
|
||
if (delta < 0) {
|
||
next();
|
||
} else {
|
||
prev();
|
||
}
|
||
}, { passive: true });
|
||
|
||
document.addEventListener("keydown", (event) => {
|
||
if (lightbox.hidden) {
|
||
return;
|
||
}
|
||
|
||
if (event.key === "Escape") {
|
||
close();
|
||
} else if (event.key === "ArrowRight") {
|
||
next();
|
||
} else if (event.key === "ArrowLeft") {
|
||
prev();
|
||
}
|
||
});
|
||
}
|
||
|
||
function initSubmenus() {
|
||
const mobileQuery = window.matchMedia("(max-width: 800px)");
|
||
|
||
document.querySelectorAll(".dropdown").forEach((dropdown) => {
|
||
const button = dropdown.querySelector(".dropdown-label");
|
||
|
||
if (!button) {
|
||
return;
|
||
}
|
||
|
||
const startsOpen = (
|
||
document.body.classList.contains("mobile-submenus-open")
|
||
&& mobileQuery.matches
|
||
);
|
||
|
||
dropdown.classList.toggle("is-open", startsOpen);
|
||
button.setAttribute("aria-expanded", startsOpen ? "true" : "false");
|
||
button.addEventListener("click", (event) => {
|
||
if (!mobileQuery.matches) {
|
||
return;
|
||
}
|
||
|
||
event.preventDefault();
|
||
const isOpen = dropdown.classList.toggle("is-open");
|
||
button.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
||
});
|
||
|
||
dropdown.addEventListener("mouseenter", () => {
|
||
if (mobileQuery.matches) {
|
||
return;
|
||
}
|
||
|
||
dropdown.classList.add("is-open");
|
||
button.setAttribute("aria-expanded", "true");
|
||
});
|
||
|
||
dropdown.addEventListener("mouseleave", () => {
|
||
if (mobileQuery.matches) {
|
||
return;
|
||
}
|
||
|
||
dropdown.classList.remove("is-open");
|
||
button.setAttribute("aria-expanded", "false");
|
||
});
|
||
|
||
dropdown.addEventListener("focusin", () => {
|
||
if (mobileQuery.matches) {
|
||
return;
|
||
}
|
||
|
||
dropdown.classList.add("is-open");
|
||
button.setAttribute("aria-expanded", "true");
|
||
});
|
||
|
||
dropdown.addEventListener("focusout", (event) => {
|
||
if (
|
||
mobileQuery.matches
|
||
|| dropdown.contains(event.relatedTarget)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
dropdown.classList.remove("is-open");
|
||
button.setAttribute("aria-expanded", "false");
|
||
});
|
||
});
|
||
}
|
||
|
||
document.querySelectorAll(".external-load-button").forEach((button) => {
|
||
const key = consentKey(button);
|
||
|
||
if (button.dataset.autoLoad === "true" || (key && storageGet(key) === "1")) {
|
||
loadEmbed(button, false);
|
||
}
|
||
});
|
||
|
||
document.addEventListener("click", (event) => {
|
||
const externalButton = event.target.closest(".external-load-button");
|
||
|
||
if (externalButton) {
|
||
loadEmbed(externalButton);
|
||
}
|
||
|
||
const galleryButton = event.target.closest(".gallery-load-button");
|
||
|
||
if (galleryButton) {
|
||
const section = galleryButton.closest(".gallery-section");
|
||
const grid = section?.querySelector(".gallery-grid");
|
||
|
||
if (grid) {
|
||
grid.hidden = false;
|
||
}
|
||
|
||
galleryButton.closest(".gallery-placeholder")?.remove();
|
||
}
|
||
});
|
||
|
||
initSubmenus();
|
||
initGallery();
|