fixed linking
This commit is contained in:
@@ -3,12 +3,14 @@
|
||||
"use strict";
|
||||
|
||||
const alphabetGematriaUi = window.AlphabetGematriaUi || {};
|
||||
const alphabetBrowserUi = window.AlphabetBrowserUi || {};
|
||||
const alphabetKabbalahUi = window.AlphabetKabbalahUi || {};
|
||||
const alphabetReferenceBuilders = window.AlphabetReferenceBuilders || {};
|
||||
const alphabetDetailUi = window.AlphabetDetailUi || {};
|
||||
|
||||
if (
|
||||
typeof alphabetKabbalahUi.buildCubePlacementButton !== "function"
|
||||
typeof alphabetBrowserUi.createAlphabetBrowser !== "function"
|
||||
|| typeof alphabetKabbalahUi.buildCubePlacementButton !== "function"
|
||||
|| typeof alphabetKabbalahUi.buildFourWorldLayersFromDataset !== "function"
|
||||
|| typeof alphabetKabbalahUi.createEmptyCubeRefs !== "function"
|
||||
|| typeof alphabetKabbalahUi.getCubePlacementForHebrewLetter !== "function"
|
||||
@@ -18,7 +20,7 @@
|
||||
|| typeof alphabetKabbalahUi.normalizeLetterId !== "function"
|
||||
|| typeof alphabetKabbalahUi.titleCase !== "function"
|
||||
) {
|
||||
throw new Error("AlphabetKabbalahUi module must load before ui-alphabet.js");
|
||||
throw new Error("AlphabetBrowserUi and AlphabetKabbalahUi modules must load before ui-alphabet.js");
|
||||
}
|
||||
|
||||
const state = {
|
||||
@@ -40,18 +42,8 @@
|
||||
}
|
||||
};
|
||||
|
||||
// ── 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));
|
||||
return browserUi.arabicDisplayName(letter);
|
||||
}
|
||||
|
||||
// ── Element cache ────────────────────────────────────────────────────
|
||||
@@ -101,299 +93,84 @@
|
||||
|
||||
// ── 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] || [];
|
||||
return browserUi.getLetters();
|
||||
}
|
||||
|
||||
function alphabetForLetter(letter) {
|
||||
if (state.activeAlphabet === "all") {
|
||||
return String(letter?.__alphabet || "").trim().toLowerCase();
|
||||
}
|
||||
return state.activeAlphabet;
|
||||
return browserUi.alphabetForLetter(letter);
|
||||
}
|
||||
|
||||
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);
|
||||
return browserUi.letterKeyByAlphabet(alphabet, letter);
|
||||
}
|
||||
|
||||
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;
|
||||
return browserUi.letterKey(letter);
|
||||
}
|
||||
|
||||
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 "?";
|
||||
return browserUi.displayGlyph(letter);
|
||||
}
|
||||
|
||||
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 "?";
|
||||
return browserUi.displayLabel(letter);
|
||||
}
|
||||
|
||||
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 "";
|
||||
return browserUi.displaySub(letter);
|
||||
}
|
||||
|
||||
function normalizeLetterType(value) {
|
||||
const key = String(value || "").trim().toLowerCase();
|
||||
if (["mother", "double", "simple"].includes(key)) {
|
||||
return key;
|
||||
}
|
||||
return "";
|
||||
return browserUi.normalizeLetterType(value);
|
||||
}
|
||||
|
||||
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;
|
||||
return browserUi.getHebrewLetterTypeMap();
|
||||
}
|
||||
|
||||
function resolveLetterType(letter) {
|
||||
const direct = normalizeLetterType(letter?.letterType);
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
|
||||
const hebrewId = normalizeId(letter?.hebrewLetterId);
|
||||
if (!hebrewId) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return getHebrewLetterTypeMap().get(hebrewId) || "";
|
||||
return browserUi.resolveLetterType(letter);
|
||||
}
|
||||
|
||||
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();
|
||||
return browserUi.buildLetterSearchText(letter);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
return browserUi.getFilteredLetters();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
browserUi.syncFilterControls();
|
||||
}
|
||||
|
||||
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.";
|
||||
}
|
||||
browserUi.applyFiltersAndRender();
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
browserUi.bindFilterControls();
|
||||
}
|
||||
|
||||
function enochianGlyphKey(letter) {
|
||||
return String(letter?.id || letter?.char || "").trim().toUpperCase();
|
||||
return browserUi.enochianGlyphKey(letter);
|
||||
}
|
||||
|
||||
function enochianGlyphCode(letter) {
|
||||
const key = enochianGlyphKey(letter);
|
||||
return key ? key.codePointAt(0) || 0 : 0;
|
||||
return browserUi.enochianGlyphCode(letter);
|
||||
}
|
||||
|
||||
function enochianGlyphUrl(letter) {
|
||||
const code = enochianGlyphCode(letter);
|
||||
return code ? `asset/img/enochian/char(${code}).png` : "";
|
||||
return browserUi.enochianGlyphUrl(letter);
|
||||
}
|
||||
|
||||
function enochianGlyphImageHtml(letter, className) {
|
||||
const src = enochianGlyphUrl(letter);
|
||||
const key = enochianGlyphKey(letter) || "?";
|
||||
if (!src) {
|
||||
return `<span class="${className}">${key}</span>`;
|
||||
}
|
||||
return `<img class="${className}" src="${src}" alt="Enochian ${key}" loading="lazy" decoding="async">`;
|
||||
return browserUi.enochianGlyphImageHtml(letter, className);
|
||||
}
|
||||
|
||||
// ── 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 = `<strong>${alphaLabel}${displayLabel(letter)}</strong><br><span class="alpha-list-sub">${displaySub(letter)}</span>`;
|
||||
|
||||
item.appendChild(glyph);
|
||||
item.appendChild(meta);
|
||||
item.addEventListener("click", () => selectLetter(key));
|
||||
listEl.appendChild(item);
|
||||
});
|
||||
browserUi.renderList();
|
||||
}
|
||||
|
||||
// ── Detail rendering ──────────────────────────────────────────────────
|
||||
@@ -537,6 +314,28 @@
|
||||
};
|
||||
}
|
||||
|
||||
const browserUi = alphabetBrowserUi.createAlphabetBrowser({
|
||||
state,
|
||||
normalizeId,
|
||||
getDomRefs: () => ({
|
||||
listEl,
|
||||
countEl,
|
||||
detailNameEl,
|
||||
detailSubEl,
|
||||
detailBodyEl,
|
||||
tabAll,
|
||||
tabHebrew,
|
||||
tabGreek,
|
||||
tabEnglish,
|
||||
tabArabic,
|
||||
tabEnochian,
|
||||
searchInputEl,
|
||||
searchClearEl,
|
||||
typeFilterEl
|
||||
}),
|
||||
renderDetail
|
||||
});
|
||||
|
||||
// ── Event delegation on detail body ──────────────────────────────────
|
||||
function attachDetailListeners() {
|
||||
if (!detailBodyEl) return;
|
||||
@@ -568,43 +367,20 @@
|
||||
|
||||
// ── 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);
|
||||
browserUi.selectLetter(key);
|
||||
}
|
||||
|
||||
// ── 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();
|
||||
}
|
||||
browserUi.switchAlphabet(alpha, selectKey);
|
||||
}
|
||||
|
||||
function updateTabs() {
|
||||
[tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => {
|
||||
if (!btn) return;
|
||||
btn.classList.toggle("alpha-tab-active", btn.dataset.alpha === state.activeAlphabet);
|
||||
});
|
||||
browserUi.updateTabs();
|
||||
}
|
||||
|
||||
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 = "";
|
||||
browserUi.resetDetail();
|
||||
}
|
||||
|
||||
// ── Public init ───────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user