refraction almost completed
This commit is contained in:
329
app/ui-calendar-data.js
Normal file
329
app/ui-calendar-data.js
Normal file
@@ -0,0 +1,329 @@
|
||||
(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
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user