/* ui-zodiac.js β Zodiac sign browser section */
(function () {
"use strict";
const zodiacReferenceBuilders = window.ZodiacReferenceBuilders || {};
if (
typeof zodiacReferenceBuilders.buildCubeSignPlacements !== "function"
|| typeof zodiacReferenceBuilders.buildMonthReferencesBySign !== "function"
|| typeof zodiacReferenceBuilders.cubePlacementLabel !== "function"
|| typeof zodiacReferenceBuilders.formatDateRange !== "function"
) {
throw new Error("ZodiacReferenceBuilders module must load before ui-zodiac.js");
}
const ELEMENT_STYLE = {
fire: { emoji: "π₯", badge: "zod-badge--fire", label: "Fire" },
earth: { emoji: "π", badge: "zod-badge--earth", label: "Earth" },
air: { emoji: "π¨", badge: "zod-badge--air", label: "Air" },
water: { emoji: "π§", badge: "zod-badge--water", label: "Water" }
};
const PLANET_SYMBOLS = {
saturn: "βοΈ", jupiter: "βοΈ", mars: "βοΈ", sol: "βοΈ",
venus: "βοΈ", mercury: "βΏοΈ", luna: "βΎοΈ"
};
const state = {
initialized: false,
entries: [],
filteredEntries: [],
selectedId: null,
searchQuery: "",
kabPaths: [],
decansBySign: {},
monthRefsBySignId: new Map(),
cubePlacementBySignId: new Map()
};
// ββ Elements ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function getElements() {
return {
listEl: document.getElementById("zodiac-sign-list"),
countEl: document.getElementById("zodiac-sign-count"),
searchEl: document.getElementById("zodiac-search-input"),
searchClearEl: document.getElementById("zodiac-search-clear"),
detailNameEl: document.getElementById("zodiac-detail-name"),
detailSubEl: document.getElementById("zodiac-detail-sub"),
detailBodyEl: document.getElementById("zodiac-detail-body")
};
}
// ββ Normalise βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function norm(s) {
return String(s || "").toLowerCase().replace(/[^a-z0-9 ]/g, "").trim();
}
function cap(s) {
return String(s || "").charAt(0).toUpperCase() + String(s || "").slice(1);
}
function buildSearchText(sign) {
return norm([
sign.name?.en, sign.meaning?.en, sign.elementId, sign.quadruplicity,
sign.planetId, sign.id
].join(" "));
}
function formatDateRange(rulesFrom) {
return zodiacReferenceBuilders.formatDateRange(rulesFrom);
}
function buildMonthReferencesBySign(referenceData) {
return zodiacReferenceBuilders.buildMonthReferencesBySign(referenceData);
}
function buildCubeSignPlacements(magickDataset) {
return zodiacReferenceBuilders.buildCubeSignPlacements(magickDataset);
}
function cubePlacementLabel(placement) {
return zodiacReferenceBuilders.cubePlacementLabel(placement);
}
// ββ List ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function applyFilter() {
const q = norm(state.searchQuery);
state.filteredEntries = q
? state.entries.filter((s) => buildSearchText(s).includes(q))
: [...state.entries];
}
function renderList(els) {
if (!els.listEl) return;
els.listEl.innerHTML = "";
state.filteredEntries.forEach((sign) => {
const active = sign.id === state.selectedId;
const el = document.createElement("div");
el.className = "planet-list-item" + (active ? " is-selected" : "");
el.setAttribute("role", "option");
el.setAttribute("aria-selected", active ? "true" : "false");
el.dataset.id = sign.id;
const elemStyle = ELEMENT_STYLE[sign.elementId] || {};
el.innerHTML = `
${sign.symbol || "?"}
${sign.name?.en || sign.id}
${elemStyle.emoji || ""}
${cap(sign.elementId)} Β· ${cap(sign.quadruplicity)} Β· ${cap(sign.planetId)}
`;
el.addEventListener("click", () => { selectById(sign.id, els); });
els.listEl.appendChild(el);
});
if (els.countEl) {
els.countEl.textContent = state.searchQuery
? `${state.filteredEntries.length} of ${state.entries.length} signs`
: `${state.entries.length} signs`;
}
}
// ββ Detail ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function renderDetail(sign, els) {
if (!els.detailNameEl) return;
const elemStyle = ELEMENT_STYLE[sign.elementId] || {};
const polarity = ["fire", "air"].includes(sign.elementId) ? "Masculine / Positive" : "Feminine / Negative";
const kabPath = state.kabPaths.find(
(p) => p.astrology?.type === "zodiac" &&
p.astrology?.name?.toLowerCase() === sign.id
);
const decans = state.decansBySign[sign.id] || [];
const monthRefs = state.monthRefsBySignId.get(String(sign.id || "").toLowerCase()) || [];
const cubePlacement = state.cubePlacementBySignId.get(String(sign.id || "").toLowerCase()) || null;
// Heading
els.detailNameEl.textContent = sign.symbol || sign.id;
els.detailSubEl.textContent = `${sign.name?.en || ""} β ${sign.meaning?.en || ""}`;
const sections = [];
// ββ Sign Details ββββββββββββββββββββββββββββββββββββββββββββββββββ
const elemBadge = `${elemStyle.emoji || ""} ${cap(sign.elementId)}`;
const quadBadge = `${cap(sign.quadruplicity)}`;
sections.push(``);
// ββ Ruling Planet βββββββββββββββββββββββββββββββββββββββββββββββββ
const planetSym = PLANET_SYMBOLS[sign.planetId] || "";
sections.push(``);
if (cubePlacement) {
sections.push(``);
}
// ββ Kabbalah Path + Trump βββββββββββββββββββββββββββββββββββββββββ
if (kabPath) {
const hl = kabPath.hebrewLetter || {};
sections.push(``);
}
// ββ Decans & Minor Arcana βββββββββββββββββββββββββββββββββββββββββ
if (decans.length) {
const decanRows = decans.map((d) => {
const ord = ["1st","2nd","3rd"][d.index - 1] || d.index;
const sym = PLANET_SYMBOLS[d.rulerPlanetId] || "";
return `
${ord}
${sym} ${cap(d.rulerPlanetId)}
`;
}).join("");
sections.push(``);
}
if (monthRefs.length) {
const monthButtons = monthRefs.map((month) =>
``
).join("");
sections.push(``);
}
// ββ Kabbalah extras βββββββββββββββββββββββββββββββββββββββββββββββ
if (sign.tribeOfIsraelId || sign.tetragrammatonPermutation) {
sections.push(``);
}
els.detailBodyEl.innerHTML = `${sections.join("")}
`;
// Attach button listeners
els.detailBodyEl.querySelectorAll("[data-nav]").forEach((btn) => {
btn.addEventListener("click", () => {
const nav = btn.dataset.nav;
if (nav === "planet") {
document.dispatchEvent(new CustomEvent("nav:planet", {
detail: { planetId: btn.dataset.planetId }
}));
} else if (nav === "kab-path") {
document.dispatchEvent(new CustomEvent("tarot:view-kab-path", {
detail: { pathNumber: Number(btn.dataset.pathNumber) }
}));
} else if (nav === "trump") {
document.dispatchEvent(new CustomEvent("kab:view-trump", {
detail: { trumpNumber: Number(btn.dataset.trumpNumber) }
}));
} else if (nav === "tarot-card") {
document.dispatchEvent(new CustomEvent("nav:tarot-trump", {
detail: { cardName: btn.dataset.cardName }
}));
} else if (nav === "calendar-month") {
document.dispatchEvent(new CustomEvent("nav:calendar-month", {
detail: { monthId: btn.dataset.monthId }
}));
} else if (nav === "cube-sign") {
document.dispatchEvent(new CustomEvent("nav:cube", {
detail: {
signId: btn.dataset.signId,
wallId: btn.dataset.wallId,
edgeId: btn.dataset.edgeId
}
}));
}
});
});
}
function resetDetail(els) {
if (els.detailNameEl) els.detailNameEl.textContent = "--";
if (els.detailSubEl) els.detailSubEl.textContent = "Select a sign to explore";
if (els.detailBodyEl) els.detailBodyEl.innerHTML = "";
}
// ββ Selection βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function selectById(id, els) {
const sign = state.entries.find((s) => s.id === id);
if (!sign) return;
state.selectedId = id;
renderList(els);
renderDetail(sign, els);
}
// ββ Public select (for incoming navigation) βββββββββββββββββββββββββββ
function selectBySignId(signId) {
const els = getElements();
if (!state.initialized) return;
const sign = state.entries.find((s) => s.id === signId);
if (sign) selectById(signId, els);
}
// ββ Init βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function ensureZodiacSection(referenceData, magickDataset) {
state.monthRefsBySignId = buildMonthReferencesBySign(referenceData);
state.cubePlacementBySignId = buildCubeSignPlacements(magickDataset);
if (state.initialized) {
const els = getElements();
const current = state.entries.find((entry) => entry.id === state.selectedId);
if (current) {
renderDetail(current, els);
}
return;
}
state.initialized = true;
const zodiacObj = magickDataset?.grouped?.astrology?.zodiac || {};
state.entries = Object.values(zodiacObj).sort((a, b) => (a.no || 0) - (b.no || 0));
const kabTree = magickDataset?.grouped?.kabbalah?.["kabbalah-tree"];
state.kabPaths = Array.isArray(kabTree?.paths) ? kabTree.paths : [];
state.decansBySign = referenceData?.decansBySign || {};
const els = getElements();
applyFilter();
renderList(els);
if (state.entries.length > 0) {
selectById(state.entries[0].id, els);
}
// Search
if (els.searchEl) {
els.searchEl.addEventListener("input", () => {
state.searchQuery = els.searchEl.value;
if (els.searchClearEl) els.searchClearEl.disabled = !state.searchQuery;
applyFilter();
renderList(els);
if (!state.filteredEntries.some((s) => s.id === state.selectedId)) {
if (state.filteredEntries.length > 0) {
selectById(state.filteredEntries[0].id, els);
} else {
state.selectedId = null;
resetDetail(els);
}
}
});
}
if (els.searchClearEl) {
els.searchClearEl.addEventListener("click", () => {
state.searchQuery = "";
if (els.searchEl) els.searchEl.value = "";
els.searchClearEl.disabled = true;
applyFilter();
renderList(els);
if (state.entries.length > 0) selectById(state.entries[0].id, els);
});
}
}
window.ZodiacSectionUi = {
ensureZodiacSection,
selectBySignId
};
})();