mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-12 01:42:20 +02:00
Squash Odysseus development history
This commit is contained in:
@@ -3,7 +3,9 @@
|
||||
// Pure constants + zero-state helpers for the calendar UI.
|
||||
// No DOM, no fetch, no global mutable state — safe to import anywhere.
|
||||
|
||||
export const WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
export const WEEKDAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
export const WEEKDAYS_SUN = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
|
||||
|
||||
export const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June',
|
||||
'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
@@ -63,17 +65,65 @@ export function _calBgImageUrl(c) {
|
||||
return _isCalBgImage(c) ? c.slice(3) : '';
|
||||
}
|
||||
|
||||
// Escape a value for safe embedding inside a single-quoted CSS `url('...')`.
|
||||
// Backslashes MUST be escaped first: otherwise a trailing/embedded `\` in the
|
||||
// (CalDAV-syncable, untrusted) bg-image URL would escape the closing quote we
|
||||
// add for `'` and let the value break out of the string (CodeQL
|
||||
// js/incomplete-sanitization). `"` is percent-encoded for good measure.
|
||||
export function _cssUrlEscape(s) {
|
||||
return String(s == null ? '' : s)
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/'/g, "\\'")
|
||||
.replace(/"/g, '%22');
|
||||
}
|
||||
|
||||
// Returns a value safe to drop into `style="background:..."`. Falls back to
|
||||
// the calendar default for bg-image events in spots where an image would be
|
||||
// too small to render usefully (small grid dots, multi-day bars).
|
||||
export function _calBgCss(c, fallback) {
|
||||
if (_isCalBgImage(c)) {
|
||||
const u = _calBgImageUrl(c);
|
||||
return u ? `center/cover no-repeat url('${u.replace(/'/g, "\\'")}')` : (fallback || 'var(--accent)');
|
||||
return u ? `center/cover no-repeat url('${_cssUrlEscape(u)}')` : (fallback || 'var(--accent)');
|
||||
}
|
||||
return c || fallback || 'var(--accent)';
|
||||
}
|
||||
|
||||
function _hexToRgb(c) {
|
||||
if (typeof c !== 'string') return null;
|
||||
const m = c.trim().match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
|
||||
if (!m) return null;
|
||||
const hex = m[1].length === 3
|
||||
? m[1].split('').map(ch => ch + ch).join('')
|
||||
: m[1];
|
||||
return {
|
||||
r: parseInt(hex.slice(0, 2), 16),
|
||||
g: parseInt(hex.slice(2, 4), 16),
|
||||
b: parseInt(hex.slice(4, 6), 16),
|
||||
};
|
||||
}
|
||||
|
||||
function _relativeLuminance({ r, g, b }) {
|
||||
return [r, g, b].map(v => {
|
||||
const c = v / 255;
|
||||
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
||||
}).reduce((sum, c, i) => sum + c * [0.2126, 0.7152, 0.0722][i], 0);
|
||||
}
|
||||
|
||||
function _contrastRatio(a, b) {
|
||||
const light = Math.max(a, b);
|
||||
const dark = Math.min(a, b);
|
||||
return (light + 0.05) / (dark + 0.05);
|
||||
}
|
||||
|
||||
export function _calReadableTextColor(bg) {
|
||||
const rgb = _hexToRgb(bg);
|
||||
if (!rgb) return 'var(--fg)';
|
||||
const lum = _relativeLuminance(rgb);
|
||||
const white = _contrastRatio(lum, 1);
|
||||
const ink = _contrastRatio(lum, 0.006);
|
||||
return ink >= white ? '#111820' : '#ffffff';
|
||||
}
|
||||
|
||||
// ── date helpers ──
|
||||
|
||||
// `YYYY-MM-DD` string from a Date.
|
||||
@@ -82,13 +132,17 @@ export function _ds(d) {
|
||||
}
|
||||
|
||||
export function _addDays(dateStr, n) {
|
||||
if (typeof dateStr !== 'string' || !dateStr) return '';
|
||||
const d = new Date(dateStr + 'T00:00:00');
|
||||
if (isNaN(d)) return '';
|
||||
d.setDate(d.getDate() + n);
|
||||
return _ds(d);
|
||||
}
|
||||
|
||||
export function _shiftDT(iso, days) {
|
||||
if (typeof iso !== 'string' || !iso) return '';
|
||||
const d = new Date(iso);
|
||||
if (isNaN(d)) return '';
|
||||
d.setDate(d.getDate() + days);
|
||||
return _ds(d) + (iso.length > 10 ? 'T' + iso.slice(11) : '');
|
||||
}
|
||||
@@ -111,7 +165,7 @@ export function _tzOffset() {
|
||||
// bucket by the USER's local date. Without this an event at
|
||||
// "2026-05-13T22:00:00Z" (07:00 May 14 JST) would render on May 13.
|
||||
export function _localDateOf(isoStr) {
|
||||
if (!isoStr) return '';
|
||||
if (typeof isoStr !== 'string' || !isoStr) return '';
|
||||
if (isoStr.length === 10) return isoStr;
|
||||
if (/[Zz]$|[+\-]\d{2}:?\d{2}$/.test(isoStr)) {
|
||||
const d = new Date(isoStr);
|
||||
|
||||
Reference in New Issue
Block a user