// shared/nav.js — lateral navigation strip across the four canonical // project stages (archive · working · staging · reviewing). Renders // only when: // 1. location.protocol is http: or https: (online — file:// has no // project structure to navigate within), AND // 2. a project segment can be detected from location.pathname (the // first path segment, when it isn't a tool HTML file). // // The strip is inserted as a sibling of
// on DOMContentLoaded — no template changes required. Each tool just // needs ../shared/nav.{js,css} in its build.sh. // // Stage URLs follow the canonical workflow folders documented at // zddc.varasys.io/reference.html#transmittal-workflow: // archive → /archive.html (archive tool, project-root mode) // working → /working/ (directory listing → mdedit auto-serves) // staging → /staging/ (directory listing → transmittal auto-serves) // reviewing → /reviewing/ (directory listing) // // If a deployment doesn't have one of these folders the link will 404 — // the strip is convention-driven, not probed. Operators on non-standard // layouts can override by setting window.zddc.nav.disabled = true before // DOMContentLoaded. (function () { 'use strict'; if (!window.zddc) window.zddc = {}; if (window.zddc.nav) return; // already loaded var STAGES = [ { key: 'archive', label: 'Archive', target: 'archive.html' }, { key: 'working', label: 'Working', target: 'working/' }, { key: 'staging', label: 'Staging', target: 'staging/' }, { key: 'reviewing', label: 'Reviewing', target: 'reviewing/' }, ]; function projectSegment(pathname) { var parts = pathname.split('/').filter(Boolean); if (parts.length === 0) return null; var first = parts[0]; // At deployment root (e.g. /archive.html?projects=A,B or // /index.html) the first segment is a tool HTML — no single // project to scope the strip to. if (first.indexOf('.') !== -1) return null; return first; } function currentStage(pathname) { var parts = pathname.split('/').filter(Boolean); if (parts.length < 2) return null; var second = parts[1]; // /working/... | staging/... | reviewing/... | archive/... for (var i = 0; i < STAGES.length; i++) { if (second === STAGES[i].key) return STAGES[i].key; } // /archive.html → still the archive stage if (second === 'archive.html') return 'archive'; return null; } function shouldRender() { if (typeof location === 'undefined') return false; if (location.protocol !== 'http:' && location.protocol !== 'https:') return false; if (window.zddc.nav && window.zddc.nav.disabled) return false; return projectSegment(location.pathname) !== null; } function buildStrip(project, active) { var nav = document.createElement('nav'); nav.className = 'zddc-stage-strip'; nav.setAttribute('aria-label', 'Project stage'); var label = document.createElement('span'); label.className = 'zddc-stage-strip__project'; label.textContent = project; nav.appendChild(label); var sep0 = document.createElement('span'); sep0.className = 'zddc-stage-strip__divider'; sep0.setAttribute('aria-hidden', 'true'); sep0.textContent = '/'; nav.appendChild(sep0); for (var i = 0; i < STAGES.length; i++) { var s = STAGES[i]; var a = document.createElement('a'); a.className = 'zddc-stage'; a.href = '/' + encodeURIComponent(project) + '/' + s.target; a.textContent = s.label; if (s.key === active) { a.classList.add('zddc-stage--active'); a.setAttribute('aria-current', 'page'); } nav.appendChild(a); if (i < STAGES.length - 1) { var sep = document.createElement('span'); sep.className = 'zddc-stage-strip__sep'; sep.setAttribute('aria-hidden', 'true'); sep.textContent = '·'; nav.appendChild(sep); } } return nav; } function mount() { if (!shouldRender()) return; var header = document.querySelector('.app-header'); if (!header) return; // Don't double-mount if a tool's main.js calls us a second time. if (header.nextElementSibling && header.nextElementSibling.classList && header.nextElementSibling.classList.contains('zddc-stage-strip')) { return; } var project = projectSegment(location.pathname); var active = currentStage(location.pathname); var strip = buildStrip(project, active); header.parentNode.insertBefore(strip, header.nextSibling); } // Expose for tests + opt-out. window.zddc.nav = { mount: mount, // Internals visible for unit tests; do not call from tools. _projectSegment: projectSegment, _currentStage: currentStage, _stages: STAGES, // Set to true before DOMContentLoaded to suppress mounting on // deployments where the canonical folder layout doesn't apply. disabled: false, }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', mount, { once: true }); } else { mount(); } })();