ZDDC/transmittal/js/main.js
ZDDC ea385b5366 Initial commit
ZDDC — Zero Day Document Control. A file-naming convention plus five
single-file HTML tools (archive, transmittal, classifier, mdedit,
landing) and an optional Go HTTP server (zddc-server) with ACL and a
virtual archive index. Self-contained, offline-capable, dependency-free.

See README.md for an overview, AGENTS.md and ARCHITECTURE.md for the
build/release/architecture detail, bootstrap/README.md for the
two-level deployment install pattern, and zddc/README.md for the
HTTP server.
2026-04-27 11:05:47 -05:00

129 lines
4.8 KiB
JavaScript

(function (app) {
'use strict';
const filesModule = app.modules.files;
const security = app.modules.security;
const mode = app.modules.mode;
// Global handler: close parent <dialog> when any [data-modal-close] button is clicked
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-modal-close]');
if (!btn) { return; }
var dlg = btn.closest('dialog');
if (dlg && dlg.open) { dlg.close(); }
});
// ── Date field + calendar picker wiring ────────────
function toIso(str) {
if (!str || !str.trim()) { return ''; }
var t = str.trim();
if (/^\d{4}-\d{2}-\d{2}$/.test(t)) { return t; }
var d = new Date(t);
if (!isNaN(d.getTime())) {
var y = d.getFullYear();
var m = String(d.getMonth() + 1).padStart(2, '0');
var day = String(d.getDate()).padStart(2, '0');
return y + '-' + m + '-' + day;
}
return t;
}
function wireDatePicker(textId, pickerId) {
var dateText = app.dom.qs(textId);
var datePicker = app.dom.qs(pickerId);
if (!dateText || !datePicker) { return; }
dateText.addEventListener('click', function () {
var iso = toIso(dateText.value);
datePicker.value = (iso && /^\d{4}-\d{2}-\d{2}$/.test(iso)) ? iso : '';
datePicker.showPicker();
});
datePicker.addEventListener('change', function () {
if (datePicker.value) {
dateText.value = datePicker.value;
dateText.dispatchEvent(new Event('input', { bubbles: true }));
}
});
dateText.addEventListener('blur', function () {
var iso = toIso(dateText.value);
if (iso !== dateText.value) {
dateText.value = iso;
dateText.dispatchEvent(new Event('input', { bubbles: true }));
}
});
}
wireDatePicker('#date', '#date-picker-hidden');
wireDatePicker('#response-due', '#response-due-picker-hidden');
app.registerInit(async function () {
// ── Type combo dropdown wiring ───────────────────────
(function () {
var typeHidden = app.dom.qs('#type');
var typeDisplay = app.dom.qs('#type-display');
var menu = app.dom.qs('#type-menu');
if (!typeHidden || !typeDisplay || !menu) { return; }
function syncToHidden() {
var text = (typeDisplay.textContent || '').trim();
typeHidden.value = text;
typeHidden.dispatchEvent(new Event('input', { bubbles: true }));
if (app.state && app.state.apply) { app.state.apply(); }
}
function isOpen() { return !menu.classList.contains('hidden'); }
function closeMenu() { menu.classList.add('hidden'); }
typeDisplay.addEventListener('input', syncToHidden);
typeDisplay.addEventListener('blur', syncToHidden);
typeDisplay.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
typeDisplay.blur();
}
});
typeDisplay.addEventListener('paste', function (e) {
e.preventDefault();
var text = (e.clipboardData || window.clipboardData).getData('text/plain');
document.execCommand('insertText', false, text.replace(/\n/g, ' '));
});
typeDisplay.addEventListener('click', function () {
if (app.state.mode !== 'edit') { return; }
if (isOpen()) { closeMenu(); } else { menu.classList.remove('hidden'); }
});
menu.addEventListener('click', function (e) {
var opt = e.target.closest('[data-value]');
if (!opt) { return; }
var val = opt.getAttribute('data-value');
typeDisplay.textContent = val;
typeHidden.value = val;
typeHidden.dispatchEvent(new Event('input', { bubbles: true }));
closeMenu();
if (app.state && app.state.apply) { app.state.apply(); }
});
document.addEventListener('click', function (e) {
if (isOpen() && !menu.contains(e.target) && e.target !== typeDisplay) {
closeMenu();
}
});
})();
filesModule.loadFromJson();
app._initialized = true;
app.state.updateHiddenFields();
app.state.apply();
await security.verifySignatureIfPresent();
mode.refresh();
});
app.ready(function () {
app.start();
});
})(window.transmittalApp);