diff options
Diffstat (limited to 'static/js/timeago.ts')
-rw-r--r-- | static/js/timeago.ts | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/static/js/timeago.ts b/static/js/timeago.ts new file mode 100644 index 0000000..7c1973d --- /dev/null +++ b/static/js/timeago.ts @@ -0,0 +1,93 @@ +(function () { + type second = number; + type millisecond = number; + + const relative = new Intl.RelativeTimeFormat("en", { + localeMatcher: "best fit", + numeric: "always", + style: "long", + }); + + function viewport(element) { + const options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : { offset: 250 }; + const view = element.getBoundingClientRect(); + return view.top >= -options.offset + && view.left >= -options.offset + && view.bottom <= (self.innerHeight || document.documentElement.clientHeight) + options.offset + && view.right <= (self.innerWidth || document.documentElement.clientWidth) + options.offset; + } + + const date = function (update) { + const elements = document.querySelectorAll("time"); + for (let i = 0; i < elements.length; ++i) { + const offscreen = !viewport(elements[i]); + const hidden = elements[i].offsetParent === null; + if ((update === "viewport") && (offscreen || hidden)) continue; + + (function (element) { + try { + if (element.dataset.type === "disabled") return; + + const time: millisecond = new Date(element.dateTime) || new Date(); + const interval: second = ((new Date().getTime() - time.getTime()) / 1000); + + const seconds: number = Math.round(interval); + const minutes: number = Math.round(seconds / 60); + const hours: number = Math.round(minutes / 60); + const days: number = Math.round(hours / 24); + + const past = Math.sign(seconds) === 1; + const future = Math.sign(seconds) === -1; + + let tiny = function (string, place) { + return string.split(" ").slice(0, place).join(" ") + string.split(" ")[place].charAt(0); + } + + if (element.dataset.type === "default") { tiny = function (string) { return string; }; } + + if (element.dataset.type === "localDate") { + return element.textContent = new Intl.DateTimeFormat([], { dateStyle: "medium", }).format(time).replace(",", ""); + } + + if (element.dataset.type === "localTime") { + return element.textContent = new Intl.DateTimeFormat([], { + hour12: false, + timeStyle: "short", + }).format(time) + " " + new Intl.DateTimeFormat([], { timeZoneName: "short" }).format(time).split(" ")[1]; + } + + if (past) { + if (seconds <= 60) { return element.textContent = tiny(relative.format(-1 * seconds, "second",), 1); } + if (minutes <= 120) { return element.textContent = tiny(relative.format(-1 * minutes, "minute",), 1); } + if (hours <= 48) { return element.textContent = tiny(relative.format(-1 * hours, "hour",), 1); } + if (days <= 7) { return element.textContent = tiny(relative.format(-1 * days, "day",), 1); } + } + + if (future) { + if (-1 * days >= 4) { return element.textContent = tiny(relative.format(-1 * days, "day",), 2); } + if (-1 * hours >= 3) { return element.textContent = tiny(relative.format(-1 * hours, "hour",), 2); } + if (-1 * minutes >= 2) { return element.textContent = tiny(relative.format(-1 * minutes, "minute",), 2); } + if (-1 * seconds >= 1) { return element.textContent = tiny(relative.format(-1 * seconds, "second",), 2); } + } + } catch (error) { + console.error("ERROR: Relative time resolution failed", error); + } + })(elements[i]); + } + }; + + const substitution = setInterval(date, 4); + + ["scroll", "URLChangedCustomEvent"].forEach(function (event) { + self.addEventListener(event, function () { + date("viewport"); + }); + }); + + self.addEventListener("DOMContentLoaded", function () { + setTimeout(function () { + clearInterval(substitution); + setInterval(function () { date("viewport"); }, 1000); + }, 1000); + }); +})(); |