mirror of
https://github.com/smittix/intercept.git
synced 2026-07-17 05:48:11 -07:00
Fix ACARS message display and add missing decoded data types
- Use global InterceptTime for all ACARS timestamps (respects Eastern/12h) - Add weather message rendering (wind, temperature, turbulence) - Add CPDLC controller-pilot message rendering (purple highlight) - Add squawk code change rendering (red highlight) - Fix engine_data crash when parsed value isn't an object - Show tail/registration alongside flight number on all cards - Increase message text truncation to 200 chars - Add FL prefix to flight level in position reports - Applied consistently across ADS-B dashboard, sidebar feed, and standalone ACARS mode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3525,15 +3525,17 @@ sudo make install</code>
|
|||||||
return '<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:' + color + ';">' + lbl + '</span>';
|
return '<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:' + color + ';">' + lbl + '</span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Similar to renderAcarsMainCard in partials/modes/acars.html — consider unifying
|
|
||||||
function renderAcarsCard(msg) {
|
function renderAcarsCard(msg) {
|
||||||
const type = msg.message_type || 'other';
|
const type = msg.message_type || 'other';
|
||||||
const badge = getAcarsTypeBadge(type);
|
const badge = getAcarsTypeBadge(type);
|
||||||
const desc = escapeHtml(msg.label_description || ('Label ' + (msg.label || '?')));
|
const desc = escapeHtml(msg.label_description || ('Label ' + (msg.label || '?')));
|
||||||
const text = msg.text || msg.msg || '';
|
const text = msg.text || msg.msg || '';
|
||||||
const truncText = escapeHtml(text.length > 120 ? text.substring(0, 120) + '...' : text);
|
const truncText = escapeHtml(text.length > 200 ? text.substring(0, 200) + '...' : text);
|
||||||
const time = msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : '';
|
const time = msg.timestamp && typeof InterceptTime !== 'undefined'
|
||||||
|
? InterceptTime.shortTime(msg.timestamp) + InterceptTime.tzSuffix()
|
||||||
|
: (msg.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : '');
|
||||||
const flight = escapeHtml(msg.flight || '');
|
const flight = escapeHtml(msg.flight || '');
|
||||||
|
const tail = escapeHtml(msg.tail || msg.reg || '');
|
||||||
|
|
||||||
let parsedHtml = '';
|
let parsedHtml = '';
|
||||||
if (msg.parsed) {
|
if (msg.parsed) {
|
||||||
@@ -3541,23 +3543,35 @@ sudo make install</code>
|
|||||||
if (type === 'position' && p.lat !== undefined) {
|
if (type === 'position' && p.lat !== undefined) {
|
||||||
parsedHtml = '<div style="color:var(--accent-green);margin-top:2px;">' +
|
parsedHtml = '<div style="color:var(--accent-green);margin-top:2px;">' +
|
||||||
p.lat.toFixed(4) + ', ' + p.lon.toFixed(4) +
|
p.lat.toFixed(4) + ', ' + p.lon.toFixed(4) +
|
||||||
(p.flight_level ? ' • ' + escapeHtml(String(p.flight_level)) : '') +
|
(p.flight_level ? ' • FL' + escapeHtml(String(p.flight_level)) : '') +
|
||||||
(p.destination ? ' → ' + escapeHtml(String(p.destination)) : '') + '</div>';
|
(p.destination ? ' → ' + escapeHtml(String(p.destination)) : '') + '</div>';
|
||||||
} else if (type === 'engine_data') {
|
} else if (type === 'engine_data') {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
Object.keys(p).forEach(k => {
|
Object.keys(p).forEach(k => {
|
||||||
parts.push(escapeHtml(k) + ': ' + escapeHtml(String(p[k].value)));
|
const val = typeof p[k] === 'object' ? p[k].value : p[k];
|
||||||
|
parts.push(escapeHtml(k) + ': ' + escapeHtml(String(val)));
|
||||||
});
|
});
|
||||||
if (parts.length) {
|
if (parts.length) {
|
||||||
parsedHtml = '<div style="color:var(--accent-orange,#ff9500);margin-top:2px;">' + parts.slice(0, 4).join(' | ') + '</div>';
|
parsedHtml = '<div style="color:var(--accent-orange,#ff9500);margin-top:2px;">' + parts.slice(0, 4).join(' | ') + '</div>';
|
||||||
}
|
}
|
||||||
} else if (type === 'oooi' && p.origin) {
|
} else if (type === 'oooi' && p.origin) {
|
||||||
parsedHtml = '<div style="color:var(--accent-cyan);margin-top:2px;">' +
|
parsedHtml = '<div style="color:var(--accent-cyan);margin-top:2px;">' +
|
||||||
escapeHtml(String(p.origin)) + ' → ' + escapeHtml(String(p.destination)) +
|
escapeHtml(String(p.origin)) + ' → ' + escapeHtml(String(p.destination)) +
|
||||||
(p.out ? ' | OUT ' + escapeHtml(String(p.out)) : '') +
|
(p.out ? ' | OUT ' + escapeHtml(String(p.out)) : '') +
|
||||||
(p.off ? ' OFF ' + escapeHtml(String(p.off)) : '') +
|
(p.off ? ' OFF ' + escapeHtml(String(p.off)) : '') +
|
||||||
(p.on ? ' ON ' + escapeHtml(String(p.on)) : '') +
|
(p.on ? ' ON ' + escapeHtml(String(p.on)) : '') +
|
||||||
(p['in'] ? ' IN ' + escapeHtml(String(p['in'])) : '') + '</div>';
|
(p['in'] ? ' IN ' + escapeHtml(String(p['in'])) : '') + '</div>';
|
||||||
|
} else if (type === 'weather' && (p.wind_speed || p.temperature)) {
|
||||||
|
const wx = [];
|
||||||
|
if (p.wind_speed) wx.push('Wind ' + escapeHtml(String(p.wind_speed)) + (p.wind_dir ? '/' + escapeHtml(String(p.wind_dir)) : ''));
|
||||||
|
if (p.temperature) wx.push(escapeHtml(String(p.temperature)) + '°C');
|
||||||
|
if (p.turbulence) wx.push('Turb: ' + escapeHtml(String(p.turbulence)));
|
||||||
|
if (wx.length) parsedHtml = '<div style="color:#00d4ff;margin-top:2px;">' + wx.join(' | ') + '</div>';
|
||||||
|
} else if (type === 'cpdlc') {
|
||||||
|
const cpdlcText = p.message || p.text || '';
|
||||||
|
if (cpdlcText) parsedHtml = '<div style="color:#b388ff;margin-top:2px;font-weight:600;">' + escapeHtml(String(cpdlcText)) + '</div>';
|
||||||
|
} else if (type === 'squawk' && p.squawk) {
|
||||||
|
parsedHtml = '<div style="color:#ff6b6b;margin-top:2px;font-weight:600;">Squawk: ' + escapeHtml(String(p.squawk)) + '</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3565,7 +3579,7 @@ sudo make install</code>
|
|||||||
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px;">' +
|
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px;">' +
|
||||||
'<span>' + badge + ' <span style="color:var(--text-primary);">' + desc + '</span></span>' +
|
'<span>' + badge + ' <span style="color:var(--text-primary);">' + desc + '</span></span>' +
|
||||||
'<span style="color:var(--text-muted);font-size:9px;">' + time + '</span></div>' +
|
'<span style="color:var(--text-muted);font-size:9px;">' + time + '</span></div>' +
|
||||||
(flight ? '<div style="color:var(--accent-cyan);font-size:9px;">' + flight + '</div>' : '') +
|
(flight || tail ? '<div style="color:var(--accent-cyan);font-size:9px;">' + flight + (tail ? ' (' + tail + ')' : '') + '</div>' : '') +
|
||||||
parsedHtml +
|
parsedHtml +
|
||||||
(truncText && type !== 'link_test' && type !== 'handshake' ?
|
(truncText && type !== 'link_test' && type !== 'handshake' ?
|
||||||
'<div style="color:var(--text-dim);font-family:var(--font-mono);font-size:9px;margin-top:2px;word-break:break-all;">' + truncText + '</div>' : '') +
|
'<div style="color:var(--text-dim);font-family:var(--font-mono);font-size:9px;margin-top:2px;word-break:break-all;">' + truncText + '</div>' : '') +
|
||||||
@@ -4398,7 +4412,9 @@ sudo make install</code>
|
|||||||
const labelDesc = data.label_description || '';
|
const labelDesc = data.label_description || '';
|
||||||
const msgType = data.message_type || 'other';
|
const msgType = data.message_type || 'other';
|
||||||
const text = data.text || data.msg || '';
|
const text = data.text || data.msg || '';
|
||||||
const time = new Date().toLocaleTimeString();
|
const time = typeof InterceptTime !== 'undefined'
|
||||||
|
? InterceptTime.shortTime(new Date()) + InterceptTime.tzSuffix()
|
||||||
|
: new Date().toLocaleTimeString();
|
||||||
|
|
||||||
// Escape user-controlled strings for safe innerHTML insertion
|
// Escape user-controlled strings for safe innerHTML insertion
|
||||||
const eFlight = escapeHtml(flight);
|
const eFlight = escapeHtml(flight);
|
||||||
|
|||||||
@@ -188,33 +188,49 @@
|
|||||||
return `<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:${color};">${lbl}</span>`;
|
return `<span style="display:inline-block;padding:1px 5px;border-radius:3px;font-size:8px;font-weight:700;color:#000;background:${color};">${lbl}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Similar to renderAcarsCard in templates/adsb_dashboard.html — consider unifying
|
|
||||||
function renderAcarsMainCard(data) {
|
function renderAcarsMainCard(data) {
|
||||||
const flight = escapeHtml(data.flight || 'UNKNOWN');
|
const flight = escapeHtml(data.flight || 'UNKNOWN');
|
||||||
|
const tail = escapeHtml(data.tail || data.reg || '');
|
||||||
const type = data.message_type || 'other';
|
const type = data.message_type || 'other';
|
||||||
const badge = acarsMainTypeBadge(type);
|
const badge = acarsMainTypeBadge(type);
|
||||||
const desc = escapeHtml(data.label_description || (data.label ? 'Label: ' + data.label : ''));
|
const desc = escapeHtml(data.label_description || (data.label ? 'Label: ' + data.label : ''));
|
||||||
const text = data.text || data.msg || '';
|
const text = data.text || data.msg || '';
|
||||||
const truncText = escapeHtml(text.length > 150 ? text.substring(0, 150) + '...' : text);
|
const truncText = escapeHtml(text.length > 200 ? text.substring(0, 200) + '...' : text);
|
||||||
const time = new Date().toLocaleTimeString();
|
const time = typeof InterceptTime !== 'undefined'
|
||||||
|
? InterceptTime.shortTime(new Date()) + InterceptTime.tzSuffix()
|
||||||
|
: new Date().toLocaleTimeString();
|
||||||
|
|
||||||
let parsedHtml = '';
|
let parsedHtml = '';
|
||||||
if (data.parsed) {
|
if (data.parsed) {
|
||||||
const p = data.parsed;
|
const p = data.parsed;
|
||||||
if (type === 'position' && p.lat !== undefined) {
|
if (type === 'position' && p.lat !== undefined) {
|
||||||
parsedHtml = `<div style="color:var(--accent-green);margin-top:2px;font-size:10px;">${p.lat.toFixed(4)}, ${p.lon.toFixed(4)}${p.flight_level ? ' • ' + escapeHtml(String(p.flight_level)) : ''}${p.destination ? ' → ' + escapeHtml(String(p.destination)) : ''}</div>`;
|
parsedHtml = `<div style="color:var(--accent-green);margin-top:2px;font-size:10px;">${p.lat.toFixed(4)}, ${p.lon.toFixed(4)}${p.flight_level ? ' • FL' + escapeHtml(String(p.flight_level)) : ''}${p.destination ? ' → ' + escapeHtml(String(p.destination)) : ''}</div>`;
|
||||||
} else if (type === 'engine_data') {
|
} else if (type === 'engine_data') {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
Object.keys(p).forEach(k => parts.push(escapeHtml(k) + ': ' + escapeHtml(String(p[k].value))));
|
Object.keys(p).forEach(k => {
|
||||||
|
const val = typeof p[k] === 'object' ? p[k].value : p[k];
|
||||||
|
parts.push(escapeHtml(k) + ': ' + escapeHtml(String(val)));
|
||||||
|
});
|
||||||
if (parts.length) parsedHtml = `<div style="color:#ff9500;margin-top:2px;font-size:10px;">${parts.slice(0, 4).join(' | ')}</div>`;
|
if (parts.length) parsedHtml = `<div style="color:#ff9500;margin-top:2px;font-size:10px;">${parts.slice(0, 4).join(' | ')}</div>`;
|
||||||
} else if (type === 'oooi' && p.origin) {
|
} else if (type === 'oooi' && p.origin) {
|
||||||
parsedHtml = `<div style="color:var(--accent-cyan);margin-top:2px;font-size:10px;">${escapeHtml(String(p.origin))} → ${escapeHtml(String(p.destination))}${p.out ? ' | OUT ' + escapeHtml(String(p.out)) : ''}${p.off ? ' OFF ' + escapeHtml(String(p.off)) : ''}${p.on ? ' ON ' + escapeHtml(String(p.on)) : ''}${p['in'] ? ' IN ' + escapeHtml(String(p['in'])) : ''}</div>`;
|
parsedHtml = `<div style="color:var(--accent-cyan);margin-top:2px;font-size:10px;">${escapeHtml(String(p.origin))} → ${escapeHtml(String(p.destination))}${p.out ? ' | OUT ' + escapeHtml(String(p.out)) : ''}${p.off ? ' OFF ' + escapeHtml(String(p.off)) : ''}${p.on ? ' ON ' + escapeHtml(String(p.on)) : ''}${p['in'] ? ' IN ' + escapeHtml(String(p['in'])) : ''}</div>`;
|
||||||
|
} else if (type === 'weather' && (p.wind_speed || p.temperature)) {
|
||||||
|
const wx = [];
|
||||||
|
if (p.wind_speed) wx.push('Wind ' + escapeHtml(String(p.wind_speed)) + (p.wind_dir ? '/' + escapeHtml(String(p.wind_dir)) : ''));
|
||||||
|
if (p.temperature) wx.push(escapeHtml(String(p.temperature)) + '°C');
|
||||||
|
if (p.turbulence) wx.push('Turb: ' + escapeHtml(String(p.turbulence)));
|
||||||
|
if (wx.length) parsedHtml = `<div style="color:#00d4ff;margin-top:2px;font-size:10px;">${wx.join(' | ')}</div>`;
|
||||||
|
} else if (type === 'cpdlc') {
|
||||||
|
const cpdlcText = p.message || p.text || '';
|
||||||
|
if (cpdlcText) parsedHtml = `<div style="color:#b388ff;margin-top:2px;font-size:10px;font-weight:600;">${escapeHtml(String(cpdlcText))}</div>`;
|
||||||
|
} else if (type === 'squawk' && p.squawk) {
|
||||||
|
parsedHtml = `<div style="color:#ff6b6b;margin-top:2px;font-size:10px;font-weight:600;">Squawk: ${escapeHtml(String(p.squawk))}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<div class="acars-feed-card" style="padding:6px 8px;border-bottom:1px solid var(--border-color);animation:fadeInMsg 0.3s ease;">
|
return `<div class="acars-feed-card" style="padding:6px 8px;border-bottom:1px solid var(--border-color);animation:fadeInMsg 0.3s ease;">
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px;">
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:2px;">
|
||||||
<span style="color:var(--accent-cyan);font-weight:bold;">${flight}</span>
|
<span style="color:var(--accent-cyan);font-weight:bold;">${flight}${tail ? ' <span style="color:var(--text-muted);font-weight:normal;font-size:9px;">(' + tail + ')</span>' : ''}</span>
|
||||||
<span style="color:var(--text-muted);font-size:9px;">${time}</span>
|
<span style="color:var(--text-muted);font-size:9px;">${time}</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:2px;">${badge} <span style="color:var(--text-primary);">${desc}</span></div>
|
<div style="margin-top:2px;">${badge} <span style="color:var(--text-primary);">${desc}</span></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user