205 lines
5.6 KiB
JavaScript
205 lines
5.6 KiB
JavaScript
(function () {
|
|
"use strict";
|
|
|
|
let config = {
|
|
calendar: null,
|
|
baseWeekOptions: null,
|
|
defaultSettings: null,
|
|
latEl: null,
|
|
lngEl: null,
|
|
nowElements: null,
|
|
calendarVisualsUi: null,
|
|
homeUi: null,
|
|
onStatus: null,
|
|
services: {},
|
|
ensure: {}
|
|
};
|
|
|
|
let referenceData = null;
|
|
let magickDataset = null;
|
|
let currentGeo = null;
|
|
let nowInterval = null;
|
|
let centeredDayKey = "";
|
|
let renderInProgress = false;
|
|
let currentTimeFormat = "minutes";
|
|
let currentSettings = null;
|
|
|
|
function setStatus(text) {
|
|
config.onStatus?.(text);
|
|
}
|
|
|
|
function getReferenceData() {
|
|
return referenceData;
|
|
}
|
|
|
|
function getMagickDataset() {
|
|
return magickDataset;
|
|
}
|
|
|
|
function getCurrentGeo() {
|
|
return currentGeo;
|
|
}
|
|
|
|
function getCurrentTimeFormat() {
|
|
return currentTimeFormat;
|
|
}
|
|
|
|
function getCurrentSettings() {
|
|
return currentSettings ? { ...currentSettings } : null;
|
|
}
|
|
|
|
function parseGeoInput() {
|
|
const latitude = Number(config.latEl?.value);
|
|
const longitude = Number(config.lngEl?.value);
|
|
|
|
if (Number.isNaN(latitude) || Number.isNaN(longitude)) {
|
|
throw new Error("Latitude/Longitude must be valid numbers.");
|
|
}
|
|
|
|
return { latitude, longitude };
|
|
}
|
|
|
|
function applyCenteredWeekWindow(date) {
|
|
const startDayOfWeek = config.services.getCenteredWeekStartDay?.(date) ?? 0;
|
|
config.calendar?.setOptions?.({
|
|
week: {
|
|
...(config.baseWeekOptions || {}),
|
|
startDayOfWeek
|
|
}
|
|
});
|
|
config.calendarVisualsUi?.applyTimeFormatTemplates?.();
|
|
config.calendar?.changeView?.("week");
|
|
config.calendar?.setDate?.(date);
|
|
}
|
|
|
|
function startNowTicker() {
|
|
if (nowInterval) {
|
|
clearInterval(nowInterval);
|
|
}
|
|
|
|
const tick = async () => {
|
|
if (!referenceData || !currentGeo || renderInProgress) {
|
|
return;
|
|
}
|
|
|
|
const now = new Date();
|
|
config.homeUi?.syncNowPanelTheme?.(now);
|
|
const currentDayKey = config.services.getDateKey?.(now) || "";
|
|
if (currentDayKey !== centeredDayKey) {
|
|
centeredDayKey = currentDayKey;
|
|
void renderWeek();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await config.services.updateNowPanel?.(referenceData, currentGeo, config.nowElements, currentTimeFormat);
|
|
config.calendarVisualsUi?.applyDynamicNowIndicatorVisual?.(now);
|
|
} catch (_error) {
|
|
}
|
|
};
|
|
|
|
void tick();
|
|
nowInterval = setInterval(() => {
|
|
void tick();
|
|
}, 1000);
|
|
}
|
|
|
|
async function renderWeek() {
|
|
if (renderInProgress) {
|
|
return;
|
|
}
|
|
|
|
renderInProgress = true;
|
|
|
|
try {
|
|
currentGeo = parseGeoInput();
|
|
config.homeUi?.syncNowPanelTheme?.(new Date());
|
|
config.homeUi?.syncNowSkyBackground?.(currentGeo);
|
|
|
|
if (!referenceData || !magickDataset) {
|
|
setStatus("Loading planetary, sign and decan tarot correspondences...");
|
|
const [loadedReference, loadedMagick] = await Promise.all([
|
|
referenceData ? Promise.resolve(referenceData) : config.services.loadReferenceData?.(),
|
|
magickDataset
|
|
? Promise.resolve(magickDataset)
|
|
: config.services.loadMagickDataset?.().catch(() => null)
|
|
]);
|
|
|
|
referenceData = loadedReference;
|
|
magickDataset = loadedMagick;
|
|
}
|
|
|
|
config.ensure.ensureTarotSection?.(referenceData, magickDataset);
|
|
config.ensure.ensurePlanetSection?.(referenceData, magickDataset);
|
|
config.ensure.ensureCyclesSection?.(referenceData);
|
|
config.ensure.ensureIChingSection?.(referenceData);
|
|
config.ensure.ensureCalendarSection?.(referenceData, magickDataset);
|
|
config.ensure.ensureHolidaySection?.(referenceData, magickDataset);
|
|
config.ensure.ensureNatalPanel?.(referenceData);
|
|
config.ensure.ensureQuizSection?.(referenceData, magickDataset);
|
|
|
|
const anchorDate = new Date();
|
|
centeredDayKey = config.services.getDateKey?.(anchorDate) || "";
|
|
|
|
applyCenteredWeekWindow(anchorDate);
|
|
|
|
const events = await config.services.buildWeekEvents?.(currentGeo, referenceData, anchorDate) || [];
|
|
config.calendar?.clear?.();
|
|
config.calendar?.createEvents?.(events);
|
|
config.calendarVisualsUi?.applySunRulerGradient?.(anchorDate);
|
|
config.calendarVisualsUi?.updateMonthStrip?.();
|
|
requestAnimationFrame(() => {
|
|
config.calendarVisualsUi?.updateMonthStrip?.();
|
|
});
|
|
|
|
setStatus(`Rendered ${events.length} planetary + tarot events for lat ${currentGeo.latitude}, lng ${currentGeo.longitude}.`);
|
|
startNowTicker();
|
|
} catch (error) {
|
|
setStatus(error?.message || "Failed to render calendar.");
|
|
} finally {
|
|
renderInProgress = false;
|
|
}
|
|
}
|
|
|
|
function applySettings(settings) {
|
|
currentTimeFormat = settings?.timeFormat || "minutes";
|
|
currentSettings = settings ? { ...settings } : { ...(config.defaultSettings || {}) };
|
|
}
|
|
|
|
function init(nextConfig = {}) {
|
|
config = {
|
|
...config,
|
|
...nextConfig,
|
|
services: {
|
|
...(config.services || {}),
|
|
...(nextConfig.services || {})
|
|
},
|
|
ensure: {
|
|
...(config.ensure || {}),
|
|
...(nextConfig.ensure || {})
|
|
}
|
|
};
|
|
|
|
if (!currentSettings) {
|
|
currentSettings = { ...(config.defaultSettings || {}) };
|
|
currentTimeFormat = currentSettings.timeFormat || "minutes";
|
|
}
|
|
|
|
centeredDayKey = config.services.getDateKey?.(new Date()) || centeredDayKey;
|
|
}
|
|
|
|
window.TarotAppRuntime = {
|
|
...(window.TarotAppRuntime || {}),
|
|
init,
|
|
parseGeoInput,
|
|
applyCenteredWeekWindow,
|
|
renderWeek,
|
|
applySettings,
|
|
getReferenceData,
|
|
getMagickDataset,
|
|
getCurrentGeo,
|
|
getCurrentTimeFormat,
|
|
getCurrentSettings
|
|
};
|
|
})();
|