|
// retoor <retoor@molodetz.nl>
|
|
|
|
const REFRESH_MS = 60000;
|
|
|
|
export class LocalTime {
|
|
constructor() {
|
|
this.localize(document);
|
|
this.observer = new MutationObserver((mutations) => {
|
|
for (const mutation of mutations) {
|
|
for (const node of mutation.addedNodes) {
|
|
if (node.nodeType === 1) this.localize(node);
|
|
}
|
|
}
|
|
});
|
|
if (document.body) {
|
|
this.observer.observe(document.body, { childList: true, subtree: true });
|
|
}
|
|
window.setInterval(() => this.refreshRelative(), REFRESH_MS);
|
|
}
|
|
|
|
format(iso, mode) {
|
|
const date = new Date(iso);
|
|
if (isNaN(date.getTime())) return null;
|
|
if (mode === "ago") return this.relative(date);
|
|
if (mode === "date") return date.toLocaleDateString();
|
|
if (mode === "time") {
|
|
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
}
|
|
return date.toLocaleString([], { dateStyle: "medium", timeStyle: "short" });
|
|
}
|
|
|
|
relative(date) {
|
|
const seconds = (Date.now() - date.getTime()) / 1000;
|
|
if (seconds < 0) return "just now";
|
|
if (seconds < 60) return "just now";
|
|
const minutes = Math.floor(seconds / 60);
|
|
if (minutes < 60) return minutes + "m ago";
|
|
const hours = Math.floor(minutes / 60);
|
|
if (hours < 24) return hours + "h ago";
|
|
const days = Math.floor(hours / 24);
|
|
if (days <= 30) return days + "d ago";
|
|
return date.toLocaleDateString();
|
|
}
|
|
|
|
apply(el) {
|
|
const iso = el.getAttribute("datetime");
|
|
if (!iso) return;
|
|
const mode = el.dataset.dtMode || "datetime";
|
|
const text = this.format(iso, mode);
|
|
if (text === null) return;
|
|
el.textContent = text;
|
|
const full = new Date(iso);
|
|
if (!isNaN(full.getTime())) {
|
|
el.title = full.toLocaleString([], { dateStyle: "full", timeStyle: "long" });
|
|
}
|
|
}
|
|
|
|
localize(root) {
|
|
if (root.matches && root.matches("[data-dt]")) this.apply(root);
|
|
if (!root.querySelectorAll) return;
|
|
root.querySelectorAll("[data-dt]").forEach((el) => this.apply(el));
|
|
}
|
|
|
|
refreshRelative() {
|
|
if (document.hidden) return;
|
|
const relative = document.querySelectorAll('[data-dt][data-dt-mode="ago"]');
|
|
if (!relative.length) return;
|
|
relative.forEach((el) => this.apply(el));
|
|
}
|
|
}
|