Lagebild-Seite: Tab Neueste Entwicklungen / Zusammenfassung

Live-Lagen (iran-konflikt, cyberangriffe) bekommen einen neuen ersten Tab
Neueste Entwicklungen mit Bullet-Cards, klickbaren Quellen-Pills und
Zeitstempel. Recherche-Lagen (deepfakes) bekommen an selber Stelle einen
Tab Zusammenfassung, der den Zusammenfassung-Abschnitt aus dem Markdown
extrahiert und mit Citation-Links rendert.

lagebild.js: renderUeberblick, renderLatestDevelopmentsHtml,
extractZusammenfassung ergaenzt. i18n-Keys tabUeberblick/Research.
Lang-Toggle aktualisiert Tab-Label und h2.

Vorschau-Karten zeigen wieder den Lagebild-Text fuer alle Lagen
(renderLatestDevelopments-Calls aus loadLiveData entfernt).
Dieser Commit ist enthalten in:
Claude Code
2026-04-19 00:19:14 +02:00
Ursprung 8b8072efe7
Commit c3bae27837
7 geänderte Dateien mit 415 neuen und 7 gelöschten Zeilen

Datei anzeigen

@@ -216,6 +216,102 @@
positionCards((activeIndex + 1) % cards.length);
});
/* ==================== NEUESTE ENTWICKLUNGEN (Live-Monitoring) ==================== */
function htmlEscape(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function normalizeSourceName(s) {
return String(s || '').toLowerCase().replace(/^(der|die|das)\s+/, '').replace(/\s+/g, ' ').trim();
}
function renderLatestDevelopments(text, sources) {
if (!text) return null;
sources = Array.isArray(sources) ? sources : [];
var lines = text.split('\n').map(function (l) { return l.trim(); })
.filter(function (l) { return l && (l.charAt(0) === '-' || l.charAt(0) === '['); });
if (!lines.length) return null;
var bulletRe = /^(?:-\s*)?\[\s*(\d{1,2})\.(\d{1,2})\.?(?:\d{2,4})?\s+(\d{1,2}:\d{2})\s*\]\s*(.+?)\s*$/;
var trailingRe = /\s*\{([^{}]+)\}\s*\.?\s*$/;
var citationRe = /\[(\d+[a-z]?)\]/g;
var junkRe = /^(unbekannt|unknown|n\/?a|keine|keine quelle|tba)$/i;
function buildPill(src, name) {
var disp = (src && src.name) || name;
var e = htmlEscape(disp);
if (src && src.url) {
return '<a href="' + htmlEscape(src.url) + '" target="_blank" rel="noopener" class="dev-source-pill" title="' + e + '">' + e + '</a>';
}
return '<span class="dev-source-pill" title="' + e + '">' + e + '</span>';
}
function lookupByName(name) {
var n = normalizeSourceName(name);
if (!n) return null;
var exact = sources.find(function (s) { return normalizeSourceName(s.name) === n; });
if (exact) return exact;
return sources.find(function (s) {
var sn = normalizeSourceName(s.name);
return sn && (sn.indexOf(n) !== -1 || n.indexOf(sn) !== -1);
}) || null;
}
var cards = [];
for (var i = 0; i < lines.length; i++) {
var m = bulletRe.exec(lines[i]);
if (!m) continue;
var date = String(m[1]).padStart(2, '0') + '.' + String(m[2]).padStart(2, '0') + '.';
var time = m[3];
var body = m[4];
var pills = '';
var t = trailingRe.exec(body);
if (t) {
body = body.replace(trailingRe, '').trim();
var names = t[1].split(',').map(function (n) { return n.trim(); }).filter(Boolean);
var seen = {};
pills = names.map(function (n) {
if (junkRe.test(n)) return '';
var key = normalizeSourceName(n);
if (seen[key]) return '';
seen[key] = true;
return buildPill(lookupByName(n), n);
}).filter(Boolean).join('');
}
if (!pills) {
var nums = [];
var cm;
while ((cm = citationRe.exec(body)) !== null) {
if (nums.indexOf(cm[1]) === -1) nums.push(cm[1]);
}
citationRe.lastIndex = 0;
if (nums.length) {
body = body.replace(citationRe, '').replace(/\s+/g, ' ').trim();
pills = nums.map(function (num) {
var src = sources.find(function (s) { return String(s.nr) === num || Number(s.nr) === Number(num); });
return src ? buildPill(src, src.name) : '';
}).filter(Boolean).join('');
}
}
var head = '<div class="dev-bullet-head">'
+ '<span class="dev-sources">' + pills + '</span>'
+ '<span class="dev-time">' + htmlEscape(time) + ' \u00b7 ' + htmlEscape(date) + '</span>'
+ '</div>';
cards.push('<div class="dev-bullet">' + head + '<div class="dev-body">' + htmlEscape(body) + '</div></div>');
}
if (!cards.length) return null;
return '<div class="dev-list-heading">Neueste Entwicklungen</div>'
+ '<div class="dev-list">' + cards.join('') + '</div>';
}
/* ==================== SIMPLE MARKDOWN ==================== */
function mdToHtml(md) {
if (!md) return '';