/* ui-gods.js — Divine Pantheons section * Individual deity browser: Greek, Roman, Egyptian, Hebrew divine names, Archangels. * Kabbalah paths are shown only as a reference at the bottom of each detail view. */ (() => { "use strict"; const godReferenceBuilders = window.GodReferenceBuilders || {}; if (typeof godReferenceBuilders.buildMonthReferencesByGod !== "function") { throw new Error("GodReferenceBuilders module must load before ui-gods.js"); } // ── State ────────────────────────────────────────────────────────────────── const state = { initialized: false, gods: [], godsByName: new Map(), monthRefsByGodId: new Map(), filteredGods: [], selectedId: null, activeTab: "greek", searchQuery: "" }; let listEl, detailNameEl, detailSubEl, detailBodyEl, countEl, searchInputEl, searchClearEl; // ── Tab definitions ──────────────────────────────────────────────────────── const TABS = [ { id: "greek", label: "Greek", emoji: "🏛️" }, { id: "roman", label: "Roman", emoji: "🦅" }, { id: "egyptian", label: "Egyptian", emoji: "𓂀" }, { id: "hebrew", label: "God Names", emoji: "✡️" }, { id: "archangel", label: "Archangels", emoji: "☀️" }, { id: "all", label: "All", emoji: "∞" }, ]; const PANTHEON_LABEL = { greek: "Greek", roman: "Roman", egyptian: "Egyptian", hebrew: "God Names", archangel: "Archangel" }; function normalizeName(value) { return String(value || "") .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .toLowerCase() .replace(/[^a-z0-9]+/g, " ") .trim(); } function tokenizeEquivalent(value) { return String(value || "") .replace(/\([^)]*\)/g, " ") .split(/\/|,|;|\bor\b|\band\b|·|—|–/i) .map((token) => token.trim()) .filter(Boolean); } function findEquivalentTarget(equivalent, currentGodId) { const tokens = tokenizeEquivalent(equivalent); for (const token of tokens) { const matches = state.godsByName.get(normalizeName(token)); if (!matches?.length) continue; const target = matches.find((x) => x.id !== currentGodId); if (target) return target; } return null; } // ── Filter ───────────────────────────────────────────────────────────────── function applyFilter() { const q = state.searchQuery.toLowerCase(); const tab = state.activeTab; state.filteredGods = state.gods.filter((g) => { if (tab !== "all" && g.pantheon !== tab) return false; if (!q) return true; const hay = [ g.name, g.epithet, g.role, ...(g.domains || []), ...(g.parents || []), ...(g.siblings || []), ...(g.children || []), ...(g.symbols || []), ...(g.equivalents || []), g.meaning, g.description, g.myth, ].filter(Boolean).join(" ").toLowerCase(); return hay.includes(q); }); const hasSelected = state.filteredGods.some((g) => g.id === state.selectedId); if (!hasSelected) { state.selectedId = state.filteredGods[0]?.id || null; } renderList(); renderDetail(state.selectedId); } // ── List ─────────────────────────────────────────────────────────────────── function renderList() { if (!listEl) return; if (countEl) countEl.textContent = `${state.filteredGods.length} deities`; if (!state.filteredGods.length) { listEl.innerHTML = `
No matches
`; return; } listEl.innerHTML = state.filteredGods.map((g) => { const isActive = state.selectedId === g.id; const tag = PANTHEON_LABEL[g.pantheon] || g.pantheon; return `
${g.name} ${tag}
${g.role || g.epithet || "—"}
`; }).join(""); listEl.querySelectorAll(".gods-list-item").forEach((el) => { el.addEventListener("click", () => selectGod(el.dataset.id)); el.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") selectGod(el.dataset.id); }); }); } // ── Detail ───────────────────────────────────────────────────────────────── function renderDetail(id) { if (!detailNameEl) return; const g = state.gods.find((x) => x.id === id); if (!g) { detailNameEl.textContent = "—"; detailSubEl.textContent = "Select a deity to explore"; detailBodyEl.innerHTML = ""; return; } detailNameEl.textContent = g.name; detailSubEl.textContent = g.epithet || g.role || ""; const cards = []; // ── Role & Domains ── if (g.role || g.domains?.length) { const domHtml = g.domains?.length ? `
${g.domains.map(d => `${d}`).join("")}
` : ""; cards.push(`
⚡ Role & Domains
${g.role ? `
${g.role}
` : ""} ${domHtml}
`); } // ── Family ── const hasFamily = [g.parents, g.siblings, g.consorts, g.children].some(a => a?.length); if (hasFamily) { const rows = [ g.parents?.length ? `
Parents${g.parents.join(", ")}
` : "", g.siblings?.length ? `
Siblings${g.siblings.join(", ")}
` : "", g.consorts?.length ? `
Consort(s)${g.consorts.join(", ")}
` : "", g.children?.length ? `
Children${g.children.join(", ")}
` : "", ].filter(Boolean).join(""); cards.push(`
👨‍👩‍👧 Family
${rows}
`); } // ── Symbols & Sacred ── if (g.symbols?.length || g.sacredAnimals?.length || g.sacredPlaces?.length) { const rows = [ g.symbols?.length ? `
Symbols${g.symbols.join(", ")}
` : "", g.sacredAnimals?.length ? `
Sacred animals${g.sacredAnimals.join(", ")}
` : "", g.sacredPlaces?.length ? `
Sacred places${g.sacredPlaces.join(", ")}
` : "", ].filter(Boolean).join(""); cards.push(`
🔱 Sacred & Symbols
${rows}
`); } // ── Hebrew Name (divine names / archangels) ── if (g.hebrew) { const title = g.pantheon === "archangel" ? "☀️ Angelic Name" : "✡️ Hebrew Name"; cards.push(`
${title}
${g.hebrew} ${g.name}
${g.meaning ? `
${g.meaning}
` : ""} ${g.sephirah ? `
Governs Sephirah ${g.sephirah}
` : ""}
`); } // ── Equivalents ── if (g.equivalents?.length) { const equivalentHtml = g.equivalents.map((equivalent) => { const target = findEquivalentTarget(equivalent, g.id); if (target) { return ``; } return `${equivalent}`; }).join(""); cards.push(`
⟺ Equivalents
${equivalentHtml}
`); } const monthRefs = state.monthRefsByGodId.get(String(g.id || "").toLowerCase()) || []; if (monthRefs.length) { const monthButtons = monthRefs.map((month) => `` ).join(""); cards.push(`
📅 Calendar Months
Linked month correspondences for ${g.name}
${monthButtons}
`); } // ── Description ── if (g.description) { cards.push(`
📖 Description
${g.description}
`); } // ── Myth ── if (g.myth) { cards.push(`
📜 Myth
${g.myth}
`); } // ── Poem ── if (g.poem) { cards.push(`
✍️ Poem
${g.poem}
`); } // ── Kabbalah reference (small, at bottom) ── if (g.kabbalahPaths?.length) { const btnHtml = g.kabbalahPaths.map(p => `` ).join(""); cards.push(`
🌳 Kabbalah Reference
Paths: ${g.kabbalahPaths.join(", ")}
${btnHtml}
`); } detailBodyEl.innerHTML = `
${cards.join("")}
`; // Attach nav button listeners detailBodyEl.querySelectorAll(".gods-nav-btn[data-event]").forEach((btn) => { btn.addEventListener("click", () => { const evtName = btn.dataset.event; const detail = {}; Object.entries(btn.dataset).forEach(([key, val]) => { if (key === "event") return; const camel = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); detail[camel] = isNaN(Number(val)) || val === "" ? val : Number(val); }); document.dispatchEvent(new CustomEvent(evtName, { detail })); }); }); detailBodyEl.querySelectorAll(".gods-equivalent-link[data-god-id]").forEach((btn) => { btn.addEventListener("click", () => { const godId = btn.dataset.godId; if (godId) selectGod(godId); }); }); } // ── Tabs ─────────────────────────────────────────────────────────────────── function renderTabs() { const tabsEl = document.getElementById("gods-tabs"); if (!tabsEl) return; tabsEl.innerHTML = TABS.map((t) => `` ).join(""); tabsEl.querySelectorAll(".gods-tab-btn").forEach((btn) => { btn.addEventListener("click", () => { state.activeTab = btn.dataset.tab; renderTabs(); applyFilter(); }); }); } // ── Public: select god by id ─────────────────────────────────────────────── function selectGod(id) { const g = state.gods.find((x) => x.id === id); if (!g) return false; if (g && state.activeTab !== "all" && g.pantheon !== state.activeTab) { state.activeTab = "all"; renderTabs(); } state.selectedId = id; applyFilter(); requestAnimationFrame(() => { const item = listEl?.querySelector(`[data-id="${id}"]`); if (item) item.scrollIntoView({ block: "nearest", behavior: "smooth" }); }); return true; } function selectById(godId) { return selectGod(godId); } function selectByName(name) { const tokens = tokenizeEquivalent(name); for (const token of tokens) { const matches = state.godsByName.get(normalizeName(token)); const target = matches?.[0]; if (target?.id) { return selectGod(target.id); } } return false; } // ── Public: navigate here from kabbalah (find first god on that path) ────── function selectByPathNo(pathNo) { const no = Number(pathNo); const g = state.gods.find((x) => x.kabbalahPaths?.includes(no)); if (g) return selectGod(g.id); return false; } // ── Init ─────────────────────────────────────────────────────────────────── function ensureGodsSection(magickDataset, referenceData = null) { if (referenceData) { state.monthRefsByGodId = godReferenceBuilders.buildMonthReferencesByGod(referenceData); } if (state.initialized) { if (state.selectedId) { renderDetail(state.selectedId); } return; } state.initialized = true; listEl = document.getElementById("gods-list"); detailNameEl = document.getElementById("gods-detail-name"); detailSubEl = document.getElementById("gods-detail-sub"); detailBodyEl = document.getElementById("gods-detail-body"); countEl = document.getElementById("gods-count"); searchInputEl = document.getElementById("gods-search-input"); searchClearEl = document.getElementById("gods-search-clear"); const godsData = magickDataset?.grouped?.["gods"]; if (!godsData?.gods?.length) { if (listEl) listEl.innerHTML = `
Failed to load gods data.
`; return; } state.gods = godsData.gods; state.godsByName = state.gods.reduce((map, god) => { const key = normalizeName(god.name); const row = map.get(key) || []; row.push(god); map.set(key, row); return map; }, new Map()); if (searchInputEl) { searchInputEl.addEventListener("input", () => { state.searchQuery = searchInputEl.value; if (searchClearEl) searchClearEl.disabled = !state.searchQuery; applyFilter(); }); } if (searchClearEl) { searchClearEl.disabled = true; searchClearEl.addEventListener("click", () => { state.searchQuery = ""; if (searchInputEl) searchInputEl.value = ""; searchClearEl.disabled = true; applyFilter(); }); } renderTabs(); applyFilter(); } // ── Expose public API ────────────────────────────────────────────────────── window.GodsSectionUi = { ensureGodsSection, selectByPathNo, selectById, selectByName }; })();