@tutao/tutanota-utils 3.91.1 → 3.91.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @file DateUtils which do not use Luxon. Used in worker as well as in client parts.
3
+ * As functions here do not use Luxon it cannot be used for calculating things in different time zones, they
4
+ * are dependent on the system time zone.
5
+ */
6
+ export const DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
7
+ /**
8
+ * Provides a date representing the beginning of the next day of the given date in local time.
9
+ */
10
+ export function getStartOfNextDay(date) {
11
+ let d = new Date(date.getTime());
12
+ d.setDate(date.getDate() + 1);
13
+ d.setHours(0, 0, 0, 0); // sets the beginning of the day in local time
14
+ return d;
15
+ }
16
+ /**
17
+ * Provides a date representing the end of the given date in local time.
18
+ */
19
+ export function getEndOfDay(date) {
20
+ let d = new Date(date.getTime());
21
+ d.setHours(23, 59, 59, 999);
22
+ return d;
23
+ }
24
+ /**
25
+ * Provides a date representing the beginning of the given date in local time.
26
+ */
27
+ export function getStartOfDay(date) {
28
+ return getHourOfDay(date, 0);
29
+ }
30
+ /**
31
+ * Provides a date representing the day of the given date at the given hour in local time.
32
+ */
33
+ export function getHourOfDay(date, hour) {
34
+ let d = new Date(date.getTime());
35
+ d.setHours(hour, 0, 0, 0);
36
+ return d;
37
+ }
38
+ export function isStartOfDay(date) {
39
+ return date.getHours() === 0 && date.getMinutes() === 0;
40
+ }
41
+ /**
42
+ * Returns true if the given date is today in local time.
43
+ */
44
+ export function isToday(date) {
45
+ return new Date().toDateString() === date.toDateString();
46
+ }
47
+ /**
48
+ * Returns true if the given dates represent the same day (time of day is ignored).
49
+ */
50
+ export function isSameDay(date1, date2) {
51
+ return date1.toDateString() === date2.toDateString();
52
+ }
53
+ /**
54
+ * Creates new date in with {@param days} added to it as if the days are just fixed
55
+ * periods of time and are not subject to daylight saving.
56
+ */
57
+ export function getDayShifted(date, days) {
58
+ return new Date(date.getTime() + days * DAY_IN_MILLIS);
59
+ }
60
+ /**
61
+ * Increment the date in place and return it
62
+ */
63
+ export function incrementDate(date, byValue) {
64
+ date.setDate(date.getDate() + byValue);
65
+ return date;
66
+ }
67
+ export function incrementMonth(d, byValue) {
68
+ const date = new Date(d);
69
+ date.setMonth(date.getMonth() + byValue);
70
+ return date;
71
+ }
72
+ export function isSameDayOfDate(date1, date2) {
73
+ return ((!date1 && !date2) ||
74
+ (date1 != null &&
75
+ date2 != null &&
76
+ date1.getFullYear() === date2.getFullYear() &&
77
+ date1.getMonth() === date2.getMonth() &&
78
+ date1.getDate() === date2.getDate()));
79
+ }
80
+ /**
81
+ * Formats as yyyy-mm-dd
82
+ */
83
+ export function formatSortableDate(date) {
84
+ const month = ("0" + (date.getMonth() + 1)).slice(-2);
85
+ const day = ("0" + date.getDate()).slice(-2);
86
+ return `${date.getFullYear()}-${month}-${day}`;
87
+ }
88
+ /**
89
+ * Formats as yyyy-mm-dd-<hh>h-<mm>m-<ss>
90
+ */
91
+ export function formatSortableDateTime(date) {
92
+ const hours = ("0" + date.getHours()).slice(-2);
93
+ const minutes = ("0" + date.getMinutes()).slice(-2);
94
+ const seconds = ("0" + date.getSeconds()).slice(-2);
95
+ return `${formatSortableDate(date)}-${hours}h${minutes}m${seconds}s`;
96
+ }
97
+ /**
98
+ * @returns {string} sortableDateTime of the current time
99
+ */
100
+ export function sortableTimestamp() {
101
+ return formatSortableDateTime(new Date());
102
+ }
103
+ export function isValidDate(date) {
104
+ return !isNaN(date.getTime());
105
+ }
106
+ /**
107
+ * not interested in any fancy calendar edge cases, only use this where approximation is ok
108
+ */
109
+ export function millisToDays(millis) {
110
+ return millis / DAY_IN_MILLIS;
111
+ }
112
+ export function daysToMillis(days) {
113
+ return days * DAY_IN_MILLIS;
114
+ }
@@ -0,0 +1,103 @@
1
+ export declare type Base64 = string;
2
+ export declare type Base64Ext = string;
3
+ export declare type Base64Url = string;
4
+ export declare type Hex = string;
5
+ export declare function uint8ArrayToArrayBuffer(uint8Array: Uint8Array): ArrayBuffer;
6
+ /**
7
+ * Converts a hex coded string into a base64 coded string.
8
+ *
9
+ * @param hex A hex encoded string.
10
+ * @return A base64 encoded string.
11
+ */
12
+ export declare function hexToBase64(hex: Hex): Base64;
13
+ /**
14
+ * Converts a base64 coded string into a hex coded string.
15
+ *
16
+ * @param base64 A base64 encoded string.
17
+ * @return A hex encoded string.
18
+ */
19
+ export declare function base64ToHex(base64: Base64): Hex;
20
+ /**
21
+ * Converts a base64 string to a url-conform base64 string. This is used for
22
+ * base64 coded url parameters.
23
+ *
24
+ * @param base64 The base64 string.
25
+ * @return The base64url string.
26
+ */
27
+ export declare function base64ToBase64Url(base64: Base64): Base64Url;
28
+ /**
29
+ * Converts a base64 string to a base64ext string. Base64ext uses another character set than base64 in order to make it sortable.
30
+ *
31
+ *
32
+ * @param base64 The base64 string.
33
+ * @return The base64Ext string.
34
+ */
35
+ export declare function base64ToBase64Ext(base64: Base64): Base64Ext;
36
+ /**
37
+ * Converts a Base64Ext string to a Base64 string and appends the padding if needed.
38
+ * @param base64ext The base64Ext string
39
+ * @returns The base64 string
40
+ */
41
+ export declare function base64ExtToBase64(base64ext: Base64Ext): Base64;
42
+ /**
43
+ * Converts a base64 url string to a "normal" base64 string. This is used for
44
+ * base64 coded url parameters.
45
+ *
46
+ * @param base64url The base64 url string.
47
+ * @return The base64 string.
48
+ */
49
+ export declare function base64UrlToBase64(base64url: Base64Url): Base64;
50
+ export declare function _stringToUtf8Uint8ArrayLegacy(string: string): Uint8Array;
51
+ export declare function _replaceLoneSurrogates(s: string | null | undefined): string;
52
+ /**
53
+ * Converts a string to a Uint8Array containing a UTF-8 string data.
54
+ *
55
+ * @param string The string to convert.
56
+ * @return The array.
57
+ */
58
+ export declare function stringToUtf8Uint8Array(string: string): Uint8Array;
59
+ export declare function _utf8Uint8ArrayToStringLegacy(uint8Array: Uint8Array): string;
60
+ /**
61
+ * Converts an Uint8Array containing UTF-8 string data into a string.
62
+ *
63
+ * @param uint8Array The Uint8Array.
64
+ * @return The string.
65
+ */
66
+ export declare function utf8Uint8ArrayToString(uint8Array: Uint8Array): string;
67
+ export declare function hexToUint8Array(hex: Hex): Uint8Array;
68
+ export declare function uint8ArrayToHex(uint8Array: Uint8Array): Hex;
69
+ /**
70
+ * Converts an Uint8Array to a Base64 encoded string.
71
+ *
72
+ * @param bytes The bytes to convert.
73
+ * @return The Base64 encoded string.
74
+ */
75
+ export declare function uint8ArrayToBase64(bytes: Uint8Array): Base64;
76
+ export declare function int8ArrayToBase64(bytes: Int8Array): Base64;
77
+ /**
78
+ * Converts a base64 encoded string to a Uint8Array.
79
+ *
80
+ * @param base64 The Base64 encoded string.
81
+ * @return The bytes.
82
+ */
83
+ export declare function base64ToUint8Array(base64: Base64): Uint8Array;
84
+ /**
85
+ * Converts a Uint8Array containing string data into a string, given the charset the data is in.
86
+ * @param charset The charset. Must be supported by TextDecoder.
87
+ * @param bytes The string data
88
+ * @trhows RangeError if the charset is not supported
89
+ * @return The string
90
+ */
91
+ export declare function uint8ArrayToString(charset: string, bytes: Uint8Array): string;
92
+ /**
93
+ * Decodes a quoted-printable piece of text in a given charset.
94
+ * This was copied and modified from https://github.com/mathiasbynens/quoted-printable/blob/master/src/quoted-printable.js (MIT licensed)
95
+ *
96
+ * @param charset Must be supported by TextEncoder
97
+ * @param input The encoded text
98
+ * @throws RangeError if the charset is not supported
99
+ * @returns The text as a JavaScript string
100
+ */
101
+ export declare function decodeQuotedPrintable(charset: string, input: string): string;
102
+ export declare function decodeBase64(charset: string, input: string): string;
103
+ export declare function stringToBase64(str: string): string;
@@ -0,0 +1,306 @@
1
+ // TODO rename methods according to their JAVA counterparts (e.g. Uint8Array == bytes, Utf8Uint8Array == bytes...)
2
+ export function uint8ArrayToArrayBuffer(uint8Array) {
3
+ if (uint8Array.byteLength === uint8Array.buffer.byteLength) {
4
+ return uint8Array.buffer;
5
+ }
6
+ else {
7
+ return new Uint8Array(uint8Array).buffer; // create a new instance with the correct length, if uint8Array is only a DataView on a longer Array.buffer
8
+ }
9
+ }
10
+ /**
11
+ * Converts a hex coded string into a base64 coded string.
12
+ *
13
+ * @param hex A hex encoded string.
14
+ * @return A base64 encoded string.
15
+ */
16
+ export function hexToBase64(hex) {
17
+ return uint8ArrayToBase64(hexToUint8Array(hex));
18
+ }
19
+ /**
20
+ * Converts a base64 coded string into a hex coded string.
21
+ *
22
+ * @param base64 A base64 encoded string.
23
+ * @return A hex encoded string.
24
+ */
25
+ export function base64ToHex(base64) {
26
+ return uint8ArrayToHex(base64ToUint8Array(base64));
27
+ }
28
+ /**
29
+ * Converts a base64 string to a url-conform base64 string. This is used for
30
+ * base64 coded url parameters.
31
+ *
32
+ * @param base64 The base64 string.
33
+ * @return The base64url string.
34
+ */
35
+ export function base64ToBase64Url(base64) {
36
+ let base64url = base64.replace(/\+/g, "-");
37
+ base64url = base64url.replace(/\//g, "_");
38
+ base64url = base64url.replace(/=/g, "");
39
+ return base64url;
40
+ }
41
+ function makeLookup(str) {
42
+ const lookup = {};
43
+ for (let i = 0; i < str.length; i++) {
44
+ lookup[str.charAt(i)] = i;
45
+ }
46
+ return lookup;
47
+ }
48
+ const base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
49
+ const base64Lookup = makeLookup(base64Alphabet);
50
+ const base64extAlphabet = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
51
+ const base64ExtLookup = makeLookup(base64extAlphabet);
52
+ /**
53
+ * Converts a base64 string to a base64ext string. Base64ext uses another character set than base64 in order to make it sortable.
54
+ *
55
+ *
56
+ * @param base64 The base64 string.
57
+ * @return The base64Ext string.
58
+ */
59
+ export function base64ToBase64Ext(base64) {
60
+ base64 = base64.replace(/=/g, "");
61
+ let base64ext = "";
62
+ for (let i = 0; i < base64.length; i++) {
63
+ let index = base64Lookup[base64.charAt(i)];
64
+ base64ext += base64extAlphabet[index];
65
+ }
66
+ return base64ext;
67
+ }
68
+ /**
69
+ * Converts a Base64Ext string to a Base64 string and appends the padding if needed.
70
+ * @param base64ext The base64Ext string
71
+ * @returns The base64 string
72
+ */
73
+ export function base64ExtToBase64(base64ext) {
74
+ let base64 = "";
75
+ for (let i = 0; i < base64ext.length; i++) {
76
+ const index = base64ExtLookup[base64ext.charAt(i)];
77
+ base64 += base64Alphabet[index];
78
+ }
79
+ let padding;
80
+ if (base64.length % 4 === 2) {
81
+ padding = "==";
82
+ }
83
+ else if (base64.length % 4 === 3) {
84
+ padding = "=";
85
+ }
86
+ else {
87
+ padding = "";
88
+ }
89
+ return base64 + padding;
90
+ }
91
+ /**
92
+ * Converts a base64 url string to a "normal" base64 string. This is used for
93
+ * base64 coded url parameters.
94
+ *
95
+ * @param base64url The base64 url string.
96
+ * @return The base64 string.
97
+ */
98
+ export function base64UrlToBase64(base64url) {
99
+ let base64 = base64url.replace(/-/g, "+");
100
+ base64 = base64.replace(/_/g, "/");
101
+ let nbrOfRemainingChars = base64.length % 4;
102
+ if (nbrOfRemainingChars === 0) {
103
+ return base64;
104
+ }
105
+ else if (nbrOfRemainingChars === 2) {
106
+ return base64 + "==";
107
+ }
108
+ else if (nbrOfRemainingChars === 3) {
109
+ return base64 + "=";
110
+ }
111
+ throw new Error("Illegal base64 string.");
112
+ }
113
+ // just for edge, as it does not support TextEncoder yet
114
+ export function _stringToUtf8Uint8ArrayLegacy(string) {
115
+ let fixedString;
116
+ try {
117
+ fixedString = encodeURIComponent(string);
118
+ }
119
+ catch (e) {
120
+ fixedString = encodeURIComponent(_replaceLoneSurrogates(string)); // we filter lone surrogates as trigger URIErrors, otherwise (see https://github.com/tutao/tutanota/issues/618)
121
+ }
122
+ let utf8 = unescape(fixedString);
123
+ let uint8Array = new Uint8Array(utf8.length);
124
+ for (let i = 0; i < utf8.length; i++) {
125
+ uint8Array[i] = utf8.charCodeAt(i);
126
+ }
127
+ return uint8Array;
128
+ }
129
+ const REPLACEMENT_CHAR = "\uFFFD";
130
+ export function _replaceLoneSurrogates(s) {
131
+ if (s == null) {
132
+ return "";
133
+ }
134
+ let result = [];
135
+ for (let i = 0; i < s.length; i++) {
136
+ let code = s.charCodeAt(i);
137
+ let char = s.charAt(i);
138
+ if (0xd800 <= code && code <= 0xdbff) {
139
+ if (s.length === i) {
140
+ // replace high surrogate without following low surrogate
141
+ result.push(REPLACEMENT_CHAR);
142
+ }
143
+ else {
144
+ let next = s.charCodeAt(i + 1);
145
+ if (0xdc00 <= next && next <= 0xdfff) {
146
+ result.push(char);
147
+ result.push(s.charAt(i + 1));
148
+ i++; // valid high and low surrogate, skip next low surrogate check
149
+ }
150
+ else {
151
+ result.push(REPLACEMENT_CHAR);
152
+ }
153
+ }
154
+ }
155
+ else if (0xdc00 <= code && code <= 0xdfff) {
156
+ // replace low surrogate without preceding high surrogate
157
+ result.push(REPLACEMENT_CHAR);
158
+ }
159
+ else {
160
+ result.push(char);
161
+ }
162
+ }
163
+ return result.join("");
164
+ }
165
+ const encoder = typeof TextEncoder == "function"
166
+ ? new TextEncoder()
167
+ : {
168
+ encode: _stringToUtf8Uint8ArrayLegacy,
169
+ };
170
+ const decoder = typeof TextDecoder == "function"
171
+ ? new TextDecoder()
172
+ : {
173
+ decode: _utf8Uint8ArrayToStringLegacy,
174
+ };
175
+ /**
176
+ * Converts a string to a Uint8Array containing a UTF-8 string data.
177
+ *
178
+ * @param string The string to convert.
179
+ * @return The array.
180
+ */
181
+ export function stringToUtf8Uint8Array(string) {
182
+ return encoder.encode(string);
183
+ }
184
+ // just for edge, as it does not support TextDecoder yet
185
+ export function _utf8Uint8ArrayToStringLegacy(uint8Array) {
186
+ let stringArray = [];
187
+ stringArray.length = uint8Array.length;
188
+ for (let i = 0; i < uint8Array.length; i++) {
189
+ stringArray[i] = String.fromCharCode(uint8Array[i]);
190
+ }
191
+ return decodeURIComponent(escape(stringArray.join("")));
192
+ }
193
+ /**
194
+ * Converts an Uint8Array containing UTF-8 string data into a string.
195
+ *
196
+ * @param uint8Array The Uint8Array.
197
+ * @return The string.
198
+ */
199
+ export function utf8Uint8ArrayToString(uint8Array) {
200
+ return decoder.decode(uint8Array);
201
+ }
202
+ export function hexToUint8Array(hex) {
203
+ let bufView = new Uint8Array(hex.length / 2);
204
+ for (let i = 0; i < bufView.byteLength; i++) {
205
+ bufView[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
206
+ }
207
+ return bufView;
208
+ }
209
+ const hexDigits = "0123456789abcdef";
210
+ export function uint8ArrayToHex(uint8Array) {
211
+ let hex = "";
212
+ for (let i = 0; i < uint8Array.byteLength; i++) {
213
+ let value = uint8Array[i];
214
+ hex += hexDigits[value >> 4] + hexDigits[value & 15];
215
+ }
216
+ return hex;
217
+ }
218
+ /**
219
+ * Converts an Uint8Array to a Base64 encoded string.
220
+ *
221
+ * @param bytes The bytes to convert.
222
+ * @return The Base64 encoded string.
223
+ */
224
+ export function uint8ArrayToBase64(bytes) {
225
+ if (bytes.length < 512) {
226
+ // Apply fails on big arrays fairly often. We tried it with 60000 but if you're already
227
+ // deep in the stack than we cannot allocate such a big argument array.
228
+ return btoa(String.fromCharCode(...bytes));
229
+ }
230
+ let binary = "";
231
+ const len = bytes.byteLength;
232
+ for (let i = 0; i < len; i++) {
233
+ binary += String.fromCharCode(bytes[i]);
234
+ }
235
+ return btoa(binary);
236
+ }
237
+ export function int8ArrayToBase64(bytes) {
238
+ // Values 0 to 127 are the same for signed and unsigned bytes
239
+ // and -128 to -1 are mapped to the same chars as 128 to 255.
240
+ let converted = new Uint8Array(bytes);
241
+ return uint8ArrayToBase64(converted);
242
+ }
243
+ /**
244
+ * Converts a base64 encoded string to a Uint8Array.
245
+ *
246
+ * @param base64 The Base64 encoded string.
247
+ * @return The bytes.
248
+ */
249
+ export function base64ToUint8Array(base64) {
250
+ if (base64.length % 4 !== 0) {
251
+ throw new Error(`invalid base64 length: ${base64} (${base64.length})`);
252
+ }
253
+ const binaryString = atob(base64);
254
+ const result = new Uint8Array(binaryString.length);
255
+ for (let i = 0; i < binaryString.length; i++) {
256
+ result[i] = binaryString.charCodeAt(i);
257
+ }
258
+ return result;
259
+ }
260
+ /**
261
+ * Converts a Uint8Array containing string data into a string, given the charset the data is in.
262
+ * @param charset The charset. Must be supported by TextDecoder.
263
+ * @param bytes The string data
264
+ * @trhows RangeError if the charset is not supported
265
+ * @return The string
266
+ */
267
+ export function uint8ArrayToString(charset, bytes) {
268
+ const decoder = new TextDecoder(charset);
269
+ return decoder.decode(bytes);
270
+ }
271
+ /**
272
+ * Decodes a quoted-printable piece of text in a given charset.
273
+ * This was copied and modified from https://github.com/mathiasbynens/quoted-printable/blob/master/src/quoted-printable.js (MIT licensed)
274
+ *
275
+ * @param charset Must be supported by TextEncoder
276
+ * @param input The encoded text
277
+ * @throws RangeError if the charset is not supported
278
+ * @returns The text as a JavaScript string
279
+ */
280
+ export function decodeQuotedPrintable(charset, input) {
281
+ return (input // https://tools.ietf.org/html/rfc2045#section-6.7, rule 3:
282
+ // “Therefore, when decoding a `Quoted-Printable` body, any trailing white
283
+ // space on a line must be deleted, as it will necessarily have been added
284
+ // by intermediate transport agents.”
285
+ .replace(/[\t\x20]$/gm, "") // Remove hard line breaks preceded by `=`. Proper `Quoted-Printable`-
286
+ // encoded data only contains CRLF line endings, but for compatibility
287
+ // reasons we support separate CR and LF too.
288
+ .replace(/=(?:\r\n?|\n|$)/g, "") // Decode escape sequences of the form `=XX` where `XX` is any
289
+ // combination of two hexidecimal digits. For optimal compatibility,
290
+ // lowercase hexadecimal digits are supported as well. See
291
+ // https://tools.ietf.org/html/rfc2045#section-6.7, note 1.
292
+ .replace(/(=([a-fA-F0-9]{2}))+/g, match => {
293
+ const hexValues = match.split(/=/);
294
+ // splitting on '=' is convenient, but adds an empty string at the start due to the first byte
295
+ hexValues.shift();
296
+ const intArray = hexValues.map(char => parseInt(char, 16));
297
+ const bytes = Uint8Array.from(intArray);
298
+ return uint8ArrayToString(charset, bytes);
299
+ }));
300
+ }
301
+ export function decodeBase64(charset, input) {
302
+ return uint8ArrayToString(charset, base64ToUint8Array(input));
303
+ }
304
+ export function stringToBase64(str) {
305
+ return uint8ArrayToBase64(stringToUtf8Uint8Array(str));
306
+ }
@@ -0,0 +1,38 @@
1
+ import type { lazyAsync } from "./Utils.js";
2
+ /**
3
+ * A wrapper for an object that shall be lazy loaded asynchronously. If loading the object is triggered in parallel (getAsync()) the object is actually only loaded once but returned to all calls of getAsync().
4
+ * If the object was loaded once it is not loaded again.
5
+ */
6
+ export declare class LazyLoaded<T> {
7
+ _isLoaded: boolean;
8
+ _loadingPromise: Promise<T> | null;
9
+ _loadedObject: T | null;
10
+ _loadFunction: lazyAsync<T>;
11
+ /**
12
+ * @param loadFunction The function that actually loads the object as soon as getAsync() is called the first time.
13
+ * @param defaultValue The value that shall be returned by getSync() or getLoaded() as long as the object is not loaded yet.
14
+ */
15
+ constructor(loadFunction: lazyAsync<T>, defaultValue?: T);
16
+ load(): this;
17
+ isLoaded(): boolean;
18
+ /**
19
+ * Loads the object if it is not loaded yet. May be called in parallel and takes care that the load function is only called once.
20
+ */
21
+ getAsync(): Promise<T>;
22
+ /**
23
+ * Returns null if the object is not loaded yet.
24
+ */
25
+ getSync(): T | null;
26
+ /**
27
+ * Only call this function if you know that the object is already loaded.
28
+ */
29
+ getLoaded(): T;
30
+ /**
31
+ * Removes the currently loaded object, so it will be loaded again with the next getAsync() call. Does not set any default value.
32
+ */
33
+ reset(): void;
34
+ /**
35
+ * Loads the object again and replaces the current one
36
+ */
37
+ reload(): Promise<T>;
38
+ }
@@ -0,0 +1,72 @@
1
+ import { neverNull } from "./Utils.js";
2
+ /**
3
+ * A wrapper for an object that shall be lazy loaded asynchronously. If loading the object is triggered in parallel (getAsync()) the object is actually only loaded once but returned to all calls of getAsync().
4
+ * If the object was loaded once it is not loaded again.
5
+ */
6
+ export class LazyLoaded {
7
+ /**
8
+ * @param loadFunction The function that actually loads the object as soon as getAsync() is called the first time.
9
+ * @param defaultValue The value that shall be returned by getSync() or getLoaded() as long as the object is not loaded yet.
10
+ */
11
+ constructor(loadFunction, defaultValue) {
12
+ this._isLoaded = false;
13
+ this._loadFunction = loadFunction;
14
+ this._loadingPromise = null;
15
+ this._loadedObject = defaultValue !== null && defaultValue !== void 0 ? defaultValue : null;
16
+ }
17
+ load() {
18
+ this.getAsync();
19
+ return this;
20
+ }
21
+ isLoaded() {
22
+ return this._isLoaded;
23
+ }
24
+ /**
25
+ * Loads the object if it is not loaded yet. May be called in parallel and takes care that the load function is only called once.
26
+ */
27
+ getAsync() {
28
+ if (this.isLoaded()) {
29
+ return Promise.resolve(neverNull(this._loadedObject));
30
+ }
31
+ else {
32
+ if (!this._loadingPromise) {
33
+ this._loadingPromise = this._loadFunction().then(result => {
34
+ this._loadedObject = result;
35
+ this._isLoaded = true;
36
+ return result;
37
+ });
38
+ }
39
+ return this._loadingPromise;
40
+ }
41
+ }
42
+ /**
43
+ * Returns null if the object is not loaded yet.
44
+ */
45
+ getSync() {
46
+ return this._loadedObject;
47
+ }
48
+ /**
49
+ * Only call this function if you know that the object is already loaded.
50
+ */
51
+ getLoaded() {
52
+ return neverNull(this._loadedObject);
53
+ }
54
+ /**
55
+ * Removes the currently loaded object, so it will be loaded again with the next getAsync() call. Does not set any default value.
56
+ */
57
+ reset() {
58
+ this._isLoaded = false;
59
+ this._loadingPromise = null;
60
+ this._loadedObject = null;
61
+ }
62
+ /**
63
+ * Loads the object again and replaces the current one
64
+ */
65
+ reload() {
66
+ return this._loadFunction().then(result => {
67
+ this._isLoaded = true;
68
+ this._loadedObject = result;
69
+ return result;
70
+ });
71
+ }
72
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Merges multiple maps into a single map with lists of values.
3
+ * @param maps
4
+ */
5
+ export declare function mergeMaps<T>(maps: Map<string, T>[]): Map<string, T[]>;
6
+ export declare function getFromMap<K, V>(map: Map<K, V>, key: K, byDefault: () => V): V;
7
+ /** Creates a new map with key and value added to {@param map}. It is like set() but for immutable map. */
8
+ export declare function addMapEntry<K, V>(map: ReadonlyMap<K, V>, key: K, value: V): Map<K, V>;
9
+ export declare function deleteMapEntry<K, V>(map: ReadonlyMap<K, V>, key: K): Map<K, V>;
@@ -0,0 +1,38 @@
1
+ import { neverNull } from "./Utils.js";
2
+ /**
3
+ * Merges multiple maps into a single map with lists of values.
4
+ * @param maps
5
+ */
6
+ export function mergeMaps(maps) {
7
+ return maps.reduce((mergedMap, map) => {
8
+ // merge same key of multiple attributes
9
+ map.forEach((value, key) => {
10
+ if (mergedMap.has(key)) {
11
+ neverNull(mergedMap.get(key)).push(value);
12
+ }
13
+ else {
14
+ mergedMap.set(key, [value]);
15
+ }
16
+ });
17
+ return mergedMap;
18
+ }, new Map());
19
+ }
20
+ export function getFromMap(map, key, byDefault) {
21
+ let value = map.get(key);
22
+ if (!value) {
23
+ value = byDefault();
24
+ map.set(key, value);
25
+ }
26
+ return value;
27
+ }
28
+ /** Creates a new map with key and value added to {@param map}. It is like set() but for immutable map. */
29
+ export function addMapEntry(map, key, value) {
30
+ const newMap = new Map(map);
31
+ newMap.set(key, value);
32
+ return newMap;
33
+ }
34
+ export function deleteMapEntry(map, key) {
35
+ const newMap = new Map(map);
36
+ newMap.delete(key);
37
+ return newMap;
38
+ }
@@ -0,0 +1,5 @@
1
+ export declare function mod(n: number, m: number): number;
2
+ /**
3
+ * Clamp value to between min and max (inclusive)
4
+ */
5
+ export declare function clamp(value: number, min: number, max: number): number;