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

898
app/ui-planets.js Normal file
View File

@@ -0,0 +1,898 @@
(function () {
const { getTarotCardDisplayName, getTarotCardSearchAliases } = window.TarotCardImages || {};
const state = {
initialized: false,
entries: [],
filteredEntries: [],
searchQuery: "",
selectedId: "",
kabbalahTargetsByPlanetId: {},
monthRefsByPlanetId: new Map(),
cubePlacementsByPlanetId: new Map()
};
function normalizePlanetToken(value) {
return String(value || "")
.trim()
.toLowerCase()
.replace(/[^a-z]/g, "");
}
function toPlanetId(value) {
const token = normalizePlanetToken(value);
if (!token) return null;
if (token === "sun") return "sol";
if (token === "moon") return "luna";
if (["saturn", "jupiter", "mars", "sol", "venus", "mercury", "luna"].includes(token)) {
return token;
}
return null;
}
function appendKabbalahTarget(map, planetId, target) {
if (!planetId || !target) return;
if (!map[planetId]) map[planetId] = [];
const key = `${target.kind}:${target.number}`;
if (map[planetId].some((entry) => `${entry.kind}:${entry.number}` === key)) return;
map[planetId].push(target);
}
function buildKabbalahTargetsByPlanet(magickDataset) {
const tree = magickDataset?.grouped?.kabbalah?.["kabbalah-tree"];
const map = {};
if (!tree) return map;
(tree.sephiroth || []).forEach((seph) => {
const planetId = toPlanetId(seph?.planet);
if (!planetId) return;
appendKabbalahTarget(map, planetId, {
kind: "sephirah",
number: seph.number,
label: `Sephirah ${seph.number} · ${seph.name}`
});
});
(tree.paths || []).forEach((path) => {
if (path?.astrology?.type !== "planet") return;
const planetId = toPlanetId(path?.astrology?.name);
if (!planetId) return;
appendKabbalahTarget(map, planetId, {
kind: "path",
number: path.pathNumber,
label: `Path ${path.pathNumber} · ${path?.tarot?.card || path?.hebrewLetter?.transliteration || ""}`.trim()
});
});
return map;
}
function buildMonthReferencesByPlanet(referenceData) {
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(planetToken, month, options = {}) {
const planetId = toPlanetId(planetToken) || normalizePlanetToken(planetToken);
if (!planetId || !month?.id) {
return;
}
if (!map.has(planetId)) {
map.set(planetId, []);
}
const rows = map.get(planetId);
const range = resolveRangeForMonth(month, options);
const key = `${month.id}|${range.startToken || ""}|${range.endToken || ""}`;
if (rows.some((entry) => entry.key === key)) {
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
});
}
months.forEach((month) => {
pushRef(month?.associations?.planetId, month);
const events = Array.isArray(month?.events) ? month.events : [];
events.forEach((event) => {
pushRef(event?.associations?.planetId, month, {
rawDateText: event?.dateRange || event?.date || ""
});
});
});
holidays.forEach((holiday) => {
const month = monthById.get(holiday?.monthId);
if (!month) {
return;
}
pushRef(holiday?.associations?.planetId, 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 buildCubePlacementsByPlanet(magickDataset) {
const map = new Map();
const cube = magickDataset?.grouped?.kabbalah?.cube || {};
const walls = Array.isArray(cube?.walls)
? cube.walls
: [];
const edges = Array.isArray(cube?.edges)
? cube.edges
: [];
function edgeWalls(edge) {
const explicitWalls = Array.isArray(edge?.walls)
? edge.walls.map((wallId) => String(wallId || "").trim().toLowerCase()).filter(Boolean)
: [];
if (explicitWalls.length >= 2) {
return explicitWalls.slice(0, 2);
}
return String(edge?.id || "")
.trim()
.toLowerCase()
.split("-")
.map((wallId) => wallId.trim())
.filter(Boolean)
.slice(0, 2);
}
function edgeLabel(edge) {
const explicitName = String(edge?.name || "").trim();
if (explicitName) {
return explicitName;
}
return edgeWalls(edge)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(" ");
}
function resolveCubeDirectionLabel(wallId, edge) {
const normalizedWallId = String(wallId || "").trim().toLowerCase();
const edgeId = String(edge?.id || "").trim().toLowerCase();
if (!normalizedWallId || !edgeId) {
return "";
}
const cubeUi = window.CubeSectionUi;
if (cubeUi && typeof cubeUi.getEdgeDirectionLabelForWall === "function") {
const directionLabel = String(cubeUi.getEdgeDirectionLabelForWall(normalizedWallId, edgeId) || "").trim();
if (directionLabel) {
return directionLabel;
}
}
return edgeLabel(edge);
}
const firstEdgeByWallId = new Map();
edges.forEach((edge) => {
edgeWalls(edge).forEach((wallId) => {
if (!firstEdgeByWallId.has(wallId)) {
firstEdgeByWallId.set(wallId, edge);
}
});
});
function pushPlacement(planetId, placement) {
if (!planetId || !placement?.wallId || !placement?.edgeId) {
return;
}
if (!map.has(planetId)) {
map.set(planetId, []);
}
const rows = map.get(planetId);
const key = `${placement.wallId}:${placement.edgeId}`;
if (rows.some((row) => `${row.wallId}:${row.edgeId}` === key)) {
return;
}
rows.push(placement);
}
walls.forEach((wall) => {
const planetId = toPlanetId(wall?.associations?.planetId || wall?.planet);
if (!planetId) {
return;
}
const wallId = String(wall?.id || "").trim().toLowerCase();
const edge = firstEdgeByWallId.get(wallId) || null;
pushPlacement(planetId, {
wallId,
edgeId: String(edge?.id || "").trim().toLowerCase(),
label: `Cube: ${wall?.name || "Wall"} Wall - ${resolveCubeDirectionLabel(wallId, edge) || "Direction"}`
});
});
return map;
}
function getElements() {
return {
planetCardListEl: document.getElementById("planet-card-list"),
planetSearchInputEl: document.getElementById("planet-search-input"),
planetSearchClearEl: document.getElementById("planet-search-clear"),
planetCountEl: document.getElementById("planet-card-count"),
planetDetailNameEl: document.getElementById("planet-detail-name"),
planetDetailTypeEl: document.getElementById("planet-detail-type"),
planetDetailSummaryEl: document.getElementById("planet-detail-summary"),
planetDetailFactsEl: document.getElementById("planet-detail-facts"),
planetDetailAtmosphereEl: document.getElementById("planet-detail-atmosphere"),
planetDetailNotableEl: document.getElementById("planet-detail-notable"),
planetDetailCorrespondenceEl: document.getElementById("planet-detail-correspondence")
};
}
function normalizeSearchValue(value) {
return String(value || "").trim().toLowerCase();
}
function buildPlanetSearchText(entry) {
const correspondence = entry?.correspondence || {};
const factValues = buildFactRows(entry).map(([, value]) => String(value || ""));
const tarotAliases = correspondence?.tarot?.majorArcana && typeof getTarotCardSearchAliases === "function"
? getTarotCardSearchAliases(correspondence.tarot.majorArcana)
: [];
const rawNumbers = [
entry?.meanDistanceFromSun?.kmMillions,
entry?.meanDistanceFromSun?.au,
entry?.orbitalPeriod?.days,
entry?.orbitalPeriod?.years,
entry?.rotationPeriodHours,
entry?.radiusKm,
entry?.diameterKm,
entry?.massKg,
entry?.gravityMs2,
entry?.escapeVelocityKms,
entry?.axialTiltDeg,
entry?.averageTempC,
entry?.moons
]
.filter((value) => Number.isFinite(value))
.map((value) => String(value));
const parts = [
entry?.name,
entry?.symbol,
entry?.classification,
entry?.summary,
entry?.atmosphere,
...(Array.isArray(entry?.notableFacts) ? entry.notableFacts : []),
...factValues,
...rawNumbers,
correspondence?.name,
correspondence?.symbol,
correspondence?.weekday,
correspondence?.tarot?.majorArcana,
...tarotAliases
];
return normalizeSearchValue(parts.filter(Boolean).join(" "));
}
function applySearchFilter(elements) {
const query = normalizeSearchValue(state.searchQuery);
state.filteredEntries = query
? state.entries.filter((entry) => buildPlanetSearchText(entry).includes(query))
: [...state.entries];
if (elements?.planetSearchClearEl) {
elements.planetSearchClearEl.disabled = !query;
}
renderList(elements);
if (!state.filteredEntries.some((entry) => entry.id === state.selectedId)) {
if (state.filteredEntries.length > 0) {
selectById(state.filteredEntries[0].id, elements);
}
return;
}
updateSelection(elements);
}
function clearChildren(element) {
if (element) {
element.replaceChildren();
}
}
function toNumber(value) {
return Number.isFinite(value) ? value : null;
}
function formatNumber(value, maximumFractionDigits = 2) {
if (!Number.isFinite(value)) {
return "--";
}
return new Intl.NumberFormat(undefined, {
maximumFractionDigits,
minimumFractionDigits: 0
}).format(value);
}
function formatSignedHours(value) {
if (!Number.isFinite(value)) {
return "--";
}
const absValue = Math.abs(value);
const formatted = `${formatNumber(absValue, 2)} h`;
return value < 0 ? `${formatted} (retrograde)` : formatted;
}
function formatMass(value) {
if (!Number.isFinite(value)) {
return "--";
}
return value.toExponential(3).replace("e+", " × 10^") + " kg";
}
function buildFactRows(entry) {
return [
["Mean distance from Sun", Number.isFinite(entry?.meanDistanceFromSun?.kmMillions) && Number.isFinite(entry?.meanDistanceFromSun?.au)
? `${formatNumber(entry.meanDistanceFromSun.kmMillions, 1)} million km (${formatNumber(entry.meanDistanceFromSun.au, 3)} AU)`
: "--"],
["Orbital period", Number.isFinite(entry?.orbitalPeriod?.days) && Number.isFinite(entry?.orbitalPeriod?.years)
? `${formatNumber(entry.orbitalPeriod.days, 2)} days (${formatNumber(entry.orbitalPeriod.years, 3)} years)`
: "--"],
["Rotation period", formatSignedHours(toNumber(entry?.rotationPeriodHours))],
["Radius", Number.isFinite(entry?.radiusKm) ? `${formatNumber(entry.radiusKm, 1)} km` : "--"],
["Diameter", Number.isFinite(entry?.diameterKm) ? `${formatNumber(entry.diameterKm, 1)} km` : "--"],
["Mass", formatMass(toNumber(entry?.massKg))],
["Surface gravity", Number.isFinite(entry?.gravityMs2) ? `${formatNumber(entry.gravityMs2, 3)} m/s²` : "--"],
["Escape velocity", Number.isFinite(entry?.escapeVelocityKms) ? `${formatNumber(entry.escapeVelocityKms, 2)} km/s` : "--"],
["Axial tilt", Number.isFinite(entry?.axialTiltDeg) ? `${formatNumber(entry.axialTiltDeg, 2)}°` : "--"],
["Average temperature", Number.isFinite(entry?.averageTempC) ? `${formatNumber(entry.averageTempC, 0)} °C` : "--"],
["Moons", Number.isFinite(entry?.moons) ? formatNumber(entry.moons, 0) : "--"]
];
}
function getDisplayTarotName(cardName, trumpNumber) {
if (!cardName) {
return "";
}
if (typeof getTarotCardDisplayName !== "function") {
return cardName;
}
if (Number.isFinite(Number(trumpNumber))) {
return getTarotCardDisplayName(cardName, { trumpNumber: Number(trumpNumber) }) || cardName;
}
return getTarotCardDisplayName(cardName) || cardName;
}
function renderCorrespondence(entry, containerEl) {
if (!containerEl) return;
containerEl.innerHTML = "";
const correspondence = entry?.correspondence;
if (!correspondence || typeof correspondence !== "object") {
const fallback = document.createElement("span");
fallback.className = "planet-text";
fallback.textContent = "No tarot/day correspondence in current local dataset.";
containerEl.appendChild(fallback);
}
const symbol = correspondence?.symbol || "";
const weekday = correspondence?.weekday || "";
const arcana = correspondence?.tarot?.majorArcana || "";
const trumpNo = correspondence?.tarot?.number;
const arcanaLabel = getDisplayTarotName(arcana, trumpNo);
// Symbol + weekday line
if (symbol || weekday) {
const line = document.createElement("span");
line.className = "planet-text";
line.textContent = [symbol, weekday].filter(Boolean).join(" \u00b7 ");
containerEl.appendChild(line);
}
// Tarot card link
if (arcana) {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "kab-tarot-link";
btn.style.marginTop = "8px";
btn.textContent = trumpNo != null ? `${arcanaLabel} \u00b7 Trump ${trumpNo}` : arcanaLabel;
btn.title = "Open in Tarot section";
btn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { trumpNumber: trumpNo ?? null, cardName: arcana }
}));
});
containerEl.appendChild(btn);
}
const planetId = toPlanetId(correspondence?.id || entry?.id || entry?.name) ||
normalizePlanetToken(correspondence?.id || entry?.id || entry?.name);
const kabbalahTargets = state.kabbalahTargetsByPlanetId[planetId] || [];
if (kabbalahTargets.length) {
const row = document.createElement("div");
row.className = "kab-god-links";
row.style.marginTop = "8px";
kabbalahTargets.forEach((target) => {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "kab-god-link";
btn.textContent = `View ${target.label}`;
btn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:kabbalah-path", {
detail: { pathNo: Number(target.number) }
}));
});
row.appendChild(btn);
});
containerEl.appendChild(row);
}
const monthRefs = state.monthRefsByPlanetId.get(planetId) || [];
if (monthRefs.length) {
const meta = document.createElement("div");
meta.className = "kab-god-meta";
meta.textContent = "Calendar month correspondences";
containerEl.appendChild(meta);
const row = document.createElement("div");
row.className = "kab-god-links";
monthRefs.forEach((month) => {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "kab-god-link";
btn.textContent = `${month.label || month.name}`;
btn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:calendar-month", {
detail: { monthId: month.id }
}));
});
row.appendChild(btn);
});
containerEl.appendChild(row);
}
const cubePlacements = state.cubePlacementsByPlanetId.get(planetId) || [];
if (cubePlacements.length) {
const meta = document.createElement("div");
meta.className = "kab-god-meta";
meta.textContent = "Cube placements";
containerEl.appendChild(meta);
const row = document.createElement("div");
row.className = "kab-god-links";
cubePlacements.forEach((placement) => {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "kab-god-link";
btn.textContent = `${placement.label}`;
btn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:cube", {
detail: {
planetId,
wallId: placement.wallId,
edgeId: placement.edgeId
}
}));
});
row.appendChild(btn);
});
containerEl.appendChild(row);
}
}
function renderDetail(entry, elements) {
if (!entry || !elements) {
return;
}
if (elements.planetDetailNameEl) {
const symbol = entry.symbol ? `${entry.symbol} ` : "";
elements.planetDetailNameEl.textContent = `${symbol}${entry.name || "--"}`;
}
if (elements.planetDetailTypeEl) {
elements.planetDetailTypeEl.textContent = entry.classification || "--";
}
if (elements.planetDetailSummaryEl) {
elements.planetDetailSummaryEl.textContent = entry.summary || "--";
}
clearChildren(elements.planetDetailFactsEl);
buildFactRows(entry).forEach(([label, value]) => {
const row = document.createElement("div");
row.className = "planet-fact-row";
const labelEl = document.createElement("span");
labelEl.className = "planet-fact-label";
labelEl.textContent = label;
const valueEl = document.createElement("span");
valueEl.className = "planet-fact-value";
valueEl.textContent = value;
row.append(labelEl, valueEl);
elements.planetDetailFactsEl?.appendChild(row);
});
if (elements.planetDetailAtmosphereEl) {
elements.planetDetailAtmosphereEl.textContent = entry.atmosphere || "--";
}
clearChildren(elements.planetDetailNotableEl);
const notableFacts = Array.isArray(entry.notableFacts) ? entry.notableFacts : [];
notableFacts.forEach((fact) => {
const item = document.createElement("li");
item.textContent = fact;
elements.planetDetailNotableEl?.appendChild(item);
});
if (elements.planetDetailCorrespondenceEl) {
renderCorrespondence(entry, elements.planetDetailCorrespondenceEl);
}
}
function updateSelection(elements) {
if (!elements?.planetCardListEl) {
return;
}
const buttons = elements.planetCardListEl.querySelectorAll(".planet-list-item");
buttons.forEach((button) => {
const isSelected = button.dataset.planetId === state.selectedId;
button.classList.toggle("is-selected", isSelected);
button.setAttribute("aria-selected", isSelected ? "true" : "false");
});
}
function selectById(id, elements) {
const entry = state.entries.find((planet) => planet.id === id);
if (!entry) {
return;
}
state.selectedId = entry.id;
updateSelection(elements);
renderDetail(entry, elements);
}
function renderList(elements) {
if (!elements?.planetCardListEl) {
return;
}
clearChildren(elements.planetCardListEl);
state.filteredEntries.forEach((entry) => {
const button = document.createElement("button");
button.type = "button";
button.className = "planet-list-item";
button.dataset.planetId = entry.id;
button.setAttribute("role", "option");
const nameEl = document.createElement("span");
nameEl.className = "planet-list-name";
const symbol = entry.symbol ? `${entry.symbol} ` : "";
nameEl.textContent = `${symbol}${entry.name || "--"}`;
const metaEl = document.createElement("span");
metaEl.className = "planet-list-meta";
metaEl.textContent = entry.classification || "--";
button.append(nameEl, metaEl);
elements.planetCardListEl.appendChild(button);
});
if (elements.planetCountEl) {
elements.planetCountEl.textContent = state.searchQuery
? `${state.filteredEntries.length} of ${state.entries.length} bodies`
: `${state.entries.length} bodies`;
}
}
function ensurePlanetSection(referenceData, magickDataset = null) {
if (state.initialized) {
return;
}
const elements = getElements();
if (!elements.planetCardListEl || !elements.planetDetailNameEl) {
return;
}
const baseList = Array.isArray(referenceData?.planetScience)
? referenceData.planetScience
: [];
if (baseList.length === 0) {
if (elements.planetDetailNameEl) {
elements.planetDetailNameEl.textContent = "Planet data unavailable";
}
if (elements.planetDetailSummaryEl) {
elements.planetDetailSummaryEl.textContent = "Could not load local science facts dataset.";
}
return;
}
const correspondences = referenceData?.planets && typeof referenceData.planets === "object"
? referenceData.planets
: {};
const correspondenceByName = Object.values(correspondences).reduce((acc, value) => {
const key = String(value?.name || "").trim().toLowerCase();
if (key) {
acc[key] = value;
}
return acc;
}, {});
state.kabbalahTargetsByPlanetId = buildKabbalahTargetsByPlanet(magickDataset);
state.monthRefsByPlanetId = buildMonthReferencesByPlanet(referenceData);
state.cubePlacementsByPlanetId = buildCubePlacementsByPlanet(magickDataset);
state.entries = baseList.map((entry) => {
const byId = correspondences[entry.id] || null;
const byName = correspondenceByName[String(entry?.name || "").trim().toLowerCase()] || null;
return {
...entry,
correspondence: byId || byName || null
};
});
state.filteredEntries = [...state.entries];
renderList(elements);
if (state.entries.length > 0) {
selectById(state.entries[0].id, elements);
}
elements.planetCardListEl.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 selectedId = button.dataset.planetId;
if (!selectedId) {
return;
}
selectById(selectedId, elements);
});
if (elements.planetSearchInputEl) {
elements.planetSearchInputEl.addEventListener("input", () => {
state.searchQuery = elements.planetSearchInputEl.value || "";
applySearchFilter(elements);
});
}
if (elements.planetSearchClearEl && elements.planetSearchInputEl) {
elements.planetSearchClearEl.addEventListener("click", () => {
elements.planetSearchInputEl.value = "";
state.searchQuery = "";
applySearchFilter(elements);
elements.planetSearchInputEl.focus();
});
}
state.initialized = true;
}
function selectByPlanetId(planetId) {
if (!state.initialized) return;
const el = getElements();
const needle = String(planetId || "").toLowerCase();
const entry = state.entries.find(e =>
String(e.id || "").toLowerCase() === needle ||
String(e.correspondence?.id || "").toLowerCase() === needle ||
String(e.name || "").toLowerCase() === needle
);
if (!entry) return;
selectById(entry.id, el);
el.planetCardListEl
?.querySelector(`[data-planet-id="${entry.id}"]`)
?.scrollIntoView({ block: "nearest" });
}
window.PlanetSectionUi = {
ensurePlanetSection,
selectByPlanetId
};
})();