// lazyflat — Leaflet flat map
// Initialised LAZILY: we only build the Leaflet instance when the container
// actually has a rendered size (> 0 height). Building it on a hidden 0×0
// container leaves Leaflet in a state where tiles never load.
let mapInstance = null;
const BERLIN_CENTER = [52.52, 13.405];
const BERLIN_ZOOM = 11;
function buildMap(el) {
if (mapInstance) {
try { mapInstance.remove(); } catch (e) {}
mapInstance = null;
}
mapInstance = L.map(el, {
zoomControl: false,
scrollWheelZoom: false,
doubleClickZoom: false,
boxZoom: false,
touchZoom: false,
keyboard: false,
}).setView(BERLIN_CENTER, BERLIN_ZOOM);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap",
maxZoom: 18,
subdomains: "abc",
}).addTo(mapInstance);
let data = [];
try { data = JSON.parse(el.dataset.flats || "[]"); } catch (e) {}
const bounds = [];
data.forEach((f) => {
if (typeof f.lat !== "number" || typeof f.lng !== "number") return;
const rent = f.rent ? Math.round(f.rent) + " €" : "";
const rooms = f.rooms ? f.rooms + " Zi" : "";
const size = f.size ? Math.round(f.size) + " m²" : "";
const meta = [rooms, size, rent].filter(Boolean).join(" · ");
const safeAddr = (f.address || "").replace(/${safeAddr}` + (meta ? `
${meta}` : "") +
`
Zur Anzeige →`,
);
bounds.push([f.lat, f.lng]);
});
if (bounds.length === 1) {
mapInstance.setView(bounds[0], 14);
} else if (bounds.length > 1) {
mapInstance.fitBounds(bounds, { padding: [30, 30] });
}
}
function ensureMap() {
const el = document.getElementById("flats-map");
if (!el || typeof L === "undefined") return;
// Container not actually visible yet → bail, we'll retry when the view toggles.
if (el.clientHeight < 10) return;
// Existing instance bound to THIS element → just recheck size.
if (mapInstance && mapInstance._container === el) {
try { mapInstance.invalidateSize(); } catch (e) {}
return;
}
buildMap(el);
}
function wireViewToggle() {
document.querySelectorAll('input[name="view_mode"]').forEach((r) => {
if (r.dataset.wired === "1") return;
r.dataset.wired = "1";
r.addEventListener("change", (e) => {
try { localStorage.setItem("lazyflat_view_mode", e.target.value); } catch (err) {}
// Wait for CSS :has() to reflow, then build/size the map.
requestAnimationFrame(() => requestAnimationFrame(ensureMap));
// belt & suspenders — re-check a couple more times in case of layout shifts.
setTimeout(ensureMap, 120);
setTimeout(ensureMap, 400);
});
});
}
function restoreView() {
let stored = null;
try { stored = localStorage.getItem("lazyflat_view_mode"); } catch (e) {}
if (!stored) return;
const el = document.querySelector(`input[name="view_mode"][value="${stored}"]`);
if (el && !el.checked) {
el.checked = true;
// Manually dispatching change would bubble and double-fire; call directly.
requestAnimationFrame(() => requestAnimationFrame(ensureMap));
setTimeout(ensureMap, 120);
setTimeout(ensureMap, 400);
}
}
function onReady() {
wireViewToggle();
restoreView();
ensureMap(); // handles the case where map view is already visible
}
document.addEventListener("DOMContentLoaded", onReady);
document.body && document.body.addEventListener("htmx:afterSwap", onReady);