Add havelseite
This commit is contained in:
325
generator/static/external-content.js
Normal file
325
generator/static/external-content.js
Normal file
@ -0,0 +1,325 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user