/** * GMIIE Universal Navigation — Back, GMIIE Home, Section Home, anchors, ecosystem hub. * Requires: data-gmiie-nav on
, /data/gmiie-sections.json, /css/gmiie-nav.css */ (function (global) { function navPaths() { const el = document.querySelector('script[src*="gmiie-nav.js"]'); const src = el && el.getAttribute('src'); if (src && src.indexOf('/static/') >= 0) { return { css: '/static/gmiie-nav.css', data: '/static/gmiie-sections.json' }; } return { css: '/css/gmiie-nav.css', data: '/data/gmiie-sections.json' }; } const SKIP_ANCHOR_RE = /^(fr-|sp-|lg-|cb-|en-|ft-|ipfs-)|(-btn$|-date$|-count$|-theme|-updated|-gen$|-meta$)/i; const SKIP_IDS = new Set([ 'form-simple', 'form-investigate', 'check-results', 'advanced-section', 'advanced-toggle-btn', ]); let registry = null; let currentSection = null; let currentSite = 'xxxiii'; function detectSite(host) { const h = (host || '').toLowerCase(); if (h.includes('blockchainfraud') || h.includes('fraud.troptionsmint')) return 'blockchainfraud'; if (h.includes('nil33')) return 'nil33'; return 'xxxiii'; } function normalizePath(pathname) { let p = (pathname || '/').split('?')[0].split('#')[0]; if (p.endsWith('/index.html')) p = p.slice(0, -10) || '/'; else if (p.endsWith('.html')) p = p.slice(0, -5) || '/'; if (p.length > 1 && p.endsWith('/')) p = p.slice(0, -1); return p || '/'; } function pageSlugFromPath(path, site) { if (path === '/' || path === '') return site === 'blockchainfraud' || site === 'nil33' ? site === 'nil33' ? 'nil33' : 'blockchainfraud' : 'home'; const slug = path.replace(/^\//, '').split('/')[0]; if (slug === 'nil' && site === 'xxxiii') return 'nil'; return slug; } function resolveSection(reg, opts) { const pageAttr = document.body.getAttribute('data-gmiie-page'); const path = normalizePath(location.pathname); const site = opts.site; const slug = pageAttr || pageSlugFromPath(path, site); let match = reg.sections.find((s) => s.id === slug) || reg.sections.find((s) => s.path === path) || reg.sections.find((s) => path.startsWith(s.path) && s.path !== '/'); if (!match && site === 'blockchainfraud') { match = reg.sections.find((s) => s.id === 'blockchainfraud'); } if (!match && site === 'nil33') { match = reg.sections.find((s) => s.id === 'nil33'); } if (!match) { match = reg.sections.find((s) => s.id === 'home'); } return match; } function anchorLabel(id, el) { if (el && el.getAttribute('data-nav-label')) return el.getAttribute('data-nav-label'); const h = el && (el.querySelector('h1,h2,h3,.sec-title,.intro-hl') || el); if (h && h !== el) { const t = (h.textContent || '').trim().replace(/\s+/g, ' '); if (t && t.length < 60) return t; } return id .split('-') .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) .join(' '); } function normalizeAnchors(raw) { if (!raw || !raw.length) return []; return raw.map((a) => { if (typeof a === 'string') return { id: a, label: anchorLabel(a) }; return { id: a.id, label: a.label || anchorLabel(a.id) }; }); } function discoverAnchors(sectionAnchors) { const fromReg = normalizeAnchors(sectionAnchors); const seen = new Set(fromReg.map((a) => a.id)); const out = fromReg.slice(); const root = document.querySelector('main') || document.body; root.querySelectorAll('[id], [data-nav-anchor]').forEach((el) => { const id = el.id || el.getAttribute('data-nav-anchor'); if (!id || seen.has(id) || SKIP_IDS.has(id) || SKIP_ANCHOR_RE.test(id)) return; if (!document.getElementById(id)) return; const tag = el.tagName; const isSection = tag === 'SECTION' || el.classList.contains('sec') || el.hasAttribute('data-nav-anchor') || el.querySelector('h1,h2,h3,.sec-title'); if (!isSection && tag !== 'SECTION' && !el.hasAttribute('data-nav-anchor')) return; seen.add(id); out.push({ id, label: anchorLabel(id, el) }); }); return out.slice(0, 24); } function ecosystemLinks(reg, site, host) { const excludeHosts = reg.ecosystemExcludeHosts || []; const excludeTags = reg.ecosystemExcludeTags || []; const hideUnykorn = excludeHosts.some((h) => host.includes(h.replace(/^www\./, ''))); return reg.sections.filter((s) => { if (!s.ecosystem) return false; if (s.site === site && s.id === currentSection?.id) return false; if (hideUnykorn && (s.tags || []).some((t) => excludeTags.includes(t))) return false; if (hideUnykorn && /unykorn/i.test(s.url || '')) return false; return true; }); } function goBack(section) { const sectionHome = section.url || (registry && registry.gmiieHome) || 'https://xxxiii.io/'; if (global.history.length > 1) { global.history.back(); } else { location.href = sectionHome; } } function labelsRedundant(section) { const longLabel = (section.label || '').trim().toLowerCase(); const shortLabel = (section.shortLabel || '').trim().toLowerCase(); if (!longLabel || !shortLabel) return false; if (longLabel === shortLabel) return true; return longLabel.includes(shortLabel) || shortLabel.includes(longLabel); } function normalizeHref(href) { if (!href) return ''; try { const u = new URL(href, location.origin); return normalizePath(u.pathname); } catch (e) { return normalizePath(href); } } function isCurrentNavLink(item) { const cur = normalizePath(location.pathname); const target = normalizeHref(item.url); if (cur === target) return true; const pageAttr = document.body.getAttribute('data-gmiie-page'); if (pageAttr && item.id && pageAttr === item.id) return true; return false; } function buildLegalDropdown(links) { if (!links || !links.length) return null; const legalDrop = document.createElement('details'); legalDrop.className = 'gmiie-nav-dropdown'; const sum = document.createElement('summary'); sum.className = 'gmiie-nav-btn'; sum.textContent = 'Legal ▾'; const menu = document.createElement('div'); menu.className = 'gmiie-nav-menu'; const hd = document.createElement('div'); hd.className = 'menu-hd'; hd.textContent = 'Trust & methodology'; menu.appendChild(hd); let hasActive = false; links.forEach(function (item) { const link = document.createElement('a'); link.href = item.url; link.textContent = item.label; if (isCurrentNavLink(item)) { link.classList.add('active'); link.setAttribute('aria-current', 'page'); hasActive = true; } menu.appendChild(link); }); if (hasActive) { legalDrop.open = true; sum.classList.add('primary'); sum.textContent = 'Legal ▾'; } legalDrop.appendChild(sum); legalDrop.appendChild(menu); return legalDrop; } function buildNav(reg, section, anchors, eco, legalLinks) { const root = document.createElement('div'); root.id = 'gmiie-nav-root'; root.setAttribute('role', 'navigation'); root.setAttribute('aria-label', 'GMIIE site navigation'); const inner = document.createElement('div'); inner.className = 'gmiie-nav-inner'; const toggle = document.createElement('button'); toggle.type = 'button'; toggle.className = 'gmiie-nav-toggle'; toggle.setAttribute('aria-label', 'Open navigation menu'); toggle.setAttribute('aria-expanded', 'false'); toggle.textContent = '☰'; const actions = document.createElement('div'); actions.className = 'gmiie-nav-actions collapsed'; const backBtn = document.createElement('button'); backBtn.type = 'button'; backBtn.className = 'gmiie-nav-btn'; backBtn.innerHTML = ' Back'; backBtn.addEventListener('click', function () { goBack(section); }); const gmiieHome = document.createElement('a'); gmiieHome.className = 'gmiie-nav-btn'; gmiieHome.href = reg.gmiieHome || 'https://xxxiii.io/'; gmiieHome.textContent = 'GMIIE Home'; const sectionHome = document.createElement('a'); sectionHome.className = 'gmiie-nav-btn primary'; sectionHome.href = section.url || reg.gmiieHome; sectionHome.textContent = section.shortLabel || section.label || 'Section'; const label = document.createElement('span'); label.className = 'gmiie-nav-label'; if (!section.shortLabel && !labelsRedundant(section)) { label.textContent = section.label || ''; } let anchorDrop = null; if (anchors.length) { anchorDrop = document.createElement('details'); anchorDrop.className = 'gmiie-nav-dropdown'; anchorDrop.open = false; const sum = document.createElement('summary'); sum.className = 'gmiie-nav-btn'; sum.textContent = 'On Page ▾'; const menu = document.createElement('div'); menu.className = 'gmiie-nav-menu'; menu.setAttribute('role', 'menu'); const hd = document.createElement('div'); hd.className = 'menu-hd'; hd.textContent = 'Jump to section'; menu.appendChild(hd); anchors.forEach(function (a) { const link = document.createElement('a'); link.href = '#' + a.id; link.textContent = a.label; link.setAttribute('role', 'menuitem'); menu.appendChild(link); }); anchorDrop.appendChild(sum); anchorDrop.appendChild(menu); } let ecoDrop = null; if (eco.length) { ecoDrop = document.createElement('details'); ecoDrop.className = 'gmiie-nav-dropdown'; ecoDrop.open = false; const sum = document.createElement('summary'); sum.className = 'gmiie-nav-btn'; sum.textContent = 'Ecosystem ▾'; const menu = document.createElement('div'); menu.className = 'gmiie-nav-menu'; const hd = document.createElement('div'); hd.className = 'menu-hd'; hd.textContent = 'GMIIE surfaces'; menu.appendChild(hd); eco.forEach(function (s) { const link = document.createElement('a'); link.href = s.url; link.textContent = s.label; if (s.url && /^https?:\/\//i.test(s.url) && !s.url.includes(location.host)) { link.target = '_blank'; link.rel = 'noopener'; } menu.appendChild(link); }); ecoDrop.appendChild(sum); ecoDrop.appendChild(menu); } const spacer = document.createElement('div'); spacer.className = 'gmiie-nav-spacer'; actions.appendChild(backBtn); actions.appendChild(gmiieHome); actions.appendChild(sectionHome); if (anchorDrop) actions.appendChild(anchorDrop); if (ecoDrop) actions.appendChild(ecoDrop); const legalDrop = buildLegalDropdown(legalLinks || reg.footerLinks); if (legalDrop) actions.appendChild(legalDrop); inner.appendChild(toggle); inner.appendChild(label); inner.appendChild(spacer); inner.appendChild(actions); root.appendChild(inner); function setMobileOpen(open) { actions.classList.toggle('open', open); actions.classList.toggle('collapsed', !open); toggle.setAttribute('aria-expanded', open ? 'true' : 'false'); toggle.textContent = open ? '✕' : '☰'; } toggle.addEventListener('click', function () { setMobileOpen(!actions.classList.contains('open')); }); document.addEventListener('click', function (e) { if (window.innerWidth >= 720) return; if (!root.contains(e.target)) setMobileOpen(false); }); return root; } function mount(reg) { const existing = document.getElementById('gmiie-nav-root'); if (existing) existing.remove(); const host = location.hostname.replace(/^www\./, ''); currentSite = detectSite(host); currentSection = resolveSection(reg, { site: currentSite }); const anchors = discoverAnchors(currentSection.anchors); const eco = ecosystemLinks(reg, currentSite, host); const nav = buildNav(reg, currentSection, anchors, eco, reg.footerLinks); const body = document.body; body.classList.add('gmiie-nav-active'); const insertBefore = body.querySelector('.troptions-top') || body.firstChild; if (insertBefore) body.insertBefore(nav, insertBefore); else body.prepend(nav); } function injectCriticalNavStyles() { if (document.getElementById('gmiie-nav-critical')) return; const s = document.createElement('style'); s.id = 'gmiie-nav-critical'; s.textContent = '#gmiie-nav-root{position:fixed;top:0;left:0;right:0;z-index:10001;height:42px;font-family:var(--mono,JetBrains Mono,Consolas,monospace);background:#1a1a1a;color:rgba(255,255,255,.88);border-bottom:2px solid #b8953a;box-shadow:0 4px 20px rgba(0,0,0,.28);}' + 'body.gmiie-nav-active{padding-top:42px;}' + '.gmiie-nav-inner{height:100%;padding:0 12px;display:flex;align-items:center;gap:8px;}' + '.gmiie-nav-btn{display:inline-flex;align-items:center;gap:4px;font:inherit;font-size:9px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.55);background:transparent;border:1px solid rgba(255,255,255,.12);padding:5px 10px;cursor:pointer;text-decoration:none;white-space:nowrap;}' + '.gmiie-nav-btn.primary{color:#d4af55;border-color:#b8953a;background:rgba(184,149,58,.08);}' + '.gmiie-nav-spacer{flex:1;}' + '.gmiie-nav-label{display:none;}' + '.gmiie-nav-toggle{display:none;width:36px;height:32px;border:1px solid rgba(255,255,255,.12);background:transparent;color:rgba(255,255,255,.88);cursor:pointer;font-size:16px;}' + '.gmiie-nav-actions{display:flex;align-items:center;gap:6px;}' + '.gmiie-nav-actions.collapsed{display:none;}' + '.gmiie-nav-dropdown>summary{list-style:none;cursor:pointer;}' + '.gmiie-nav-dropdown>summary::-webkit-details-marker{display:none;}' + '.gmiie-nav-dropdown:not([open]) .gmiie-nav-menu{display:none;}' + '.gmiie-nav-menu{position:absolute;top:calc(100% + 4px);left:0;min-width:200px;max-height:min(70vh,420px);overflow-y:auto;background:#131925;border:1px solid #1f2533;box-shadow:0 12px 40px rgba(0,0,0,.35);z-index:10002;padding:6px 0;}' + '.gmiie-nav-menu a{display:block;padding:8px 14px;font-size:9px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9da3b4;text-decoration:none;}' + '@media(max-width:719px){.gmiie-nav-toggle{display:inline-flex;align-items:center;justify-content:center;}.gmiie-nav-actions:not(.open){display:none!important;}.gmiie-nav-spacer{display:none;}}' + '@media(min-width:720px){.gmiie-nav-inner{padding:0 24px;}.gmiie-nav-actions.collapsed{display:flex;}}'; document.head.appendChild(s); } function init() { if (!document.body.hasAttribute('data-gmiie-nav')) return; injectCriticalNavStyles(); const paths = navPaths(); if (!document.querySelector('link[href="' + paths.css + '"]')) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = paths.css; document.head.appendChild(link); } fetch(paths.data, { cache: 'default' }) .then(function (r) { if (!r.ok) throw new Error('sections fetch ' + r.status); return r.json(); }) .then(function (data) { registry = data; mount(data); }) .catch(function () { registry = { gmiieHome: 'https://xxxiii.io/', sections: [ { id: 'home', label: 'GMIIE Home', url: 'https://xxxiii.io/', path: '/', site: 'xxxiii', ecosystem: true, }, { id: 'fraud', label: 'Fraud Desk', url: 'https://xxxiii.io/fraud', path: '/fraud', site: 'xxxiii', ecosystem: true, }, { id: 'blockchainfraud', label: 'BlockchainFraud.org', url: 'https://blockchainfraud.org/', path: '/', site: 'blockchainfraud', ecosystem: true, }, ], }; mount(registry); }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } global.GMIIENav = { refresh: function () { if (registry) mount(registry); } }; })();