diff --git a/app/ui-alphabet-browser.js b/app/ui-alphabet-browser.js
new file mode 100644
index 0000000..d8100dc
--- /dev/null
+++ b/app/ui-alphabet-browser.js
@@ -0,0 +1,405 @@
+(function () {
+ "use strict";
+
+ const ARABIC_DISPLAY_NAMES = {
+ alif: "Alif", ba: "Ba", jeem: "Jeem", dal: "Dal", ha: "Ha",
+ waw: "Waw", zayn: "Zayn", ha_khaa: "Haa", ta_tay: "Tta", ya: "Ya",
+ kaf: "Kaf", lam: "Lam", meem: "Meem", nun: "Nun", seen: "Seen",
+ ayn: "Ayn", fa: "Fa", sad: "Sad", qaf: "Qaf", ra: "Ra",
+ sheen: "Sheen", ta: "Ta", tha: "Tha", kha: "Kha",
+ dhal: "Dhal", dad: "Dad", dha: "Dha", ghayn: "Ghayn"
+ };
+
+ function createAlphabetBrowser(dependencies) {
+ const {
+ state,
+ normalizeId,
+ getDomRefs,
+ renderDetail
+ } = dependencies || {};
+
+ function capitalize(value) {
+ return value ? value.charAt(0).toUpperCase() + value.slice(1) : "";
+ }
+
+ function arabicDisplayName(letter) {
+ return ARABIC_DISPLAY_NAMES[letter && letter.name]
+ || (String(letter && letter.name || "").charAt(0).toUpperCase() + String(letter && letter.name || "").slice(1));
+ }
+
+ 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) {
+ 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() {
+ const { searchInputEl, typeFilterEl, searchClearEl } = getDomRefs();
+
+ 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();
+ const { detailSubEl } = getDomRefs();
+ if (detailSubEl) {
+ detailSubEl.textContent = "No letters match the current filter.";
+ }
+ }
+
+ function bindFilterControls() {
+ const { searchInputEl, typeFilterEl, searchClearEl } = getDomRefs();
+
+ 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 `
`;
+ }
+
+ function renderList() {
+ const { listEl, countEl } = getDomRefs();
+ 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 ? `${capitalize(alphabet)} · ` : "";
+ meta.innerHTML = `${alphaLabel}${displayLabel(letter)}
${displaySub(letter)}`;
+
+ item.appendChild(glyph);
+ item.appendChild(meta);
+ item.addEventListener("click", () => selectLetter(key));
+ listEl.appendChild(item);
+ });
+ }
+
+ function resetDetail() {
+ const { detailNameEl, detailSubEl, detailBodyEl } = getDomRefs();
+ if (detailNameEl) {
+ detailNameEl.textContent = "--";
+ detailNameEl.classList.remove("alpha-detail-glyph");
+ }
+ if (detailSubEl) detailSubEl.textContent = "Select a letter to explore";
+ if (detailBodyEl) detailBodyEl.innerHTML = "";
+ }
+
+ function selectLetter(key) {
+ state.selectedKey = key;
+ renderList();
+ const letters = getFilteredLetters();
+ const letter = letters.find((entry) => letterKey(entry) === key) || getLetters().find((entry) => letterKey(entry) === key);
+ if (letter) {
+ renderDetail(letter);
+ }
+ }
+
+ function updateTabs() {
+ const { tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian } = getDomRefs();
+ [tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => {
+ if (!btn) return;
+ btn.classList.toggle("alpha-tab-active", btn.dataset.alpha === state.activeAlphabet);
+ });
+ }
+
+ function switchAlphabet(alpha, selectKey) {
+ state.activeAlphabet = alpha;
+ state.selectedKey = selectKey || null;
+ updateTabs();
+ syncFilterControls();
+ renderList();
+ if (selectKey) {
+ const letters = getFilteredLetters();
+ const letter = letters.find((entry) => letterKey(entry) === selectKey) || getLetters().find((entry) => letterKey(entry) === selectKey);
+ if (letter) {
+ renderDetail(letter);
+ }
+ } else {
+ resetDetail();
+ }
+ }
+
+ return {
+ arabicDisplayName,
+ getLetters,
+ alphabetForLetter,
+ letterKeyByAlphabet,
+ letterKey,
+ displayGlyph,
+ displayLabel,
+ displaySub,
+ normalizeLetterType,
+ getHebrewLetterTypeMap,
+ resolveLetterType,
+ buildLetterSearchText,
+ getFilteredLetters,
+ syncFilterControls,
+ applyFiltersAndRender,
+ bindFilterControls,
+ enochianGlyphKey,
+ enochianGlyphCode,
+ enochianGlyphUrl,
+ enochianGlyphImageHtml,
+ renderList,
+ resetDetail,
+ selectLetter,
+ switchAlphabet,
+ updateTabs
+ };
+ }
+
+ window.AlphabetBrowserUi = {
+ createAlphabetBrowser
+ };
+})();
\ No newline at end of file
diff --git a/app/ui-alphabet.js b/app/ui-alphabet.js
index e9d878f..813a506 100644
--- a/app/ui-alphabet.js
+++ b/app/ui-alphabet.js
@@ -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 `${key}`;
- }
- return `
`;
+ 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 = `${alphaLabel}${displayLabel(letter)}
${displaySub(letter)}`;
-
- 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 ───────────────────────────────────────────────────────
diff --git a/app/ui-cube-selection.js b/app/ui-cube-selection.js
new file mode 100644
index 0000000..62e114b
--- /dev/null
+++ b/app/ui-cube-selection.js
@@ -0,0 +1,277 @@
+(function () {
+ "use strict";
+
+ function createCubeSelectionHelpers(dependencies) {
+ const {
+ state,
+ normalizeId,
+ normalizeEdgeId,
+ normalizeLetterKey,
+ toFiniteNumber,
+ getWalls,
+ getWallById,
+ getEdges,
+ getEdgeById,
+ getEdgeWalls,
+ getEdgesForWall,
+ getEdgeLetterId,
+ getWallFaceLetterId,
+ getEdgePathEntry,
+ getConnectorById,
+ snapRotationToWall,
+ render,
+ getElements
+ } = dependencies || {};
+
+ function applyPlacement(placement) {
+ const fallbackWallId = normalizeId(getWalls()[0]?.id);
+ const nextWallId = normalizeId(placement?.wallId || placement?.wall?.id || state.selectedWallId || fallbackWallId);
+ const wall = getWallById(nextWallId);
+ if (!wall) {
+ return false;
+ }
+
+ state.selectedWallId = normalizeId(wall.id);
+
+ const candidateEdgeId = normalizeEdgeId(placement?.edgeId || placement?.edge?.id);
+ const wallEdges = getEdgesForWall(state.selectedWallId);
+ const resolvedEdgeId = candidateEdgeId && getEdgeById(candidateEdgeId)
+ ? candidateEdgeId
+ : normalizeEdgeId(wallEdges[0]?.id || getEdges()[0]?.id);
+
+ state.selectedEdgeId = resolvedEdgeId;
+ state.selectedNodeType = "wall";
+ state.selectedConnectorId = null;
+ render(getElements());
+ return true;
+ }
+
+ function selectEdgeById(edgeId, preferredWallId = "") {
+ const edge = getEdgeById(edgeId);
+ if (!edge) {
+ return false;
+ }
+
+ const currentWallId = normalizeId(state.selectedWallId);
+ const preferredId = normalizeId(preferredWallId);
+ const edgeWalls = getEdgeWalls(edge);
+ const nextWallId = preferredId && edgeWalls.includes(preferredId)
+ ? preferredId
+ : (edgeWalls.includes(currentWallId) ? currentWallId : (edgeWalls[0] || currentWallId));
+
+ state.selectedEdgeId = normalizeEdgeId(edge.id);
+ state.selectedNodeType = "wall";
+ state.selectedConnectorId = null;
+
+ if (nextWallId) {
+ if (nextWallId !== currentWallId) {
+ state.selectedWallId = nextWallId;
+ snapRotationToWall(nextWallId);
+ } else if (!state.selectedWallId) {
+ state.selectedWallId = nextWallId;
+ }
+ }
+
+ render(getElements());
+ return true;
+ }
+
+ function selectWallById(wallId) {
+ if (!state.initialized) {
+ return false;
+ }
+
+ const wall = getWallById(wallId);
+ if (!wall) {
+ return false;
+ }
+
+ state.selectedWallId = normalizeId(wall.id);
+ state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(state.selectedWallId)[0]?.id || getEdges()[0]?.id);
+ state.selectedNodeType = "wall";
+ state.selectedConnectorId = null;
+ snapRotationToWall(state.selectedWallId);
+ render(getElements());
+ return true;
+ }
+
+ function selectConnectorById(connectorId) {
+ if (!state.initialized) {
+ return false;
+ }
+
+ const connector = getConnectorById(connectorId);
+ if (!connector) {
+ return false;
+ }
+
+ const fromWallId = normalizeId(connector.fromWallId);
+ if (fromWallId && getWallById(fromWallId)) {
+ state.selectedWallId = fromWallId;
+ state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(fromWallId)[0]?.id || getEdges()[0]?.id);
+ snapRotationToWall(fromWallId);
+ }
+
+ state.showConnectorLines = true;
+ state.selectedNodeType = "connector";
+ state.selectedConnectorId = normalizeId(connector.id);
+ render(getElements());
+ return true;
+ }
+
+ function selectCenterNode() {
+ if (!state.initialized) {
+ return false;
+ }
+
+ state.showPrimalPoint = true;
+ state.selectedNodeType = "center";
+ state.selectedConnectorId = null;
+ render(getElements());
+ return true;
+ }
+
+ function selectPlacement(criteria = {}) {
+ if (!state.initialized) {
+ return false;
+ }
+
+ const wallId = normalizeId(criteria.wallId);
+ const connectorId = normalizeId(criteria.connectorId);
+ const edgeId = normalizeEdgeId(criteria.edgeId || criteria.directionId);
+ const hebrewLetterId = normalizeLetterKey(criteria.hebrewLetterId);
+ const signId = normalizeId(criteria.signId || criteria.zodiacSignId);
+ const planetId = normalizeId(criteria.planetId);
+ const pathNo = toFiniteNumber(criteria.pathNo || criteria.kabbalahPathNumber);
+ const trumpNo = toFiniteNumber(criteria.trumpNumber || criteria.tarotTrumpNumber);
+ const nodeType = normalizeId(criteria.nodeType);
+ const centerRequested = nodeType === "center"
+ || Boolean(criteria.center)
+ || Boolean(criteria.primalPoint)
+ || normalizeId(criteria.centerId) === "primal-point";
+ const edges = getEdges();
+
+ const findEdgeBy = (predicate) => edges.find((edge) => predicate(edge)) || null;
+
+ const findWallForEdge = (edge, preferredWallId) => {
+ const edgeWalls = getEdgeWalls(edge);
+ if (preferredWallId && edgeWalls.includes(preferredWallId)) {
+ return preferredWallId;
+ }
+ return edgeWalls[0] || normalizeId(getWalls()[0]?.id);
+ };
+
+ if (connectorId) {
+ return selectConnectorById(connectorId);
+ }
+
+ if (centerRequested) {
+ return selectCenterNode();
+ }
+
+ if (edgeId) {
+ const edge = getEdgeById(edgeId);
+ if (!edge) {
+ return false;
+ }
+ return applyPlacement({
+ wallId: findWallForEdge(edge, wallId),
+ edgeId
+ });
+ }
+
+ if (wallId) {
+ const wall = getWallById(wallId);
+ if (!wall) {
+ return false;
+ }
+
+ if (!edgeId) {
+ return selectWallById(wallId);
+ }
+
+ const firstEdge = getEdgesForWall(wallId)[0] || null;
+ return applyPlacement({ wallId, edgeId: firstEdge?.id });
+ }
+
+ if (hebrewLetterId) {
+ const byHebrew = findEdgeBy((edge) => getEdgeLetterId(edge) === hebrewLetterId);
+ if (byHebrew) {
+ return applyPlacement({
+ wallId: findWallForEdge(byHebrew),
+ edgeId: byHebrew.id
+ });
+ }
+
+ const byWallFace = getWalls().find((wall) => getWallFaceLetterId(wall) === hebrewLetterId) || null;
+ if (byWallFace) {
+ const byWallFaceId = normalizeId(byWallFace.id);
+ const firstEdge = getEdgesForWall(byWallFaceId)[0] || null;
+ return applyPlacement({ wallId: byWallFaceId, edgeId: firstEdge?.id });
+ }
+ }
+
+ if (signId) {
+ const bySign = findEdgeBy((edge) => {
+ const astrology = getEdgePathEntry(edge)?.astrology || {};
+ return normalizeId(astrology.type) === "zodiac" && normalizeId(astrology.name) === signId;
+ });
+ if (bySign) {
+ return applyPlacement({
+ wallId: findWallForEdge(bySign),
+ edgeId: bySign.id
+ });
+ }
+ }
+
+ if (pathNo != null) {
+ const byPath = findEdgeBy((edge) => toFiniteNumber(getEdgePathEntry(edge)?.pathNumber) === pathNo);
+ if (byPath) {
+ return applyPlacement({
+ wallId: findWallForEdge(byPath),
+ edgeId: byPath.id
+ });
+ }
+ }
+
+ if (trumpNo != null) {
+ const byTrump = findEdgeBy((edge) => {
+ const tarot = getEdgePathEntry(edge)?.tarot || {};
+ return toFiniteNumber(tarot.trumpNumber) === trumpNo;
+ });
+ if (byTrump) {
+ return applyPlacement({
+ wallId: findWallForEdge(byTrump),
+ edgeId: byTrump.id
+ });
+ }
+ }
+
+ if (planetId) {
+ const wall = getWalls().find((entry) => normalizeId(entry?.associations?.planetId) === planetId);
+ if (wall) {
+ const wallIdByPlanet = normalizeId(wall.id);
+ return applyPlacement({
+ wallId: wallIdByPlanet,
+ edgeId: getEdgesForWall(wallIdByPlanet)[0]?.id
+ });
+ }
+ }
+
+ return false;
+ }
+
+ return {
+ applyPlacement,
+ selectEdgeById,
+ selectWallById,
+ selectConnectorById,
+ selectCenterNode,
+ selectPlacement
+ };
+ }
+
+ window.CubeSelectionHelpers = {
+ createCubeSelectionHelpers
+ };
+})();
\ No newline at end of file
diff --git a/app/ui-cube.js b/app/ui-cube.js
index 3886a80..52ff254 100644
--- a/app/ui-cube.js
+++ b/app/ui-cube.js
@@ -122,6 +122,7 @@
const cubeDetailUi = window.CubeDetailUi || {};
const cubeChassisUi = window.CubeChassisUi || {};
const cubeMathHelpers = window.CubeMathHelpers || {};
+ const cubeSelectionHelpers = window.CubeSelectionHelpers || {};
function getElements() {
return {
@@ -256,6 +257,10 @@
throw new Error("CubeMathHelpers.createCubeMathHelpers is unavailable. Ensure app/ui-cube-math.js loads before app/ui-cube.js.");
}
+ if (typeof cubeSelectionHelpers.createCubeSelectionHelpers !== "function") {
+ throw new Error("CubeSelectionHelpers.createCubeSelectionHelpers is unavailable. Ensure app/ui-cube-selection.js loads before app/ui-cube.js.");
+ }
+
function normalizeAngle(angle) {
return cubeMathUi.normalizeAngle(angle);
}
@@ -432,6 +437,27 @@
getCubeCenterData
});
+ const cubeSelectionUi = cubeSelectionHelpers.createCubeSelectionHelpers({
+ state,
+ normalizeId,
+ normalizeEdgeId,
+ normalizeLetterKey,
+ toFiniteNumber,
+ getWalls,
+ getWallById,
+ getEdges,
+ getEdgeById,
+ getEdgeWalls,
+ getEdgesForWall,
+ getEdgeLetterId,
+ getWallFaceLetterId,
+ getEdgePathEntry,
+ getConnectorById,
+ snapRotationToWall,
+ render,
+ getElements
+ });
+
function getEdgeAstrologySymbol(edge) {
return cubeMathUi.getEdgeAstrologySymbol(edge);
}
@@ -488,26 +514,7 @@
}
function applyPlacement(placement) {
- const fallbackWallId = normalizeId(getWalls()[0]?.id);
- const nextWallId = normalizeId(placement?.wallId || placement?.wall?.id || state.selectedWallId || fallbackWallId);
- const wall = getWallById(nextWallId);
- if (!wall) {
- return false;
- }
-
- state.selectedWallId = normalizeId(wall.id);
-
- const candidateEdgeId = normalizeEdgeId(placement?.edgeId || placement?.edge?.id);
- const wallEdges = getEdgesForWall(state.selectedWallId);
- const resolvedEdgeId = candidateEdgeId && getEdgeById(candidateEdgeId)
- ? candidateEdgeId
- : normalizeEdgeId(wallEdges[0]?.id || getEdges()[0]?.id);
-
- state.selectedEdgeId = resolvedEdgeId;
- state.selectedNodeType = "wall";
- state.selectedConnectorId = null;
- render(getElements());
- return true;
+ return cubeSelectionUi.applyPlacement(placement);
}
function toDisplayText(value) {
@@ -559,33 +566,7 @@
}
function selectEdgeById(edgeId, preferredWallId = "") {
- const edge = getEdgeById(edgeId);
- if (!edge) {
- return false;
- }
-
- const currentWallId = normalizeId(state.selectedWallId);
- const preferredId = normalizeId(preferredWallId);
- const edgeWalls = getEdgeWalls(edge);
- const nextWallId = preferredId && edgeWalls.includes(preferredId)
- ? preferredId
- : (edgeWalls.includes(currentWallId) ? currentWallId : (edgeWalls[0] || currentWallId));
-
- state.selectedEdgeId = normalizeEdgeId(edge.id);
- state.selectedNodeType = "wall";
- state.selectedConnectorId = null;
-
- if (nextWallId) {
- if (nextWallId !== currentWallId) {
- state.selectedWallId = nextWallId;
- snapRotationToWall(nextWallId);
- } else if (!state.selectedWallId) {
- state.selectedWallId = nextWallId;
- }
- }
-
- render(getElements());
- return true;
+ return cubeSelectionUi.selectEdgeById(edgeId, preferredWallId);
}
function renderDetail(elements, walls) {
@@ -710,195 +691,19 @@
}
function selectWallById(wallId) {
- if (!state.initialized) {
- return false;
- }
-
- const wall = getWallById(wallId);
- if (!wall) {
- return false;
- }
-
- state.selectedWallId = normalizeId(wall.id);
- state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(state.selectedWallId)[0]?.id || getEdges()[0]?.id);
- state.selectedNodeType = "wall";
- state.selectedConnectorId = null;
- snapRotationToWall(state.selectedWallId);
- render(getElements());
- return true;
+ return cubeSelectionUi.selectWallById(wallId);
}
function selectConnectorById(connectorId) {
- if (!state.initialized) {
- return false;
- }
-
- const connector = getConnectorById(connectorId);
- if (!connector) {
- return false;
- }
-
- const fromWallId = normalizeId(connector.fromWallId);
- if (fromWallId && getWallById(fromWallId)) {
- state.selectedWallId = fromWallId;
- state.selectedEdgeId = normalizeEdgeId(getEdgesForWall(fromWallId)[0]?.id || getEdges()[0]?.id);
- snapRotationToWall(fromWallId);
- }
-
- state.showConnectorLines = true;
- state.selectedNodeType = "connector";
- state.selectedConnectorId = normalizeId(connector.id);
- render(getElements());
- return true;
+ return cubeSelectionUi.selectConnectorById(connectorId);
}
function selectCenterNode() {
- if (!state.initialized) {
- return false;
- }
-
- state.showPrimalPoint = true;
- state.selectedNodeType = "center";
- state.selectedConnectorId = null;
- render(getElements());
- return true;
+ return cubeSelectionUi.selectCenterNode();
}
function selectPlacement(criteria = {}) {
- if (!state.initialized) {
- return false;
- }
-
- const wallId = normalizeId(criteria.wallId);
- const connectorId = normalizeId(criteria.connectorId);
- const edgeId = normalizeEdgeId(criteria.edgeId || criteria.directionId);
- const hebrewLetterId = normalizeLetterKey(criteria.hebrewLetterId);
- const signId = normalizeId(criteria.signId || criteria.zodiacSignId);
- const planetId = normalizeId(criteria.planetId);
- const pathNo = toFiniteNumber(criteria.pathNo || criteria.kabbalahPathNumber);
- const trumpNo = toFiniteNumber(criteria.trumpNumber || criteria.tarotTrumpNumber);
- const nodeType = normalizeId(criteria.nodeType);
- const centerRequested = nodeType === "center"
- || Boolean(criteria.center)
- || Boolean(criteria.primalPoint)
- || normalizeId(criteria.centerId) === "primal-point";
- const edges = getEdges();
-
- const findEdgeBy = (predicate) => edges.find((edge) => predicate(edge)) || null;
-
- const findWallForEdge = (edge, preferredWallId) => {
- const edgeWalls = getEdgeWalls(edge);
- if (preferredWallId && edgeWalls.includes(preferredWallId)) {
- return preferredWallId;
- }
- return edgeWalls[0] || normalizeId(getWalls()[0]?.id);
- };
-
- if (connectorId) {
- return selectConnectorById(connectorId);
- }
-
- if (centerRequested) {
- return selectCenterNode();
- }
-
- if (edgeId) {
- const edge = getEdgeById(edgeId);
- if (!edge) {
- return false;
- }
- return applyPlacement({
- wallId: findWallForEdge(edge, wallId),
- edgeId
- });
- }
-
- if (wallId) {
- const wall = getWallById(wallId);
- if (!wall) {
- return false;
- }
-
- // if an explicit edge id was not provided (or was empty) we treat this
- // as a request to show the wall/face itself rather than any particular
- // edge direction. `applyPlacement` only knows how to highlight edges,
- // so we fall back to selecting the wall directly in that case. this
- // is the behaviour we want when navigating from a "face" letter like
- // dalet, where the placement computed by ui-alphabet leaves edgeId
- // blank.
- if (!edgeId) {
- return selectWallById(wallId);
- }
-
- const firstEdge = getEdgesForWall(wallId)[0] || null;
- return applyPlacement({ wallId, edgeId: firstEdge?.id });
- }
-
- if (hebrewLetterId) {
- const byHebrew = findEdgeBy((edge) => getEdgeLetterId(edge) === hebrewLetterId);
- if (byHebrew) {
- return applyPlacement({
- wallId: findWallForEdge(byHebrew),
- edgeId: byHebrew.id
- });
- }
-
- const byWallFace = getWalls().find((wall) => getWallFaceLetterId(wall) === hebrewLetterId) || null;
- if (byWallFace) {
- const byWallFaceId = normalizeId(byWallFace.id);
- const firstEdge = getEdgesForWall(byWallFaceId)[0] || null;
- return applyPlacement({ wallId: byWallFaceId, edgeId: firstEdge?.id });
- }
- }
-
- if (signId) {
- const bySign = findEdgeBy((edge) => {
- const astrology = getEdgePathEntry(edge)?.astrology || {};
- return normalizeId(astrology.type) === "zodiac" && normalizeId(astrology.name) === signId;
- });
- if (bySign) {
- return applyPlacement({
- wallId: findWallForEdge(bySign),
- edgeId: bySign.id
- });
- }
- }
-
- if (pathNo != null) {
- const byPath = findEdgeBy((edge) => toFiniteNumber(getEdgePathEntry(edge)?.pathNumber) === pathNo);
- if (byPath) {
- return applyPlacement({
- wallId: findWallForEdge(byPath),
- edgeId: byPath.id
- });
- }
- }
-
- if (trumpNo != null) {
- const byTrump = findEdgeBy((edge) => {
- const tarot = getEdgePathEntry(edge)?.tarot || {};
- return toFiniteNumber(tarot.trumpNumber) === trumpNo;
- });
- if (byTrump) {
- return applyPlacement({
- wallId: findWallForEdge(byTrump),
- edgeId: byTrump.id
- });
- }
- }
-
- if (planetId) {
- const wall = getWalls().find((entry) => normalizeId(entry?.associations?.planetId) === planetId);
- if (wall) {
- const wallIdByPlanet = normalizeId(wall.id);
- return applyPlacement({
- wallId: wallIdByPlanet,
- edgeId: getEdgesForWall(wallIdByPlanet)[0]?.id
- });
- }
- }
-
- return false;
+ return cubeSelectionUi.selectPlacement(criteria);
}
function selectByHebrewLetterId(hebrewLetterId) {
diff --git a/app/ui-now-helpers.js b/app/ui-now-helpers.js
index 6e42e7d..2facf1c 100644
--- a/app/ui-now-helpers.js
+++ b/app/ui-now-helpers.js
@@ -509,6 +509,7 @@
}
window.NowUiHelpers = {
+ getSignStartDate,
findNextDecanTransition,
findNextMoonPhaseTransition,
formatCountdown,
diff --git a/app/ui-now.js b/app/ui-now.js
index 7070bff..bbab26d 100644
--- a/app/ui-now.js
+++ b/app/ui-now.js
@@ -4,6 +4,7 @@
const {
DAY_IN_MS,
getDateKey,
+ getDecanForDate,
getMoonPhaseName,
calcPlanetaryHoursForDayAndLocation
} = window.TarotCalc;
@@ -13,6 +14,7 @@
typeof nowUiHelpers.findNextDecanTransition !== "function"
|| typeof nowUiHelpers.findNextMoonPhaseTransition !== "function"
|| typeof nowUiHelpers.formatCountdown !== "function"
+ || typeof nowUiHelpers.getSignStartDate !== "function"
|| typeof nowUiHelpers.getDisplayTarotName !== "function"
|| typeof nowUiHelpers.setNowCardImage !== "function"
|| typeof nowUiHelpers.updateNowStats !== "function"
@@ -128,7 +130,7 @@
? `${sunInfo.sign.id}-${sunInfo.decan?.index || 1}`
: "no-decan";
if (sunInfo?.sign) {
- const signStartDate = getSignStartDate(now, sunInfo.sign);
+ const signStartDate = nowUiHelpers.getSignStartDate(now, sunInfo.sign);
const daysSinceSignStart = (now.getTime() - signStartDate.getTime()) / DAY_IN_MS;
const signDegree = Math.min(29.9, Math.max(0, daysSinceSignStart));
const signMajorName = nowUiHelpers.getDisplayTarotName(sunInfo.sign.tarot.majorArcana, sunInfo.sign.tarot.trumpNumber);
diff --git a/app/ui-tarot.js b/app/ui-tarot.js
index d392c66..cf754e5 100644
--- a/app/ui-tarot.js
+++ b/app/ui-tarot.js
@@ -188,6 +188,19 @@
}
};
+ const MINOR_PLURAL_BY_RANK = {
+ ace: "aces",
+ two: "twos",
+ three: "threes",
+ four: "fours",
+ five: "fives",
+ six: "sixes",
+ seven: "sevens",
+ eight: "eights",
+ nine: "nines",
+ ten: "tens"
+ };
+
function slugify(value) {
return String(value || "")
.trim()
@@ -473,19 +486,6 @@
return tarotCardDerivationsUi.buildTypeLabel(card);
}
- const MINOR_PLURAL_BY_RANK = {
- ace: "aces",
- two: "twos",
- three: "threes",
- four: "fours",
- five: "fives",
- six: "sixes",
- seven: "sevens",
- eight: "eights",
- nine: "nines",
- ten: "tens"
- };
-
function findSephirahForMinorCard(card, kabTree) {
return tarotCardDerivationsUi.findSephirahForMinorCard(card, kabTree);
}
diff --git a/index.html b/index.html
index 5aa3fb6..5283302 100644
--- a/index.html
+++ b/index.html
@@ -804,8 +804,10 @@
+
+