- CesiumJS viewer (token-free, baseLayer:false), Carto dark + EOX Sentinel-2 - clock spans now +/-6h CLAMPED; built-in timeline/animation = the space-time slider - HUD: brand, LIVE/SCRUBBED chip, Data/Photo mode toggle, layer rows, credits - lighting is mode-aware: OFF in Data (high-contrast asset map), ON in Photo (realistic day/night terminator) — refines SPEC 6.1's global-lighting note - main.js defines the layer-factory contract + dynamic loader; layers land next - serve.py now forwards OpenSky X-Rate-Limit-* headers for the aircraft status line Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
63 lines
2.3 KiB
JavaScript
63 lines
2.3 KiB
JavaScript
// HUD controller: layer registry, per-feed status lines, LIVE/SCRUBBED chip.
|
||
|
||
const rows = new Map();
|
||
|
||
// Register a layer row. Returns a handle so callers can flip the checkbox.
|
||
export function addLayer(id, name, defaultOn, onToggle) {
|
||
const row = document.createElement('div');
|
||
row.className = 'layer-row';
|
||
row.innerHTML = `
|
||
<label>
|
||
<input type="checkbox" ${defaultOn ? 'checked' : ''} />
|
||
<span class="dot" data-state="${defaultOn ? 'ok' : 'off'}"></span>
|
||
<span class="lname"></span>
|
||
</label>
|
||
<div class="lstatus"></div>`;
|
||
row.querySelector('.lname').textContent = name;
|
||
const cb = row.querySelector('input');
|
||
cb.addEventListener('change', () => onToggle(cb.checked));
|
||
document.getElementById('layers').appendChild(row);
|
||
rows.set(id, row);
|
||
return { setChecked: (v) => { cb.checked = v; } };
|
||
}
|
||
|
||
// state ∈ 'ok' | 'warn' | 'err' | 'off'
|
||
export function setStatus(id, text, state = 'ok') {
|
||
const row = rows.get(id);
|
||
if (!row) return;
|
||
row.querySelector('.lstatus').textContent = text;
|
||
row.querySelector('.dot').dataset.state = state;
|
||
}
|
||
|
||
export function setLiveChip(isLive) {
|
||
const chip = document.getElementById('live-chip');
|
||
if (!chip) return;
|
||
chip.textContent = isLive ? 'LIVE' : 'SCRUBBED';
|
||
chip.title = isLive ? 'Clock is at wall-clock now' : 'Live layers paused — scrubbed off now';
|
||
chip.dataset.state = isLive ? 'live' : 'scrub';
|
||
}
|
||
|
||
// Small fixed overlay for primitive picks (aircraft), which have no Cesium InfoBox.
|
||
export function showPickOverlay(title, rowPairs) {
|
||
const el = document.getElementById('pick-overlay');
|
||
if (!el) return;
|
||
el.innerHTML =
|
||
`<div class="po-close" role="button" aria-label="close">×</div>` +
|
||
`<div class="po-title"></div>` +
|
||
rowPairs.map(() => `<div class="po-row"><span></span><span></span></div>`).join('');
|
||
el.querySelector('.po-title').textContent = title;
|
||
const rowEls = el.querySelectorAll('.po-row');
|
||
rowPairs.forEach(([k, v], i) => {
|
||
const spans = rowEls[i].querySelectorAll('span');
|
||
spans[0].textContent = k;
|
||
spans[1].textContent = v;
|
||
});
|
||
el.querySelector('.po-close').addEventListener('click', hidePickOverlay);
|
||
el.hidden = false;
|
||
}
|
||
|
||
export function hidePickOverlay() {
|
||
const el = document.getElementById('pick-overlay');
|
||
if (el) el.hidden = true;
|
||
}
|