update app and index.html
This commit is contained in:
@@ -278,6 +278,8 @@ appRuntime.init?.({
|
||||
calendarVisualsUi,
|
||||
homeUi,
|
||||
hasTarotAccess: () => hasTarotFeatureAccess(),
|
||||
shouldPollNow: () => (sectionStateUi.getActiveSection?.() || "home") === "home" && document.hidden !== true,
|
||||
nowPollIntervalMs: 5 * 60 * 1000,
|
||||
onStatus: (text) => setStatus(text),
|
||||
services: {
|
||||
getCenteredWeekStartDay,
|
||||
|
||||
+45
-4
@@ -12,6 +12,8 @@
|
||||
homeUi: null,
|
||||
onStatus: null,
|
||||
hasTarotAccess: () => false,
|
||||
shouldPollNow: () => true,
|
||||
nowPollIntervalMs: 5 * 60 * 1000,
|
||||
services: {},
|
||||
ensure: {}
|
||||
};
|
||||
@@ -20,6 +22,7 @@
|
||||
let magickDataset = null;
|
||||
let currentGeo = null;
|
||||
let nowInterval = null;
|
||||
let runtimeListenersBound = false;
|
||||
let centeredDayKey = "";
|
||||
let renderInProgress = false;
|
||||
let currentTimeFormat = "minutes";
|
||||
@@ -74,11 +77,17 @@
|
||||
}
|
||||
|
||||
function startNowTicker() {
|
||||
if (nowInterval) {
|
||||
clearInterval(nowInterval);
|
||||
}
|
||||
stopNowTicker();
|
||||
|
||||
const pollIntervalMs = Number.isFinite(Number(config.nowPollIntervalMs))
|
||||
? Math.max(1000, Math.trunc(Number(config.nowPollIntervalMs)))
|
||||
: 5 * 60 * 1000;
|
||||
|
||||
const tick = async () => {
|
||||
if (config.shouldPollNow?.() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!referenceData || !currentGeo || renderInProgress) {
|
||||
return;
|
||||
}
|
||||
@@ -102,7 +111,27 @@
|
||||
void tick();
|
||||
nowInterval = setInterval(() => {
|
||||
void tick();
|
||||
}, 1000);
|
||||
}, pollIntervalMs);
|
||||
}
|
||||
|
||||
function stopNowTicker() {
|
||||
if (!nowInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(nowInterval);
|
||||
nowInterval = null;
|
||||
}
|
||||
|
||||
function syncNowTickerState() {
|
||||
if (config.shouldPollNow?.() === false) {
|
||||
stopNowTicker();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nowInterval && referenceData && currentGeo) {
|
||||
startNowTicker();
|
||||
}
|
||||
}
|
||||
|
||||
async function renderWeek() {
|
||||
@@ -194,6 +223,18 @@
|
||||
}
|
||||
|
||||
centeredDayKey = config.services.getDateKey?.(new Date()) || centeredDayKey;
|
||||
|
||||
if (!runtimeListenersBound) {
|
||||
document.addEventListener("section:changed", () => {
|
||||
syncNowTickerState();
|
||||
});
|
||||
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
syncNowTickerState();
|
||||
});
|
||||
|
||||
runtimeListenersBound = true;
|
||||
}
|
||||
}
|
||||
|
||||
window.TarotAppRuntime = {
|
||||
|
||||
+84
-2
@@ -11,6 +11,14 @@
|
||||
const textLexiconCache = new Map();
|
||||
const textLexiconOccurrencesCache = new Map();
|
||||
const textSearchCache = new Map();
|
||||
const NOW_SNAPSHOT_MIN_INTERVAL_MS = 5 * 60 * 1000;
|
||||
let nowSnapshotCache = {
|
||||
geoKey: "",
|
||||
fetchedAtMs: 0,
|
||||
value: null,
|
||||
pendingGeoKey: "",
|
||||
pendingPromise: null
|
||||
};
|
||||
|
||||
const DATA_ROOT = "data";
|
||||
const MAGICK_ROOT = DATA_ROOT;
|
||||
@@ -215,6 +223,26 @@
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
function normalizeGeoKey(geo) {
|
||||
const latitude = Number(geo?.latitude);
|
||||
const longitude = Number(geo?.longitude);
|
||||
|
||||
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return `${latitude.toFixed(6)},${longitude.toFixed(6)}`;
|
||||
}
|
||||
|
||||
function isNowSnapshotPollingEnabled() {
|
||||
if (typeof document !== "undefined" && document.hidden === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const activeSection = String(window.TarotSectionStateUi?.getActiveSection?.() || "home").trim();
|
||||
return activeSection === "home";
|
||||
}
|
||||
|
||||
function toApiAssetUrl(assetPath) {
|
||||
const { apiBaseUrl, apiKey } = resolveConnectionSettings();
|
||||
const normalizedAssetPath = String(assetPath || "")
|
||||
@@ -247,6 +275,13 @@
|
||||
textLexiconCache.clear();
|
||||
textLexiconOccurrencesCache.clear();
|
||||
textSearchCache.clear();
|
||||
nowSnapshotCache = {
|
||||
geoKey: "",
|
||||
fetchedAtMs: 0,
|
||||
value: null,
|
||||
pendingGeoKey: "",
|
||||
pendingPromise: null
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeTarotName(value) {
|
||||
@@ -370,11 +405,58 @@
|
||||
}
|
||||
|
||||
async function fetchNowSnapshot(geo, timestamp = new Date()) {
|
||||
return fetchJson(buildApiUrl("/api/v1/now", {
|
||||
const geoKey = normalizeGeoKey(geo);
|
||||
const nowMs = Date.now();
|
||||
|
||||
if (!isNowSnapshotPollingEnabled()) {
|
||||
if (geoKey && nowSnapshotCache.pendingPromise && nowSnapshotCache.pendingGeoKey === geoKey) {
|
||||
return nowSnapshotCache.pendingPromise;
|
||||
}
|
||||
|
||||
if (geoKey && geoKey === nowSnapshotCache.geoKey && nowSnapshotCache.value) {
|
||||
return nowSnapshotCache.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
geoKey
|
||||
&& geoKey === nowSnapshotCache.geoKey
|
||||
&& nowSnapshotCache.value
|
||||
&& Number.isFinite(nowSnapshotCache.fetchedAtMs)
|
||||
&& (nowMs - nowSnapshotCache.fetchedAtMs) < NOW_SNAPSHOT_MIN_INTERVAL_MS
|
||||
) {
|
||||
return nowSnapshotCache.value;
|
||||
}
|
||||
|
||||
if (geoKey && nowSnapshotCache.pendingPromise && nowSnapshotCache.pendingGeoKey === geoKey) {
|
||||
return nowSnapshotCache.pendingPromise;
|
||||
}
|
||||
|
||||
const requestPromise = fetchJson(buildApiUrl("/api/v1/now", {
|
||||
latitude: geo?.latitude,
|
||||
longitude: geo?.longitude,
|
||||
date: timestamp instanceof Date ? timestamp.toISOString() : timestamp
|
||||
}));
|
||||
}))
|
||||
.then((snapshot) => {
|
||||
if (geoKey) {
|
||||
nowSnapshotCache.geoKey = geoKey;
|
||||
nowSnapshotCache.fetchedAtMs = Date.now();
|
||||
nowSnapshotCache.value = snapshot;
|
||||
}
|
||||
return snapshot;
|
||||
})
|
||||
.finally(() => {
|
||||
if (nowSnapshotCache.pendingPromise === requestPromise) {
|
||||
nowSnapshotCache.pendingPromise = null;
|
||||
nowSnapshotCache.pendingGeoKey = "";
|
||||
}
|
||||
});
|
||||
|
||||
nowSnapshotCache.pendingPromise = requestPromise;
|
||||
nowSnapshotCache.pendingGeoKey = geoKey;
|
||||
return requestPromise;
|
||||
}
|
||||
|
||||
async function loadTarotCards(filters = {}) {
|
||||
|
||||
+203
-6
@@ -10,6 +10,91 @@
|
||||
dhal: "Dhal", dad: "Dad", dha: "Dha", ghayn: "Ghayn"
|
||||
};
|
||||
|
||||
const GREEK_NATIVE_NAMES = {
|
||||
alpha: { classical: "ἄλφα", koine: "άλφα" },
|
||||
beta: { classical: "βῆτα", koine: "βήτα" },
|
||||
gamma: { classical: "γάμμα", koine: "γάμμα" },
|
||||
delta: { classical: "δέλτα", koine: "δέλτα" },
|
||||
epsilon: { classical: "ἒ ψιλόν", koine: "έψιλον" },
|
||||
zeta: { classical: "ζῆτα", koine: "ζήτα" },
|
||||
eta: { classical: "ἦτα", koine: "ήτα" },
|
||||
theta: { classical: "θῆτα", koine: "θήτα" },
|
||||
iota: { classical: "ἰῶτα", koine: "ιώτα" },
|
||||
kappa: { classical: "κάππα", koine: "κάππα" },
|
||||
lambda: { classical: "λάμβδα", koine: "λάμδα" },
|
||||
mu: { classical: "μῦ", koine: "μι" },
|
||||
nu: { classical: "νῦ", koine: "νι" },
|
||||
xi: { classical: "ξῖ", koine: "ξι" },
|
||||
omicron: { classical: "ὂ μικρόν", koine: "όμικρον" },
|
||||
pi: { classical: "πῖ", koine: "πι" },
|
||||
rho: { classical: "ῥῶ", koine: "ρω" },
|
||||
sigma: { classical: "σῖγμα", koine: "σίγμα" },
|
||||
tau: { classical: "ταῦ", koine: "ταυ" },
|
||||
upsilon: { classical: "ὖ ψιλόν", koine: "ύψιλον" },
|
||||
phi: { classical: "φῖ", koine: "φι" },
|
||||
chi: { classical: "χῖ", koine: "χι" },
|
||||
psi: { classical: "ψῖ", koine: "ψι" },
|
||||
omega: { classical: "ὦ μέγα", koine: "ωμέγα" },
|
||||
digamma: { classical: "δίγαμμα", koine: "δίγαμμα" },
|
||||
qoppa: { classical: "κόππα", koine: "κόππα" },
|
||||
sampi: { classical: "σαμπί", koine: "σαμπί" }
|
||||
};
|
||||
|
||||
const GREEK_TRANSLITERATIONS = {
|
||||
alpha: { classical: "A", koine: "A" },
|
||||
beta: { classical: "B", koine: "V" },
|
||||
gamma: { classical: "G", koine: "G" },
|
||||
delta: { classical: "D", koine: "Th" },
|
||||
epsilon: { classical: "E", koine: "E" },
|
||||
zeta: { classical: "Z", koine: "Z" },
|
||||
eta: { classical: "E", koine: "I" },
|
||||
theta: { classical: "Th", koine: "Th" },
|
||||
iota: { classical: "I", koine: "I" },
|
||||
kappa: { classical: "K", koine: "K" },
|
||||
lambda: { classical: "L", koine: "L" },
|
||||
mu: { classical: "M", koine: "M" },
|
||||
nu: { classical: "N", koine: "N" },
|
||||
xi: { classical: "X", koine: "X" },
|
||||
omicron: { classical: "O", koine: "O" },
|
||||
pi: { classical: "P", koine: "P" },
|
||||
rho: { classical: "R", koine: "R" },
|
||||
sigma: { classical: "S", koine: "S" },
|
||||
tau: { classical: "T", koine: "T" },
|
||||
upsilon: { classical: "U/Y", koine: "I" },
|
||||
phi: { classical: "Ph", koine: "F" },
|
||||
chi: { classical: "Kh/Ch", koine: "Ch" },
|
||||
psi: { classical: "Ps", koine: "Ps" },
|
||||
omega: { classical: "O", koine: "O" },
|
||||
digamma: { classical: "W", koine: "V" },
|
||||
qoppa: { classical: "Q", koine: "Q" },
|
||||
sampi: { classical: "Ss/Ts", koine: "Ss/Ts" }
|
||||
};
|
||||
|
||||
const HEBREW_NATIVE_NAMES = {
|
||||
alef: "אלף",
|
||||
bet: "בית",
|
||||
gimel: "גימל",
|
||||
dalet: "דלת",
|
||||
he: "הא",
|
||||
vav: "וו",
|
||||
zayin: "זין",
|
||||
het: "חית",
|
||||
tet: "טית",
|
||||
yod: "יוד",
|
||||
kaf: "כף",
|
||||
lamed: "למד",
|
||||
mem: "מם",
|
||||
nun: "נון",
|
||||
samekh: "סמך",
|
||||
ayin: "עין",
|
||||
pe: "פה",
|
||||
tsadi: "צדי",
|
||||
qof: "קוף",
|
||||
resh: "ריש",
|
||||
shin: "שין",
|
||||
tav: "תו"
|
||||
};
|
||||
|
||||
function createAlphabetBrowser(dependencies) {
|
||||
const {
|
||||
state,
|
||||
@@ -22,15 +107,109 @@
|
||||
return value ? value.charAt(0).toUpperCase() + value.slice(1) : "";
|
||||
}
|
||||
|
||||
function alphabetDisplayLabel(alphabet) {
|
||||
if (alphabet === "greek") return "Greek (Classical/Koine)";
|
||||
if (alphabet === "greekArchaic") return "Greek (Archaic)";
|
||||
return capitalize(alphabet);
|
||||
}
|
||||
|
||||
function arabicDisplayName(letter) {
|
||||
return ARABIC_DISPLAY_NAMES[letter && letter.name]
|
||||
|| (String(letter && letter.name || "").charAt(0).toUpperCase() + String(letter && letter.name || "").slice(1));
|
||||
}
|
||||
|
||||
function greekNativeNames(letter) {
|
||||
const key = String(letter?.name || "").trim().toLowerCase();
|
||||
const names = GREEK_NATIVE_NAMES[key];
|
||||
if (!names) {
|
||||
return { classical: "", koine: "" };
|
||||
}
|
||||
|
||||
return {
|
||||
classical: String(names.classical || "").trim(),
|
||||
koine: String(names.koine || "").trim()
|
||||
};
|
||||
}
|
||||
|
||||
function toGreekUppercase(value) {
|
||||
return String(value || "")
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
function formatGreekNativeNames(letter, options = {}) {
|
||||
const useUppercase = options?.uppercase === true;
|
||||
const names = greekNativeNames(letter);
|
||||
const classicalName = useUppercase ? toGreekUppercase(names.classical) : names.classical;
|
||||
const koineName = useUppercase ? toGreekUppercase(names.koine) : names.koine;
|
||||
if (classicalName && koineName && classicalName !== koineName) {
|
||||
return `${classicalName} / ${koineName}`;
|
||||
}
|
||||
|
||||
return classicalName || koineName || "";
|
||||
}
|
||||
|
||||
function hebrewNativeName(letter) {
|
||||
const key = String(letter?.hebrewLetterId || "").trim().toLowerCase();
|
||||
return HEBREW_NATIVE_NAMES[key] || "";
|
||||
}
|
||||
|
||||
function greekTransliterationVariants(letter) {
|
||||
const key = String(letter?.name || "").trim().toLowerCase();
|
||||
const variants = GREEK_TRANSLITERATIONS[key];
|
||||
if (!variants) {
|
||||
const fallback = String(letter?.transliteration || "").trim();
|
||||
return {
|
||||
classical: fallback,
|
||||
koine: fallback
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
classical: String(variants.classical || "").trim(),
|
||||
koine: String(variants.koine || "").trim()
|
||||
};
|
||||
}
|
||||
|
||||
function formatGreekTransliteration(letter) {
|
||||
const variants = greekTransliterationVariants(letter);
|
||||
if (variants.classical && variants.koine && variants.classical !== variants.koine) {
|
||||
return `${variants.classical} / ${variants.koine}`;
|
||||
}
|
||||
return variants.classical || variants.koine || String(letter?.transliteration || "").trim();
|
||||
}
|
||||
|
||||
function greekPlaceValueByIndex(index, alphabet) {
|
||||
const value = Number(index);
|
||||
if (!Number.isFinite(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const position = Math.trunc(value);
|
||||
if (position <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (alphabet === "greekArchaic") {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (position <= 9) {
|
||||
return position;
|
||||
}
|
||||
|
||||
if (position <= 18) {
|
||||
return (position - 9) * 10;
|
||||
}
|
||||
|
||||
return (position - 18) * 100;
|
||||
}
|
||||
|
||||
function getLetters() {
|
||||
if (!state.alphabets) return [];
|
||||
if (state.activeAlphabet === "all") {
|
||||
const alphabetOrder = ["hebrew", "greek", "english", "arabic", "enochian"];
|
||||
const alphabetOrder = ["hebrew", "greek", "greekArchaic", "english", "arabic", "enochian"];
|
||||
return alphabetOrder.flatMap((alphabet) => {
|
||||
const rows = Array.isArray(state.alphabets?.[alphabet]) ? state.alphabets[alphabet] : [];
|
||||
return rows.map((row) => ({ ...row, __alphabet: alphabet }));
|
||||
@@ -49,6 +228,7 @@
|
||||
function letterKeyByAlphabet(alphabet, letter) {
|
||||
if (alphabet === "hebrew") return letter.hebrewLetterId;
|
||||
if (alphabet === "greek") return letter.name;
|
||||
if (alphabet === "greekArchaic") return letter.name;
|
||||
if (alphabet === "english") return letter.letter;
|
||||
if (alphabet === "arabic") return letter.name;
|
||||
if (alphabet === "enochian") return letter.id;
|
||||
@@ -68,6 +248,7 @@
|
||||
const alphabet = alphabetForLetter(letter);
|
||||
if (alphabet === "hebrew") return letter.char;
|
||||
if (alphabet === "greek") return letter.char;
|
||||
if (alphabet === "greekArchaic") return letter.char;
|
||||
if (alphabet === "english") return letter.letter;
|
||||
if (alphabet === "arabic") return letter.char;
|
||||
if (alphabet === "enochian") return letter.char;
|
||||
@@ -78,6 +259,7 @@
|
||||
const alphabet = alphabetForLetter(letter);
|
||||
if (alphabet === "hebrew") return letter.name;
|
||||
if (alphabet === "greek") return letter.displayName;
|
||||
if (alphabet === "greekArchaic") return letter.displayName;
|
||||
if (alphabet === "english") return letter.letter;
|
||||
if (alphabet === "arabic") return arabicDisplayName(letter);
|
||||
if (alphabet === "enochian") return letter.title;
|
||||
@@ -86,8 +268,23 @@
|
||||
|
||||
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 === "hebrew") {
|
||||
const nativeName = hebrewNativeName(letter);
|
||||
return `${nativeName ? `${nativeName} · ` : ""}${letter.transliteration} · ${letter.letterType} · ${letter.numerology}`;
|
||||
}
|
||||
if (alphabet === "greek") {
|
||||
const nativeName = formatGreekNativeNames(letter, { uppercase: true });
|
||||
const transliteration = formatGreekTransliteration(letter);
|
||||
const orderlyValue = Number.isFinite(Number(letter?.numerology)) ? Math.trunc(Number(letter.numerology)) : "—";
|
||||
const placeValue = greekPlaceValueByIndex(letter?.index, alphabet);
|
||||
return `${nativeName ? `${nativeName} · ` : ""}${transliteration} · isopsephy orderly ${orderlyValue}${Number.isFinite(placeValue) ? ` · 1-10-100 ${placeValue}` : ""}${letter.archaic ? " · archaic" : ""}`;
|
||||
}
|
||||
if (alphabet === "greekArchaic") {
|
||||
const nativeName = formatGreekNativeNames(letter, { uppercase: true });
|
||||
const transliteration = formatGreekTransliteration(letter);
|
||||
const orderlyValue = Number.isFinite(Number(letter?.numerology)) ? Math.trunc(Number(letter.numerology)) : "—";
|
||||
return `${nativeName ? `${nativeName} · ` : ""}${transliteration} · isopsephy orderly ${orderlyValue} · 1-10-100 ${orderlyValue} · 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}`;
|
||||
@@ -315,7 +512,7 @@
|
||||
|
||||
const meta = document.createElement("span");
|
||||
meta.className = "alpha-list-meta";
|
||||
const alphaLabel = alphabet ? `${capitalize(alphabet)} · ` : "";
|
||||
const alphaLabel = alphabet ? `${alphabetDisplayLabel(alphabet)} · ` : "";
|
||||
meta.innerHTML = `<strong>${alphaLabel}${displayLabel(letter)}</strong><br><span class="alpha-list-sub">${displaySub(letter)}</span>`;
|
||||
|
||||
item.appendChild(glyph);
|
||||
@@ -346,8 +543,8 @@
|
||||
}
|
||||
|
||||
function updateTabs() {
|
||||
const { tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian } = getDomRefs();
|
||||
[tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => {
|
||||
const { tabAll, tabHebrew, tabGreek, tabGreekArchaic, tabEnglish, tabArabic, tabEnochian } = getDomRefs();
|
||||
[tabAll, tabHebrew, tabGreek, tabGreekArchaic, tabEnglish, tabArabic, tabEnochian].forEach((btn) => {
|
||||
if (!btn) return;
|
||||
btn.classList.toggle("alpha-tab-active", btn.dataset.alpha === state.activeAlphabet);
|
||||
});
|
||||
|
||||
+382
-15
@@ -215,6 +215,329 @@
|
||||
.replace(/[^A-Z]/g, "");
|
||||
}
|
||||
|
||||
const GREEK_NATIVE_NAMES = {
|
||||
alpha: { classical: "ἄλφα", koine: "άλφα" },
|
||||
beta: { classical: "βῆτα", koine: "βήτα" },
|
||||
gamma: { classical: "γάμμα", koine: "γάμμα" },
|
||||
delta: { classical: "δέλτα", koine: "δέλτα" },
|
||||
epsilon: { classical: "ἒ ψιλόν", koine: "έψιλον" },
|
||||
zeta: { classical: "ζῆτα", koine: "ζήτα" },
|
||||
eta: { classical: "ἦτα", koine: "ήτα" },
|
||||
theta: { classical: "θῆτα", koine: "θήτα" },
|
||||
iota: { classical: "ἰῶτα", koine: "ιώτα" },
|
||||
kappa: { classical: "κάππα", koine: "κάππα" },
|
||||
lambda: { classical: "λάμβδα", koine: "λάμδα" },
|
||||
mu: { classical: "μῦ", koine: "μι" },
|
||||
nu: { classical: "νῦ", koine: "νι" },
|
||||
xi: { classical: "ξῖ", koine: "ξι" },
|
||||
omicron: { classical: "ὂ μικρόν", koine: "όμικρον" },
|
||||
pi: { classical: "πῖ", koine: "πι" },
|
||||
rho: { classical: "ῥῶ", koine: "ρω" },
|
||||
sigma: { classical: "σῖγμα", koine: "σίγμα" },
|
||||
tau: { classical: "ταῦ", koine: "ταυ" },
|
||||
upsilon: { classical: "ὖ ψιλόν", koine: "ύψιλον" },
|
||||
phi: { classical: "φῖ", koine: "φι" },
|
||||
chi: { classical: "χῖ", koine: "χι" },
|
||||
psi: { classical: "ψῖ", koine: "ψι" },
|
||||
omega: { classical: "ὦ μέγα", koine: "ωμέγα" },
|
||||
digamma: { classical: "δίγαμμα", koine: "δίγαμμα" },
|
||||
qoppa: { classical: "κόππα", koine: "κόππα" },
|
||||
sampi: { classical: "σαμπί", koine: "σαμπί" }
|
||||
};
|
||||
|
||||
const GREEK_TRANSLITERATIONS = {
|
||||
alpha: { classical: "A", koine: "A" },
|
||||
beta: { classical: "B", koine: "V" },
|
||||
gamma: { classical: "G", koine: "G" },
|
||||
delta: { classical: "D", koine: "Th" },
|
||||
epsilon: { classical: "E", koine: "E" },
|
||||
zeta: { classical: "Z", koine: "Z" },
|
||||
eta: { classical: "E", koine: "I" },
|
||||
theta: { classical: "Th", koine: "Th" },
|
||||
iota: { classical: "I", koine: "I" },
|
||||
kappa: { classical: "K", koine: "K" },
|
||||
lambda: { classical: "L", koine: "L" },
|
||||
mu: { classical: "M", koine: "M" },
|
||||
nu: { classical: "N", koine: "N" },
|
||||
xi: { classical: "X", koine: "X" },
|
||||
omicron: { classical: "O", koine: "O" },
|
||||
pi: { classical: "P", koine: "P" },
|
||||
rho: { classical: "R", koine: "R" },
|
||||
sigma: { classical: "S", koine: "S" },
|
||||
tau: { classical: "T", koine: "T" },
|
||||
upsilon: { classical: "U/Y", koine: "I" },
|
||||
phi: { classical: "Ph", koine: "F" },
|
||||
chi: { classical: "Kh/Ch", koine: "Ch" },
|
||||
psi: { classical: "Ps", koine: "Ps" },
|
||||
omega: { classical: "O", koine: "O" },
|
||||
digamma: { classical: "W", koine: "V" },
|
||||
qoppa: { classical: "Q", koine: "Q" },
|
||||
sampi: { classical: "Ss/Ts", koine: "Ss/Ts" }
|
||||
};
|
||||
|
||||
const HEBREW_NATIVE_NAMES = {
|
||||
alef: "אלף",
|
||||
bet: "בית",
|
||||
gimel: "גימל",
|
||||
dalet: "דלת",
|
||||
he: "הא",
|
||||
vav: "וו",
|
||||
zayin: "זין",
|
||||
het: "חית",
|
||||
tet: "טית",
|
||||
yod: "יוד",
|
||||
kaf: "כף",
|
||||
lamed: "למד",
|
||||
mem: "מם",
|
||||
nun: "נון",
|
||||
samekh: "סמך",
|
||||
ayin: "עין",
|
||||
pe: "פה",
|
||||
tsadi: "צדי",
|
||||
qof: "קוף",
|
||||
resh: "ריש",
|
||||
shin: "שין",
|
||||
tav: "תו"
|
||||
};
|
||||
|
||||
function greekNativeNamesByLetter(letter) {
|
||||
const key = String(letter?.name || "").trim().toLowerCase();
|
||||
const names = GREEK_NATIVE_NAMES[key];
|
||||
if (!names) {
|
||||
return { classical: "", koine: "" };
|
||||
}
|
||||
|
||||
return {
|
||||
classical: String(names.classical || "").trim(),
|
||||
koine: String(names.koine || "").trim()
|
||||
};
|
||||
}
|
||||
|
||||
function formatGreekNativeNamesByLetter(letter) {
|
||||
const names = greekNativeNamesByLetter(letter);
|
||||
if (names.classical && names.koine && names.classical !== names.koine) {
|
||||
return `${names.classical} / ${names.koine}`;
|
||||
}
|
||||
|
||||
return names.classical || names.koine || "";
|
||||
}
|
||||
|
||||
function toGreekUppercase(value) {
|
||||
return String(value || "")
|
||||
.normalize("NFD")
|
||||
.replace(/[\u0300-\u036f]/g, "")
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
function formatGreekNativeUppercaseByLetter(letter) {
|
||||
const names = greekNativeNamesByLetter(letter);
|
||||
const classicalName = toGreekUppercase(names.classical);
|
||||
const koineName = toGreekUppercase(names.koine);
|
||||
if (classicalName && koineName && classicalName !== koineName) {
|
||||
return `${classicalName} / ${koineName}`;
|
||||
}
|
||||
|
||||
return classicalName || koineName || "";
|
||||
}
|
||||
|
||||
function greekTransliterationVariantsByLetter(letter) {
|
||||
const key = String(letter?.name || "").trim().toLowerCase();
|
||||
const variants = GREEK_TRANSLITERATIONS[key];
|
||||
if (!variants) {
|
||||
const fallback = String(letter?.transliteration || "").trim();
|
||||
return {
|
||||
classical: fallback,
|
||||
koine: fallback
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
classical: String(variants.classical || "").trim(),
|
||||
koine: String(variants.koine || "").trim()
|
||||
};
|
||||
}
|
||||
|
||||
function formatGreekTransliterationByLetter(letter) {
|
||||
const variants = greekTransliterationVariantsByLetter(letter);
|
||||
if (variants.classical && variants.koine && variants.classical !== variants.koine) {
|
||||
return `${variants.classical} / ${variants.koine}`;
|
||||
}
|
||||
|
||||
return variants.classical || variants.koine || String(letter?.transliteration || "").trim();
|
||||
}
|
||||
|
||||
function greekPlaceValueByIndex(index) {
|
||||
const value = Number(index);
|
||||
if (!Number.isFinite(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const position = Math.trunc(value);
|
||||
if (position <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (position <= 9) {
|
||||
return position;
|
||||
}
|
||||
|
||||
if (position <= 18) {
|
||||
return (position - 9) * 10;
|
||||
}
|
||||
|
||||
return (position - 18) * 100;
|
||||
}
|
||||
|
||||
function buildGreekGematriaMap(alphabets, mode = "orderly") {
|
||||
const map = new Map();
|
||||
const greekClassical = Array.isArray(alphabets?.greek) ? alphabets.greek : [];
|
||||
const greekArchaic = Array.isArray(alphabets?.greekArchaic) ? alphabets.greekArchaic : [];
|
||||
|
||||
[...greekClassical, ...greekArchaic].forEach((entry) => {
|
||||
const usePlaceValue = mode === "place-value";
|
||||
const value = usePlaceValue && !entry?.archaic
|
||||
? greekPlaceValueByIndex(entry?.index)
|
||||
: Number(entry?.numerology);
|
||||
if (!Number.isFinite(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
[entry?.char, entry?.charLower, entry?.charFinal].forEach((glyph) => {
|
||||
const key = String(glyph || "").trim();
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
map.set(key, Math.trunc(value));
|
||||
});
|
||||
});
|
||||
|
||||
if (!map.has("ς") && map.has("σ")) {
|
||||
map.set("ς", map.get("σ"));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function buildHebrewGematriaMap(alphabets) {
|
||||
const map = new Map();
|
||||
const hebrewLetters = Array.isArray(alphabets?.hebrew) ? alphabets.hebrew : [];
|
||||
|
||||
hebrewLetters.forEach((entry) => {
|
||||
const value = Number(entry?.numerology);
|
||||
const glyph = String(entry?.char || "").trim();
|
||||
if (!glyph || !Number.isFinite(value)) {
|
||||
return;
|
||||
}
|
||||
map.set(glyph, Math.trunc(value));
|
||||
});
|
||||
|
||||
const finals = {
|
||||
ך: "כ",
|
||||
ם: "מ",
|
||||
ן: "נ",
|
||||
ף: "פ",
|
||||
ץ: "צ"
|
||||
};
|
||||
|
||||
Object.entries(finals).forEach(([finalForm, baseForm]) => {
|
||||
if (!map.has(finalForm) && map.has(baseForm)) {
|
||||
map.set(finalForm, map.get(baseForm));
|
||||
}
|
||||
});
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function computeNameGematria(name, valueMap, options = {}) {
|
||||
if (!(valueMap instanceof Map) || valueMap.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizeGreek = options?.normalizeGreek === true;
|
||||
const normalized = normalizeGreek
|
||||
? String(name || "").normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||
: String(name || "");
|
||||
|
||||
let sum = 0;
|
||||
let matched = 0;
|
||||
for (const glyph of normalized) {
|
||||
const key = String(glyph || "").trim();
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
const value = valueMap.get(key);
|
||||
if (!Number.isFinite(value)) {
|
||||
continue;
|
||||
}
|
||||
sum += value;
|
||||
matched += 1;
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
function computeNameGematriaWithBreakdown(name, valueMap, options = {}) {
|
||||
if (!(valueMap instanceof Map) || valueMap.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalizeGreek = options?.normalizeGreek === true;
|
||||
const normalized = normalizeGreek
|
||||
? String(name || "").normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||
: String(name || "");
|
||||
|
||||
let total = 0;
|
||||
const parts = [];
|
||||
|
||||
for (const glyph of normalized) {
|
||||
const key = String(glyph || "").trim();
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = valueMap.get(key);
|
||||
if (!Number.isFinite(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const numericValue = Math.trunc(value);
|
||||
total += numericValue;
|
||||
parts.push(`${key}(${numericValue})`);
|
||||
}
|
||||
|
||||
if (!parts.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
total,
|
||||
breakdown: `${parts.join(" + ")} = ${total}`
|
||||
};
|
||||
}
|
||||
|
||||
function hebrewNativeNameByLetter(letter) {
|
||||
const key = String(letter?.hebrewLetterId || "").trim().toLowerCase();
|
||||
return HEBREW_NATIVE_NAMES[key] || "";
|
||||
}
|
||||
|
||||
function findGreekEquivalentForHebrew(letter, greekLetters) {
|
||||
const greekEquivalentKey = String(letter?.greekEquivalent || "").trim().toLowerCase();
|
||||
if (greekEquivalentKey) {
|
||||
return greekLetters.find((entry) => String(entry?.name || "").trim().toLowerCase() === greekEquivalentKey) || null;
|
||||
}
|
||||
|
||||
const hebrewId = String(letter?.hebrewLetterId || "").trim().toLowerCase();
|
||||
if (!hebrewId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return greekLetters.find((entry) => String(entry?.hebrewLetterId || "").trim().toLowerCase() === hebrewId) || null;
|
||||
}
|
||||
|
||||
function extractEnglishLetterRefs(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return [...new Set(value.map((entry) => normalizeLatinLetter(entry)).filter(Boolean))];
|
||||
@@ -230,7 +553,9 @@
|
||||
|
||||
function renderAlphabetEquivalentCard(activeAlphabet, letter, context) {
|
||||
const hebrewLetters = Array.isArray(context.alphabets?.hebrew) ? context.alphabets.hebrew : [];
|
||||
const greekLetters = Array.isArray(context.alphabets?.greek) ? context.alphabets.greek : [];
|
||||
const greekClassicalLetters = Array.isArray(context.alphabets?.greek) ? context.alphabets.greek : [];
|
||||
const greekArchaicLetters = Array.isArray(context.alphabets?.greekArchaic) ? context.alphabets.greekArchaic : [];
|
||||
const greekLetters = [...greekClassicalLetters, ...greekArchaicLetters];
|
||||
const englishLetters = Array.isArray(context.alphabets?.english) ? context.alphabets.english : [];
|
||||
const arabicLetters = Array.isArray(context.alphabets?.arabic) ? context.alphabets.arabic : [];
|
||||
const enochianLetters = Array.isArray(context.alphabets?.enochian) ? context.alphabets.enochian : [];
|
||||
@@ -259,7 +584,7 @@
|
||||
|
||||
if (activeAlphabet === "hebrew") {
|
||||
addHebrewId(letter?.hebrewLetterId);
|
||||
} else if (activeAlphabet === "greek") {
|
||||
} else if (activeAlphabet === "greek" || activeAlphabet === "greekArchaic") {
|
||||
addHebrewId(letter?.hebrewLetterId);
|
||||
englishLetters
|
||||
.filter((entry) => context.normalizeId(entry?.greekEquivalent) === context.normalizeId(letter?.name))
|
||||
@@ -309,13 +634,14 @@
|
||||
if (!(viaHebrew || viaEnglish)) {
|
||||
return;
|
||||
}
|
||||
if (activeAlphabet === "greek" && key === activeGreekKey) {
|
||||
const greekAlphabet = grk?.archaic ? "greekArchaic" : "greek";
|
||||
if ((activeAlphabet === "greek" || activeAlphabet === "greekArchaic") && key === activeGreekKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
buttons.push(`<button class="alpha-sister-btn" data-alpha="greek" data-key="${grk.name}">
|
||||
buttons.push(`<button class="alpha-sister-btn" data-alpha="${greekAlphabet}" data-key="${grk.name}">
|
||||
<span class="alpha-sister-glyph">${grk.char}</span>
|
||||
<span class="alpha-sister-name">Greek: ${grk.displayName} (${grk.transliteration}) · isopsephy ${grk.numerology}</span>
|
||||
<span class="alpha-sister-name">Greek${grk?.archaic ? " (Archaic)" : ""}: ${grk.displayName} (${formatGreekTransliterationByLetter(grk)}) · isopsephy ${grk.numerology}</span>
|
||||
</button>`);
|
||||
});
|
||||
|
||||
@@ -378,6 +704,14 @@
|
||||
|
||||
function renderHebrewDetail(context) {
|
||||
const { letter, detailSubEl, detailBodyEl } = context;
|
||||
const greekClassicalLetters = Array.isArray(context.alphabets?.greek) ? context.alphabets.greek : [];
|
||||
const greekArchaicLetters = Array.isArray(context.alphabets?.greekArchaic) ? context.alphabets.greekArchaic : [];
|
||||
const greekLetters = [...greekClassicalLetters, ...greekArchaicLetters];
|
||||
const greekEquivalent = findGreekEquivalentForHebrew(letter, greekLetters);
|
||||
const greekEquivalentNativeName = formatGreekNativeNamesByLetter(greekEquivalent);
|
||||
const hebrewNativeName = hebrewNativeNameByLetter(letter);
|
||||
const hebrewNameGematria = computeNameGematriaWithBreakdown(hebrewNativeName, buildHebrewGematriaMap(context.alphabets));
|
||||
|
||||
detailSubEl.textContent = `${letter.name} — ${letter.transliteration}`;
|
||||
detailBodyEl.innerHTML = "";
|
||||
|
||||
@@ -385,8 +719,12 @@
|
||||
sections.push(context.card("Letter Details", `
|
||||
<dl class="alpha-dl">
|
||||
<dt>Character</dt><dd>${letter.char}</dd>
|
||||
<dt>Name</dt><dd>${letter.name}</dd>
|
||||
<dt>Name (English)</dt><dd>${letter.name}</dd>
|
||||
<dt>Name (Hebrew)</dt><dd>${hebrewNativeName || "—"}</dd>
|
||||
<dt>Name Gematria (Hebrew)</dt><dd>${Number.isFinite(hebrewNameGematria?.total) ? hebrewNameGematria.total : "—"}</dd>
|
||||
<dt>Name Gematria Breakdown (Hebrew)</dt><dd>${hebrewNameGematria?.breakdown || "—"}</dd>
|
||||
<dt>Transliteration</dt><dd>${letter.transliteration}</dd>
|
||||
<dt>Greek Equivalent</dt><dd>${greekEquivalent ? `${greekEquivalent.char} ${greekEquivalent.displayName}${greekEquivalentNativeName ? ` (${greekEquivalentNativeName})` : ""}` : "—"}</dd>
|
||||
<dt>Meaning</dt><dd>${letter.meaning}</dd>
|
||||
<dt>Gematria Value</dt><dd>${letter.numerology}</dd>
|
||||
<dt>Letter Type</dt><dd class="alpha-badge alpha-badge--${letter.letterType}">${letter.letterType}</dd>
|
||||
@@ -450,10 +788,26 @@
|
||||
context.attachDetailListeners();
|
||||
}
|
||||
|
||||
function renderGreekDetail(context) {
|
||||
function renderGreekDetail(context, alphabetKey = "greek") {
|
||||
const { letter, detailSubEl, detailBodyEl } = context;
|
||||
const archaicBadge = letter.archaic ? ' <span class="alpha-badge alpha-badge--archaic">archaic</span>' : "";
|
||||
detailSubEl.textContent = `${letter.displayName}${letter.archaic ? " (archaic)" : ""} — ${letter.transliteration}`;
|
||||
const setLabel = alphabetKey === "greekArchaic" ? "Archaic" : "Classical/Koine";
|
||||
const greekNativeNames = greekNativeNamesByLetter(letter);
|
||||
const greekNativeNameCombined = formatGreekNativeNamesByLetter(letter);
|
||||
const greekNativeUppercase = formatGreekNativeUppercaseByLetter(letter);
|
||||
const greekClassicalUppercase = toGreekUppercase(greekNativeNames.classical);
|
||||
const greekKoineUppercase = toGreekUppercase(greekNativeNames.koine);
|
||||
const greekTranslit = greekTransliterationVariantsByLetter(letter);
|
||||
const greekTranslitCombined = formatGreekTransliterationByLetter(letter);
|
||||
const greekOrderlyMap = buildGreekGematriaMap(context.alphabets, "orderly");
|
||||
const greekPlaceValueMap = buildGreekGematriaMap(context.alphabets, "place-value");
|
||||
const greekClassicalNameGematriaOrderly = computeNameGematriaWithBreakdown(greekNativeNames.classical, greekOrderlyMap, { normalizeGreek: true });
|
||||
const greekClassicalNameGematriaPlace = computeNameGematriaWithBreakdown(greekNativeNames.classical, greekPlaceValueMap, { normalizeGreek: true });
|
||||
const greekKoineNameGematriaOrderly = computeNameGematriaWithBreakdown(greekNativeNames.koine, greekOrderlyMap, { normalizeGreek: true });
|
||||
const greekKoineNameGematriaPlace = computeNameGematriaWithBreakdown(greekNativeNames.koine, greekPlaceValueMap, { normalizeGreek: true });
|
||||
const orderlyLetterValue = Number.isFinite(Number(letter?.numerology)) ? Math.trunc(Number(letter.numerology)) : null;
|
||||
const placeLetterValue = letter?.archaic ? orderlyLetterValue : greekPlaceValueByIndex(letter?.index);
|
||||
detailSubEl.textContent = `${letter.displayName}${letter.archaic ? " (archaic)" : ""} — ${greekTranslitCombined} · ${setLabel}`;
|
||||
detailBodyEl.innerHTML = "";
|
||||
|
||||
const sections = [];
|
||||
@@ -465,20 +819,33 @@
|
||||
<dt>Uppercase</dt><dd>${letter.char}</dd>
|
||||
<dt>Lowercase</dt><dd>${letter.charLower || "—"}</dd>
|
||||
${charRow}
|
||||
<dt>Name</dt><dd>${letter.displayName}${archaicBadge}</dd>
|
||||
<dt>Transliteration</dt><dd>${letter.transliteration}</dd>
|
||||
<dt>Name (English)</dt><dd>${letter.displayName}${archaicBadge}</dd>
|
||||
<dt>Name (Greek)</dt><dd>${greekNativeNameCombined || "—"}</dd>
|
||||
<dt>Name (Greek Uppercase)</dt><dd>${greekNativeUppercase || "—"}</dd>
|
||||
<dt>Name (Greek Classical)</dt><dd>${greekNativeNames.classical || "—"}</dd>
|
||||
<dt>Name (Greek Classical Uppercase)</dt><dd>${greekClassicalUppercase || "—"}</dd>
|
||||
<dt>Name (Greek Koine)</dt><dd>${greekNativeNames.koine || "—"}</dd>
|
||||
<dt>Name (Greek Koine Uppercase)</dt><dd>${greekKoineUppercase || "—"}</dd>
|
||||
<dt>Transliteration</dt><dd>${greekTranslitCombined || "—"}</dd>
|
||||
<dt>Transliteration (Classical)</dt><dd>${greekTranslit.classical || "—"}</dd>
|
||||
<dt>Transliteration (Koine)</dt><dd>${greekTranslit.koine || "—"}</dd>
|
||||
<dt>IPA</dt><dd>${letter.ipa || "—"}</dd>
|
||||
<dt>Isopsephy Value</dt><dd>${letter.numerology}</dd>
|
||||
<dt>Isopsephy Value (Orderly)</dt><dd>${Number.isFinite(orderlyLetterValue) ? orderlyLetterValue : "—"}</dd>
|
||||
<dt>Isopsephy Value (1-10-100)</dt><dd>${Number.isFinite(placeLetterValue) ? placeLetterValue : "—"}</dd>
|
||||
<dt>Name Gematria (Greek Classical)</dt><dd>orderly ${Number.isFinite(greekClassicalNameGematriaOrderly?.total) ? greekClassicalNameGematriaOrderly.total : "—"} · 1-10-100 ${Number.isFinite(greekClassicalNameGematriaPlace?.total) ? greekClassicalNameGematriaPlace.total : "—"}</dd>
|
||||
<dt>Name Breakdown (Greek Classical)</dt><dd>orderly: ${greekClassicalNameGematriaOrderly?.breakdown || "—"} · 1-10-100: ${greekClassicalNameGematriaPlace?.breakdown || "—"}</dd>
|
||||
<dt>Name Gematria (Greek Koine)</dt><dd>orderly ${Number.isFinite(greekKoineNameGematriaOrderly?.total) ? greekKoineNameGematriaOrderly.total : "—"} · 1-10-100 ${Number.isFinite(greekKoineNameGematriaPlace?.total) ? greekKoineNameGematriaPlace.total : "—"}</dd>
|
||||
<dt>Name Breakdown (Greek Koine)</dt><dd>orderly: ${greekKoineNameGematriaOrderly?.breakdown || "—"} · 1-10-100: ${greekKoineNameGematriaPlace?.breakdown || "—"}</dd>
|
||||
<dt>Meaning / Origin</dt><dd>${letter.meaning || "—"}</dd>
|
||||
</dl>
|
||||
`));
|
||||
|
||||
const positionRootCard = renderPositionDigitalRootCard(letter, "greek", context);
|
||||
const positionRootCard = renderPositionDigitalRootCard(letter, alphabetKey, context);
|
||||
if (positionRootCard) {
|
||||
sections.push(positionRootCard);
|
||||
}
|
||||
|
||||
const equivalentsCard = renderAlphabetEquivalentCard("greek", letter, context);
|
||||
const equivalentsCard = renderAlphabetEquivalentCard(alphabetKey, letter, context);
|
||||
if (equivalentsCard) {
|
||||
sections.push(equivalentsCard);
|
||||
}
|
||||
@@ -617,8 +984,8 @@
|
||||
const alphabet = context.alphabet;
|
||||
if (alphabet === "hebrew") {
|
||||
renderHebrewDetail(context);
|
||||
} else if (alphabet === "greek") {
|
||||
renderGreekDetail(context);
|
||||
} else if (alphabet === "greek" || alphabet === "greekArchaic") {
|
||||
renderGreekDetail(context, alphabet);
|
||||
} else if (alphabet === "english") {
|
||||
renderEnglishDetail(context);
|
||||
} else if (alphabet === "arabic") {
|
||||
|
||||
@@ -154,7 +154,9 @@
|
||||
const map = new Map();
|
||||
const alphabets = getAlphabets() || {};
|
||||
const hebrewLetters = Array.isArray(alphabets.hebrew) ? alphabets.hebrew : [];
|
||||
const greekLetters = Array.isArray(alphabets.greek) ? alphabets.greek : [];
|
||||
const greekClassicalLetters = Array.isArray(alphabets.greek) ? alphabets.greek : [];
|
||||
const greekArchaicLetters = Array.isArray(alphabets.greekArchaic) ? alphabets.greekArchaic : [];
|
||||
const greekLetters = [...greekClassicalLetters, ...greekArchaicLetters];
|
||||
|
||||
hebrewLetters.forEach((entry) => {
|
||||
const mapped = transliterationToBaseLetters(entry?.transliteration, baseAlphabet);
|
||||
|
||||
+18
-2
@@ -49,7 +49,7 @@
|
||||
|
||||
// ── Element cache ────────────────────────────────────────────────────
|
||||
let listEl, countEl, detailNameEl, detailSubEl, detailBodyEl;
|
||||
let tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian;
|
||||
let tabAll, tabHebrew, tabGreek, tabGreekArchaic, tabEnglish, tabArabic, tabEnochian;
|
||||
let searchInputEl, searchClearEl, typeFilterEl;
|
||||
let gematriaCipherEl, gematriaInputEl, gematriaResultEl, gematriaBreakdownEl;
|
||||
let gematriaModeEls, gematriaMatchesEl, gematriaInputLabelEl, gematriaCipherLabelEl;
|
||||
@@ -64,6 +64,7 @@
|
||||
tabAll = document.getElementById("alpha-tab-all");
|
||||
tabHebrew = document.getElementById("alpha-tab-hebrew");
|
||||
tabGreek = document.getElementById("alpha-tab-greek");
|
||||
tabGreekArchaic = document.getElementById("alpha-tab-greek-archaic");
|
||||
tabEnglish = document.getElementById("alpha-tab-english");
|
||||
tabArabic = document.getElementById("alpha-tab-arabic");
|
||||
tabEnochian = document.getElementById("alpha-tab-enochian");
|
||||
@@ -356,6 +357,7 @@
|
||||
tabAll,
|
||||
tabHebrew,
|
||||
tabGreek,
|
||||
tabGreekArchaic,
|
||||
tabEnglish,
|
||||
tabArabic,
|
||||
tabEnochian,
|
||||
@@ -466,7 +468,7 @@
|
||||
}
|
||||
|
||||
// Attach tab listeners
|
||||
[tabAll, tabHebrew, tabGreek, tabEnglish, tabArabic, tabEnochian].forEach((btn) => {
|
||||
[tabAll, tabHebrew, tabGreek, tabGreekArchaic, tabEnglish, tabArabic, tabEnochian].forEach((btn) => {
|
||||
if (!btn) return;
|
||||
btn.addEventListener("click", () => {
|
||||
switchAlphabet(btn.dataset.alpha, null);
|
||||
@@ -482,6 +484,20 @@
|
||||
}
|
||||
|
||||
function selectGreekLetterByName(name) {
|
||||
const greekLetters = Array.isArray(state.alphabets?.greek) ? state.alphabets.greek : [];
|
||||
const archaicLetters = Array.isArray(state.alphabets?.greekArchaic) ? state.alphabets.greekArchaic : [];
|
||||
const targetKey = String(name || "").trim().toLowerCase();
|
||||
|
||||
if (archaicLetters.some((entry) => String(entry?.name || "").trim().toLowerCase() === targetKey)) {
|
||||
switchAlphabet("greekArchaic", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (greekLetters.some((entry) => String(entry?.name || "").trim().toLowerCase() === targetKey)) {
|
||||
switchAlphabet("greek", name);
|
||||
return;
|
||||
}
|
||||
|
||||
switchAlphabet("greek", name);
|
||||
}
|
||||
|
||||
|
||||
@@ -83,12 +83,20 @@
|
||||
}
|
||||
|
||||
function setActiveSection(nextSection) {
|
||||
const previousSection = activeSection;
|
||||
const requestedSection = VALID_SECTIONS.has(nextSection) ? nextSection : "home";
|
||||
const normalized = config.isSectionAccessible?.(requestedSection) === false
|
||||
? "home"
|
||||
: requestedSection;
|
||||
activeSection = normalized;
|
||||
|
||||
document.dispatchEvent(new CustomEvent("section:changed", {
|
||||
detail: {
|
||||
previousSection,
|
||||
activeSection
|
||||
}
|
||||
}));
|
||||
|
||||
const elements = config.elements || {};
|
||||
const ensure = config.ensure || {};
|
||||
const referenceData = getReferenceData();
|
||||
|
||||
+9
-8
@@ -1012,7 +1012,8 @@
|
||||
<div class="alpha-tabs">
|
||||
<button id="alpha-tab-all" class="alpha-tab-btn alpha-tab-active" data-alpha="all">All</button>
|
||||
<button id="alpha-tab-hebrew" class="alpha-tab-btn" data-alpha="hebrew">Hebrew</button>
|
||||
<button id="alpha-tab-greek" class="alpha-tab-btn" data-alpha="greek">Greek</button>
|
||||
<button id="alpha-tab-greek" class="alpha-tab-btn" data-alpha="greek">Greek (Classical/Koine)</button>
|
||||
<button id="alpha-tab-greek-archaic" class="alpha-tab-btn" data-alpha="greekArchaic">Greek (Archaic)</button>
|
||||
<button id="alpha-tab-english" class="alpha-tab-btn" data-alpha="english">English</button>
|
||||
<button id="alpha-tab-arabic" class="alpha-tab-btn" data-alpha="arabic">Arabic</button>
|
||||
<button id="alpha-tab-enochian" class="alpha-tab-btn" data-alpha="enochian">Enochian</button>
|
||||
@@ -1312,14 +1313,14 @@
|
||||
<script src="node_modules/astronomy-engine/astronomy.browser.min.js"></script>
|
||||
<script src="app/astro-calcs.js"></script>
|
||||
<script src="app/app-config.js?v=20260529-access-gating-01"></script>
|
||||
<script src="app/data-service.js?v=20260529-access-gating-01"></script>
|
||||
<script src="app/data-service.js?v=20260601-now-polling-03"></script>
|
||||
<script src="app/calendar-events.js"></script>
|
||||
<script src="app/card-images.js?v=20260527-tarot-deck-gallery-01"></script>
|
||||
<script src="app/ui-tarot-lightbox.js?v=20260528-tarot-variant-sequence-02"></script>
|
||||
<script src="app/ui-tarot-house.js?v=20260401-house-top-date-01"></script>
|
||||
<script src="app/ui-tarot-relations.js"></script>
|
||||
<script src="app/ui-now-helpers.js?v=20260314-now-planets-grid-01"></script>
|
||||
<script src="app/ui-now.js?v=20260529-access-gating-01"></script>
|
||||
<script src="app/ui-now.js?v=20260601-now-polling-03"></script>
|
||||
<script src="app/ui-natal.js"></script>
|
||||
<script src="app/tarot-database-builders.js?v=20260424-decan-ranges-01"></script>
|
||||
<script src="app/tarot-database-assembly.js?v=20260402-princess-links-01"></script>
|
||||
@@ -1355,9 +1356,9 @@
|
||||
<script src="app/ui-cube-selection.js?v=20260424-cube-fixes-01"></script>
|
||||
<script src="app/ui-cube.js?v=20260424-association-web-01"></script>
|
||||
<script src="app/ui-alphabet-gematria.js?v=20260323-word-meta-01"></script>
|
||||
<script src="app/ui-alphabet-browser.js?v=20260309-enochian-api"></script>
|
||||
<script src="app/ui-alphabet-browser.js?v=20260531-greek-gematria-04"></script>
|
||||
<script src="app/ui-alphabet-references.js"></script>
|
||||
<script src="app/ui-alphabet-detail.js?v=20260529-access-gating-02"></script>
|
||||
<script src="app/ui-alphabet-detail.js?v=20260531-greek-gematria-04"></script>
|
||||
<script src="app/ui-alphabet-kabbalah.js"></script>
|
||||
<script src="app/ui-alphabet.js?v=20260424-detail-inline-links-02"></script>
|
||||
<script src="app/ui-alphabet-text.js?v=20260315-text-search-ui-01"></script>
|
||||
@@ -1383,9 +1384,9 @@
|
||||
<script src="app/ui-calendar-formatting.js?v=20260307b"></script>
|
||||
<script src="app/ui-calendar-visuals.js?v=20260307b"></script>
|
||||
<script src="app/ui-home-calendar.js?v=20260415-stellarium-toggle-01"></script>
|
||||
<script src="app/ui-section-state.js?v=20260529-access-gating-01"></script>
|
||||
<script src="app/app-runtime.js?v=20260529-access-gating-01"></script>
|
||||
<script src="app.js?v=20260529-access-gating-01"></script>
|
||||
<script src="app/ui-section-state.js?v=20260531-now-polling-02"></script>
|
||||
<script src="app/app-runtime.js?v=20260531-now-polling-02"></script>
|
||||
<script src="app.js?v=20260531-now-polling-02"></script>
|
||||
<script src="app/navigation-detail-test-harness.js?v=20260401-universal-detail-02"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user