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>
68 lines
2.8 KiB
JavaScript
68 lines
2.8 KiB
JavaScript
// ui_field.js — shared form-field builders (dmr-web, svx-web, svxlink-web).
|
|
// Canonical copy: callmgr2/www-mumien/lib/ui_field.js; other repos carry a COPY.
|
|
//
|
|
// makeToggleBtn(isOn, onFlip(next, btn), extraCls) Enable/Disable button; .value 'true'/'false', .setOn()
|
|
// makeBoolSelect(cls, val, disabled) same button, fires 'change' — drop-in for a true/false <select>
|
|
// isBoolVal(v) only 'true'/'false'/true/false count (never 0/1: those are numbers)
|
|
// initTooltips(container) / cleanupTooltips() Bootstrap tooltips for [data-bs-toggle="tooltip"]
|
|
(function () {
|
|
'use strict';
|
|
function makeToggleBtn(isOn, onFlip, extraCls) {
|
|
const b = document.createElement('button');
|
|
b.type = 'button';
|
|
const paint = (state) => {
|
|
b.className = 'btn btn-sm ' + (state ? 'btn-warning' : 'btn-success') + (extraCls ? ' ' + extraCls : '');
|
|
b.textContent = state ? 'Disable' : 'Enable';
|
|
b.value = state ? 'true' : 'false'; // callers read .value === 'true'
|
|
b.dataset.on = state ? '1' : '0';
|
|
};
|
|
paint(!!isOn);
|
|
b.setOn = (state) => paint(!!state); // extern styrning (t.ex. mutual-exclusion)
|
|
b.addEventListener('click', () => {
|
|
if (b.disabled) return;
|
|
const next = b.dataset.on !== '1';
|
|
paint(next);
|
|
if (onFlip) onFlip(next, b);
|
|
});
|
|
return b;
|
|
}
|
|
|
|
function makeBoolSelect(cls, val, disabled) {
|
|
const on = (val === true || val === 'true' || val === '1' || val === 1);
|
|
const b = makeToggleBtn(on, (_next, btn) => {
|
|
btn.dispatchEvent(new Event('change', { bubbles: true })); // drives existing change handlers
|
|
}, cls);
|
|
if (disabled) b.disabled = true;
|
|
return b;
|
|
}
|
|
|
|
function isBoolVal(v) {
|
|
// Only unambiguous 'true'/'false' count as bool. '0'/'1' are REMOVED here —
|
|
// they collide with numeric fields (spacing_ms=0, burst_count=1, timeouts/
|
|
// intervals of 0/1) that would otherwise be wrongly rendered as a true/false
|
|
// toggle. The daemon stores bools as 'true'/'false'; any legacy '1'/'0' bool
|
|
// becomes a number field but is still type-corrected server-side on save
|
|
// (coerce_to_template_type in sysconfig.php).
|
|
return v === 'true' || v === 'false';
|
|
}
|
|
|
|
function cleanupTooltips() {
|
|
document.querySelectorAll('.tooltip').forEach(el => el.remove());
|
|
}
|
|
|
|
function initTooltips(container) {
|
|
cleanupTooltips();
|
|
if (!window.bootstrap || !window.bootstrap.Tooltip) return;
|
|
const root = container || document;
|
|
root.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
|
|
try { bootstrap.Tooltip.getOrCreateInstance(el, {delay: {show: 400, hide: 100}}); } catch(_) {}
|
|
});
|
|
}
|
|
|
|
window.makeToggleBtn = makeToggleBtn;
|
|
window.makeBoolSelect = makeBoolSelect;
|
|
window.isBoolVal = isBoolVal;
|
|
window.initTooltips = initTooltips;
|
|
window.cleanupTooltips = cleanupTooltips;
|
|
})();
|