updated relationship display in cycle details to use inline links instead of alpha nav buttons, and added related cycle links to planet details; added element display to astrology alphabet details; updated decan range display in calendar detail panels; updated tarot card detail script to latest version; added element script to index.html

This commit is contained in:
2026-04-24 04:25:27 -07:00
parent e18ec31cf9
commit 7cdcdb4456
12 changed files with 473 additions and 109 deletions
+17 -5
View File
@@ -258,6 +258,16 @@
return sign?.name?.en || sign?.name || sign?.id || fallback || "Unknown";
}
function formatDegreeRangeLabel(startDegree, endDegree) {
const start = Number(startDegree);
const end = Number(endDegree);
if (!Number.isFinite(start) || !Number.isFinite(end)) {
return "";
}
return `${String(Math.trunc(start)).padStart(2, "0")}°–${String(Math.trunc(end)).padStart(2, "0")}°`;
}
function buildDecanMetadata(decan, sign) {
if (!decan || !sign) {
return null;
@@ -268,9 +278,9 @@
return null;
}
const startDegree = (index - 1) * 10;
const endDegree = startDegree + 10;
const dateRange = buildDecanDateRange(sign, index, decan);
const startDegree = (index - 1) * 10;
const endDegree = startDegree + 9;
const dateRange = buildDecanDateRange(sign, index, decan);
return {
decan,
@@ -282,6 +292,7 @@
startDegree,
endDegree,
dateRange,
degreeRangeLabel: formatDegreeRangeLabel(startDegree, endDegree),
normalizedCardName: normalizeMinorTarotCardName(decan.tarotMinorArcana || "")
};
}
@@ -566,7 +577,7 @@
return;
}
const { startDegree, endDegree, dateRange, signId: metaSignId, signName, signSymbol, index } = decanMeta;
const { startDegree, endDegree, dateRange, degreeRangeLabel, signId: metaSignId, signName, signSymbol, index } = decanMeta;
const ruler = planets[decan.rulerPlanetId] || null;
const cardKey = canonicalCardName(cardName);
@@ -591,7 +602,7 @@
createRelation(
"decan",
`${metaSignId}-${index}`,
`Decan ${decan.index}: ${sign.symbol || ""} ${signName} (${startDegree}°–${endDegree}°)${dateRange ? ` · ${dateRange.label}` : ""}`.trim(),
`Decan ${decan.index}: ${sign.symbol || ""} ${signName} (${degreeRangeLabel || `${startDegree}°–${endDegree}°`})${dateRange ? ` · ${dateRange.label}` : ""}`.trim(),
{
signId: metaSignId,
signName,
@@ -599,6 +610,7 @@
index,
startDegree,
endDegree,
degreeRangeLabel: degreeRangeLabel || null,
dateStart: dateRange?.startToken || null,
dateEnd: dateRange?.endToken || null,
dateRange: dateRange?.label || null
+29 -3
View File
@@ -115,7 +115,7 @@
return context.card("Astrology", `
<dl class="alpha-dl">
<dt>Type</dt><dd>Element</dd>
<dt>Element</dt><dd>${elemEmoji[id] || ""} ${context.cap(id)}</dd>
<dt>Element</dt><dd>${elemEmoji[id] || ""} ${context.inlineNavBtn(context.cap(id), "nav:elements", { "element-id": id })}</dd>
</dl>
`);
}
@@ -127,6 +127,32 @@
`);
}
function renderElementOrPlanetValue(value, context) {
const token = context.normalizeId(value);
if (!token) {
return "—";
}
if (context.PLANET_SYMBOLS[token]) {
return `${context.PLANET_SYMBOLS[token]} ${context.inlineNavBtn(context.cap(token), "nav:planet", { "planet-id": token })}`;
}
if (["air", "water", "fire", "earth"].includes(token)) {
return context.inlineNavBtn(context.cap(token), "nav:elements", { "element-id": token });
}
return value || "—";
}
function renderTarotValue(value, context) {
const label = String(value || "").trim();
if (!label) {
return "—";
}
return context.inlineNavBtn(label, "nav:tarot-trump", { "card-name": label });
}
function renderHebrewDualityCard(letter, context) {
const duality = context.HEBREW_DOUBLE_DUALITY[context.normalizeId(letter?.hebrewLetterId)];
if (!duality) {
@@ -553,8 +579,8 @@
<dt>Name</dt><dd>${letter.title}</dd>
<dt>English Letters</dt><dd>${englishRefs.join(" / ") || "—"}</dd>
<dt>Transliteration</dt><dd>${letter.transliteration || "—"}</dd>
<dt>Element / Planet</dt><dd>${letter.elementOrPlanet || "—"}</dd>
<dt>Tarot</dt><dd>${letter.tarot || "—"}</dd>
<dt>Element / Planet</dt><dd>${renderElementOrPlanetValue(letter.elementOrPlanet, context)}</dd>
<dt>Tarot</dt><dd>${renderTarotValue(letter.tarot, context)}</dd>
<dt>Numerology</dt><dd>${letter.numerology || "—"}</dd>
<dt>Glyph Source</dt><dd>API asset: img/enochian (sourced from dCode set)</dd>
<dt>Position</dt><dd>#${letter.index} of 21</dd>
+11 -1
View File
@@ -1,6 +1,15 @@
(function () {
"use strict";
function formatDegreeLabel(value) {
const numeric = Number(value);
if (!Number.isFinite(numeric)) {
return "--";
}
return String(Math.trunc(numeric)).padStart(2, "0");
}
function buildDecanWindow(context, sign, decanIndex) {
const { buildSignDateBounds, addDays, formatDateLabel } = context;
const bounds = buildSignDateBounds(sign);
@@ -78,7 +87,7 @@
seen.add(key);
const startDegree = (Number(decan.index) - 1) * 10;
const endDegree = startDegree + 10;
const endDegree = startDegree + 9;
const signName = sign?.name?.en || sign?.name || signId;
rows.push({
@@ -89,6 +98,7 @@
decanIndex: Number(decan.index),
startDegree,
endDegree,
degreeRangeLabel: `${formatDegreeLabel(startDegree)}°–${formatDegreeLabel(endDegree)}°`,
startTime: window.start.getTime(),
endTime: window.end.getTime(),
startMonth: window.start.getMonth() + 1,
+35 -7
View File
@@ -27,6 +27,26 @@
return `<button class="${className}" data-nav="${nav}" ${dataAttrs}>${label}</button>`;
}
function buildSignInlineButton(signId, signName, signSymbol) {
if (!signId) {
return `${signSymbol ? `${signSymbol} ` : ""}${signName || "--"}`.trim();
}
const label = `${signSymbol ? `${signSymbol} ` : ""}${signName || signId}`.trim();
return inlineNavButton(label, "zodiac", { "sign-id": signId });
}
function buildHebrewInlineButton(hebrewLetterId, label) {
if (!hebrewLetterId) {
return label || "--";
}
return inlineNavButton(label || hebrewLetterId, "alphabet", {
alphabet: "hebrew",
"hebrew-letter-id": hebrewLetterId
});
}
function buildMajorArcanaRowsForMonth(context) {
const { month, api, getState } = context;
const currentState = getState();
@@ -142,6 +162,8 @@
? `${row.hebrewLetterChar ? `${row.hebrewLetterChar} ` : ""}${row.hebrewLetterName || row.hebrewLetterId}`
: "--";
const displayCardName = api.getDisplayTarotName(row.cardName, row.trumpNumber);
const signLabel = buildSignInlineButton(row.signId, row.signName, row.signSymbol);
const hebrewLabel = buildHebrewInlineButton(row.hebrewLetterId, label);
return `
<div class="cal-item-row">
@@ -149,11 +171,12 @@
<span class="cal-item-name">${displayCardName}${row.trumpNumber != null ? ` · Trump ${row.trumpNumber}` : ""}</span>
<span class="planet-list-meta">${row.rangeLabel}</span>
</div>
<div class="planet-list-meta">${row.signSymbol} ${row.signName} · Hebrew: ${label}</div>
<div class="planet-list-meta">${signLabel} · Hebrew: ${hebrewLabel}</div>
<div class="planet-text detail-inline-value">
Days ${inlineNavButton(row.rangeLabel, "calendar-day-range", { "range-start": row.dayStart, "range-end": row.dayEnd })}
· Tarot ${inlineNavButton(displayCardName, "tarot-card", { "card-name": row.cardName, "trump-number": row.trumpNumber ?? "" })}
${row.hebrewLetterId ? ` · Hebrew ${inlineNavButton(label, "alphabet", { alphabet: "hebrew", "hebrew-letter-id": row.hebrewLetterId })}` : ""}
${row.hebrewLetterId ? ` · Hebrew ${hebrewLabel}` : ""}
${row.signId ? ` · Zodiac ${signLabel}` : ""}
</div>
</div>
`;
@@ -202,13 +225,14 @@
const list = rows.map((row) => {
const displayCardName = api.getDisplayTarotName(row.cardName);
const signLabel = buildSignInlineButton(row.signId, row.signName, row.signSymbol);
return `
<div class="cal-item-row">
<div class="cal-item-head">
<span class="cal-item-name">${row.signSymbol} ${row.signName} · Decan ${row.decanIndex}</span>
<span class="planet-list-meta">${row.startDegree}°–${row.endDegree}° · ${row.dateRange}</span>
<span class="cal-item-name">${signLabel} · Decan ${row.decanIndex}</span>
<span class="planet-list-meta">${row.degreeRangeLabel || `${row.startDegree}°–${row.endDegree}°`} · ${row.dateRange}</span>
</div>
<div class="planet-text detail-inline-value">Tarot ${inlineNavButton(displayCardName, "tarot-card", { "card-name": row.cardName })}</div>
<div class="planet-text detail-inline-value">Tarot ${inlineNavButton(displayCardName, "tarot-card", { "card-name": row.cardName })} · Zodiac ${signLabel}</div>
</div>
`;
}).join("");
@@ -291,6 +315,10 @@
const { month, api, getState, buildAssociationButtons, renderHolidaysCard } = context;
const currentState = getState();
const gregorianStartDate = api.getGregorianReferenceDateForCalendarMonth(month);
const zodiacSignId = month?.associations?.zodiacSignId || findSignIdByAstrologyName(month?.zodiacSign, context);
const zodiacLabel = buildSignInlineButton(zodiacSignId, api.cap(month.zodiacSign) || "--", "");
const hebrewLetterId = month?.associations?.hebrewLetterId || String(month?.hebrewLetterId || "").trim();
const hebrewLabel = buildHebrewInlineButton(hebrewLetterId, month.hebrewLetter || "--");
const factsRows = [
["Hebrew Name", month.nativeName || "--"],
["Month Order", month.leapYearOnly ? `${month.order} (leap year only)` : String(month.order)],
@@ -298,10 +326,10 @@
["Month Start (Gregorian)", api.formatGregorianReferenceDate(gregorianStartDate)],
["Days", month.daysVariant ? `${month.days}${month.daysVariant} (varies)` : String(month.days || "--")],
["Season", month.season || "--"],
["Zodiac Sign", api.cap(month.zodiacSign) || "--"],
["Zodiac Sign", zodiacLabel],
["Tribe of Israel", month.tribe || "--"],
["Sense", month.sense || "--"],
["Hebrew Letter", month.hebrewLetter || "--"]
["Hebrew Letter", hebrewLabel]
].map(([dt, dd]) => `<dt>${dt}</dt><dd>${dd}</dd>`).join("");
const monthOrder = Number(month?.order);
+91 -21
View File
@@ -104,6 +104,71 @@
return list;
}
function createAstrologyValue(astrology, context) {
const type = toDisplayText(astrology?.type).toLowerCase();
const name = toDisplayText(astrology?.name);
if (!name) {
return "";
}
if (type === "planet") {
return createInlineEventLink(name, "nav:planet", {
planetId: context.normalizeId(name)
});
}
if (type === "zodiac") {
return createInlineEventLink(name, "nav:zodiac", {
signId: context.normalizeId(name)
});
}
return name;
}
function createElementValue(elementName, context) {
const label = toDisplayText(elementName);
const elementId = context.normalizeId(label);
if (!elementId) {
return label;
}
return createInlineEventLink(label, "nav:elements", { elementId });
}
function appendPathAssociationRows(rows, pathEntry, context) {
if (!Array.isArray(rows) || !pathEntry) {
return;
}
const astrologyValue = createAstrologyValue(pathEntry?.astrology, context);
if (astrologyValue) {
rows.push({ label: "Astrology", value: astrologyValue });
}
const tarotCard = toDisplayText(pathEntry?.tarot?.card);
const tarotTrumpNumber = context.toFiniteNumber(pathEntry?.tarot?.trumpNumber);
if (tarotCard || tarotTrumpNumber != null) {
rows.push({
label: "Tarot",
value: createInlineEventLink(tarotCard || `Trump ${tarotTrumpNumber}`, "nav:tarot-trump", {
cardName: tarotCard,
trumpNumber: tarotTrumpNumber
})
});
}
const pathNo = context.toFiniteNumber(pathEntry?.pathNumber);
if (pathNo != null) {
rows.push({
label: "Path",
value: createInlineEventLink(`Path ${pathNo}`, "nav:kabbalah-path", { pathNo })
});
}
}
function createNavButton(label, eventName, detail) {
const button = document.createElement("button");
button.type = "button";
@@ -155,7 +220,7 @@
})
: centerLetterText
},
{ label: "Element", value: center?.element }
{ label: "Element", value: createElementValue(center?.element, context) || center?.element }
];
if (centerTarotCard || centerTrumpNo != null) {
@@ -225,9 +290,8 @@
const pathNo = toFiniteNumber(connectorPath?.pathNumber);
const tarotCard = toDisplayText(connectorPath?.tarot?.card);
const tarotTrumpNumber = toFiniteNumber(connectorPath?.tarot?.trumpNumber);
const astrologyType = toDisplayText(connectorPath?.astrology?.type);
const astrologyName = toDisplayText(connectorPath?.astrology?.name);
const astrologySummary = [astrologyType, astrologyName].filter(Boolean).join(": ");
const astrologySummary = createAstrologyValue(connectorPath?.astrology, context)
|| [toDisplayText(connectorPath?.astrology?.type), toDisplayText(connectorPath?.astrology?.name)].filter(Boolean).join(": ");
elements.detailNameEl.textContent = connector?.name || "Mother Connector";
elements.detailSubEl.textContent = ["Mother Letter", letterText].filter(Boolean).join(" · ") || "Mother Letter";
@@ -329,9 +393,8 @@
const edgeLetterId = getEdgeLetterId(selectedEdge);
const edgeLetter = getEdgeLetter(selectedEdge);
const edgePath = getEdgePathEntry(selectedEdge);
const astrologyType = toDisplayText(edgePath?.astrology?.type);
const astrologyName = toDisplayText(edgePath?.astrology?.name);
const astrologySymbol = getEdgeAstrologySymbol(selectedEdge);
const astrologyName = toDisplayText(edgePath?.astrology?.name);
const astrologyText = astrologySymbol && astrologyName
? `${astrologySymbol} ${astrologyName}`
: astrologySymbol || astrologyName;
@@ -361,12 +424,7 @@
},
{
label: "Astrology",
value: astrologyType === "zodiac" && astrologyName
? createInlineValue([
astrologySymbol ? `${astrologySymbol} ` : "",
createInlineEventLink(astrologyName, "nav:zodiac", { signId: normalizeId(astrologyName) })
])
: astrologyText
value: createAstrologyValue(edgePath?.astrology, context) || astrologyText
}
];
@@ -420,6 +478,7 @@
getWallEdgeDirections,
getWallFaceLetterId,
getWallFaceLetter,
getPathEntryByLetterId,
getHebrewLetterName,
getEdgeLetter,
localDirectionOrder,
@@ -458,6 +517,7 @@
bodyEl.innerHTML = "";
const wallAssociations = wall.associations || {};
const faceLetterPath = getPathEntryByLetterId(wallFaceLetterId);
const wallFaceLetterName = getHebrewLetterName(wallFaceLetterId) || toDisplayText(wallFaceLetterId);
const faceLetterLabel = [wallFaceLetter, wallFaceLetterName].filter(Boolean).join(" ");
const wallRows = [
@@ -478,7 +538,7 @@
})
: wallFaceLetterText
},
{ label: "Element", value: wall.element },
{ label: "Element", value: createElementValue(wall.element, context) || wall.element },
{
label: "Planet",
value: wallAssociations.planetId
@@ -497,6 +557,22 @@
}
];
if (faceLetterPath) {
appendPathAssociationRows(wallRows, faceLetterPath, context);
} else {
const directTarotCard = toDisplayText(wallAssociations?.tarotCard || wall?.tarotCard);
const directTrumpNumber = toFiniteNumber(wallAssociations?.tarotTrumpNumber);
if (directTarotCard || directTrumpNumber != null) {
wallRows.push({
label: "Tarot",
value: createInlineEventLink(directTarotCard || `Trump ${directTrumpNumber}`, "nav:tarot-trump", {
cardName: directTarotCard,
trumpNumber: directTrumpNumber
})
});
}
}
bodyEl.appendChild(createMetaCard("Wall Details", createDetailList(wallRows)));
if (Array.isArray(wall.keywords) && wall.keywords.length) {
@@ -612,9 +688,8 @@
? [edgeLetter, edgeLetterName].filter(Boolean).join(" ")
: "";
const edgePath = getEdgePathEntry(edge);
const astrologyType = toDisplayText(edgePath?.astrology?.type);
const astrologyName = toDisplayText(edgePath?.astrology?.name);
const astrologySymbol = getEdgeAstrologySymbol(edge);
const astrologyName = toDisplayText(edgePath?.astrology?.name);
const astrologyText = astrologySymbol && astrologyName
? `${astrologySymbol} ${astrologyName}`
: astrologySymbol || astrologyName;
@@ -655,12 +730,7 @@
},
{
label: "Astrology",
value: astrologyType === "zodiac" && astrologyName
? createInlineValue([
astrologySymbol ? `${astrologySymbol} ` : "",
createInlineEventLink(astrologyName, "nav:zodiac", { signId: normalizeId(astrologyName) })
])
: astrologyText
value: createAstrologyValue(edgePath?.astrology, context) || astrologyText
}
];
+10
View File
@@ -735,6 +735,15 @@
return state.kabbalahPathsByLetterId.get(hebrewLetterId) || null;
}
function getPathEntryByLetterId(letterId) {
const normalizedLetterId = normalizeLetterKey(letterId);
if (!normalizedLetterId) {
return null;
}
return state.kabbalahPathsByLetterId.get(normalizedLetterId) || null;
}
const cubeMathUi = cubeMathHelpers.createCubeMathHelpers({
state,
CUBE_VERTICES,
@@ -940,6 +949,7 @@
getWallEdgeDirections,
getConnectorById,
getConnectorPathEntry,
getPathEntryByLetterId,
getCubeCenterData,
getCenterLetterId,
getCenterLetterSymbol,
+56 -10
View File
@@ -90,6 +90,18 @@
.join(" ");
}
function resolveHebrewLetterId(letterName) {
const token = normalize(letterName).replace(/[^a-z]/g, "");
if (!token) {
return "";
}
if (token === "yod") return "yod";
if (token === "vav") return "vav";
if (token === "heh") return "he";
return token;
}
function appendInlineParts(target, parts) {
(Array.isArray(parts) ? parts : []).forEach((part) => {
if (part instanceof Node) {
@@ -171,6 +183,7 @@
const aceCardName = ACE_BY_ELEMENT_ID[id] || "";
const hebrewLetter = HEBREW_LETTER_CHAR_BY_ELEMENT_ID[id] || "";
const hebrewLetterName = HEBREW_LETTER_NAME_BY_ELEMENT_ID[id] || "";
const hebrewLetterId = resolveHebrewLetterId(hebrewLetterName);
const courtRank = COURT_RANK_BY_ELEMENT_ID[id] || "";
const courtCardNames = courtRank
? COURT_SUITS.map((suit) => `${courtRank} of ${suit}`)
@@ -187,6 +200,7 @@
aceCardName,
hebrewLetter,
hebrewLetterName,
hebrewLetterId,
courtRank,
courtCardNames,
smallCardGroups,
@@ -267,16 +281,48 @@
const detailsCard = document.createElement("div");
detailsCard.className = "planet-meta-card";
detailsCard.innerHTML = `
<strong>Element Details</strong>
<dl class="alpha-dl">
<dt>Name</dt><dd>${entry.name}</dd>
<dt>Symbol</dt><dd>${entry.symbol || "--"}</dd>
<dt>Hebrew Letter</dt><dd>${entry.hebrewLetter || "--"}</dd>
<dt>Court Rank</dt><dd>${entry.courtRank || "--"}</dd>
<dt>ID</dt><dd>${entry.id}</dd>
</dl>
`;
const detailsTitle = document.createElement("strong");
detailsTitle.textContent = "Element Details";
const detailsList = document.createElement("dl");
detailsList.className = "alpha-dl";
function appendDetailRow(label, value) {
const term = document.createElement("dt");
term.textContent = label;
const detail = document.createElement("dd");
if (value instanceof Node) {
detail.appendChild(value);
} else {
detail.textContent = String(value || "--");
}
detailsList.append(term, detail);
}
appendDetailRow("Name", entry.name);
appendDetailRow("Symbol", entry.symbol || "--");
const hebrewLetterLabel = `${entry.hebrewLetter || ""} ${entry.hebrewLetterName || ""}`.trim() || "--";
appendDetailRow(
"Hebrew Letter",
entry.hebrewLetterId
? createInlineButton(hebrewLetterLabel, () => {
document.dispatchEvent(new CustomEvent("nav:alphabet", {
detail: {
alphabet: "hebrew",
hebrewLetterId: entry.hebrewLetterId
}
}));
})
: hebrewLetterLabel
);
appendDetailRow("Court Rank", entry.courtRank || "--");
appendDetailRow("ID", entry.id);
detailsCard.append(detailsTitle, detailsList);
const tarotCard = document.createElement("div");
tarotCard.className = "planet-meta-card";
+4 -5
View File
@@ -253,9 +253,9 @@
: (confidence === "exact" ? "exact" : "approximate");
const monthName = monthLabelForCalendar(holiday?.calendarId, holiday?.monthId);
const holidayDate = holiday?.dateText || holiday?.date || holiday?.dateRange || "--";
const sourceMonthLink = holiday?.monthId
? `<div class="planet-text detail-inline-value">Source month ${buildInlineNavButton(`${calendarLabel(holiday?.calendarId)} ${monthName}`, "calendar-month", { "calendar-id": holiday.calendarId || "", "month-id": holiday.monthId })}</div>`
: "";
const sourceMonthValue = holiday?.monthId
? buildInlineNavButton(monthName, "calendar-month", { "calendar-id": holiday.calendarId || "", "month-id": holiday.monthId })
: monthName;
return `
<div class="planet-meta-grid">
@@ -264,7 +264,7 @@
<div class="planet-text">
<dl class="alpha-dl">
<dt>Source Calendar</dt><dd>${calendarLabel(holiday?.calendarId)}</dd>
<dt>Source Month</dt><dd>${monthName}</dd>
<dt>Source Month</dt><dd>${sourceMonthValue}</dd>
<dt>Source Date</dt><dd>${holidayDate}</dd>
<dt>Reference Year</dt><dd>${state.selectedYear}</dd>
<dt>Conversion</dt><dd>${confidenceLabel}</dd>
@@ -284,7 +284,6 @@
<div class="planet-meta-card">
<strong>Description</strong>
<div class="planet-text">${holiday?.description || "--"}</div>
${sourceMonthLink}
</div>
<div class="planet-meta-card">
<strong>Associations</strong>
+42 -3
View File
@@ -95,6 +95,18 @@
return normalizePlanetInfluence(ICHING_PLANET_BY_PLANET_ID[planetId]);
}
function resolvePlanetId(value) {
const normalizedValue = normalizePlanetInfluence(value);
if (!normalizedValue) {
return "";
}
return Object.entries(ICHING_PLANET_BY_PLANET_ID).find(([planetId, label]) => (
normalizePlanetInfluence(planetId) === normalizedValue
|| normalizePlanetInfluence(label) === normalizedValue
))?.[0] || "";
}
function getBinaryPattern(value, expectedLength = 0) {
const raw = String(value || "").trim();
if (!raw) {
@@ -437,6 +449,35 @@
});
}
function renderPlanetInfluence(entry, elements) {
if (!elements?.ichingDetailPlanetEl) {
return;
}
clearChildren(elements.ichingDetailPlanetEl);
const label = entry?.planetaryInfluence || "--";
const planetId = resolvePlanetId(entry?.planetaryInfluence)
|| resolvePlanetId(resolveAssociationPlanetInfluence(entry?.associations));
if (!planetId) {
elements.ichingDetailPlanetEl.textContent = label;
return;
}
const button = document.createElement("button");
button.type = "button";
button.className = "detail-inline-link";
button.textContent = label;
button.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("nav:planet", {
detail: { planetId }
}));
});
elements.ichingDetailPlanetEl.appendChild(button);
}
function renderDetail(entry, elements) {
if (!entry || !elements) {
return;
@@ -482,9 +523,7 @@
}
}
if (elements.ichingDetailPlanetEl) {
elements.ichingDetailPlanetEl.textContent = entry.planetaryInfluence || "--";
}
renderPlanetInfluence(entry, elements);
renderKeywords(entry, elements);
renderTrigrams(entry, elements);
+143 -40
View File
@@ -34,6 +34,19 @@
return String(signId || "").trim().toLowerCase();
}
function formatPaddedDegreeRange(startDegree, endDegree) {
const start = Number(startDegree);
const end = Number(endDegree);
if (!Number.isFinite(start) || !Number.isFinite(end)) {
return "";
}
const normalizedStart = Math.trunc(start);
const normalizedEnd = Math.trunc(end) - (Math.trunc(end) - Math.trunc(start) === 10 ? 1 : 0);
return `(${String(normalizedStart).padStart(2, "0")}°-${String(normalizedEnd).padStart(2, "0")}°)`;
}
function buildDecanSummaryRelations(relations) {
const decanRelations = (relations || []).filter((relation) => relation?.type === "decan");
const signWindowRelations = (relations || []).filter((relation) => relation?.type === "signWindow");
@@ -98,9 +111,7 @@
? String(getDisplayCardName?.(decanCardName) || decanCardName).trim()
: "";
const signLabel = `${signSymbol} ${signName}`.replace(/\s+/g, " ").trim();
const degreeLabel = Number.isFinite(startDegree) && Number.isFinite(endDegree)
? `(${startDegree}°-${endDegree}°)`
: "";
const degreeLabel = formatPaddedDegreeRange(startDegree, endDegree);
const dateLabel = normalizeInlineDateRange(dateRange);
const summaryParts = [
rulerLabel,
@@ -138,9 +149,7 @@
.map((entry) => String(getDisplayCardName?.(entry?.data?.cardName) || entry?.data?.cardName || "").trim())
.filter(Boolean))];
const signLabel = `${signSymbol} ${signName}`.replace(/\s+/g, " ").trim();
const degreeLabel = Number.isFinite(startDegree) && Number.isFinite(endDegree)
? `(${startDegree}°-${endDegree}°)`
: "";
const degreeLabel = formatPaddedDegreeRange(startDegree, endDegree);
const dateLabel = normalizeInlineDateRange(dateRange);
const summaryParts = [
cardLabels.join(", "),
@@ -181,6 +190,47 @@
});
}
function createInlineButton(label, eventName, detail) {
const button = document.createElement("button");
button.type = "button";
button.className = "detail-inline-link";
button.textContent = String(label || "—");
button.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent(eventName, { detail }));
});
return button;
}
function createInlineValue(parts) {
const inline = document.createElement("div");
inline.className = "planet-text detail-inline-value";
(Array.isArray(parts) ? parts : []).forEach((part) => {
if (part instanceof Node) {
inline.appendChild(part);
return;
}
const text = String(part || "");
if (text) {
inline.appendChild(document.createTextNode(text));
}
});
return inline;
}
function resolvePlanetId(value) {
const token = String(value || "").trim().toLowerCase();
if (!token) {
return "";
}
if (token === "sun") return "sol";
if (token === "moon") return "luna";
return token;
}
function collectDetailRelations(card) {
const allRelations = (card.relations || [])
.map((relation, index) => normalizeRelationObject(relation, index))
@@ -447,63 +497,116 @@
const letter = kabPath.hebrewLetter || {};
const fromName = kabTree.sephiroth.find((seph) => seph.number === kabPath.connects.from)?.name || kabPath.connects.from;
const toName = kabTree.sephiroth.find((seph) => seph.number === kabPath.connects.to)?.name || kabPath.connects.to;
const astro = kabPath.astrology ? `${kabPath.astrology.name} (${kabPath.astrology.type})` : "";
const astrologyType = String(kabPath.astrology?.type || "").trim().toLowerCase();
const astrologyName = String(kabPath.astrology?.name || "").trim();
kabPathEl.innerHTML = `
<strong>Kabbalah Tree &#8212; Path ${kabPath.pathNumber}</strong>
<strong>Kabbalah Tree</strong>
<div class="tarot-kab-path-row">
<span class="tarot-kab-letter" title="${letter.transliteration || ""}">${letter.char || ""}</span>
<span class="tarot-kab-meta">
<span class="tarot-kab-name">${letter.transliteration || ""} &mdash; &ldquo;${letter.meaning || ""}&rdquo; &middot; ${letter.letterType || ""}</span>
<span class="tarot-kab-connects">${fromName} &rarr; ${toName}${astro ? " &middot; " + astro : ""}</span>
<span class="tarot-kab-name"></span>
<span class="tarot-kab-connects"></span>
</span>
</div>`;
const btn = document.createElement("button");
btn.type = "button";
btn.className = "detail-inline-link";
btn.textContent = `Path ${kabPath.pathNumber}`;
btn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("tarot:view-kab-path", {
detail: { pathNumber: kabPath.pathNumber }
const pathNameEl = kabPathEl.querySelector(".tarot-kab-name");
if (pathNameEl) {
pathNameEl.appendChild(createInlineButton(`Path ${kabPath.pathNumber}`, "nav:kabbalah-path", {
pathNo: kabPath.pathNumber
}));
});
const inline = document.createElement("div");
inline.className = "planet-text detail-inline-value";
inline.appendChild(document.createTextNode("Kabbalah "));
inline.appendChild(btn);
kabPathEl.appendChild(inline);
pathNameEl.appendChild(document.createTextNode(`${letter.transliteration || ""}`));
if (letter.meaning) {
pathNameEl.appendChild(document.createTextNode(` - \"${letter.meaning}\"`));
}
if (letter.letterType) {
pathNameEl.appendChild(document.createTextNode(` - ${letter.letterType}`));
}
}
const connectsEl = kabPathEl.querySelector(".tarot-kab-connects");
if (connectsEl) {
connectsEl.appendChild(createInlineButton(fromName, "nav:kabbalah-path", {
pathNo: kabPath.connects.from
}));
connectsEl.appendChild(document.createTextNode(" → "));
connectsEl.appendChild(createInlineButton(toName, "nav:kabbalah-path", {
pathNo: kabPath.connects.to
}));
if (astrologyName) {
connectsEl.appendChild(document.createTextNode(" · "));
if (astrologyType === "planet") {
connectsEl.appendChild(createInlineButton(astrologyName, "nav:planet", {
planetId: resolvePlanetId(astrologyName)
}));
} else if (astrologyType === "zodiac") {
connectsEl.appendChild(createInlineButton(astrologyName, "nav:zodiac", {
signId: astrologyName.toLowerCase()
}));
} else {
connectsEl.appendChild(document.createTextNode(`${astrologyName} (${astrologyType || "astrology"})`));
}
}
}
const letterId = String(letter.transliteration || "").trim().toLowerCase();
if (letterId) {
kabPathEl.appendChild(createInlineValue([
"Hebrew ",
createInlineButton(`${letter.char || ""} ${letter.transliteration || letterId}`.trim(), "nav:alphabet", {
alphabet: "hebrew",
hebrewLetterId: letterId
})
]));
}
kabPathEl.hidden = false;
} else if (kabSeph) {
const hebrewName = kabSeph.nameHebrew ? ` (${kabSeph.nameHebrew})` : "";
const translation = kabSeph.translation ? `${kabSeph.translation}` : "";
const planetInfo = kabSeph.planet || "";
const tarotInfo = kabSeph.tarot ? ` · ${kabSeph.tarot}` : "";
const resolvedPlanetId = resolvePlanetId(planetInfo);
kabPathEl.innerHTML = `
<strong>Kabbalah Tree &#8212; Sephirah ${kabSeph.number}</strong>
<strong>Kabbalah Tree</strong>
<div class="tarot-kab-path-row">
<span class="tarot-kab-letter" title="${kabSeph.name || ""}">${kabSeph.number}</span>
<span class="tarot-kab-letter" title="${kabSeph.name || ""}"></span>
<span class="tarot-kab-meta">
<span class="tarot-kab-name">${kabSeph.name || ""}${hebrewName}${translation}</span>
<span class="tarot-kab-connects">${planetInfo}${tarotInfo}</span>
<span class="tarot-kab-name"></span>
<span class="tarot-kab-connects"></span>
</span>
</div>`;
const btn = document.createElement("button");
btn.type = "button";
btn.className = "detail-inline-link";
btn.textContent = `Sephirah ${kabSeph.number}`;
btn.addEventListener("click", () => {
document.dispatchEvent(new CustomEvent("tarot:view-kab-path", {
detail: { pathNumber: kabSeph.number }
const sephirahLetterEl = kabPathEl.querySelector(".tarot-kab-letter");
if (sephirahLetterEl) {
sephirahLetterEl.appendChild(createInlineButton(`${kabSeph.number}`, "nav:kabbalah-path", {
pathNo: kabSeph.number
}));
});
const inline = document.createElement("div");
inline.className = "planet-text detail-inline-value";
inline.appendChild(document.createTextNode("Kabbalah "));
inline.appendChild(btn);
kabPathEl.appendChild(inline);
}
const sephirahNameEl = kabPathEl.querySelector(".tarot-kab-name");
if (sephirahNameEl) {
sephirahNameEl.appendChild(createInlineButton(`Sephirah ${kabSeph.number}${kabSeph.name || "Sephirah"}${hebrewName}${translation}`, "nav:kabbalah-path", {
pathNo: kabSeph.number
}));
}
const sephirahConnectsEl = kabPathEl.querySelector(".tarot-kab-connects");
if (sephirahConnectsEl) {
if (resolvedPlanetId) {
sephirahConnectsEl.appendChild(createInlineButton(planetInfo, "nav:planet", { planetId: resolvedPlanetId }));
} else if (planetInfo) {
sephirahConnectsEl.appendChild(document.createTextNode(planetInfo));
}
if (tarotInfo) {
if (sephirahConnectsEl.childNodes.length) {
sephirahConnectsEl.appendChild(document.createTextNode(" · "));
}
sephirahConnectsEl.appendChild(document.createTextNode(String(kabSeph.tarot || "")));
}
}
kabPathEl.hidden = false;
} else {
kabPathEl.hidden = true;
+24 -3
View File
@@ -59,6 +59,14 @@
return String(s || "").charAt(0).toUpperCase() + String(s || "").slice(1);
}
function normalizeHebrewLetterId(value) {
return String(value || "")
.trim()
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
function buildSearchText(sign) {
return norm([
sign.name?.en, sign.meaning?.en, sign.elementId, sign.quadruplicity,
@@ -142,7 +150,7 @@
const sections = [];
// ── Sign Details ──────────────────────────────────────────────────
const elemBadge = `<span class="zod-badge ${elemStyle.badge || ""}">${elemStyle.emoji || ""} ${cap(sign.elementId)}</span>`;
const elemBadge = `<button class="detail-inline-link zod-badge ${elemStyle.badge || ""}" data-nav="element" data-element-id="${sign.elementId}">${elemStyle.emoji || ""} ${cap(sign.elementId)}</button>`;
const quadBadge = `<span class="zod-badge zod-badge--quad">${cap(sign.quadruplicity)}</span>`;
sections.push(`<div class="planet-meta-card">
<strong>Sign Details</strong>
@@ -178,13 +186,15 @@
// ── Kabbalah Path + Trump ─────────────────────────────────────────
if (kabPath) {
const hl = kabPath.hebrewLetter || {};
const hebrewLetterId = normalizeHebrewLetterId(hl.transliteration);
const hebrewLetterLabel = hl.transliteration || hl.char || "";
sections.push(`<div class="planet-meta-card">
<strong>Kabbalah & Major Arcana</strong>
<div class="planet-text">
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px">
<span class="zod-hebrew-glyph">${hl.char || ""}</span>
<div>
<div style="font-weight:600">${hl.transliteration || ""} (${hl.meaning || ""})</div>
<div style="font-weight:600">${hebrewLetterId ? `<button class="detail-inline-link" data-nav="alphabet" data-hebrew-letter-id="${hebrewLetterId}">${hebrewLetterLabel}</button>` : hebrewLetterLabel}${hl.meaning ? ` (${hl.meaning})` : ""}</div>
<div class="planet-list-meta">${cap(hl.letterType || "")} letter · <button class="detail-inline-link" data-nav="kab-path" data-path-number="${kabPath.pathNumber}">Path ${kabPath.pathNumber}</button></div>
</div>
</div>
@@ -203,7 +213,7 @@
const sym = PLANET_SYMBOLS[d.rulerPlanetId] || "";
return `<div class="zod-decan-row">
<span class="zod-decan-ord">${ord}</span>
<span class="zod-decan-planet">${sym} ${cap(d.rulerPlanetId)}</span>
<span class="zod-decan-planet">${sym ? `${sym} ` : ""}<button class="detail-inline-link" data-nav="planet" data-planet-id="${d.rulerPlanetId}">${cap(d.rulerPlanetId)}</button></span>
<button class="detail-inline-link" data-nav="tarot-card" data-card-name="${d.tarotMinorArcana}">
${d.tarotMinorArcana}
</button>
@@ -249,6 +259,17 @@
document.dispatchEvent(new CustomEvent("nav:planet", {
detail: { planetId: btn.dataset.planetId }
}));
} else if (nav === "element") {
document.dispatchEvent(new CustomEvent("nav:elements", {
detail: { elementId: btn.dataset.elementId }
}));
} else if (nav === "alphabet") {
document.dispatchEvent(new CustomEvent("nav:alphabet", {
detail: {
alphabet: "hebrew",
hebrewLetterId: btn.dataset.hebrewLetterId
}
}));
} else if (nav === "kab-path") {
document.dispatchEvent(new CustomEvent("tarot:view-kab-path", {
detail: { pathNumber: Number(btn.dataset.pathNumber) }
+11 -11
View File
@@ -1191,47 +1191,47 @@
<script src="app/ui-now-helpers.js?v=20260314-now-planets-grid-01"></script>
<script src="app/ui-now.js?v=20260314-now-planets-grid-01"></script>
<script src="app/ui-natal.js"></script>
<script src="app/tarot-database-builders.js?v=20260402-princess-links-01"></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>
<script src="app/tarot-database.js?v=20260402-princess-links-01"></script>
<script src="app/ui-calendar-dates.js"></script>
<script src="app/ui-calendar-detail-panels.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-calendar-detail-panels.js?v=20260424-association-web-02"></script>
<script src="app/ui-calendar-detail.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-calendar-data.js"></script>
<script src="app/ui-calendar-data.js?v=20260424-decan-ranges-01"></script>
<script src="app/ui-calendar.js"></script>
<script src="app/ui-holidays-data.js"></script>
<script src="app/ui-holidays-render.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-holidays-render.js?v=20260424-association-web-01"></script>
<script src="app/ui-holidays.js"></script>
<script src="app/ui-tarot-card-derivations.js?v=20260307b"></script>
<script src="app/ui-tarot-detail.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-tarot-detail.js?v=20260424-association-web-04"></script>
<script src="app/ui-tarot-relation-display.js?v=20260307b"></script>
<script src="app/ui-tarot.js?v=20260402-frame-lightbox-01"></script>
<script src="app/ui-planets-references.js"></script>
<script src="app/ui-planets.js?v=20260424-detail-inline-links-01"></script>
<script src="app/ui-cycles.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-elements.js?v=20260424-detail-inline-links-01"></script>
<script src="app/ui-elements.js?v=20260424-association-web-01"></script>
<script src="app/ui-audio-notes.js?v=20260314-audio-notes-02"></script>
<script src="app/ui-audio-circle.js?v=20260314-audio-circle-01"></script>
<script src="app/ui-iching-references.js"></script>
<script src="app/ui-iching.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-iching.js?v=20260424-association-web-01"></script>
<script src="app/ui-rosicrucian-cross.js"></script>
<script src="app/ui-kabbalah-detail.js?v=20260424-detail-inline-links-01"></script>
<script src="app/ui-kabbalah-views.js"></script>
<script src="app/ui-kabbalah.js?v=20260312-tree-export-01"></script>
<script src="app/ui-cube-detail.js?v=20260424-detail-inline-links-01"></script>
<script src="app/ui-cube-detail.js?v=20260424-association-web-02"></script>
<script src="app/ui-cube-chassis.js?v=20260424-cube-fixes-01"></script>
<script src="app/ui-cube-math.js"></script>
<script src="app/ui-cube-selection.js?v=20260424-cube-fixes-01"></script>
<script src="app/ui-cube.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-references.js"></script>
<script src="app/ui-alphabet-detail.js?v=20260424-detail-inline-links-02"></script>
<script src="app/ui-alphabet-detail.js?v=20260424-association-web-01"></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>
<script src="app/ui-zodiac-references.js"></script>
<script src="app/ui-zodiac.js?v=20260424-detail-inline-links-01"></script>
<script src="app/ui-zodiac.js?v=20260424-association-web-02"></script>
<script src="app/ui-quiz-bank-builtins-domains.js"></script>
<script src="app/ui-quiz-bank-builtins.js"></script>
<script src="app/ui-quiz-bank.js"></script>