1// Moments. The server writes every time in UTC (`aldebaran_time::moment`): it knows neither this clock 2// nor this timezone. Here a `time[data-ago]` is rewritten as how long ago or how soon, with the date and 3// time where the reader is as its tooltip; an element with `data-when` keeps its text and gets only the 4// tooltip. Parts of the page are replaced as they change, so new moments are found as they arrive, and 5// all are brought up to date each minute. 6(() => { 7 const local = new Intl.DateTimeFormat(undefined, { 8 year: "numeric", month: "short", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "short", 9 }); 10 const relative = new Intl.RelativeTimeFormat(undefined, { numeric: "always" }); 11 const UNITS = [["year", 31536000], ["month", 2592000], ["day", 86400], ["hour", 3600], ["minute", 60]]; 12 function distance(seconds) { 13 const size = Math.abs(seconds); 14 if (size < 60) return seconds < 0 ? "just now" : "in under a minute"; 15 const [unit, length] = UNITS.find(([, length]) => size >= length); 16 return relative.format(Math.trunc(seconds / length), unit); 17 } 18 function set(element, name, value) { 19 if (element[name] !== value) element[name] = value; 20 } 21 function refresh() { 22 const now = Date.now(); 23 for (const time of document.querySelectorAll("time[data-ago][datetime]")) { 24 const at = Date.parse(time.dateTime); 25 if (Number.isNaN(at)) continue; 26 set(time, "textContent", distance((at - now) / 1000)); 27 set(time, "title", local.format(at)); 28 } 29 for (const element of document.querySelectorAll("[data-when]")) { 30 const at = Date.parse(element.dataset.when); 31 if (!Number.isNaN(at)) set(element, "title", local.format(at)); 32 } 33 } 34 refresh(); 35 // Writing a moment's own text is a change too; `set` writes nothing that is already so, which is what 36 // ends it. 37 let due = false; 38 new MutationObserver(() => { 39 if (due) return; 40 due = true; 41 requestAnimationFrame(() => { due = false; refresh(); }); 42 }).observe(document.body, { childList: true, subtree: true }); 43 setInterval(refresh, 60000); 44})();