fix: 3D-Karussell + exakte Leaflet-Karte wie /lagebild
- 3D-Perspektiv-Karussell: Zentrale Card gross, seitliche klein/gekippt - Klick auf seitliche Cards wechselt Ansicht, Dot-Navigation - Karte mit exakten Pulse-Markern (Ring + Dot Animation) - Dark Popups und Dark Legende wie bei /lagen/iran-konflikt/ - Kategorie-Farben und Labels aus der API Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -44,7 +44,6 @@
|
||||
/* ==================== HERO VIDEOS ==================== */
|
||||
var videos = document.querySelectorAll('.hero-video');
|
||||
var currentVideo = 0;
|
||||
var ROTATION_INTERVAL = 12000;
|
||||
var rotationTimer;
|
||||
|
||||
function switchVideo() {
|
||||
@@ -57,10 +56,9 @@
|
||||
}
|
||||
|
||||
function startRotation() {
|
||||
rotationTimer = setInterval(switchVideo, ROTATION_INTERVAL);
|
||||
rotationTimer = setInterval(switchVideo, 12000);
|
||||
}
|
||||
|
||||
// Pause when tab hidden
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (document.hidden) {
|
||||
clearInterval(rotationTimer);
|
||||
@@ -73,10 +71,40 @@
|
||||
|
||||
if (videos.length > 1) startRotation();
|
||||
|
||||
/* ==================== 3D CAROUSEL ==================== */
|
||||
var cards = document.querySelectorAll('.carousel-card');
|
||||
var dots = document.querySelectorAll('.carousel-dot');
|
||||
var activeIndex = 0;
|
||||
|
||||
function positionCards(idx) {
|
||||
activeIndex = idx;
|
||||
cards.forEach(function (card, i) {
|
||||
card.classList.remove('active', 'left', 'right', 'hidden');
|
||||
if (i === idx) card.classList.add('active');
|
||||
else if (i === (idx - 1 + cards.length) % cards.length) card.classList.add('left');
|
||||
else if (i === (idx + 1) % cards.length) card.classList.add('right');
|
||||
else card.classList.add('hidden');
|
||||
});
|
||||
dots.forEach(function (dot, i) {
|
||||
dot.classList.toggle('active', i === idx);
|
||||
});
|
||||
}
|
||||
|
||||
cards.forEach(function (card, i) {
|
||||
card.addEventListener('click', function () {
|
||||
if (!card.classList.contains('active')) positionCards(i);
|
||||
});
|
||||
});
|
||||
|
||||
dots.forEach(function (dot, i) {
|
||||
dot.addEventListener('click', function () { positionCards(i); });
|
||||
});
|
||||
|
||||
positionCards(0);
|
||||
|
||||
/* ==================== SIMPLE MARKDOWN ==================== */
|
||||
function mdToText(md) {
|
||||
function mdToHtml(md) {
|
||||
if (!md) return '';
|
||||
// Remove ## headers and bold markers, keep text
|
||||
return md
|
||||
.replace(/^## .+$/gm, '')
|
||||
.replace(/^### .+$/gm, '')
|
||||
@@ -101,17 +129,13 @@
|
||||
return 'Aktualisiert vor ' + diffD + (diffD === 1 ? ' Tag' : ' Tagen');
|
||||
}
|
||||
|
||||
var liveData = null;
|
||||
|
||||
function loadLiveData() {
|
||||
fetch('/lagen/iran-konflikt/data/current.json?t=' + Date.now())
|
||||
.then(function (r) { if (!r.ok) throw new Error(r.status); return r.json(); })
|
||||
.then(function (data) {
|
||||
liveData = data;
|
||||
var inc = data.incident || {};
|
||||
var lag = data.current_lagebild || {};
|
||||
|
||||
// Stats
|
||||
var ea = document.getElementById('stat-articles');
|
||||
var es = document.getElementById('stat-sources');
|
||||
var ef = document.getElementById('stat-factchecks');
|
||||
@@ -125,10 +149,8 @@
|
||||
var excerptEl = document.getElementById('excerpt-text');
|
||||
var toggleBtn = document.getElementById('excerpt-toggle');
|
||||
if (excerptEl && lag.summary_markdown) {
|
||||
var html = mdToText(lag.summary_markdown);
|
||||
excerptEl.innerHTML = html;
|
||||
excerptEl.innerHTML = mdToHtml(lag.summary_markdown);
|
||||
toggleBtn.style.display = 'inline-block';
|
||||
|
||||
toggleBtn.addEventListener('click', function () {
|
||||
var expanded = excerptEl.classList.toggle('expanded');
|
||||
this.textContent = expanded ? 'Weniger anzeigen' : 'Weiterlesen';
|
||||
@@ -146,8 +168,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
/* ==================== LEAFLET MAP ==================== */
|
||||
function initMap(locations, categoryLabels) {
|
||||
/* ==================== LEAFLET MAP (exact lagebild style) ==================== */
|
||||
function initMap(locations, apiLabels) {
|
||||
var mapEl = document.getElementById('map-container');
|
||||
if (!mapEl || typeof L === 'undefined') return;
|
||||
|
||||
@@ -155,46 +177,91 @@
|
||||
center: [33.0, 48.0],
|
||||
zoom: 5,
|
||||
zoomControl: true,
|
||||
scrollWheelZoom: false
|
||||
scrollWheelZoom: false,
|
||||
minZoom: 2,
|
||||
maxBounds: [[-85, -180], [85, 180]],
|
||||
maxBoundsViscosity: 1.0
|
||||
});
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', {
|
||||
attribution: '© OpenStreetMap',
|
||||
maxZoom: 18
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
maxZoom: 19,
|
||||
noWrap: true
|
||||
}).addTo(map);
|
||||
|
||||
var catColors = {
|
||||
primary: '#E74C3C',
|
||||
secondary: '#F39C12',
|
||||
tertiary: '#3498DB',
|
||||
mentioned: '#95A5A6'
|
||||
// Exact same pulse icon as lagebild
|
||||
function pulseIcon(color) {
|
||||
return L.divIcon({
|
||||
className: '',
|
||||
html: '<div class="pulse-marker-wrapper">'
|
||||
+ '<div class="pulse-marker-ring" style="border-color:' + color + '"></div>'
|
||||
+ '<div class="pulse-marker-ring" style="border-color:' + color + '"></div>'
|
||||
+ '<div class="pulse-marker-dot" style="background:' + color + ';box-shadow:0 0 10px ' + color + '"></div>'
|
||||
+ '</div>',
|
||||
iconSize: [20, 20],
|
||||
iconAnchor: [10, 10],
|
||||
popupAnchor: [0, -12]
|
||||
});
|
||||
}
|
||||
|
||||
var categoryColors = {
|
||||
primary: '#ef4444',
|
||||
secondary: '#f59e0b',
|
||||
tertiary: '#3b82f6',
|
||||
mentioned: '#7b7b7b'
|
||||
};
|
||||
|
||||
var defaultLabels = {
|
||||
primary: 'Hauptgeschehen',
|
||||
secondary: 'Reaktionen',
|
||||
tertiary: 'Beteiligte',
|
||||
mentioned: 'Erwähnt'
|
||||
};
|
||||
|
||||
var categoryLabels = {};
|
||||
['primary', 'secondary', 'tertiary', 'mentioned'].forEach(function (k) {
|
||||
categoryLabels[k] = (apiLabels && apiLabels[k]) || defaultLabels[k];
|
||||
});
|
||||
|
||||
var usedCategories = {};
|
||||
var bounds = [];
|
||||
|
||||
locations.forEach(function (loc) {
|
||||
if (!loc.lat || !loc.lon) return;
|
||||
var cat = loc.category || 'mentioned';
|
||||
var color = catColors[cat] || catColors.mentioned;
|
||||
var catClass = 'cat-' + cat;
|
||||
var color = categoryColors[cat] || '#7b7b7b';
|
||||
usedCategories[cat] = true;
|
||||
|
||||
var icon = L.divIcon({
|
||||
className: 'pulse-marker ' + catClass,
|
||||
iconSize: [12, 12],
|
||||
iconAnchor: [6, 6]
|
||||
});
|
||||
var popup = '<strong style="color:#E8ECF4;">' + (loc.name || '') + '</strong>';
|
||||
if (loc.country_code) popup += ' <span style="color:#8896AB;font-size:0.8rem;">(' + loc.country_code + ')</span>';
|
||||
popup += '<br><span style="font-size:0.85rem;color:#8896AB;">' + (loc.article_count || 0) + ' Artikel</span>';
|
||||
|
||||
var marker = L.marker([loc.lat, loc.lon], { icon: icon }).addTo(map);
|
||||
var label = categoryLabels[cat] || cat;
|
||||
marker.bindPopup('<strong>' + loc.name + '</strong><br>' + label + ' (' + (loc.article_count || 0) + ' Artikel)');
|
||||
L.marker([loc.lat, loc.lon], { icon: pulseIcon(color) })
|
||||
.addTo(map)
|
||||
.bindPopup(popup);
|
||||
bounds.push([loc.lat, loc.lon]);
|
||||
});
|
||||
|
||||
// Dark legend (exact lagebild style)
|
||||
var legend = L.control({ position: 'bottomright' });
|
||||
legend.onAdd = function () {
|
||||
var div = L.DomUtil.create('div');
|
||||
div.style.cssText = 'background:#151D2E;padding:10px 14px;border-radius:4px;border:1px solid #1E2D45;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:0.8rem;line-height:1.8;color:#E8ECF4;';
|
||||
var html = '<strong style="color:#C8A851;">Legende</strong><br>';
|
||||
['primary', 'secondary', 'tertiary', 'mentioned'].forEach(function (cat) {
|
||||
if (usedCategories[cat]) {
|
||||
html += '<span style="color:' + categoryColors[cat] + ';">●</span> ' + categoryLabels[cat] + '<br>';
|
||||
}
|
||||
});
|
||||
div.innerHTML = html;
|
||||
return div;
|
||||
};
|
||||
legend.addTo(map);
|
||||
|
||||
if (bounds.length > 0) {
|
||||
map.fitBounds(bounds, { padding: [30, 30], maxZoom: 7 });
|
||||
}
|
||||
|
||||
// Fix tile rendering on hidden tab / late load
|
||||
setTimeout(function () { map.invalidateSize(); }, 500);
|
||||
}
|
||||
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren