Fix: Leaflet 'L is not defined' - Integrity-Hashes entfernt + Guard

- SRI integrity-Hashes von Leaflet CDN-Links entfernt (verursachen
  stille Blockierung wenn Hash nicht passt)
- renderMap() prueft ob L verfuegbar ist und merkt sich Locations
- retryPendingMap() rendert nachtraeglich wenn Leaflet spaeter bereit ist
- 2s Retry-Timer nach init() als Fallback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-03-04 22:46:58 +01:00
Ursprung 2f6dd97100
Commit 75df9fc66d
3 geänderte Dateien mit 33 neuen und 8 gelöschten Zeilen

Datei anzeigen

@@ -13,10 +13,10 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css"> <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css">
<link rel="stylesheet" href="/static/css/style.css?v=20260304d"> <link rel="stylesheet" href="/static/css/style.css?v=20260304e">
</head> </head>
<body> <body>
<a href="#main-content" class="skip-link">Zum Hauptinhalt springen</a> <a href="#main-content" class="skip-link">Zum Hauptinhalt springen</a>
@@ -558,12 +558,12 @@
<div class="toast-container" id="toast-container" aria-live="polite" aria-atomic="true"></div> <div class="toast-container" id="toast-container" aria-live="polite" aria-atomic="true"></div>
<script src="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack-all.js"></script> <script src="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack-all.js"></script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script> <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script> <script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js"></script>
<script src="/static/js/api.js?v=20260304d"></script> <script src="/static/js/api.js?v=20260304e"></script>
<script src="/static/js/ws.js?v=20260304d"></script> <script src="/static/js/ws.js?v=20260304e"></script>
<script src="/static/js/components.js?v=20260304d"></script> <script src="/static/js/components.js?v=20260304e"></script>
<script src="/static/js/layout.js?v=20260304d"></script> <script src="/static/js/layout.js?v=20260304e"></script>
<script src="/static/js/app.js?v=20260304d"></script> <script src="/static/js/app.js?v=20260304e"></script>
</body> </body>
</html> </html>

Datei anzeigen

@@ -497,6 +497,9 @@ const App = {
await this.selectIncident(id); await this.selectIncident(id);
} }
} }
// Leaflet-Karte nachladen falls CDN langsam war
setTimeout(() => UI.retryPendingMap(), 2000);
}, },
async loadIncidents() { async loadIncidents() {

Datei anzeigen

@@ -602,12 +602,26 @@ const UI = {
_map: null, _map: null,
_mapCluster: null, _mapCluster: null,
_pendingLocations: null,
renderMap(locations) { renderMap(locations) {
const container = document.getElementById('map-container'); const container = document.getElementById('map-container');
const emptyEl = document.getElementById('map-empty'); const emptyEl = document.getElementById('map-empty');
const statsEl = document.getElementById('map-stats'); const statsEl = document.getElementById('map-stats');
if (!container) return; if (!container) return;
// Leaflet noch nicht geladen? Locations merken und spaeter rendern
if (typeof L === 'undefined') {
this._pendingLocations = locations;
// Statistik trotzdem anzeigen
if (locations && locations.length > 0) {
const totalArticles = locations.reduce((s, l) => s + l.article_count, 0);
if (statsEl) statsEl.textContent = `${locations.length} Orte / ${totalArticles} Artikel`;
if (emptyEl) emptyEl.style.display = 'none';
}
return;
}
if (!locations || locations.length === 0) { if (!locations || locations.length === 0) {
if (emptyEl) emptyEl.style.display = 'flex'; if (emptyEl) emptyEl.style.display = 'flex';
if (statsEl) statsEl.textContent = ''; if (statsEl) statsEl.textContent = '';
@@ -723,6 +737,14 @@ const UI = {
if (this._map) this._map.invalidateSize(); if (this._map) this._map.invalidateSize();
}, },
retryPendingMap() {
if (this._pendingLocations && typeof L !== 'undefined') {
const locs = this._pendingLocations;
this._pendingLocations = null;
this.renderMap(locs);
}
},
/** /**
* HTML escapen. * HTML escapen.
*/ */