refraction almost completed
This commit is contained in:
311
app/ui-holidays-render.js
Normal file
311
app/ui-holidays-render.js
Normal file
@@ -0,0 +1,311 @@
|
||||
/* ui-holidays-render.js - Render/search helpers for the holiday repository */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function planetLabel(planetId, context) {
|
||||
const { state, cap } = context;
|
||||
if (!planetId) {
|
||||
return "Planet";
|
||||
}
|
||||
|
||||
const planet = state.planetsById.get(planetId);
|
||||
if (!planet) {
|
||||
return cap(planetId);
|
||||
}
|
||||
|
||||
return `${planet.symbol || ""} ${planet.name || cap(planetId)}`.trim();
|
||||
}
|
||||
|
||||
function zodiacLabel(signId, context) {
|
||||
const { state, cap } = context;
|
||||
if (!signId) {
|
||||
return "Zodiac";
|
||||
}
|
||||
|
||||
const sign = state.signsById.get(signId);
|
||||
if (!sign) {
|
||||
return cap(signId);
|
||||
}
|
||||
|
||||
return `${sign.symbol || ""} ${sign.name || cap(signId)}`.trim();
|
||||
}
|
||||
|
||||
function godLabel(godId, godName, context) {
|
||||
const { state, cap } = context;
|
||||
if (godName) {
|
||||
return godName;
|
||||
}
|
||||
|
||||
if (!godId) {
|
||||
return "Deity";
|
||||
}
|
||||
|
||||
const god = state.godsById.get(godId);
|
||||
return god?.name || cap(godId);
|
||||
}
|
||||
|
||||
function hebrewLabel(hebrewLetterId, context) {
|
||||
const { state, cap } = context;
|
||||
if (!hebrewLetterId) {
|
||||
return "Hebrew Letter";
|
||||
}
|
||||
|
||||
const letter = state.hebrewById.get(hebrewLetterId);
|
||||
if (!letter) {
|
||||
return cap(hebrewLetterId);
|
||||
}
|
||||
|
||||
return `${letter.char || ""} ${letter.name || cap(hebrewLetterId)}`.trim();
|
||||
}
|
||||
|
||||
function computeDigitalRoot(value) {
|
||||
let current = Math.abs(Math.trunc(Number(value)));
|
||||
if (!Number.isFinite(current)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (current >= 10) {
|
||||
current = String(current)
|
||||
.split("")
|
||||
.reduce((sum, digit) => sum + Number(digit), 0);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
function buildAssociationButtons(associations, context) {
|
||||
const { getDisplayTarotName, resolveTarotTrumpNumber } = context;
|
||||
if (!associations || typeof associations !== "object") {
|
||||
return '<div class="planet-text">--</div>';
|
||||
}
|
||||
|
||||
const buttons = [];
|
||||
|
||||
if (associations.planetId) {
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="planet" data-planet-id="${associations.planetId}">${planetLabel(associations.planetId, context)} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (associations.zodiacSignId) {
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="zodiac" data-sign-id="${associations.zodiacSignId}">${zodiacLabel(associations.zodiacSignId, context)} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (Number.isFinite(Number(associations.numberValue))) {
|
||||
const rawNumber = Math.trunc(Number(associations.numberValue));
|
||||
if (rawNumber >= 0) {
|
||||
const numberValue = computeDigitalRoot(rawNumber);
|
||||
if (numberValue != null) {
|
||||
const label = rawNumber === numberValue
|
||||
? `Number ${numberValue}`
|
||||
: `Number ${numberValue} (from ${rawNumber})`;
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="number" data-number-value="${numberValue}">${label} -></button>`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (associations.tarotCard) {
|
||||
const trumpNumber = resolveTarotTrumpNumber(associations.tarotCard);
|
||||
const explicitTrumpNumber = Number(associations.tarotTrumpNumber);
|
||||
const tarotTrumpNumber = Number.isFinite(explicitTrumpNumber) ? explicitTrumpNumber : trumpNumber;
|
||||
const tarotLabel = getDisplayTarotName(associations.tarotCard, tarotTrumpNumber);
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="tarot-card" data-card-name="${associations.tarotCard}" data-trump-number="${tarotTrumpNumber ?? ""}">${tarotLabel} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (associations.godId || associations.godName) {
|
||||
const label = godLabel(associations.godId, associations.godName, context);
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="god" data-god-id="${associations.godId || ""}" data-god-name="${associations.godName || label}">${label} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (associations.hebrewLetterId) {
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="alphabet" data-alphabet="hebrew" data-hebrew-letter-id="${associations.hebrewLetterId}">${hebrewLabel(associations.hebrewLetterId, context)} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (associations.kabbalahPathNumber != null) {
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="kabbalah" data-path-no="${associations.kabbalahPathNumber}">Path ${associations.kabbalahPathNumber} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (associations.iChingPlanetaryInfluence) {
|
||||
buttons.push(
|
||||
`<button class="alpha-nav-btn" data-nav="iching" data-planetary-influence="${associations.iChingPlanetaryInfluence}">I Ching - ${associations.iChingPlanetaryInfluence} -></button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (!buttons.length) {
|
||||
return '<div class="planet-text">--</div>';
|
||||
}
|
||||
|
||||
return `<div class="alpha-nav-btns">${buttons.join("")}</div>`;
|
||||
}
|
||||
|
||||
function associationSearchText(associations, context) {
|
||||
const { getTarotCardSearchAliases } = context;
|
||||
if (!associations || typeof associations !== "object") {
|
||||
return "";
|
||||
}
|
||||
|
||||
const tarotAliases = associations.tarotCard && typeof getTarotCardSearchAliases === "function"
|
||||
? getTarotCardSearchAliases(associations.tarotCard, { trumpNumber: associations.tarotTrumpNumber })
|
||||
: [];
|
||||
|
||||
return [
|
||||
associations.planetId,
|
||||
associations.zodiacSignId,
|
||||
associations.numberValue,
|
||||
associations.tarotCard,
|
||||
associations.tarotTrumpNumber,
|
||||
...tarotAliases,
|
||||
associations.godId,
|
||||
associations.godName,
|
||||
associations.hebrewLetterId,
|
||||
associations.kabbalahPathNumber,
|
||||
associations.iChingPlanetaryInfluence
|
||||
].filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
function holidaySearchText(holiday, context) {
|
||||
const { normalizeSearchValue } = context;
|
||||
return normalizeSearchValue([
|
||||
holiday?.name,
|
||||
holiday?.kind,
|
||||
holiday?.date,
|
||||
holiday?.dateRange,
|
||||
holiday?.dateText,
|
||||
holiday?.monthDayStart,
|
||||
holiday?.calendarId,
|
||||
holiday?.description,
|
||||
associationSearchText(holiday?.associations, context)
|
||||
].filter(Boolean).join(" "));
|
||||
}
|
||||
|
||||
function renderList(context) {
|
||||
const {
|
||||
elements,
|
||||
state,
|
||||
filterBySource,
|
||||
normalizeSourceFilter,
|
||||
calendarLabel,
|
||||
monthLabelForCalendar,
|
||||
selectByHolidayId
|
||||
} = context;
|
||||
const { listEl, countEl } = elements;
|
||||
if (!listEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
listEl.innerHTML = "";
|
||||
|
||||
state.filteredHolidays.forEach((holiday) => {
|
||||
const isSelected = holiday.id === state.selectedHolidayId;
|
||||
const itemEl = document.createElement("div");
|
||||
itemEl.className = `planet-list-item${isSelected ? " is-selected" : ""}`;
|
||||
itemEl.setAttribute("role", "option");
|
||||
itemEl.setAttribute("aria-selected", isSelected ? "true" : "false");
|
||||
itemEl.dataset.holidayId = holiday.id;
|
||||
|
||||
const sourceCalendar = calendarLabel(holiday.calendarId);
|
||||
const sourceMonth = monthLabelForCalendar(holiday.calendarId, holiday.monthId);
|
||||
const sourceDate = holiday?.dateText || holiday?.date || holiday?.dateRange || "--";
|
||||
|
||||
itemEl.innerHTML = `
|
||||
<div class="planet-list-name">${holiday?.name || holiday?.id || "Holiday"}</div>
|
||||
<div class="planet-list-meta">${sourceCalendar} - ${sourceMonth} - ${sourceDate}</div>
|
||||
`;
|
||||
|
||||
itemEl.addEventListener("click", () => {
|
||||
selectByHolidayId(holiday.id, elements);
|
||||
});
|
||||
|
||||
listEl.appendChild(itemEl);
|
||||
});
|
||||
|
||||
if (countEl) {
|
||||
const sourceFiltered = filterBySource(state.holidays);
|
||||
const activeFilter = normalizeSourceFilter(state.selectedSource);
|
||||
const sourceLabel = activeFilter === "all"
|
||||
? ""
|
||||
: ` (${calendarLabel(activeFilter)})`;
|
||||
countEl.textContent = state.searchQuery
|
||||
? `${state.filteredHolidays.length} of ${sourceFiltered.length} holidays${sourceLabel}`
|
||||
: `${sourceFiltered.length} holidays${sourceLabel}`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderHolidayDetail(holiday, context) {
|
||||
const {
|
||||
state,
|
||||
calendarLabel,
|
||||
monthLabelForCalendar,
|
||||
resolveHolidayGregorianDate,
|
||||
formatGregorianReferenceDate,
|
||||
formatCalendarDateFromGregorian
|
||||
} = context;
|
||||
const gregorianDate = resolveHolidayGregorianDate(holiday);
|
||||
const gregorianRef = formatGregorianReferenceDate(gregorianDate);
|
||||
const hebrewRef = formatCalendarDateFromGregorian(gregorianDate, "hebrew");
|
||||
const islamicRef = formatCalendarDateFromGregorian(gregorianDate, "islamic");
|
||||
const confidence = String(holiday?.conversionConfidence || holiday?.datePrecision || "approximate").toLowerCase();
|
||||
const confidenceLabel = (!(gregorianDate instanceof Date) || Number.isNaN(gregorianDate.getTime()))
|
||||
? "unresolved"
|
||||
: (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="alpha-nav-btns"><button class="alpha-nav-btn" data-nav="calendar-month" data-calendar-id="${holiday.calendarId || ""}" data-month-id="${holiday.monthId}">Open ${calendarLabel(holiday?.calendarId)} ${monthName} -></button></div>`
|
||||
: "";
|
||||
|
||||
return `
|
||||
<div class="planet-meta-grid">
|
||||
<div class="planet-meta-card">
|
||||
<strong>Holiday Facts</strong>
|
||||
<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 Date</dt><dd>${holidayDate}</dd>
|
||||
<dt>Reference Year</dt><dd>${state.selectedYear}</dd>
|
||||
<dt>Conversion</dt><dd>${confidenceLabel}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="planet-meta-card">
|
||||
<strong>Cross-Calendar Dates</strong>
|
||||
<div class="planet-text">
|
||||
<dl class="alpha-dl">
|
||||
<dt>Gregorian</dt><dd>${gregorianRef}</dd>
|
||||
<dt>Hebrew</dt><dd>${hebrewRef}</dd>
|
||||
<dt>Islamic</dt><dd>${islamicRef}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
${buildAssociationButtons(holiday?.associations, context)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
window.HolidayRenderUi = {
|
||||
holidaySearchText,
|
||||
renderList,
|
||||
renderHolidayDetail
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user