Initial commit

This commit is contained in:
2026-03-07 01:09:00 -08:00
commit af7d63717e
102 changed files with 68739 additions and 0 deletions

882
app/ui-iching.js Normal file
View File

@@ -0,0 +1,882 @@
(function () {
const { getTarotCardSearchAliases } = window.TarotCardImages || {};
const state = {
initialized: false,
hexagrams: [],
filteredHexagrams: [],
trigramsByName: {},
tarotByTrigramName: {},
monthRefsByHexagramNumber: new Map(),
searchQuery: "",
selectedNumber: null
};
const ICHING_PLANET_BY_PLANET_ID = {
sol: "Sun",
luna: "Moon",
mercury: "Mercury",
venus: "Venus",
mars: "Mars",
jupiter: "Jupiter",
saturn: "Saturn",
earth: "Earth",
uranus: "Uranus",
neptune: "Neptune",
pluto: "Pluto"
};
function getElements() {
return {
ichingCardListEl: document.getElementById("iching-card-list"),
ichingSearchInputEl: document.getElementById("iching-search-input"),
ichingSearchClearEl: document.getElementById("iching-search-clear"),
ichingCountEl: document.getElementById("iching-card-count"),
ichingDetailNameEl: document.getElementById("iching-detail-name"),
ichingDetailTypeEl: document.getElementById("iching-detail-type"),
ichingDetailSummaryEl: document.getElementById("iching-detail-summary"),
ichingDetailJudgementEl: document.getElementById("iching-detail-judgement"),
ichingDetailImageEl: document.getElementById("iching-detail-image"),
ichingDetailBinaryEl: document.getElementById("iching-detail-binary"),
ichingDetailLineEl: document.getElementById("iching-detail-line"),
ichingDetailKeywordsEl: document.getElementById("iching-detail-keywords"),
ichingDetailTrigramsEl: document.getElementById("iching-detail-trigrams"),
ichingDetailPlanetEl: document.getElementById("iching-detail-planet"),
ichingDetailTarotEl: document.getElementById("iching-detail-tarot"),
ichingDetailCalendarEl: document.getElementById("iching-detail-calendar")
};
}
function normalizeSearchValue(value) {
return String(value || "").trim().toLowerCase();
}
function clearChildren(element) {
if (element) {
element.replaceChildren();
}
}
function getTrigramByName(name) {
const key = normalizeSearchValue(name);
return key ? state.trigramsByName[key] || null : null;
}
function normalizePlanetInfluence(value) {
return String(value || "")
.trim()
.toLowerCase()
.replace(/[^a-z]/g, "");
}
function resolveAssociationPlanetInfluence(associations) {
if (!associations || typeof associations !== "object") {
return "";
}
const explicit = normalizePlanetInfluence(associations.iChingPlanetaryInfluence || associations.planetaryInfluence);
if (explicit) {
return explicit;
}
const planetId = normalizePlanetInfluence(associations.planetId);
if (!planetId) {
return "";
}
return normalizePlanetInfluence(ICHING_PLANET_BY_PLANET_ID[planetId]);
}
function buildMonthReferencesByHexagram(referenceData, hexagrams) {
const map = new Map();
const months = Array.isArray(referenceData?.calendarMonths) ? referenceData.calendarMonths : [];
const holidays = Array.isArray(referenceData?.celestialHolidays) ? referenceData.celestialHolidays : [];
const monthById = new Map(months.map((month) => [month.id, month]));
function parseMonthDayToken(value) {
const text = String(value || "").trim();
const match = text.match(/^(\d{1,2})-(\d{1,2})$/);
if (!match) {
return null;
}
const monthNo = Number(match[1]);
const dayNo = Number(match[2]);
if (!Number.isInteger(monthNo) || !Number.isInteger(dayNo) || monthNo < 1 || monthNo > 12 || dayNo < 1 || dayNo > 31) {
return null;
}
return { month: monthNo, day: dayNo };
}
function parseMonthDayTokensFromText(value) {
const text = String(value || "");
const matches = [...text.matchAll(/(\d{1,2})-(\d{1,2})/g)];
return matches
.map((match) => ({ month: Number(match[1]), day: Number(match[2]) }))
.filter((token) => Number.isInteger(token.month) && Number.isInteger(token.day) && token.month >= 1 && token.month <= 12 && token.day >= 1 && token.day <= 31);
}
function toDateToken(token, year) {
if (!token) {
return null;
}
return new Date(year, token.month - 1, token.day, 12, 0, 0, 0);
}
function splitMonthDayRangeByMonth(startToken, endToken) {
const startDate = toDateToken(startToken, 2025);
const endBase = toDateToken(endToken, 2025);
if (!startDate || !endBase) {
return [];
}
const wrapsYear = endBase.getTime() < startDate.getTime();
const endDate = wrapsYear ? toDateToken(endToken, 2026) : endBase;
if (!endDate) {
return [];
}
const segments = [];
let cursor = new Date(startDate);
while (cursor.getTime() <= endDate.getTime()) {
const monthEnd = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0, 12, 0, 0, 0);
const segmentEnd = monthEnd.getTime() < endDate.getTime() ? monthEnd : endDate;
segments.push({
monthNo: cursor.getMonth() + 1,
startDay: cursor.getDate(),
endDay: segmentEnd.getDate()
});
cursor = new Date(segmentEnd.getFullYear(), segmentEnd.getMonth(), segmentEnd.getDate() + 1, 12, 0, 0, 0);
}
return segments;
}
function tokenToString(monthNo, dayNo) {
return `${String(monthNo).padStart(2, "0")}-${String(dayNo).padStart(2, "0")}`;
}
function formatRangeLabel(monthName, startDay, endDay) {
if (!Number.isFinite(startDay) || !Number.isFinite(endDay)) {
return monthName;
}
if (startDay === endDay) {
return `${monthName} ${startDay}`;
}
return `${monthName} ${startDay}-${endDay}`;
}
function resolveRangeForMonth(month, options = {}) {
const monthOrder = Number(month?.order);
const monthStart = parseMonthDayToken(month?.start);
const monthEnd = parseMonthDayToken(month?.end);
if (!Number.isFinite(monthOrder) || !monthStart || !monthEnd) {
return {
startToken: String(month?.start || "").trim() || null,
endToken: String(month?.end || "").trim() || null,
label: month?.name || month?.id || "",
isFullMonth: true
};
}
let startToken = parseMonthDayToken(options.startToken);
let endToken = parseMonthDayToken(options.endToken);
if (!startToken || !endToken) {
const tokens = parseMonthDayTokensFromText(options.rawDateText);
if (tokens.length >= 2) {
startToken = tokens[0];
endToken = tokens[1];
} else if (tokens.length === 1) {
startToken = tokens[0];
endToken = tokens[0];
}
}
if (!startToken || !endToken) {
startToken = monthStart;
endToken = monthEnd;
}
const segments = splitMonthDayRangeByMonth(startToken, endToken);
const segment = segments.find((entry) => entry.monthNo === monthOrder) || null;
const useStart = segment ? { month: monthOrder, day: segment.startDay } : startToken;
const useEnd = segment ? { month: monthOrder, day: segment.endDay } : endToken;
const startText = tokenToString(useStart.month, useStart.day);
const endText = tokenToString(useEnd.month, useEnd.day);
const isFullMonth = startText === month.start && endText === month.end;
return {
startToken: startText,
endToken: endText,
label: isFullMonth
? (month.name || month.id)
: formatRangeLabel(month.name || month.id, useStart.day, useEnd.day),
isFullMonth
};
}
function pushRef(hexagramNumber, month, options = {}) {
if (!Number.isFinite(hexagramNumber) || !month?.id) {
return;
}
if (!map.has(hexagramNumber)) {
map.set(hexagramNumber, []);
}
const rows = map.get(hexagramNumber);
const range = resolveRangeForMonth(month, options);
const rowKey = `${month.id}|${range.startToken || ""}|${range.endToken || ""}`;
if (rows.some((entry) => entry.key === rowKey)) {
return;
}
rows.push({
id: month.id,
name: month.name || month.id,
order: Number.isFinite(Number(month.order)) ? Number(month.order) : 999,
label: range.label,
startToken: range.startToken,
endToken: range.endToken,
isFullMonth: range.isFullMonth,
key: rowKey
});
}
function collectRefs(associations, month, options = {}) {
const associationInfluence = resolveAssociationPlanetInfluence(associations);
if (!associationInfluence) {
return;
}
hexagrams.forEach((hexagram) => {
const hexagramInfluence = normalizePlanetInfluence(hexagram?.planetaryInfluence);
if (hexagramInfluence && hexagramInfluence === associationInfluence) {
pushRef(hexagram.number, month, options);
}
});
}
months.forEach((month) => {
collectRefs(month?.associations, month);
const events = Array.isArray(month?.events) ? month.events : [];
events.forEach((event) => {
collectRefs(event?.associations, month, {
rawDateText: event?.dateRange || event?.date || ""
});
});
});
holidays.forEach((holiday) => {
const month = monthById.get(holiday?.monthId);
if (!month) {
return;
}
collectRefs(holiday?.associations, month, {
rawDateText: holiday?.dateRange || holiday?.date || ""
});
});
map.forEach((rows, key) => {
const preciseMonthIds = new Set(
rows
.filter((entry) => !entry.isFullMonth)
.map((entry) => entry.id)
);
const filtered = rows.filter((entry) => {
if (!entry.isFullMonth) {
return true;
}
return !preciseMonthIds.has(entry.id);
});
filtered.sort((left, right) => {
if (left.order !== right.order) {
return left.order - right.order;
}
const startLeft = parseMonthDayToken(left.startToken);
const startRight = parseMonthDayToken(right.startToken);
const dayLeft = startLeft ? startLeft.day : 999;
const dayRight = startRight ? startRight.day : 999;
if (dayLeft !== dayRight) {
return dayLeft - dayRight;
}
return String(left.label || left.name || "").localeCompare(String(right.label || right.name || ""));
});
map.set(key, filtered);
});
return map;
}
function getBinaryPattern(value, expectedLength = 0) {
const raw = String(value || "").trim();
if (!raw) {
return "";
}
if (/^[01]+$/.test(raw)) {
if (!expectedLength || raw.length === expectedLength) {
return raw;
}
return "";
}
if (/^[|:]+$/.test(raw)) {
const mapped = raw
.split("")
.map((char) => (char === "|" ? "1" : "0"))
.join("");
if (!expectedLength || mapped.length === expectedLength) {
return mapped;
}
}
return "";
}
function createLineStack(binaryPattern, variant = "trigram") {
const container = document.createElement("div");
container.className = `iching-lines iching-lines-${variant}`;
binaryPattern.split("").forEach((bit) => {
const line = document.createElement("div");
line.className = bit === "1" ? "iching-line is-yang" : "iching-line is-yin";
container.appendChild(line);
});
return container;
}
function buildHexagramSearchText(entry) {
const upper = getTrigramByName(entry?.upperTrigram);
const lower = getTrigramByName(entry?.lowerTrigram);
const upperCards = upper ? state.tarotByTrigramName[normalizeSearchValue(upper.name)] || [] : [];
const lowerCards = lower ? state.tarotByTrigramName[normalizeSearchValue(lower.name)] || [] : [];
const tarotAliasText = typeof getTarotCardSearchAliases === "function"
? [...upperCards, ...lowerCards]
.flatMap((cardName) => getTarotCardSearchAliases(cardName))
.join(" ")
: [...upperCards, ...lowerCards].join(" ");
const parts = [
entry?.number,
entry?.name,
entry?.chineseName,
entry?.pinyin,
entry?.judgement,
entry?.image,
entry?.upperTrigram,
entry?.lowerTrigram,
entry?.planetaryInfluence,
entry?.binary,
entry?.lineDiagram,
...(Array.isArray(entry?.keywords) ? entry.keywords : []),
upper?.name,
upper?.chineseName,
upper?.pinyin,
upper?.element,
upper?.attribute,
upper?.binary,
upper?.description,
lower?.name,
lower?.chineseName,
lower?.pinyin,
lower?.element,
lower?.attribute,
lower?.binary,
lower?.description,
upperCards.join(" "),
lowerCards.join(" "),
tarotAliasText
];
return normalizeSearchValue(parts.filter((part) => part !== null && part !== undefined).join(" "));
}
function updateSelection(elements) {
if (!elements?.ichingCardListEl) {
return;
}
const buttons = elements.ichingCardListEl.querySelectorAll(".planet-list-item");
buttons.forEach((button) => {
const isSelected = Number(button.dataset.hexagramNumber) === state.selectedNumber;
button.classList.toggle("is-selected", isSelected);
button.setAttribute("aria-selected", isSelected ? "true" : "false");
});
}
function renderEmptyDetail(elements, detailName = "No hexagrams found") {
if (!elements) {
return;
}
if (elements.ichingDetailNameEl) {
elements.ichingDetailNameEl.textContent = detailName;
}
if (elements.ichingDetailTypeEl) {
elements.ichingDetailTypeEl.textContent = "--";
}
if (elements.ichingDetailSummaryEl) {
elements.ichingDetailSummaryEl.textContent = "--";
}
if (elements.ichingDetailJudgementEl) {
elements.ichingDetailJudgementEl.textContent = "--";
}
if (elements.ichingDetailImageEl) {
elements.ichingDetailImageEl.textContent = "--";
}
if (elements.ichingDetailBinaryEl) {
elements.ichingDetailBinaryEl.textContent = "--";
}
if (elements.ichingDetailLineEl) {
clearChildren(elements.ichingDetailLineEl);
elements.ichingDetailLineEl.textContent = "--";
}
if (elements.ichingDetailPlanetEl) {
elements.ichingDetailPlanetEl.textContent = "--";
}
if (elements.ichingDetailTarotEl) {
elements.ichingDetailTarotEl.textContent = "--";
}
if (elements.ichingDetailCalendarEl) {
clearChildren(elements.ichingDetailCalendarEl);
elements.ichingDetailCalendarEl.textContent = "--";
}
clearChildren(elements.ichingDetailKeywordsEl);
clearChildren(elements.ichingDetailTrigramsEl);
}
function renderKeywords(entry, elements) {
clearChildren(elements.ichingDetailKeywordsEl);
const keywords = Array.isArray(entry?.keywords) ? entry.keywords : [];
if (!keywords.length) {
if (elements.ichingDetailKeywordsEl) {
elements.ichingDetailKeywordsEl.textContent = "--";
}
return;
}
keywords.forEach((keyword) => {
const chip = document.createElement("span");
chip.className = "tarot-keyword-chip";
chip.textContent = keyword;
elements.ichingDetailKeywordsEl?.appendChild(chip);
});
}
function createTrigramCard(label, trigram) {
const card = document.createElement("div");
card.className = "iching-trigram-card";
if (!trigram) {
card.textContent = `${label}: --`;
return card;
}
const title = document.createElement("div");
title.className = "iching-trigram-title";
const chinese = trigram.chineseName ? ` (${trigram.chineseName})` : "";
title.textContent = `${label}: ${trigram.name || "--"}${chinese}`;
const meta = document.createElement("div");
meta.className = "iching-trigram-meta";
const attribute = trigram.attribute || "--";
const element = trigram.element || "--";
meta.textContent = `${attribute} · ${element}`;
const diagram = document.createElement("div");
diagram.className = "iching-trigram-diagram";
const binaryPattern = getBinaryPattern(trigram.binary || trigram.lineDiagram, 3);
if (binaryPattern) {
const binaryLabel = document.createElement("div");
binaryLabel.className = "iching-line-label";
binaryLabel.textContent = binaryPattern;
diagram.append(binaryLabel, createLineStack(binaryPattern, "trigram"));
} else {
diagram.textContent = "--";
}
card.append(title, meta, diagram);
if (trigram.description) {
const description = document.createElement("div");
description.className = "planet-text";
description.textContent = trigram.description;
card.appendChild(description);
}
return card;
}
function renderTrigrams(entry, elements) {
clearChildren(elements.ichingDetailTrigramsEl);
const upper = getTrigramByName(entry?.upperTrigram);
const lower = getTrigramByName(entry?.lowerTrigram);
elements.ichingDetailTrigramsEl?.append(
createTrigramCard("Upper", upper),
createTrigramCard("Lower", lower)
);
}
function renderTarotCorrespondences(entry, elements) {
if (!elements?.ichingDetailTarotEl) {
return;
}
const upperKey = normalizeSearchValue(entry?.upperTrigram);
const lowerKey = normalizeSearchValue(entry?.lowerTrigram);
const upperCards = upperKey ? state.tarotByTrigramName[upperKey] || [] : [];
const lowerCards = lowerKey ? state.tarotByTrigramName[lowerKey] || [] : [];
const upperTrigram = upperKey ? state.trigramsByName[upperKey] : null;
const lowerTrigram = lowerKey ? state.trigramsByName[lowerKey] : null;
const upperLabel = upperTrigram?.element || entry?.upperTrigram || "--";
const lowerLabel = lowerTrigram?.element || entry?.lowerTrigram || "--";
const lines = [];
if (upperKey) {
lines.push(`Upper (${upperLabel}): ${upperCards.length ? upperCards.join(", ") : "--"}`);
}
if (lowerKey) {
lines.push(`Lower (${lowerLabel}): ${lowerCards.length ? lowerCards.join(", ") : "--"}`);
}
elements.ichingDetailTarotEl.textContent = lines.length ? lines.join("\n\n") : "--";
}
function renderCalendarMonths(entry, elements) {
if (!elements?.ichingDetailCalendarEl) {
return;
}
clearChildren(elements.ichingDetailCalendarEl);
const rows = state.monthRefsByHexagramNumber.get(entry?.number) || [];
if (!rows.length) {
elements.ichingDetailCalendarEl.textContent = "--";
return;
}
rows.forEach((month) => {
const button = document.createElement("button");
button.type = "button";
button.className = "alpha-nav-btn";
button.textContent = `${month.label || month.name}`;
button.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:calendar-month", {
detail: { monthId: month.id }
}));
});
elements.ichingDetailCalendarEl.appendChild(button);
});
}
function renderDetail(entry, elements) {
if (!entry || !elements) {
return;
}
const number = Number.isFinite(entry.number) ? entry.number : "--";
if (elements.ichingDetailNameEl) {
elements.ichingDetailNameEl.textContent = `${number} · ${entry.name || "--"}`;
}
if (elements.ichingDetailTypeEl) {
const chinese = entry.chineseName || "--";
const pinyin = entry.pinyin || "--";
const upper = entry.upperTrigram || "--";
const lower = entry.lowerTrigram || "--";
elements.ichingDetailTypeEl.textContent = `Hexagram · ${chinese} · ${pinyin} · ${upper}/${lower}`;
}
if (elements.ichingDetailSummaryEl) {
elements.ichingDetailSummaryEl.textContent = entry.judgement || "--";
}
if (elements.ichingDetailJudgementEl) {
elements.ichingDetailJudgementEl.textContent = entry.judgement || "--";
}
if (elements.ichingDetailImageEl) {
elements.ichingDetailImageEl.textContent = entry.image || "--";
}
if (elements.ichingDetailBinaryEl) {
const binary = entry.binary || "--";
elements.ichingDetailBinaryEl.textContent = `Binary: ${binary}`;
}
if (elements.ichingDetailLineEl) {
clearChildren(elements.ichingDetailLineEl);
const linePattern = getBinaryPattern(entry.binary, 6) || getBinaryPattern(entry.lineDiagram, 6);
if (linePattern) {
elements.ichingDetailLineEl.appendChild(createLineStack(linePattern, "hexagram"));
} else {
elements.ichingDetailLineEl.textContent = "--";
}
}
if (elements.ichingDetailPlanetEl) {
elements.ichingDetailPlanetEl.textContent = entry.planetaryInfluence || "--";
}
renderKeywords(entry, elements);
renderTrigrams(entry, elements);
renderTarotCorrespondences(entry, elements);
renderCalendarMonths(entry, elements);
}
function selectByNumber(number, elements) {
const numeric = Number(number);
if (!Number.isFinite(numeric)) {
return;
}
const entry = state.hexagrams.find((hexagram) => hexagram.number === numeric);
if (!entry) {
return;
}
state.selectedNumber = entry.number;
updateSelection(elements);
renderDetail(entry, elements);
}
function renderList(elements) {
if (!elements?.ichingCardListEl) {
return;
}
clearChildren(elements.ichingCardListEl);
state.filteredHexagrams.forEach((entry) => {
const button = document.createElement("button");
button.type = "button";
button.className = "planet-list-item";
button.dataset.hexagramNumber = String(entry.number);
button.setAttribute("role", "option");
const nameEl = document.createElement("span");
nameEl.className = "planet-list-name";
const number = Number.isFinite(entry.number) ? `#${entry.number} ` : "";
nameEl.textContent = `${number}${entry.name || "--"}`;
const metaEl = document.createElement("span");
metaEl.className = "planet-list-meta";
const upper = entry.upperTrigram || "--";
const lower = entry.lowerTrigram || "--";
metaEl.textContent = `${upper}/${lower} · ${entry.planetaryInfluence || "--"}`;
button.append(nameEl, metaEl);
elements.ichingCardListEl.appendChild(button);
});
if (elements.ichingCountEl) {
elements.ichingCountEl.textContent = state.searchQuery
? `${state.filteredHexagrams.length} of ${state.hexagrams.length} hexagrams`
: `${state.hexagrams.length} hexagrams`;
}
}
function applySearchFilter(elements) {
const query = normalizeSearchValue(state.searchQuery);
state.filteredHexagrams = query
? state.hexagrams.filter((entry) => buildHexagramSearchText(entry).includes(query))
: [...state.hexagrams];
if (elements?.ichingSearchClearEl) {
elements.ichingSearchClearEl.disabled = !query;
}
renderList(elements);
if (!state.filteredHexagrams.some((entry) => entry.number === state.selectedNumber)) {
if (state.filteredHexagrams.length > 0) {
selectByNumber(state.filteredHexagrams[0].number, elements);
} else {
state.selectedNumber = null;
updateSelection(elements);
renderEmptyDetail(elements);
}
return;
}
updateSelection(elements);
}
function ensureIChingSection(referenceData) {
const elements = getElements();
if (state.initialized) {
state.monthRefsByHexagramNumber = buildMonthReferencesByHexagram(referenceData, state.hexagrams);
const selected = state.hexagrams.find((hexagram) => hexagram.number === state.selectedNumber);
if (selected) {
renderDetail(selected, elements);
}
return;
}
if (!elements.ichingCardListEl || !elements.ichingDetailNameEl) {
return;
}
const iChing = referenceData?.iChing;
const trigrams = Array.isArray(iChing?.trigrams) ? iChing.trigrams : [];
const hexagrams = Array.isArray(iChing?.hexagrams) ? iChing.hexagrams : [];
const correspondences = iChing?.correspondences;
if (!hexagrams.length) {
renderEmptyDetail(elements, "I Ching data unavailable");
return;
}
state.trigramsByName = trigrams.reduce((acc, trigram) => {
const key = normalizeSearchValue(trigram?.name);
if (key) {
acc[key] = trigram;
}
return acc;
}, {});
const tarotToTrigram = Array.isArray(correspondences?.tarotToTrigram)
? correspondences.tarotToTrigram
: [];
state.tarotByTrigramName = tarotToTrigram.reduce((acc, row) => {
const key = normalizeSearchValue(row?.trigram);
const tarotCard = String(row?.tarot || "").trim();
if (!key || !tarotCard) {
return acc;
}
if (!Array.isArray(acc[key])) {
acc[key] = [];
}
if (!acc[key].includes(tarotCard)) {
acc[key].push(tarotCard);
}
return acc;
}, {});
state.hexagrams = [...hexagrams]
.map((entry) => ({
...entry,
number: Number(entry?.number)
}))
.filter((entry) => Number.isFinite(entry.number))
.sort((a, b) => a.number - b.number);
state.monthRefsByHexagramNumber = buildMonthReferencesByHexagram(referenceData, state.hexagrams);
state.filteredHexagrams = [...state.hexagrams];
renderList(elements);
if (state.hexagrams.length > 0) {
selectByNumber(state.hexagrams[0].number, elements);
}
elements.ichingCardListEl.addEventListener("click", (event) => {
const target = event.target;
if (!(target instanceof Node)) {
return;
}
const button = target instanceof Element
? target.closest(".planet-list-item")
: null;
if (!(button instanceof HTMLButtonElement)) {
return;
}
const selectedNumber = button.dataset.hexagramNumber;
if (!selectedNumber) {
return;
}
selectByNumber(selectedNumber, elements);
});
if (elements.ichingSearchInputEl) {
elements.ichingSearchInputEl.addEventListener("input", () => {
state.searchQuery = elements.ichingSearchInputEl.value || "";
applySearchFilter(elements);
});
}
if (elements.ichingSearchClearEl && elements.ichingSearchInputEl) {
elements.ichingSearchClearEl.addEventListener("click", () => {
elements.ichingSearchInputEl.value = "";
state.searchQuery = "";
applySearchFilter(elements);
elements.ichingSearchInputEl.focus();
});
}
state.initialized = true;
}
function selectByHexagramNumber(number) {
if (!state.initialized) {
return false;
}
const elements = getElements();
const numeric = Number(number);
if (!Number.isFinite(numeric)) {
return false;
}
const entry = state.hexagrams.find((hexagram) => hexagram.number === numeric);
if (!entry) {
return false;
}
selectByNumber(entry.number, elements);
elements.ichingCardListEl
?.querySelector(`[data-hexagram-number="${entry.number}"]`)
?.scrollIntoView({ block: "nearest" });
return true;
}
function selectByPlanetaryInfluence(planetaryInfluence) {
if (!state.initialized) {
return false;
}
const targetInfluence = normalizePlanetInfluence(planetaryInfluence);
if (!targetInfluence) {
return false;
}
const entry = state.hexagrams.find((hexagram) =>
normalizePlanetInfluence(hexagram?.planetaryInfluence) === targetInfluence
);
if (!entry) {
return false;
}
return selectByHexagramNumber(entry.number);
}
window.IChingSectionUi = {
ensureIChingSection,
selectByHexagramNumber,
selectByPlanetaryInfluence
};
})();