Files
sunscope/assets/js/events.js
T
fraxle 85c415d1f5 1.5.7
Profile memory
Comment cleanup
2026-05-18 23:02:41 +01:00

130 lines
4.8 KiB
JavaScript

// ------------------------------------------------------------------------
// events.js - Cosmic & weather event engine for SunScope.
//
// This is the public-facing barrel module. It composes the smaller files
// under ./events/ and exposes the same API as before, so callers
// (app.js, components.js) keep working without changes.
//
// Returns the "active event" (or null) based on:
// 1. PROMO_OVERRIDE - manually set a message for promotions etc.
// 2. Hardcoded cosmic calendar (eclipses, meteor showers, alignments)
// 3. Weather-derived events (stargazing, sunset, heat spike, storm)
//
// To set a promo banner, edit PROMO_OVERRIDE below.
// Set it back to null when done.
//
// Each event object:
// { id, emoji, title, message, color, textColor, type }
//
// type: 'cosmic' | 'weather' | 'promo'
//
// Where to find things:
// Cosmic calendar data ........ ./events/cosmic-calendar.js
// Almanac calendar data ....... ./events/almanac-calendar.js
// Weather check functions ..... ./events/weather-checks.js
// Dynamic message helper ...... ./events/dynamic-message.js
// Lens overlay SVG renderer ... ./events/lens-overlay.js
// ------------------------------------------------------------------------
import { COSMIC_CALENDAR } from './events/cosmic-calendar.js';
import {
checkStargazing,
checkSunset,
checkHeatSpike,
checkStorm,
checkFrost,
} from './events/weather-checks.js';
import { dynamicCosmicMessage } from './events/dynamic-message.js';
// Re-export so callers that imported these from events.js keep working.
export { getUpcomingEvents } from './events/almanac-calendar.js';
export { getLensOverlaySVG } from './events/lens-overlay.js';
// --- PROMO OVERRIDE ------------------------------------------------------
// Set this to show a custom banner regardless of weather or cosmic events.
// Leave as null for automatic event detection.
//
// Example:
// export const PROMO_OVERRIDE = {
// id: 'promo-summer',
// emoji: '--',
// title: 'Summer Sale',
// message: 'SunScope Extra - 20% off this weekend only.',
// color: '#c8922a',
// textColor: '#fff',
// type: 'promo',
// };
//
export const PROMO_OVERRIDE = null;
// --- CELL TAG LOGIC ------------------------------------------------------
// Returns the subset of active events that should show an icon for this row.
export function getCellTagEvents(events, row) {
if (!events || events.length === 0) return [];
return events.filter(ev => {
if (ev.type === 'promo') return false;
if (ev.nightOnly && row.elev >= -5) return false;
if (ev.id === 'heat-spike' && row.elev < 0) return false;
// Sunset/sunrise events: only show icon during the actual twilight window
if (ev.isoRange) {
if (row.iso < ev.isoRange[0] || row.iso > ev.isoRange[1]) return false;
}
return true;
});
}
// --- MAIN EXPORT ---------------------------------------------------------
// Returns ALL active events for the given rows/date as an array.
// Empty array = nothing active.
//
// Order within array:
// 1. PROMO_OVERRIDE (if set, returned alone)
// 2. All matching cosmic calendar events for this date
// 3. All matching weather-derived events
export function getActiveEvents(rows, location) {
// 1. Manual promo override - shown alone, no mixing with other events
if (PROMO_OVERRIDE) return [PROMO_OVERRIDE];
// 2. Cosmic calendar - use the date of the rows being viewed, not today.
const dateStr = (rows && rows.length > 0)
? rows[0].iso.slice(0, 10)
: new Date().toISOString().slice(0, 10);
const cosmicHits = COSMIC_CALENDAR
.filter(ev => dateStr >= ev.start && dateStr <= ev.end)
.map(ev => Object.assign({}, ev, { message: dynamicCosmicMessage(ev, dateStr) }));
// 3. Weather-derived events (all that match, not just first)
const weatherEvents = [];
if (rows && rows.length > 0) {
const checks = [
checkHeatSpike(rows),
checkStorm(rows),
checkFrost(rows),
checkSunset(rows, location),
checkStargazing(rows),
];
checks.forEach(ev => { if (ev) weatherEvents.push(ev); });
}
return [...cosmicHits, ...weatherEvents];
}
// --- LENS EVENT PICKER ---------------------------------------------------
// From an array of active events, returns the single one closest to its peak
// (most "intense"). Weather events without a peak use today's date.
// Returns null if events array is empty.
export function getLensEvent(events) {
if (!events || events.length === 0) return null;
if (events.length === 1) return events[0];
const today = new Date().toISOString().slice(0, 10);
return events.reduce((best, ev) => {
const peakB = best.peak || today;
const peakE = ev.peak || today;
const distB = Math.abs(new Date(today) - new Date(peakB));
const distE = Math.abs(new Date(today) - new Date(peakE));
return distE < distB ? ev : best;
});
}