Stripped from the internal repo: per-box deploy trees, Go services (nodelist/astmeta/proxy/svxdr), deploy/sync scripts and all secrets. Config templates for nginx, PHP-FPM, Redis and tmpfiles live in install/ with CHANGEME placeholders. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
96 lines
3.3 KiB
JavaScript
96 lines
3.3 KiB
JavaScript
// fmt.js — shared formatting helpers for every web (dmr-web, svx-web, svxlink-web).
|
|
// Canonical copy: callmgr2/www-mumien/lib/fmt.js; other repos carry a COPY.
|
|
// Every function here replaced an identical local copy; outputs are unchanged.
|
|
(function () {
|
|
'use strict';
|
|
|
|
// HTML-escape for innerHTML/attribute use.
|
|
function escHtml(s) {
|
|
return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
|
}
|
|
// Two-digit zero pad (clock fields).
|
|
function fmt2(n) { return String(n).padStart(2, '0'); }
|
|
|
|
// Age in seconds → "12s" / "5m" / "3h" / "2d"; "—" when unknown.
|
|
function fmtAge(s) {
|
|
if (s == null || s < 0) return '—';
|
|
if (s < 60) return s + 's';
|
|
if (s < 3600) return Math.floor(s / 60) + 'm';
|
|
if (s < 86400) return Math.floor(s / 3600) + 'h';
|
|
return Math.floor(s / 86400) + 'd';
|
|
}
|
|
// Duration in seconds → "42s" / "3m 5s" / "1h 07m".
|
|
function fmtDur(s) {
|
|
s = Number(s) || 0;
|
|
if (s < 60) return s + 's';
|
|
const m = Math.floor(s / 60), r = s % 60;
|
|
if (s < 3600) return m + 'm ' + r + 's';
|
|
const h = Math.floor(s / 3600), mm = Math.floor((s % 3600) / 60);
|
|
return h + 'h ' + mm + 'm';
|
|
}
|
|
// Unix seconds → local date/time string; "—" when missing.
|
|
function fmtTs(ts) {
|
|
if (!ts) return '—';
|
|
try { return new Date(ts * 1000).toLocaleString(); } catch (_) { return '—'; }
|
|
}
|
|
|
|
// Callsigns: "SA7AUX-R" / "SA7AUX (x)" → "SA7AUX"; natural sort by prefix,
|
|
// number, suffix so SM7A < SM7B < SM70A.
|
|
function baseCallsign(cs) {
|
|
return String(cs || '').split(/[-(]/)[0].trim();
|
|
}
|
|
function splitForSort(cs) {
|
|
const b = baseCallsign(cs).toUpperCase();
|
|
const m = b.match(/^([A-Z]+)(\d+)([A-Z0-9]*)$/);
|
|
if (!m) return { pre: b, num: Number.POSITIVE_INFINITY, post: '' };
|
|
return { pre: m[1], num: Number(m[2]), post: (m[3] || '') };
|
|
}
|
|
function callsignCompare(a, b) {
|
|
const A = splitForSort(a);
|
|
const B = splitForSort(b);
|
|
if (A.pre !== B.pre) return A.pre < B.pre ? -1 : 1;
|
|
if (A.num !== B.num) return A.num - B.num;
|
|
if (A.post !== B.post) return A.post < B.post ? -1 : 1;
|
|
const ba = baseCallsign(a).toUpperCase();
|
|
const bb = baseCallsign(b).toUpperCase();
|
|
if (ba !== bb) return ba < bb ? -1 : 1;
|
|
const fa = String(a).toUpperCase();
|
|
const fb = String(b).toUpperCase();
|
|
if (fa !== fb) return fa < fb ? -1 : 1;
|
|
return 0;
|
|
}
|
|
|
|
// Frequencies: "145,600 MHz" → 145.6; fmtFreq → "145.60000" (5 decimals).
|
|
function parseMHz(v) {
|
|
if (v == null) return NaN;
|
|
if (typeof v === 'number') return v;
|
|
const s = String(v).replace(',', '.').trim();
|
|
const m = s.match(/(\d+(?:\.\d+)?)/);
|
|
return m ? Number(m[1]) : NaN;
|
|
}
|
|
function fmtFreq(v) {
|
|
if (v == null) return '';
|
|
if (typeof v === 'number') {
|
|
if (!Number.isFinite(v)) return '';
|
|
return v.toFixed(5);
|
|
}
|
|
const s = String(v).trim();
|
|
if (!s) return '';
|
|
const mhz = parseMHz(s);
|
|
if (Number.isFinite(mhz) && mhz > 0) {
|
|
return mhz.toFixed(5);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
window.escHtml = escHtml;
|
|
window.fmt2 = fmt2;
|
|
window.fmtAge = fmtAge;
|
|
window.fmtDur = fmtDur;
|
|
window.fmtTs = fmtTs;
|
|
window.baseCallsign = baseCallsign;
|
|
window.callsignCompare = callsignCompare;
|
|
window.parseMHz = parseMHz;
|
|
window.fmtFreq = fmtFreq;
|
|
})();
|