aboutsummaryrefslogtreecommitdiff
path: root/static/ts/lib.ts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--static/ts/lib.ts (renamed from static/lib.ts)188
1 files changed, 177 insertions, 11 deletions
diff --git a/static/lib.ts b/static/ts/lib.ts
index 2ef5b596..d503ac5d 100644
--- a/static/lib.ts
+++ b/static/ts/lib.ts
@@ -1,48 +1,112 @@
+/**
+ General procedures which in theory could be used anywhere.
+
+ Besides exporting the mentioned functions, this module also
+ extends some base classes.
+
+ @module
+ */
export {
makeElement, date_to_percent,
parseDate, gensym, to_local, to_boolean,
- asList, round_time
+ asList, round_time,
+ format_date,
}
/*
- General procedures which in theory could be used anywhere.
- */
-
-/*
* https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
declare global {
interface Object {
+ /**
+ Introduce a format method on ALL objects
+
+ The default implementation simply stringifies the object, but this
+ allows any component to overwrite it, allowing for generic custom
+ formatting of data.
+
+ This also means that the format string is ignored for the default
+ implementation.
+
+ See `Data.prototype.format`.
+ */
format: (fmt: string) => string
}
+ /** HTMLElement extensions */
interface HTMLElement {
+ /**
+ "Private" property, storing the "true" add event listener. The
+ exposed addEventListener is later overwritten to also store a list of
+ which event listeners are added.
+ */
_addEventListener: (name: string, proc: (e: Event) => void) => void
- listeners: Map<string, ((e: Event) => void)[]>
+
+ /**
+ Contains all listeners added through `addEventListener`.
+
+ The keys are the same as to `addEventListener` ('load', 'mouseover',
+ ...)
+
+ Values are simply a list of all added listeners, probably in addition
+ order.
+ */
+ listeners: Map<string, ((e: Event) => void)[]>;
+
+ /**
+ Returns listeners.
+
+ TODO why does this exist?
+ */
getListeners: () => Map<string, ((e: Event) => void)[]>
}
interface Date {
+ /**
+ A proper date formatter for javascript.
+
+ See {@ref format_date} for details
+ */
format: (fmt: string) => string
+
+ /** Indicates if the date object is in UTC or local time. */
utc: boolean
+
+ /**
+ Indicates that the object only contains a date component.
+
+ This means that any time is ignored in most operations.
+ */
dateonly: boolean
// type: 'date' | 'date-time'
}
interface DOMTokenList {
+ /**
+ Searches a DOMTokenList for anything matching.
+
+ DOMTokenLists are returned by `.classList` and similar.
+
+ @return Returns the matching index, and the matched value,
+ or `undefined` if nothing was found.
+ */
find: (regex: string) => [number, string] | undefined
}
interface HTMLCollection {
+ /** Adds an iterator to HTMLCollections */
forEach: (proc: ((el: Element) => void)) => void
}
interface HTMLCollectionOf<T> {
+ /** Adds an iterator to HTMLCollections */
forEach: (proc: ((el: T) => void)) => void
}
}
+/** See interface above */
HTMLElement.prototype._addEventListener = HTMLElement.prototype.addEventListener;
+/** See interface above */
HTMLElement.prototype.addEventListener = function(name: string, proc: (e: Event) => void) {
if (!this.listeners) this.listeners = new Map
if (!this.listeners.get(name)) this.listeners.set(name, []);
@@ -50,6 +114,7 @@ HTMLElement.prototype.addEventListener = function(name: string, proc: (e: Event)
this.listeners.get(name)!.push(proc);
return this._addEventListener(name, proc);
};
+/** See interface above */
HTMLElement.prototype.getListeners = function() {
return this.listeners;
}
@@ -70,6 +135,13 @@ HTMLElement.prototype.getListeners = function() {
century, due to how javascript works (...).
*/
+/**
+ * Takes a string `str`, which should be in ISO-8601 date-format, and
+ * returns a javascript Date object.
+ * Handles date-times, with and without seconds, trailing `Z' for
+ * time-zones, and dates without times.
+ * If no time is given the `dateonly` attribute is set to yes.
+ */
function parseDate(str: string): Date {
let year: number;
let month: number;
@@ -112,6 +184,11 @@ function parseDate(str: string): Date {
return date;
}
+/**
+ * Returns a Date object (which may be new) which is guaranteed in local time.
+ * This means that the `utc` field is `false`, and that
+ * `to_local(current_time())` should show what your wall-clock shows.
+ */
function to_local(date: Date): Date {
if (!date.utc) return date;
@@ -120,6 +197,27 @@ function to_local(date: Date): Date {
/* -------------------------------------------------- */
+/**
+ * Creates a new DOM element of type `name`, with all keys in
+ * `attr` transfered to it. For example, the equivalent of
+
+ * ```html
+ * <input type='number'/>
+ * ```
+
+ * would be
+
+ * ```js
+ * values.push(makeElement('input', {
+ * type: 'number',
+ * }));
+ * ```
+ *
+ * @param name HTML tagname
+ * @param attr Attributes which will be set on the created element.
+ * @param actualAttr Attributes which will be set on the created element,
+ * but through `el.setAttribute` instead of `el[key] =`...
+ */
function makeElement(name: string, attr = {}, actualAttr = {}): HTMLElement {
let element: HTMLElement = document.createElement(name);
for (let [key, value] of Object.entries(attr)) {
@@ -131,6 +229,21 @@ function makeElement(name: string, attr = {}, actualAttr = {}): HTMLElement {
return element;
}
+/**
+ Round clock time to closest interval.
+
+ @param time
+ The desired clock-time, in decimal time. So 12:30 would be given as 12.30.
+
+ @param fraction
+ The time interval to round to. To round to nearest half hour, give 0.5.
+
+ @example
+ ```js
+ > round_time(10.1, 15/60)
+ 10
+ ```
+ */
function round_time(time: number, fraction: number): number {
let scale = 1 / fraction;
return Math.round(time * scale) / scale;
@@ -142,14 +255,26 @@ function round_time(time: number, fraction: number): number {
Just doing (new Date()/(86400*1000)) would be nice, but there's no good
way to get the time in the current day.
*/
+/**
+ * Retuns how far along the date specified by `date` is, between 0
+ * and 100, where 00:00 maps to 0, and 23:59 to ~100.
+ */
function date_to_percent(date: Date): number /* in 0, 100 */ {
return (date.getHours() + (date.getMinutes() / 60)) * 100 / 24;
}
/* if only there was such a thing as a let to wrap around my lambda... */
/* js infix to not collide with stuff generated backend */
+/**
+ * Generates a new string which is (hopefully) globally unique.
+ * Compare with `gensym` from Lisp.
+ */
const gensym = (counter => (prefix = "gensym") => prefix + "js" + ++counter)(0)
+/**
+ * Ensures that `thing` is a list. Returning it outright if it
+ * already is one, otherwise wrapping it in a list.
+*/
function asList<T>(thing: Array<T> | T): Array<T> {
if (thing instanceof Array) {
return thing;
@@ -159,6 +284,16 @@ function asList<T>(thing: Array<T> | T): Array<T> {
}
+/**
+ Smartly converts a value into a boolean.
+
+ Booleans are returned as if,
+
+ Strings are parsed, mapping `'true'` onto `true`, `'false'` onto `false`,
+ empty strings onto `false`, and anything else onto `true`.
+
+ Anything else is left onto JavaScript to coerce a boolean.
+ */
function to_boolean(value: any): boolean {
switch (typeof value) {
case 'string':
@@ -182,12 +317,43 @@ function datepad(thing: number | string, width = 2): string {
return (thing + "").padStart(width, "0");
}
-function format_date(date: Date, str: string): string {
+/**
+ Format a date into a string.
+
+ @param date
+ The datetime to format
+
+ @param format
+ How the date should be converted into a string.
+
+ The format is similar to `strftime`, but with tilde (`~`) characters
+ instead of percent signs, to match how Scheme does it. Valid format
+ specifiers are:
+
+ | Fmt | Output | Width¹ |
+ |------|----------------------------------|--------|
+ | `~~` | Literal '~' | |
+ | `~Y` | Year | 4 |
+ | `~m` | Month number | 2 |
+ | `~d` | Day of month | 2 |
+ | `~H` | Hour | 2 |
+ | `~M` | Minute | 2 |
+ | `~S` | Second | 2 |
+ | `~Z` | 'Z' if date is UTC, otherwise '' | |
+ | `~L` | Converts date to local time² | |
+
+ - ¹ These fields will be left padded with zeroes to that width
+ - ² This forces the output to be in local time, possibly converting
+ timezone if needed. It then outputs nothing.
+ See {@link to_local `to_local`} for details.
+
+*/
+function format_date(date: Date, format: string): string {
let fmtmode = false;
let outstr = "";
- for (var i = 0; i < str.length; i++) {
+ for (var i = 0; i < format.length; i++) {
if (fmtmode) {
- switch (str[i]) {
+ switch (format[i]) {
/* Moves the date into local time. */
case 'L': date = to_local(date); break;
case 'Y': outstr += datepad(date.getFullYear(), 4); break;
@@ -199,10 +365,10 @@ function format_date(date: Date, str: string): string {
case 'Z': if (date.utc) outstr += 'Z'; break;
}
fmtmode = false;
- } else if (str[i] == '~') {
+ } else if (format[i] == '~') {
fmtmode = true;
} else {
- outstr += str[i];
+ outstr += format[i];
}
}
return outstr;