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:
@@ -360,3 +360,73 @@ a { color:inherit; text-decoration:none; }
|
||||
color: #0A1832;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* === Neueste Entwicklungen (Live-Monitoring Vorschau) === */
|
||||
.dev-list-heading {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.8px;
|
||||
text-transform: uppercase;
|
||||
color: #C8A851;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.dev-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.dev-bullet {
|
||||
background: rgba(30, 45, 69, 0.45);
|
||||
border-left: 3px solid #C8A851;
|
||||
border-radius: 4px;
|
||||
padding: 8px 12px;
|
||||
text-align: left;
|
||||
}
|
||||
.dev-bullet-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.dev-sources {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
.dev-source-pill {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
background: rgba(200, 168, 81, 0.15);
|
||||
color: #E8ECF4;
|
||||
border-radius: 3px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
line-height: 1.5;
|
||||
max-width: 180px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
a.dev-source-pill:hover {
|
||||
background: rgba(200, 168, 81, 0.3);
|
||||
text-decoration: none;
|
||||
color: #E8ECF4;
|
||||
}
|
||||
.dev-time {
|
||||
color: #8896AB;
|
||||
font-size: 0.7rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.dev-body {
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.45;
|
||||
color: #E8ECF4;
|
||||
}
|
||||
|
||||
@@ -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, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"');
|
||||
}
|
||||
|
||||
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 '';
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren