246 lines
8.4 KiB
Plaintext
246 lines
8.4 KiB
Plaintext
---
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export interface Props {
|
|
display:
|
|
| "weekday"
|
|
| "weekday-number"
|
|
| "weekday-of"
|
|
| "weekday-number-of"
|
|
| "year"
|
|
| "january-day"
|
|
| "february-day"
|
|
| "if-leap-year";
|
|
prefix?: string;
|
|
suffix?: string;
|
|
offset?: number;
|
|
resultModulo?: number;
|
|
value?: string;
|
|
valueAlt?: string;
|
|
valueClass?: string;
|
|
}
|
|
|
|
const {
|
|
display,
|
|
prefix,
|
|
suffix,
|
|
offset,
|
|
resultModulo,
|
|
valueClass: customClass,
|
|
value,
|
|
valueAlt,
|
|
} = Astro.props;
|
|
---
|
|
|
|
<span class={cn(`doomsday-display doomsday-${display}`)}
|
|
>{prefix}<span
|
|
class={cn("doomsday-value", customClass)}
|
|
data-offset={offset}
|
|
data-resultmodulo={resultModulo}
|
|
data-value={value}
|
|
data-valuealt={valueAlt}><slot /></span
|
|
>{suffix}</span
|
|
>
|
|
|
|
<script>
|
|
function isLeapYear(year: number) {
|
|
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
}
|
|
function optionalModulo(value: number, modulo?: number): number {
|
|
if (modulo === undefined) {
|
|
return value;
|
|
}
|
|
return (value + modulo) % modulo;
|
|
}
|
|
function dateFromValueString(value: string, year: number): Date | null {
|
|
const parts = value.split(".");
|
|
if (parts.length !== 3) {
|
|
return null;
|
|
}
|
|
const [dayStr, monthStr, _] = parts;
|
|
const day = parseInt(dayStr);
|
|
const month = parseInt(monthStr) - 1;
|
|
if (isNaN(day) || isNaN(month) || month < 0 || month > 11) {
|
|
return null;
|
|
}
|
|
const date = new Date(year, month, day);
|
|
return date;
|
|
}
|
|
function handleOffset<T>(
|
|
element: HTMLElement,
|
|
fn: (offset: number, modulo?: number) => T,
|
|
): T {
|
|
const offset = parseInt(element.dataset.offset || "0");
|
|
const resultModulo = element.dataset.resultmodulo
|
|
? parseInt(element.dataset.resultmodulo)
|
|
: undefined;
|
|
return fn(offset, resultModulo);
|
|
}
|
|
const weekdays = [
|
|
"Sunday",
|
|
"Monday",
|
|
"Tuesday",
|
|
"Wednesday",
|
|
"Thursday",
|
|
"Friday",
|
|
"Saturday",
|
|
];
|
|
const currentYearInput = document.getElementById(
|
|
"doomsday-year",
|
|
) as HTMLInputElement | null;
|
|
|
|
let firstUpdate = true;
|
|
const url = new URL(window.location.href);
|
|
const yearParam = url.searchParams.get("year");
|
|
const parsedYearParam = yearParam ? parseInt(yearParam) : null;
|
|
const initialYear =
|
|
parsedYearParam && !isNaN(parsedYearParam) && parsedYearParam >= 1583
|
|
? parsedYearParam
|
|
: new Date().getFullYear();
|
|
function update() {
|
|
const selectedYear =
|
|
!firstUpdate && currentYearInput && currentYearInput.value
|
|
? parseInt(currentYearInput.value)
|
|
: initialYear;
|
|
firstUpdate = false;
|
|
console.log("Selected Year:", selectedYear);
|
|
const isSelectedYearLeap = isLeapYear(selectedYear);
|
|
const febDoomsdayDay = 28 + (isSelectedYearLeap ? 1 : 0);
|
|
|
|
// Display the selected year
|
|
const yearElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-year > .doomsday-value",
|
|
);
|
|
yearElements.forEach((element) => {
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset, modulo) =>
|
|
optionalModulo(selectedYear + offset, modulo).toString(),
|
|
);
|
|
});
|
|
|
|
// Display the doomsday weekday for the selected year
|
|
const doomsdayWeekdayElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-weekday > .doomsday-value",
|
|
);
|
|
const doomsdayDate = new Date(selectedYear, 2, febDoomsdayDay);
|
|
|
|
const doomsdayOfSelectedYear = doomsdayDate.getDay();
|
|
const doomsdayOfSelectedYearInText = weekdays[doomsdayOfSelectedYear];
|
|
|
|
doomsdayWeekdayElements.forEach((element) => {
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset) => {
|
|
if (offset === 0) {
|
|
return doomsdayOfSelectedYearInText;
|
|
} else {
|
|
const adjustedDoomsday = optionalModulo(
|
|
doomsdayOfSelectedYear + offset,
|
|
7,
|
|
);
|
|
return weekdays[adjustedDoomsday];
|
|
}
|
|
},
|
|
);
|
|
});
|
|
|
|
// Display the weekday number of the doomsday for the selected year
|
|
const doomsdayWeekdayNumberElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-weekday-number > .doomsday-value",
|
|
);
|
|
|
|
doomsdayWeekdayNumberElements.forEach((element) => {
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset) =>
|
|
optionalModulo(
|
|
doomsdayOfSelectedYear + offset,
|
|
7,
|
|
).toString(),
|
|
).toString();
|
|
});
|
|
|
|
// Display the doomsday date for January and February
|
|
const janDoomsdayDay = isSelectedYearLeap ? 4 : 3;
|
|
const janDoomsdayDayElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-january-day > .doomsday-value",
|
|
);
|
|
janDoomsdayDayElements.forEach((element) => {
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset, modulo) =>
|
|
optionalModulo(janDoomsdayDay + offset, modulo).toString(),
|
|
);
|
|
});
|
|
const febDoomsdayDayElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-february-day > .doomsday-value",
|
|
);
|
|
febDoomsdayDayElements.forEach((element) => {
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset, modulo) =>
|
|
optionalModulo(febDoomsdayDay + offset, modulo).toString(),
|
|
);
|
|
});
|
|
|
|
// Weekday of a specific date
|
|
const weekdayOfElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-weekday-of > .doomsday-value",
|
|
);
|
|
weekdayOfElements.forEach((element) => {
|
|
const dateStr = (element as HTMLElement).dataset.value || "";
|
|
const date = dateFromValueString(dateStr, selectedYear);
|
|
if (date === null) {
|
|
element.textContent = "INVALID DATE";
|
|
element.classList.add("text-red-500");
|
|
return;
|
|
}
|
|
const weekday = date.getDay();
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset) => {
|
|
const adjustedWeekday = optionalModulo(weekday + offset, 7);
|
|
return weekdays[adjustedWeekday];
|
|
},
|
|
);
|
|
});
|
|
const weekdayNumberOfElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-weekday-number-of > .doomsday-value",
|
|
);
|
|
weekdayNumberOfElements.forEach((element) => {
|
|
const dateStr = (element as HTMLElement).dataset.value || "";
|
|
const date = dateFromValueString(dateStr, selectedYear);
|
|
if (date === null) {
|
|
element.textContent = "INVALID DATE";
|
|
element.classList.add("text-red-500");
|
|
return;
|
|
}
|
|
const weekday = date.getDay();
|
|
element.textContent = handleOffset(
|
|
element as HTMLElement,
|
|
(offset, modulo) => {
|
|
const adjustedWeekday = optionalModulo(
|
|
weekday + offset,
|
|
modulo,
|
|
);
|
|
return adjustedWeekday.toString();
|
|
},
|
|
);
|
|
});
|
|
|
|
// Conditional on the leap year status
|
|
const ifLeapYearElements = document.querySelectorAll(
|
|
".doomsday-display.doomsday-if-leap-year > .doomsday-value",
|
|
);
|
|
ifLeapYearElements.forEach((element) => {
|
|
element.textContent = isSelectedYearLeap
|
|
? (element as HTMLElement).dataset.value || ""
|
|
: (element as HTMLElement).dataset.valuealt || "";
|
|
});
|
|
}
|
|
currentYearInput?.addEventListener("change", update);
|
|
|
|
update();
|
|
</script>
|