2026-03-07 01:09:00 -08:00
|
|
|
|
(function () {
|
2026-03-07 13:38:13 -08:00
|
|
|
|
"use strict";
|
|
|
|
|
|
|
2026-03-07 01:09:00 -08:00
|
|
|
|
const { getTarotCardDisplayName, getTarotCardSearchAliases } = window.TarotCardImages || {};
|
2026-03-07 13:38:13 -08:00
|
|
|
|
const planetReferenceBuilders = window.PlanetReferenceBuilders || {};
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
typeof planetReferenceBuilders.buildMonthReferencesByPlanet !== "function"
|
|
|
|
|
|
|| typeof planetReferenceBuilders.buildCubePlacementsByPlanet !== "function"
|
|
|
|
|
|
) {
|
|
|
|
|
|
throw new Error("PlanetReferenceBuilders module must load before ui-planets.js");
|
|
|
|
|
|
}
|
2026-03-07 01:09:00 -08:00
|
|
|
|
|
|
|
|
|
|
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 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);
|
2026-03-07 13:38:13 -08:00
|
|
|
|
state.monthRefsByPlanetId = planetReferenceBuilders.buildMonthReferencesByPlanet({
|
|
|
|
|
|
referenceData,
|
|
|
|
|
|
toPlanetId,
|
|
|
|
|
|
normalizePlanetToken
|
|
|
|
|
|
});
|
|
|
|
|
|
state.cubePlacementsByPlanetId = planetReferenceBuilders.buildCubePlacementsByPlanet({
|
|
|
|
|
|
magickDataset,
|
|
|
|
|
|
toPlanetId
|
|
|
|
|
|
});
|
2026-03-07 01:09:00 -08:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
};
|
|
|
|
|
|
})();
|