+//
+// SPDX-License-Identifier: BSD-2-Clause
+
+// Satellite Pass Scheduling UI
+// Manages the satellite overlay section within the background decoding scheduler.
+// Communicates with scheduler.js via a thin window API for shared state access.
+
+(function () {
+ "use strict";
+
+ // ── DOM references (cached once) ──────────────────────────────────
+ const dom = {
+ enabled: document.getElementById("scheduler-sat-enabled"),
+ pretune: document.getElementById("scheduler-sat-pretune"),
+ body: document.getElementById("scheduler-sat-body"),
+ tbody: document.getElementById("scheduler-sat-tbody"),
+ addBtn: document.getElementById("scheduler-sat-add-btn"),
+ passStatus: document.getElementById("scheduler-sat-pass-status"),
+ formWrap: document.getElementById("sch-sat-form-wrap"),
+ formTitle: document.getElementById("sch-sat-form-title"),
+ form: document.getElementById("sch-sat-form"),
+ formCancel: document.getElementById("sch-sat-form-cancel"),
+ preset: document.getElementById("scheduler-sat-preset"),
+ name: document.getElementById("scheduler-sat-name"),
+ norad: document.getElementById("scheduler-sat-norad"),
+ bookmark: document.getElementById("scheduler-sat-bookmark"),
+ minEl: document.getElementById("scheduler-sat-min-el"),
+ priority: document.getElementById("scheduler-sat-priority"),
+ centerHz: document.getElementById("scheduler-sat-center-hz"),
+ };
+
+ // ── Local state ───────────────────────────────────────────────────
+ let editIdx = null; // null = adding, number = editing
+
+ // ── Scheduler bridge ──────────────────────────────────────────────
+ // These accessors call into scheduler.js via window.schedulerBridge,
+ // which is set up by scheduler.js after it initializes.
+ function getBridge() {
+ return window.schedulerBridge || {};
+ }
+
+ function getConfig() {
+ const b = getBridge();
+ return typeof b.getConfig === "function" ? b.getConfig() : null;
+ }
+
+ function getStatus() {
+ const b = getBridge();
+ return typeof b.getStatus === "function" ? b.getStatus() : null;
+ }
+
+ function getBookmarks() {
+ const b = getBridge();
+ return typeof b.getBookmarks === "function" ? b.getBookmarks() : [];
+ }
+
+ function bmName(id) {
+ const bm = getBookmarks().find(function (b) { return b.id === id; });
+ return bm ? bm.name : String(id || "");
+ }
+
+ function escHtml(s) {
+ return String(s)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """);
+ }
+
+ function formatFreq(hz) {
+ if (hz >= 1e6) return (hz / 1e6).toFixed(3) + " MHz";
+ if (hz >= 1e3) return (hz / 1e3).toFixed(1) + " kHz";
+ return hz + " Hz";
+ }
+
+ // ── Satellite config helpers ──────────────────────────────────────
+ function getSatelliteEntries() {
+ var config = getConfig();
+ return (config && config.satellites && Array.isArray(config.satellites.entries))
+ ? config.satellites.entries
+ : [];
+ }
+
+ function ensureSatelliteConfig() {
+ var config = getConfig();
+ if (!config) return { enabled: false, pretune_secs: 60, entries: [] };
+ if (!config.satellites) config.satellites = { enabled: false, pretune_secs: 60, entries: [] };
+ if (!config.satellites.entries) config.satellites.entries = [];
+ return config.satellites;
+ }
+
+ function collectSatelliteConfig() {
+ var enabled = dom.enabled ? dom.enabled.checked : false;
+ var pretune = dom.pretune ? parseInt(dom.pretune.value, 10) : 60;
+ return {
+ enabled: enabled,
+ pretune_secs: isNaN(pretune) || pretune < 0 ? 60 : pretune,
+ entries: getSatelliteEntries(),
+ };
+ }
+
+ // ── Render: section ───────────────────────────────────────────────
+ function renderSection() {
+ var config = getConfig();
+ var satCfg = (config && config.satellites) || {};
+ var enabled = !!satCfg.enabled;
+
+ if (dom.enabled) dom.enabled.checked = enabled;
+ if (dom.pretune) dom.pretune.value = satCfg.pretune_secs != null ? satCfg.pretune_secs : 60;
+ if (dom.body) dom.body.style.display = enabled ? "" : "none";
+
+ renderEntries();
+ renderPassStatus();
+ }
+
+ // ── Render: entries table ─────────────────────────────────────────
+ function renderEntries() {
+ if (!dom.tbody) return;
+ var entries = getSatelliteEntries();
+ var frag = document.createDocumentFragment();
+
+ entries.forEach(function (entry, idx) {
+ var tr = document.createElement("tr");
+
+ var tdSat = document.createElement("td");
+ tdSat.textContent = entry.satellite || "";
+ tr.appendChild(tdSat);
+
+ var tdNorad = document.createElement("td");
+ tdNorad.textContent = entry.norad_id || "";
+ tr.appendChild(tdNorad);
+
+ var tdBm = document.createElement("td");
+ tdBm.textContent = bmName(entry.bookmark_id);
+ tr.appendChild(tdBm);
+
+ var tdEl = document.createElement("td");
+ tdEl.textContent = (entry.min_elevation_deg != null ? entry.min_elevation_deg + "\u00B0" : "5\u00B0");
+ tr.appendChild(tdEl);
+
+ var tdPrio = document.createElement("td");
+ tdPrio.textContent = entry.priority || 0;
+ tr.appendChild(tdPrio);
+
+ var tdActions = document.createElement("td");
+
+ var editBtn = document.createElement("button");
+ editBtn.className = "sch-write";
+ editBtn.type = "button";
+ editBtn.textContent = "Edit";
+ editBtn.addEventListener("click", function () {
+ openForm(entry, idx);
+ });
+ tdActions.appendChild(editBtn);
+
+ var removeBtn = document.createElement("button");
+ removeBtn.className = "sch-write";
+ removeBtn.type = "button";
+ removeBtn.textContent = "Remove";
+ removeBtn.addEventListener("click", function () {
+ removeEntry(idx);
+ });
+ tdActions.appendChild(removeBtn);
+
+ tr.appendChild(tdActions);
+ frag.appendChild(tr);
+ });
+
+ dom.tbody.replaceChildren(frag);
+ }
+
+ // ── Render: pass status ───────────────────────────────────────────
+ function renderPassStatus() {
+ if (!dom.passStatus) return;
+ var entries = getSatelliteEntries();
+ if (entries.length === 0) {
+ dom.passStatus.innerHTML = "";
+ return;
+ }
+ var status = getStatus();
+ if (status && status.active_satellite) {
+ dom.passStatus.innerHTML =
+ 'PASS ACTIVE: ' +
+ escHtml(status.active_satellite) +
+ '';
+ } else {
+ dom.passStatus.innerHTML =
+ 'No satellite pass active. Predictions available in the SAT tab.';
+ }
+ }
+
+ // ── Render: bookmark dropdown ─────────────────────────────────────
+ function renderBookmarkSelect(selectedId) {
+ if (!dom.bookmark) return;
+ dom.bookmark.innerHTML = '';
+ getBookmarks().forEach(function (bm) {
+ var opt = document.createElement("option");
+ opt.value = bm.id;
+ opt.textContent = bm.name + " (" + formatFreq(bm.freq_hz) + " " + bm.mode + ")";
+ if (bm.id === selectedId) opt.selected = true;
+ dom.bookmark.appendChild(opt);
+ });
+ }
+
+ // ── Entry management ──────────────────────────────────────────────
+ function removeEntry(idx) {
+ var sat = ensureSatelliteConfig();
+ sat.entries.splice(idx, 1);
+ renderEntries();
+ }
+
+ // ── Form: open ────────────────────────────────────────────────────
+ function openForm(entry, idx) {
+ editIdx = (idx != null) ? idx : null;
+
+ if (dom.formTitle) dom.formTitle.textContent = entry ? "Edit Satellite" : "Add Satellite";
+ if (dom.preset) dom.preset.value = "";
+ if (dom.name) dom.name.value = entry ? (entry.satellite || "") : "";
+ if (dom.norad) dom.norad.value = entry ? (entry.norad_id || "") : "";
+ if (dom.minEl) dom.minEl.value = entry && entry.min_elevation_deg != null ? entry.min_elevation_deg : 5;
+ if (dom.priority) dom.priority.value = entry && entry.priority != null ? entry.priority : 0;
+ if (dom.centerHz) dom.centerHz.value = entry && entry.center_hz ? entry.center_hz : "";
+
+ renderBookmarkSelect(entry ? entry.bookmark_id : null);
+
+ if (dom.formWrap) {
+ dom.formWrap.style.display = "flex";
+ if (dom.name) dom.name.focus();
+ }
+ }
+
+ // ── Form: close ───────────────────────────────────────────────────
+ function closeForm() {
+ if (dom.formWrap) dom.formWrap.style.display = "none";
+ editIdx = null;
+ }
+
+ // ── Form: submit ──────────────────────────────────────────────────
+ function onFormSubmit(e) {
+ e.preventDefault();
+
+ var satellite = dom.name ? dom.name.value.trim() : "";
+ var noradId = dom.norad ? parseInt(dom.norad.value, 10) : NaN;
+ var bmId = dom.bookmark ? dom.bookmark.value : "";
+
+ if (!satellite) { alert("Please enter a satellite name."); return; }
+ if (isNaN(noradId) || noradId <= 0) { alert("Please enter a valid NORAD catalog number."); return; }
+ if (!bmId) { alert("Please select a bookmark."); return; }
+
+ var minEl = dom.minEl ? parseFloat(dom.minEl.value) : 5;
+ var prio = dom.priority ? parseInt(dom.priority.value, 10) : 0;
+ var centerHzRaw = dom.centerHz ? parseInt(dom.centerHz.value, 10) : NaN;
+
+ var sat = ensureSatelliteConfig();
+
+ var entryData = {
+ satellite: satellite,
+ norad_id: noradId,
+ bookmark_id: bmId,
+ min_elevation_deg: isNaN(minEl) ? 5 : minEl,
+ priority: isNaN(prio) ? 0 : prio,
+ center_hz: !isNaN(centerHzRaw) && centerHzRaw > 0 ? centerHzRaw : null,
+ bookmark_ids: [],
+ };
+
+ if (editIdx !== null) {
+ var existing = sat.entries[editIdx];
+ entryData.id = existing ? existing.id : ("sat_" + Date.now().toString(36));
+ sat.entries[editIdx] = entryData;
+ } else {
+ entryData.id = "sat_" + Date.now().toString(36);
+ sat.entries.push(entryData);
+ }
+
+ closeForm();
+ renderEntries();
+ }
+
+ // ── Preset change handler ─────────────────────────────────────────
+ function onPresetChange() {
+ if (!dom.preset || !dom.preset.value) return;
+ var parts = dom.preset.value.split("|");
+ if (dom.name) dom.name.value = parts[0] || "";
+ if (dom.norad) dom.norad.value = parts[1] || "";
+ }
+
+ // ── Wire all events ───────────────────────────────────────────────
+ function wireEvents() {
+ if (dom.enabled) {
+ dom.enabled.addEventListener("change", function () {
+ if (dom.body) dom.body.style.display = dom.enabled.checked ? "" : "none";
+ });
+ }
+ if (dom.addBtn) dom.addBtn.addEventListener("click", function () { openForm(null, null); });
+ if (dom.form) dom.form.addEventListener("submit", onFormSubmit);
+ if (dom.formCancel) dom.formCancel.addEventListener("click", closeForm);
+ if (dom.preset) dom.preset.addEventListener("change", onPresetChange);
+ }
+
+ // ── Public API ────────────────────────────────────────────────────
+ window.satScheduler = {
+ wireEvents: wireEvents,
+ renderSection: renderSection,
+ renderPassStatus: renderPassStatus,
+ collectSatelliteConfig: collectSatelliteConfig,
+ };
+})();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/sat.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/sat.js
index 170c5d1..c853ac2 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/sat.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/sat.js
@@ -3,27 +3,46 @@
// History view: filterable table of all decoded images
// Predictions view: next 24 h passes for ham satellites
-// ── DOM references ──────────────────────────────────────────────────
-const satStatus = document.getElementById("sat-status");
-const satLiveView = document.getElementById("sat-live-view");
-const satHistoryView = document.getElementById("sat-history-view");
-const satPredictionsView = document.getElementById("sat-predictions-view");
-const satLiveLatest = document.getElementById("sat-live-latest");
-const satHistoryList = document.getElementById("sat-history-list");
-const satHistoryCount = document.getElementById("sat-history-count");
-const satFilterInput = document.getElementById("sat-filter");
-const satSortSelect = document.getElementById("sat-sort");
-const satTypeFilter = document.getElementById("sat-type-filter");
-const satAptState = document.getElementById("sat-apt-state");
-const satLrptState = document.getElementById("sat-lrpt-state");
+// ── DOM references (cached once) ───────────────────────────────────
+const satDom = {
+ status: document.getElementById("sat-status"),
+ liveView: document.getElementById("sat-live-view"),
+ historyView: document.getElementById("sat-history-view"),
+ predictionsView: document.getElementById("sat-predictions-view"),
+ liveLatest: document.getElementById("sat-live-latest"),
+ historyList: document.getElementById("sat-history-list"),
+ historyCount: document.getElementById("sat-history-count"),
+ filterInput: document.getElementById("sat-filter"),
+ sortSelect: document.getElementById("sat-sort"),
+ typeFilter: document.getElementById("sat-type-filter"),
+ aptState: document.getElementById("sat-apt-state"),
+ lrptState: document.getElementById("sat-lrpt-state"),
+ viewLiveBtn: document.getElementById("sat-view-live"),
+ viewHistoryBtn: document.getElementById("sat-view-history"),
+ viewPredBtn: document.getElementById("sat-view-predictions"),
+ predFilter: document.getElementById("sat-pred-filter"),
+ predMinEl: document.getElementById("sat-pred-min-el"),
+ predCategory: document.getElementById("sat-pred-category"),
+ predCurrentList: document.getElementById("sat-pred-current-list"),
+ predUpcomingList: document.getElementById("sat-pred-list"),
+ predCurrentSec: document.getElementById("sat-pred-current-section"),
+ predUpcomingSec: document.getElementById("sat-pred-upcoming-section"),
+ predStatus: document.getElementById("sat-pred-status"),
+};
// ── State ───────────────────────────────────────────────────────────
let satImageHistory = [];
const SAT_MAX_IMAGES = 100;
-const SAT_PRED_PAGE_SIZE = 50; // max rows before "Show more"
+const SAT_PRED_PAGE_SIZE = 50;
let satPredShowAll = false;
let satFilterText = "";
let satActiveView = "live"; // "live" | "history" | "predictions"
+let satPredData = [];
+let satPredFilterText = "";
+let satPredMinEl = 0;
+let satPredCategory = "all";
+let satPredSatCount = 0;
+let satPredCountdownTimer = null;
// ── UI scheduler helper ─────────────────────────────────────────────
function scheduleSatUi(key, job) {
@@ -35,23 +54,16 @@ function scheduleSatUi(key, job) {
}
// ── View switching ──────────────────────────────────────────────────
-const satViewLiveBtn = document.getElementById("sat-view-live");
-const satViewHistoryBtn = document.getElementById("sat-view-history");
-const satViewPredictionsBtn = document.getElementById("sat-view-predictions");
-
function switchSatView(view) {
const leavingPredictions = satActiveView === "predictions" && view !== "predictions";
satActiveView = view;
- if (satLiveView) satLiveView.style.display = view === "live" ? "" : "none";
- if (satHistoryView) satHistoryView.style.display = view === "history" ? "" : "none";
- if (satPredictionsView) satPredictionsView.style.display = view === "predictions" ? "" : "none";
- if (satViewLiveBtn) satViewLiveBtn.classList.toggle("sat-view-active", view === "live");
- if (satViewHistoryBtn) satViewHistoryBtn.classList.toggle("sat-view-active", view === "history");
- if (satViewPredictionsBtn) satViewPredictionsBtn.classList.toggle("sat-view-active", view === "predictions");
- // Clear prediction DOM when leaving to reduce node count.
- if (leavingPredictions) {
- clearPredictionDom();
- }
+ if (satDom.liveView) satDom.liveView.style.display = view === "live" ? "" : "none";
+ if (satDom.historyView) satDom.historyView.style.display = view === "history" ? "" : "none";
+ if (satDom.predictionsView) satDom.predictionsView.style.display = view === "predictions" ? "" : "none";
+ if (satDom.viewLiveBtn) satDom.viewLiveBtn.classList.toggle("sat-view-active", view === "live");
+ if (satDom.viewHistoryBtn) satDom.viewHistoryBtn.classList.toggle("sat-view-active", view === "history");
+ if (satDom.viewPredBtn) satDom.viewPredBtn.classList.toggle("sat-view-active", view === "predictions");
+ if (leavingPredictions) clearPredictionDom();
if (view === "history") {
renderSatHistoryTable();
} else if (view === "predictions") {
@@ -61,39 +73,38 @@ function switchSatView(view) {
}
function clearPredictionDom() {
- if (satPredCountdownTimer) { clearInterval(satPredCountdownTimer); satPredCountdownTimer = null; }
- if (satPredCurrentList) satPredCurrentList.innerHTML = "";
- if (satPredUpcomingList) satPredUpcomingList.innerHTML = "";
+ stopCountdownTimer();
+ if (satDom.predCurrentList) satDom.predCurrentList.innerHTML = "";
+ if (satDom.predUpcomingList) satDom.predUpcomingList.innerHTML = "";
}
window.clearSatPredictionDom = clearPredictionDom;
-satViewLiveBtn?.addEventListener("click", () => switchSatView("live"));
-satViewHistoryBtn?.addEventListener("click", () => switchSatView("history"));
-satViewPredictionsBtn?.addEventListener("click", () => switchSatView("predictions"));
+satDom.viewLiveBtn?.addEventListener("click", () => switchSatView("live"));
+satDom.viewHistoryBtn?.addEventListener("click", () => switchSatView("history"));
+satDom.viewPredBtn?.addEventListener("click", () => switchSatView("predictions"));
// ── Live view: decoder state ────────────────────────────────────────
-// Updated from app.js render() via window.updateSatLiveState
let _lastSatAptOn = null, _lastSatLrptOn = null;
window.updateSatLiveState = function (update) {
- if (!satAptState || !satLrptState) return;
+ if (!satDom.aptState || !satDom.lrptState) return;
const aptOn = !!update.wxsat_decode_enabled;
const lrptOn = !!update.lrpt_decode_enabled;
if (aptOn !== _lastSatAptOn) {
_lastSatAptOn = aptOn;
- satAptState.textContent = aptOn ? "Listening" : "Idle";
- satAptState.className = "sat-live-value " + (aptOn ? "sat-state-listening" : "sat-state-idle");
+ satDom.aptState.textContent = aptOn ? "Listening" : "Idle";
+ satDom.aptState.className = "sat-live-value " + (aptOn ? "sat-state-listening" : "sat-state-idle");
}
if (lrptOn !== _lastSatLrptOn) {
_lastSatLrptOn = lrptOn;
- satLrptState.textContent = lrptOn ? "Listening" : "Idle";
- satLrptState.className = "sat-live-value " + (lrptOn ? "sat-state-listening" : "sat-state-idle");
+ satDom.lrptState.textContent = lrptOn ? "Listening" : "Idle";
+ satDom.lrptState.className = "sat-live-value " + (lrptOn ? "sat-state-listening" : "sat-state-idle");
}
};
function renderSatLatestCard() {
- if (!satLiveLatest) return;
+ if (!satDom.liveLatest) return;
if (satImageHistory.length === 0) {
- satLiveLatest.innerHTML =
+ satDom.liveLatest.innerHTML =
'No images decoded yet. Enable a decoder and wait for a satellite pass.
';
return;
}
@@ -124,19 +135,17 @@ function renderSatLatestCard() {
html += ` `;
}
html += ``;
- satLiveLatest.innerHTML = html;
+ satDom.liveLatest.innerHTML = html;
}
// ── History view: table ─────────────────────────────────────────────
function getSatFilteredHistory() {
let items = satImageHistory;
- // Type filter
- const typeVal = satTypeFilter ? satTypeFilter.value : "all";
+ const typeVal = satDom.typeFilter ? satDom.typeFilter.value : "all";
if (typeVal === "apt") items = items.filter((i) => i._decoder === "apt");
else if (typeVal === "lrpt") items = items.filter((i) => i._decoder === "lrpt");
- // Text filter
if (satFilterText) {
items = items.filter((i) => {
const haystack = [
@@ -145,18 +154,13 @@ function getSatFilteredHistory() {
i.channels || "",
i.channel_a || "",
i.channel_b || "",
- ]
- .join(" ")
- .toUpperCase();
+ ].join(" ").toUpperCase();
return haystack.includes(satFilterText);
});
}
- // Sort
- const sortVal = satSortSelect ? satSortSelect.value : "newest";
- if (sortVal === "oldest") {
- items = items.slice().reverse();
- }
+ const sortVal = satDom.sortSelect ? satDom.sortSelect.value : "newest";
+ if (sortVal === "oldest") items = items.slice().reverse();
return items;
}
@@ -194,18 +198,18 @@ function renderSatHistoryRow(img) {
}
function renderSatHistoryTable() {
- if (!satHistoryList) return;
+ if (!satDom.historyList) return;
const items = getSatFilteredHistory();
const fragment = document.createDocumentFragment();
for (let i = 0; i < items.length; i += 1) {
fragment.appendChild(renderSatHistoryRow(items[i]));
}
- satHistoryList.replaceChildren(fragment);
+ satDom.historyList.replaceChildren(fragment);
- if (satHistoryCount) {
+ if (satDom.historyCount) {
const total = satImageHistory.length;
const shown = items.length;
- satHistoryCount.textContent =
+ satDom.historyCount.textContent =
total === 0
? "No images yet"
: shown === total
@@ -238,7 +242,7 @@ function addSatImage(img, decoder) {
// ── Server callbacks ────────────────────────────────────────────────
window.onServerSatImage = function (msg) {
- if (satStatus) satStatus.textContent = "Image received (NOAA APT)";
+ if (satDom.status) satDom.status.textContent = "Image received (NOAA APT)";
addSatImage(msg, "apt");
if (msg.geo_bounds && msg.path && window.addSatMapOverlay) {
window.addSatMapOverlay(msg);
@@ -246,7 +250,7 @@ window.onServerSatImage = function (msg) {
};
window.onServerLrptImage = function (msg) {
- if (satStatus) satStatus.textContent = "Image received (Meteor LRPT)";
+ if (satDom.status) satDom.status.textContent = "Image received (Meteor LRPT)";
addSatImage(msg, "lrpt");
if (msg.geo_bounds && msg.path && window.addSatMapOverlay) {
window.addSatMapOverlay(msg);
@@ -255,7 +259,7 @@ window.onServerLrptImage = function (msg) {
window.resetSatHistoryView = function () {
satImageHistory = [];
- if (satHistoryList) satHistoryList.innerHTML = "";
+ if (satDom.historyList) satDom.historyList.innerHTML = "";
renderSatLatestCard();
renderSatHistoryTable();
if (window.clearSatMapOverlays) window.clearSatMapOverlays();
@@ -288,13 +292,13 @@ lrptDecodeToggleBtn?.addEventListener("click", async () => {
});
// ── Filter / sort event listeners ───────────────────────────────────
-satFilterInput?.addEventListener("input", () => {
- satFilterText = satFilterInput.value.trim().toUpperCase();
+satDom.filterInput?.addEventListener("input", () => {
+ satFilterText = satDom.filterInput.value.trim().toUpperCase();
renderSatHistoryTable();
});
-satSortSelect?.addEventListener("change", () => renderSatHistoryTable());
-satTypeFilter?.addEventListener("change", () => renderSatHistoryTable());
+satDom.sortSelect?.addEventListener("change", () => renderSatHistoryTable());
+satDom.typeFilter?.addEventListener("change", () => renderSatHistoryTable());
// ── Settings: clear history ─────────────────────────────────────────
document
@@ -309,55 +313,7 @@ document
}
});
-// ── Predictions view ────────────────────────────────────────────────
-let satPredData = [];
-let satPredFilterText = "";
-let satPredMinEl = 0;
-let satPredCategory = "all";
-let satPredSatCount = 0;
-let satPredCountdownTimer = null;
-const satPredFilterInput = document.getElementById("sat-pred-filter");
-const satPredMinElSelect = document.getElementById("sat-pred-min-el");
-const satPredCategorySelect = document.getElementById("sat-pred-category");
-const satPredCurrentList = document.getElementById("sat-pred-current-list");
-const satPredUpcomingList = document.getElementById("sat-pred-list");
-const satPredCurrentSection = document.getElementById("sat-pred-current-section");
-const satPredUpcomingSection = document.getElementById("sat-pred-upcoming-section");
-const satPredStatus = document.getElementById("sat-pred-status");
-
-function getFilteredPredictions() {
- let items = satPredData;
- if (satPredCategory !== "all") {
- items = items.filter((p) => p.category === satPredCategory);
- }
- if (satPredMinEl > 0) {
- items = items.filter((p) => p.max_elevation_deg >= satPredMinEl);
- }
- if (satPredFilterText) {
- items = items.filter((p) => p.satellite.toUpperCase().includes(satPredFilterText));
- }
- return items;
-}
-
-function applyPredFilters() {
- renderSatPredictions(getFilteredPredictions());
-}
-
-satPredFilterInput?.addEventListener("input", () => {
- satPredFilterText = satPredFilterInput.value.trim().toUpperCase();
- applyPredFilters();
-});
-
-satPredMinElSelect?.addEventListener("change", () => {
- satPredMinEl = parseInt(satPredMinElSelect.value, 10) || 0;
- applyPredFilters();
-});
-
-satPredCategorySelect?.addEventListener("change", () => {
- satPredCategory = satPredCategorySelect.value;
- applyPredFilters();
-});
-
+// ── Predictions: helpers ────────────────────────────────────────────
function azToCardinal(deg) {
const dirs = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
return dirs[Math.round(deg / 45) % 8];
@@ -367,9 +323,7 @@ function formatPredTime(ms) {
const d = new Date(ms);
const now = new Date();
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
- const day = d.getUTCDay() !== now.getUTCDay()
- ? dayNames[d.getUTCDay()] + " "
- : "";
+ const day = d.getUTCDay() !== now.getUTCDay() ? dayNames[d.getUTCDay()] + " " : "";
const hh = String(d.getUTCHours()).padStart(2, "0");
const mm = String(d.getUTCMinutes()).padStart(2, "0");
return `${day}${hh}:${mm}`;
@@ -387,31 +341,126 @@ function formatCountdown(ms) {
return `${m}:${String(s).padStart(2, "0")}`;
}
-function renderSatPredictions(passes, error) {
- const currentList = satPredCurrentList;
- const upcomingList = satPredUpcomingList;
- const currentSection = satPredCurrentSection;
- const upcomingSection = satPredUpcomingSection;
- const status = satPredStatus;
+function elevationClass(deg) {
+ if (deg >= 45) return "sat-pred-el-high";
+ if (deg >= 10) return "sat-pred-el-mid";
+ return "sat-pred-el-low";
+}
- // Stop any previous countdown timer
- if (satPredCountdownTimer) { clearInterval(satPredCountdownTimer); satPredCountdownTimer = null; }
+// ── Predictions: countdown timer management ─────────────────────────
+function stopCountdownTimer() {
+ if (satPredCountdownTimer) {
+ clearInterval(satPredCountdownTimer);
+ satPredCountdownTimer = null;
+ }
+}
+
+function startCountdownTimer(container) {
+ const countdownEls = container ? container.querySelectorAll(".sat-pred-col-countdown") : [];
+ if (countdownEls.length === 0) return;
+
+ satPredCountdownTimer = setInterval(() => {
+ if (satActiveView !== "predictions") {
+ stopCountdownTimer();
+ return;
+ }
+ const n = Date.now();
+ let anyActive = false;
+ for (const el of countdownEls) {
+ const los = parseInt(el.dataset.los, 10);
+ const rem = los - n;
+ if (rem > 0) {
+ el.textContent = formatCountdown(rem);
+ anyActive = true;
+ } else {
+ el.textContent = "0:00";
+ }
+ }
+ if (!anyActive) {
+ stopCountdownTimer();
+ renderSatPredictions(getFilteredPredictions());
+ }
+ }, 1000);
+}
+
+// ── Predictions: row builders ───────────────────────────────────────
+function buildCurrentPassRow(pass, now) {
+ const row = document.createElement("div");
+ row.className = "sat-pred-row-current";
+ const dir = `${azToCardinal(pass.azimuth_aos_deg)} \u2192 ${azToCardinal(pass.azimuth_los_deg)}`;
+ const remaining = Math.max(0, pass.los_ms - now);
+ row.innerHTML = [
+ `${pass.satellite}`,
+ `${pass.max_elevation_deg.toFixed(1)}\u00B0`,
+ `${formatPredTime(pass.aos_ms)}`,
+ `${formatPredTime(pass.los_ms)}`,
+ `${formatCountdown(remaining)}`,
+ `${dir}`,
+ ].join("");
+ return row;
+}
+
+function buildUpcomingPassRow(pass) {
+ const row = document.createElement("div");
+ row.className = "sat-pred-row";
+ const dir = `${azToCardinal(pass.azimuth_aos_deg)} \u2192 ${azToCardinal(pass.azimuth_los_deg)}`;
+ row.innerHTML = [
+ `${formatPredTime(pass.aos_ms)}`,
+ `${pass.satellite}`,
+ `${pass.max_elevation_deg.toFixed(1)}\u00B0`,
+ `${formatPredDuration(pass.duration_s)}`,
+ `${dir}`,
+ ].join("");
+ return row;
+}
+
+// ── Predictions: filter state ───────────────────────────────────────
+function getFilteredPredictions() {
+ let items = satPredData;
+ if (satPredCategory !== "all") items = items.filter((p) => p.category === satPredCategory);
+ if (satPredMinEl > 0) items = items.filter((p) => p.max_elevation_deg >= satPredMinEl);
+ if (satPredFilterText) items = items.filter((p) => p.satellite.toUpperCase().includes(satPredFilterText));
+ return items;
+}
+
+function applyPredFilters() {
+ renderSatPredictions(getFilteredPredictions());
+}
+
+satDom.predFilter?.addEventListener("input", () => {
+ satPredFilterText = satDom.predFilter.value.trim().toUpperCase();
+ applyPredFilters();
+});
+
+satDom.predMinEl?.addEventListener("change", () => {
+ satPredMinEl = parseInt(satDom.predMinEl.value, 10) || 0;
+ applyPredFilters();
+});
+
+satDom.predCategory?.addEventListener("change", () => {
+ satPredCategory = satDom.predCategory.value;
+ applyPredFilters();
+});
+
+// ── Predictions: main render ────────────────────────────────────────
+function renderSatPredictions(passes, error) {
+ stopCountdownTimer();
if (error) {
- if (currentList) currentList.innerHTML = "";
- if (upcomingList) upcomingList.innerHTML = "";
- if (currentSection) currentSection.style.display = "none";
- if (upcomingSection) upcomingSection.style.display = "none";
- if (status) status.textContent = error;
+ if (satDom.predCurrentList) satDom.predCurrentList.innerHTML = "";
+ if (satDom.predUpcomingList) satDom.predUpcomingList.innerHTML = "";
+ if (satDom.predCurrentSec) satDom.predCurrentSec.style.display = "none";
+ if (satDom.predUpcomingSec) satDom.predUpcomingSec.style.display = "none";
+ if (satDom.predStatus) satDom.predStatus.textContent = error;
return;
}
if (!Array.isArray(passes) || passes.length === 0) {
- if (currentList) currentList.innerHTML = "";
- if (upcomingList) upcomingList.innerHTML = "";
- if (currentSection) currentSection.style.display = "none";
- if (upcomingSection) upcomingSection.style.display = "none";
- if (status) status.textContent = "No passes found in the next 24 hours.";
+ if (satDom.predCurrentList) satDom.predCurrentList.innerHTML = "";
+ if (satDom.predUpcomingList) satDom.predUpcomingList.innerHTML = "";
+ if (satDom.predCurrentSec) satDom.predCurrentSec.style.display = "none";
+ if (satDom.predUpcomingSec) satDom.predUpcomingSec.style.display = "none";
+ if (satDom.predStatus) satDom.predStatus.textContent = "No passes found in the next 24 hours.";
return;
}
@@ -420,61 +469,25 @@ function renderSatPredictions(passes, error) {
const upcoming = passes.filter((p) => p.aos_ms > now);
// ── Current passes ──
- if (currentSection) currentSection.style.display = current.length > 0 ? "" : "none";
- if (currentList) {
+ if (satDom.predCurrentSec) satDom.predCurrentSec.style.display = current.length > 0 ? "" : "none";
+ if (satDom.predCurrentList) {
if (current.length === 0) {
- currentList.innerHTML = "";
+ satDom.predCurrentList.innerHTML = "";
} else {
const frag = document.createDocumentFragment();
- for (const pass of current) {
- const row = document.createElement("div");
- row.className = "sat-pred-row-current";
- const elClass = pass.max_elevation_deg >= 45
- ? "sat-pred-el-high"
- : pass.max_elevation_deg >= 10
- ? "sat-pred-el-mid"
- : "sat-pred-el-low";
- const dir = `${azToCardinal(pass.azimuth_aos_deg)} → ${azToCardinal(pass.azimuth_los_deg)}`;
- const remaining = Math.max(0, pass.los_ms - now);
- row.innerHTML = [
- `${pass.satellite}`,
- `${pass.max_elevation_deg.toFixed(1)}°`,
- `${formatPredTime(pass.aos_ms)}`,
- `${formatPredTime(pass.los_ms)}`,
- `${formatCountdown(remaining)}`,
- `${dir}`,
- ].join("");
- frag.appendChild(row);
- }
- currentList.replaceChildren(frag);
+ for (const pass of current) frag.appendChild(buildCurrentPassRow(pass, now));
+ satDom.predCurrentList.replaceChildren(frag);
}
}
- // ── Upcoming passes (capped to reduce DOM node count) ──
+ // ── Upcoming passes ──
const upcomingLimit = satPredShowAll ? upcoming.length : SAT_PRED_PAGE_SIZE;
const visibleUpcoming = upcoming.slice(0, upcomingLimit);
const hiddenCount = upcoming.length - visibleUpcoming.length;
- if (upcomingSection) upcomingSection.style.display = upcoming.length > 0 ? "" : "none";
- if (upcomingList) {
+ if (satDom.predUpcomingSec) satDom.predUpcomingSec.style.display = upcoming.length > 0 ? "" : "none";
+ if (satDom.predUpcomingList) {
const frag = document.createDocumentFragment();
- for (const pass of visibleUpcoming) {
- const row = document.createElement("div");
- row.className = "sat-pred-row";
- const elClass = pass.max_elevation_deg >= 45
- ? "sat-pred-el-high"
- : pass.max_elevation_deg >= 10
- ? "sat-pred-el-mid"
- : "sat-pred-el-low";
- const dir = `${azToCardinal(pass.azimuth_aos_deg)} → ${azToCardinal(pass.azimuth_los_deg)}`;
- row.innerHTML = [
- `${formatPredTime(pass.aos_ms)}`,
- `${pass.satellite}`,
- `${pass.max_elevation_deg.toFixed(1)}°`,
- `${formatPredDuration(pass.duration_s)}`,
- `${dir}`,
- ].join("");
- frag.appendChild(row);
- }
+ for (const pass of visibleUpcoming) frag.appendChild(buildUpcomingPassRow(pass));
if (hiddenCount > 0) {
const moreRow = document.createElement("div");
moreRow.className = "sat-pred-row";
@@ -487,54 +500,27 @@ function renderSatPredictions(passes, error) {
});
frag.appendChild(moreRow);
}
- upcomingList.replaceChildren(frag);
+ satDom.predUpcomingList.replaceChildren(frag);
}
// ── Status ──
- if (status) {
- let text = `${current.length} active · ${upcoming.length} upcoming · times in UTC`;
- if (satPredSatCount > 0) text += ` · ${satPredSatCount} satellites tracked`;
- status.textContent = text;
+ if (satDom.predStatus) {
+ let text = `${current.length} active \u00B7 ${upcoming.length} upcoming \u00B7 times in UTC`;
+ if (satPredSatCount > 0) text += ` \u00B7 ${satPredSatCount} satellites tracked`;
+ satDom.predStatus.textContent = text;
}
- // ── Countdown timer: update "time left" every second ──
- // Only run when predictions view is actually visible.
+ // ── Countdown timer ──
if (current.length > 0 && satActiveView === "predictions") {
- const countdownEls = currentList ? currentList.querySelectorAll(".sat-pred-col-countdown") : [];
- if (countdownEls.length > 0) {
- satPredCountdownTimer = setInterval(() => {
- // Pause timer if predictions view was hidden (e.g. switched tabs).
- if (satActiveView !== "predictions") {
- clearInterval(satPredCountdownTimer);
- satPredCountdownTimer = null;
- return;
- }
- const n = Date.now();
- let anyActive = false;
- for (const el of countdownEls) {
- const los = parseInt(el.dataset.los, 10);
- const rem = los - n;
- if (rem > 0) {
- el.textContent = formatCountdown(rem);
- anyActive = true;
- } else {
- el.textContent = "0:00";
- }
- }
- if (!anyActive) {
- clearInterval(satPredCountdownTimer);
- satPredCountdownTimer = null;
- renderSatPredictions(getFilteredPredictions());
- }
- }, 1000);
- }
+ startCountdownTimer(satDom.predCurrentList);
}
}
+// ── Predictions: data loading ───────────────────────────────────────
async function loadSatPredictions() {
- if (satPredStatus) satPredStatus.textContent = "Loading predictions\u2026";
- if (satPredCurrentList) satPredCurrentList.innerHTML = "";
- if (satPredUpcomingList) satPredUpcomingList.innerHTML = "";
+ if (satDom.predStatus) satDom.predStatus.textContent = "Loading predictions\u2026";
+ if (satDom.predCurrentList) satDom.predCurrentList.innerHTML = "";
+ if (satDom.predUpcomingList) satDom.predUpcomingList.innerHTML = "";
try {
const resp = await fetch("/sat_passes");
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
@@ -554,11 +540,9 @@ async function loadSatPredictions() {
// ── Navigate to map centered on satellite image bounds ──────────────
window.satShowOnMap = function (south, west, north, east) {
- // Enable sat filter if not active
if (typeof window.enableMapSourceFilter === "function") {
window.enableMapSourceFilter("sat");
}
- // Navigate to the center of the image bounds
const lat = (south + north) / 2;
const lon = (west + east) / 2;
if (window.navigateToAprsMap) {
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
index 24bb843..fe56d43 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/scheduler.js
@@ -19,7 +19,7 @@
let interleaveTicker = null;
let schedulerStepPending = false;
let schEntryEditIdx = null; // null = adding, number = editing that index
- let schSatEditIdx = null; // null = adding, number = editing satellite entry
+ // Satellite entry editing state moved to sat-scheduler.js
// -------------------------------------------------------------------------
// Init
@@ -866,225 +866,30 @@
}
// -------------------------------------------------------------------------
- // Satellite overlay
+ // Satellite overlay (delegated to sat-scheduler.js)
// -------------------------------------------------------------------------
- function getSatelliteEntries() {
- return (currentConfig && currentConfig.satellites && Array.isArray(currentConfig.satellites.entries))
- ? currentConfig.satellites.entries
- : [];
- }
-
- function ensureSatelliteConfig() {
- if (!currentConfig) currentConfig = { remote: currentRigId, mode: "disabled", entries: [] };
- if (!currentConfig.satellites) currentConfig.satellites = { enabled: false, pretune_secs: 60, entries: [] };
- if (!currentConfig.satellites.entries) currentConfig.satellites.entries = [];
- return currentConfig.satellites;
- }
-
- function collectSatelliteConfig() {
- const enabledEl = document.getElementById("scheduler-sat-enabled");
- const pretuneEl = document.getElementById("scheduler-sat-pretune");
- const enabled = enabledEl ? enabledEl.checked : false;
- const pretune = pretuneEl ? parseInt(pretuneEl.value, 10) : 60;
- return {
- enabled: enabled,
- pretune_secs: isNaN(pretune) || pretune < 0 ? 60 : pretune,
- entries: getSatelliteEntries(),
- };
- }
-
function renderSatelliteSection() {
- const satCfg = (currentConfig && currentConfig.satellites) || {};
- const enabled = !!satCfg.enabled;
- const enabledEl = document.getElementById("scheduler-sat-enabled");
- if (enabledEl) enabledEl.checked = enabled;
-
- const pretuneEl = document.getElementById("scheduler-sat-pretune");
- if (pretuneEl) pretuneEl.value = satCfg.pretune_secs != null ? satCfg.pretune_secs : 60;
-
- const bodyEl = document.getElementById("scheduler-sat-body");
- if (bodyEl) bodyEl.style.display = enabled ? "" : "none";
-
- renderSatelliteEntries();
- renderSatPassStatus();
- }
-
- function renderSatelliteEntries() {
- const tbody = document.getElementById("scheduler-sat-tbody");
- if (!tbody) return;
- tbody.innerHTML = "";
- const entries = getSatelliteEntries();
- entries.forEach(function (entry, idx) {
- const tr = document.createElement("tr");
- tr.innerHTML =
- "" + escHtml(entry.satellite || "") + " | " +
- "" + (entry.norad_id || "") + " | " +
- "" + escHtml(bmName(entry.bookmark_id)) + " | " +
- "" + (entry.min_elevation_deg != null ? entry.min_elevation_deg + "\u00B0" : "5\u00B0") + " | " +
- "" + (entry.priority || 0) + " | " +
- '' +
- '' +
- '' +
- ' | ';
- tbody.appendChild(tr);
- });
- tbody.querySelectorAll(".sch-sat-edit-btn").forEach(function (btn) {
- btn.addEventListener("click", function () {
- const i = parseInt(btn.dataset.idx, 10);
- const entry = getSatelliteEntries()[i];
- if (entry) schOpenSatForm(entry, i);
- });
- });
- tbody.querySelectorAll(".sch-sat-remove-btn").forEach(function (btn) {
- btn.addEventListener("click", function () {
- removeSatEntry(parseInt(btn.dataset.idx, 10));
- });
- });
- }
-
- function removeSatEntry(idx) {
- const sat = ensureSatelliteConfig();
- sat.entries.splice(idx, 1);
- renderSatelliteEntries();
- }
-
- function schOpenSatForm(entry, idx) {
- schSatEditIdx = (idx != null) ? idx : null;
-
- const titleEl = document.getElementById("sch-sat-form-title");
- if (titleEl) titleEl.textContent = entry ? "Edit Satellite" : "Add Satellite";
-
- const presetEl = document.getElementById("scheduler-sat-preset");
- const nameEl = document.getElementById("scheduler-sat-name");
- const noradEl = document.getElementById("scheduler-sat-norad");
- const bmEl = document.getElementById("scheduler-sat-bookmark");
- const minElEl = document.getElementById("scheduler-sat-min-el");
- const prioEl = document.getElementById("scheduler-sat-priority");
- const centerHzEl = document.getElementById("scheduler-sat-center-hz");
-
- if (presetEl) presetEl.value = "";
- if (nameEl) nameEl.value = entry ? (entry.satellite || "") : "";
- if (noradEl) noradEl.value = entry ? (entry.norad_id || "") : "";
- if (bmEl) bmEl.value = entry ? (entry.bookmark_id || "") : "";
- if (minElEl) minElEl.value = entry && entry.min_elevation_deg != null ? entry.min_elevation_deg : 5;
- if (prioEl) prioEl.value = entry && entry.priority != null ? entry.priority : 0;
- if (centerHzEl) centerHzEl.value = entry && entry.center_hz ? entry.center_hz : "";
-
- // Populate bookmark dropdown
- renderBookmarkSelect("scheduler-sat-bookmark", entry ? entry.bookmark_id : null);
-
- const wrap = document.getElementById("sch-sat-form-wrap");
- if (wrap) {
- wrap.style.display = "flex";
- if (nameEl) nameEl.focus();
- }
- }
-
- function schCloseSatForm() {
- const wrap = document.getElementById("sch-sat-form-wrap");
- if (wrap) wrap.style.display = "none";
- schSatEditIdx = null;
- }
-
- function schSatFormSubmit(e) {
- e.preventDefault();
-
- const nameEl = document.getElementById("scheduler-sat-name");
- const noradEl = document.getElementById("scheduler-sat-norad");
- const bmEl = document.getElementById("scheduler-sat-bookmark");
- const minElEl = document.getElementById("scheduler-sat-min-el");
- const prioEl = document.getElementById("scheduler-sat-priority");
- const centerHzEl = document.getElementById("scheduler-sat-center-hz");
-
- const satellite = nameEl ? nameEl.value.trim() : "";
- const noradId = noradEl ? parseInt(noradEl.value, 10) : NaN;
- const bmId = bmEl ? bmEl.value : "";
-
- if (!satellite) { alert("Please enter a satellite name."); return; }
- if (isNaN(noradId) || noradId <= 0) { alert("Please enter a valid NORAD catalog number."); return; }
- if (!bmId) { alert("Please select a bookmark."); return; }
-
- const minEl = minElEl ? parseFloat(minElEl.value) : 5;
- const prio = prioEl ? parseInt(prioEl.value, 10) : 0;
- const centerHzRaw = centerHzEl ? parseInt(centerHzEl.value, 10) : NaN;
-
- const sat = ensureSatelliteConfig();
-
- const entryData = {
- satellite: satellite,
- norad_id: noradId,
- bookmark_id: bmId,
- min_elevation_deg: isNaN(minEl) ? 5 : minEl,
- priority: isNaN(prio) ? 0 : prio,
- center_hz: !isNaN(centerHzRaw) && centerHzRaw > 0 ? centerHzRaw : null,
- bookmark_ids: [],
- };
-
- if (schSatEditIdx !== null) {
- const existing = sat.entries[schSatEditIdx];
- entryData.id = existing ? existing.id : ("sat_" + Date.now().toString(36));
- sat.entries[schSatEditIdx] = entryData;
- } else {
- entryData.id = "sat_" + Date.now().toString(36);
- sat.entries.push(entryData);
- }
-
- schCloseSatForm();
- renderSatelliteEntries();
- }
-
- function wireSatPresetChange() {
- const presetEl = document.getElementById("scheduler-sat-preset");
- if (!presetEl || presetEl._wired) return;
- presetEl._wired = true;
- presetEl.addEventListener("change", function () {
- if (!presetEl.value) return;
- const parts = presetEl.value.split("|");
- const nameEl = document.getElementById("scheduler-sat-name");
- const noradEl = document.getElementById("scheduler-sat-norad");
- if (nameEl) nameEl.value = parts[0] || "";
- if (noradEl) noradEl.value = parts[1] || "";
- });
+ if (window.satScheduler) window.satScheduler.renderSection();
}
function renderSatPassStatus() {
- const el = document.getElementById("scheduler-sat-pass-status");
- if (!el) return;
- const entries = getSatelliteEntries();
- if (entries.length === 0) {
- el.innerHTML = "";
- return;
- }
- // Show active satellite from status if available.
- if (currentSchedulerStatus && currentSchedulerStatus.active_satellite) {
- el.innerHTML =
- 'PASS ACTIVE: ' +
- escHtml(currentSchedulerStatus.active_satellite) +
- '';
- } else {
- el.innerHTML = 'No satellite pass active. Predictions available in the SAT tab.';
- }
+ if (window.satScheduler) window.satScheduler.renderPassStatus();
+ }
+
+ function collectSatelliteConfig() {
+ return window.satScheduler
+ ? window.satScheduler.collectSatelliteConfig()
+ : { enabled: false, pretune_secs: 60, entries: [] };
}
function wireSatelliteEvents() {
- const enabledEl = document.getElementById("scheduler-sat-enabled");
- if (enabledEl) {
- enabledEl.addEventListener("change", function () {
- const bodyEl = document.getElementById("scheduler-sat-body");
- if (bodyEl) bodyEl.style.display = enabledEl.checked ? "" : "none";
- });
- }
-
- const addBtn = document.getElementById("scheduler-sat-add-btn");
- if (addBtn) addBtn.addEventListener("click", function () { schOpenSatForm(null, null); });
-
- const satForm = document.getElementById("sch-sat-form");
- if (satForm) satForm.addEventListener("submit", schSatFormSubmit);
-
- const cancelBtn = document.getElementById("sch-sat-form-cancel");
- if (cancelBtn) cancelBtn.addEventListener("click", schCloseSatForm);
-
- wireSatPresetChange();
+ // Expose bridge for sat-scheduler.js to access shared state.
+ window.schedulerBridge = {
+ getConfig: function () { return currentConfig; },
+ getStatus: function () { return currentSchedulerStatus; },
+ getBookmarks: function () { return bookmarkList; },
+ };
+ if (window.satScheduler) window.satScheduler.wireEvents();
}
// -------------------------------------------------------------------------
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs
index a738066..575595e 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/api.rs
@@ -1689,6 +1689,7 @@ define_gz_cache!(gz_cw_js, status::CW_JS, "cw.js");
define_gz_cache!(gz_sat_js, status::SAT_JS, "sat.js");
define_gz_cache!(gz_bookmarks_js, status::BOOKMARKS_JS, "bookmarks.js");
define_gz_cache!(gz_scheduler_js, status::SCHEDULER_JS, "scheduler.js");
+define_gz_cache!(gz_sat_scheduler_js, status::SAT_SCHEDULER_JS, "sat-scheduler.js");
define_gz_cache!(gz_background_decode_js, status::BACKGROUND_DECODE_JS, "background-decode.js");
define_gz_cache!(gz_vchan_js, status::VCHAN_JS, "vchan.js");
@@ -2249,6 +2250,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(sat_js)
.service(bookmarks_js)
.service(scheduler_js)
+ .service(sat_scheduler_js)
.service(background_decode_js)
.service(vchan_js)
// Virtual channels
@@ -2421,6 +2423,12 @@ async fn scheduler_js(req: HttpRequest) -> impl Responder {
static_asset_response(&req, "application/javascript; charset=utf-8", &c.gz, &c.etag)
}
+#[get("/sat-scheduler.js")]
+async fn sat_scheduler_js(req: HttpRequest) -> impl Responder {
+ let c = gz_sat_scheduler_js();
+ static_asset_response(&req, "application/javascript; charset=utf-8", &c.gz, &c.etag)
+}
+
#[get("/background-decode.js")]
async fn background_decode_js(req: HttpRequest) -> impl Responder {
let c = gz_background_decode_js();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/src/status.rs b/src/trx-client/trx-frontend/trx-frontend-http/src/status.rs
index 6bd906c..c02c3c9 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/src/status.rs
+++ b/src/trx-client/trx-frontend/trx-frontend-http/src/status.rs
@@ -27,6 +27,7 @@ pub const CW_JS: &str = include_str!("../assets/web/plugins/cw.js");
pub const SAT_JS: &str = include_str!("../assets/web/plugins/sat.js");
pub const BOOKMARKS_JS: &str = include_str!("../assets/web/plugins/bookmarks.js");
pub const SCHEDULER_JS: &str = include_str!("../assets/web/plugins/scheduler.js");
+pub const SAT_SCHEDULER_JS: &str = include_str!("../assets/web/plugins/sat-scheduler.js");
pub const BACKGROUND_DECODE_JS: &str = include_str!("../assets/web/plugins/background-decode.js");
pub const VCHAN_JS: &str = include_str!("../assets/web/plugins/vchan.js");