diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index 9885f7d3..b2909005 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -645,7 +645,7 @@ function flushDeferredDecodeMapSync() { if (!decodeMapSyncPending || decodeHistoryReplayActive || !window.trx?.map?.aprsMap) return; decodeMapSyncPending = false; scheduleUiFrameJob("decode-map-maintenance", () => { - window.trx.map?.pruneMapHistory(); + window.trx.modules.map?.pruneMapHistory(); }); } @@ -1279,7 +1279,7 @@ function applyRigList(activeRigId, rigIds, displayNames) { if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker(); if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); } - window.trx.map?.updateMapRigFilter(); + window.trx.modules.map?.updateMapRigFilter(); } @@ -1305,7 +1305,7 @@ async function refreshRigList() { serverRigs = rigs; serverActiveRigId = data.active_remote || null; applyRigList(data.active_remote, rigIds, displayNames); - window.trx.map?.syncAprsReceiverMarker(); + window.trx.modules.map?.syncAprsReceiverMarker(); } catch (e) { // Non-fatal: SSE/status path still drives main UI. } @@ -3143,9 +3143,9 @@ function render(update) { const grid = latLonToMaidenhead(serverLat, serverLon); locationSubtitle.textContent = `Location: ${grid}`; locationSubtitle.style.display = ""; - window.trx.map?.reverseGeocodeLocation(serverLat, serverLon, grid); + window.trx.modules.map?.reverseGeocodeLocation(serverLat, serverLon, grid); } - window.trx.map?.syncAprsReceiverMarker(); + window.trx.modules.map?.syncAprsReceiverMarker(); if (typeof update.initial_map_zoom === "number" && Number.isFinite(update.initial_map_zoom)) { initialMapZoom = Math.max(1, Math.round(update.initial_map_zoom)); } @@ -3894,7 +3894,7 @@ async function switchRigFromSelect(selectEl) { if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId); if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId); if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || ""); - window.trx.map?.syncAprsReceiverMarker(); + window.trx.modules.map?.syncAprsReceiverMarker(); // Switch this session's rig and reconnect SSE to the new rig's // state channel. try { @@ -4480,23 +4480,23 @@ function updateTabHistory(name, replaceHistory = false) { } // Initialise the Leaflet map, waiting for both Leaflet (L) and map-core.js -// (window.trx.map) if they haven't loaded yet. +// (window.trx.modules.map) if they haven't loaded yet. let _mapInitTimer = null; function _initMapWhenReady() { const loadingEl = document.getElementById("map-loading"); - if (window.trx.map && typeof L !== "undefined") { + if (window.trx.modules.map && typeof L !== "undefined") { if (_mapInitTimer) { clearInterval(_mapInitTimer); _mapInitTimer = null; } if (loadingEl) loadingEl.classList.add("is-hidden"); - window.trx.map.initAprsMap(); - window.trx.map.sizeAprsMapToViewport(); + window.trx.modules.map.initAprsMap(); + window.trx.modules.map.sizeAprsMapToViewport(); // The map panel was just made visible (display:none → ""); the browser // may not have laid it out yet, so getBoundingClientRect() can return // stale/zero dimensions. Double-rAF ensures a full layout pass has // completed before we re-measure and tell Leaflet about its real size. requestAnimationFrame(() => { requestAnimationFrame(() => { - window.trx.map.sizeAprsMapToViewport(); - if (window.trx.map.aprsMap) window.trx.map.aprsMap.invalidateSize(); + window.trx.modules.map.sizeAprsMapToViewport(); + if (window.trx.modules.map.aprsMap) window.trx.modules.map.aprsMap.invalidateSize(); }); }); return; @@ -4544,7 +4544,7 @@ function navigateToTab(name, options = {}) { _initMapWhenReady(); } if (name === "statistics") { - window.trx.map?.scheduleStatsRender(); + window.trx.modules.map?.scheduleStatsRender(); } if (name === "recorder") { refreshRecorderStatus(); @@ -4721,10 +4721,11 @@ if (headerAuthBtn) { // ── Shared namespace for lazy-loaded modules ──────────────────────────────── // Modules (map-core.js, screenshot.js) access core state and utilities via // window.trx. Modules register their own APIs as sub-namespaces -// (e.g. window.trx.map, window.trx.screenshot). -window.trx = Object.create(null); +// (e.g. window.trx.modules.map, window.trx.modules.screenshot). +const trxState = Object.create(null); +const trxModules = Object.create(null); // -- State getters (backed by core-scoped variables) -- -Object.defineProperties(window.trx, { +Object.defineProperties(trxState, { serverLat: { get() { return serverLat; }, set(v) { serverLat = v; } }, serverLon: { get() { return serverLon; }, set(v) { serverLon = v; } }, lastFreqHz: { get() { return lastFreqHz; } }, @@ -4762,7 +4763,7 @@ Object.defineProperties(window.trx, { signalOverlayGl: { get() { return signalOverlayGl; } }, }); // -- Shared utility functions -- -Object.assign(window.trx, { +const trxCore = Object.freeze({ saveSetting, loadSetting, showHint, escapeMapHtml, formatFreq, formatFreqForHumans, formatWavelength, formatBwLabel, formatUptime, formatSigStrength, formatSignal, postPath, scheduleUiFrameJob, navigateToTab, rigBadgeColor, @@ -4772,18 +4773,19 @@ Object.assign(window.trx, { currentTheme, canvasPalette, currentStyle, cssColorToRgba, rgbaWithAlpha, isBinsArray, estimateNoiseFloorDb, spectrumVisibleRange, drawSpectrum, - bandForHz: function(hz) { return window.trx.map?.bandForHz?.(hz); }, + bandForHz: function(hz) { return trxModules.map?.bandForHz?.(hz); }, markDecodeMapSyncPending, decodeHistoryMapRenderingDeferred, updateDocumentTitle, activeChannelRds, }); -Object.defineProperties(window.trx, { +Object.defineProperties(trxState, { decodeHistoryReplayActive: { get() { return decodeHistoryReplayActive; } }, decodeMapSyncPending: { get() { return decodeMapSyncPending; } }, _activeTab: { get() { return _activeTab; } }, locationSubtitle: { get() { return locationSubtitle; } }, }); +window.trx = Object.freeze({ state: trxState, core: trxCore, modules: trxModules }); // Load plugin scripts now that window.trx is populated. Dynamic scripts are // async so they must not be created before the namespace they depend on exists. @@ -4797,7 +4799,7 @@ window.addEventListener("resize", resizeHeaderSignalCanvas); // ── Map module (extracted to map-core.js, lazy-loaded) ────────────────────── // The map, statistics, and geolocation code (~3,450 lines) has been moved to // map-core.js and is loaded on demand when the Map tab is first activated. -// Core communicates with the map module via window.trx.map.* namespace. +// Core communicates with the map module via window.trx.modules.map.* namespace. // ── Geo utilities (shared with map-core.js via window.trx) ───────────────── function haversineKm(lat1, lon1, lat2, lon2) { @@ -4932,7 +4934,7 @@ document.querySelectorAll(".sub-tab-bar").forEach(_wireSubTabBar); window.addEventListener("resize", () => { const mapTab = document.getElementById("tab-map"); if (!mapTab || mapTab.style.display === "none") return; - window.trx.map?.sizeAprsMapToViewport(); + window.trx.modules.map?.sizeAprsMapToViewport(); }); // --- Signal measurement --- @@ -6102,8 +6104,8 @@ function dispatchDecodeMessage(msg, skipStats) { if (msg.type === "wefax" && window.onServerWefax) window.onServerWefax(msg); if (msg.type === "wefax_progress" && window.onServerWefaxProgress) window.onServerWefaxProgress(msg); if (!skipStats && msg.type && msg.type !== "lrpt_image" && msg.type !== "lrpt_progress" && msg.type !== "wefax" && msg.type !== "wefax_progress") { - window.trx.map?.statsRecordDecode(msg.type, msg.rig_id || msg.remote || null); - window.trx.map?.scheduleStatsRender(); + window.trx.modules.map?.statsRecordDecode(msg.type, msg.rig_id || msg.remote || null); + window.trx.modules.map?.scheduleStatsRender(); } } @@ -6112,10 +6114,10 @@ function dispatchDecodeBatch(batch) { // Record statistics for every message in the batch regardless of dispatch path. for (const msg of batch) { if (msg.type && msg.type !== "lrpt_image" && msg.type !== "lrpt_progress" && msg.type !== "wefax" && msg.type !== "wefax_progress") { - window.trx.map?.statsRecordDecode(msg.type, msg.rig_id || msg.remote || null); + window.trx.modules.map?.statsRecordDecode(msg.type, msg.rig_id || msg.remote || null); } } - window.trx.map?.scheduleStatsRender(); + window.trx.modules.map?.scheduleStatsRender(); const type = String(batch[0]?.type || ""); const uniformType = batch.every((msg) => String(msg?.type || "") === type); if (uniformType) { @@ -6200,9 +6202,9 @@ function restoreDecodeHistoryGroup(kind, messages) { // Record statistics for restored history messages. if (kind !== "lrpt_image" && kind !== "lrpt_progress" && kind !== "wefax" && kind !== "wefax_progress") { for (const msg of messages) { - window.trx.map?.statsRecordDecode(kind, msg.rig_id || msg.remote || null, msg.ts_ms || undefined); + window.trx.modules.map?.statsRecordDecode(kind, msg.rig_id || msg.remote || null, msg.ts_ms || undefined); } - window.trx.map?.scheduleStatsRender(); + window.trx.modules.map?.scheduleStatsRender(); } if (kind === "ais") { if (window.restoreAisHistory) { window.restoreAisHistory(messages); } @@ -8000,12 +8002,12 @@ window.addEventListener("keydown", (event) => { // S — spectrum screenshot (lazy-loads screenshot.js on first use) if (key === "s") { event.preventDefault(); - if (window.trx.screenshot) { - void window.trx.screenshot.captureSpectrumScreenshot(); + if (window.trx.modules.screenshot) { + void window.trx.modules.screenshot.captureSpectrumScreenshot(); } else { const s = document.createElement("script"); s.src = "/screenshot.js"; - s.onload = () => { void window.trx.screenshot?.captureSpectrumScreenshot(); }; + s.onload = () => { void window.trx.modules.screenshot?.captureSpectrumScreenshot(); }; document.body.appendChild(s); } return; diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/map-core.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/map-core.js index da82dc7d..2e69dd8b 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/map-core.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/map-core.js @@ -3,10 +3,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Map, statistics, and geolocation module (lazy-loaded on map tab activation). -// Communicates with app.js core via window.trx namespace. +// Communicates with app.js through explicit state/core/module services. (function () { "use strict"; - const T = window.trx; + const { state: T, core: C, modules } = window.trx; // Destructure shared utility functions for convenience const { saveSetting, loadSetting, showHint, escapeMapHtml, formatFreq, formatFreqForHumans, @@ -14,7 +14,7 @@ formatUptime, latLonToMaidenhead, locatorToLatLon, haversineKm, formatDistanceKm, formatTimeAgo, currentDecodeHistoryRetentionMs, formatWavelength, bookmarkDistanceText, buildBookmarkTooltipText, - nearestBookmarkForHz } = T; + nearestBookmarkForHz } = C; function updateMapRigFilter() { const el = document.getElementById("map-rig-filter"); @@ -261,7 +261,7 @@ if (canRenderMap) { refreshAprsTrack(call, entry); } else { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); } if (!visible) { if (canRenderMap && selectedAprsTrackCall && String(selectedAprsTrackCall) === String(call)) { @@ -294,7 +294,7 @@ if (canRenderMap) { refreshAisTrack(key, entry); } else { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); } if (!visible) { if (canRenderMap && selectedAisTrackMmsi && String(selectedAisTrackMmsi) === String(key)) { @@ -337,7 +337,7 @@ entry.stations = new Set(); entry.bandMeta = new Map(); if (canRenderMap) setRetainedMapMarkerVisible(entry.marker, false); - else T.markDecodeMapSyncPending(); + else C.markDecodeMapSyncPending(); return false; } const nextStations = new Set(); @@ -352,7 +352,7 @@ ); const count = Math.max(nextDetails.size, nextStations.size || 0, 1); if (!canRenderMap) { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); return true; } ensureDecodeLocatorMarker(entry); @@ -392,7 +392,7 @@ pruneLocatorEntry(key, entry, cutoffMs); } if (!aprsMap || T.decodeHistoryReplayActive) { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); return; } rebuildDecodeContactPaths(); @@ -415,7 +415,7 @@ function locatorFilterColor(type) { const hues = locatorThemeHues(); - const lightTheme = T.currentTheme() === "light"; + const lightTheme = C.currentTheme() === "light"; const sat = lightTheme ? 66 : 76; const light = lightTheme ? 42 : 56; const hue = type === "bookmark" @@ -539,7 +539,7 @@ } function locatorThemeHues() { - const pal = T.canvasPalette(); + const pal = C.canvasPalette(); const baseHue = paletteHue(pal?.spectrumLine, 145); const waveHue = paletteHue(pal?.waveformLine, baseHue + 34); const peakHue = paletteHue(pal?.waveformPeak, baseHue - 42); @@ -560,7 +560,7 @@ function locatorBandChipColor(label) { const hues = locatorThemeHues(); - const lightTheme = T.currentTheme() === "light"; + const lightTheme = C.currentTheme() === "light"; const hue = wrapHue(hues.bandBase + locatorBandIndex(label) * 137.508); const sat = lightTheme ? 68 : 78; const light = lightTheme ? 44 : 58; @@ -606,7 +606,7 @@ const safeCount = Math.max(1, Number.isFinite(count) ? count : 1); const intensity = Math.min(1, Math.log2(safeCount + 1) / 5); const hue = locatorHueForEntry(entry); - const lightTheme = T.currentTheme() === "light"; + const lightTheme = C.currentTheme() === "light"; const strokeSat = lightTheme ? 62 : 74; const fillSat = lightTheme ? 68 : 78; const strokeLight = lightTheme ? 40 : 56; @@ -1573,7 +1573,7 @@ stageResizeObserver = new ResizeObserver(() => sizeAprsMapToViewport()); stageResizeObserver.observe(stage); } - updateMapBaseLayerForTheme(T.currentTheme()); + updateMapBaseLayerForTheme(C.currentTheme()); syncAprsReceiverMarker(); // Rebuild popup content on open (keeps age/distance/rig list fresh) @@ -2307,18 +2307,18 @@ existing.rigIds.add(msgRigId); } if (!visible) { - if (!T.decodeHistoryMapRenderingDeferred()) { + if (!C.decodeHistoryMapRenderingDeferred()) { setRetainedMapMarkerVisible(existing.marker, false); } else { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); } return; } - if (!T.decodeHistoryMapRenderingDeferred()) { + if (!C.decodeHistoryMapRenderingDeferred()) { ensureVdesMarker(key, existing); setRetainedMapMarkerVisible(existing.marker, true); } else { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); } if (aprsMap && existing.marker && !T.decodeHistoryReplayActive) { existing.marker.setLatLng([msg.lat, msg.lon]); @@ -2334,11 +2334,11 @@ }; vdesMarkers.set(key, entry); if (!visible) return; - if (!T.decodeHistoryMapRenderingDeferred()) { + if (!C.decodeHistoryMapRenderingDeferred()) { ensureVdesMarker(key, entry); setRetainedMapMarkerVisible(entry.marker, true); } else { - T.markDecodeMapSyncPending(); + C.markDecodeMapSyncPending(); } if (aprsMap && entry.marker && !T.decodeHistoryReplayActive) { entry.marker.setPopupContent(popupHtml); @@ -2365,7 +2365,7 @@ if (T.locationSubtitle) { T.locationSubtitle.textContent = `Location: ${grid} · ${label}`; } - T.updateDocumentTitle(T.activeChannelRds()); + C.updateDocumentTitle(C.activeChannelRds()); }) .catch(() => {}); } @@ -2443,8 +2443,8 @@ } function scheduleDecodeMapMaintenance() { - if (T.decodeHistoryMapRenderingDeferred()) { - T.markDecodeMapSyncPending(); + if (C.decodeHistoryMapRenderingDeferred()) { + C.markDecodeMapSyncPending(); return; } scheduleUiFrameJob("decode-map-maintenance", () => { @@ -3461,7 +3461,7 @@ } // Register module API for core to call - window.trx.map = { + modules.map = { initAprsMap, sizeAprsMapToViewport, syncAprsReceiverMarker, diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/screenshot.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/screenshot.js index f861d18d..7f57cc5a 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/screenshot.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/screenshot.js @@ -257,7 +257,7 @@ } // Register module API - window.trx.screenshot = { + window.trx.modules.screenshot = { captureSpectrumScreenshot, buildSpectrumSnapshotCanvas, saveCanvasAsPng,