329 lines
10 KiB
JavaScript
329 lines
10 KiB
JavaScript
(function () {
|
||
"use strict";
|
||
|
||
function buildDecanWindow(context, sign, decanIndex) {
|
||
const { buildSignDateBounds, addDays, formatDateLabel } = context;
|
||
const bounds = buildSignDateBounds(sign);
|
||
const index = Number(decanIndex);
|
||
if (!bounds || !Number.isFinite(index)) {
|
||
return null;
|
||
}
|
||
|
||
const start = addDays(bounds.start, (index - 1) * 10);
|
||
const nominalEnd = addDays(start, 9);
|
||
const end = nominalEnd.getTime() > bounds.end.getTime() ? bounds.end : nominalEnd;
|
||
|
||
return {
|
||
start,
|
||
end,
|
||
label: `${formatDateLabel(start)}–${formatDateLabel(end)}`
|
||
};
|
||
}
|
||
|
||
function listMonthNumbersBetween(start, end) {
|
||
const result = [];
|
||
const seen = new Set();
|
||
const cursor = new Date(start.getFullYear(), start.getMonth(), 1);
|
||
const limit = new Date(end.getFullYear(), end.getMonth(), 1);
|
||
|
||
while (cursor.getTime() <= limit.getTime()) {
|
||
const monthNo = cursor.getMonth() + 1;
|
||
if (!seen.has(monthNo)) {
|
||
seen.add(monthNo);
|
||
result.push(monthNo);
|
||
}
|
||
cursor.setMonth(cursor.getMonth() + 1);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
function buildDecanTarotRowsForMonth(context, month) {
|
||
const { state, normalizeMinorTarotCardName } = context;
|
||
const monthOrder = Number(month?.order);
|
||
if (!Number.isFinite(monthOrder)) {
|
||
return [];
|
||
}
|
||
|
||
const rows = [];
|
||
const seen = new Set();
|
||
const decansBySign = state.referenceData?.decansBySign || {};
|
||
|
||
Object.entries(decansBySign).forEach(([signId, decans]) => {
|
||
const sign = state.signsById.get(signId);
|
||
if (!sign || !Array.isArray(decans)) {
|
||
return;
|
||
}
|
||
|
||
decans.forEach((decan) => {
|
||
const window = buildDecanWindow(context, sign, decan?.index);
|
||
if (!window) {
|
||
return;
|
||
}
|
||
|
||
const monthsTouched = listMonthNumbersBetween(window.start, window.end);
|
||
if (!monthsTouched.includes(monthOrder)) {
|
||
return;
|
||
}
|
||
|
||
const cardName = normalizeMinorTarotCardName(decan?.tarotMinorArcana);
|
||
if (!cardName) {
|
||
return;
|
||
}
|
||
|
||
const key = `${cardName}|${signId}|${decan.index}`;
|
||
if (seen.has(key)) {
|
||
return;
|
||
}
|
||
seen.add(key);
|
||
|
||
const startDegree = (Number(decan.index) - 1) * 10;
|
||
const endDegree = startDegree + 10;
|
||
const signName = sign?.name?.en || sign?.name || signId;
|
||
|
||
rows.push({
|
||
cardName,
|
||
signId,
|
||
signName,
|
||
signSymbol: sign?.symbol || "",
|
||
decanIndex: Number(decan.index),
|
||
startDegree,
|
||
endDegree,
|
||
startTime: window.start.getTime(),
|
||
endTime: window.end.getTime(),
|
||
startMonth: window.start.getMonth() + 1,
|
||
startDay: window.start.getDate(),
|
||
endMonth: window.end.getMonth() + 1,
|
||
endDay: window.end.getDate(),
|
||
dateRange: window.label
|
||
});
|
||
});
|
||
});
|
||
|
||
rows.sort((left, right) => {
|
||
if (left.startTime !== right.startTime) {
|
||
return left.startTime - right.startTime;
|
||
}
|
||
if (left.decanIndex !== right.decanIndex) {
|
||
return left.decanIndex - right.decanIndex;
|
||
}
|
||
return left.cardName.localeCompare(right.cardName);
|
||
});
|
||
|
||
return rows;
|
||
}
|
||
|
||
function getMonthDayLinkRows(context, month) {
|
||
const { state, getDaysInMonth, resolveCalendarDayToGregorian, formatIsoDate } = context;
|
||
const cacheKey = `${state.selectedCalendar}|${state.selectedYear}|${month?.id || ""}`;
|
||
if (state.dayLinksCache.has(cacheKey)) {
|
||
return state.dayLinksCache.get(cacheKey);
|
||
}
|
||
|
||
let dayCount = null;
|
||
if (state.selectedCalendar === "gregorian") {
|
||
dayCount = getDaysInMonth(state.selectedYear, Number(month?.order));
|
||
} else if (state.selectedCalendar === "hebrew" || state.selectedCalendar === "islamic") {
|
||
const baseDays = Number(month?.days);
|
||
const variantDays = Number(month?.daysVariant);
|
||
if (Number.isFinite(baseDays) && Number.isFinite(variantDays)) {
|
||
dayCount = Math.max(Math.trunc(baseDays), Math.trunc(variantDays));
|
||
} else if (Number.isFinite(baseDays)) {
|
||
dayCount = Math.trunc(baseDays);
|
||
} else if (Number.isFinite(variantDays)) {
|
||
dayCount = Math.trunc(variantDays);
|
||
}
|
||
}
|
||
|
||
if (!Number.isFinite(dayCount) || dayCount <= 0) {
|
||
state.dayLinksCache.set(cacheKey, []);
|
||
return [];
|
||
}
|
||
|
||
const rows = [];
|
||
for (let day = 1; day <= dayCount; day += 1) {
|
||
const gregorianDate = resolveCalendarDayToGregorian(month, day);
|
||
rows.push({
|
||
day,
|
||
gregorianDate: formatIsoDate(gregorianDate),
|
||
isResolved: Boolean(gregorianDate && !Number.isNaN(gregorianDate.getTime()))
|
||
});
|
||
}
|
||
|
||
state.dayLinksCache.set(cacheKey, rows);
|
||
return rows;
|
||
}
|
||
|
||
function associationSearchText(context, associations) {
|
||
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 eventSearchText(context, event) {
|
||
const { normalizeSearchValue } = context;
|
||
return normalizeSearchValue([
|
||
event?.name,
|
||
event?.date,
|
||
event?.dateRange,
|
||
event?.description,
|
||
associationSearchText(context, event?.associations)
|
||
].filter(Boolean).join(" "));
|
||
}
|
||
|
||
function holidaySearchText(context, holiday) {
|
||
const { normalizeSearchValue } = context;
|
||
return normalizeSearchValue([
|
||
holiday?.name,
|
||
holiday?.kind,
|
||
holiday?.date,
|
||
holiday?.dateRange,
|
||
holiday?.dateText,
|
||
holiday?.monthDayStart,
|
||
holiday?.calendarId,
|
||
holiday?.description,
|
||
associationSearchText(context, holiday?.associations)
|
||
].filter(Boolean).join(" "));
|
||
}
|
||
|
||
function buildHolidayList(context, month) {
|
||
const { state, normalizeText, resolveHolidayGregorianDate } = context;
|
||
const calendarId = state.selectedCalendar;
|
||
const monthOrder = Number(month?.order);
|
||
|
||
const fromRepo = state.calendarHolidays.filter((holiday) => {
|
||
const holidayCalendarId = normalizeText(holiday?.calendarId).toLowerCase();
|
||
if (holidayCalendarId !== calendarId) {
|
||
return false;
|
||
}
|
||
|
||
const isDirectMonthMatch = normalizeText(holiday?.monthId).toLowerCase() === normalizeText(month?.id).toLowerCase();
|
||
if (isDirectMonthMatch) {
|
||
return true;
|
||
}
|
||
|
||
if (calendarId === "gregorian" && holiday?.dateRule && Number.isFinite(monthOrder)) {
|
||
const computedDate = resolveHolidayGregorianDate(holiday);
|
||
return computedDate instanceof Date
|
||
&& !Number.isNaN(computedDate.getTime())
|
||
&& (computedDate.getMonth() + 1) === Math.trunc(monthOrder);
|
||
}
|
||
|
||
return false;
|
||
});
|
||
|
||
if (fromRepo.length) {
|
||
return [...fromRepo].sort((left, right) => {
|
||
const leftDate = resolveHolidayGregorianDate(left);
|
||
const rightDate = resolveHolidayGregorianDate(right);
|
||
const leftDay = Number.isFinite(Number(left?.day))
|
||
? Number(left.day)
|
||
: ((leftDate instanceof Date && !Number.isNaN(leftDate.getTime())) ? leftDate.getDate() : NaN);
|
||
const rightDay = Number.isFinite(Number(right?.day))
|
||
? Number(right.day)
|
||
: ((rightDate instanceof Date && !Number.isNaN(rightDate.getTime())) ? rightDate.getDate() : NaN);
|
||
if (Number.isFinite(leftDay) && Number.isFinite(rightDay) && leftDay !== rightDay) {
|
||
return leftDay - rightDay;
|
||
}
|
||
return normalizeText(left?.name).localeCompare(normalizeText(right?.name));
|
||
});
|
||
}
|
||
|
||
const seen = new Set();
|
||
const ordered = [];
|
||
|
||
(month?.holidayIds || []).forEach((holidayId) => {
|
||
const holiday = state.holidays.find((item) => item.id === holidayId);
|
||
if (holiday && !seen.has(holiday.id)) {
|
||
seen.add(holiday.id);
|
||
ordered.push(holiday);
|
||
}
|
||
});
|
||
|
||
state.holidays.forEach((holiday) => {
|
||
if (holiday?.monthId === month.id && !seen.has(holiday.id)) {
|
||
seen.add(holiday.id);
|
||
ordered.push(holiday);
|
||
}
|
||
});
|
||
|
||
return ordered;
|
||
}
|
||
|
||
function buildMonthSearchText(context, month) {
|
||
const { state, normalizeSearchValue } = context;
|
||
const monthHolidays = buildHolidayList(context, month);
|
||
const holidayText = monthHolidays.map((holiday) => holidaySearchText(context, holiday)).join(" ");
|
||
|
||
if (state.selectedCalendar === "gregorian") {
|
||
const events = Array.isArray(month?.events) ? month.events : [];
|
||
return normalizeSearchValue([
|
||
month?.name,
|
||
month?.id,
|
||
month?.start,
|
||
month?.end,
|
||
month?.coreTheme,
|
||
month?.seasonNorth,
|
||
month?.seasonSouth,
|
||
associationSearchText(context, month?.associations),
|
||
events.map((event) => eventSearchText(context, event)).join(" "),
|
||
holidayText
|
||
].filter(Boolean).join(" "));
|
||
}
|
||
|
||
const wheelAssocText = month?.associations
|
||
? [
|
||
Array.isArray(month.associations.themes) ? month.associations.themes.join(" ") : "",
|
||
Array.isArray(month.associations.deities) ? month.associations.deities.join(" ") : "",
|
||
month.associations.element,
|
||
month.associations.direction
|
||
].filter(Boolean).join(" ")
|
||
: "";
|
||
|
||
return normalizeSearchValue([
|
||
month?.name,
|
||
month?.id,
|
||
month?.nativeName,
|
||
month?.meaning,
|
||
month?.season,
|
||
month?.description,
|
||
month?.zodiacSign,
|
||
month?.tribe,
|
||
month?.element,
|
||
month?.type,
|
||
month?.date,
|
||
month?.hebrewLetter,
|
||
holidayText,
|
||
wheelAssocText
|
||
].filter(Boolean).join(" "));
|
||
}
|
||
|
||
window.CalendarDataUi = {
|
||
getMonthDayLinkRows,
|
||
buildDecanTarotRowsForMonth,
|
||
associationSearchText,
|
||
eventSearchText,
|
||
holidaySearchText,
|
||
buildHolidayList,
|
||
buildMonthSearchText
|
||
};
|
||
})(); |