diff --git a/conf/example.json b/conf/example.json new file mode 100644 index 0000000..4db7b20 --- /dev/null +++ b/conf/example.json @@ -0,0 +1,5 @@ +{ + "view_mode": "table", + "calendar_id": 6, + "timezone_shift": 0 +} diff --git a/data/example.kal.json b/data/example.kal.json index 6649e6b..47cc274 100644 --- a/data/example.kal.json +++ b/data/example.kal.json @@ -3,93 +3,119 @@ { "id": 1, "object": { - "name": "Anton" + "name": "christian.frass" } }, { "id": 2, "object": { - "name": "Berta" + "name": "andre.weichert" } }, { "id": 3, "object": { - "name": "Caesar" + "name": "steffen.doegnitz" + } + }, + { + "id": 4, + "object": { + "name": "frank.dietrich" + } + }, + { + "id": 5, + "object": { + "name": "michael.berger" + } + }, + { + "id": 6, + "object": { + "name": "roland.schroeder" + } + }, + { + "id": 7, + "object": { + "name": "rene.hahn" + } + }, + { + "id": 8, + "object": { + "name": "max.meierhof" + } + }, + { + "id": 9, + "object": { + "name": "klaus.kleba" + } + }, + { + "id": 10, + "object": { + "name": "tim.detzner" } } ], "calendars": [ { - "id": 1, + "id": 5, "object": { "kind": "concrete", "data": { - "name": "Garten", + "name": "BV", + "private": false, + "hue": 0.0000000000000000, "users": [ - { - "id": 1, - "role": "editor" - } ], "events": [ { - "name": "Unkraut jähten", + "name": "9. Bundesparteitag | 1. Sitzung | Tag 1", "begin": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 12}, + "date": {"year": 2024, "month": 10, "day": 18}, "time": {"hour": 10, "minute": 0, "second": 0} }, - "end": null, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 10, "day": 18}, + "time": {"hour": 18, "minute": 0, "second": 0} + }, + "location": "Halle", "description": null }, { - "name": "Kartoffeln ernten", + "name": "9. Bundesparteitag | 1. Sitzung | Tag 2", "begin": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 24}, - "time": {"hour": 17, "minute": 0, "second": 0} + "date": {"year": 2024, "month": 10, "day": 19}, + "time": {"hour": 10, "minute": 0, "second": 0} }, "end": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 24}, - "time": {"hour": 20, "minute": 0, "second": 0} + "date": {"year": 2024, "month": 10, "day": 19}, + "time": {"hour": 18, "minute": 0, "second": 0} }, + "location": "Halle", "description": null - } - ] - } - } - }, - { - "id": 2, - "object": { - "kind": "concrete", - "data": { - "name": "Kochen", - "users": [ - { - "id": 1, - "role": "editor" }, { - "id": 2, - "role": "viewer" - } - ], - "events": [ - { - "name": "Krautnudeln", + "name": "9. Bundesparteitag | 1. Sitzung | Tag 3", "begin": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 21}, - "time": {"hour": 17, "minute": 0, "second": 0} + "date": {"year": 2024, "month": 10, "day": 20}, + "time": {"hour": 10, "minute": 0, "second": 0} }, "end": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 21}, - "time": {"hour": 19, "minute": 0, "second": 0} + "date": {"year": 2024, "month": 10, "day": 20}, + "time": {"hour": 18, "minute": 0, "second": 0} }, + "location": "Halle", "description": null } ] @@ -101,26 +127,70 @@ "object": { "kind": "concrete", "data": { - "name": "Band", + "name": "LV Sachsen", + "private": false, + "hue": 0.6180339887498949, "users": [ { - "id": 2, + "id": 9, "role": "editor" } ], "events": [ { - "name": "Auftritt im Park", + "name": "Sören Pellmann zu den Landtagswahlen im Osten", "begin": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 21}, - "time": {"hour": 20, "minute": 0, "second": 0} + "date": {"year": 2024, "month": 9, "day": 11}, + "time": {"hour": 18, "minute": 0, "second": 0} + }, + "end": null, + "location": "online: https://v2202002113208108062.supersrv.de/b/har-jbu-lxy-rx1", + "description": null + }, + { + "name": "Erinnern versammeln. Praktiken für die Zukünfte einer Gesellschaft der Vielen.", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 13}, + "time": {"hour": 17, "minute": 0, "second": 0} }, "end": { "timezone_shift": 2, - "date": {"year": 2024, "month": 9, "day": 21}, + "date": {"year": 2024, "month": 9, "day": 15}, + "time": null + }, + "location": "Weltecho, Annaberger Straße 24, 09111 Chemnitz", + "description": null + }, + { + "name": "Parteikonvent zur Auswertung des Wahljahres", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 14}, + "time": {"hour": 10, "minute": 0, "second": 0} + }, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 14}, + "time": {"hour": 16, "minute": 30, "second": 0} + }, + "location": "Veranstaltungs- und Kulturforum STADTPARK | Hammertal 3 | 09669 Frankenberg/Sachsen", + "description": null + }, + { + "name": "Ist die extreme Rechte noch zu stoppen?", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 19}, + "time": {"hour": 19, "minute": 0, "second": 0} + }, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 19}, "time": {"hour": 21, "minute": 0, "second": 0} }, + "location": "online: https://www.dielinke-sachsen.de/termine/?termin_ort=digital-internet-stream", "description": null } ] @@ -130,18 +200,134 @@ { "id": 4, "object": { - "kind": "caldav", + "kind": "concrete", "data": { - "source_url": "https://export.kalender.digital/ics/0/3e10dae66950379d4cc8/gesamterkalender.ics?past_months=3&future_months=36" + "name": "KV Zwickau", + "private": false, + "hue": 0.4721359549995796, + "events": [ + { + "name": "Vorstands-Sitzung", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 5}, + "time": {"hour": 18, "minute": 0, "second": 0} + }, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 5}, + "time": {"hour": 21, "minute": 0, "second": 0} + }, + "location": "Zwickau, Innere Schneeberger Straße 17", + "description": null + } + ] } } }, { - "id": 5, + "id": 1, "object": { - "kind": "caldav", + "kind": "concrete", "data": { - "source_url": "https://roydfalk:uywvui66svLHFMUp6LndxNO9ZASNrY9vmUDcAPPNIr7PWmjAMpEQce0JiA9AAeUH@vikunja.linke.sx/dav/projects/4/" + "name": "OV Glauchau", + "private": false, + "users": [ + { + "id": 1, + "role": "editor" + }, + { + "id": 5, + "role": "editor" + }, + { + "id": 6, + "role": "editor" + } + ], + "hue": 0.09016994374947451, + "events": [ + { + "name": "Kinderspieletag", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 8}, + "time": {"hour": 12, "minute": 0, "second": 0} + }, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 8}, + "time": {"hour": 16, "minute": 0, "second": 0} + }, + "location": null, + "description": null + }, + { + "name": "Mitglieder-Sitzung", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 19}, + "time": {"hour": 17, "minute": 30, "second": 0} + }, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 19}, + "time": {"hour": 19, "minute": 0, "second": 0} + }, + "location": null, + "description": null + } + ] + } + } + }, + { + "id": 2, + "object": { + "kind": "concrete", + "data": { + "name": "OV Zwickau", + "private": false, + "hue": 0.09016994374947451, + "users": [ + { + "id": 7, + "role": "editor" + }, + { + "id": 8, + "role": "viewer" + } + ], + "events": [ + { + "name": "4ter Christopher Street Day", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 8, "day": 31}, + "time": {"hour": 10, "minute": 0, "second": 0} + }, + "end": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 8, "day": 31}, + "time": {"hour": 17, "minute": 0, "second": 0} + }, + "location": "Zwickau", + "description": null + }, + { + "name": "Regionaltreffen Westsachsen: Schule ohne Rassismus – Schule mit Courage", + "begin": { + "timezone_shift": 2, + "date": {"year": 2024, "month": 9, "day": 19}, + "time": {"hour": 9, "minute": 0, "second": 0} + }, + "end": null, + "location": null, + "description": null + } + ] } } }, @@ -154,10 +340,24 @@ "sources": [ 1, 2, - 3 + 3, + 4, + 5 ] } } + }, + { + "id": 7, + "object": { + "kind": "caldav", + "data": { + "name": "Lixer", + "private": false, + "read_only": true, + "source_url": "https://export.kalender.digital/ics/0/3e10dae66950379d4cc8/gesamterkalender.ics?past_months=3&future_months=36" + } + } } ] } diff --git a/doc/konzept.md b/doc/konzept.md new file mode 100644 index 0000000..e8bb6fa --- /dev/null +++ b/doc/konzept.md @@ -0,0 +1,21 @@ +- Kalender sollen unabhängig von Nutzern bestehen können +- einem Kalender können beliebig viele Nutzer zugeordnet werden, die jeweils bestimmte Berechtigungen haben (z.B. als Rollen "admin", "editor", "viewer", …) +- Berechtigungen: + - Kalender anlegen + - Kalender-Stammdaten ändern + - Kalender-Einträge lesen + - Kalender-Einträge erstellen + - Kalender-Einträge ändern + - Kalender-Einträge entfernen +- Kalender sind für gewöhnlichen öffentlich +- es gibt verschiedene Arten von Kalendern: + - konkret + - enthält Veranstaltungen + - Sammlung + - enthält selbst keine Veranstaltungen + - verweist auf beliebig viele andere Kalender (Kreise vermeiden) + - extern + - über CalDAV + - sollte read-only- und read/write-Modus haben +- nach dem Anmelden sieht man eine Liste mit öffentlichen Kalendern, gruppiert nach jewiliger Rolle +- Entwurfsname: "zeitbild" diff --git a/lib/plankton/plankton.d.ts b/lib/plankton/plankton.d.ts index 22fc7d6..28f1eb6 100644 --- a/lib/plankton/plankton.d.ts +++ b/lib/plankton/plankton.d.ts @@ -29,8 +29,6 @@ type type_datetimeobject = { date: type_date; time: type_time; }; -declare var process: any; -declare var require: any; declare class Buffer { constructor(x: string, modifier?: string); toString(modifier?: string): string; @@ -299,7 +297,10 @@ declare module lib_plankton.pod { */ class class_pod { private subject; - private constructor(); + constructor(subject: type_pod); + tear(): type_pod; + static empty(): class_pod; + static filled(value: type_value): class_pod; is_empty(): boolean; is_filled(): boolean; cull(): type_value; @@ -692,10 +693,6 @@ declare namespace lib_plankton.call { }>; } declare namespace lib_plankton.file { - /** - * @author fenris - */ - function exists(path: string): Promise; /** * @author fenris */ @@ -703,24 +700,1062 @@ declare namespace lib_plankton.file { /** * @author fenris */ - function read_buffer(path: string): Promise; + function write(path: string, content: string): Promise; /** * @author fenris */ - function read_stdin(): Promise; + function blob_read_text(blob: Blob): lib_plankton.call.type_promise; /** * @author fenris */ - function write(path: string, content: string, options?: { - encoding?: string; - }): Promise; + function blob_read_arraybuffer(blob: Blob): lib_plankton.call.type_promise; /** * @author fenris */ - function write_buffer(path: string, content: Buffer, options?: {}): Promise; + function blob_read_dataurl(blob: Blob): lib_plankton.call.type_promise; + /** + * @author fenris + */ + function blob_write_text(text: string): lib_plankton.call.type_promise; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + function map_clear(map_forEach: (procedure: (value?: type_value, key?: type_key) => void) => void, map_delete: (key: type_key) => void): void; + /** + * @author fenris + */ + function map_keys(map_forEach: (procedure: (value?: type_value, key?: type_key) => void) => void): Array; + /** + * @author fenris + */ + function map_values(map_forEach: (procedure: (value?: type_value, key?: type_key) => void) => void): Array; + /** + * @author fenris + */ + function map_pairs(map_forEach: (procedure: (value?: type_value, key?: type_key) => void) => void): Array<{ + key: type_key; + value: type_value; + }>; + /** + * @author fenris + */ + function map_toString(map_forEach: (procedure: (value?: type_value, key?: type_key) => void) => void, show_key?: (key: type_key) => string, show_value?: (value: type_value) => string): string; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + interface interface_map { + /** + * @desc [accessor] + */ + has(key: type_key): boolean; + /** + * @desc [accessor] + */ + get(key: type_key, fallback?: lib_plankton.pod.class_pod): type_value; + /** + * @desc [mutator] + */ + set(key: type_key, value: type_value): void; + /** + * @desc [mutator] + */ + delete(key: type_key): void; + /** + * @desc [accessor] + */ + forEach(procedure: ((value?: type_value, key?: type_key) => void)): void; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + abstract class class_mapbase implements interface_map { + /** + * @desc [constructor] + */ + constructor(); + /** + * @implementation + */ + abstract has(key: type_key): boolean; + /** + * @implementation + */ + abstract get(key: type_key, fallback?: lib_plankton.pod.class_pod): type_value; + /** + * @implementation + */ + abstract set(key: type_key, value: type_value): void; + /** + * @implementation + */ + abstract delete(key: type_key): void; + /** + * @implementation + */ + abstract forEach(procedure: ((value?: type_value, key?: type_key) => void)): void; + /** + * @desc [mutator] + */ + clear(): void; + /** + */ + get_safe(key: type_key): lib_plankton.pod.class_pod; + /** + * @desc [accessor] + */ + keys(): Array; + /** + * @desc [accessor] + */ + values(): Array; + /** + * @desc [accessor] + */ + pairs(): Array<{ + key: type_key; + value: type_value; + }>; + /** + * @desc [accessor] + */ + toString(show_key?: ((key: type_key) => string), show_value?: ((value: type_value) => string)): string; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + interface interface_memory { + /** + * @desc [accessor] the number of elements + */ + size(): int; + /** + * @desc [accessor] reads the takeable element + */ + scan(): type_element; + /** + * @desc [mutator] inserts an element + */ + give(element: type_element): void; + /** + * @desc [mutator] removes an element and returns it + */ + take(): type_element; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + class class_pair implements interface_cloneable>, interface_collatable>, interface_hashable, interface_showable { + /** + * @author fenris + */ + protected first: type_first; + /** + * @author fenris + */ + protected second: type_second; + /** + * @author fenris + */ + constructor(first: type_first, second: type_second); + /** + * @desc [accessor] [getter] + * @author fenris + */ + first_get(): type_first; + /** + * @desc [accessor] [getter] + * @author fenris + */ + second_get(): type_second; + /** + * @desc [mutator] [setter] + * @author fenris + */ + first_set(first: type_first): void; + /** + * @desc [mutator] [setter] + * @author fenris + */ + second_set(second: type_second): void; + /** + * @desc [accessor] + * @author fenris + */ + swap(): class_pair; + /** + * @desc [accessor] + * @author fenris + */ + transform(transform_first: (first: type_first) => type_first_, transform_second: (second: type_second) => type_second_): class_pair; + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _clone(): class_pair; + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _hash(): string; + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _collate(pair: class_pair): boolean; + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _show(): string; + } +} +declare namespace lib_plankton.structures { /** */ - function delete_(path: string): Promise; + type type_collation = ((x: type_element, y: type_element) => boolean); + /** + * @author fenris + */ + export type type_set = { + elements: Array; + }; + /** + * @author fenris + */ + export function set_construct(collation: type_collation, elements?: Array): type_set; + /** + * @desc [accessor] + * @author fenris + */ + export function set_size(subject: type_set): int; + /** + * @desc [accessor] + * @author fenris + */ + export function set_has(collation: type_collation, subject: type_set, element: type_element): boolean; + /** + * @desc [mutator] + * @author fenris + */ + export function set_add(collation: type_collation, subject: type_set, element: type_element): void; + /** + * @desc [mutator] + * @author fenris + */ + export function set_pop(subject: type_set): lib_plankton.pod.type_pod; + /** + * @desc [accessor] + * @author fenris + */ + export function set_forEach(subject: type_set, function_: ((element: type_element) => void)): void; + /** + * @desc [accessor] + * @author fenris + */ + export function set_map(collation: type_collation, subject: type_set, transformator: ((element: type_element_from) => type_element_to)): type_set; + /** + * @desc [accessor] + * @author fenris + */ + export function set_filter(subject: type_set, predicate: ((element: type_element) => boolean)): type_set; + /** + * @desc [accessor] + * @author fenris + */ + export function set_dump(subject: type_set): Array; + /** + * @desc [accessor] + * @author fenris + */ + export function set_subset(collation: type_collation, subject: type_set, object: type_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + export function set_superset(collation: type_collation, subject: type_set, object: type_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + export function set_equals(collation: type_collation, subject: type_set, object: type_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + export function set_toString(show_element: ((element: type_element) => string), subject: type_set): string; + /** + * @desc [accessor] + * @author fenris + */ + export function set_empty(subject: type_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + export function set_union(collation: type_collation, subject: type_set, object: type_set): type_set; + /** + * @desc [accessor] + * @author fenris + */ + export function set_intersection(collation: type_collation, subject: type_set, object: type_set): type_set; + /** + * @desc [accessor] + * @author fenris + */ + export function set_difference(collation: type_collation, subject: type_set, object: type_set): type_set; + /** + * @desc [accessor] + * @author fenris + */ + export function set_symmetric_difference(collation: type_collation, subject: type_set, object: type_set): type_set; + /** + * @author fenris + */ + export function set_union_all(collation: type_collation, sets: Array>): type_set; + /** + * @author fenris + */ + export function set_intersection_all(collation: type_collation, sets: Array>): type_set; + export {}; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + class class_set implements interface_collatable>, interface_showable { + /** + * @author fenris + */ + protected subject: type_set; + /** + * @author fenris + */ + protected equality: (element1: type_element, element2: type_element) => boolean; + /** + * @author fenris + */ + constructor(elements?: Array, equality?: (element1: type_element, element2: type_element) => boolean); + /** + * @author fenris + */ + protected static from_subject(equality: (element1: type_element, element2: type_element) => boolean, subject: type_set): class_set; + /** + * @desc [accessor] + * @author fenris + */ + size(): int; + /** + * @desc [accessor] + * @author fenris + */ + has(element: type_element): boolean; + /** + * @desc [mutator] + * @author fenris + */ + add(element: type_element): void; + /** + * @desc [mutator] + * @author fenris + */ + pop(): lib_plankton.pod.class_pod; + /** + * @desc [accessor] + * @author fenris + */ + forEach(function_: (element: type_element) => void): void; + /** + * @desc [accessor] + * @author fenris + */ + map(transformator: (element: type_element) => type_element_, equality?: (x: type_element_, y: type_element_) => boolean): class_set; + /** + * @desc [accessor] + * @author fenris + */ + filter(predicate: (element: type_element) => boolean): class_set; + /** + * @desc [accessor] + * @author fenris + */ + dump(): Array; + /** + * @desc [accessor] + * @author fenris + */ + subset(set: class_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + superset(set: class_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + equals(set: class_set): boolean; + /** + * @desc [accessor] + * @author fenris + */ + toString(): string; + /** + * @desc [accessor] + * @author fenris + */ + empty(): boolean; + /** + * @desc [accessor] + * @author fenris + */ + union(set: class_set): class_set; + /** + * @desc [accessor] + * @author fenris + */ + intersection(set: class_set): class_set; + /** + * @desc [accessor] + * @author fenris + */ + difference(set: class_set): class_set; + /** + * @desc [accessor] + * @author fenris + */ + symmetric_difference(set: class_set): class_set; + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _collate(set: class_set): boolean; + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _show(): string; + /** + * @author fenris + */ + static union_all(sets: Array>): class_set; + /** + * @author fenris + */ + static intersection_all(sets: Array>): class_set; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + type type_stack = { + elements: Array; + }; + /** + * @author fenris + */ + function stack_construct(): type_stack; + /** + * @author fenris + */ + function stack_size(subject: type_stack): int; + /** + * @author fenris + */ + function stack_scan(subject: type_stack): type_element; + /** + * @author fenris + */ + function stack_take(subject: type_stack): type_element; + /** + * @author fenris + */ + function stack_give(subject: type_stack, element: type_element): void; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + abstract class class_stack implements interface_memory { + /** + * @author fenris + */ + protected subject: type_stack; + /** + * @author fenris + */ + constructor(); + /** + * @override + * @author fenris + */ + size(): int; + /** + * @override + * @author fenris + */ + scan(): type_element; + /** + * @override + * @author fenris + */ + take(): type_element; + /** + * @override + * @author fenris + */ + give(element: type_element): void; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + type type_queue = { + elements: Array; + }; + /** + * @author fenris + */ + function queue_construct(): type_queue; + /** + * @author fenris + */ + function queue_size(subject: type_queue): int; + /** + * @author fenris + */ + function queue_scan(subject: type_queue): type_element; + /** + * @author fenris + */ + function queue_take(subject: type_queue): type_element; + /** + * @author fenris + */ + function queue_give(subject: type_queue, element: type_element): void; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + abstract class class_queue implements interface_memory { + /** + * @author fenris + */ + protected subject: type_queue; + /** + * @author fenris + */ + constructor(); + /** + * @override + * @author fenris + */ + size(): int; + /** + * @override + * @author fenris + */ + scan(): type_element; + /** + * @override + * @author fenris + */ + take(): type_element; + /** + * @override + * @author fenris + */ + give(element: type_element): void; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + type type_simplemap = { + memory: { + [key: string]: type_value; + }; + }; + /** + * @author fenris + */ + function simplemap_construct(): type_simplemap; + /** + * @author fenris + */ + function simplemap_has(subject: type_simplemap, key: string): boolean; + /** + * @author fenris + */ + function simplemap_get_safe(subject: type_simplemap, key: string): lib_plankton.pod.type_pod; + /** + * @author fenris + */ + function simplemap_get(subject: type_simplemap, key: string, fallback?: lib_plankton.pod.type_pod): type_value; + /** + * @author fenris + */ + function simplemap_set(subject: type_simplemap, key: string, value: type_value): void; + /** + * @author fenris + */ + function simplemap_delete(subject: type_simplemap, key: string): void; + /** + * @author fenris + */ + function simplemap_clear(subject: type_simplemap): void; + /** + * @author fenris + */ + function simplemap_forEach(subject: type_simplemap, function_: (value?: type_value, key?: string) => void): void; + /** + * @author fenris + */ + function simplemap_from_object(object: { + [key: string]: type_value; + }): type_simplemap; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + class class_simplemap extends class_mapbase implements interface_map { + /** + * @author fenris + * @desc [attribute] + */ + protected subject: type_simplemap; + /** + * @author fenris + */ + static make(): class_simplemap; + /** + * @author fenris + */ + static from_object(object: { + [key: string]: type_value; + }): class_simplemap; + /** + * @author fenris + * @desc [constructor] + */ + protected constructor(subject?: type_simplemap); + /** + * @author fenris + * @implementation + */ + has(key: string): boolean; + /** + * @author fenris + * @implementation + */ + get(key: string, fallback?: lib_plankton.pod.class_pod): type_value; + /** + * @author fenris + * @implementation + */ + set(key: string, value: type_value): void; + /** + * @author fenris + * @implementation + */ + delete(key: string): void; + /** + * @author fenris + * @implementation + */ + forEach(procedure: (value?: type_value, key?: string) => void): void; + } +} +declare namespace lib_plankton.structures { + /** + */ + type type_pair = { + key: type_key; + value: type_value; + }; + /** + * @author fenris + * @desc we base the hashmap on a simplemap, whos keys are the hashes and whos values are the key/value-pairs + */ + export type type_hashmap = { + core: type_simplemap>; + hashing: ((key: type_key) => string); + }; + /** + * @author fenris + */ + export function hashmap_construct(hashing: ((key: type_key) => string), pairs: Array<{ + key: type_key; + value: type_value; + }>): type_hashmap; + /** + * @author fenris + */ + export function hashmap_has(subject: type_hashmap, key: type_key): boolean; + /** + * @author fenris + */ + export function hashmap_get(subject: type_hashmap, key: type_key, fallback?: lib_plankton.pod.type_pod): type_value; + /** + * @author fenris + */ + export function hashmap_set(subject: type_hashmap, key: type_key, value: type_value): void; + /** + * @author fenris + */ + export function hashmap_delete(subject: type_hashmap, key: type_key): void; + /** + * @author fenris + */ + export function hashmap_clear(subject: type_hashmap): void; + /** + * @author fenris + */ + export function hashmap_forEach(subject: type_hashmap, procedure: ((value?: type_value, key?: type_key) => void)): void; + /** + * @author fenris + */ + export function hashmap_dump(subject: type_hashmap): Array>; + export {}; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + class class_hashmap extends class_mapbase implements interface_map { + /** + * @author fenris + * @desc [attribute] + */ + protected subject: type_hashmap; + /** + * @author fenris + * @desc [constructor] + */ + constructor(hashing?: ((key: type_key) => string), pairs?: Array<{ + key: type_key; + value: type_value; + }>); + /** + * @author fenris + * @implementation + */ + has(key: type_key): boolean; + /** + * @author fenris + * @implementation + */ + get(key: type_key, fallback?: lib_plankton.pod.class_pod): type_value; + /** + * @author fenris + * @implementation + */ + set(key: type_key, value: type_value): void; + /** + * @author fenris + * @implementation + */ + delete(key: type_key): void; + /** + * @author fenris + * @implementation + */ + forEach(procedure: ((value?: type_value, key?: type_key) => void)): void; + } +} +declare namespace lib_plankton.structures { + /** + */ + type type_collation = ((key1: type_key, key2: type_key) => boolean); + /** + */ + export type type_collatemap = { + pairs: Array<{ + key: type_key; + value: type_value; + }>; + }; + /** + */ + export function collatemap_construct(): type_collatemap; + /** + */ + export function collatemap_has(collation: type_collation, subject: type_collatemap, key: type_key): boolean; + /** + * @todo use .find + */ + export function collatemap_get(collation: type_collation, subject: type_collatemap, key: type_key, fallback?: lib_plankton.pod.type_pod): type_value; + /** + */ + export function collatemap_set(collation: type_collation, subject: type_collatemap, key: type_key, value: type_value): void; + /** + */ + export function collatemap_delete(collation: type_collation, subject: type_collatemap, key: type_key): void; + /** + */ + export function collatemap_forEach(subject: type_collatemap, function_: ((value?: type_value, key?: type_key) => void)): void; + export {}; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + class class_collatemap extends class_mapbase implements interface_map { + /** + * @author fenris + * @desc [attribute] + */ + protected collation: (key1: type_key, key2: type_key) => boolean; + /** + * @author fenris + * @desc [attribute] + */ + protected subject: type_collatemap; + /** + * @author fenris + * @desc [constructor] + */ + constructor(collation?: (key1: type_key, key2: type_key) => boolean); + /** + * @author fenris + * @implementation + */ + has(key: type_key): boolean; + /** + * @author fenris + * @implementation + */ + get(key: type_key, fallback?: lib_plankton.pod.class_pod): type_value; + /** + * @author fenris + * @implementation + */ + set(key: type_key, value: type_value): void; + /** + * @author fenris + * @implementation + */ + delete(key: type_key): void; + /** + * @author fenris + * @implementation + */ + forEach(procedure: (value?: type_value, key?: type_key) => void): void; + } + /** + * @author fenris + * @deprecated + */ + var class_map: typeof class_collatemap; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + type type_edge = { + from: type_node; + to: type_node; + }; + /** + * @author fenris + */ + class class_graph { + /** + * @author fenris + */ + protected equality: (node1: type_node, node2: type_node) => boolean; + /** + * @author fenris + */ + protected nodes: Array; + /** + * @author fenris + */ + protected edges: Array>; + /** + * @author fenris + */ + constructor(equality?: (node1: type_node, node2: type_node) => boolean, nodes?: Array, edges?: Array>); + /** + * @desc [accessor] [getter] + * @author fenris + */ + nodes_get(): Array; + /** + * @desc [mutator] + * @author fenris + */ + add_node(node: type_node): void; + /** + * @desc [accessor] [getter] + * @author fenris + */ + edges_get(): Array>; + /** + * @desc [mutator] + * @author fenris + */ + add_edge(edge: type_edge): void; + /** + * @desc [accessor] + * @author fenris + */ + has(node: type_node): boolean; + /** + * @desc [accessor] + * @author fenris + */ + outgoing(node: type_node): Array>; + /** + * @desc [accessor] + * @author fenris + */ + incoming(node: type_node): Array>; + /** + * @desc [accessor] + * @author fenris + */ + without(pivot: type_node): class_graph; + /** + * @desc [accessor] returns the topologic sorting of the nodes (if it exists) + * @author fenris + */ + topsort(): Array; + /** + * @desc [accessor] returns the reduced version of a graph representing an order relation (implicit transitivity) + * @author fenris + */ + hasse(): class_graph; + /** + * @author fenris + */ + output_dot({ "extract_id": extract_id, "extract_label": extract_label, "rotate": rotate, }?: { + extract_id?: (node: type_node) => string; + extract_label?: (node: type_node) => string; + rotate?: boolean; + }): Object; + } +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + const relation_le: string; + const relation_ge: string; + const relation_lt: string; + const relation_gt: string; + const relation_eq: string; + /** + * @author fenris + */ + type type_binnode = { + data: type_data; + left: type_binnode; + right: type_binnode; + depth: int; + }; + /** + * @author fenris + */ + type type_bintree = { + root: type_binnode; + }; + /** + * @author fenris + */ + function bintree_construct(): type_bintree; + /** + * @author fenris + */ + function bintree_depth(subject: type_bintree): int; + /** + * @author fenris + * @todo remove later on + */ + function bintree_check_depths(subject: type_bintree): boolean; + /** + * @author fenris + */ + function bintree_insert(compare: (x: type_data, y: type_data) => boolean, subject: type_bintree, data: type_data, rebalance?: boolean): void; + /** + * @author fenris + */ + function bintree_search(compare: (x: type_data, y: type_data) => boolean, subject: type_bintree, data: type_data, relation?: string): Array; + /** + * @author fenris + * @deprecated only used for AVL-Tree-Index atm. + */ + function bintree_find(compare: (x: type_data, y: type_data) => boolean, subject: type_bintree, data: type_data): type_data; + /** + * @author fenris + */ + function bintree_traverse(subject: type_bintree): Array; + /** + * @author fenris + */ + function bintree_show(show_data: (data: type_data) => string, subject: type_bintree): string; + /** + * @author fenris + * @todo tidy up or remove + */ + function bintree_to_graph(subject: type_bintree): class_graph; +} +declare namespace lib_plankton.structures { + /** + * @author fenris + */ + class class_bintree { + /** + * @author fenris + */ + protected subject: type_bintree; + /** + * @author fenris + */ + protected compare: (x: type_data, y: type_data) => boolean; + /** + * @author fenris + */ + constructor(compare?: (x: type_data, y: type_data) => boolean); + /** + * @author fenris + */ + depth(): int; + /** + * @author fenris + */ + insert(data: type_data, rebalance?: boolean): void; + /** + * @author fenris + */ + delete(data: type_data): void; + /** + * @author fenris + */ + search(relation: string, data: type_data): Array; + /** + * @author fenris + */ + traverse(): Array; + /** + * @author fenris + */ + show(): string; + } } declare namespace lib_plankton.code { /** @@ -907,13 +1942,26 @@ declare namespace lib_plankton.code { } declare namespace lib_plankton.json { /** - * @author fenris */ - function encode(x: any, formatted?: boolean): string; + type type_source = any; + /** + */ + type type_target = string; /** * @author fenris */ - function decode(x: string): any; + export function encode(source: type_source, options?: { + formatted?: boolean; + }): type_target; + /** + * @author fenris + */ + export function decode(target: type_target): type_source; + /** + * @author fenris + */ + export function implementation_code(): lib_plankton.code.type_code; + export {}; } declare namespace lib_plankton.json { /** @@ -1003,66 +2051,9 @@ declare namespace lib_plankton.log { } declare namespace lib_plankton.log { /** - * output for writing log entries to stdout + * output for writing log entries to web console */ - class class_channel_stdout extends class_channel { - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { - /** - */ - class class_channel_file extends class_channel { - /** - * the path of the log file - */ - private path; - /** - */ - private human_readable; - /** - * [constructor] - */ - constructor(path: string, human_readable: boolean); - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { - /** - */ - class class_channel_email extends class_channel { - /** - */ - private smtp_credentials; - /** - */ - private sender; - /** - */ - private receivers; - /** - * [constructor] - */ - constructor(smtp_credentials: { - host: string; - port: int; - username: string; - password: string; - }, sender: string, receivers: Array); - /** - */ - add(entry: type_entry): void; - } -} -declare namespace lib_plankton.log { - /** - * output for desktop notifications via "libnotify" - */ - class class_channel_notify extends class_channel { + class class_channel_console extends class_channel { /** */ add(entry: type_entry): void; @@ -2092,8 +3083,9 @@ declare namespace lib_plankton.color { type type_color = { model: type_model_rgb; }; +} +declare namespace lib_plankton.color { /** - * @author fenris */ function make_hsv({ "hue": hue, "saturation": saturation, "value": value }: { hue?: float; @@ -2101,118 +3093,99 @@ declare namespace lib_plankton.color { value?: float; }): type_color; /** - * @author fenris */ function make_hsl(model_hsl: type_model_hsl): type_color; /** - * @author fenris */ function make_rgb(model_rgb: type_model_rgb): type_color; /** - * @author fenris */ function to_hsv(color: type_color): type_model_hsv; /** - * @author fenris */ function to_hsl(color: type_color): type_model_hsl; /** - * @author fenris */ function to_rgb(color: type_color): type_model_rgb; /** - * @author fenris */ function to_cmyk(color: type_color): type_model_rgb; /** - * @author fenris */ function add(color1: type_color, color2: type_color): type_color; /** - * @author fenris */ function multiply(color1: type_color, color2: type_color): type_color; /** - * @author fenris * @todo blend through other model? */ function blend(color1: type_color, color2: type_color, strength?: float): type_color; /** - * @author fenris */ - function mix(color1: type_color, color2: type_color, strength1?: float, strength2?: float): type_color; + function mix(color1: type_color, color2: type_color, { "strength1": option_strength1, "strength2": option_strength2, }?: { + strength1?: float; + strength2?: float; + }): type_color; /** - * @author fenris */ function output_rgb(color: type_color): string; /** - * @author fenris */ function output_hex(color: type_color): string; /** - * @author fenris */ function output_dot(color: type_color): string; /** - * @author fenris */ - function give_generic({ "n": n, "offset": offset, "saturation": saturation, "value": value, }: { - n: int; + function give_generic(n: int, { "offset": option_offset, "saturation": option_saturation, "value": option_value, }: { offset?: float; saturation?: float; value?: float; }): type_color; /** - * @author fenris */ - function give_gray(value?: float): type_color; + function give_gray({ "value": option_value, }: { + value?: float; + }): type_color; /** - * @author fenris */ function give_black(): type_color; /** - * @author fenris */ function give_white(): type_color; /** - * @author fenris */ - function give_red({ "saturation": saturation, "value": value, }?: { + function give_red({ "saturation": option_saturation, "value": option_value, }?: { saturation?: float; value?: float; }): type_color; /** - * @author fenris */ - function give_green({ "saturation": saturation, "value": value, }?: { + function give_green({ "saturation": option_saturation, "value": option_value, }?: { saturation?: float; value?: float; }): type_color; /** - * @author fenris */ - function give_blue({ "saturation": saturation, "value": value, }?: { + function give_blue({ "saturation": option_saturation, "value": option_value, }?: { saturation?: float; value?: float; }): type_color; /** - * @author fenris */ - function give_yellow({ "saturation": saturation, "value": value, }?: { + function give_yellow({ "saturation": option_saturation, "value": option_value, }?: { saturation?: float; value?: float; }): type_color; /** - * @author fenris */ - function give_cyan({ "saturation": saturation, "value": value, }?: { + function give_cyan({ "saturation": option_saturation, "value": option_value, }?: { saturation?: float; value?: float; }): type_color; /** - * @author fenris */ - function give_magenta({ "saturation": saturation, "value": value, }?: { + function give_magenta({ "saturation": option_saturation, "value": option_value, }?: { saturation?: float; value?: float; }): type_color; @@ -3038,12 +4011,6 @@ declare namespace lib_plankton.url { }; } declare namespace lib_plankton.url { - /** - */ - function query_args_encode(query_args: Record): string; - /** - */ - function query_args_decode(query: string): Record; /** * @author fenris */ diff --git a/lib/plankton/plankton.js b/lib/plankton/plankton.js index bb63f46..bc36cbe 100644 --- a/lib/plankton/plankton.js +++ b/lib/plankton/plankton.js @@ -53,6 +53,14 @@ You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:base«. If not, see . */ ; +/* +declare class console { + static log(...args : any[]) : void; + static info(...args : any[]) : void; + static warn(...args : any[]) : void; + static error(...args : any[]) : void; +}; + */ var lib_plankton; (function (lib_plankton) { var base; @@ -61,7 +69,7 @@ var lib_plankton; * @author fenris */ function environment() { - return "node"; + return "web"; } base.environment = environment; })(base = lib_plankton.base || (lib_plankton.base = {})); @@ -584,6 +592,9 @@ var lib_plankton; */ class class_pod { constructor(subject) { this.subject = subject; } + tear() { return this.subject; } + static empty() { return (new class_pod(pod.make_empty())); } + static filled(value) { return (new class_pod(pod.make_filled(value))); } is_empty() { return (!pod.is_filled(this.subject)); } is_filled() { return pod.is_filled(this.subject); } cull() { return pod.cull(this.subject); } @@ -1573,138 +1584,2422 @@ var lib_plankton; (function (lib_plankton) { var file; (function (file) { - /** - * @author fenris - */ - function exists(path) { - var nm_fs = require("fs"); - return (new Promise(function (resolve, reject) { - nm_fs.stat(path, function (error, stats) { - if (error) { - resolve(false); - } - else { - resolve(true); - } - }); - })); - } - file.exists = exists; /** * @author fenris */ function read(path) { - var nm_fs = require("fs"); - return (new Promise(function (resolve, reject) { - nm_fs.readFile(path, { - "encoding": "utf8", - "flag": "r" - }, function (error, content) { - if (error == null) { - resolve(content); - } - else { - reject(error); - } - }); - })); + return (fetch("/" + path) + .then(function (result) { return result.text(); })); } file.read = read; /** * @author fenris */ - function read_buffer(path) { - var nm_fs = require("fs"); - return (new Promise(function (resolve, reject) { - nm_fs.readFile(path, { - "flag": "r" - }, function (error, content) { - if (error == null) { - resolve(content); - } - else { - reject(error); - } - }); - })); - } - file.read_buffer = read_buffer; - /** - * @author fenris - */ - function read_stdin() { - return (new Promise(function (resolve, reject) { - var input_raw = ""; - process.stdin.setEncoding("utf8"); - process.stdin.on("readable", function () { - var chunk; - while ((chunk = process.stdin.read()) !== null) { - input_raw += chunk; - } - }); - process.stdin.on("end", function () { - resolve(input_raw); - }); - })); - } - file.read_stdin = read_stdin; - /** - * @author fenris - */ - function write(path, content, options) { - if (options === void 0) { options = {}; } - options = Object.assign({ - "encoding": "utf-8" - }, options); - var nm_fs = require("fs"); - return (new Promise(function (resolve, reject) { - nm_fs.writeFile(path, content, { - "encoding": options.encoding, - "flag": "w" - }, function (error) { - if (error == null) { - resolve(undefined); - } - else { - reject(error); - } - }); - })); + function write(path, content) { + return Promise.reject(new Error("not implemented / not possible")); } file.write = write; /** * @author fenris */ - function write_buffer(path, content, options) { - if (options === void 0) { options = {}; } - options = Object.assign({}, options); - var nm_fs = require("fs"); - return (new Promise(function (resolve, reject) { - nm_fs.writeFile(path, content, { - "flag": "w" - }, function (error) { - if (error == null) { - resolve(undefined); - } - else { - reject(error); - } + function blob_read_text(blob) { + return (lib_plankton.call.promise_make(function (resolve, reject) { + var reader = (new FileReader()); + reader.addEventListener("load", function (event) { + resolve((reader.result)); }); + reader.addEventListener("error", function (event) { + reject(new Error("reading file failed")); + }); + reader.addEventListener("abort", function (event) { + reject(new Error("reading file aborted")); + }); + reader.readAsText(blob); })); } - file.write_buffer = write_buffer; + file.blob_read_text = blob_read_text; + /** + * @author fenris + */ + function blob_read_arraybuffer(blob) { + return (lib_plankton.call.promise_make(function (resolve, reject) { + var reader = (new FileReader()); + reader.addEventListener("load", function (event) { + resolve((reader.result)); + }); + reader.addEventListener("error", function (event) { + reject(new Error("reading file failed")); + }); + reader.addEventListener("abort", function (event) { + reject(new Error("reading file aborted")); + }); + reader.readAsArrayBuffer(blob); + })); + } + file.blob_read_arraybuffer = blob_read_arraybuffer; + /** + * @author fenris + */ + function blob_read_dataurl(blob) { + return (lib_plankton.call.promise_make(function (resolve, reject) { + var reader = new FileReader(); + reader.addEventListener("load", function (event) { + resolve((reader.result)); + }); + reader.addEventListener("error", function (event) { + reject(new Error("reading file failed")); + }); + reader.addEventListener("abort", function (event) { + reject(new Error("reading file aborted")); + }); + reader.readAsDataURL(blob); + })); + } + file.blob_read_dataurl = blob_read_dataurl; + /** + * @author fenris + */ + function blob_write_text(text) { + var blob = (new Blob([text], { "type": "text/plain" })); + return lib_plankton.call.promise_resolve(blob); + } + file.blob_write_text = blob_write_text; + })(file = lib_plankton.file || (lib_plankton.file = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + function map_clear(map_forEach, map_delete) { + map_forEach((value, key) => { + map_delete(key); + }); + } + structures.map_clear = map_clear; + /** + * @author fenris + */ + function map_keys(map_forEach) { + let keys = []; + map_forEach((value, key) => { + keys.push(key); + }); + return keys; + } + structures.map_keys = map_keys; + /** + * @author fenris + */ + function map_values(map_forEach) { + let values = []; + map_forEach((value, key) => { + values.push(value); + }); + return values; + } + structures.map_values = map_values; + /** + * @author fenris + */ + function map_pairs(map_forEach) { + let pairs = []; + map_forEach((value, key) => { + let pair = { "key": key, "value": value }; + pairs.push(pair); + }); + return pairs; + } + structures.map_pairs = map_pairs; + /** + * @author fenris + */ + function map_toString(map_forEach, show_key = instance_show, show_value = instance_show) { + return ("[" + + + (map_pairs(map_forEach) + .map(({ "key": key, "value": value }) => (show_key(key) + + + " -> " + + + show_value(value))) + .join(", ")) + + + "]"); + } + structures.map_toString = map_toString; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_mapbase { + /** + * @desc [constructor] + */ + constructor() { + } + /** + * @desc [mutator] + */ + clear() { + return (structures.map_clear((procedure) => this.forEach(procedure), (key) => this.delete(key))); + } + /** + */ + get_safe(key) { + try { + const value = this.get(key); + return lib_plankton.pod.class_pod.filled(value); + } + catch (error) { + return lib_plankton.pod.class_pod.empty(); + } + } + /** + * @desc [accessor] + */ + keys() { + return (structures.map_keys((procedure) => this.forEach(procedure))); + } + /** + * @desc [accessor] + */ + values() { + return (structures.map_values((procedure) => this.forEach(procedure))); + } + /** + * @desc [accessor] + */ + pairs() { + return (structures.map_pairs((procedure) => this.forEach(procedure))); + } + /** + * @desc [accessor] + */ + toString(show_key = instance_show, show_value = instance_show) { + return (structures.map_toString((procedure) => this.forEach(procedure), show_key, show_value)); + } + } + structures.class_mapbase = class_mapbase; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_pair { + /** + * @author fenris + */ + constructor(first, second) { + this.first = first; + this.second = second; + } + /** + * @desc [accessor] [getter] + * @author fenris + */ + first_get() { + return this.first; + } + /** + * @desc [accessor] [getter] + * @author fenris + */ + second_get() { + return this.second; + } + /** + * @desc [mutator] [setter] + * @author fenris + */ + first_set(first) { + this.first = first; + } + /** + * @desc [mutator] [setter] + * @author fenris + */ + second_set(second) { + this.second = second; + } + /** + * @desc [accessor] + * @author fenris + */ + swap() { + return (new class_pair(this.second, this.first)); + } + /** + * @desc [accessor] + * @author fenris + */ + transform(transform_first, transform_second) { + return (new class_pair(transform_first(this.first), transform_second(this.second))); + } + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _clone() { + return (new class_pair(instance_clone(this.first), instance_clone(this.second))); + } + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _hash() { + return ("pair_" + instance_hash(this.first) + "_" + instance_hash(this.second) + ""); + } + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _collate(pair) { + return (instance_collate(this.first, pair.first) + && + instance_collate(this.second, pair.second)); + } + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _show() { + return ("(" + instance_show(this.first) + "," + instance_show(this.second) + ")"); + } + } + structures.class_pair = class_pair; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + function set_construct(collation, elements = []) { + let subject = { + "elements": [] + }; + elements.forEach(element => set_add(collation, subject, element)); + return subject; + } + structures.set_construct = set_construct; + /** + * @desc [accessor] + * @author fenris + */ + function set_size(subject) { + return subject.elements.length; + } + structures.set_size = set_size; + /** + * @desc [accessor] + * @author fenris + */ + function set_has(collation, subject, element) { + return (subject.elements + .some(element_ => collation(element, element_))); + } + structures.set_has = set_has; + /** + * @desc [mutator] + * @author fenris + */ + function set_add(collation, subject, element) { + if (!set_has(collation, subject, element)) { + subject.elements.push(element); + } + } + structures.set_add = set_add; + /** + * @desc [mutator] + * @author fenris + */ + function set_pop(subject) { + if (subject.elements.length == 0) { + return lib_plankton.pod.make_empty(); + } + else { + return lib_plankton.pod.make_filled(subject.elements.pop()); + } + } + structures.set_pop = set_pop; + /** + * @desc [accessor] + * @author fenris + */ + function set_forEach(subject, function_) { + subject.elements.forEach(element => function_(element)); + } + structures.set_forEach = set_forEach; + /** + * @desc [accessor] + * @author fenris + */ + function set_map(collation, subject, transformator) { + return (set_construct(collation, subject.elements.map(element_from => transformator(element_from)))); + } + structures.set_map = set_map; + /** + * @desc [accessor] + * @author fenris + */ + function set_filter(subject, predicate) { + return (set_construct((x, y) => false, subject.elements.filter(element => predicate(element)))); + } + structures.set_filter = set_filter; + /** + * @desc [accessor] + * @author fenris + */ + function set_dump(subject) { + return subject.elements; + } + structures.set_dump = set_dump; + /** + * @desc [accessor] + * @author fenris + */ + function set_subset(collation, subject, object) { + return (subject.elements + .every(element => set_has(collation, object, element))); + } + structures.set_subset = set_subset; + /** + * @desc [accessor] + * @author fenris + */ + function set_superset(collation, subject, object) { + return (object.elements + .every(element => set_has(collation, subject, element))); + } + structures.set_superset = set_superset; + /** + * @desc [accessor] + * @author fenris + */ + function set_equals(collation, subject, object) { + return (set_subset(collation, subject, object) + && + set_superset(collation, subject, object)); + } + structures.set_equals = set_equals; + /** + * @desc [accessor] + * @author fenris + */ + function set_toString(show_element, subject) { + return ("{" + + + subject.elements.map(element => show_element(element)).join(",") + + + "}"); + } + structures.set_toString = set_toString; + /** + * @desc [accessor] + * @author fenris + */ + function set_empty(subject) { + return (subject.elements.length == 0); + } + structures.set_empty = set_empty; + /** + * @desc [accessor] + * @author fenris + */ + function set_union(collation, subject, object) { + let result = set_construct(collation, []); + subject.elements.forEach(element => set_add(collation, result, element)); + object.elements.forEach(element => set_add(collation, result, element)); + return result; + } + structures.set_union = set_union; + /** + * @desc [accessor] + * @author fenris + */ + function set_intersection(collation, subject, object) { + let result = set_construct(collation, []); + subject.elements.forEach(element => { + if (set_has(collation, object, element)) { + set_add(collation, result, element); + } + }); + return result; + } + structures.set_intersection = set_intersection; + /** + * @desc [accessor] + * @author fenris + */ + function set_difference(collation, subject, object) { + let result = set_construct(collation, []); + subject.elements.forEach(element => { + if (!set_has(collation, object, element)) { + set_add(collation, result, element); + } + }); + return result; + } + structures.set_difference = set_difference; + /** + * @desc [accessor] + * @author fenris + */ + function set_symmetric_difference(collation, subject, object) { + // X $ Y = (X ∪ Y) \ (X ∩ Y) + return (set_difference(collation, set_union(collation, subject, object), set_intersection(collation, subject, object))); + } + structures.set_symmetric_difference = set_symmetric_difference; + /** + * @author fenris + */ + function set_union_all(collation, sets) { + return (sets.reduce((x, y) => ((x == null) ? y : set_union(collation, x, y)), null)); + } + structures.set_union_all = set_union_all; + /** + * @author fenris + */ + function set_intersection_all(collation, sets) { + return (sets.reduce((x, y) => ((x == null) ? y : set_intersection(collation, x, y)), null)); + } + structures.set_intersection_all = set_intersection_all; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_set { + /** + * @author fenris + */ + constructor(elements = [], equality = instance_collate /**/) { + this.equality = equality; + this.subject = structures.set_construct(equality, elements); + } + /** + * @author fenris + */ + static from_subject(equality = instance_collate /**/, subject) { + let set = new class_set([], equality); + set.subject = subject; + return set; + } + /** + * @desc [accessor] + * @author fenris + */ + size() { + return structures.set_size(this.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + has(element) { + return structures.set_has(this.equality, this.subject, element); + } + /** + * @desc [mutator] + * @author fenris + */ + add(element) { + return structures.set_add(this.equality, this.subject, element); + } + /** + * @desc [mutator] + * @author fenris + */ + pop() { + return (new lib_plankton.pod.class_pod(structures.set_pop(this.subject))); + } + /** + * @desc [accessor] + * @author fenris + */ + forEach(function_) { + return structures.set_forEach(this.subject, function_); + } + /** + * @desc [accessor] + * @author fenris + */ + map(transformator, equality = instance_collate /**/) { + return (class_set.from_subject(equality, structures.set_map(equality, this.subject, transformator))); + } + /** + * @desc [accessor] + * @author fenris + */ + filter(predicate) { + return (class_set.from_subject(this.equality, structures.set_filter(this.subject, predicate))); + } + /** + * @desc [accessor] + * @author fenris + */ + dump() { + return structures.set_dump(this.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + subset(set) { + return structures.set_subset(this.equality, this.subject, set.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + superset(set) { + return structures.set_superset(this.equality, this.subject, set.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + equals(set) { + return structures.set_equals(this.equality, this.subject, set.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + toString() { + return structures.set_toString(instance_show, this.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + empty() { + return structures.set_empty(this.subject); + } + /** + * @desc [accessor] + * @author fenris + */ + union(set) { + return (class_set.from_subject(this.equality, structures.set_union(this.equality, this.subject, set.subject))); + } + /** + * @desc [accessor] + * @author fenris + */ + intersection(set) { + return (class_set.from_subject(this.equality, structures.set_intersection(this.equality, this.subject, set.subject))); + } + /** + * @desc [accessor] + * @author fenris + */ + difference(set) { + return (class_set.from_subject(this.equality, structures.set_difference(this.equality, this.subject, set.subject))); + } + /** + * @desc [accessor] + * @author fenris + */ + symmetric_difference(set) { + return (class_set.from_subject(this.equality, structures.set_symmetric_difference(this.equality, this.subject, set.subject))); + } + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _collate(set) { + return structures.set_equals(this.equality, this.subject, set.subject); + } + /** + * @desc [accessor] [implementation] + * @author fenris + */ + _show() { + return structures.set_toString(instance_show, this.subject); + } + /** + * @author fenris + */ + static union_all(sets) { + if (sets.length === 0) { + let message = "empty union"; + // console.warn("--", message); + return (new class_set()); + } + else { + return (class_set.from_subject(sets[0].equality, structures.set_union_all(sets[0].equality, sets.map(set => set.subject)))); + } + } + /** + * @author fenris + */ + static intersection_all(sets) { + if (sets.length === 0) { + let message = "empty intersection is not defined"; + throw (new Error(message)); + } + else { + return (class_set.from_subject(sets[0].equality, structures.set_intersection_all(sets[0].equality, sets.map(set => set.subject)))); + } + } + } + structures.class_set = class_set; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + function stack_construct() { + return { + "elements": [] + }; + } + structures.stack_construct = stack_construct; + /** + * @author fenris + */ + function stack_size(subject) { + return subject.elements.length; + } + structures.stack_size = stack_size; + /** + * @author fenris + */ + function stack_scan(subject) { + if (stack_size(subject) == 0) { + let message = "empty"; + throw (new Error(message)); + } + else { + return subject.elements[subject.elements.length - 1]; + } + } + structures.stack_scan = stack_scan; + /** + * @author fenris + */ + function stack_take(subject) { + let element = stack_scan(subject); + subject.elements.pop(); + return element; + } + structures.stack_take = stack_take; + /** + * @author fenris + */ + function stack_give(subject, element) { + subject.elements.push(element); + } + structures.stack_give = stack_give; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_stack { + /** + * @author fenris + */ + constructor() { + this.subject = structures.stack_construct(); + } + /** + * @override + * @author fenris + */ + size() { + return structures.stack_size(this.subject); + } + /** + * @override + * @author fenris + */ + scan() { + return structures.stack_scan(this.subject); + } + /** + * @override + * @author fenris + */ + take() { + return structures.stack_take(this.subject); + } + /** + * @override + * @author fenris + */ + give(element) { + return structures.stack_give(this.subject, element); + } + } + structures.class_stack = class_stack; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + function queue_construct() { + return { + "elements": [] + }; + } + structures.queue_construct = queue_construct; + /** + * @author fenris + */ + function queue_size(subject) { + return subject.elements.length; + } + structures.queue_size = queue_size; + /** + * @author fenris + */ + function queue_scan(subject) { + if (queue_size(subject) == 0) { + let message = "empty"; + throw (new Error(message)); + } + else { + return subject.elements[0]; + } + } + structures.queue_scan = queue_scan; + /** + * @author fenris + */ + function queue_take(subject) { + let element = queue_scan(subject); + subject.elements.shift(); + return element; + } + structures.queue_take = queue_take; + /** + * @author fenris + */ + function queue_give(subject, element) { + subject.elements.push(element); + } + structures.queue_give = queue_give; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_queue { + /** + * @author fenris + */ + constructor() { + this.subject = structures.queue_construct(); + } + /** + * @override + * @author fenris + */ + size() { + return structures.queue_size(this.subject); + } + /** + * @override + * @author fenris + */ + scan() { + return structures.queue_scan(this.subject); + } + /** + * @override + * @author fenris + */ + take() { + return structures.queue_take(this.subject); + } + /** + * @override + * @author fenris + */ + give(element) { + return structures.queue_give(this.subject, element); + } + } + structures.class_queue = class_queue; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + function simplemap_construct() { + return { + "memory": {} + }; + } + structures.simplemap_construct = simplemap_construct; + /** + * @author fenris + */ + function simplemap_has(subject, key) { + return (key in subject.memory); + } + structures.simplemap_has = simplemap_has; + /** + * @author fenris + */ + function simplemap_get_safe(subject, key) { + if (key in subject.memory) { + return lib_plankton.pod.make_filled(subject.memory[key]); + } + else { + return lib_plankton.pod.make_empty(); + } + } + structures.simplemap_get_safe = simplemap_get_safe; + /** + * @author fenris + */ + function simplemap_get(subject, key, fallback = lib_plankton.pod.make_empty()) { + if (key in subject.memory) { + return subject.memory[key]; + } + else { + if (!lib_plankton.pod.is_filled(fallback)) { + throw (new Error("key not found")); + } + else { + return lib_plankton.pod.cull(fallback); + } + } + } + structures.simplemap_get = simplemap_get; + /** + * @author fenris + */ + function simplemap_set(subject, key, value) { + subject.memory[key] = value; + } + structures.simplemap_set = simplemap_set; + /** + * @author fenris + */ + function simplemap_delete(subject, key) { + delete subject.memory[key]; + } + structures.simplemap_delete = simplemap_delete; + /** + * @author fenris + */ + function simplemap_clear(subject) { + // subject.memory = {}; + Object.keys(subject.memory).forEach(key => { delete subject.memory[key]; }); + } + structures.simplemap_clear = simplemap_clear; + /** + * @author fenris + */ + function simplemap_forEach(subject, function_) { + Object.keys(subject.memory).forEach(key => { + let value = subject.memory[key]; + function_(value, key); + }); + } + structures.simplemap_forEach = simplemap_forEach; + /** + * @author fenris + */ + function simplemap_from_object(object) { + let subject = simplemap_construct(); + Object.keys(object).forEach((key) => { + let value = object[key]; + subject.memory[key] = value; + }); + return subject; + } + structures.simplemap_from_object = simplemap_from_object; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_simplemap extends structures.class_mapbase { + /** + * @author fenris + */ + static make() { + const subject = structures.simplemap_construct(); + return (new class_simplemap(subject)); + } + /** + * @author fenris + */ + static from_object(object) { + return (new class_simplemap(structures.simplemap_from_object(object))); + } + /** + * @author fenris + * @desc [constructor] + */ + constructor(subject = structures.simplemap_construct()) { + super(); + this.subject = subject; + } + /** + * @author fenris + * @implementation + */ + has(key) { + return structures.simplemap_has(this.subject, key); + } + /** + * @author fenris + * @implementation + */ + get(key, fallback = lib_plankton.pod.class_pod.empty()) { + return structures.simplemap_get(this.subject, key, fallback.tear()); + } + /** + * @author fenris + * @implementation + */ + set(key, value) { + return structures.simplemap_set(this.subject, key, value); + } + /** + * @author fenris + * @implementation + */ + delete(key) { + return structures.simplemap_delete(this.subject, key); + } + /** + * @author fenris + * @implementation + */ + forEach(procedure) { + return structures.simplemap_forEach(this.subject, procedure); + } + } + structures.class_simplemap = class_simplemap; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + function hashmap_construct(hashing, pairs) { + const subject = { + "core": structures.simplemap_construct(), + "hashing": hashing, + }; + pairs.forEach((pair) => { + hashmap_set(subject, pair.key, pair.value); + }); + return subject; + } + structures.hashmap_construct = hashmap_construct; + /** + * @author fenris + */ + function hashmap_has(subject, key) { + const key_ = subject.hashing(key); + return structures.simplemap_has(subject.core, key_); + } + structures.hashmap_has = hashmap_has; + /** + * @author fenris + */ + function hashmap_get(subject, key, fallback = lib_plankton.pod.make_empty()) { + /* + we have to adjust the fallback argument, so that it can be used in our underlying simplemap core + therefore we pack the value together with any key as dummy + */ + const fallback_ = lib_plankton.pod.propagate(fallback, (value) => ({ + "key": key, + "value": value + })); + const key_ = subject.hashing(key); + return (structures.simplemap_get(subject.core, key_, fallback_) + .value); + } + structures.hashmap_get = hashmap_get; + /** + * @author fenris + */ + function hashmap_set(subject, key, value) { + const key_ = subject.hashing(key); + return (structures.simplemap_set(subject.core, key_, { "key": key, "value": value })); + } + structures.hashmap_set = hashmap_set; + /** + * @author fenris + */ + function hashmap_delete(subject, key) { + const key_ = subject.hashing(key); + return (structures.simplemap_delete(subject.core, key_)); + } + structures.hashmap_delete = hashmap_delete; + /** + * @author fenris + */ + function hashmap_clear(subject) { + return structures.simplemap_clear(subject.core); + } + structures.hashmap_clear = hashmap_clear; + /** + * @author fenris + */ + function hashmap_forEach(subject, procedure) { + return (structures.simplemap_forEach(subject.core, ({ "key": key, "value": value }, key_) => { + procedure(value, key); + })); + } + structures.hashmap_forEach = hashmap_forEach; + /** + * @author fenris + */ + function hashmap_dump(subject) { + let result = []; + hashmap_forEach(subject, (value, key) => { + result.push({ + "key": key, + "value": value, + }); + }); + return result; + } + structures.hashmap_dump = hashmap_dump; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_hashmap extends structures.class_mapbase { + /** + * @author fenris + * @desc [constructor] + */ + constructor(hashing = instance_hash, pairs = []) { + super(); + this.subject = structures.hashmap_construct(hashing, pairs); + } + /** + * @author fenris + * @implementation + */ + has(key) { + return structures.hashmap_has(this.subject, key); + } + /** + * @author fenris + * @implementation + */ + get(key, fallback = lib_plankton.pod.class_pod.empty()) { + return structures.hashmap_get(this.subject, key, fallback.tear()); + } + /** + * @author fenris + * @implementation + */ + set(key, value) { + return structures.hashmap_set(this.subject, key, value); + } + /** + * @author fenris + * @implementation + */ + delete(key) { + return structures.hashmap_delete(this.subject, key); + } + /** + * @author fenris + * @implementation + */ + forEach(procedure) { + return structures.hashmap_forEach(this.subject, procedure); + } + } + structures.class_hashmap = class_hashmap; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { /** */ - function delete_(path) { - var nm_fs = require("fs"); - return (new Promise(function (resolve, reject) { - nm_fs.unlink(path, function () { - resolve(undefined); - }); - })); + function collatemap_construct() { + return { + "pairs": [], + }; } - file.delete_ = delete_; - })(file = lib_plankton.file || (lib_plankton.file = {})); + structures.collatemap_construct = collatemap_construct; + /** + */ + function collatemap_has(collation, subject, key) { + return subject.pairs.some((pair) => collation(key, pair.key)); + } + structures.collatemap_has = collatemap_has; + /** + * @todo use .find + */ + function collatemap_get(collation, subject, key, fallback) { + let value; + const found = (subject.pairs + .some((pair) => { + if (collation(key, pair.key)) { + value = pair.value; + return true; + } + else { + return false; + } + })); + if (found) { + return value; + } + else { + if (!lib_plankton.pod.is_filled(fallback)) { + throw (new Error("key not found")); + } + else { + return lib_plankton.pod.cull(fallback); + } + } + } + structures.collatemap_get = collatemap_get; + /** + */ + function collatemap_set(collation, subject, key, value) { + const found = (subject.pairs + .some((pair) => { + if (collation(key, pair.key)) { + pair.value = value; + return true; + } + else { + return false; + } + })); + if (found) { + // nothing + } + else { + subject.pairs.push({ + "key": key, + "value": value + }); + } + } + structures.collatemap_set = collatemap_set; + /** + */ + function collatemap_delete(collation, subject, key) { + let index; + const found = (subject.pairs + .some((pair, index_) => { + if (collation(key, pair.key)) { + index = index_; + return true; + } + else { + return false; + } + })); + if (found) { + subject.pairs.splice(index, 1); + } + else { + // do nothing + } + } + structures.collatemap_delete = collatemap_delete; + /** + */ + function collatemap_forEach( + // collation : type_collation, + subject, function_) { + subject.pairs + .forEach((pair) => { + function_(pair.value, pair.key); + }); + } + structures.collatemap_forEach = collatemap_forEach; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_collatemap extends structures.class_mapbase { + /** + * @author fenris + * @desc [constructor] + */ + constructor(collation = instance_collate) { + super(); + this.subject = structures.collatemap_construct(); + this.collation = collation; + } + /** + * @author fenris + * @implementation + */ + has(key) { + return structures.collatemap_has(this.collation, this.subject, key); + } + /** + * @author fenris + * @implementation + */ + get(key, fallback = lib_plankton.pod.class_pod.empty()) { + return structures.collatemap_get(this.collation, this.subject, key, fallback.tear()); + } + /** + * @author fenris + * @implementation + */ + set(key, value) { + return structures.collatemap_set(this.collation, this.subject, key, value); + } + /** + * @author fenris + * @implementation + */ + delete(key) { + return structures.collatemap_delete(this.collation, this.subject, key); + } + /** + * @author fenris + * @implementation + */ + forEach(procedure) { + return structures.collatemap_forEach(this.subject, procedure); + } + } + structures.class_collatemap = class_collatemap; + /** + * @author fenris + * @deprecated + */ + structures.class_map = class_collatemap; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_graph { + /** + * @author fenris + */ + constructor(equality = null, nodes = [], edges = []) { + if (equality == null) { + // equality = ((node1, node2) => lib_plankton.trait.call("collatable", "collate", {"kind": "auto"}, {"first": node1, "second": node2})); + equality = instance_collate /**/; + } + this.equality = equality; + this.nodes = nodes; + this.edges = edges; + } + /** + * @desc [accessor] [getter] + * @author fenris + */ + nodes_get() { + return this.nodes; + } + /** + * @desc [mutator] + * @author fenris + */ + add_node(node) { + this.nodes.push(node); + } + /** + * @desc [accessor] [getter] + * @author fenris + */ + edges_get() { + return this.edges; + } + /** + * @desc [mutator] + * @author fenris + */ + add_edge(edge) { + this.edges.push(edge); + } + /** + * @desc [accessor] + * @author fenris + */ + has(node) { + return this.nodes.some(node_ => this.equality(node, node_)); + } + /** + * @desc [accessor] + * @author fenris + */ + outgoing(node) { + return this.edges.filter(edge => this.equality(edge.from, node)); + } + /** + * @desc [accessor] + * @author fenris + */ + incoming(node) { + return this.edges.filter(edge => this.equality(edge.to, node)); + } + /** + * @desc [accessor] + * @author fenris + */ + without(pivot) { + return (new class_graph(this.equality, this.nodes.filter(node => (!this.equality(node, pivot))), this.edges.filter(edge => ((!this.equality(edge.from, pivot)) && (!this.equality(edge.to, pivot)))))); + } + /** + * @desc [accessor] returns the topologic sorting of the nodes (if it exists) + * @author fenris + */ + topsort() { + let graph = this; + if (graph.nodes.length == 0) { + return []; + } + else { + let pivot; + let found = graph.nodes.some(node => { + let count = graph.edges.filter(edge => this.equality(edge.to, node)).length; + if (count == 0) { + pivot = node; + return true; + } + else { + // console.info("'" + String(node) + "' has " + count.toString() + " incoming edges"); + return false; + } + }); + if (found) { + return [pivot].concat(graph.without(pivot).topsort()); + } + else { + throw (new Error("circular dependencies found")); + } + } + } + /** + * @desc [accessor] returns the reduced version of a graph representing an order relation (implicit transitivity) + * @author fenris + */ + hasse() { + return (new class_graph(this.equality, this.nodes, this.edges.filter((edge) => { + let reachable = (this.outgoing(edge.from) + .map((edge_) => edge_.to) + .map((node) => this.outgoing(node).map((edge_) => edge_.to)) + .reduce((x, y) => x.concat(y), [])); + return (!reachable.some(node => this.equality(node, edge.to))); + }))); + } + /** + * @author fenris + */ + output_dot({ "extract_id": extract_id = null, "extract_label": extract_label = null, "rotate": rotate = false, } = {}) { + let index = node => { + let i = -1; + let found = this.nodes.some((node_, i_) => { + if (this.equality(node, node_)) { + i = i_; + return true; + } + else { + return false; + } + }); + return i; + }; + if (extract_id == null) { + // instance_hash + extract_id = (node => index(node).toFixed(0)); + } + if (extract_label == null) { + extract_label = instance_show; + } + let nodeid = (node) => { + return ("node_" + extract_id(node)); + }; + return ({ + "common": { + "fontname": "Monospace", + "rankdir": (rotate ? "LR" : "TB"), + }, + "nodes": { + "head": { + "fontname": "Monospace", + "style": "filled", + "fillcolor": "0.35+0.6+0.8", + }, + "list": this.nodes_get() + .map((node, index) => { + return { + "id": nodeid(node), + "attributes": { + "label": extract_label(node), + } + }; + }), + }, + "edges": { + "head": { + "fontname": "Monospace" + }, + "list": this.edges_get() + .map((edge, index) => { + return { + "id_from": nodeid(edge.from), + "id_to": nodeid(edge.to), + "attributes": {} + }; + }), + } + }); + } + } + structures.class_graph = class_graph; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + structures.relation_le = "le"; + structures.relation_ge = "ge"; + structures.relation_lt = "lt"; + structures.relation_gt = "gt"; + structures.relation_eq = "eq"; + /** + * @author fenris + */ + function binnode_construct(data) { + return { + "data": data, + "left": null, + "right": null, + "depth": 1, + }; + } + /** + * @author fenris + */ + function binnode_isdef(subject) { + return (!(subject === null)); + } + /** + * @author fenris + */ + function binnode_child_get(subject, left) { + if (left) { + return subject.left; + } + else { + return subject.right; + } + } + /** + * @author fenris + */ + function binnode_child_set(subject, left, value) { + if (left) { + subject.left = value; + } + else { + subject.right = value; + } + } + /** + * @author fenris + * @todo remove later on + */ + function binnode_update_depth(subject) { + subject.depth = (1 + + + Math.max((binnode_isdef(subject.left) ? subject.left.depth : 0), (binnode_isdef(subject.right) ? subject.right.depth : 0))); + } + /** + * @author fenris + * @todo remove later + */ + function binnode_depth(subject) { + return (1 + + + Math.max((binnode_isdef(subject.left) ? binnode_depth(subject.left) : 0), (binnode_isdef(subject.right) ? binnode_depth(subject.right) : 0))); + } + /** + * @author fenris + * @todo remove later on + */ + function binnode_check_depths(subject) { + return ((binnode_isdef(subject.left) ? binnode_check_depths(subject.left) : true) + && + (binnode_isdef(subject.right) ? binnode_check_depths(subject.right) : true) + && + (subject.depth === binnode_depth(subject))); + } + /** + * @author fenris + */ + function binnode_imbalance(subject) { + if (true) { + return ((binnode_isdef(subject.right) ? subject.right.depth : 0) + - + (binnode_isdef(subject.left) ? subject.left.depth : 0)); + } + else { + return ((binnode_isdef(subject.right) ? binnode_depth(subject.right) : 0) + - + (binnode_isdef(subject.left) ? binnode_depth(subject.left) : 0)); + } + } + /** + * @author fenris + */ + function binnode_insert(compare, subject, data) { + let node; + const left = compare(data, subject.data); + if (binnode_child_get(subject, left) == null) { + node = binnode_construct(data); + binnode_child_set(subject, left, node); + } + else { + node = binnode_insert(compare, binnode_child_get(subject, left), data); + } + binnode_update_depth(subject); + return node; + } + /** + * @author fenris + */ + function binnode_update_parent(subject, parent, child) { + if (binnode_isdef(parent)) { + if (parent.left === subject) { + parent.left = child; + } + else if (parent.right === subject) { + parent.right = child; + } + else { + console.warn("-- updating parent failed :/"); + } + } + } + /** + * @author fenris + */ + function binnode_drag(subject, left, parent = null) { + let child = binnode_child_get(subject, !left); + let grandchild = binnode_child_get(child, left); + binnode_child_set(subject, !left, grandchild); + binnode_child_set(child, left, subject); + binnode_update_parent(subject, parent, child); + return child; + } + /** + * @author fenris + */ + function binnode_rebalance(subject, parent = null) { + // rebalance children first + { + if (binnode_isdef(subject.left)) { + binnode_rebalance(subject.left, subject); + if (binnode_isdef(parent)) + binnode_update_depth(parent); + } + if (binnode_isdef(subject.right)) { + binnode_rebalance(subject.right, subject); + if (binnode_isdef(parent)) + binnode_update_depth(parent); + } + } + // now rebalance the current node + { + const imbalance = binnode_imbalance(subject); + if (Math.abs(imbalance) >= 2) { + const left = (imbalance < 0); + let child = binnode_child_get(subject, left); + const imbalance_ = binnode_imbalance(child); + if (imbalance * imbalance_ < 0) { + // sub dragging + let child_ = binnode_drag(child, left, subject); + binnode_update_depth(binnode_child_get(child_, left)); + binnode_update_depth(child_); + } + // core dragging + let subject_ = binnode_drag(subject, !left, parent); + binnode_update_depth(subject); + binnode_update_depth(subject_); + if (parent != null) + binnode_update_depth(parent); + return subject_; + } + else { + return subject; + } + } + } + /** + * @author fenris + * @desc see misc/foo.py for some interesting facts explaining the handlers + * @desc lists all nodes that match the given piece of the data with the given relation + */ + function binnode_search(compare, subject, data, relation = "eq") { + const x = subject.data; + const y = data; + const situation_le = compare(x, y); + const situation_ge = compare(y, x); + const situation_lt = (situation_le && (!situation_ge)); + const situation_gt = ((!situation_le) && situation_ge); + const situation_eq = (situation_le && situation_ge); + // recursive call + const deepen = (left) => { + const child = binnode_child_get(subject, left); + if (binnode_isdef(child)) { + return binnode_search(compare, child, data, relation); + } + else { + return []; + } + }; + /* + handlers that describe how the search is to be executed; + if "subject", the current node is added to the search results (~ situation) + if "left", the left subtree is regarded as well + if "right", the right subtree is regarded as well + */ + const handlers = { + "lt": { + "subject": (situation_lt), + "left": (true), + "right": (situation_lt), + }, + "le": { + "subject": (situation_le), + "left": (true), + "right": (situation_lt), + }, + "eq": { + "subject": (situation_eq), + "left": (!situation_lt), + "right": (situation_lt), + }, + "ge": { + "subject": (situation_ge), + "left": (!situation_lt), + "right": (true), + }, + "gt": { + "subject": (situation_gt), + "left": (situation_gt), + "right": (true), + }, + }; + if (relation in handlers) { + let result = []; + const handler = handlers[relation]; + if (handler.subject) { + result = result.concat([subject]); + } + if (handler.left) { + result = result.concat(deepen(true)); + } + if (handler.right) { + result = result.concat(deepen(false)); + } + return result; + } + else { + const message = `unhandled relation '${relation}'`; + throw (new Error(message)); + } + } + /** + * @author fenris + * @desc returns the first found node, which matches the given data or "null" if not found + */ + function binnode_find(compare, subject, data) { + const le = compare(data, subject.data); + const ge = compare(subject.data, data); + if (le && ge) { + return subject; + } + else { + const child = binnode_child_get(subject, le); + return ((binnode_isdef(child)) + ? binnode_find(compare, child, data) + : null); + } + } + /** + * @author fenris + */ + function binnode_traverse(subject) { + return ([] + .concat((binnode_isdef(subject.left) ? binnode_traverse(subject.left) : [])) + .concat([subject.data]) + .concat((binnode_isdef(subject.right) ? binnode_traverse(subject.right) : []))); + } + /** + * @author fenris + */ + function binnode_show(show_data, subject, depth = 0) { + const indent = (n, sequence = " ") => sequence["repeat"](n); + let str = ""; + { + str += indent(depth); + str += show_data(subject.data); + str += (" " + "[" + binnode_imbalance(subject).toFixed(0) + "]"); + str += (" " + "{" + binnode_depth(subject).toFixed(0) + "/" + subject.depth.toFixed(0) + "}"); + str += "\n"; + } + if (binnode_isdef(subject.left) || binnode_isdef(subject.right)) { + // left + { + if (binnode_isdef(subject.left)) { + str += binnode_show(show_data, subject.left, depth + 1); + } + else { + str += (indent(depth + 1) + "~" + "\n"); + } + } + // right + { + if (binnode_isdef(subject.right)) { + str += binnode_show(show_data, subject.right, depth + 1); + } + else { + str += (indent(depth + 1) + "~" + "\n"); + } + } + } + return str; + } + /** + * @author fenris + */ + function bintree_construct() { + return { + "root": null, + }; + } + structures.bintree_construct = bintree_construct; + /** + * @author fenris + */ + function bintree_depth(subject) { + return ((binnode_isdef(subject.root)) + ? binnode_depth(subject.root) + : 0); + } + structures.bintree_depth = bintree_depth; + /** + * @author fenris + * @todo remove later on + */ + function bintree_check_depths(subject) { + return ((binnode_isdef(subject.root)) + ? binnode_check_depths(subject.root) + : true); + } + structures.bintree_check_depths = bintree_check_depths; + /** + * @author fenris + */ + function bintree_insert(compare, subject, data, rebalance = true) { + // basic insertion + { + if (binnode_isdef(subject.root)) { + binnode_insert(compare, subject.root, data); + } + else { + subject.root = binnode_construct(data); + } + } + // rebalancing + { + if (rebalance) { + subject.root = binnode_rebalance(subject.root); + } + } + } + structures.bintree_insert = bintree_insert; + /** + * @author fenris + */ + function bintree_search(compare, subject, data, relation = undefined) { + const binnodes = ((binnode_isdef(subject.root)) + ? binnode_search(compare, subject.root, data, relation) + : []); + return binnodes.map(binnode => binnode.data); + } + structures.bintree_search = bintree_search; + /** + * @author fenris + * @deprecated only used for AVL-Tree-Index atm. + */ + function bintree_find(compare, subject, data) { + const binnode = ((binnode_isdef(subject.root)) + ? binnode_find(compare, subject.root, data) + : null); + if (binnode == null) { + const message = "not found"; + throw (new Error(message)); + } + else { + return binnode.data; + } + } + structures.bintree_find = bintree_find; + /** + * @author fenris + */ + function bintree_traverse(subject) { + return ((binnode_isdef(subject.root)) + ? binnode_traverse(subject.root) + : []); + } + structures.bintree_traverse = bintree_traverse; + /** + * @author fenris + */ + function bintree_show(show_data, subject) { + return ((binnode_isdef(subject.root)) + ? binnode_show(show_data, subject.root) + : "--"); + } + structures.bintree_show = bintree_show; + /** + * @author fenris + * @todo tidy up or remove + */ + function binnode_gather(subject, result = { "nodes": [], "edges": [], "lastid": 0 }) { + result.lastid += 1; + const node = { "id": result.lastid, "subject": subject }; + result.nodes.push(node); + if (binnode_isdef(subject.left)) { + // result.lastid += 1; + const node_ = { "id": result.lastid + 1, "subject": subject.left }; + result.edges.push({ "from": node, "to": node_ }); + result = binnode_gather(subject.left, result); + } + if (binnode_isdef(subject.right)) { + // result.lastid += 1; + const node_ = { "id": result.lastid + 1, "subject": subject.right }; + result.edges.push({ "from": node, "to": node_ }); + result = binnode_gather(subject.right, result); + } + return result; + } + /** + * @author fenris + * @todo tidy up or remove + */ + function bintree_to_graph(subject) { + const gathering = binnode_gather(subject.root); + const graph = new structures.class_graph((x, y) => (x.data == y.data), gathering.nodes, gathering.edges); + return graph; + } + structures.bintree_to_graph = bintree_to_graph; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); +})(lib_plankton || (lib_plankton = {})); +/* +This file is part of »bacterio-plankton:structures«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:structures« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:structures« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:structures«. If not, see . + */ +var lib_plankton; +(function (lib_plankton) { + var structures; + (function (structures) { + /** + * @author fenris + */ + class class_bintree { + /** + * @author fenris + */ + constructor(compare = instance_compare) { + this.subject = structures.bintree_construct(); + this.compare = compare; + } + /** + * @author fenris + */ + depth() { + return structures.bintree_depth(this.subject); + } + /** + * @author fenris + */ + insert(data, rebalance = true) { + return structures.bintree_insert(this.compare, this.subject, data, rebalance); + } + /** + * @author fenris + */ + delete(data) { + let message = "not implemented"; + throw (new Error(message)); + } + /** + * @author fenris + */ + search(relation, data) { + return structures.bintree_search(this.compare, this.subject, data, relation); + } + /** + * @author fenris + */ + traverse() { + return structures.bintree_traverse(this.subject); + } + /** + * @author fenris + */ + show() { + return structures.bintree_show(instance_show /**/, this.subject); + } + } + structures.class_bintree = class_bintree; + })(structures = lib_plankton.structures || (lib_plankton.structures = {})); })(lib_plankton || (lib_plankton = {})); /* This file is part of »bacterio-plankton:code«. @@ -2174,17 +4469,30 @@ var lib_plankton; /** * @author fenris */ - function encode(x, formatted = false) { - return JSON.stringify(x, undefined, formatted ? "\t" : undefined); + function encode(source, options = {}) { + options = Object.assign({ + "formatted": false, + }, options); + return JSON.stringify(source, undefined, (options.formatted ? "\t" : undefined)); } json.encode = encode; /** * @author fenris */ - function decode(x) { - return JSON.parse(x); + function decode(target) { + return JSON.parse(target); } json.decode = decode; + /** + * @author fenris + */ + function implementation_code() { + return { + "encode": x => encode(x), + "decode": decode, + }; + } + json.implementation_code = implementation_code; })(json = lib_plankton.json || (lib_plankton.json = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -2300,31 +4608,8 @@ var lib_plankton; */ function send(smtp_credentials, sender, receivers, subject, content) { return __awaiter(this, void 0, void 0, function () { - var nm_nodemailer, transporter, info; return __generator(this, function (_a) { - switch (_a.label) { - case 0: - nm_nodemailer = require("nodemailer"); - transporter = nm_nodemailer.createTransport({ - "host": smtp_credentials.host, - "port": smtp_credentials.port, - "secure": false, - "auth": { - "user": smtp_credentials.username, - "pass": smtp_credentials.password - }, - "debug": true - }); - return [4 /*yield*/, transporter.sendMail({ - "from": sender, - "to": receivers.join(", "), - "subject": subject, - "text": content - })]; - case 1: - info = _a.sent(); - return [2 /*return*/]; - } + return [2 /*return*/, Promise.reject(new Error("not implemented"))]; }); }); } @@ -2542,236 +4827,49 @@ var lib_plankton; var log; (function (log) { /** - * output for writing log entries to stdout + * output for writing log entries to web console */ - var class_channel_stdout = /** @class */ (function (_super) { - __extends(class_channel_stdout, _super); - function class_channel_stdout() { + var class_channel_console = /** @class */ (function (_super) { + __extends(class_channel_console, _super); + function class_channel_console() { return _super !== null && _super.apply(this, arguments) || this; } /** */ - class_channel_stdout.prototype.add = function (entry) { - process.stdout.write(("<" + (new Date(Date.now())).toISOString().slice(0, 19) + ">") + class_channel_console.prototype.add = function (entry) { + var _a; + var renderers = (_a = {}, + _a[log.enum_level.debug] = { + "renderer": function (i, d) { return console.log(i, d); }, + "show_level": true + }, + _a[log.enum_level.info] = { + "renderer": function (i, d) { return console.info(i, d); }, + "show_level": false + }, + _a[log.enum_level.notice] = { + "renderer": function (i, d) { return console.log(i, d); }, + "show_level": true + }, + _a[log.enum_level.warning] = { + "renderer": function (i, d) { return console.warn(i, d); }, + "show_level": false + }, + _a[log.enum_level.error] = { + "renderer": function (i, d) { return console.error(i, d); }, + "show_level": false + }, + _a); + var setting = renderers[entry.level]; + setting.renderer(((setting.show_level + ? ("[" + log.level_show(entry.level) + "]" + " ") + : "") + - " " - + - ("[" + log.level_show(entry.level) + "]") - + - " " - + - ("" + entry.incident + "") - + - ": " - + - JSON.stringify(entry.details, undefined, " ") - + - "\n"); + ("" + entry.incident + "")), entry.details); }; - return class_channel_stdout; + return class_channel_console; }(log.class_channel)); - log.class_channel_stdout = class_channel_stdout; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with »bacterio-plankton:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - */ - var class_channel_file = /** @class */ (function (_super) { - __extends(class_channel_file, _super); - /** - * [constructor] - */ - function class_channel_file(path, human_readable) { - var _this = _super.call(this) || this; - _this.path = path; - _this.human_readable = human_readable; - return _this; - } - /** - */ - class_channel_file.prototype.add = function (entry) { - var _this = this; - var nm_fs = require("fs"); - var line = (this.human_readable - ? (("<" + (new Date(Date.now())).toISOString().slice(0, 19) + ">") - + - " " - + - ("[" + log.level_show(entry.level) + "]") - + - " " - + - ("" + entry.incident + "") - + - ": " - + - JSON.stringify(entry.details, undefined, " ") - + - "\n") - : (JSON.stringify({ - "timestamp": lib_plankton.base.get_current_timestamp(), - "level_number": entry.level, - "level_name": log.level_show(entry.level), - "incident": entry.incident, - "details": entry.details - }) - + - "\n")); - nm_fs.writeFile(this.path, line, { - "flag": "a+" - }, function (error) { - if (error !== null) { - process.stderr.write('-- [plankton] could not add log entry to file ' + _this.path + "\n"); - } - else { - // do nothing - } - }); - }; - return class_channel_file; - }(log.class_channel)); - log.class_channel_file = class_channel_file; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with »bacterio-plankton:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - */ - var class_channel_email = /** @class */ (function (_super) { - __extends(class_channel_email, _super); - /** - * [constructor] - */ - function class_channel_email(smtp_credentials, sender, receivers) { - var _this = _super.call(this) || this; - _this.smtp_credentials = smtp_credentials; - _this.sender = sender; - _this.receivers = receivers; - return _this; - } - /** - */ - class_channel_email.prototype.add = function (entry) { - var nm_fs = require("fs"); - lib_plankton.email.send(this.smtp_credentials, this.sender, this.receivers, (("[" + log.level_show(entry.level) + "]") - + - " " - + - ("" + entry.incident + "")), JSON.stringify(entry.details, undefined, " ")); - }; - return class_channel_email; - }(log.class_channel)); - log.class_channel_email = class_channel_email; - })(log = lib_plankton.log || (lib_plankton.log = {})); -})(lib_plankton || (lib_plankton = {})); -/* -This file is part of »bacterio-plankton:log«. - -Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' - - -»bacterio-plankton:log« is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -»bacterio-plankton:lang« is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with »bacterio-plankton:log«. If not, see . - */ -var lib_plankton; -(function (lib_plankton) { - var log; - (function (log) { - /** - * output for desktop notifications via "libnotify" - */ - var class_channel_notify = /** @class */ (function (_super) { - __extends(class_channel_notify, _super); - function class_channel_notify() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - */ - class_channel_notify.prototype.add = function (entry) { - var nm_child_process = require("child_process"); - var command = ("notify-send" - + - " " - + - ("'" - + - ("[" + log.level_show(entry.level) + "]") - + - " " - + - entry.incident - + - "'") - + - " " - + - ("'" - + - (Object.keys(entry.details) - .map(function (key) { return (key + ": " + JSON.stringify(entry.details[key])); }) - .join("\n")) - + - "'")); - nm_child_process.exec(command, function (error, stdout, stderr) { - // do noting - }); - }; - return class_channel_notify; - }(log.class_channel)); - log.class_channel_notify = class_channel_notify; + log.class_channel_console = class_channel_console; })(log = lib_plankton.log || (lib_plankton.log = {})); })(lib_plankton || (lib_plankton = {})); /* @@ -2862,26 +4960,14 @@ var lib_plankton; /** */ function channel_make(description) { - var _a, _b, _c, _d, _e; + var _a; switch (description.kind) { default: { throw (new Error("unhandled log channel kind: " + description.kind)); break; } - case "stdout": { - return (new log.class_channel_minlevel(new log.class_channel_stdout(), translate_level((_a = description.data["threshold"]) !== null && _a !== void 0 ? _a : "debug"))); - break; - } - case "file": { - return (new log.class_channel_minlevel(new log.class_channel_file(((_b = description.data["path"]) !== null && _b !== void 0 ? _b : "/tmp/plankton.log"), false), translate_level((_c = description.data["threshold"]) !== null && _c !== void 0 ? _c : "debug"))); - break; - } - case "email": { - return (new log.class_channel_minlevel(new log.class_channel_email(description.data["smtp_credentials"], description.data["sender"], description.data["receivers"]), translate_level((_d = description.data["threshold"]) !== null && _d !== void 0 ? _d : "debug"))); - break; - } - case "notify": { - return (new log.class_channel_minlevel(new log.class_channel_notify(), translate_level((_e = description.data["threshold"]) !== null && _e !== void 0 ? _e : "debug"))); + case "console": { + return (new log.class_channel_minlevel(new log.class_channel_console(), translate_level((_a = description.data["threshold"]) !== null && _a !== void 0 ? _a : "debug"))); break; } } @@ -2891,8 +4977,7 @@ var lib_plankton; */ function conf_default() { return [ - new log.class_channel_minlevel(new log.class_channel_stdout(), log.enum_level.notice), - new log.class_channel_minlevel(new log.class_channel_notify(), log.enum_level.error), + new log.class_channel_minlevel(new log.class_channel_console(), log.enum_level.notice), ]; } log.conf_default = conf_default; @@ -3030,7 +5115,7 @@ var lib_plankton; */ log.conf_push([ log.channel_make({ - "kind": "stdout", + "kind": "console", "data": { "threshold": "info" } @@ -5865,6 +7950,25 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public License +along with »bacterio-plankton:color«. If not, see . + */ +/* +This file is part of »bacterio-plankton:color«. + +Copyright 2016-2024 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' + + +»bacterio-plankton:color« is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +»bacterio-plankton:color« is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:color«. If not, see . */ @@ -5873,7 +7977,6 @@ var lib_plankton; var color; (function (color_1) { /** - * @author fenris */ function make_hsv({ "hue": hue = 0.0, "saturation": saturation = 0.5, "value": value = 0.5 }) { const h = lib_plankton.math.clamp(0, 1, hue); @@ -5895,7 +7998,6 @@ var lib_plankton; } color_1.make_hsv = make_hsv; /** - * @author fenris */ function make_hsl(model_hsl) { const { "hue": h, "saturation": s, "lightness": l } = model_hsl; @@ -5910,7 +8012,6 @@ var lib_plankton; } color_1.make_hsl = make_hsl; /** - * @author fenris */ function make_rgb(model_rgb) { return { @@ -5923,7 +8024,6 @@ var lib_plankton; } color_1.make_rgb = make_rgb; /** - * @author fenris */ function to_hsv(color) { const { "red": r, "green": g, "blue": b } = color.model; @@ -5958,7 +8058,6 @@ var lib_plankton; } color_1.to_hsv = to_hsv; /** - * @author fenris */ function to_hsl(color) { const { "hue": h, "saturation": s, "value": v } = to_hsv(color); @@ -5973,21 +8072,18 @@ var lib_plankton; } color_1.to_hsl = to_hsl; /** - * @author fenris */ function to_rgb(color) { return color.model; } color_1.to_rgb = to_rgb; /** - * @author fenris */ function to_cmyk(color) { throw (new Error("not implemented")); } color_1.to_cmyk = to_cmyk; /** - * @author fenris */ function add(color1, color2) { const rgb1 = to_rgb(color1); @@ -6000,7 +8096,6 @@ var lib_plankton; } color_1.add = add; /** - * @author fenris */ function multiply(color1, color2) { const rgb1 = to_rgb(color1); @@ -6013,7 +8108,6 @@ var lib_plankton; } color_1.multiply = multiply; /** - * @author fenris * @todo blend through other model? */ function blend(color1, color2, strength = 0.5) { @@ -6028,14 +8122,16 @@ var lib_plankton; } color_1.blend = blend; /** - * @author fenris */ - function mix(color1, color2, strength1 = 1, strength2 = 1) { - return (blend(color1, color2, strength1 / (strength1 + strength2))); + function mix(color1, color2, { "strength1": option_strength1 = 1, "strength2": option_strength2 = 1, } = {}) { + return (blend(color1, color2, (option_strength1 + / + (option_strength1 + + + option_strength2)))); } color_1.mix = mix; /** - * @author fenris */ function output_rgb(color) { const format = (value => Math.round(value * 255).toFixed(0)); @@ -6044,7 +8140,6 @@ var lib_plankton; } color_1.output_rgb = output_rgb; /** - * @author fenris */ function output_hex(color) { const rgb = to_rgb(color); @@ -6060,86 +8155,107 @@ var lib_plankton; } color_1.output_hex = output_hex; /** - * @author fenris */ function output_dot(color) { - let hsv = to_hsv(color); + const hsv = to_hsv(color); let format = function (value) { - let str = value.toFixed(8); - return str; + return value.toFixed(8); }; return ("" + ["hue", "saturation", "value"].map(key => hsv[key]).map(format).join("+") + ""); } color_1.output_dot = output_dot; /** - * @author fenris */ - function give_generic({ "n": n, "offset": offset = 0, "saturation": saturation = undefined, "value": value = undefined, }) { - let hue = (((lib_plankton.math.phi * n) + offset) % 1); - return make_hsv({ "hue": hue, "saturation": saturation, "value": value }); + function give_generic(n, { "offset": option_offset = 0, "saturation": option_saturation = undefined, "value": option_value = undefined, }) { + return make_hsv({ + "hue": (((lib_plankton.math.phi + * + n) + + + option_offset) + % + 1), + "saturation": option_saturation, + "value": option_value, + }); } color_1.give_generic = give_generic; /** - * @author fenris */ - function give_gray(value = 0.5) { - return make_hsv({ "hue": 0, "saturation": 0, "value": value }); + function give_gray({ "value": option_value = 0.5, }) { + return make_hsv({ "hue": 0, "saturation": 0, "value": option_value }); } color_1.give_gray = give_gray; /** - * @author fenris */ function give_black() { - return give_gray(0.0); + return give_gray({ "value": 0.0 }); } color_1.give_black = give_black; /** - * @author fenris */ function give_white() { - return give_gray(1.0); + return give_gray({ "value": 1.0 }); } color_1.give_white = give_white; /** - * @author fenris */ - function give_red({ "saturation": saturation = undefined, "value": value = undefined, } = {}) { - return make_hsv({ "hue": 0 / 6, "saturation": saturation, "value": value }); + function give_red({ "saturation": option_saturation = undefined, "value": option_value = undefined, } = {}) { + return make_hsv({ + "hue": (0 / 6), + "saturation": option_saturation, + "value": option_value + }); } color_1.give_red = give_red; /** - * @author fenris */ - function give_green({ "saturation": saturation = undefined, "value": value = undefined, } = {}) { - return make_hsv({ "hue": 2 / 6, "saturation": saturation, "value": value }); + function give_green({ "saturation": option_saturation = undefined, "value": option_value = undefined, } = {}) { + return make_hsv({ + "hue": (2 / 6), + "saturation": option_saturation, + "value": option_value + }); } color_1.give_green = give_green; /** - * @author fenris */ - function give_blue({ "saturation": saturation = undefined, "value": value = undefined, } = {}) { - return make_hsv({ "hue": 4 / 6, "saturation": saturation, "value": value }); + function give_blue({ "saturation": option_saturation = undefined, "value": option_value = undefined, } = {}) { + return make_hsv({ + "hue": (4 / 6), + "saturation": option_saturation, + "value": option_value + }); } color_1.give_blue = give_blue; /** - * @author fenris */ - function give_yellow({ "saturation": saturation = undefined, "value": value = undefined, } = {}) { - return make_hsv({ "hue": 1 / 6, "saturation": saturation, "value": value }); + function give_yellow({ "saturation": option_saturation = undefined, "value": option_value = undefined, } = {}) { + return make_hsv({ + "hue": (1 / 6), + "saturation": option_saturation, + "value": option_value + }); } color_1.give_yellow = give_yellow; /** - * @author fenris */ - function give_cyan({ "saturation": saturation = undefined, "value": value = undefined, } = {}) { - return make_hsv({ "hue": 3 / 6, "saturation": saturation, "value": value }); + function give_cyan({ "saturation": option_saturation = undefined, "value": option_value = undefined, } = {}) { + return make_hsv({ + "hue": (3 / 6), + "saturation": option_saturation, + "value": option_value + }); } color_1.give_cyan = give_cyan; /** - * @author fenris */ - function give_magenta({ "saturation": saturation = undefined, "value": value = undefined, } = {}) { - return make_hsv({ "hue": 5 / 6, "saturation": saturation, "value": value }); + function give_magenta({ "saturation": option_saturation = undefined, "value": option_value = undefined, } = {}) { + return make_hsv({ + "hue": (5 / 6), + "saturation": option_saturation, + "value": option_value + }); } color_1.give_magenta = give_magenta; })(color = lib_plankton.color || (lib_plankton.color = {})); @@ -6227,8 +8343,7 @@ var lib_plankton; * @author fenris */ static give_generic({ "n": n, "offset": offset = undefined, "saturation": saturation = undefined, "value": value = undefined, }) { - return (class_color._cram(color_2.give_generic({ - "n": n, + return (class_color._cram(color_2.give_generic(n, { "offset": offset, "saturation": saturation, "value": value, @@ -6240,7 +8355,7 @@ var lib_plankton; * @author fenris */ static give_gray(value = 0.5) { - return (class_color._cram(color_2.give_gray(value))); + return (class_color._cram(color_2.give_gray({ "value": value }))); } /** * @author fenris @@ -8667,26 +10782,6 @@ var lib_plankton; (function (lib_plankton) { var url; (function (url_1) { - /** - */ - function query_args_encode(query_args) { - return (Object.entries(query_args) - .map(([key, value]) => (key + "=" + value)) - .join("&")); - } - url_1.query_args_encode = query_args_encode; - /** - */ - function query_args_decode(query) { - return (Object.fromEntries(query.split("&") - .map((part) => { - const components = part.split("="); - const key = components[0]; - const value = components.slice(1).join("="); - return [key, value]; - }))); - } - url_1.query_args_decode = query_args_decode; /** * @author fenris */ @@ -8727,7 +10822,7 @@ var lib_plankton; // path { if (url.path !== null) { - result += (url.path); + result += url.path; } } // query diff --git a/source/helpers.ts b/source/helpers.ts deleted file mode 100644 index 9fb2a85..0000000 --- a/source/helpers.ts +++ /dev/null @@ -1,521 +0,0 @@ - -/** - */ -function date_object_get_week_of_year( - date : Date -) : int -{ - let date_ : Date = new Date(date.getTime()); - date_.setHours(0, 0, 0, 0); - // Thursday in current week decides the year. - date_.setDate(date_.getDate() + 3 - (date_.getDay() + 6) % 7); - // January 4 is always in week 1. - let week1 : Date = new Date(date_.getFullYear(), 0, 4); - // Adjust to Thursday in week 1 and count number of weeks from date to week1. - return ( - 1 - + - Math.round( - ( - ((date_.getTime() - week1.getTime()) / 86400000) - - - 3 - + - (week1.getDay() + 6) % 7 - ) - / - 7 - ) - ); -} - - -/** - * @todo unite with type_datetimeobject? - */ -type type_datetime = { - timezone_shift : int; - date : type_date; - time : ( - null - | - type_time - ); -}; - - -/** - * @todo timezone_shift - */ -function datetime_from_date_object( - date : Date, - options : { - timezone_shift ?: int; - } = {} -) : type_datetime -{ - options = Object.assign( - { - "timezone_shift": 0, - }, - options - ); - const date_ : Date = lib_plankton.call.convey( - date, - [ - (x : Date) => x.getTime(), - (x : int) => (x + (((options.timezone_shift as int) * (60 * 60)) * 1000)), - (x : int) => new Date(x), - ] - ); - const iso_string : string = date_.toISOString(); - return { - "timezone_shift": (options.timezone_shift as int), - "date": { - "year": parseInt(iso_string.slice(0, 4)), - "month": parseInt(iso_string.slice(5, 7)), - "day": parseInt(iso_string.slice(8, 10)), - }, - "time": { - "hour": parseInt(iso_string.slice(11, 13)), - "minute": parseInt(iso_string.slice(14, 16)), - "second": parseInt(iso_string.slice(17, 19)), - }, - }; -} - - - -/** - * @todo negative shift? - */ -function datetime_to_date_object( - datetime : type_datetime -) : Date -{ - const iso_string : string = lib_plankton.string.coin( - "{{year}}-{{month}}-{{day}}T{{hour}}:{{minute}}:{{second}}.000+{{shift}}", - { - "year": datetime.date.year.toFixed(0).padStart(4, "0"), - "month": datetime.date.month.toFixed(0).padStart(2, "0"), - "day": datetime.date.day.toFixed(0).padStart(2, "0"), - "hour": ((datetime.time !== null) ? datetime.time.hour : 0).toFixed(0).padStart(2, "0"), - "minute": ((datetime.time !== null) ? datetime.time.minute : 0).toFixed(0).padStart(2, "0"), - "second": ((datetime.time !== null) ? datetime.time.second : 0).toFixed(0).padStart(2, "0"), - "shift": (datetime.timezone_shift.toFixed(0).padStart(2, "0") + ":00"), - } - ); - return (new Date(iso_string)); -} - - -/** - */ - -type type_pit = int; - - -/** - */ -function pit_to_date_object( - pit : type_pit -) : Date -{ - return (new Date(pit * 1000)); -} - - -/** - */ -function pit_from_date_object( - date_object : Date -) : type_pit -{ - return Math.round(date_object.getTime() / 1000); -} - - -/** - */ -function pit_now( -) : type_pit -{ - return pit_from_date_object(new Date(Date.now())); -} - - -/** - * @todo timezone - */ -function pit_to_datetime( - pit : type_pit, - options : { - timezone_shift ?: int - } = {} -) : type_datetime -{ - options = Object.assign( - { - "timezone_shift": 0, - }, - options - ); - const date_object : Date = pit_to_date_object(pit); - return datetime_from_date_object( - date_object, - { - "timezone_shift": (options.timezone_shift as int), - } - ); -} - - -/** - */ -function pit_from_datetime( - datetime : type_datetime -) : type_pit -{ - return lib_plankton.call.convey( - datetime, - [ - datetime_to_date_object, - pit_from_date_object, - ] - ); -} - - -/** - */ -function pit_is_before( - pit : type_pit, - reference : type_pit -) : boolean -{ - return (pit < reference); -} - - -/** - */ -function pit_is_after( - pit : type_pit, - reference : type_pit -) : boolean -{ - return (pit > reference); -} - - -/** - */ -function pit_is_between( - pit : type_pit, - reference_left : type_pit, - reference_right : type_pit -) : boolean -{ - return ( - pit_is_after(pit, reference_left) - && - pit_is_before(pit, reference_right) - ); -} - - -/** - */ -function pit_shift_hour( - pit : type_pit, - increment : int -) : type_pit -{ - return (pit + (60 * 60 * increment)); -} - - -/** - */ -function pit_shift_day( - pit : type_pit, - increment : int -) : type_pit -{ - return (pit + (60 * 60 * 24 * increment)); -} - - -/** - */ -function pit_shift_week( - pit : type_pit, - increment : int -) : type_pit -{ - return (pit + (60 * 60 * 24 * 7 * increment)); -} - - -/** - */ -function pit_shift_year( - pit : type_pit, - increment : int -) : type_pit -{ - return (pit + (60 * 60 * 24 * 365 * increment)); -} - - -/** - */ -function pit_trunc_minute( - pit : type_pit -) : type_pit -{ - const datetime_input : type_datetime = pit_to_datetime(pit); - const datetime_output : type_datetime = { - "timezone_shift": 0, - "date": { - "year": datetime_input.date.year, - "month": datetime_input.date.month, - "day": datetime_input.date.day, - }, - "time": { - "hour": ( - (datetime_input.time === null) - ? - 0 - : - datetime_input.time.hour - ), - "minute": ( - (datetime_input.time === null) - ? - 0 - : - datetime_input.time.minute - ), - "second": 0, - }, - }; - return pit_from_datetime(datetime_output); -} - - -/** - */ -function pit_trunc_hour( - pit : type_pit -) : type_pit -{ - const datetime_input : type_datetime = pit_to_datetime(pit); - const datetime_output : type_datetime = { - "timezone_shift": 0, - "date": { - "year": datetime_input.date.year, - "month": datetime_input.date.month, - "day": datetime_input.date.day, - }, - "time": { - "hour": ( - (datetime_input.time === null) - ? - 0 - : - datetime_input.time.hour - ), - "minute": 0, - "second": 0, - }, - }; - return pit_from_datetime(datetime_output); -} - - -/** - */ -function pit_trunc_day( - pit : type_pit -) : type_pit -{ - const datetime_input : type_datetime = pit_to_datetime(pit); - const datetime_output : type_datetime = { - "timezone_shift": 0, - "date": { - "year": datetime_input.date.year, - "month": datetime_input.date.month, - "day": datetime_input.date.day, - }, - "time": { - "hour": 0, - "minute": 0, - "second": 0, - }, - }; - return pit_from_datetime(datetime_output); -} - - -/** - */ -function pit_trunc_week( - pit : type_pit -) : type_pit -{ - const date_object : Date = pit_to_date_object(pit); - return lib_plankton.call.convey( - date_object.getDay(), - [ - (x : int) => ((x === 0) ? 7 : x), - (x : int) => (x - 1), - (x : int) => pit_shift_day(pit, (-x)), - pit_trunc_day - ] - ); -} - - -/** - */ -function pit_trunc_month( - pit : type_pit -) : type_pit -{ - const datetime_input : type_datetime = pit_to_datetime(pit); - const datetime_output : type_datetime = { - "timezone_shift": 0, - "date": { - "year": datetime_input.date.year, - "month": datetime_input.date.month, - "day": 1, - }, - "time": { - "hour": 0, - "minute": 0, - "second": 0, - }, - }; - return pit_from_datetime(datetime_output); -} - - -/** - */ -function pit_trunc_year( - pit : type_pit -) : type_pit -{ - const datetime_input : type_datetime = pit_to_datetime(pit); - const datetime_output : type_datetime = { - "timezone_shift": 0, - "date": { - "year": datetime_input.date.year, - "month": 1, - "day": 1, - }, - "time": { - "hour": 0, - "minute": 0, - "second": 0, - }, - }; - return pit_from_datetime(datetime_output); -} - - -/** - * @param year year according to specified timezone shift - * @param week week according to specified timezone shift - * @return the begin of the week (monday, 00:00) - */ -function pit_from_year_and_week( - year : int, - week : int, - options : { - timezone_shift ?: int; - } = {} -) : type_pit -{ - options = Object.assign( - { - "timezone_shift": 0, - }, - options - ); - return lib_plankton.call.convey( - { - "timezone_shift": (options.timezone_shift as int), - "date": { - "year": year, - "month": 1, - "day": 1, - }, - "time": { - "hour": 0, - "minute": 0, - "second": 0 - } - }, - [ - pit_from_datetime, - (x : type_pit) => pit_shift_week(x, (week - 1)), - pit_trunc_week, - ] - ); -} - - -/** - * @todo timezone - */ -function ical_datetime_to_own_datetime( - ical_datetime : lib_plankton.ical.type_datetime -) : type_datetime -{ - return { - "timezone_shift": 0, - "date": { - "year": ical_datetime.date.year, - "month": ical_datetime.date.month, - "day": ical_datetime.date.day, - }, - "time": ( - (ical_datetime.time === null) - ? - null - : - { - "hour": ical_datetime.time.hour, - "minute": ical_datetime.time.minute, - "second": ical_datetime.time.second, - } - ) - }; -} - - -/** - * @todo timezone - */ -function ical_dt_to_own_datetime( - ical_dt: lib_plankton.ical.type_dt -) : type_datetime -{ - return { - "timezone_shift": 0, - "date": ical_dt.value.date, - "time": ( - (ical_dt.value.time === null) - ? - null - : - { - "hour": ical_dt.value.time.hour, - "minute": ical_dt.value.time.minute, - "second": ical_dt.value.time.second, - } - ) - }; -} diff --git a/source/index.html b/source/index.html new file mode 100644 index 0000000..185ef1a --- /dev/null +++ b/source/index.html @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/source/logic.ts b/source/logic.ts deleted file mode 100644 index 3c58a79..0000000 --- a/source/logic.ts +++ /dev/null @@ -1,1002 +0,0 @@ - -/** - */ -type type_role = ( - "editor" - | - "viewer" -); - - -/** - */ -type type_user_id = int; - - -/** - */ -type type_user_object = { - name : string; -}; - - -/** - */ -type type_event = { - name : string; - begin : type_datetime; - end : ( - null - | - type_datetime - ); - description : ( - null - | - string - ); -}; - - -/** - */ -type type_calendar_id = int; - - -/** - * @todo bei "collection" Kreise vermeiden - */ -type type_calendar_object = ( - { - kind : "concrete"; - data : { - name : string; - users : Array< - { - id : type_user_id; - role : type_role; - } - >; - events : Array; - }; - } - | - { - kind : "collection"; - data : { - name : string; - sources : Array< - type_calendar_id - >; - } - } - | - { - kind : "caldav"; - data : { - source_url : string; - } - } -); - - -/** - */ -type type_datamodel = { - users : Array< - { - id : type_user_id; - object : type_user_object; - } - >; - calendars : Array< - { - id : type_calendar_id; - object : type_calendar_object; - } - >; -}; - - -/** - */ -function calendar_list( - data : type_datamodel -) : Array< - { - key : type_calendar_id; - preview : { - name : string; - } - } -> -{ - return ( - data.calendars - .map( - (calendar_entry) => { - switch (calendar_entry.object.kind) { - case "concrete": { - return { - "key": calendar_entry.id, - "preview": { - "name": calendar_entry.object.data.name, - } - }; - break; - } - case "collection": { - return { - "key": calendar_entry.id, - "preview": { - "name": calendar_entry.object.data.name, - } - }; - } - case "caldav": { - return { - "key": calendar_entry.id, - "preview": { - "name": "(imported)", - } - }; - } - } - } - ) - ); -} - - -/** - */ -function calendar_read( - data : type_datamodel, - calendar_id : type_calendar_id -) : type_calendar_object -{ - const hits = ( - data.calendars - .filter( - (calendar_entry) => (calendar_entry.id === calendar_id) - ) - ); - if (hits.length <= 0) { - throw (new Error("not found")); - } - else { - return hits[0].object; - } -} - - -/** - */ -async function calendar_gather_events( - data : type_datamodel, - calendar_id : type_calendar_id, - from_pit : type_pit, - to_pit : type_pit -) : Promise< - Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - > -> -{ - const calendar_object : type_calendar_object = calendar_read( - data, - calendar_id - ); - switch (calendar_object.kind) { - case "concrete": { - return Promise.resolve( - calendar_object.data.events - .filter( - (event) => pit_is_between( - pit_from_datetime(event.begin), - from_pit, - to_pit - ) - ) - .map( - (event) => ({"calendar_id": calendar_id, "event": event}) - ) - ); - break; - } - case "collection": { - return ( - Promise.all( - calendar_object.data.sources - .map( - (source_calendar_id) => calendar_gather_events( - data, - source_calendar_id, - from_pit, - to_pit - ) - ) - ) - .then( - (sources) => Promise.resolve( - sources - .reduce( - (x, y) => x.concat(y), - [] - ) - ) - ) - ) - break; - } - case "caldav": { - const url : lib_plankton.url.type_url = lib_plankton.url.decode(calendar_object.data.source_url); - const http_request : lib_plankton.http.type_request = { - "version": "HTTP/1.1", - "scheme": ((url.scheme === "https") ? "https" : "http"), - "host": url.host, - "path": (url.path ?? "/"), - "query": url.query, - "method": lib_plankton.http.enum_method.get, - "headers": {}, - "body": null, - }; - // TODO: cache? - const http_response : lib_plankton.http.type_response = await lib_plankton.http.call( - http_request, - { - } - ); - const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode( - http_response.body.toString(), - { - } - ); - return Promise.resolve( - vcalendar.vevents - .map( - (vevent : lib_plankton.ical.type_vevent) => ( - (vevent.dtstart !== undefined) - ? - { - "name": ( - (vevent.summary !== undefined) - ? - vevent.summary - : - "???" - ), - "begin": ical_dt_to_own_datetime(vevent.dtstart), - "end": ( - (vevent.dtend !== undefined) - ? - ical_dt_to_own_datetime(vevent.dtend) - : - null - ), - "description": ( - (vevent.description !== undefined) - ? - vevent.description - : - null - ), - } - : - null - ) - ) - .filter( - (event) => (event !== null) - ) - .filter( - (event) => pit_is_between( - pit_from_datetime(event.begin), - from_pit, - to_pit - ) - ) - .map( - (event) => ({"calendar_id": calendar_id, "event": event}) - ) - ); - break; - } - } -} - - -/** - * @todo kein "while" - */ -async function calendar_view_table_data( - data : type_datamodel, - calendar_id : type_calendar_id, - options : { - from ?: { - year : int; - week : int; - }, - to ?: { - year : int; - week : int; - }, - timezone_shift ?: int; - } = {} -) : Promise< - Array< - { - week : int; - data : Array< - { - pit : type_pit; - entries : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - >; - today : boolean; - } - >; - } - > -> -{ - const now_pit : type_pit = pit_now(); - options = Object.assign( - { - "from": lib_plankton.call.convey( - now_pit, - [ - (x : type_pit) => pit_shift_week(x, -1), - pit_to_date_object, - (x : Date) => ({ - "year": x.getFullYear(), - "week": date_object_get_week_of_year(x), - }) - ] - ), - "to": lib_plankton.call.convey( - now_pit, - [ - (x : type_pit) => pit_shift_week(x, +4), - pit_to_date_object, - (x : Date) => ({ - "year": x.getFullYear(), - "week": date_object_get_week_of_year(x), - }) - ] - ), - "timezone_shift": 0, - }, - options - ); - /* - const calendar_object : type_calendar_object = calendar_read( - data, - calendar_id - ); - */ - const from_pit : type_pit = pit_from_year_and_week( - (options.from as {year : int; week : int}).year, - (options.from as {year : int; week : int}).week, - { - "timezone_shift": (options.timezone_shift as int), - } - ); - const to_pit : type_pit = pit_from_year_and_week( - (options.to as {year : int; week : int}).year, - (options.to as {year : int; week : int}).week, - { - "timezone_shift": (options.timezone_shift as int), - } - ); - - // prepare - const entries : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - > = await calendar_gather_events( - data, - calendar_id, - from_pit, - to_pit - ); - let result : Array< - { - week : int; - data : Array< - { - pit : type_pit; - entries : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - >; - today : boolean; - } - >; - } - > = []; - let row : Array< - { - pit : type_pit; - entries : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - >; - today : boolean; - } - > = []; - let day : int = 0; - while (true) { - const pit_current : type_pit = pit_shift_day(from_pit, day); - if (pit_is_before(pit_current, to_pit)) { - day += 1; - row.push( - { - "pit": pit_current, - "entries": [], - "today": false, // TODO - } - ); - if (day % 7 === 0) { - result.push( - { - "week": ( - (options.from as {year : int; week : int}).week - + - Math.floor(day / 7) - - - 1 // TODO - ), - "data": row - } - ); - row = []; - } - else { - // do nothing - } - } - else { - break; - } - } - - // fill - { - // events - ( - entries - .forEach( - (entry) => { - const distance_seconds : int = (pit_from_datetime(entry.event.begin) - from_pit); - const distance_days : int = (distance_seconds / (60 * 60 * 24)); - - const week : int = Math.floor(Math.floor(distance_days) / 7); - const day : int = (Math.floor(distance_days) % 7); - - if ((week >= 0) && (week < result.length)) { - result[week].data[day].entries.push(entry); - } - else { - // do nothing - } - } - ) - ); - // today - { - const distance_seconds : int = (now_pit - from_pit); - const distance_days : int = (distance_seconds / (60 * 60 * 24)); - - const week : int = Math.floor(Math.floor(distance_days) / 7); - const day : int = (Math.floor(distance_days) % 7); - - if ((week >= 0) && (week < result.length)) { - result[week].data[day].today = true; - } - else { - // do nothing - } - } - } - - return Promise.resolve(result); -} - - -/** - */ -async function calendar_view_table_html( - data : type_datamodel, - calendar_id : type_calendar_id, - options : { - from ?: { - year : int; - week : int; - }; - to ?: { - year : int; - week : int; - }; - timezone_shift ?: int; - } = {} -) : Promise -{ - const stuff : Array< - { - week : int; - data : Array< - { - pit : type_pit; - entries : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - >; - today : boolean; - } - >; - } - > = await calendar_view_table_data( - data, - calendar_id, - options - ); - return Promise.resolve( - new lib_plankton.xml.class_node_complex( - "div", - { - "class": "calendar", - }, - [ - new lib_plankton.xml.class_node_complex( - "style", - {}, - [ - new lib_plankton.xml.class_node_text( - "html {background-color: #111; color: #FFF; font-family: sans-serif;}\n" - + - "table {width: 100%; border-collapse: collapse;}\n" - + - ".calendar-cell {border: 1px solid #444; padding: 8px; vertical-align: top;}\n" - + - ".calendar-cell-day {width: 13.5%;}\n" - + - ".calendar-cell-week {width: 5.5%;}\n" - + - ".calendar-cell-regular {width: 13.5%; height: 150px;}\n" - + - ".calendar-cell-today {background-color: #333;}\n" - + - ".calendar-day {font-size: 0.75em;}\n" - + - ".calendar-events {margin: 0; padding: 0; list-style-type: none;}\n" - + - ".calendar-event_entry {margin: 4px; padding: 4px; border-radius: 2px; font-size: 0.75em; color: #FFF; font-weight: bold; cursor: pointer;}\n" - ) - ] - ), - new lib_plankton.xml.class_node_complex( - "table", - { - }, - [ - new lib_plankton.xml.class_node_complex( - "thead", - { - }, - [ - new lib_plankton.xml.class_node_complex( - "tr", - { - }, - ( - [ - new lib_plankton.xml.class_node_complex( - "th", - { - "class": "calendar-cell", - }, - [ - ] - ), - ] - .concat( - ["Mo","Di","Mi","Do","Fr","Sa","So"] - .map( - (day) => new lib_plankton.xml.class_node_complex( - "th", - { - "class": "calendar-cell calendar-cell-day", - }, - [ - new lib_plankton.xml.class_node_text(day) - ] - ) - ) - ) - ) - ) - ] - ), - new lib_plankton.xml.class_node_complex( - "tbody", - { - }, - ( - stuff - .map( - (row) => ( - new lib_plankton.xml.class_node_complex( - "tr", - { - }, - ( - [ - new lib_plankton.xml.class_node_complex( - "th", - { - "class": "calendar-cell calendar-cell-week", - }, - [ - new lib_plankton.xml.class_node_text( - row.week.toFixed(0).padStart(2, "0") - ) - ] - ), - ] - .concat( - row.data - .map( - (cell) => ( - new lib_plankton.xml.class_node_complex( - "td", - { - "class": ( - ( - ["calendar-cell", "calendar-cell-regular"] - .concat(cell.today ? ["calendar-cell-today"] : []) - ) - .join(" ") - ), - "title": lib_plankton.call.convey( - cell.pit, - [ - pit_to_datetime, - (x : type_datetime) => lib_plankton.string.coin( - "{{year}}-{{month}}-{{day}}", - { - "year": x.date.year.toFixed(0).padStart(4, "0"), - "month": x.date.month.toFixed(0).padStart(2, "0"), - "day": x.date.day.toFixed(0).padStart(2, "0"), - } - ), - ] - ), - }, - [ - new lib_plankton.xml.class_node_complex( - "span", - { - "class": "calendar-day", - }, - [ - new lib_plankton.xml.class_node_text( - lib_plankton.call.convey( - cell.pit, - [ - pit_to_datetime, - (x : type_datetime) => lib_plankton.string.coin( - "{{day}}", - { - "year": x.date.year.toFixed(0).padStart(4, "0"), - "month": x.date.month.toFixed(0).padStart(2, "0"), - "day": x.date.day.toFixed(0).padStart(2, "0"), - } - ), - ] - ) - ) - ] - ), - new lib_plankton.xml.class_node_complex( - "ul", - { - "class": "calendar-events", - }, - ( - cell.entries - .map( - entry => ( - new lib_plankton.xml.class_node_complex( - "li", - { - "class": "calendar-event_entry", - "style": lib_plankton.string.coin( - "background-color: {{color}}", - { - "color": lib_plankton.call.convey( - entry.calendar_id, - [ - (n : int) => ({"n": n, "saturation": 0.25, "value": 0.5}), - lib_plankton.color.give_generic, - lib_plankton.color.output_hex, - ] - ), - } - ), - "title": ( - lib_plankton.string.coin( - "[{{calendar_name}}] {{event_name}}\n", - { - "calendar_name": entry.calendar_id.toFixed(0), - "event_name": entry.event.name, - } - ) - + - "--\n" - + - ( - (entry.event.begin.time !== null) - ? - lib_plankton.string.coin( - "{{label}}: {{value}}\n", - { - "label": "Anfang", // TODO: translate - "value": lib_plankton.string.coin( - "{{hour}}:{{minute}}", - { - "hour": entry.event.begin.time.hour.toFixed(0).padStart(2, "0"), - "minute": entry.event.begin.time.minute.toFixed(0).padStart(2, "0"), - } - ), // TODO: outsource - } - ) - : - "" - ) - + - ( - (entry.event.end !== null) - ? - lib_plankton.string.coin( - "{{label}}: {{value}}\n", - { - "label": "Ende", // TODO: translate - "value": ( - [ - ( - ( - (entry.event.end.date.year !== entry.event.begin.date.year) - || - (entry.event.end.date.month !== entry.event.begin.date.month) - || - (entry.event.end.date.day !== entry.event.begin.date.day) - ) - ? - lib_plankton.string.coin( - "{{year}}-{{month}}-{{day}}", - { - "year": entry.event.end.date.year.toFixed(0).padStart(4, "0"), - "month": entry.event.end.date.month.toFixed(0).padStart(2, "0"), - "day": entry.event.end.date.month.toFixed(0).padStart(2, "0"), - } - ) - : - null - ), - ( - (entry.event.end.time !== null) - ? - lib_plankton.string.coin( - "{{hour}}:{{minute}}", - { - "hour": entry.event.end.time.hour.toFixed(0).padStart(2, "0"), - "minute": entry.event.end.time.minute.toFixed(0).padStart(2, "0"), - } - ) - : - null - ), - ] - .filter(x => (x !== null)) - .join(",") - ), - } - ) - : - "" - ) - + - ( - (entry.event.description !== null) - ? - ( - "--\n" - + - lib_plankton.string.coin( - "{{description}}\n", - { - "description": ( - (entry.event.description !== null) - ? - entry.event.description - : - "?" - ), - } - ) - ) - : - "" - ) - ), - }, - [ - new lib_plankton.xml.class_node_text(entry.event.name), - ] - ) - ) - ) - ) - ), - ] - ) - ) - ) - ) - ) - ) - ) - ) - ) - ) - ] - ) - ] - ).compile() - ); -} - - -/** - */ -async function calendar_view_list_data( - data : type_datamodel, - calendar_id : type_calendar_id, - options : { - from ?: type_pit; - to ?: type_pit; - timezone_shift ?: int; - } = {} -) : Promise< - Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - > -> -{ - const now_pit : type_pit = pit_now(); - options = Object.assign( - { - "from": lib_plankton.call.convey( - now_pit, - [ - (x : type_pit) => pit_shift_day(x, -1), - ] - ), - "to": lib_plankton.call.convey( - now_pit, - [ - (x : type_pit) => pit_shift_week(x, +4), - ] - ), - "timezone_shift": 0, - }, - options - ); - - const entries : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - > = await calendar_gather_events( - data, - calendar_id, - (options.from as type_pit), - (options.to as type_pit) - ); - // TODO: optimize - entries.sort( - (entry_1, entry_2) => (pit_from_datetime(entry_1.event.begin) - pit_from_datetime(entry_2.event.begin)) - ); - - return Promise.resolve(entries); -} - - -/** - */ -async function calendar_view_list_html( - data : type_datamodel, - calendar_id : type_calendar_id, - options : { - from ?: type_pit; - to ?: type_pit; - timezone_shift ?: int; - } = {} -) : Promise -{ - const stuff : Array< - { - calendar_id : type_calendar_id; - event : type_event; - } - > = await calendar_view_list_data( - data, - calendar_id, - options - ); - return Promise.resolve( - new lib_plankton.xml.class_node_complex( - "div", - { - "class": "list", - }, - [ - new lib_plankton.xml.class_node_complex( - "style", - {}, - [ - new lib_plankton.xml.class_node_text( - "html {background-color: #111; color: #FFF; font-family: sans-serif;}\n" - + - "table {width: 100%; border-collapse: collapse;}\n" - ) - ] - ), - new lib_plankton.xml.class_node_complex( - "ul", - { - "class": "list-events", - }, - ( - stuff - .map( - (entry) => ( - new lib_plankton.xml.class_node_complex( - "li", - { - "class": "list-event_entry", - }, - [ - new lib_plankton.xml.class_node_text( - JSON.stringify(entry) - ), - ] - ) - ) - ) - ) - ), - ] - ).compile() - ); -} - diff --git a/source/logic/backend.ts b/source/logic/backend.ts new file mode 100644 index 0000000..7a71b2c --- /dev/null +++ b/source/logic/backend.ts @@ -0,0 +1,339 @@ +/** + */ +namespace _zeitbild.frontend.resources.backend +{ + + /** + */ + var _data : _zeitbild.frontend.type_datamodel; + + + /** + */ + export async function init( + ) : Promise + { + const path : string = "data.json"; + if (_data === undefined) { + _data = lib_plankton.call.convey( + await lib_plankton.file.read(path), + [ + lib_plankton.json.decode, + (data_raw : any) => ( + ({ + "users": data_raw["users"], + "calendars": ( + data_raw["calendars"] + .map( + (calendar_entry_raw : any) => ({ + "id": calendar_entry_raw["id"], + "object": ( + ((calendar_object_raw) => { + switch (calendar_object_raw["kind"]) { + default: { + return calendar_object_raw; + break; + } + case "concrete": { + return { + "kind": "concrete", + "data": { + "name": calendar_object_raw["data"]["name"], + "private": ( + calendar_object_raw["data"]["private"] + ?? + false + ), + "users": calendar_object_raw["data"]["users"], + "events": ( + calendar_object_raw["data"]["events"] + .map( + (event_raw : any) => ({ + "name": event_raw["name"], + "begin": event_raw["begin"], + "end": ( + ( + ( + event_raw["end"] + ?? + null + ) + === + null + ) + ? + null + : + event_raw["end"] + ), + "location": ( + event_raw["location"] + ?? + null + ), + "description": ( + event_raw["description"] + ?? + null + ), + }) + ) + ), + }, + }; + break; + } + } + }) (calendar_entry_raw["object"]) + ), + }) + ) + ), + }) as type_datamodel + ), + ] + ); + } + else { + // do nothing + } + return Promise.resolve(undefined); + } + + + /** + */ + export async function calendar_list( + ) : Promise< + Array< + { + key : type_calendar_id; + preview : { + name : string; + } + } + > + > + { + await init(); + return Promise.resolve( + _data.calendars + .map( + (calendar_entry) => { + switch (calendar_entry.object.kind) { + case "concrete": { + return { + "key": calendar_entry.id, + "preview": { + "name": calendar_entry.object.data.name, + } + }; + break; + } + case "collection": { + return { + "key": calendar_entry.id, + "preview": { + "name": calendar_entry.object.data.name, + } + }; + } + case "caldav": { + return { + "key": calendar_entry.id, + "preview": { + "name": "(imported)", + } + }; + } + } + } + ) + ); + } + + + /** + */ + export async function calendar_read( + calendar_id : type_calendar_id + ) : Promise + { + await init(); + const hits = ( + _data.calendars + .filter( + (calendar_entry) => (calendar_entry.id === calendar_id) + ) + ); + if (hits.length <= 0) { + return Promise.reject(new Error("not found")); + } + else { + return Promise.resolve(hits[0].object); + } + } + + + /** + * @todo prevent loops + */ + export async function calendar_gather_events( + calendar_id : type_calendar_id, + from_pit : _zeitbild.frontend.helpers.type_pit, + to_pit : _zeitbild.frontend.helpers.type_pit + ) : Promise< + Array< + { + calendar_id : type_calendar_id; + calendar_name : string; + event : type_event; + } + > + > + { + await init(); + const calendar_object : type_calendar_object = await calendar_read( + calendar_id + ); + lib_plankton.log.info( + "calendar_gather_events", + { + "calendar_id": calendar_id, + } + ); + switch (calendar_object.kind) { + case "concrete": { + return Promise.resolve( + calendar_object.data.events + .filter( + (event) => _zeitbild.frontend.helpers.pit_is_between( + _zeitbild.frontend.helpers.pit_from_datetime(event.begin), + from_pit, + to_pit + ) + ) + .map( + (event) => ({ + "calendar_id": calendar_id, + "calendar_name": calendar_object.data.name, + "event": event + }) + ) + ); + break; + } + case "collection": { + return ( + Promise.all( + calendar_object.data.sources + .map( + (source_calendar_id) => calendar_gather_events( + source_calendar_id, + from_pit, + to_pit + ) + ) + ) + .then( + (entries) => Promise.resolve( + entries + .reduce( + (x, y) => x.concat(y), + [] + ) + ) + ) + ); + break; + } + case "caldav": { + const url : lib_plankton.url.type_url = lib_plankton.url.decode( + calendar_object.data.source_url + ); + const http_request : lib_plankton.http.type_request = { + "version": "HTTP/2", + "scheme": ((url.scheme === "https") ? "https" : "http"), + "host": url.host, + "path": (url.path ?? "/"), + "query": url.query, + "method": lib_plankton.http.enum_method.get, + "headers": {}, + "body": null, + }; + // TODO: cache? + const http_response : lib_plankton.http.type_response = await lib_plankton.http.call( + http_request, + { + } + ); + const vcalendar : lib_plankton.ical.type_vcalendar = lib_plankton.ical.ics_decode( + http_response.body.toString(), + { + } + ); + return Promise.resolve( + vcalendar.vevents + .map( + (vevent : lib_plankton.ical.type_vevent) => ( + (vevent.dtstart !== undefined) + ? + { + "name": ( + (vevent.summary !== undefined) + ? + vevent.summary + : + "???" + ), + "begin": _zeitbild.frontend.helpers.ical_dt_to_own_datetime(vevent.dtstart), + "end": ( + (vevent.dtend !== undefined) + ? + _zeitbild.frontend.helpers.ical_dt_to_own_datetime(vevent.dtend) + : + null + ), + "location": ( + (vevent.location !== undefined) + ? + vevent.location + : + null + ), + "description": ( + (vevent.description !== undefined) + ? + vevent.description + : + null + ), + } + : + null + ) + ) + .filter( + (event) => (event !== null) + ) + .filter( + (event) => _zeitbild.frontend.helpers.pit_is_between( + _zeitbild.frontend.helpers.pit_from_datetime(event.begin), + from_pit, + to_pit + ) + ) + .map( + (event) => ({ + "calendar_id": calendar_id, + "calendar_name": calendar_object.data.name, + "event": event, + }) + ) + ); + break; + } + } + } + +} diff --git a/source/main.ts b/source/logic/cli.ts similarity index 92% rename from source/main.ts rename to source/logic/cli.ts index 00560bc..cd3af7e 100644 --- a/source/main.ts +++ b/source/logic/cli.ts @@ -58,7 +58,8 @@ async function main( // init lib_plankton.log.conf_push( [ - lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "debug", "path": "/tmp/kalender.log"}}), + // lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "debug", "path": "/tmp/kalender.log"}}), + lib_plankton.log.channel_make({"kind": "file", "data": {"threshold": "info", "path": "/dev/stderr"}}), // lib_plankton.log.channel_make({"kind": "stdout", "data": {"threshold": "info"}}), ] ); @@ -134,15 +135,15 @@ async function main( ), ] ); - let output : string; + let content : string; switch (args["view_mode"]) { default: { - output = ""; + content = ""; throw (new Error("invalid view mode")); break; } case "table": { - output = await calendar_view_table_html( + content = await calendar_view_table_html( data, args.calendar_id, { @@ -152,7 +153,7 @@ async function main( break; } case "list": { - output = await calendar_view_list_html( + content = await calendar_view_list_html( data, args.calendar_id, { @@ -162,6 +163,12 @@ async function main( break; } } + const output : string = template_coin( + "main", + { + "content": content, + } + ); process.stdout.write(output); } return Promise.resolve(undefined); diff --git a/source/logic/helpers.ts b/source/logic/helpers.ts new file mode 100644 index 0000000..e3ebf88 --- /dev/null +++ b/source/logic/helpers.ts @@ -0,0 +1,590 @@ + +/** + */ +namespace _zeitbild.frontend.helpers +{ + + /** + */ + var _template_cache : Record = {}; + + + /** + * @todo caching + */ + export async function template_coin( + name : string, + data : Record + ) : Promise + { + let content : string; + if (! (name in _template_cache)) { + content = ( + ( + await lib_plankton.file.read( + lib_plankton.string.coin( + "templates/{{name}}.html.tpl", + { + "name": name, + } + ) + ) + ) + .toString() + ); + _template_cache[name] = content; + } + else { + content = _template_cache[name]; + } + return Promise.resolve( + lib_plankton.string.coin( + content, + data + ) + ); + } + + + /** + * @todo outsource + */ + export async function promise_row( + members : Array< + () => Promise + > + ) : Promise< + Array< + type_result + > + > + { + let results : Array = []; + for await (const member of members) { + results.push(await member()); + } + return Promise.resolve>(results); + } + + + /** + */ + export function date_object_get_week_of_year( + date : Date + ) : int + { + let date_ : Date = new Date(date.getTime()); + date_.setHours(0, 0, 0, 0); + // Thursday in current week decides the year. + date_.setDate(date_.getDate() + 3 - (date_.getDay() + 6) % 7); + // January 4 is always in week 1. + let week1 : Date = new Date(date_.getFullYear(), 0, 4); + // Adjust to Thursday in week 1 and count number of weeks from date to week1. + return ( + 1 + + + Math.round( + ( + ((date_.getTime() - week1.getTime()) / 86400000) + - + 3 + + + (week1.getDay() + 6) % 7 + ) + / + 7 + ) + ); + } + + + /** + * @todo unite with type_datetimeobject? + */ + export type type_datetime = { + timezone_shift : int; + date : type_date; + time : ( + null + | + type_time + ); + }; + + + /** + * @todo timezone_shift + */ + function datetime_from_date_object( + date : Date, + options : { + timezone_shift ?: int; + } = {} + ) : type_datetime + { + options = Object.assign( + { + "timezone_shift": 0, + }, + options + ); + const date_ : Date = lib_plankton.call.convey( + date, + [ + (x : Date) => x.getTime(), + (x : int) => (x + (((options.timezone_shift as int) * (60 * 60)) * 1000)), + (x : int) => new Date(x), + ] + ); + const iso_string : string = date_.toISOString(); + return { + "timezone_shift": (options.timezone_shift as int), + "date": { + "year": parseInt(iso_string.slice(0, 4)), + "month": parseInt(iso_string.slice(5, 7)), + "day": parseInt(iso_string.slice(8, 10)), + }, + "time": { + "hour": parseInt(iso_string.slice(11, 13)), + "minute": parseInt(iso_string.slice(14, 16)), + "second": parseInt(iso_string.slice(17, 19)), + }, + }; + } + + + + /** + * @todo negative shift? + */ + function datetime_to_date_object( + datetime : type_datetime + ) : Date + { + const iso_string : string = lib_plankton.string.coin( + "{{year}}-{{month}}-{{day}}T{{hour}}:{{minute}}:{{second}}.000+{{shift}}", + { + "year": datetime.date.year.toFixed(0).padStart(4, "0"), + "month": datetime.date.month.toFixed(0).padStart(2, "0"), + "day": datetime.date.day.toFixed(0).padStart(2, "0"), + "hour": ((datetime.time !== null) ? datetime.time.hour : 0).toFixed(0).padStart(2, "0"), + "minute": ((datetime.time !== null) ? datetime.time.minute : 0).toFixed(0).padStart(2, "0"), + "second": ((datetime.time !== null) ? datetime.time.second : 0).toFixed(0).padStart(2, "0"), + "shift": (datetime.timezone_shift.toFixed(0).padStart(2, "0") + ":00"), + } + ); + return (new Date(iso_string)); + } + + + /** + */ + export type type_pit = int; + + + /** + */ + export function pit_to_date_object( + pit : type_pit + ) : Date + { + return (new Date(pit * 1000)); + } + + + /** + */ + function pit_from_date_object( + date_object : Date + ) : type_pit + { + return Math.round(date_object.getTime() / 1000); + } + + + /** + */ + export function pit_now( + ) : type_pit + { + return pit_from_date_object(new Date(Date.now())); + } + + + /** + * @todo timezone + */ + export function pit_to_datetime( + pit : type_pit, + options : { + timezone_shift ?: int + } = {} + ) : type_datetime + { + options = Object.assign( + { + "timezone_shift": 0, + }, + options + ); + const date_object : Date = pit_to_date_object(pit); + return datetime_from_date_object( + date_object, + { + "timezone_shift": (options.timezone_shift as int), + } + ); + } + + + /** + */ + export function pit_from_datetime( + datetime : type_datetime + ) : type_pit + { + return lib_plankton.call.convey( + datetime, + [ + datetime_to_date_object, + pit_from_date_object, + ] + ); + } + + + /** + */ + export function pit_is_before( + pit : type_pit, + reference : type_pit + ) : boolean + { + return (pit < reference); + } + + + /** + */ + function pit_is_after( + pit : type_pit, + reference : type_pit + ) : boolean + { + return (pit > reference); + } + + + /** + */ + export function pit_is_between( + pit : type_pit, + reference_left : type_pit, + reference_right : type_pit + ) : boolean + { + return ( + pit_is_after(pit, reference_left) + && + pit_is_before(pit, reference_right) + ); + } + + + /** + */ + function pit_shift_hour( + pit : type_pit, + increment : int + ) : type_pit + { + return (pit + (60 * 60 * increment)); + } + + + /** + */ + export function pit_shift_day( + pit : type_pit, + increment : int + ) : type_pit + { + return (pit + (60 * 60 * 24 * increment)); + } + + + /** + */ + export function pit_shift_week( + pit : type_pit, + increment : int + ) : type_pit + { + return (pit + (60 * 60 * 24 * 7 * increment)); + } + + + /** + */ + function pit_shift_year( + pit : type_pit, + increment : int + ) : type_pit + { + return (pit + (60 * 60 * 24 * 365 * increment)); + } + + + /** + */ + function pit_trunc_minute( + pit : type_pit + ) : type_pit + { + const datetime_input : type_datetime = pit_to_datetime(pit); + const datetime_output : type_datetime = { + "timezone_shift": 0, + "date": { + "year": datetime_input.date.year, + "month": datetime_input.date.month, + "day": datetime_input.date.day, + }, + "time": { + "hour": ( + (datetime_input.time === null) + ? + 0 + : + datetime_input.time.hour + ), + "minute": ( + (datetime_input.time === null) + ? + 0 + : + datetime_input.time.minute + ), + "second": 0, + }, + }; + return pit_from_datetime(datetime_output); + } + + + /** + */ + function pit_trunc_hour( + pit : type_pit + ) : type_pit + { + const datetime_input : type_datetime = pit_to_datetime(pit); + const datetime_output : type_datetime = { + "timezone_shift": 0, + "date": { + "year": datetime_input.date.year, + "month": datetime_input.date.month, + "day": datetime_input.date.day, + }, + "time": { + "hour": ( + (datetime_input.time === null) + ? + 0 + : + datetime_input.time.hour + ), + "minute": 0, + "second": 0, + }, + }; + return pit_from_datetime(datetime_output); + } + + + /** + */ + function pit_trunc_day( + pit : type_pit + ) : type_pit + { + const datetime_input : type_datetime = pit_to_datetime(pit); + const datetime_output : type_datetime = { + "timezone_shift": 0, + "date": { + "year": datetime_input.date.year, + "month": datetime_input.date.month, + "day": datetime_input.date.day, + }, + "time": { + "hour": 0, + "minute": 0, + "second": 0, + }, + }; + return pit_from_datetime(datetime_output); + } + + + /** + */ + export function pit_trunc_week( + pit : type_pit + ) : type_pit + { + const date_object : Date = pit_to_date_object(pit); + return lib_plankton.call.convey( + date_object.getDay(), + [ + (x : int) => ((x === 0) ? 7 : x), + (x : int) => (x - 1), + (x : int) => pit_shift_day(pit, (-x)), + pit_trunc_day + ] + ); + } + + + /** + */ + function pit_trunc_month( + pit : type_pit + ) : type_pit + { + const datetime_input : type_datetime = pit_to_datetime(pit); + const datetime_output : type_datetime = { + "timezone_shift": 0, + "date": { + "year": datetime_input.date.year, + "month": datetime_input.date.month, + "day": 1, + }, + "time": { + "hour": 0, + "minute": 0, + "second": 0, + }, + }; + return pit_from_datetime(datetime_output); + } + + + /** + */ + function pit_trunc_year( + pit : type_pit + ) : type_pit + { + const datetime_input : type_datetime = pit_to_datetime(pit); + const datetime_output : type_datetime = { + "timezone_shift": 0, + "date": { + "year": datetime_input.date.year, + "month": 1, + "day": 1, + }, + "time": { + "hour": 0, + "minute": 0, + "second": 0, + }, + }; + return pit_from_datetime(datetime_output); + } + + + /** + * @param year year according to specified timezone shift + * @param week week according to specified timezone shift + * @return the begin of the week (monday, 00:00) + */ + export function pit_from_year_and_week( + year : int, + week : int, + options : { + timezone_shift ?: int; + } = {} + ) : type_pit + { + options = Object.assign( + { + "timezone_shift": 0, + }, + options + ); + return lib_plankton.call.convey( + { + "timezone_shift": (options.timezone_shift as int), + "date": { + "year": year, + "month": 1, + "day": 1, + }, + "time": { + "hour": 0, + "minute": 0, + "second": 0 + } + }, + [ + pit_from_datetime, + (x : type_pit) => pit_shift_week(x, (week - 1)), + pit_trunc_week, + ] + ); + } + + + /** + * @todo timezone + */ + function ical_datetime_to_own_datetime( + ical_datetime : lib_plankton.ical.type_datetime + ) : type_datetime + { + return { + "timezone_shift": 0, + "date": { + "year": ical_datetime.date.year, + "month": ical_datetime.date.month, + "day": ical_datetime.date.day, + }, + "time": ( + (ical_datetime.time === null) + ? + null + : + { + "hour": ical_datetime.time.hour, + "minute": ical_datetime.time.minute, + "second": ical_datetime.time.second, + } + ) + }; + } + + + /** + * @todo timezone + */ + export function ical_dt_to_own_datetime( + ical_dt: lib_plankton.ical.type_dt + ) : type_datetime + { + return { + "timezone_shift": 0, + "date": ical_dt.value.date, + "time": ( + (ical_dt.value.time === null) + ? + null + : + { + "hour": ical_dt.value.time.hour, + "minute": ical_dt.value.time.minute, + "second": ical_dt.value.time.second, + } + ) + }; + } + +} diff --git a/source/logic/main.ts b/source/logic/main.ts new file mode 100644 index 0000000..35b3327 --- /dev/null +++ b/source/logic/main.ts @@ -0,0 +1,70 @@ +/** + */ +namespace _zeitbild.frontend +{ + + /** + */ + type type_conf = { + view_mode : string; + calendar_id : int; + timezone_shift : int; + }; + + + /** + */ + export async function main( + ) : Promise + { + // init + lib_plankton.log.conf_push( + [ + lib_plankton.log.channel_make({"kind": "console", "data": {"threshold": "info"}}), + ] + ); + + // conf + const conf : type_conf = lib_plankton.json.decode(await lib_plankton.file.read("conf.json")); + + // exec + let content : string; + switch (conf.view_mode) { + default: { + content = ""; + throw (new Error("invalid view mode")); + break; + } + case "table": { + content = await _zeitbild.frontend.view.calendar_view_table_html( + conf.calendar_id, + { + "from": { + "year": 2024, + "week": 35 + }, + "to": { + "year": 2024, + "week": 43 + }, + "timezone_shift": conf.timezone_shift, + } + ); + break; + } + case "list": { + content = await _zeitbild.frontend.view.calendar_view_list_html( + conf.calendar_id, + { + "timezone_shift": conf.timezone_shift, + } + ); + break; + } + } + (document.querySelector("body") as HTMLBodyElement).innerHTML = content; + return Promise.resolve(undefined); + } + +} + diff --git a/source/logic/types.ts b/source/logic/types.ts new file mode 100644 index 0000000..ac17b04 --- /dev/null +++ b/source/logic/types.ts @@ -0,0 +1,115 @@ + +/** + */ +namespace _zeitbild.frontend +{ + + /** + */ + type type_role = ( + "editor" + | + "viewer" + ); + + + /** + */ + type type_user_id = int; + + + /** + */ + type type_user_object = { + name : string; + }; + + + /** + */ + export type type_event = { + name : string; + begin : _zeitbild.frontend.helpers.type_datetime; + end : ( + null + | + _zeitbild.frontend.helpers.type_datetime + ); + location : ( + null + | + string + ); + description : ( + null + | + string + ); + }; + + + /** + */ + export type type_calendar_id = int; + + + /** + * @todo bei "collection" Kreise vermeiden + */ + export type type_calendar_object = ( + { + kind : "concrete"; + data : { + name : string; + private : boolean; + users : Array< + { + id : type_user_id; + role : type_role; + } + >; + events : Array; + }; + } + | + { + kind : "collection"; + data : { + name : string; + private : boolean; + sources : Array< + type_calendar_id + >; + } + } + | + { + kind : "caldav"; + data : { + name : string; + private : boolean; + read_only : boolean; + source_url : string; + } + } + ); + + + /** + */ + export type type_datamodel = { + users : Array< + { + id : type_user_id; + object : type_user_object; + } + >; + calendars : Array< + { + id : type_calendar_id; + object : type_calendar_object; + } + >; + }; + +} diff --git a/source/logic/view.ts b/source/logic/view.ts new file mode 100644 index 0000000..b44f838 --- /dev/null +++ b/source/logic/view.ts @@ -0,0 +1,695 @@ + +/** + */ +namespace _zeitbild.frontend.view +{ + + /** + */ + function event_generate_tooltip( + calendar_name : string, + event : type_event + ) : string + { + return ( + lib_plankton.string.coin( + "[{{calendar_name}}] {{event_name}}\n", + { + "calendar_name": calendar_name, + "event_name": event.name, + } + ) + + + "--\n" + + + ( + (event.begin.time !== null) + ? + lib_plankton.string.coin( + "{{label}}: {{value}}\n", + { + "label": "Anfang", // TODO: translate + "value": lib_plankton.string.coin( + "{{hour}}:{{minute}}", + { + "hour": event.begin.time.hour.toFixed(0).padStart(2, "0"), + "minute": event.begin.time.minute.toFixed(0).padStart(2, "0"), + } + ), // TODO: outsource + } + ) + : + "" + ) + + + ( + (event.end !== null) + ? + lib_plankton.string.coin( + "{{label}}: {{value}}\n", + { + "label": "Ende", // TODO: translate + "value": ( + [ + ( + ( + (event.end.date.year !== event.begin.date.year) + || + (event.end.date.month !== event.begin.date.month) + || + (event.end.date.day !== event.begin.date.day) + ) + ? + lib_plankton.string.coin( + "{{year}}-{{month}}-{{day}}", + { + "year": event.end.date.year.toFixed(0).padStart(4, "0"), + "month": event.end.date.month.toFixed(0).padStart(2, "0"), + "day": event.end.date.month.toFixed(0).padStart(2, "0"), + } + ) + : + null + ), + ( + (event.end.time !== null) + ? + lib_plankton.string.coin( + "{{hour}}:{{minute}}", + { + "hour": event.end.time.hour.toFixed(0).padStart(2, "0"), + "minute": event.end.time.minute.toFixed(0).padStart(2, "0"), + } + ) + : + null + ), + ] + .filter(x => (x !== null)) + .join(",") + ), + } + ) + : + "" + ) + + + ( + (event.location !== null) + ? + ( + lib_plankton.string.coin( + "{{label}}: {{value}}\n", + { + "label": "Ort", // TODO + "value": event.location, + } + ) + ) + : + "" + ) + + + ( + (event.description !== null) + ? + ( + "--\n" + + + lib_plankton.string.coin( + "{{description}}\n", + { + "description": event.description, + } + ) + ) + : + "" + ) + ); + } + + + /** + * @todo kein "while" + */ + async function calendar_view_table_data( + calendar_id : type_calendar_id, + options : { + from ?: { + year : int; + week : int; + }, + to ?: { + year : int; + week : int; + }, + timezone_shift ?: int; + } = {} + ) : Promise< + { + sources : lib_plankton.structures.type_hashmap< + type_calendar_id, + { + name : string; + } + >; + rows : Array< + { + week : int; + data : Array< + { + pit : _zeitbild.frontend.helpers.type_pit; + entries : Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + >; + today : boolean; + } + >; + } + > + } + > + { + const now_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_now(); + options = Object.assign( + { + "from": lib_plankton.call.convey( + now_pit, + [ + (x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, -1), + _zeitbild.frontend.helpers.pit_to_date_object, + (x : Date) => ({ + "year": x.getFullYear(), + "week": _zeitbild.frontend.helpers.date_object_get_week_of_year(x), + }) + ] + ), + "to": lib_plankton.call.convey( + now_pit, + [ + (x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, +4), + _zeitbild.frontend.helpers.pit_to_date_object, + (x : Date) => ({ + "year": x.getFullYear(), + "week": _zeitbild.frontend.helpers.date_object_get_week_of_year(x), + }) + ] + ), + "timezone_shift": 0, + }, + options + ); + /* + const calendar_object : type_calendar_object = calendar_read( + data, + calendar_id + ); + */ + const from_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_from_year_and_week( + (options.from as {year : int; week : int}).year, + (options.from as {year : int; week : int}).week, + { + "timezone_shift": (options.timezone_shift as int), + } + ); + const to_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_from_year_and_week( + (options.to as {year : int; week : int}).year, + (options.to as {year : int; week : int}).week, + { + "timezone_shift": (options.timezone_shift as int), + } + ); + + // prepare + const entries : Array< + { + calendar_id : type_calendar_id; + calendar_name : string; + event : type_event; + } + > = await _zeitbild.frontend.resources.backend.calendar_gather_events( + calendar_id, + from_pit, + to_pit + ); + let result : { + sources : lib_plankton.structures.type_hashmap< + type_calendar_id, + { + name : string; + } + >; + rows : Array< + { + week : int; + data : Array< + { + pit : _zeitbild.frontend.helpers.type_pit; + entries : Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + >; + today : boolean; + } + >; + } + >; + } = { + "sources": lib_plankton.structures.hashmap_construct( + x => x.toFixed(0), + ( + entries + .map( + (entry) => ( + { + "key": entry.calendar_id, + "value": { + "name": entry.calendar_name, + } + } + ) + ) + ) + ), + "rows": [], + }; + let row : Array< + { + pit : _zeitbild.frontend.helpers.type_pit; + entries : Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + >; + today : boolean; + } + > = []; + let day : int = 0; + while (true) { + const pit_current : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_shift_day( + from_pit, + day + ); + if ( + _zeitbild.frontend.helpers.pit_is_before( + pit_current, + to_pit + ) + ) { + day += 1; + row.push( + { + "pit": pit_current, + "entries": [], + "today": false, // TODO + } + ); + if (day % 7 === 0) { + result.rows.push( + { + "week": ( + (options.from as {year : int; week : int}).week + + + Math.floor(day / 7) + - + 1 // TODO + ), + "data": row + } + ); + row = []; + } + else { + // do nothing + } + } + else { + break; + } + } + + // fill + { + // events + ( + entries + .forEach( + (entry) => { + const distance_seconds : int = ( + _zeitbild.frontend.helpers.pit_from_datetime(entry.event.begin) + - + from_pit + ); + const distance_days : int = (distance_seconds / (60 * 60 * 24)); + + const week : int = Math.floor(Math.floor(distance_days) / 7); + const day : int = (Math.floor(distance_days) % 7); + + if ((week >= 0) && (week < result.rows.length)) { + result.rows[week].data[day].entries.push(entry); + } + else { + // do nothing + } + } + ) + ); + // today + { + const distance_seconds : int = ( + now_pit + - + from_pit + ); + const distance_days : int = (distance_seconds / (60 * 60 * 24)); + + const week : int = Math.floor(Math.floor(distance_days) / 7); + const day : int = (Math.floor(distance_days) % 7); + + if ((week >= 0) && (week < result.rows.length)) { + result.rows[week].data[day].today = true; + } + else { + // do nothing + } + } + } + + return Promise.resolve(result); + } + + + /** + */ + export async function calendar_view_table_html( + calendar_id : type_calendar_id, + options : { + from ?: { + year : int; + week : int; + }; + to ?: { + year : int; + week : int; + }; + timezone_shift ?: int; + } = {} + ) : Promise + { + const stuff : { + sources : lib_plankton.structures.type_hashmap< + type_calendar_id, + { + name : string; + } + >; + rows : Array< + { + week : int; + data : Array< + { + pit : _zeitbild.frontend.helpers.type_pit; + entries : Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + >; + today : boolean; + } + >; + } + >; + } = await calendar_view_table_data( + calendar_id, + options + ); + const sources : lib_plankton.structures.type_hashmap< + type_calendar_id, + { + name : string; + color : lib_plankton.color.type_color; + } + > = lib_plankton.structures.hashmap_construct( + (x => x.toFixed(0)), + lib_plankton.structures.hashmap_dump( + stuff.sources + ) + .map( + (pair) => ({ + "key": pair.key, + "value": { + "name": pair.value.name, + "color": lib_plankton.color.give_generic( + (pair.key + 0.2), + { + "saturation": 0.375, + "value": 0.375, + } + ), + } + }) + ) + ); + return _zeitbild.frontend.helpers.template_coin( + "tableview", + { + "sources": ( + await _zeitbild.frontend.helpers.promise_row( + lib_plankton.structures.hashmap_dump(sources) + .map( + ({"key": calendar_id, "value": data}) => async () => _zeitbild.frontend.helpers.template_coin( + "tableview-sources-entry", + { + "name": data.name, + "color": lib_plankton.color.output_hex(data.color), + } + ) + ) + ) + ).join(""), + "rows": ( + await _zeitbild.frontend.helpers.promise_row( + stuff.rows + .map( + (row) => async () => _zeitbild.frontend.helpers.template_coin( + "tableview-row", + { + "week": row.week.toFixed(0).padStart(2, "0"), + "cells": ( + await _zeitbild.frontend.helpers.promise_row( + row.data + .map( + (cell) => async () => _zeitbild.frontend.helpers.template_coin( + "tableview-cell", + { + "extra_classes": ( + [""] + .concat(cell.today ? ["calendar-cell-today"] : []) + .join(" ") + ), + "title": lib_plankton.call.convey( + cell.pit, + [ + _zeitbild.frontend.helpers.pit_to_datetime, + (x : _zeitbild.frontend.helpers.type_datetime) => lib_plankton.string.coin( + "{{year}}-{{month}}-{{day}}", + { + "year": x.date.year.toFixed(0).padStart(4, "0"), + "month": x.date.month.toFixed(0).padStart(2, "0"), + "day": x.date.day.toFixed(0).padStart(2, "0"), + } + ), + ] + ), + "day": lib_plankton.call.convey( + cell.pit, + [ + _zeitbild.frontend.helpers.pit_to_datetime, + (x : _zeitbild.frontend.helpers.type_datetime) => lib_plankton.string.coin( + "{{day}}", + { + "year": x.date.year.toFixed(0).padStart(4, "0"), + "month": x.date.month.toFixed(0).padStart(2, "0"), + "day": x.date.day.toFixed(0).padStart(2, "0"), + } + ), + ] + ), + "entries": ( + await _zeitbild.frontend.helpers.promise_row( + cell.entries + .map( + (entry) => () => _zeitbild.frontend.helpers.template_coin( + "tableview-cell-entry", + { + "color": lib_plankton.color.output_hex( + lib_plankton.structures.hashmap_get( + sources, + entry.calendar_id + ).color + ), + "title": event_generate_tooltip( + lib_plankton.structures.hashmap_get( + sources, + entry.calendar_id + ).name, + entry.event + ), + "name": entry.event.name, + } + ) + ) + ) + ).join(""), + } + ) + ) + ) + ).join(""), + } + ) + ) + ) + ).join(""), + } + ); + } + + + /** + */ + async function calendar_view_list_data( + calendar_id : type_calendar_id, + options : { + from ?: _zeitbild.frontend.helpers.type_pit; + to ?: _zeitbild.frontend.helpers.type_pit; + timezone_shift ?: int; + } = {} + ) : Promise< + Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + > + > + { + const now_pit : _zeitbild.frontend.helpers.type_pit = _zeitbild.frontend.helpers.pit_now(); + options = Object.assign( + { + "from": lib_plankton.call.convey( + now_pit, + [ + (x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_day(x, -1), + ] + ), + "to": lib_plankton.call.convey( + now_pit, + [ + (x : _zeitbild.frontend.helpers.type_pit) => _zeitbild.frontend.helpers.pit_shift_week(x, +4), + ] + ), + "timezone_shift": 0, + }, + options + ); + + const entries : Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + > = await _zeitbild.frontend.resources.backend.calendar_gather_events( + calendar_id, + (options.from as _zeitbild.frontend.helpers.type_pit), + (options.to as _zeitbild.frontend.helpers.type_pit) + ); + // TODO: optimize + entries.sort( + (entry_1, entry_2) => ( + _zeitbild.frontend.helpers.pit_from_datetime(entry_1.event.begin) + - + _zeitbild.frontend.helpers.pit_from_datetime(entry_2.event.begin) + ) + ); + + return Promise.resolve(entries); + } + + + /** + */ + export async function calendar_view_list_html( + calendar_id : type_calendar_id, + options : { + from ?: _zeitbild.frontend.helpers.type_pit; + to ?: _zeitbild.frontend.helpers.type_pit; + timezone_shift ?: int; + } = {} + ) : Promise + { + const stuff : Array< + { + calendar_id : type_calendar_id; + event : type_event; + } + > = await calendar_view_list_data( + calendar_id, + options + ); + return Promise.resolve( + new lib_plankton.xml.class_node_complex( + "div", + { + "class": "list", + }, + [ + new lib_plankton.xml.class_node_complex( + "style", + {}, + [ + new lib_plankton.xml.class_node_text( + "html {background-color: #111; color: #FFF; font-family: sans-serif;}\n" + + + "table {width: 100%; border-collapse: collapse;}\n" + ) + ] + ), + new lib_plankton.xml.class_node_complex( + "ul", + { + "class": "list-events", + }, + ( + stuff + .map( + (entry) => ( + new lib_plankton.xml.class_node_complex( + "li", + { + "class": "list-event_entry", + }, + [ + new lib_plankton.xml.class_node_text( + JSON.stringify(entry) + ), + ] + ) + ) + ) + ) + ), + ] + ).compile() + ); + } + +} diff --git a/source/style/main.css b/source/style/main.css new file mode 100644 index 0000000..aeabb9b --- /dev/null +++ b/source/style/main.css @@ -0,0 +1,78 @@ +html { + background-color: #111; + color: #FFF; + font-family: sans-serif; +} + +.calendar { + display: flex; + flex-direction: row; + flex-wrap: wrap; +} + +.calendar-pane-left { + flex-basis: 12.5%; +} + +.calendar-pane-right { + flex-basis: 87.5%; +} + +.tableview-sources { + margin: 0; + padding: 0; + list-style-type: none; +} + +.tableview-sources-entry { + margin: 8px; + padding: 4px; +} + +.calendar table { + width: 100%; + border-collapse: collapse; +} + +.calendar-cell { + border: 1px solid #888; + padding: 8px; + vertical-align: top; +} + +.calendar-cell-day { + width: 13.5%; +} + +.calendar-cell-week { + width: 5.5%; +} + +.calendar-cell-regular { + width: 13.5%; + height: 120px; +} + +.calendar-cell-today { + outline: 4px solid #FFF; +} + +.calendar-day { + font-size: 0.75em; + cursor: help; +} + +.calendar-events { + margin: 0; padding: 0; + list-style-type: none; +} + +.calendar-event_entry { + margin: 4px; + padding: 4px; + border-radius: 2px; + font-size: 0.75em; + color: #FFF; + font-weight: bold; + cursor: pointer; +} diff --git a/source/templates/main.html.tpl b/source/templates/main.html.tpl new file mode 100644 index 0000000..db89e2e --- /dev/null +++ b/source/templates/main.html.tpl @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/source/templates/tableview-cell-entry.html.tpl b/source/templates/tableview-cell-entry.html.tpl new file mode 100644 index 0000000..d0346b1 --- /dev/null +++ b/source/templates/tableview-cell-entry.html.tpl @@ -0,0 +1,3 @@ +
  • + {{name}} +
  • diff --git a/source/templates/tableview-cell.html.tpl b/source/templates/tableview-cell.html.tpl new file mode 100644 index 0000000..fa4a53f --- /dev/null +++ b/source/templates/tableview-cell.html.tpl @@ -0,0 +1,8 @@ + + + {{day}} + +
      +{{entries}} +
    + diff --git a/source/templates/tableview-row.html.tpl b/source/templates/tableview-row.html.tpl new file mode 100644 index 0000000..29e19af --- /dev/null +++ b/source/templates/tableview-row.html.tpl @@ -0,0 +1,6 @@ + + + {{week}} + +{{cells}} + diff --git a/source/templates/tableview-sources-entry.html.tpl b/source/templates/tableview-sources-entry.html.tpl new file mode 100644 index 0000000..a78d64c --- /dev/null +++ b/source/templates/tableview-sources-entry.html.tpl @@ -0,0 +1 @@ +
  • {{name}}
  • diff --git a/source/templates/tableview.html.tpl b/source/templates/tableview.html.tpl new file mode 100644 index 0000000..b476b5a --- /dev/null +++ b/source/templates/tableview.html.tpl @@ -0,0 +1,27 @@ +
    +
    +
      +{{sources}} +
    +
    +
    + + + + + + + + + + + + + + +{{rows}} + +
    MoDiMiDoFrSaSo
    +
    +
    + diff --git a/tools/build b/tools/build index 1f4e770..82e348f 100755 --- a/tools/build +++ b/tools/build @@ -16,6 +16,22 @@ def main(): metavar = "", help = "output directory", ) + argument_parser.add_argument( + "-c", + "--conf-path", + type = str, + default = None, + metavar = "", + help = "conf path", + ) + argument_parser.add_argument( + "-d", + "--data-path", + type = str, + default = None, + metavar = "", + help = "data path", + ) args = argument_parser.parse_args() ## exec @@ -28,6 +44,28 @@ def main(): " ".join(targets), ) ) + if True: + if (args.conf_path is None): + pass + else: + _os.system( + "cp %s %s/conf.json" + % ( + args.conf_path, + args.output_directory, + ) + ) + if True: + if (args.data_path is None): + pass + else: + _os.system( + "cp %s %s/data.json" + % ( + args.data_path, + args.output_directory, + ) + ) _sys.stdout.write("%s\n" % args.output_directory) diff --git a/tools/makefile b/tools/makefile index 9321f25..a479915 100644 --- a/tools/makefile +++ b/tools/makefile @@ -6,37 +6,58 @@ dir_temp := /tmp/kalender-temp dir_build := build dir_tools := tools -cmd_log := echo "--" cmd_cat := cat cmd_chmod := chmod -cmd_mkdir := mkdir -p cmd_cp := cp +cmd_log := echo "--" +cmd_mkdir := mkdir -p cmd_tsc := ${dir_tools}/typescript/node_modules/.bin/tsc ## rules .PHONY: default -default: ${dir_build}/kalender +default: index templates style logic -${dir_temp}/kalender-unlinked.js: \ +.PHONY: index +index: ${dir_build}/index.html + +${dir_build}/index.html: \ + ${dir_source}/index.html + @ ${cmd_log} "index …" + @ ${cmd_cp} -u -v $^ $@ + +.PHONY: templates +templates: \ + $(wildcard ${dir_source}/templates/*) + @ ${cmd_log} "templates …" + @ ${cmd_mkdir} ${dir_build}/templates + @ ${cmd_cp} -r -u -v ${dir_source}/templates/* ${dir_build}/templates/ + +.PHONY: style +style: \ + $(wildcard ${dir_source}/style/*) + @ ${cmd_log} "style …" + @ ${cmd_mkdir} ${dir_build} + @ ${cmd_cat} ${dir_source}/style/* > ${dir_build}/style.css + +.PHONY: logic +logic: ${dir_build}/logic.js + +${dir_temp}/logic-unlinked.js: \ ${dir_lib}/plankton/plankton.d.ts \ - ${dir_source}/helpers.ts \ - ${dir_source}/logic.ts \ - ${dir_source}/main.ts - @ ${cmd_log} "compile …" + ${dir_source}/logic/helpers.ts \ + ${dir_source}/logic/types.ts \ + ${dir_source}/logic/backend.ts \ + ${dir_source}/logic/view.ts \ + ${dir_source}/logic/main.ts + @ ${cmd_log} "logic | compile …" @ ${cmd_mkdir} $(dir $@) - @ ${cmd_tsc} --lib es2020 --strict $^ --outFile $@ + @ ${cmd_tsc} --lib dom,es2020 --strict $^ --outFile $@ -${dir_temp}/head.js: - @ ${cmd_mkdir} $(dir $@) - @ echo "#!/usr/bin/env node" > $@ - -${dir_build}/kalender: \ - ${dir_temp}/head.js \ +${dir_build}/logic.js: \ ${dir_lib}/plankton/plankton.js \ - ${dir_temp}/kalender-unlinked.js - @ ${cmd_log} "link …" + ${dir_temp}/logic-unlinked.js + @ ${cmd_log} "logic | link …" @ ${cmd_mkdir} $(dir $@) @ ${cmd_cat} $^ > $@ - @ ${cmd_chmod} +x $@ diff --git a/tools/update-plankton b/tools/update-plankton index 41270c0..ccf3d0d 100755 --- a/tools/update-plankton +++ b/tools/update-plankton @@ -8,6 +8,7 @@ modules="" modules="${modules} base" modules="${modules} call" modules="${modules} file" +modules="${modules} structures" modules="${modules} json" modules="${modules} args" modules="${modules} string" @@ -23,5 +24,5 @@ modules="${modules} url" mkdir -p ${dir} cd ${dir} -ptk bundle node ${modules} +ptk bundle web ${modules} cd - > /dev/null