Verbesserungen kategoireansicht, karte aof ortsansicht

This commit is contained in:
Simon Martens
2025-09-28 17:34:45 +02:00
parent 2318c0c06d
commit 3b19ea94b6
5 changed files with 526 additions and 282 deletions

View File

@@ -29,8 +29,8 @@ class $ extends HTMLElement {
if (!i) if (!i)
return; return;
i.querySelectorAll(".person-item").forEach((s) => { i.querySelectorAll(".person-item").forEach((s) => {
var c, d; var c, h;
const o = ((c = s.querySelector(".person-name")) == null ? void 0 : c.textContent) || "", r = ((d = s.querySelector(".person-life")) == null ? void 0 : d.textContent) || ""; const o = ((c = s.querySelector(".person-name")) == null ? void 0 : c.textContent) || "", r = ((h = s.querySelector(".person-life")) == null ? void 0 : h.textContent) || "";
!e || o.toLowerCase().includes(e) || r.toLowerCase().includes(e) ? s.style.display = "block" : s.style.display = "none"; !e || o.toLowerCase().includes(e) || r.toLowerCase().includes(e) ? s.style.display = "block" : s.style.display = "none";
}); });
} }
@@ -50,7 +50,7 @@ class O extends HTMLElement {
} }
} }
customElements.define("place-jump-filter", O); customElements.define("place-jump-filter", O);
class V extends HTMLElement { class R extends HTMLElement {
connectedCallback() { connectedCallback() {
const e = this.querySelector("#category-search"); const e = this.querySelector("#category-search");
e && e.addEventListener("input", (t) => { e && e.addEventListener("input", (t) => {
@@ -63,8 +63,8 @@ class V extends HTMLElement {
}); });
} }
} }
customElements.define("category-jump-filter", V); customElements.define("category-jump-filter", R);
class R extends HTMLElement { class V extends HTMLElement {
constructor() { constructor() {
super(), this.issuesByYear = {}; super(), this.issuesByYear = {};
} }
@@ -179,7 +179,7 @@ class R extends HTMLElement {
i.disabled = !o; i.disabled = !o;
} }
} }
customElements.define("year-jump-filter", R); customElements.define("year-jump-filter", V);
class z extends HTMLElement { class z extends HTMLElement {
constructor() { constructor() {
super(), this.isOpen = !1; super(), this.isOpen = !1;
@@ -339,9 +339,9 @@ document.addEventListener("DOMContentLoaded", function() {
}); });
const k = []; const k = [];
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
I(); H();
}); });
const I = function() { const H = function() {
for (; k.length > 0; ) { for (; k.length > 0; ) {
const a = k.shift(); const a = k.shift();
try { try {
@@ -425,18 +425,18 @@ class F extends HTMLElement {
const s = n.getAttribute("id"), o = n.querySelector(".akteur-werke-section"), r = n.querySelector(".akteur-beitraege-section"); const s = n.getAttribute("id"), o = n.querySelector(".akteur-werke-section"), r = n.querySelector(".akteur-beitraege-section");
let l = !1; let l = !1;
if (o) { if (o) {
const c = o.getBoundingClientRect(), d = c.top < window.innerHeight, u = c.bottom > 0; const c = o.getBoundingClientRect(), h = c.top < window.innerHeight, d = c.bottom > 0;
d && u && (l = !0); h && d && (l = !0);
} }
if (r && !l) { if (r && !l) {
const c = r.getBoundingClientRect(), d = c.top < window.innerHeight, u = c.bottom > 0; const c = r.getBoundingClientRect(), h = c.top < window.innerHeight, d = c.bottom > 0;
d && u && (l = !0); h && d && (l = !0);
} }
if (!o && !r) { if (!o && !r) {
const c = n.querySelector("div:first-child"); const c = n.querySelector("div:first-child");
if (c) { if (c) {
const d = c.getBoundingClientRect(), u = d.top >= 0, h = d.bottom <= window.innerHeight; const h = c.getBoundingClientRect(), d = h.top >= 0, u = h.bottom <= window.innerHeight;
u && h && (l = !0); d && u && (l = !0);
} }
} }
l && e.push(s); l && e.push(s);
@@ -453,8 +453,8 @@ class F extends HTMLElement {
const n = document.getElementById("scrollspy-nav"), s = n.getBoundingClientRect(); const n = document.getElementById("scrollspy-nav"), s = n.getBoundingClientRect();
let o = 1 / 0, r = -1 / 0; let o = 1 / 0, r = -1 / 0;
t.forEach((c) => { t.forEach((c) => {
const d = c.getBoundingClientRect(), u = d.top - s.top + n.scrollTop, h = u + d.height; const h = c.getBoundingClientRect(), d = h.top - s.top + n.scrollTop, u = d + h.height;
o = Math.min(o, u), r = Math.max(r, h); o = Math.min(o, d), r = Math.max(r, u);
}); });
let l = r - o; let l = r - o;
i.style.top = `${o}px`, i.style.height = `${l}px`, i.style.opacity = "1", setTimeout(() => this.ensureMarkerVisibility(), 100); i.style.top = `${o}px`, i.style.height = `${l}px`, i.style.opacity = "1", setTimeout(() => this.ensureMarkerVisibility(), 100);
@@ -490,9 +490,9 @@ class F extends HTMLElement {
document.documentElement.clientHeight, document.documentElement.clientHeight,
document.documentElement.scrollHeight, document.documentElement.scrollHeight,
document.documentElement.offsetHeight document.documentElement.offsetHeight
), s = window.innerHeight, o = n - s, r = o > 0 ? window.scrollY / o : 0, l = t.clientHeight, d = t.scrollHeight - l; ), s = window.innerHeight, o = n - s, r = o > 0 ? window.scrollY / o : 0, l = t.clientHeight, h = t.scrollHeight - l;
if (d > 0) { if (h > 0) {
const u = r * d, h = i.getBoundingClientRect(), p = t.getBoundingClientRect(), b = h.top - p.top + t.scrollTop, g = l / 2, y = b - g, w = 0.7, x = w * u + (1 - w) * y, v = Math.max(0, Math.min(d, x)), E = t.scrollTop; const d = r * h, u = i.getBoundingClientRect(), p = t.getBoundingClientRect(), m = u.top - p.top + t.scrollTop, g = l / 2, w = m - g, x = 0.7, f = x * d + (1 - x) * w, v = Math.max(0, Math.min(h, f)), E = t.scrollTop;
Math.abs(v - E) > 10 && t.scrollTo({ Math.abs(v - E) > 10 && t.scrollTo({
top: v, top: v,
behavior: "smooth" behavior: "smooth"
@@ -566,7 +566,7 @@ class j extends HTMLElement {
this.countElement && (t === "" ? this.countElement.textContent = `Alle Orte (${this.originalCount})` : e === 0 ? this.countElement.textContent = `Keine Orte gefunden für "${t}"` : this.countElement.textContent = `${e} von ${this.originalCount} Orten`); this.countElement && (t === "" ? this.countElement.textContent = `Alle Orte (${this.originalCount})` : e === 0 ? this.countElement.textContent = `Keine Orte gefunden für "${t}"` : this.countElement.textContent = `${e} von ${this.originalCount} Orten`);
} }
} }
class K extends HTMLElement { class _ extends HTMLElement {
constructor() { constructor() {
super(), this.isExpanded = !1, this.isLoading = !1, this.hasLoaded = !1, this.boundHandleClick = this.handleClick.bind(this), this.boundHandleMapClick = this.handleMapClick.bind(this), this.boundHandleHeadingHover = this.handleHeadingHover.bind(this), this.boundHandleHeadingLeave = this.handleHeadingLeave.bind(this); super(), this.isExpanded = !1, this.isLoading = !1, this.hasLoaded = !1, this.boundHandleClick = this.handleClick.bind(this), this.boundHandleMapClick = this.handleMapClick.bind(this), this.boundHandleHeadingHover = this.handleHeadingHover.bind(this), this.boundHandleHeadingLeave = this.handleHeadingLeave.bind(this);
} }
@@ -707,10 +707,10 @@ class W extends HTMLElement {
if (!this.places.length || !this.pointsContainer) if (!this.places.length || !this.pointsContainer)
return; return;
const e = { xmin: 2555e3, ymin: 135e4, xmax: 7405e3, ymax: 55e5 }, t = { lon: 10, lat: 52 }, i = (s, o) => { const e = { xmin: 2555e3, ymin: 135e4, xmax: 7405e3, ymax: 55e5 }, t = { lon: 10, lat: 52 }, i = (s, o) => {
const d = t.lon * Math.PI / 180, u = t.lat * Math.PI / 180, h = o * Math.PI / 180, p = s * Math.PI / 180, b = Math.sqrt( const h = t.lon * Math.PI / 180, d = t.lat * Math.PI / 180, u = o * Math.PI / 180, p = s * Math.PI / 180, m = Math.sqrt(
2 / (1 + Math.sin(u) * Math.sin(p) + Math.cos(u) * Math.cos(p) * Math.cos(h - d)) 2 / (1 + Math.sin(d) * Math.sin(p) + Math.cos(d) * Math.cos(p) * Math.cos(u - h))
), g = 6371e3 * b * Math.cos(p) * Math.sin(h - d), y = 6371e3 * b * (Math.cos(u) * Math.sin(p) - Math.sin(u) * Math.cos(p) * Math.cos(h - d)), w = g + 4321e3, x = y + 321e4, v = e.xmax - e.xmin, E = e.ymax - e.ymin, L = (w - e.xmin) / v * 100, C = (e.ymax - x) / E * 100; ), g = 6371e3 * m * Math.cos(p) * Math.sin(u - h), w = 6371e3 * m * (Math.cos(d) * Math.sin(p) - Math.sin(d) * Math.cos(p) * Math.cos(u - h)), x = g + 4321e3, f = w + 321e4, v = e.xmax - e.xmin, E = e.ymax - e.ymin, S = (x - e.xmin) / v * 100, L = (e.ymax - f) / E * 100;
return { x: L, y: C }; return { x: S, y: L };
}, n = []; }, n = [];
this.places.forEach((s) => { this.places.forEach((s) => {
if (s.lat && s.lng) { if (s.lat && s.lng) {
@@ -719,8 +719,8 @@ class W extends HTMLElement {
n.push(l); n.push(l);
const c = document.createElement("div"); const c = document.createElement("div");
c.className = "map-point hidden", c.style.left = `${l.x}%`, c.style.top = `${l.y}%`, c.style.transformOrigin = "center"; c.className = "map-point hidden", c.style.left = `${l.x}%`, c.style.top = `${l.y}%`, c.style.transformOrigin = "center";
const d = `${s.name}${s.toponymName && s.toponymName !== s.name ? ` (${s.toponymName})` : ""}`; const h = `${s.name}${s.toponymName && s.toponymName !== s.name ? ` (${s.toponymName})` : ""}`;
c.dataset.placeId = s.id, c.dataset.tooltipText = d, c.addEventListener("mouseenter", (u) => this.showTooltip(u)), c.addEventListener("mouseleave", () => this.hideTooltip()), c.addEventListener("mousemove", (u) => this.updateTooltipPosition(u)), c.addEventListener("click", (u) => this.scrollToPlace(u)), this.pointsContainer.appendChild(c), this.mapPoints.set(s.id, c); c.dataset.placeId = s.id, c.dataset.tooltipText = h, c.addEventListener("mouseenter", (d) => this.showTooltip(d)), c.addEventListener("mouseleave", () => this.hideTooltip()), c.addEventListener("mousemove", (d) => this.updateTooltipPosition(d)), c.addEventListener("click", (d) => this.scrollToPlace(d)), this.pointsContainer.appendChild(c), this.mapPoints.set(s.id, c);
} }
} }
}), n.length > 0 && this.autoZoomToPoints(n); }), n.length > 0 && this.autoZoomToPoints(n);
@@ -728,20 +728,20 @@ class W extends HTMLElement {
// Calculate bounding box of all points for the auto-zoom // Calculate bounding box of all points for the auto-zoom
autoZoomToPoints(e) { autoZoomToPoints(e) {
let t = 100, i = 0, n = 100, s = 0; let t = 100, i = 0, n = 100, s = 0;
e.forEach((f) => { e.forEach((y) => {
f.x < t && (t = f.x), f.x > i && (i = f.x), f.y < n && (n = f.y), f.y > s && (s = f.y); y.x < t && (t = y.x), y.x > i && (i = y.x), y.y < n && (n = y.y), y.y > s && (s = y.y);
}); });
const o = 0.06, r = i - t, l = s - n, c = r * o, d = l * o, u = Math.max(0, t - c), h = Math.min(100, i + c), p = Math.max(0, n - d), b = Math.min(100, s + d), g = h - u, y = b - p, w = 5 / 7, x = g / y; const o = 0.06, r = i - t, l = s - n, c = r * o, h = l * o, d = Math.max(0, t - c), u = Math.min(100, i + c), p = Math.max(0, n - h), m = Math.min(100, s + h), g = u - d, w = m - p, x = 5 / 7, f = g / w;
let v = { x: u, y: p, width: g, height: y }; let v = { x: d, y: p, width: g, height: w };
if (x > w) { if (f > x) {
const f = g / w; const y = g / x;
v.y = p - (f - y) / 2, v.height = f; v.y = p - (y - w) / 2, v.height = y;
} else { } else {
const f = y * w; const y = w * x;
v.x = u - (f - g) / 2, v.width = f; v.x = d - (y - g) / 2, v.width = y;
} }
const E = 100 / v.width, L = -v.x, C = -v.y, N = `scale(${E}) translate(${L}%, ${C}%)`, P = this.querySelector(".transform-wrapper"); const E = 100 / v.width, S = -v.x, L = -v.y, T = `scale(${E}) translate(${S}%, ${L}%)`, P = this.querySelector(".transform-wrapper");
P && (P.style.transform = N); P && (P.style.transform = T);
} }
initializeScrollspy() { initializeScrollspy() {
const e = document.querySelectorAll("place-accordion[data-place-id]"); const e = document.querySelectorAll("place-accordion[data-place-id]");
@@ -755,19 +755,14 @@ class W extends HTMLElement {
}); });
}, },
{ {
// Trigger when element enters viewport // Trigger when any part enters viewport - better for small elements
threshold: 0.1, threshold: 0,
// No root margin for precise detection // Add some margin to trigger slightly before/after entering viewport
rootMargin: "0px" rootMargin: "10px 0px"
} }
), e.forEach((t) => { ), e.forEach((t) => {
this.intersectionObserver.observe(t); this.intersectionObserver.observe(t);
}), setTimeout(() => { }));
e.forEach((t) => {
const i = t.getBoundingClientRect(), n = i.top < window.innerHeight && i.bottom > 0, s = t.getAttribute("data-place-id"), o = this.mapPoints.get(s);
o && n && this.setPointActive(o);
});
}, 50));
} }
setPointActive(e) { setPointActive(e) {
e.className = "map-point absolute w-1.5 h-1.5 bg-red-500 border border-red-700 rounded-full shadow-md -translate-x-1/2 -translate-y-1/2 transition-all duration-300 opacity-100 saturate-100 z-20 cursor-pointer hover:w-2 hover:h-2 hover:bg-red-600 hover:z-30"; e.className = "map-point absolute w-1.5 h-1.5 bg-red-500 border border-red-700 rounded-full shadow-md -translate-x-1/2 -translate-y-1/2 transition-all duration-300 opacity-100 saturate-100 z-20 cursor-pointer hover:w-2 hover:h-2 hover:bg-red-600 hover:z-30";
@@ -832,29 +827,102 @@ class W extends HTMLElement {
} }
handleHeadingHoverEvent(e) { handleHeadingHoverEvent(e) {
const { placeId: t, action: i } = e.detail, n = this.mapPoints.get(t); const { placeId: t, action: i } = e.detail, n = this.mapPoints.get(t);
if (n) n && (i === "show" ? (this.currentHoveredPlaceId = t, n.classList.remove("w-1", "h-1", "w-1.5", "h-1.5"), n.classList.add("w-2.5", "h-2.5"), n.style.zIndex = "25") : i === "hide" && (this.currentHoveredPlaceId = "", n.classList.remove("w-2.5", "h-2.5"), n.className.includes("bg-red-500") ? n.classList.add("w-1.5", "h-1.5") : n.classList.add("w-1", "h-1"), n.style.zIndex = ""));
if (i === "show") {
if (this.currentHoveredPlaceId = t, n.classList.remove("w-1", "h-1", "w-1.5", "h-1.5"), n.classList.add("w-2", "h-2"), n.style.zIndex = "25", this.isNewPopupBlocked(t))
return;
const s = n.dataset.tooltipText;
if (this.tooltip && s) {
this.tooltip.textContent = s;
const o = n.getBoundingClientRect(), r = this.mapElement.getBoundingClientRect(), l = o.left - r.left + o.width / 2, c = o.top - r.top + o.height / 2;
this.tooltip.style.left = `${l}px`, this.tooltip.style.top = `${c}px`, this.clearTimeouts(), this.isTooltipVisible ? (this.tooltip.classList.remove("opacity-0"), this.tooltip.classList.add("opacity-100")) : this.showTimeout = setTimeout(() => {
this.tooltip.classList.remove("opacity-0"), this.tooltip.classList.add("opacity-100"), this.isTooltipVisible = !0;
}, 150);
}
} else i === "hide" && (this.currentHoveredPlaceId = "", this.clearTimeouts(), this.hideTimeout = setTimeout(() => {
this.tooltip && (this.tooltip.classList.remove("opacity-100"), this.tooltip.classList.add("opacity-0"), this.isTooltipVisible = !1);
}, 150), n.classList.remove("w-2", "h-2"), n.className.includes("bg-red-500") ? n.classList.add("w-1.5", "h-1.5") : n.classList.add("w-1", "h-1"), n.style.zIndex = "");
} }
disconnectedCallback() { disconnectedCallback() {
this.intersectionObserver && (this.intersectionObserver.disconnect(), this.intersectionObserver = null), this.clearTimeouts(), document.removeEventListener("place-heading-hover", this.boundHandleHeadingHoverEvent), window.removeEventListener("scroll", this.boundHandleScroll), document.removeEventListener("scroll", this.boundHandleScroll); this.intersectionObserver && (this.intersectionObserver.disconnect(), this.intersectionObserver = null), this.clearTimeouts(), document.removeEventListener("place-heading-hover", this.boundHandleHeadingHoverEvent), window.removeEventListener("scroll", this.boundHandleScroll), document.removeEventListener("scroll", this.boundHandleScroll);
} }
} }
class K extends HTMLElement {
constructor() {
super(), this.place = null, this.mapElement = null, this.pointsContainer = null, this.tooltip = null;
}
connectedCallback() {
this.parseData(), this.render(), this.initializeMap();
}
parseData() {
try {
const e = this.dataset.place;
e && (this.place = JSON.parse(e));
} catch (e) {
console.error("Failed to parse place data:", e), this.place = null;
}
}
render() {
this.innerHTML = `
<div class="map-container relative w-full aspect-[5/7] overflow-hidden bg-slate-100">
<div class="transform-wrapper absolute top-0 left-0 w-full h-auto origin-top-left">
<img src="/assets/Europe.svg" alt="Map of Europe" class="block w-full h-auto">
<div class="points-container absolute top-0 left-0 w-full h-full"></div>
</div>
<div class="absolute bottom-0 right-0 h-auto text-[0.6rem] bg-white px-0.5 bg-opacity-[0.5] border">
<i class="ri-creative-commons-line"></i>
<a href="https://commons.wikimedia.org/wiki/File:Europe_laea_topography.svg" target="_blank" class="">
Wikimedia Commons
</a>
</div>
<!-- Tooltip -->
<div class="map-tooltip absolute bg-slate-800 text-white text-sm px-2 py-1 rounded shadow-lg pointer-events-none opacity-0 transition-opacity duration-200 z-30 whitespace-nowrap" style="transform: translate(-50%, -100%); margin-top: -8px;"></div>
</div>
`, this.mapElement = this.querySelector(".map-container"), this.pointsContainer = this.querySelector(".points-container"), this.tooltip = this.querySelector(".map-tooltip");
}
initializeMap() {
if (!this.place || !this.place.lat || !this.place.lng || !this.pointsContainer)
return;
const e = { xmin: 2555e3, ymin: 135e4, xmax: 7405e3, ymax: 55e5 }, t = { lon: 10, lat: 52 }, i = (r, l) => {
const u = t.lon * Math.PI / 180, p = t.lat * Math.PI / 180, m = l * Math.PI / 180, g = r * Math.PI / 180, w = Math.sqrt(
2 / (1 + Math.sin(p) * Math.sin(g) + Math.cos(p) * Math.cos(g) * Math.cos(m - u))
), x = 6371e3 * w * Math.cos(g) * Math.sin(m - u), f = 6371e3 * w * (Math.cos(p) * Math.sin(g) - Math.sin(p) * Math.cos(g) * Math.cos(m - u)), v = x + 4321e3, E = f + 321e4, S = e.xmax - e.xmin, L = e.ymax - e.ymin, T = (v - e.xmin) / S * 100, P = (e.ymax - E) / L * 100;
return { x: T, y: P };
}, n = parseFloat(this.place.lat), s = parseFloat(this.place.lng), o = i(n, s);
if (o.x >= 0 && o.x <= 100 && o.y >= 0 && o.y <= 100) {
const r = document.createElement("div");
r.style.left = `${o.x}%`, r.style.top = `${o.y}%`, r.style.transformOrigin = "center", r.className = "absolute w-2 h-2 bg-red-500 border border-red-700 rounded-full shadow-md -translate-x-1/2 -translate-y-1/2 z-20";
const l = `${this.place.name}${this.place.toponymName && this.place.toponymName !== this.place.name ? ` (${this.place.toponymName})` : ""}`;
r.dataset.tooltipText = l, r.addEventListener("mouseenter", (c) => this.showTooltip(c)), r.addEventListener("mouseleave", () => this.hideTooltip()), r.addEventListener("mousemove", (c) => this.updateTooltipPosition(c)), this.pointsContainer.appendChild(r), this.autoZoomToPoint(o);
}
}
autoZoomToPoint(e) {
let i = Math.max(0, e.x - 20), n = Math.min(100, e.x + 20), s = Math.max(0, e.y - 20), o = Math.min(100, e.y + 20), r = n - i, l = o - s;
const c = 5 / 7, h = r / l;
let d = { x: i, y: s, width: r, height: l };
if (h > c) {
const f = r / c;
d.y = s - (f - l) / 2, d.height = f;
} else {
const f = l * c;
d.x = i - (f - r) / 2, d.width = f;
}
d.x < 0 && (d.width += d.x, d.x = 0), d.y < 0 && (d.height += d.y, d.y = 0), d.x + d.width > 100 && (d.width = 100 - d.x), d.y + d.height > 100 && (d.height = 100 - d.y);
const u = 30;
if (d.width < u) {
const f = (u - d.width) / 2;
d.x = Math.max(0, d.x - f), d.width = Math.min(u, 100 - d.x);
}
if (d.height < u) {
const f = (u - d.height) / 2;
d.y = Math.max(0, d.y - f), d.height = Math.min(u, 100 - d.y);
}
const p = 100 / d.width, m = -d.x, g = -d.y, w = `scale(${p}) translate(${m}%, ${g}%)`, x = this.querySelector(".transform-wrapper");
x && (x.style.transform = w);
}
showTooltip(e) {
const i = e.target.dataset.tooltipText;
this.tooltip && i && (this.tooltip.textContent = i, this.updateTooltipPosition(e), this.tooltip.classList.remove("opacity-0"), this.tooltip.classList.add("opacity-100"));
}
hideTooltip() {
this.tooltip && (this.tooltip.classList.remove("opacity-100"), this.tooltip.classList.add("opacity-0"));
}
updateTooltipPosition(e) {
if (!this.tooltip) return;
const t = this.mapElement.getBoundingClientRect(), i = e.clientX - t.left, n = e.clientY - t.top;
this.tooltip.style.left = `${i}px`, this.tooltip.style.top = `${n}px`;
}
}
customElements.define("places-filter", j); customElements.define("places-filter", j);
customElements.define("place-accordion", K); customElements.define("place-accordion", _);
customElements.define("places-map", W); customElements.define("places-map", W);
customElements.define("places-map-single", K);
class Y extends HTMLElement { class Y extends HTMLElement {
constructor() { constructor() {
super(), this.searchInput = null, this.itemCards = [], this.countElement = null, this.debounceTimer = null, this.originalCount = 0; super(), this.searchInput = null, this.itemCards = [], this.countElement = null, this.debounceTimer = null, this.originalCount = 0;
@@ -1046,23 +1114,23 @@ class Z extends HTMLElement {
} }
} }
show(e, t, i, n = !1, s = 0, o = null, r = null, l = null) { show(e, t, i, n = !1, s = 0, o = null, r = null, l = null) {
const c = this.querySelector("#single-page-image"), d = this.querySelector("#page-number"), u = this.querySelector("#page-icon"); const c = this.querySelector("#single-page-image"), h = this.querySelector("#page-number"), d = this.querySelector("#page-icon");
this.querySelector("#page-indicator"), c.src = e, c.alt = t, this.currentPageNumber = i, this.currentIsBeilage = n, this.currentPartNumber = o; this.querySelector("#page-indicator"), c.src = e, c.alt = t, this.currentPageNumber = i, this.currentIsBeilage = n, this.currentPartNumber = o;
let h; let u;
if (l) if (l)
h = l; u = l;
else { else {
const b = this.getIssueContext(i); const m = this.getIssueContext(i);
h = b ? `${b}, ${i}` : `${i}`; u = m ? `${m}, ${i}` : `${i}`;
} }
if (d.innerHTML = h, s && i === s) { if (h.innerHTML = u, s && i === s) {
d.style.position = "relative"; h.style.position = "relative";
const b = d.querySelector(".target-page-dot"); const m = h.querySelector(".target-page-dot");
b && b.remove(); m && m.remove();
const g = document.createElement("span"); const g = document.createElement("span");
g.className = "target-page-dot absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full z-10", g.title = "verlinkte Seite", d.appendChild(g); g.className = "target-page-dot absolute -top-1 -right-1 w-3 h-3 bg-red-500 rounded-full z-10", g.title = "verlinkte Seite", h.appendChild(g);
} }
r ? r === "part-number" && o !== null ? u.innerHTML = `<span class="part-number bg-slate-100 text-slate-800 font-bold px-1.5 py-0.5 rounded border border-slate-400 flex items-center justify-center">${o}. Teil</span>` : u.innerHTML = this.generateIconFromType(r) : u.innerHTML = this.generateFallbackIcon(i, n, o), this.updateNavigationButtons(), this.style.display = "block", this.setAttribute("active", "true"); r ? r === "part-number" && o !== null ? d.innerHTML = `<span class="part-number bg-slate-100 text-slate-800 font-bold px-1.5 py-0.5 rounded border border-slate-400 flex items-center justify-center">${o}. Teil</span>` : d.innerHTML = this.generateIconFromType(r) : d.innerHTML = this.generateFallbackIcon(i, n, o), this.updateNavigationButtons(), this.style.display = "block", this.setAttribute("active", "true");
const p = this.querySelector(".flex-1.overflow-auto"); const p = this.querySelector(".flex-1.overflow-auto");
p && (p.scrollTop = 0), document.body.style.overflow = "hidden", document.dispatchEvent( p && (p.scrollTop = 0), document.body.style.overflow = "hidden", document.dispatchEvent(
new CustomEvent("singlepageviewer:opened", { new CustomEvent("singlepageviewer:opened", {
@@ -1190,10 +1258,10 @@ class Z extends HTMLElement {
o = i.getAttribute("data-page-icon-type"), i.querySelector(".part-number") && (o = "part-number"); o = i.getAttribute("data-page-icon-type"), i.querySelector(".part-number") && (o = "part-number");
const c = i.querySelector(".page-indicator"); const c = i.querySelector(".page-indicator");
if (c) { if (c) {
const d = c.cloneNode(!0); const h = c.cloneNode(!0);
d.querySelectorAll("i").forEach((p) => p.remove()), d.querySelectorAll( h.querySelectorAll("i").forEach((p) => p.remove()), h.querySelectorAll(
'[class*="target-page-dot"], .target-page-indicator' '[class*="target-page-dot"], .target-page-indicator'
).forEach((p) => p.remove()), r = d.textContent.trim(); ).forEach((p) => p.remove()), r = h.textContent.trim();
} }
this.show( this.show(
n.src, n.src,
@@ -1247,12 +1315,12 @@ class Z extends HTMLElement {
if (o) { if (o) {
const c = o.querySelector(".page-indicator"); const c = o.querySelector(".page-indicator");
if (c) { if (c) {
const d = c.textContent.trim(), u = d.match(/(\d{1,2}\.\d{1,2}\.\d{4}\s+Nr\.\s+\d+)/); const h = c.textContent.trim(), d = h.match(/(\d{1,2}\.\d{1,2}\.\d{4}\s+Nr\.\s+\d+)/);
if (d)
return d[1];
const u = h.match(/(\d{4})\s+Nr\.\s+(\d+)/);
if (u) if (u)
return u[1]; return `${u[1]} Nr. ${u[2]}`;
const h = d.match(/(\d{4})\s+Nr\.\s+(\d+)/);
if (h)
return `${h[1]} Nr. ${h[2]}`;
} }
} }
const l = document.title.match(/(\d{4}).*Nr\.\s*(\d+)/); const l = document.title.match(/(\d{4}).*Nr\.\s*(\d+)/);
@@ -1281,7 +1349,7 @@ window.addEventListener("beforeunload", function() {
const a = document.querySelector("single-page-viewer"); const a = document.querySelector("single-page-viewer");
a && a.close(); a && a.close();
}); });
class J extends HTMLElement { class X extends HTMLElement {
constructor() { constructor() {
super(), this.isVisible = !1, this.scrollHandler = null, this.htmxAfterSwapHandler = null; super(), this.isVisible = !1, this.scrollHandler = null, this.htmxAfterSwapHandler = null;
} }
@@ -1322,8 +1390,8 @@ class J extends HTMLElement {
}); });
} }
} }
customElements.define("scroll-to-top-button", J); customElements.define("scroll-to-top-button", X);
class X extends HTMLElement { class J extends HTMLElement {
constructor() { constructor() {
super(), this.pageObserver = null, this.pageContainers = /* @__PURE__ */ new Map(), this.singlePageViewerActive = !1, this.singlePageViewerCurrentPage = null, this.boundHandleSinglePageViewer = this.handleSinglePageViewer.bind(this); super(), this.pageObserver = null, this.pageContainers = /* @__PURE__ */ new Map(), this.singlePageViewerActive = !1, this.singlePageViewerCurrentPage = null, this.boundHandleSinglePageViewer = this.handleSinglePageViewer.bind(this);
} }
@@ -1408,7 +1476,7 @@ class X extends HTMLElement {
} }
const o = t.getBoundingClientRect(), r = e.getBoundingClientRect(); const o = t.getBoundingClientRect(), r = e.getBoundingClientRect();
if (!(r.top >= o.top && r.bottom <= o.bottom)) { if (!(r.top >= o.top && r.bottom <= o.bottom)) {
const c = t.scrollTop, d = r.top - o.top + c, u = o.height, h = r.height, p = d - (u - h) / 2; const c = t.scrollTop, h = r.top - o.top + c, d = o.height, u = r.height, p = h - (d - u) / 2;
t.scrollTo({ t.scrollTo({
top: Math.max(0, p), top: Math.max(0, p),
behavior: "smooth" behavior: "smooth"
@@ -1442,8 +1510,8 @@ class X extends HTMLElement {
this.pageObserver && (this.pageObserver.disconnect(), this.pageObserver = null), document.removeEventListener("singlepageviewer:opened", this.boundHandleSinglePageViewer), document.removeEventListener("singlepageviewer:closed", this.boundHandleSinglePageViewer), document.removeEventListener("singlepageviewer:pagechanged", this.boundHandleSinglePageViewer), this.pageContainers.clear(); this.pageObserver && (this.pageObserver.disconnect(), this.pageObserver = null), document.removeEventListener("singlepageviewer:opened", this.boundHandleSinglePageViewer), document.removeEventListener("singlepageviewer:closed", this.boundHandleSinglePageViewer), document.removeEventListener("singlepageviewer:pagechanged", this.boundHandleSinglePageViewer), this.pageContainers.clear();
} }
} }
customElements.define("inhaltsverzeichnis-scrollspy", X); customElements.define("inhaltsverzeichnis-scrollspy", J);
class _ extends HTMLElement { class G extends HTMLElement {
constructor() { constructor() {
super(), this.innerHTML = ` super(), this.innerHTML = `
<div id="error-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center backdrop-blur-sm"> <div id="error-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50 flex items-center justify-center backdrop-blur-sm">
@@ -1491,30 +1559,30 @@ class _ extends HTMLElement {
window.showErrorModal = (e) => this.show(e), window.closeErrorModal = () => this.close(); window.showErrorModal = (e) => this.show(e), window.closeErrorModal = () => this.close();
} }
} }
customElements.define("error-modal", _); customElements.define("error-modal", G);
window.currentPageContainers = window.currentPageContainers || []; window.currentPageContainers = window.currentPageContainers || [];
window.currentActiveIndex = window.currentActiveIndex || 0; window.currentActiveIndex = window.currentActiveIndex || 0;
window.pageObserver = window.pageObserver || null; window.pageObserver = window.pageObserver || null;
function G(a, e, t, i = null) { function U(a, e, t, i = null) {
let n = document.querySelector("single-page-viewer"); let n = document.querySelector("single-page-viewer");
n || (n = document.createElement("single-page-viewer"), document.body.appendChild(n)); n || (n = document.createElement("single-page-viewer"), document.body.appendChild(n));
const s = a.closest('[data-beilage="true"]') !== null, o = window.templateData && window.templateData.targetPage ? window.templateData.targetPage : 0, r = a.closest(".newspaper-page-container, .piece-page-container"); const s = a.closest('[data-beilage="true"]') !== null, o = window.templateData && window.templateData.targetPage ? window.templateData.targetPage : 0, r = a.closest(".newspaper-page-container, .piece-page-container");
let l = null, c = null; let l = null, c = null;
if (r) { if (r) {
l = r.getAttribute("data-page-icon-type"), r.querySelector(".part-number") && (l = "part-number"); l = r.getAttribute("data-page-icon-type"), r.querySelector(".part-number") && (l = "part-number");
const u = r.querySelector(".page-indicator"); const d = r.querySelector(".page-indicator");
if (u) { if (d) {
const h = u.cloneNode(!0); const u = d.cloneNode(!0);
h.querySelectorAll("i").forEach((g) => g.remove()), h.querySelectorAll('[class*="target-page-dot"], .target-page-indicator').forEach((g) => g.remove()), c = h.textContent.trim(); u.querySelectorAll("i").forEach((g) => g.remove()), u.querySelectorAll('[class*="target-page-dot"], .target-page-indicator').forEach((g) => g.remove()), c = u.textContent.trim();
} }
} }
n.show(a.src, a.alt, e, s, o, i, l, c); n.show(a.src, a.alt, e, s, o, i, l, c);
} }
function H() { function M() {
document.getElementById("pageModal").classList.add("hidden"); document.getElementById("pageModal").classList.add("hidden");
} }
function U() { function Q() {
if (window.pageObserver && (window.pageObserver.disconnect(), window.pageObserver = null), window.currentPageContainers = Array.from(document.querySelectorAll(".newspaper-page-container")), window.currentActiveIndex = 0, S(), document.querySelector(".newspaper-page-container")) { if (window.pageObserver && (window.pageObserver.disconnect(), window.pageObserver = null), window.currentPageContainers = Array.from(document.querySelectorAll(".newspaper-page-container")), window.currentActiveIndex = 0, C(), document.querySelector(".newspaper-page-container")) {
let e = /* @__PURE__ */ new Set(); let e = /* @__PURE__ */ new Set();
window.pageObserver = new IntersectionObserver( window.pageObserver = new IntersectionObserver(
(t) => { (t) => {
@@ -1523,7 +1591,7 @@ function U() {
n !== -1 && (i.isIntersecting ? e.add(n) : e.delete(n)); n !== -1 && (i.isIntersecting ? e.add(n) : e.delete(n));
}), e.size > 0) { }), e.size > 0) {
const n = Array.from(e).sort((s, o) => s - o)[0]; const n = Array.from(e).sort((s, o) => s - o)[0];
n !== window.currentActiveIndex && (window.currentActiveIndex = n, S()); n !== window.currentActiveIndex && (window.currentActiveIndex = n, C());
} }
}, },
{ {
@@ -1534,13 +1602,13 @@ function U() {
}); });
} }
} }
function Q() { function ee() {
if (window.currentActiveIndex > 0) { if (window.currentActiveIndex > 0) {
let a = -1; let a = -1;
const e = []; const e = [];
window.currentPageContainers.forEach((i, n) => { window.currentPageContainers.forEach((i, n) => {
const s = i.getBoundingClientRect(), o = window.innerHeight, r = Math.max(s.top, 0), l = Math.min(s.bottom, o), c = Math.max(0, l - r), d = s.height; const s = i.getBoundingClientRect(), o = window.innerHeight, r = Math.max(s.top, 0), l = Math.min(s.bottom, o), c = Math.max(0, l - r), h = s.height;
c / d >= 0.3 && e.push(n); c / h >= 0.3 && e.push(n);
}); });
const t = Math.min(...e); const t = Math.min(...e);
for (let i = t - 1; i >= 0; i--) for (let i = t - 1; i >= 0; i--)
@@ -1551,17 +1619,17 @@ function Q() {
a === -1 && t > 0 && (a = t - 1), a >= 0 && (window.currentActiveIndex = a, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({ a === -1 && t > 0 && (a = t - 1), a >= 0 && (window.currentActiveIndex = a, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({
block: "start" block: "start"
}), setTimeout(() => { }), setTimeout(() => {
S(); C();
}, 100)); }, 100));
} }
} }
function ee() { function te() {
if (window.currentActiveIndex < window.currentPageContainers.length - 1) { if (window.currentActiveIndex < window.currentPageContainers.length - 1) {
let a = -1; let a = -1;
const e = []; const e = [];
window.currentPageContainers.forEach((i, n) => { window.currentPageContainers.forEach((i, n) => {
const s = i.getBoundingClientRect(), o = window.innerHeight, r = Math.max(s.top, 0), l = Math.min(s.bottom, o), c = Math.max(0, l - r), d = s.height; const s = i.getBoundingClientRect(), o = window.innerHeight, r = Math.max(s.top, 0), l = Math.min(s.bottom, o), c = Math.max(0, l - r), h = s.height;
c / d >= 0.3 && e.push(n); c / h >= 0.3 && e.push(n);
}); });
const t = Math.max(...e); const t = Math.max(...e);
for (let i = t + 1; i < window.currentPageContainers.length; i++) for (let i = t + 1; i < window.currentPageContainers.length; i++)
@@ -1572,11 +1640,11 @@ function ee() {
a === -1 && t < window.currentPageContainers.length - 1 && (a = t + 1), a >= 0 && a < window.currentPageContainers.length && (window.currentActiveIndex = a, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({ a === -1 && t < window.currentPageContainers.length - 1 && (a = t + 1), a >= 0 && a < window.currentPageContainers.length && (window.currentActiveIndex = a, window.currentPageContainers[window.currentActiveIndex].scrollIntoView({
block: "start" block: "start"
}), setTimeout(() => { }), setTimeout(() => {
S(); C();
}, 100)); }, 100));
} }
} }
function te() { function ie() {
if (q()) { if (q()) {
const e = document.querySelector("#newspaper-content .newspaper-page-container"); const e = document.querySelector("#newspaper-content .newspaper-page-container");
e && e.scrollIntoView({ e && e.scrollIntoView({
@@ -1609,14 +1677,14 @@ function q() {
} }
return !1; return !1;
} }
function S() { function C() {
const a = document.getElementById("prevPageBtn"), e = document.getElementById("nextPageBtn"), t = document.getElementById("beilageBtn"); const a = document.getElementById("prevPageBtn"), e = document.getElementById("nextPageBtn"), t = document.getElementById("beilageBtn");
if (a && (a.style.display = "flex", window.currentActiveIndex <= 0 ? (a.disabled = !0, a.classList.add("opacity-50", "cursor-not-allowed"), a.classList.remove("hover:bg-gray-200")) : (a.disabled = !1, a.classList.remove("opacity-50", "cursor-not-allowed"), a.classList.add("hover:bg-gray-200"))), e && (e.style.display = "flex", window.currentActiveIndex >= window.currentPageContainers.length - 1 ? (e.disabled = !0, e.classList.add("opacity-50", "cursor-not-allowed"), e.classList.remove("hover:bg-gray-200")) : (e.disabled = !1, e.classList.remove("opacity-50", "cursor-not-allowed"), e.classList.add("hover:bg-gray-200"))), t) { if (a && (a.style.display = "flex", window.currentActiveIndex <= 0 ? (a.disabled = !0, a.classList.add("opacity-50", "cursor-not-allowed"), a.classList.remove("hover:bg-gray-200")) : (a.disabled = !1, a.classList.remove("opacity-50", "cursor-not-allowed"), a.classList.add("hover:bg-gray-200"))), e && (e.style.display = "flex", window.currentActiveIndex >= window.currentPageContainers.length - 1 ? (e.disabled = !0, e.classList.add("opacity-50", "cursor-not-allowed"), e.classList.remove("hover:bg-gray-200")) : (e.disabled = !1, e.classList.remove("opacity-50", "cursor-not-allowed"), e.classList.add("hover:bg-gray-200"))), t) {
const i = q(), n = t.querySelector("i"); const i = q(), n = t.querySelector("i");
i ? (t.title = "Zur Hauptausgabe", t.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-800 border border-gray-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", n && (n.className = "ri-file-text-line text-lg lg:text-xl")) : (t.title = "Zu Beilage", t.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-amber-100 hover:bg-amber-200 text-amber-700 hover:text-amber-800 border border-amber-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", n && (n.className = "ri-attachment-line text-lg lg:text-xl")); i ? (t.title = "Zur Hauptausgabe", t.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-800 border border-gray-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", n && (n.className = "ri-file-text-line text-lg lg:text-xl")) : (t.title = "Zu Beilage", t.className = "w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-amber-100 hover:bg-amber-200 text-amber-700 hover:text-amber-800 border border-amber-300 transition-colors duration-200 flex items-center justify-center cursor-pointer", n && (n.className = "ri-attachment-line text-lg lg:text-xl"));
} }
} }
function ie() { function ne() {
const a = document.getElementById("shareLinkBtn"); const a = document.getElementById("shareLinkBtn");
let e = ""; let e = "";
if (window.currentActiveIndex !== void 0 && window.currentPageContainers && window.currentPageContainers[window.currentActiveIndex]) { if (window.currentActiveIndex !== void 0 && window.currentPageContainers && window.currentPageContainers[window.currentActiveIndex]) {
@@ -1628,54 +1696,54 @@ function ie() {
title: document.title, title: document.title,
url: t url: t
}).catch((i) => { }).catch((i) => {
T(t, a); I(t, a);
}) : T(t, a); }) : I(t, a);
} }
function T(a, e) { function I(a, e) {
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(a).then(() => { navigator.clipboard.writeText(a).then(() => {
m(e, "Link kopiert!"); b(e, "Link kopiert!");
}).catch((t) => { }).catch((t) => {
m(e, "Kopieren fehlgeschlagen"); b(e, "Kopieren fehlgeschlagen");
}); });
else { else {
const t = document.createElement("textarea"); const t = document.createElement("textarea");
t.value = a, document.body.appendChild(t), t.select(); t.value = a, document.body.appendChild(t), t.select();
try { try {
const i = document.execCommand("copy"); const i = document.execCommand("copy");
m(e, i ? "Link kopiert!" : "Kopieren fehlgeschlagen"); b(e, i ? "Link kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
m(e, "Kopieren fehlgeschlagen"); b(e, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(t); document.body.removeChild(t);
} }
} }
} }
function ne() { function se() {
const a = document.getElementById("citationBtn"), e = document.title || "KGPZ"; const a = document.getElementById("citationBtn"), e = document.title || "KGPZ";
let t = window.location.origin + window.location.pathname; let t = window.location.origin + window.location.pathname;
t.includes("#") && (t = t.split("#")[0]); t.includes("#") && (t = t.split("#")[0]);
const i = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), n = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${e}. Digital verfügbar unter: ${t} (Zugriff: ${i}).`; const i = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), n = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${e}. Digital verfügbar unter: ${t} (Zugriff: ${i}).`;
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(n).then(() => { navigator.clipboard.writeText(n).then(() => {
m(a, "Zitation kopiert!"); b(a, "Zitation kopiert!");
}).catch((s) => { }).catch((s) => {
m(a, "Kopieren fehlgeschlagen"); b(a, "Kopieren fehlgeschlagen");
}); });
else { else {
const s = document.createElement("textarea"); const s = document.createElement("textarea");
s.value = n, document.body.appendChild(s), s.select(); s.value = n, document.body.appendChild(s), s.select();
try { try {
const o = document.execCommand("copy"); const o = document.execCommand("copy");
m(a, o ? "Zitation kopiert!" : "Kopieren fehlgeschlagen"); b(a, o ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
m(a, "Kopieren fehlgeschlagen"); b(a, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(s); document.body.removeChild(s);
} }
} }
} }
function m(a, e) { function b(a, e) {
const t = document.querySelector(".simple-popup"); const t = document.querySelector(".simple-popup");
t && t.remove(); t && t.remove();
const i = document.createElement("div"); const i = document.createElement("div");
@@ -1695,8 +1763,8 @@ function m(a, e) {
`; `;
const n = a.getBoundingClientRect(), s = window.innerHeight, o = window.innerWidth; const n = a.getBoundingClientRect(), s = window.innerHeight, o = window.innerWidth;
let r = n.left - 10, l = n.bottom + 8; let r = n.left - 10, l = n.bottom + 8;
const c = 120, d = 32; const c = 120, h = 32;
r + c > o && (r = n.right - c + 10), l + d > s && (l = n.top - d - 8), i.style.left = Math.max(5, r) + "px", i.style.top = Math.max(5, l) + "px", document.body.appendChild(i), setTimeout(() => { r + c > o && (r = n.right - c + 10), l + h > s && (l = n.top - h - 8), i.style.left = Math.max(5, r) + "px", i.style.top = Math.max(5, l) + "px", document.body.appendChild(i), setTimeout(() => {
i.style.opacity = "1"; i.style.opacity = "1";
}, 10), setTimeout(() => { }, 10), setTimeout(() => {
i.style.opacity = "0", setTimeout(() => { i.style.opacity = "0", setTimeout(() => {
@@ -1704,7 +1772,7 @@ function m(a, e) {
}, 200); }, 200);
}, 2e3); }, 2e3);
} }
function se(a, e, t = !1) { function oe(a, e, t = !1) {
let i = ""; let i = "";
if (t) if (t)
i = window.location.origin + window.location.pathname + `#beilage-1-page-${a}`; i = window.location.origin + window.location.pathname + `#beilage-1-page-${a}`;
@@ -1719,24 +1787,24 @@ function se(a, e, t = !1) {
const n = i; const n = i;
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(n).then(() => { navigator.clipboard.writeText(n).then(() => {
m(e, "Link kopiert!"); b(e, "Link kopiert!");
}).catch((s) => { }).catch((s) => {
m(e, "Kopieren fehlgeschlagen"); b(e, "Kopieren fehlgeschlagen");
}); });
else { else {
const s = document.createElement("textarea"); const s = document.createElement("textarea");
s.value = n, document.body.appendChild(s), s.select(); s.value = n, document.body.appendChild(s), s.select();
try { try {
const o = document.execCommand("copy"); const o = document.execCommand("copy");
m(e, o ? "Link kopiert!" : "Kopieren fehlgeschlagen"); b(e, o ? "Link kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
m(e, "Kopieren fehlgeschlagen"); b(e, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(s); document.body.removeChild(s);
} }
} }
} }
function oe(a, e) { function re(a, e) {
const t = document.title || "KGPZ", i = window.location.pathname.split("/"); const t = document.title || "KGPZ", i = window.location.pathname.split("/");
let n; let n;
if (i.length >= 3) { if (i.length >= 3) {
@@ -1747,33 +1815,33 @@ function oe(a, e) {
const s = n, o = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), r = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${t}, Seite ${a}. Digital verfügbar unter: ${s} (Zugriff: ${o}).`; const s = n, o = (/* @__PURE__ */ new Date()).toLocaleDateString("de-DE"), r = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${t}, Seite ${a}. Digital verfügbar unter: ${s} (Zugriff: ${o}).`;
if (navigator.clipboard) if (navigator.clipboard)
navigator.clipboard.writeText(r).then(() => { navigator.clipboard.writeText(r).then(() => {
m(e, "Zitation kopiert!"); b(e, "Zitation kopiert!");
}).catch((l) => { }).catch((l) => {
m(e, "Kopieren fehlgeschlagen"); b(e, "Kopieren fehlgeschlagen");
}); });
else { else {
const l = document.createElement("textarea"); const l = document.createElement("textarea");
l.value = r, document.body.appendChild(l), l.select(); l.value = r, document.body.appendChild(l), l.select();
try { try {
const c = document.execCommand("copy"); const c = document.execCommand("copy");
m(e, c ? "Zitation kopiert!" : "Kopieren fehlgeschlagen"); b(e, c ? "Zitation kopiert!" : "Kopieren fehlgeschlagen");
} catch { } catch {
m(e, "Kopieren fehlgeschlagen"); b(e, "Kopieren fehlgeschlagen");
} finally { } finally {
document.body.removeChild(l); document.body.removeChild(l);
} }
} }
} }
function M() { function A() {
U(), window.addEventListener("scroll", function() { Q(), window.addEventListener("scroll", function() {
clearTimeout(window.scrollTimeout), window.scrollTimeout = setTimeout(() => { clearTimeout(window.scrollTimeout), window.scrollTimeout = setTimeout(() => {
S(); C();
}, 50); }, 50);
}), document.addEventListener("keydown", function(a) { }), document.addEventListener("keydown", function(a) {
a.key === "Escape" && H(); a.key === "Escape" && M();
}); });
} }
function A() { function B() {
const a = window.location.pathname; const a = window.location.pathname;
document.querySelectorAll(".citation-link[data-citation-url]").forEach((t) => { document.querySelectorAll(".citation-link[data-citation-url]").forEach((t) => {
const i = t.getAttribute("data-citation-url"); const i = t.getAttribute("data-citation-url");
@@ -1783,14 +1851,14 @@ function A() {
else { else {
const s = a.match(/^\/(\d{4})\/(\d+)(?:\/(\d+))?$/), o = i.match(/^\/(\d{4})\/(\d+)$/); const s = a.match(/^\/(\d{4})\/(\d+)(?:\/(\d+))?$/), o = i.match(/^\/(\d{4})\/(\d+)$/);
if (s && o) { if (s && o) {
const [, r, l, c] = s, [, d, u] = o; const [, r, l, c] = s, [, h, d] = o;
r === d && l === u && (n = !0); r === h && l === d && (n = !0);
} }
} }
n ? (t.classList.add("text-red-700", "pointer-events-none"), t.setAttribute("aria-current", "page")) : (t.classList.remove("text-red-700", "pointer-events-none"), t.removeAttribute("aria-current")); n ? (t.classList.add("text-red-700", "pointer-events-none"), t.setAttribute("aria-current", "page")) : (t.classList.remove("text-red-700", "pointer-events-none"), t.removeAttribute("aria-current"));
}); });
} }
function B() { function N() {
const a = window.location.pathname, e = document.body; const a = window.location.pathname, e = document.body;
e.classList.remove( e.classList.remove(
"page-akteure", "page-akteure",
@@ -1802,21 +1870,21 @@ function B() {
"page-edition" "page-edition"
), a.includes("/akteure/") || a.includes("/autoren") ? e.classList.add("page-akteure") : a.match(/\/\d{4}\/\d+/) ? e.classList.add("page-ausgabe") : a.includes("/search") || a.includes("/suche") ? e.classList.add("page-search") : a.includes("/ort/") ? e.classList.add("page-ort") : a.includes("/kategorie/") ? e.classList.add("page-kategorie") : a.includes("/beitrag/") ? e.classList.add("page-piece") : a.includes("/edition") && e.classList.add("page-edition"); ), a.includes("/akteure/") || a.includes("/autoren") ? e.classList.add("page-akteure") : a.match(/\/\d{4}\/\d+/) ? e.classList.add("page-ausgabe") : a.includes("/search") || a.includes("/suche") ? e.classList.add("page-search") : a.includes("/ort/") ? e.classList.add("page-ort") : a.includes("/kategorie/") ? e.classList.add("page-kategorie") : a.includes("/beitrag/") ? e.classList.add("page-piece") : a.includes("/edition") && e.classList.add("page-edition");
} }
window.enlargePage = G; window.enlargePage = U;
window.closeModal = H; window.closeModal = M;
window.scrollToPreviousPage = Q; window.scrollToPreviousPage = ee;
window.scrollToNextPage = ee; window.scrollToNextPage = te;
window.scrollToBeilage = te; window.scrollToBeilage = ie;
window.shareCurrentPage = ie; window.shareCurrentPage = ne;
window.generateCitation = ne; window.generateCitation = se;
window.copyPagePermalink = se; window.copyPagePermalink = oe;
window.generatePageCitation = oe; window.generatePageCitation = re;
N();
B(); B();
A(); document.querySelector(".newspaper-page-container") && A();
document.querySelector(".newspaper-page-container") && M(); let ae = function(a) {
let re = function(a) { N(), B(), H(), setTimeout(() => {
B(), A(), I(), setTimeout(() => { document.querySelector(".newspaper-page-container") && A();
document.querySelector(".newspaper-page-container") && M();
}, 50); }, 50);
}; };
document.body.addEventListener("htmx:afterSettle", re); document.body.addEventListener("htmx:afterSettle", ae);

File diff suppressed because one or more lines are too long

View File

@@ -71,70 +71,66 @@
{{- /* Pieces List */ -}} {{- /* Pieces List */ -}}
<div> <div>
<h2 class="text-xl font-semibold text-slate-800 mb-4"> <h2 class="font-bold mb-4">
<i class="ri-newspaper-line mr-2"></i><u class="decoration underline-offset-3">Beiträge</u> ({{ .model.PieceCount }}) <i class="ri-newspaper-line mr-2"></i><u class="decoration underline-offset-3">Beiträge</u> ({{ .model.PieceCount }})
</h2> </h2>
{{- if .model.Pieces -}} {{- if .model.Pieces -}}
<div class="space-y-6 max-w-[85ch]"> <div class="columns-2 gap-6 hyphens-auto">
<div> <h3 class="font-bold font-serif text-slate-800 mb-1 break-inside-avoid">{{ .model.Year }}</h3>
<h3 class="text-lg font-bold font-serif text-slate-800 mb-3">{{ .model.Year }}</h3>
<div class="space-y-1"> {{- /* Group pieces by title within the year */ -}}
{{- /* Group pieces by title within the year */ -}} {{- $groupedPieces := dict -}}
{{- $groupedPieces := dict -}} {{- range $_, $p := .model.Pieces -}}
{{- range $_, $p := .model.Pieces -}} {{- $groupKey := "" -}}
{{- $groupKey := "" -}} {{- if $p.Title -}}
{{- if $p.Title -}} {{- $groupKey = index $p.Title 0 -}}
{{- $groupKey = index $p.Title 0 -}} {{- else if $p.Incipit -}}
{{- else if $p.Incipit -}} {{- $groupKey = index $p.Incipit 0 -}}
{{- $groupKey = index $p.Incipit 0 -}} {{- else -}}
{{- else -}} {{- $groupKey = printf "untitled-%s" $p.ID -}}
{{- $groupKey = printf "untitled-%s" $p.ID -}} {{- end -}}
{{- end -}}
{{- $existing := index $groupedPieces $groupKey -}} {{- $existing := index $groupedPieces $groupKey -}}
{{- if $existing -}} {{- if $existing -}}
{{- $groupedPieces = merge $groupedPieces (dict $groupKey (append $existing $p)) -}} {{- $groupedPieces = merge $groupedPieces (dict $groupKey (append $existing $p)) -}}
{{- else -}} {{- else -}}
{{- $groupedPieces = merge $groupedPieces (dict $groupKey (slice $p)) -}} {{- $groupedPieces = merge $groupedPieces (dict $groupKey (slice $p)) -}}
{{- end -}} {{- end -}}
{{- end -}} {{- end -}}
{{- range $groupKey, $groupedItems := $groupedPieces -}} {{- range $groupKey, $groupedItems := $groupedPieces -}}
<div> <div class="break-inside-avoid mb-1">
<div class="pb-1 text-lg indent-4"> <div class="pb-1 indent-4">
{{- /* Use first piece for display text with colon format for places */ -}} {{- /* Use first piece for display text with colon format for places */ -}}
{{ template "_unified_piece_entry" (dict "Piece" (index $groupedItems 0) "CurrentActorID" "" "DisplayMode" "place" "ShowPlaceTags" false "UseColonFormat" true "ShowContinuation" false) }} {{ template "_unified_piece_entry" (dict "Piece" (index $groupedItems 0) "CurrentActorID" "" "DisplayMode" "place" "ShowPlaceTags" false "UseColonFormat" true "ShowContinuation" false) }}
{{- /* Show all citations from all pieces in this group inline with commas */ -}} {{- /* Show all citations from all pieces in this group inline with commas */ -}}
{{ " " }}{{- range $groupIndex, $groupItem := $groupedItems -}} {{ " " }}{{- range $groupIndex, $groupItem := $groupedItems -}}
{{- range $issueIndex, $issue := $groupItem.IssueRefs -}} {{- range $issueIndex, $issue := $groupItem.IssueRefs -}}
{{- /* Only show citations for the current year */ -}} {{- /* Only show citations for the current year */ -}}
{{- if eq $issue.When.Year $.model.Year -}} {{- if eq $issue.When.Year $.model.Year -}}
{{- if or (gt $groupIndex 0) (gt $issueIndex 0) }}, {{ end -}} {{- if or (gt $groupIndex 0) (gt $issueIndex 0) }}, {{ end -}}
<span class="text-blue-600 hover:text-blue-700 underline decoration-dotted hover:decoration-solid [&>a]:text-blue-600 [&>a:hover]:text-blue-700">{{- template "_citation" $issue -}}</span> <span class="text-blue-600 hover:text-blue-700 underline decoration-dotted hover:decoration-solid [&>a]:text-blue-600 [&>a:hover]:text-blue-700">{{- template "_citation" $issue -}}</span>
{{- end -}}
{{- end -}}
{{- end -}} {{- end -}}
{{- end -}}
{{- end -}}
{{- /* Add "Ganzer Beitrag" link if piece spans multiple issues */ -}} {{- /* Add "Ganzer Beitrag" link if piece spans multiple issues */ -}}
{{- $firstGroupItem := index $groupedItems 0 -}} {{- $firstGroupItem := index $groupedItems 0 -}}
{{- if gt (len $firstGroupItem.IssueRefs) 1 -}} {{- if gt (len $firstGroupItem.IssueRefs) 1 -}}
{{ " " }}<div class="inline-flex items-center gap-1 px-2 py-1 bg-blue-50 {{ " " }}<div class="inline-flex items-center gap-1 px-2 py-1 bg-blue-50
hover:bg-blue-100 text-blue-700 hover:text-blue-800 border border-blue-200 hover:bg-blue-100 text-blue-700 hover:text-blue-800 border border-blue-200
hover:border-blue-300 rounded text-xs font-medium transition-colors duration-200"> hover:border-blue-300 rounded text-xs font-medium transition-colors duration-200">
<i class="ri-file-copy-2-line text-xs"></i> <i class="ri-file-copy-2-line text-xs"></i>
<a href="{{ GetPieceURL $firstGroupItem.ID }}" class=""> <a href="{{ GetPieceURL $firstGroupItem.ID }}" class="">
Ganzer Beitrag Ganzer Beitrag
</a> </a>
</div>
{{- end }}
</div> </div>
</div> {{- end }}
{{- end -}} </div>
</div> </div>
</div> {{- end -}}
</div> </div>
{{- else -}} {{- else -}}
<div class="bg-slate-50 rounded-lg p-8 text-center"> <div class="bg-slate-50 rounded-lg p-8 text-center">

View File

@@ -1,8 +1,24 @@
<!-- Single Place Detail View --> <!-- Single Place Detail View -->
<div class="max-w-7xl mx-auto px-8 py-8"> <div class="grid grid-cols-1 lg:grid-cols-7 gap-8">
<div class="bg-white px-6 py-6 rounded w-full lg:min-w-[800px] xl:min-w-[900px]"> {{- /* Main content - Place details */ -}}
{{ template "_back_navigation" . }} <div class="lg:col-span-5 pt-4">
{{ template "_place_header" .model.SelectedPlace }} <div class="bg-white px-6 py-6 rounded w-full">
{{ template "_place_pieces" .model.SelectedPlace }} {{ template "_back_navigation" . }}
{{ template "_place_header" .model.SelectedPlace }}
{{ template "_place_pieces" .model.SelectedPlace }}
</div>
</div>
{{- /* Sidebar - Map */ -}}
<div class="lg:col-span-2 sticky top-0 self-start">
<div class="bg-white rounded">
{{ if .model.SelectedPlace.Place.Geo }}
{{ $geonames := GetGeonames .model.SelectedPlace.Place.Geo }}
{{ if and $geonames $geonames.Lat $geonames.Lng }}
{{ $placeJSON := printf `{"id":"%s","name":"%s","toponymName":"%s","lat":"%s","lng":"%s"}` .model.SelectedPlace.Place.ID (index .model.SelectedPlace.Place.Names 0) $geonames.Name $geonames.Lat $geonames.Lng }}
<places-map-single data-place="{{ $placeJSON }}" class="w-full"></places-map-single>
{{ end }}
{{ end }}
</div>
</div> </div>
</div> </div>

View File

@@ -596,10 +596,10 @@ export class PlacesMap extends HTMLElement {
}); });
}, },
{ {
// Trigger when element enters viewport // Trigger when any part enters viewport - better for small elements
threshold: 0.1, threshold: 0,
// No root margin for precise detection // Add some margin to trigger slightly before/after entering viewport
rootMargin: "0px", rootMargin: "10px 0px",
}, },
); );
@@ -608,20 +608,6 @@ export class PlacesMap extends HTMLElement {
this.intersectionObserver.observe(container); this.intersectionObserver.observe(container);
}); });
// Force an immediate check by triggering a scroll event
setTimeout(() => {
// Manually trigger intersection calculation
placeContainers.forEach((container) => {
const rect = container.getBoundingClientRect();
const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
const placeId = container.getAttribute("data-place-id");
const mapPoint = this.mapPoints.get(placeId);
if (mapPoint && isVisible) {
this.setPointActive(mapPoint);
}
});
}, 50);
} }
setPointActive(point) { setPointActive(point) {
@@ -646,6 +632,7 @@ export class PlacesMap extends HTMLElement {
return; return;
} }
if (this.tooltip && tooltipText) { if (this.tooltip && tooltipText) {
// Set tooltip content and position // Set tooltip content and position
this.tooltip.textContent = tooltipText; this.tooltip.textContent = tooltipText;
@@ -784,63 +771,18 @@ export class PlacesMap extends HTMLElement {
// Track the currently hovered place ID // Track the currently hovered place ID
this.currentHoveredPlaceId = placeId; this.currentHoveredPlaceId = placeId;
// Give the point a subtle highlight by making it larger immediately // Give the point a more visible highlight by making it larger immediately
mapPoint.classList.remove("w-1", "h-1", "w-1.5", "h-1.5"); mapPoint.classList.remove("w-1", "h-1", "w-1.5", "h-1.5");
mapPoint.classList.add("w-2", "h-2"); mapPoint.classList.add("w-2.5", "h-2.5");
mapPoint.style.zIndex = "25"; mapPoint.style.zIndex = "25";
// Don't show NEW tooltip if scrolling blocked (behavior 4) // No tooltip when hovering over place titles - only visual feedback
if (this.isNewPopupBlocked(placeId)) {
return;
}
const tooltipText = mapPoint.dataset.tooltipText;
if (this.tooltip && tooltipText) {
// Set tooltip content and position
this.tooltip.textContent = tooltipText;
// Position tooltip at the map point location
const pointRect = mapPoint.getBoundingClientRect();
const mapRect = this.mapElement.getBoundingClientRect();
const x = pointRect.left - mapRect.left + pointRect.width / 2;
const y = pointRect.top - mapRect.top + pointRect.height / 2;
this.tooltip.style.left = `${x}px`;
this.tooltip.style.top = `${y}px`;
// Clear any existing timeouts
this.clearTimeouts();
if (this.isTooltipVisible) {
// Behavior 2b: Popup was visible -- instant popup
this.tooltip.classList.remove("opacity-0");
this.tooltip.classList.add("opacity-100");
} else {
// Behavior 2a: No popup was visible -- Popup after 150ms
this.showTimeout = setTimeout(() => {
this.tooltip.classList.remove("opacity-0");
this.tooltip.classList.add("opacity-100");
this.isTooltipVisible = true;
}, 150);
}
}
} else if (action === "hide") { } else if (action === "hide") {
// Clear the currently hovered place ID // Clear the currently hovered place ID
this.currentHoveredPlaceId = ""; this.currentHoveredPlaceId = "";
// Behavior 3: Leave tile or map point -- close after 150ms delay
// NOTE: This is NOT blocked during scroll - existing popup should close when mouse leaves
this.clearTimeouts();
this.hideTimeout = setTimeout(() => {
if (this.tooltip) {
this.tooltip.classList.remove("opacity-100");
this.tooltip.classList.add("opacity-0");
this.isTooltipVisible = false;
}
}, 150);
// Remove point highlight - restore original size based on current state // Remove point highlight - restore original size based on current state
mapPoint.classList.remove("w-2", "h-2"); mapPoint.classList.remove("w-2.5", "h-2.5");
// Check if this point is currently active or inactive // Check if this point is currently active or inactive
if (mapPoint.className.includes("bg-red-500")) { if (mapPoint.className.includes("bg-red-500")) {
// Active point // Active point
@@ -872,7 +814,229 @@ export class PlacesMap extends HTMLElement {
} }
} }
/**
* Places Map Single Web Component
* Embeds an SVG map with a single highlighted place
*/
export class PlacesMapSingle extends HTMLElement {
constructor() {
super();
this.place = null;
this.mapElement = null;
this.pointsContainer = null;
this.tooltip = null;
}
connectedCallback() {
this.parseData();
this.render();
this.initializeMap();
}
parseData() {
try {
const placeData = this.dataset.place;
if (placeData) {
this.place = JSON.parse(placeData);
}
} catch (error) {
console.error("Failed to parse place data:", error);
this.place = null;
}
}
render() {
this.innerHTML = `
<div class="map-container relative w-full aspect-[5/7] overflow-hidden bg-slate-100">
<div class="transform-wrapper absolute top-0 left-0 w-full h-auto origin-top-left">
<img src="/assets/Europe.svg" alt="Map of Europe" class="block w-full h-auto">
<div class="points-container absolute top-0 left-0 w-full h-full"></div>
</div>
<div class="absolute bottom-0 right-0 h-auto text-[0.6rem] bg-white px-0.5 bg-opacity-[0.5] border">
<i class="ri-creative-commons-line"></i>
<a href="https://commons.wikimedia.org/wiki/File:Europe_laea_topography.svg" target="_blank" class="">
Wikimedia Commons
</a>
</div>
<!-- Tooltip -->
<div class="map-tooltip absolute bg-slate-800 text-white text-sm px-2 py-1 rounded shadow-lg pointer-events-none opacity-0 transition-opacity duration-200 z-30 whitespace-nowrap" style="transform: translate(-50%, -100%); margin-top: -8px;"></div>
</div>
`;
this.mapElement = this.querySelector(".map-container");
this.pointsContainer = this.querySelector(".points-container");
this.tooltip = this.querySelector(".map-tooltip");
}
initializeMap() {
if (!this.place || !this.place.lat || !this.place.lng || !this.pointsContainer) {
return;
}
// Map extent constants
const MAP_EXTENT_METERS = { xmin: 2555000, ymin: 1350000, xmax: 7405000, ymax: 5500000 };
const PROJECTION_CENTER = { lon: 10, lat: 52 };
// Convert lat/lng to % calculation
const convertLatLngToPercent = (lat, lng) => {
const R = 6371000;
const FE = 4321000;
const FN = 3210000;
const lon_0_rad = (PROJECTION_CENTER.lon * Math.PI) / 180;
const lat_0_rad = (PROJECTION_CENTER.lat * Math.PI) / 180;
const lon_rad = (lng * Math.PI) / 180;
const lat_rad = (lat * Math.PI) / 180;
const k_prime = Math.sqrt(
2 /
(1 +
Math.sin(lat_0_rad) * Math.sin(lat_rad) +
Math.cos(lat_0_rad) * Math.cos(lat_rad) * Math.cos(lon_rad - lon_0_rad)),
);
const x_proj = R * k_prime * Math.cos(lat_rad) * Math.sin(lon_rad - lon_0_rad);
const y_proj =
R *
k_prime *
(Math.cos(lat_0_rad) * Math.sin(lat_rad) -
Math.sin(lat_0_rad) * Math.cos(lat_rad) * Math.cos(lon_rad - lon_0_rad));
const finalX = x_proj + FE;
const finalY = y_proj + FN;
const mapWidthMeters = MAP_EXTENT_METERS.xmax - MAP_EXTENT_METERS.xmin;
const mapHeightMeters = MAP_EXTENT_METERS.ymax - MAP_EXTENT_METERS.ymin;
const xPercent = ((finalX - MAP_EXTENT_METERS.xmin) / mapWidthMeters) * 100;
const yPercent = ((MAP_EXTENT_METERS.ymax - finalY) / mapHeightMeters) * 100;
return { x: xPercent, y: yPercent };
};
const lat = parseFloat(this.place.lat);
const lng = parseFloat(this.place.lng);
const position = convertLatLngToPercent(lat, lng);
// Only add point if it's within the visible map area
if (position.x >= 0 && position.x <= 100 && position.y >= 0 && position.y <= 100) {
const point = document.createElement("div");
point.style.left = `${position.x}%`;
point.style.top = `${position.y}%`;
point.style.transformOrigin = "center";
// Single highlighted point - moderately sized for zoomed view
point.className = "absolute w-2 h-2 bg-red-500 border border-red-700 rounded-full shadow-md -translate-x-1/2 -translate-y-1/2 z-20";
const tooltipText = `${this.place.name}${this.place.toponymName && this.place.toponymName !== this.place.name ? ` (${this.place.toponymName})` : ""}`;
point.dataset.tooltipText = tooltipText;
// Add hover event listeners
point.addEventListener("mouseenter", (e) => this.showTooltip(e));
point.addEventListener("mouseleave", () => this.hideTooltip());
point.addEventListener("mousemove", (e) => this.updateTooltipPosition(e));
this.pointsContainer.appendChild(point);
// Auto-zoom to the single point with some padding
this.autoZoomToPoint(position);
}
}
autoZoomToPoint(position) {
// Create a balanced area around the point for zooming
const PADDING = 20; // 20% padding around the point
let minX = Math.max(0, position.x - PADDING);
let maxX = Math.min(100, position.x + PADDING);
let minY = Math.max(0, position.y - PADDING);
let maxY = Math.min(100, position.y + PADDING);
let width = maxX - minX;
let height = maxY - minY;
// Maintain 5:7 aspect ratio
const ASPECT_RATIO = 5 / 7;
const pointsAspectRatio = width / height;
let finalViewBox = { x: minX, y: minY, width: width, height: height };
if (pointsAspectRatio > ASPECT_RATIO) {
const newTargetHeight = width / ASPECT_RATIO;
finalViewBox.y = minY - (newTargetHeight - height) / 2;
finalViewBox.height = newTargetHeight;
} else {
const newTargetWidth = height * ASPECT_RATIO;
finalViewBox.x = minX - (newTargetWidth - width) / 2;
finalViewBox.width = newTargetWidth;
}
// Ensure the final viewbox stays within map boundaries (0-100%)
if (finalViewBox.x < 0) {
finalViewBox.width += finalViewBox.x;
finalViewBox.x = 0;
}
if (finalViewBox.y < 0) {
finalViewBox.height += finalViewBox.y;
finalViewBox.y = 0;
}
if (finalViewBox.x + finalViewBox.width > 100) {
finalViewBox.width = 100 - finalViewBox.x;
}
if (finalViewBox.y + finalViewBox.height > 100) {
finalViewBox.height = 100 - finalViewBox.y;
}
// Ensure minimum zoom level - don't zoom in too much if constrained by boundaries
const MIN_VIEWPORT_SIZE = 30; // Minimum 30% of map should be visible
if (finalViewBox.width < MIN_VIEWPORT_SIZE) {
const expansion = (MIN_VIEWPORT_SIZE - finalViewBox.width) / 2;
finalViewBox.x = Math.max(0, finalViewBox.x - expansion);
finalViewBox.width = Math.min(MIN_VIEWPORT_SIZE, 100 - finalViewBox.x);
}
if (finalViewBox.height < MIN_VIEWPORT_SIZE) {
const expansion = (MIN_VIEWPORT_SIZE - finalViewBox.height) / 2;
finalViewBox.y = Math.max(0, finalViewBox.y - expansion);
finalViewBox.height = Math.min(MIN_VIEWPORT_SIZE, 100 - finalViewBox.y);
}
// Apply transformation
const scale = 100 / finalViewBox.width;
const translateX = -finalViewBox.x;
const translateY = -finalViewBox.y;
const transformValue = `scale(${scale}) translate(${translateX}%, ${translateY}%)`;
const transformWrapper = this.querySelector(".transform-wrapper");
if (transformWrapper) {
transformWrapper.style.transform = transformValue;
}
}
showTooltip(event) {
const point = event.target;
const tooltipText = point.dataset.tooltipText;
if (this.tooltip && tooltipText) {
this.tooltip.textContent = tooltipText;
this.updateTooltipPosition(event);
this.tooltip.classList.remove("opacity-0");
this.tooltip.classList.add("opacity-100");
}
}
hideTooltip() {
if (this.tooltip) {
this.tooltip.classList.remove("opacity-100");
this.tooltip.classList.add("opacity-0");
}
}
updateTooltipPosition(event) {
if (!this.tooltip) return;
const mapRect = this.mapElement.getBoundingClientRect();
const x = event.clientX - mapRect.left;
const y = event.clientY - mapRect.top;
this.tooltip.style.left = `${x}px`;
this.tooltip.style.top = `${y}px`;
}
}
// Register the custom elements // Register the custom elements
customElements.define("places-filter", PlacesFilter); customElements.define("places-filter", PlacesFilter);
customElements.define("place-accordion", PlaceAccordion); customElements.define("place-accordion", PlaceAccordion);
customElements.define("places-map", PlacesMap); customElements.define("places-map", PlacesMap);
customElements.define("places-map-single", PlacesMapSingle);