/* ui-alphabet.js — Multi-alphabet browser (English / Hebrew / Greek / Arabic / Enochian) */ (function () { "use strict"; const alphabetGematriaUi = window.AlphabetGematriaUi || {}; const alphabetKabbalahUi = window.AlphabetKabbalahUi || {}; const alphabetReferenceBuilders = window.AlphabetReferenceBuilders || {}; const alphabetDetailUi = window.AlphabetDetailUi || {}; if ( typeof alphabetKabbalahUi.buildCubePlacementButton !== "function" || typeof alphabetKabbalahUi.buildFourWorldLayersFromDataset !== "function" || typeof alphabetKabbalahUi.createEmptyCubeRefs !== "function" || typeof alphabetKabbalahUi.getCubePlacementForHebrewLetter !== "function" || typeof alphabetKabbalahUi.getCubePlacementForPlanet !== "function" || typeof alphabetKabbalahUi.getCubePlacementForSign !== "function" || typeof alphabetKabbalahUi.normalizeId !== "function" || typeof alphabetKabbalahUi.normalizeLetterId !== "function" || typeof alphabetKabbalahUi.titleCase !== "function" ) { throw new Error("AlphabetKabbalahUi module must load before ui-alphabet.js"); } const state = { initialized: false, alphabets: null, activeAlphabet: "all", selectedKey: null, filters: { query: "", letterType: "" }, fourWorldLayers: [], monthRefsByHebrewId: new Map(), cubeRefs: { hebrewPlacementById: new Map(), signPlacementById: new Map(), planetPlacementById: new Map(), pathPlacementByNo: new Map() } }; // ── Arabic display name table ───────────────────────────────────────── const ARABIC_DISPLAY_NAMES = { alif: "Alif", ba: "Ba", jeem: "Jeem", dal: "Dal", ha: "H\u0101", waw: "W\u0101w", zayn: "Zayn", ha_khaa: "\u1e24\u0101", ta_tay: "\u1e6c\u0101", ya: "Y\u0101", kaf: "K\u0101f", lam: "L\u0101m", meem: "M\u012bm", nun: "N\u016bn", seen: "S\u012bn", ayn: "\u02bfAyn", fa: "F\u0101", sad: "\u1e62\u0101d", qaf: "Q\u0101f", ra: "R\u0101", sheen: "Sh\u012bn", ta: "T\u0101", tha: "Th\u0101", kha: "Kh\u0101", dhal: "Dh\u0101l", dad: "\u1e0c\u0101d", dha: "\u1e92\u0101", ghayn: "Ghayn" }; function arabicDisplayName(letter) { return ARABIC_DISPLAY_NAMES[letter && letter.name] || (String(letter && letter.name || "").charAt(0).toUpperCase() + String(letter && letter.name || "").slice(1)); } // ── Element cache ──────────────────────────────────────────────────── let listEl, countEl, detailNameEl, detailSubEl, detailBodyEl; let tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian; let searchInputEl, searchClearEl, typeFilterEl; let gematriaCipherEl, gematriaInputEl, gematriaResultEl, gematriaBreakdownEl; function getElements() { listEl = document.getElementById("alpha-letter-list"); countEl = document.getElementById("alpha-letter-count"); detailNameEl = document.getElementById("alpha-detail-name"); detailSubEl = document.getElementById("alpha-detail-sub"); detailBodyEl = document.getElementById("alpha-detail-body"); tabAll = document.getElementById("alpha-tab-all"); tabHebrew = document.getElementById("alpha-tab-hebrew"); tabGreek = document.getElementById("alpha-tab-greek"); tabEnglish = document.getElementById("alpha-tab-english"); tabArabic = document.getElementById("alpha-tab-arabic"); tabEnochian = document.getElementById("alpha-tab-enochian"); searchInputEl = document.getElementById("alpha-search-input"); searchClearEl = document.getElementById("alpha-search-clear"); typeFilterEl = document.getElementById("alpha-type-filter"); gematriaCipherEl = document.getElementById("alpha-gematria-cipher"); gematriaInputEl = document.getElementById("alpha-gematria-input"); gematriaResultEl = document.getElementById("alpha-gematria-result"); gematriaBreakdownEl = document.getElementById("alpha-gematria-breakdown"); } function getGematriaElements() { getElements(); return { cipherEl: gematriaCipherEl, inputEl: gematriaInputEl, resultEl: gematriaResultEl, breakdownEl: gematriaBreakdownEl }; } function ensureGematriaCalculator() { alphabetGematriaUi.init?.({ getAlphabets: () => state.alphabets, getGematriaElements }); alphabetGematriaUi.ensureCalculator?.(); } // ── Data helpers ───────────────────────────────────────────────────── function getLetters() { if (!state.alphabets) return []; if (state.activeAlphabet === "all") { const alphabetOrder = ["hebrew", "greek", "english", "arabic", "enochian"]; return alphabetOrder.flatMap((alphabet) => { const rows = Array.isArray(state.alphabets?.[alphabet]) ? state.alphabets[alphabet] : []; return rows.map((row) => ({ ...row, __alphabet: alphabet })); }); } return state.alphabets[state.activeAlphabet] || []; } function alphabetForLetter(letter) { if (state.activeAlphabet === "all") { return String(letter?.__alphabet || "").trim().toLowerCase(); } return state.activeAlphabet; } function letterKeyByAlphabet(alphabet, letter) { if (alphabet === "hebrew") return letter.hebrewLetterId; if (alphabet === "greek") return letter.name; if (alphabet === "english") return letter.letter; if (alphabet === "arabic") return letter.name; if (alphabet === "enochian") return letter.id; return String(letter.index); } function letterKey(letter) { // Stable unique key per alphabet + entry const alphabet = alphabetForLetter(letter); const key = letterKeyByAlphabet(alphabet, letter); if (state.activeAlphabet === "all") { return `${alphabet}:${key}`; } return key; } function displayGlyph(letter) { const alphabet = alphabetForLetter(letter); if (alphabet === "hebrew") return letter.char; if (alphabet === "greek") return letter.char; if (alphabet === "english") return letter.letter; if (alphabet === "arabic") return letter.char; if (alphabet === "enochian") return letter.char; return "?"; } function displayLabel(letter) { const alphabet = alphabetForLetter(letter); if (alphabet === "hebrew") return letter.name; if (alphabet === "greek") return letter.displayName; if (alphabet === "english") return letter.letter; if (alphabet === "arabic") return arabicDisplayName(letter); if (alphabet === "enochian") return letter.title; return "?"; } function displaySub(letter) { const alphabet = alphabetForLetter(letter); if (alphabet === "hebrew") return `${letter.transliteration} · ${letter.letterType} · ${letter.numerology}`; if (alphabet === "greek") return `${letter.transliteration} · isopsephy ${letter.numerology}${letter.archaic ? " · archaic" : ""}`; if (alphabet === "english") return `Pythagorean ${letter.pythagorean}`; if (alphabet === "arabic") return `${letter.transliteration} · abjad ${letter.abjad} · ${letter.nameArabic}`; if (alphabet === "enochian") return `${letter.transliteration} · ${Array.isArray(letter.englishLetters) ? letter.englishLetters.join("/") : ""} · value ${letter.numerology}`; return ""; } function normalizeLetterType(value) { const key = String(value || "").trim().toLowerCase(); if (["mother", "double", "simple"].includes(key)) { return key; } return ""; } function getHebrewLetterTypeMap() { const map = new Map(); const hebrewLetters = Array.isArray(state.alphabets?.hebrew) ? state.alphabets.hebrew : []; hebrewLetters.forEach((entry) => { const hebrewId = normalizeId(entry?.hebrewLetterId); const letterType = normalizeLetterType(entry?.letterType); if (hebrewId && letterType) { map.set(hebrewId, letterType); } }); return map; } function resolveLetterType(letter) { const direct = normalizeLetterType(letter?.letterType); if (direct) { return direct; } const hebrewId = normalizeId(letter?.hebrewLetterId); if (!hebrewId) { return ""; } return getHebrewLetterTypeMap().get(hebrewId) || ""; } function buildLetterSearchText(letter) { const chunks = []; chunks.push(String(displayLabel(letter) || "")); chunks.push(String(displayGlyph(letter) || "")); chunks.push(String(displaySub(letter) || "")); chunks.push(String(letter?.transliteration || "")); chunks.push(String(letter?.meaning || "")); chunks.push(String(letter?.nameArabic || "")); chunks.push(String(letter?.title || "")); chunks.push(String(letter?.letter || "")); chunks.push(String(letter?.displayName || "")); chunks.push(String(letter?.name || "")); chunks.push(String(letter?.index || "")); chunks.push(String(resolveLetterType(letter) || "")); return chunks .join(" ") .toLowerCase(); } function getFilteredLetters() { const letters = getLetters(); const query = String(state.filters.query || "").trim().toLowerCase(); const letterTypeFilter = normalizeLetterType(state.filters.letterType); const numericPosition = /^\d+$/.test(query) ? Number(query) : null; return letters.filter((letter) => { if (letterTypeFilter) { const entryType = resolveLetterType(letter); if (entryType !== letterTypeFilter) { return false; } } if (!query) { return true; } const index = Number(letter?.index); if (Number.isFinite(numericPosition) && Number.isFinite(index) && index === numericPosition) { return true; } return buildLetterSearchText(letter).includes(query); }); } function syncFilterControls() { if (searchInputEl && searchInputEl.value !== state.filters.query) { searchInputEl.value = state.filters.query; } if (typeFilterEl && typeFilterEl.value !== state.filters.letterType) { typeFilterEl.value = state.filters.letterType; } if (searchClearEl) { const hasFilter = Boolean(String(state.filters.query || "").trim()) || Boolean(state.filters.letterType); searchClearEl.disabled = !hasFilter; } } function applyFiltersAndRender() { const filteredLetters = getFilteredLetters(); const selectedInFiltered = filteredLetters.some((letter) => letterKey(letter) === state.selectedKey); if (!selectedInFiltered) { state.selectedKey = filteredLetters[0] ? letterKey(filteredLetters[0]) : null; } renderList(); const selected = filteredLetters.find((letter) => letterKey(letter) === state.selectedKey) || filteredLetters[0] || null; if (selected) { renderDetail(selected); return; } resetDetail(); if (detailSubEl) { detailSubEl.textContent = "No letters match the current filter."; } } function bindFilterControls() { if (searchInputEl) { searchInputEl.addEventListener("input", () => { state.filters.query = String(searchInputEl.value || ""); syncFilterControls(); applyFiltersAndRender(); }); } if (typeFilterEl) { typeFilterEl.addEventListener("change", () => { state.filters.letterType = normalizeLetterType(typeFilterEl.value); syncFilterControls(); applyFiltersAndRender(); }); } if (searchClearEl) { searchClearEl.addEventListener("click", () => { state.filters.query = ""; state.filters.letterType = ""; syncFilterControls(); applyFiltersAndRender(); }); } } function enochianGlyphKey(letter) { return String(letter?.id || letter?.char || "").trim().toUpperCase(); } function enochianGlyphCode(letter) { const key = enochianGlyphKey(letter); return key ? key.codePointAt(0) || 0 : 0; } function enochianGlyphUrl(letter) { const code = enochianGlyphCode(letter); return code ? `asset/img/enochian/char(${code}).png` : ""; } function enochianGlyphImageHtml(letter, className) { const src = enochianGlyphUrl(letter); const key = enochianGlyphKey(letter) || "?"; if (!src) { return `${key}`; } return `Enochian ${key}`; } // ── List rendering ──────────────────────────────────────────────────── function renderList() { if (!listEl) return; const allLetters = getLetters(); const letters = getFilteredLetters(); if (countEl) { countEl.textContent = letters.length === allLetters.length ? `${letters.length}` : `${letters.length}/${allLetters.length}`; } listEl.innerHTML = ""; letters.forEach((letter) => { const key = letterKey(letter); const item = document.createElement("div"); item.className = "planet-list-item alpha-list-item" + (key === state.selectedKey ? " is-selected" : ""); item.setAttribute("role", "option"); item.setAttribute("aria-selected", key === state.selectedKey ? "true" : "false"); item.dataset.key = key; const glyph = document.createElement("span"); const alphabet = alphabetForLetter(letter); const glyphVariantClass = alphabet === "arabic" ? " alpha-list-glyph--arabic" : alphabet === "enochian" ? " alpha-list-glyph--enochian" : ""; glyph.className = "alpha-list-glyph" + glyphVariantClass; if (alphabet === "enochian") { const image = document.createElement("img"); image.className = "alpha-enochian-glyph-img alpha-enochian-glyph-img--list"; image.src = enochianGlyphUrl(letter); image.alt = `Enochian ${enochianGlyphKey(letter) || "?"}`; image.loading = "lazy"; image.decoding = "async"; image.addEventListener("error", () => { glyph.textContent = enochianGlyphKey(letter) || "?"; }); glyph.appendChild(image); } else { glyph.textContent = displayGlyph(letter); } const meta = document.createElement("span"); meta.className = "alpha-list-meta"; const alphaLabel = alphabet ? `${cap(alphabet)} · ` : ""; meta.innerHTML = `${alphaLabel}${displayLabel(letter)}
${displaySub(letter)}`; item.appendChild(glyph); item.appendChild(meta); item.addEventListener("click", () => selectLetter(key)); listEl.appendChild(item); }); } // ── Detail rendering ────────────────────────────────────────────────── function renderDetail(letter) { if (!detailNameEl) return; const alphabet = alphabetForLetter(letter); detailNameEl.replaceChildren(); if (alphabet === "enochian") { const image = document.createElement("img"); image.className = "alpha-enochian-glyph-img alpha-enochian-glyph-img--detail"; image.src = enochianGlyphUrl(letter); image.alt = `Enochian ${enochianGlyphKey(letter) || "?"}`; image.loading = "lazy"; image.decoding = "async"; image.addEventListener("error", () => { detailNameEl.textContent = enochianGlyphKey(letter) || "?"; }); detailNameEl.appendChild(image); } else { detailNameEl.textContent = displayGlyph(letter); } detailNameEl.classList.add("alpha-detail-glyph"); detailNameEl.classList.toggle("alpha-detail-glyph--arabic", alphabet === "arabic"); detailNameEl.classList.toggle("alpha-detail-glyph--enochian", alphabet === "enochian"); if (typeof alphabetDetailUi.renderDetail === "function") { alphabetDetailUi.renderDetail(getDetailRenderContext(letter, alphabet)); } } function card(title, bodyHTML) { return `
${title}
${bodyHTML}
`; } const PLANET_SYMBOLS = { mercury: "☿︎", luna: "☾︎", venus: "♀︎", sol: "☉︎", jupiter: "♃︎", mars: "♂︎", saturn: "♄︎" }; const ZODIAC_SYMBOLS = { aries: "♈︎", taurus: "♉︎", gemini: "♊︎", cancer: "♋︎", leo: "♌︎", virgo: "♍︎", libra: "♎︎", scorpio: "♏︎", sagittarius: "♐︎", capricorn: "♑︎", aquarius: "♒︎", pisces: "♓︎" }; const HEBREW_DOUBLE_DUALITY = { bet: { left: "Life", right: "Death" }, gimel: { left: "Peace", right: "War" }, dalet: { left: "Wisdom", right: "Folly" }, kaf: { left: "Wealth", right: "Poverty" }, pe: { left: "Beauty", right: "Ugliness" }, resh: { left: "Fruitfulness", right: "Sterility" }, tav: { left: "Dominion", right: "Slavery" } }; function normalizeId(value) { return alphabetKabbalahUi.normalizeId(value); } function normalizeLetterId(value) { return alphabetKabbalahUi.normalizeLetterId(value); } function titleCase(value) { return alphabetKabbalahUi.titleCase(value); } function buildFourWorldLayersFromDataset(magickDataset) { return alphabetKabbalahUi.buildFourWorldLayersFromDataset(magickDataset); } function buildMonthReferencesByHebrew(referenceData, alphabets) { if (typeof alphabetReferenceBuilders.buildMonthReferencesByHebrew !== "function") { return new Map(); } return alphabetReferenceBuilders.buildMonthReferencesByHebrew(referenceData, alphabets); } function createEmptyCubeRefs() { return alphabetKabbalahUi.createEmptyCubeRefs(); } function buildCubeReferences(magickDataset) { if (typeof alphabetReferenceBuilders.buildCubeReferences !== "function") { return createEmptyCubeRefs(); } return alphabetReferenceBuilders.buildCubeReferences(magickDataset); } function getCubePlacementForHebrewLetter(hebrewLetterId, pathNo = null) { return alphabetKabbalahUi.getCubePlacementForHebrewLetter(state.cubeRefs, hebrewLetterId, pathNo); } function getCubePlacementForPlanet(planetId) { return alphabetKabbalahUi.getCubePlacementForPlanet(state.cubeRefs, planetId); } function getCubePlacementForSign(signId) { return alphabetKabbalahUi.getCubePlacementForSign(state.cubeRefs, signId); } function cubePlacementBtn(placement, fallbackDetail = null) { return alphabetKabbalahUi.buildCubePlacementButton(placement, navBtn, fallbackDetail); } function cap(s) { return s ? s.charAt(0).toUpperCase() + s.slice(1) : ""; } function navBtn(label, event, detail) { const attrs = Object.entries(detail).map(([k, v]) => `data-${k}="${v}"`).join(" "); return ``; } function getDetailRenderContext(letter, alphabet) { return { letter, alphabet, detailSubEl, detailBodyEl, alphabets: state.alphabets, fourWorldLayers: state.fourWorldLayers, monthRefsByHebrewId: state.monthRefsByHebrewId, PLANET_SYMBOLS, ZODIAC_SYMBOLS, HEBREW_DOUBLE_DUALITY, card, navBtn, cap, normalizeId, normalizeLetterId, getCubePlacementForPlanet, getCubePlacementForSign, getCubePlacementForHebrewLetter, cubePlacementBtn, arabicDisplayName, enochianGlyphImageHtml, attachDetailListeners }; } // ── Event delegation on detail body ────────────────────────────────── function attachDetailListeners() { if (!detailBodyEl) return; // Nav buttons — generic: forward all data-* (except data-event) as the event detail detailBodyEl.querySelectorAll(".alpha-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; // Convert kebab data keys to camelCase (e.g. planet-id → planetId) 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 })); }); }); // Sister letter cross-navigation within this section detailBodyEl.querySelectorAll(".alpha-sister-btn[data-alpha]").forEach((btn) => { btn.addEventListener("click", () => { const alpha = btn.dataset.alpha; const key = btn.dataset.key; switchAlphabet(alpha, key); }); }); } // ── Selection ───────────────────────────────────────────────────────── function selectLetter(key) { state.selectedKey = key; renderList(); const letters = getFilteredLetters(); const letter = letters.find((l) => letterKey(l) === key) || getLetters().find((l) => letterKey(l) === key); if (letter) renderDetail(letter); } // ── Alphabet switching ──────────────────────────────────────────────── function switchAlphabet(alpha, selectKey) { state.activeAlphabet = alpha; state.selectedKey = selectKey || null; updateTabs(); syncFilterControls(); renderList(); if (selectKey) { const letters = getFilteredLetters(); const letter = letters.find((l) => letterKey(l) === selectKey) || getLetters().find((l) => letterKey(l) === selectKey); if (letter) renderDetail(letter); } else { resetDetail(); } } function updateTabs() { [tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => { if (!btn) return; btn.classList.toggle("alpha-tab-active", btn.dataset.alpha === state.activeAlphabet); }); } function resetDetail() { if (detailNameEl) { detailNameEl.textContent = "--"; detailNameEl.classList.remove("alpha-detail-glyph"); } if (detailSubEl) detailSubEl.textContent = "Select a letter to explore"; if (detailBodyEl) detailBodyEl.innerHTML = ""; } // ── Public init ─────────────────────────────────────────────────────── function ensureAlphabetSection(magickDataset, referenceData = null) { const grouped = magickDataset?.grouped || {}; const alphabetData = (grouped["alphabets"] && grouped["alphabets"]["hebrew"]) ? grouped["alphabets"] : null; if (alphabetData) { state.alphabets = alphabetData; alphabetGematriaUi.refreshScriptMap?.(); } state.fourWorldLayers = buildFourWorldLayersFromDataset(magickDataset); state.cubeRefs = buildCubeReferences(magickDataset); if (referenceData && state.alphabets) { state.monthRefsByHebrewId = buildMonthReferencesByHebrew(referenceData, state.alphabets); } if (state.initialized) { ensureGematriaCalculator(); syncFilterControls(); renderList(); const letters = getFilteredLetters(); const selected = letters.find((entry) => letterKey(entry) === state.selectedKey) || letters[0]; if (selected) { renderDetail(selected); } else { resetDetail(); if (detailSubEl) { detailSubEl.textContent = "No letters match the current filter."; } } return; } state.initialized = true; // alphabets.json is a top-level file → grouped["alphabets"] = the data object getElements(); ensureGematriaCalculator(); bindFilterControls(); syncFilterControls(); if (!state.alphabets) { if (detailSubEl) detailSubEl.textContent = "Alphabet data not loaded."; return; } // Attach tab listeners [tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => { if (!btn) return; btn.addEventListener("click", () => { switchAlphabet(btn.dataset.alpha, null); }); }); switchAlphabet("all", null); } // ── Incoming navigation ─────────────────────────────────────────────── function selectLetterByHebrewId(hebrewLetterId) { switchAlphabet("hebrew", hebrewLetterId); } function selectGreekLetterByName(name) { switchAlphabet("greek", name); } function selectEnglishLetter(letter) { switchAlphabet("english", letter); } function selectArabicLetter(name) { switchAlphabet("arabic", name); } function selectEnochianLetter(id) { switchAlphabet("enochian", id); } window.AlphabetSectionUi = { ensureAlphabetSection, selectLetterByHebrewId, selectGreekLetterByName, selectEnglishLetter, selectArabicLetter, selectEnochianLetter }; })();