/* ═══════════════════════════════════════════════════════ CONTRABAND // Resource Database Controller ═══════════════════════════════════════════════════════ */ (function () { 'use strict'; const API = '/api/contraband'; const root = document.getElementById('contrabandRoot'); if (!root) return; let state = { view: 'index', categories: [], searchTimeout: null }; // ─── Utilities ─────────────────────────────────────── function esc(s) { const d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; } function fmt(n) { return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } // ─── API Fetch ─────────────────────────────────────── async function api(endpoint) { const res = await fetch(API + endpoint); if (!res.ok) throw new Error(`API ${res.status}`); return res.json(); } // ─── Render: Index View ────────────────────────────── function renderIndex(data) { state.view = 'index'; state.categories = data.categories || []; let html = ''; // Stats bar html += `
DATABASE● ONLINE
TOTAL ASSETS${fmt(data.total_entries)}
STARRED${fmt(data.total_starred)}
CATEGORIES${data.total_categories}
STATUS● DECLASSIFIED
`; // Search html += `
`; // Search results container html += `
`; // Category grid html += `
`; for (const cat of state.categories) { if (cat.entry_count === 0) continue; html += `
${cat.icon}
${esc(cat.code)}
${esc(cat.name)}
${fmt(cat.entry_count)} assets ⭐ ${cat.starred_count}
● ACCESSIBLE
`; } html += `
`; // End of grid root.innerHTML = html; bindIndexEvents(); } // ─── Render: Category Detail ───────────────────────── function renderCategory(cat) { state.view = 'detail'; let html = ''; // Back button html += `
◄ BACK TO INDEX
`; // Detail header html += `
${esc(cat.code)}
${esc(cat.icon)} ${esc(cat.name)}
${fmt(cat.entry_count)} ASSETS ⭐ ${cat.starred_count} STARRED ${cat.subcategory_count} SECTIONS
`; // Search within category html += `
`; // Filter buttons html += `
`; // Subcategories html += `
`; for (let i = 0; i < cat.subcategories.length; i++) { const sub = cat.subcategories[i]; if (sub.entries.length === 0 && sub.notes.length === 0) continue; html += `
`; html += ``; // Notes if (sub.notes && sub.notes.length > 0) { html += ``; } // Entries html += `
`; } html += `
`; // End of grid root.innerHTML = html; bindDetailEvents(cat); } // ─── Render: Single Entry ──────────────────────────── function renderEntry(entry) { const starClass = entry.starred ? ' starred' : ''; const starIcon = entry.starred ? '⭐' : '·'; let html = `
`; html += `${starIcon}`; html += `
`; if (entry.url) { html += `
${esc(entry.name || entry.url)}
`; } else if (entry.name) { html += `
${esc(entry.name)}
`; } if (entry.description) { html += `
${esc(entry.description)}
`; } if (entry.extra_links && entry.extra_links.length > 0) { html += `
`; for (const link of entry.extra_links) { html += `${esc(link.name)}`; } html += `
`; } html += `
`; return html; } // ─── Render: Search Results ────────────────────────── function renderSearchResults(data) { const container = document.getElementById('crtSearchResults'); const countEl = document.getElementById('crtSearchCount'); const grid = document.getElementById('crtGrid'); if (!data.results || data.results.length === 0) { container.innerHTML = data.query ? '
NO MATCHING ASSETS FOUND
' : ''; countEl.textContent = data.query ? '0 RESULTS' : ''; if (grid) grid.style.display = data.query ? 'none' : ''; return; } countEl.textContent = `${data.results.length} RESULTS`; if (grid) grid.style.display = 'none'; let html = ''; for (const r of data.results) { const starIcon = r.starred ? '⭐ ' : ''; html += `
`; html += `
${esc(r.category_code)} // ${esc(r.category_name)} / ${esc(r.subcategory)}
`; html += `
${starIcon}`; if (r.url) { html += `${esc(r.name || r.url)}`; } else { html += esc(r.name); } html += `
`; if (r.description) { html += `
${esc(r.description)}
`; } html += `
`; } container.innerHTML = html; } // ─── Event Bindings: Index ─────────────────────────── function bindIndexEvents() { // Card clicks document.querySelectorAll('.crt-card').forEach(card => { card.addEventListener('click', () => { const slug = card.dataset.slug; loadCategory(slug); }); }); // Global search const searchInput = document.getElementById('crtSearch'); if (searchInput) { searchInput.addEventListener('input', () => { clearTimeout(state.searchTimeout); const q = searchInput.value.trim(); if (q.length < 2) { renderSearchResults({ query: '', results: [] }); return; } state.searchTimeout = setTimeout(async () => { try { const data = await api(`/search?q=${encodeURIComponent(q)}&limit=100`); renderSearchResults(data); } catch (e) { console.warn('Search failed:', e); } }, 300); }); } } // ─── Event Bindings: Detail ────────────────────────── function bindDetailEvents(cat) { // Back button document.getElementById('crtBack').addEventListener('click', () => { loadIndex(); }); // Subcategory toggle (collapse/expand) document.querySelectorAll('.crt-sub-header').forEach(header => { header.addEventListener('click', () => { const idx = header.dataset.toggle; const entries = document.getElementById(`crtEntries${idx}`); if (!entries) return; const isCollapsed = header.classList.contains('collapsed'); const notes = document.getElementById(`crtNotes${idx}`); if (isCollapsed) { header.classList.remove('collapsed'); entries.style.display = ''; if (notes) notes.style.display = ''; header.querySelector('.crt-sub-toggle').textContent = '▾'; } else { header.classList.add('collapsed'); entries.style.display = 'none'; if (notes) notes.style.display = 'none'; header.querySelector('.crt-sub-toggle').textContent = '▸'; } }); }); // Filter buttons document.querySelectorAll('.crt-filter-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.crt-filter-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); const filter = btn.dataset.filter; document.querySelectorAll('.crt-entry').forEach(entry => { if (filter === 'starred') { entry.style.display = entry.classList.contains('starred') ? '' : 'none'; } else { entry.style.display = ''; } }); }); }); // Category search (local filter) const catSearch = document.getElementById('crtCatSearch'); if (catSearch) { catSearch.addEventListener('input', () => { const q = catSearch.value.trim().toLowerCase(); document.querySelectorAll('.crt-entry').forEach(entry => { if (!q) { entry.style.display = ''; return; } const text = entry.textContent.toLowerCase(); entry.style.display = text.includes(q) ? '' : 'none'; }); // Hide empty subcategories document.querySelectorAll('.crt-subcategory').forEach(sub => { const visible = sub.querySelectorAll('.crt-entry:not([style*="display: none"])'); sub.style.display = visible.length > 0 || !q ? '' : 'none'; }); }); } } // ─── Load Functions ────────────────────────────────── async function loadIndex() { root.innerHTML = '
ACCESSING CONTRABAND DATABASE...
'; try { const data = await api(''); renderIndex(data); history.pushState({ view: 'index' }, '', '/depot/contraband'); } catch (e) { root.innerHTML = `
DATABASE ACCESS DENIED // ${esc(e.message)}
`; } } async function loadCategory(slug) { root.innerHTML = '
DECRYPTING CATEGORY DATA...
'; try { const data = await api(`/${slug}`); renderCategory(data); history.pushState({ view: 'detail', slug }, '', `/depot/contraband?cat=${slug}`); window.scrollTo({ top: 0, behavior: 'smooth' }); } catch (e) { root.innerHTML = `
CATEGORY NOT FOUND // ${esc(e.message)}
`; } } // ─── Handle Back/Forward ───────────────────────────── window.addEventListener('popstate', (e) => { if (e.state && e.state.view === 'detail' && e.state.slug) { loadCategory(e.state.slug); } else { loadIndex(); } }); // ─── Init ──────────────────────────────────────────── function init() { // Check URL for category param const params = new URLSearchParams(window.location.search); const cat = params.get('cat'); if (cat) { loadCategory(cat); } else { loadIndex(); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();