' + esc(p.key) + 'The identity. Two jobs can share a name, never a path.
' +
'
Filament
' + (p.grams == null ? 'not known yet' : '' + g(p.grams) + ' ' + weightProv(p) + (p.status === 'failed' ? 'Failed prints record the full job weight. Correct by hand if needed.' : '')) + '
' + sw(sug.c, 'swatch-sm') + '' + esc(short(sug)) + 'Suggested: same color, loaded in ' + esc(where(sug).short) + '. Not charged until you confirm.
' : '') +
'
';
});
return out;
}
function loadDrawer(cid) {
var c = container(cid), inIt = contents(cid);
var h = head('' + ICON.printers + '', 'Load into ' + esc(c.c.label), esc(c.printer.name) + ' · ' + inIt.length + ' of ' + c.c.cap + ' in use');
var cands = SPOOLS.filter(function (s) { return !s.cont; });
var body = '
' +
cands.map(function (s) { return ''; }).join('') + '
' +
'
Scanning the holder\'s QR label does the same thing from a phone (existing /c/:id scan session).
';
setTimeout(function () { main.innerHTML = keep; if (PAGE.after) PAGE.after(); }, 1600);
}
if (kind === 'offline') { fresh.stale = true; fresh.n = 0; tick(); var b = $('#offline-banner'); if (!b) { b = document.createElement('div'); b.id = 'offline-banner'; b.className = 'banner'; b.setAttribute('role', 'alert'); b.innerHTML = '⚠Can\'t reach Extrudex. You are looking at data from 09:41:07, and it is not updating. Retrying every 10 s. Saving is paused.'; main.parentNode.insertBefore(b, main); } }
if (kind === 'online') { fresh.stale = false; fresh.n = 0; tick(); var ob = $('#offline-banner'); if (ob) ob.parentNode.removeChild(ob); }
}
/* ── wiring ──────────────────────────────────────────────── */
function parseHash() {
var m = /^#(spool|print|load)=([\w-]+)(?:&tab=(\w+))?(?:&state=(\w+))?/.exec(location.hash);
if (m) { var id = m[1] === 'load' ? m[2] : +m[2]; openDrawer(m[1], id, m[3], false); if (m[4]) { D.state = m[4]; renderDrawer(); } }
else if (D.kind) closeDrawer(false);
}
function init(page, render) {
var s = shell(page.name);
document.body.insertAdjacentHTML('afterbegin', 'Skip to content
' + s.rail + '' + s.top + '
' + s.tabbar + s.rest);
window.PAGE = page;
$('#page').innerHTML = render();
if (page.after) page.after();
setTheme(document.documentElement.getAttribute('data-theme'));
setInterval(tick, 1000);
parseHash();
var pq = /[?&]palette=([^&]*)/.exec(location.search); if (pq) palOpen(decodeURIComponent(pq[1]).replace(/\+/g, ' '));
}
document.addEventListener('click', function (e) {
var t = e.target, a;
if ((a = t.closest('[data-set-theme]'))) return setTheme(a.getAttribute('data-set-theme'));
if (t.closest('[data-toggle-theme]')) return setTheme(document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark');
if (t.closest('[data-palette]')) return palOpen();
if (t.id === 'pal') return palClose();
if ((a = t.closest('.pal-item'))) return palRun(+a.getAttribute('data-i'));
if ((a = t.closest('[data-menu]'))) { var m = $('#' + a.getAttribute('data-menu')), open = !m.classList.contains('is-open'); $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); }); m.classList.toggle('is-open', open); a.setAttribute('aria-expanded', String(open)); if (open) { var f = m.querySelector('button,a'); if (f) f.focus(); } e.stopPropagation(); return; }
if (!t.closest('.menu')) $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); });
if ((a = t.closest('[data-demo]'))) { $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); }); return demo(a.getAttribute('data-demo')); }
if ((a = t.closest('[data-soon]'))) { e.preventDefault(); return toast('' + a.getAttribute('data-soon') + ' keeps its current screen on dev. It isn\'t redesigned in this iteration.'); }
if ((a = t.closest('[data-spool]'))) { e.preventDefault(); D.list = null; return openDrawer('spool', +a.getAttribute('data-spool'), a.getAttribute('data-tab-open') || 'overview'); }
if ((a = t.closest('[data-print]'))) { e.preventDefault(); return openDrawer('print', +a.getAttribute('data-print')); }
if ((a = t.closest('[data-load-into]'))) { e.preventDefault(); return openDrawer('load', a.getAttribute('data-load-into')); }
if ((a = t.closest('[data-add-spool]'))) { e.preventDefault(); return openDrawer('new', 0, 'edit'); }
if (t.closest('[data-close-drawer]')) return closeDrawer();
if ((a = t.closest('#drawer [data-tab]'))) { D.tab = a.getAttribute('data-tab'); D.state = 'normal'; renderDrawer(); history.replaceState({ d: 1 }, '', '#' + D.kind + '=' + D.id + (D.tab !== 'overview' ? '&tab=' + D.tab : '')); var f = $('#drawer [autofocus]'); if (f) f.focus(); return; }
if ((a = t.closest('[data-state]'))) { D.state = a.getAttribute('data-state'); return renderDrawer(); }
if ((a = t.closest('[data-step]'))) { var i = D.list.indexOf(D.id) + +a.getAttribute('data-step'); if (D.list[i] != null) openDrawer('spool', D.list[i], D.tab, false); history.replaceState({ d: 1 }, '', '#spool=' + D.id + (D.tab !== 'overview' ? '&tab=' + D.tab : '')); return; }
if ((a = t.closest('[data-dest]'))) { $$('[data-dest]').forEach(function (b) { b.setAttribute('aria-pressed', String(b === a)); }); $('#m-save').disabled = false; var dc = a.getAttribute('data-dest'), cc = dc !== 'storage' && container(dc); $('#m-save').textContent = cc ? 'Load into ' + cc.printer.model + ' · ' + cc.c.label : 'Move to storage'; return; }
if ((a = t.closest('[data-load]'))) { $$('[data-load]').forEach(function (b) { b.setAttribute('aria-pressed', String(b === a)); }); var ls = spool(+a.getAttribute('data-load')); $('#l-save').disabled = false; $('#l-save').textContent = 'Load ' + ls.color + ' · ' + ls.brand; $('#l-save').setAttribute('data-sid', ls.id); return; }
if (t.id === 'l-save') { var sid = +t.getAttribute('data-sid'), ss = spool(sid), cc2 = container(D.id); ss.cont = D.id; closeDrawer(); toast('Loaded ' + esc(ss.color + ' · ' + ss.brand) + ' into ' + esc(cc2.printer.model + ' · ' + cc2.c.label) + ' View'); if (PAGE.refresh) PAGE.refresh(); return; }
if (t.id === 'm-save') { var dest = $('[data-dest][aria-pressed="true"]').getAttribute('data-dest'), sp = spool(D.id); sp.cont = dest === 'storage' ? null : dest; sp.v++; D.tab = 'overview'; renderDrawer(); toast('Moved ' + esc(sp.color) + ' to ' + esc(where(sp).text)); if (PAGE.refresh) PAGE.refresh(); return; }
if (t.id === 'w-save' || t.id === 'w-next') { var ws = spool(D.id), before = ws.rem, nv = Math.round(+$('#scale').value.replace(/[^\d.]/g, '') - ws.tare); ws.rem = nv; ws.remSrc = 'weighed'; ws.weighedAt = '18 Sep 09:41'; ws.v++; toast('Weighed ' + esc(ws.color + ' · ' + ws.brand) + ': ' + g(before) + ' → ' + g(nv)); if (PAGE.refresh) PAGE.refresh(); if (t.id === 'w-next') { var nx = D.list[D.list.indexOf(D.id) + 1]; openDrawer('spool', nx, 'weigh', false); history.replaceState({ d: 1 }, '', '#spool=' + nx + '&tab=weigh'); } else { D.tab = 'overview'; renderDrawer(); } return; }
if (t.closest('[data-save]')) { var sv = D.kind === 'new'; closeDrawer(); D.dirty = false; return toast(sv ? 'Added spool #57. Print its label' : 'Saved changes'); }
if (t.closest('[data-discard]')) { D.dirty = false; return closeDrawer(); }
if (t.closest('[data-keep]')) { $('#discard-bar').hidden = true; return; }
if ((a = t.closest('[data-fake]'))) { toast(a.getAttribute('data-fake')); return; }
if ((a = t.closest('[data-delete]'))) { var ds = spool(+a.getAttribute('data-delete')); $('#drawer .drawer-foot').innerHTML = '
Delete ' + esc(ds.color + ' · ' + fullName(ds)) + '?Its print history stays. Prints keep their grams and cost and only lose the link to this spool. This can\'t be undone.
'; $('#drawer .drawer-foot [autofocus]').focus(); return; }
});
document.addEventListener('input', function (e) {
if (e.target.id === 'pal-q') { P.sel = 0; return palRender(); }
if (e.target.closest('#drawer .drawer-body') && D.tab === 'edit') D.dirty = true;
if (e.target.id === 'scale') {
var s = spool(D.id), v = +e.target.value.replace(/[^\d.]/g, ''), r = Math.round(v - s.tare);
var ok = v > 0 && v >= s.tare && r <= s.tot + 1;
$('#w-after').textContent = v ? g(r) : '—';
$('#w-diff').innerHTML = v && ok ? '' + (r - s.rem > 0 ? '+' : '') + (r - s.rem).toLocaleString('en-US') + ' g' : '';
$('#w-note').innerHTML = !v ? 'Type the reading and the result appears here before you save.' : v < s.tare ? '⚠ ' + g(v) + ' is less than the ' + g(s.tare) + ' empty spool. One of them is wrong. Nothing will be saved.' : r > s.tot + 1 ? '⚠ That leaves ' + g(r) + ', more than the spool holds full (' + g(s.tot) + '). Check the total or the tare.' : 'Saved with the tare it used, so a later tare correction can re-base it.';
$('#w-save').disabled = !ok; if ($('#w-next')) $('#w-next').disabled = !ok;
}
if (e.target.id === 'load-q') { var q = e.target.value.toLowerCase(); $$('[data-load]').forEach(function (b) { b.hidden = q && b.getAttribute('data-hay').indexOf(q) < 0; }); }
});
document.addEventListener('keydown', function (e) {
var typing = /INPUT|SELECT|TEXTAREA/.test(document.activeElement.tagName);
if ((e.key === 'k' && (e.metaKey || e.ctrlKey)) || (e.key === '/' && !typing)) { e.preventDefault(); return palOpen(); }
if ($('#pal').classList.contains('is-open')) {
if (e.key === 'Escape') { e.preventDefault(); return palClose(); }
if (e.key === 'ArrowDown') { e.preventDefault(); P.sel = Math.min(P.items.length - 1, P.sel + 1); return palRender(); }
if (e.key === 'ArrowUp') { e.preventDefault(); P.sel = Math.max(0, P.sel - 1); return palRender(); }
if (e.key === 'Enter') { e.preventDefault(); return palRun(P.sel); }
return;
}
if (e.key === 'Escape') { if ($('.menu.is-open')) { $$('.menu.is-open').forEach(function (x) { x.classList.remove('is-open'); }); return; } if (D.kind) return closeDrawer(); }
if (D.kind === 'spool' && !typing && D.list && (e.key === 'j' || e.key === 'k')) { var b = $('[data-step="' + (e.key === 'j' ? 1 : -1) + '"]'); if (b && !b.disabled) b.click(); }
if (e.key === 'Enter' && document.activeElement.matches('tr[data-row-spool],tr[data-row-print]')) document.activeElement.click();
if (D.kind && e.key === 'Tab') { var dr = $('#drawer'), f = $$('button:not([disabled]),a[href],input,select,[tabindex="0"]', dr).filter(function (x) { return x.offsetParent; }); if (!f.length) return; if (e.shiftKey && document.activeElement === f[0]) { e.preventDefault(); f[f.length - 1].focus(); } else if (!e.shiftKey && document.activeElement === f[f.length - 1]) { e.preventDefault(); f[0].focus(); } }
});
window.addEventListener('popstate', parseHash);
window.X = { init: init, esc: esc, g: g, kg: kg, usd: usd, dur: dur, when: when, ago: ago, since: since, spool: spool, printer: printer, print: print, container: container, contents: contents, fullName: fullName, short: short, where: where, gross: gross, value: value, low: low, sw: sw, meter: meter, tareProv: tareProv, remProv: remProv, weightProv: weightProv, durCell: durCell, printSt: printSt, printerSt: printerSt, needs: needs, printsNeeding: printsNeeding, spoolLinks: spoolLinks, openDrawer: openDrawer, D: D, toast: toast, ICON: ICON };
})();