@tutao/tutanota-utils 3.94.0 → 3.94.1
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.
- package/dist/ArrayUtils.d.ts +39 -44
- package/dist/ArrayUtils.js +229 -220
- package/dist/AsyncResult.d.ts +15 -17
- package/dist/AsyncResult.js +21 -33
- package/dist/CollectionUtils.d.ts +1 -1
- package/dist/CollectionUtils.js +1 -1
- package/dist/DateUtils.d.ts +18 -18
- package/dist/DateUtils.js +38 -40
- package/dist/Encoding.d.ts +25 -25
- package/dist/Encoding.js +181 -175
- package/dist/LazyLoaded.d.ts +32 -32
- package/dist/LazyLoaded.js +66 -65
- package/dist/MapUtils.d.ts +4 -4
- package/dist/MapUtils.js +25 -24
- package/dist/MathUtils.d.ts +2 -2
- package/dist/MathUtils.js +2 -2
- package/dist/PromiseMap.d.ts +4 -8
- package/dist/PromiseMap.js +50 -49
- package/dist/PromiseUtils.d.ts +16 -19
- package/dist/PromiseUtils.js +76 -72
- package/dist/SortedArray.d.ts +11 -11
- package/dist/SortedArray.js +30 -30
- package/dist/StringUtils.d.ts +14 -14
- package/dist/StringUtils.js +36 -31
- package/dist/TypeRef.d.ts +11 -11
- package/dist/TypeRef.js +15 -15
- package/dist/Utils.d.ts +53 -59
- package/dist/Utils.js +227 -207
- package/dist/index.d.ts +19 -145
- package/dist/index.js +14 -140
- package/dist/tsbuildinfo +1 -1
- package/package.json +3 -2
package/dist/Utils.js
CHANGED
|
@@ -1,94 +1,100 @@
|
|
|
1
|
-
import { TypeRef } from "./TypeRef.js"
|
|
1
|
+
import { TypeRef } from "./TypeRef.js";
|
|
2
2
|
export function defer() {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
let ret = {};
|
|
4
|
+
ret.promise = new Promise((resolve, reject) => {
|
|
5
|
+
ret.resolve = resolve;
|
|
6
|
+
ret.reject = reject;
|
|
7
|
+
});
|
|
8
|
+
return ret;
|
|
9
9
|
}
|
|
10
10
|
export function deferWithHandler(handler) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const deferred = {};
|
|
12
|
+
deferred.promise = new Promise((resolve, reject) => {
|
|
13
|
+
deferred.resolve = resolve;
|
|
14
|
+
deferred.reject = reject;
|
|
15
|
+
}).then(handler);
|
|
16
|
+
return deferred;
|
|
17
17
|
}
|
|
18
18
|
export async function asyncFind(array, finder) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
for (let i = 0; i < array.length; i++) {
|
|
20
|
+
const item = array[i];
|
|
21
|
+
if (await finder(item, i, array.length)) {
|
|
22
|
+
return item;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
26
|
}
|
|
27
27
|
export async function asyncFindAndMap(array, finder) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
for (let i = 0; i < array.length; i++) {
|
|
29
|
+
const item = array[i];
|
|
30
|
+
const mapped = await finder(item, i, array.length);
|
|
31
|
+
if (mapped) {
|
|
32
|
+
return mapped;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Calls an executor function for slices of nbrOfElementsInGroup items of the given array until the executor function returns false.
|
|
39
39
|
*/
|
|
40
40
|
export function executeInGroups(array, nbrOfElementsInGroup, executor) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
if (array.length > 0) {
|
|
42
|
+
let nextSlice = Math.min(array.length, nbrOfElementsInGroup);
|
|
43
|
+
return executor(array.slice(0, nextSlice)).then(doContinue => {
|
|
44
|
+
if (doContinue) {
|
|
45
|
+
return executeInGroups(array.slice(nextSlice), nbrOfElementsInGroup, executor);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
return Promise.resolve();
|
|
51
|
+
}
|
|
51
52
|
}
|
|
52
53
|
export function neverNull(object) {
|
|
53
|
-
|
|
54
|
+
return object;
|
|
54
55
|
}
|
|
55
56
|
export function assertNotNull(object, message = "null") {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
if (object == null) {
|
|
58
|
+
throw new Error("AssertNotNull failed : " + message);
|
|
59
|
+
}
|
|
60
|
+
return object;
|
|
60
61
|
}
|
|
61
62
|
export function isNotNull(t) {
|
|
62
|
-
|
|
63
|
+
return t != null;
|
|
63
64
|
}
|
|
64
65
|
export function assert(assertion, message) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
if (!resolveMaybeLazy(assertion)) {
|
|
67
|
+
throw new Error(`Assertion failed: ${message}`);
|
|
68
|
+
}
|
|
68
69
|
}
|
|
69
70
|
export function downcast(object) {
|
|
70
|
-
|
|
71
|
+
return object;
|
|
71
72
|
}
|
|
72
73
|
export function clone(instance) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
74
|
+
if (instance instanceof Uint8Array) {
|
|
75
|
+
return downcast(instance.slice());
|
|
76
|
+
}
|
|
77
|
+
else if (instance instanceof Array) {
|
|
78
|
+
return downcast(instance.map(i => clone(i)));
|
|
79
|
+
}
|
|
80
|
+
else if (instance instanceof Date) {
|
|
81
|
+
return new Date(instance.getTime());
|
|
82
|
+
}
|
|
83
|
+
else if (instance instanceof TypeRef) {
|
|
84
|
+
return instance;
|
|
85
|
+
}
|
|
86
|
+
else if (instance instanceof Object) {
|
|
87
|
+
// Can only pass null or Object, cannot pass undefined
|
|
88
|
+
const copy = Object.create(Object.getPrototypeOf(instance) || null);
|
|
89
|
+
Object.assign(copy, instance);
|
|
90
|
+
for (let key of Object.keys(copy)) {
|
|
91
|
+
copy[key] = clone(copy[key]);
|
|
92
|
+
}
|
|
93
|
+
return copy;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return instance;
|
|
97
|
+
}
|
|
92
98
|
}
|
|
93
99
|
/**
|
|
94
100
|
* Function which accepts another function. On first invocation
|
|
@@ -96,17 +102,18 @@ export function clone(instance) {
|
|
|
96
102
|
* on consequent invocations.
|
|
97
103
|
*/
|
|
98
104
|
export function lazyMemoized(source) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
// Using separate variable for tracking because value can be undefined and we want to the function call only once
|
|
106
|
+
let cached = false;
|
|
107
|
+
let value;
|
|
108
|
+
return () => {
|
|
109
|
+
if (cached) {
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
cached = true;
|
|
114
|
+
return (value = source());
|
|
115
|
+
}
|
|
116
|
+
};
|
|
110
117
|
}
|
|
111
118
|
/**
|
|
112
119
|
* Returns a cached version of {@param fn}.
|
|
@@ -115,43 +122,44 @@ export function lazyMemoized(source) {
|
|
|
115
122
|
* Only remembers the last argument.
|
|
116
123
|
*/
|
|
117
124
|
export function memoized(fn) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
let lastArg;
|
|
126
|
+
let lastResult;
|
|
127
|
+
let didCache = false;
|
|
128
|
+
return arg => {
|
|
129
|
+
if (!didCache || arg !== lastArg) {
|
|
130
|
+
lastArg = arg;
|
|
131
|
+
didCache = true;
|
|
132
|
+
lastResult = fn(arg);
|
|
133
|
+
}
|
|
134
|
+
return lastResult;
|
|
135
|
+
};
|
|
129
136
|
}
|
|
130
137
|
/**
|
|
131
138
|
* Function which returns what was passed into it
|
|
132
139
|
*/
|
|
133
140
|
export function identity(t) {
|
|
134
|
-
|
|
141
|
+
return t;
|
|
135
142
|
}
|
|
136
143
|
/**
|
|
137
144
|
* Function which does nothing.
|
|
138
145
|
*/
|
|
139
|
-
export function noOp() {
|
|
146
|
+
export function noOp() {
|
|
147
|
+
}
|
|
140
148
|
/**
|
|
141
149
|
* Return a function, which executed {@param toThrottle} only after it is not invoked for {@param timeout} ms.
|
|
142
150
|
* Executes function with the last passed arguments
|
|
143
151
|
* @return {Function}
|
|
144
152
|
*/
|
|
145
153
|
export function debounce(timeout, toThrottle) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
let timeoutId;
|
|
155
|
+
let toInvoke;
|
|
156
|
+
return downcast((...args) => {
|
|
157
|
+
if (timeoutId) {
|
|
158
|
+
clearTimeout(timeoutId);
|
|
159
|
+
}
|
|
160
|
+
toInvoke = toThrottle.bind(null, ...args);
|
|
161
|
+
timeoutId = setTimeout(toInvoke, timeout);
|
|
162
|
+
});
|
|
155
163
|
}
|
|
156
164
|
/**
|
|
157
165
|
* Returns a debounced function. When invoked for the first time, will just invoke
|
|
@@ -161,102 +169,113 @@ export function debounce(timeout, toThrottle) {
|
|
|
161
169
|
* but ones in the middle (which happen too often) are discarded.}
|
|
162
170
|
*/
|
|
163
171
|
export function debounceStart(timeout, toThrottle) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
172
|
+
let timeoutId;
|
|
173
|
+
let lastInvoked = 0;
|
|
174
|
+
return downcast((...args) => {
|
|
175
|
+
if (Date.now() - lastInvoked < timeout) {
|
|
176
|
+
timeoutId && clearTimeout(timeoutId);
|
|
177
|
+
timeoutId = setTimeout(() => {
|
|
178
|
+
timeoutId = null;
|
|
179
|
+
toThrottle.apply(null, args);
|
|
180
|
+
}, timeout);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
toThrottle.apply(null, args);
|
|
184
|
+
}
|
|
185
|
+
lastInvoked = Date.now();
|
|
186
|
+
});
|
|
178
187
|
}
|
|
179
188
|
export function randomIntFromInterval(min, max) {
|
|
180
|
-
|
|
189
|
+
return Math.floor(Math.random() * (max - min + 1) + min);
|
|
181
190
|
}
|
|
182
191
|
export function errorToString(error) {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
let errorString = error.name ? error.name : "?";
|
|
193
|
+
if (error.message) {
|
|
194
|
+
errorString += `\n Error message: ${error.message}`;
|
|
195
|
+
}
|
|
196
|
+
if (error.stack) {
|
|
197
|
+
// the error id is included in the stacktrace
|
|
198
|
+
errorString += `\nStacktrace: \n${error.stack}`;
|
|
199
|
+
}
|
|
200
|
+
return errorString;
|
|
192
201
|
}
|
|
193
202
|
/**
|
|
194
203
|
* Like {@link Object.entries} but preserves the type of the key and value
|
|
195
204
|
*/
|
|
196
205
|
export function objectEntries(object) {
|
|
197
|
-
|
|
206
|
+
return downcast(Object.entries(object));
|
|
198
207
|
}
|
|
199
208
|
/**
|
|
200
209
|
* modified deepEquals from ospec is only needed as long as we use custom classes (TypeRef) and Date is not properly handled
|
|
201
210
|
*/
|
|
202
211
|
export function deepEqual(a, b) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
212
|
+
if (a === b)
|
|
213
|
+
return true;
|
|
214
|
+
if (xor(a === null, b === null) || xor(a === undefined, b === undefined))
|
|
215
|
+
return false;
|
|
216
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
217
|
+
const aIsArgs = isArguments(a), bIsArgs = isArguments(b);
|
|
218
|
+
if (a.length === b.length && ((a instanceof Array && b instanceof Array) || (aIsArgs && bIsArgs))) {
|
|
219
|
+
const aKeys = Object.getOwnPropertyNames(a), bKeys = Object.getOwnPropertyNames(b);
|
|
220
|
+
if (aKeys.length !== bKeys.length)
|
|
221
|
+
return false;
|
|
222
|
+
for (let i = 0; i < aKeys.length; i++) {
|
|
223
|
+
if (!hasOwn.call(b, aKeys[i]) || !deepEqual(a[aKeys[i]], b[aKeys[i]]))
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
if (a instanceof Date && b instanceof Date)
|
|
229
|
+
return a.getTime() === b.getTime();
|
|
230
|
+
if (a instanceof Object && b instanceof Object && !aIsArgs && !bIsArgs) {
|
|
231
|
+
for (let i in a) {
|
|
232
|
+
if (!(i in b) || !deepEqual(a[i], b[i]))
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
for (let i in b) {
|
|
236
|
+
if (!(i in a))
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
if (typeof Buffer === "function" && a instanceof Buffer && b instanceof Buffer) {
|
|
242
|
+
for (let i = 0; i < a.length; i++) {
|
|
243
|
+
if (a[i] !== b[i])
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
if (a.valueOf() === b.valueOf())
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
return false;
|
|
236
252
|
}
|
|
237
253
|
function xor(a, b) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
254
|
+
const aBool = !!a;
|
|
255
|
+
const bBool = !!b;
|
|
256
|
+
return (aBool && !bBool) || (bBool && !aBool);
|
|
241
257
|
}
|
|
242
258
|
function isArguments(a) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
259
|
+
if ("callee" in a) {
|
|
260
|
+
for (let i in a)
|
|
261
|
+
if (i === "callee")
|
|
262
|
+
return false;
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const hasOwn = {}.hasOwnProperty;
|
|
249
267
|
/**
|
|
250
268
|
* returns an array of top-level properties that are in both objA and objB, but differ in value
|
|
251
269
|
* does not handle functions or circular references
|
|
252
270
|
* treats undefined and null as equal
|
|
253
271
|
*/
|
|
254
272
|
export function getChangedProps(objA, objB) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
273
|
+
if (objA == null || objB == null || objA === objB)
|
|
274
|
+
return [];
|
|
275
|
+
return Object.keys(objA)
|
|
276
|
+
.filter(k => Object.keys(objB).includes(k))
|
|
277
|
+
.filter(k => ![null, undefined].includes(objA[k]) || ![null, undefined].includes(objB[k]))
|
|
278
|
+
.filter(k => !deepEqual(objA[k], objB[k]));
|
|
260
279
|
}
|
|
261
280
|
/**
|
|
262
281
|
* Disallow set, delete and clear on Map.
|
|
@@ -265,75 +284,76 @@ export function getChangedProps(objA, objB) {
|
|
|
265
284
|
* @return {unknown}
|
|
266
285
|
*/
|
|
267
286
|
export function freezeMap(myMap) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
287
|
+
function mapSet(key, value) {
|
|
288
|
+
throw new Error("Can't add property " + key + ", map is not extensible");
|
|
289
|
+
}
|
|
290
|
+
function mapDelete(key) {
|
|
291
|
+
throw new Error("Can't delete property " + key + ", map is frozen");
|
|
292
|
+
}
|
|
293
|
+
function mapClear() {
|
|
294
|
+
throw new Error("Can't clear map, map is frozen");
|
|
295
|
+
}
|
|
296
|
+
const anyMap = downcast(myMap);
|
|
297
|
+
anyMap.set = mapSet;
|
|
298
|
+
anyMap.delete = mapDelete;
|
|
299
|
+
anyMap.clear = mapClear;
|
|
300
|
+
Object.freeze(anyMap);
|
|
301
|
+
return anyMap;
|
|
283
302
|
}
|
|
284
303
|
export function addressDomain(senderAddress) {
|
|
285
|
-
|
|
304
|
+
return senderAddress.slice(senderAddress.lastIndexOf("@") + 1);
|
|
286
305
|
}
|
|
287
306
|
/**
|
|
288
307
|
* Ignores the fact that Object.keys returns also not owned properties.
|
|
289
308
|
*/
|
|
290
309
|
export function typedKeys(obj) {
|
|
291
|
-
|
|
310
|
+
return downcast(Object.keys(obj));
|
|
292
311
|
}
|
|
293
312
|
/**
|
|
294
313
|
* Ignores the fact that Object.keys returns also not owned properties.
|
|
295
314
|
*/
|
|
296
315
|
export function typedEntries(obj) {
|
|
297
|
-
|
|
316
|
+
return downcast(Object.entries(obj));
|
|
298
317
|
}
|
|
299
318
|
/**
|
|
300
319
|
* Ignores the fact that Object.keys returns also not owned properties.
|
|
301
320
|
*/
|
|
302
321
|
export function typedValues(obj) {
|
|
303
|
-
|
|
322
|
+
return downcast(Object.values(obj));
|
|
304
323
|
}
|
|
305
324
|
export function resolveMaybeLazy(maybe) {
|
|
306
|
-
|
|
325
|
+
return typeof maybe === "function" ? maybe() : maybe;
|
|
307
326
|
}
|
|
308
327
|
export function getAsLazy(maybe) {
|
|
309
|
-
|
|
328
|
+
return typeof maybe === "function" ? downcast(maybe) : () => maybe;
|
|
310
329
|
}
|
|
311
330
|
export function mapLazily(maybe, mapping) {
|
|
312
|
-
|
|
331
|
+
return () => mapping(resolveMaybeLazy(maybe));
|
|
313
332
|
}
|
|
314
333
|
/**
|
|
315
334
|
* Stricter version of parseInt() from MDN. parseInt() allows some arbitrary characters at the end of the string.
|
|
316
335
|
* Returns NaN in case there's anything non-number in the string.
|
|
317
336
|
*/
|
|
318
337
|
export function filterInt(value) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
338
|
+
if (/^\d+$/.test(value)) {
|
|
339
|
+
return parseInt(value, 10);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
return NaN;
|
|
343
|
+
}
|
|
324
344
|
}
|
|
325
345
|
export function insideRect(point, rect) {
|
|
326
|
-
|
|
346
|
+
return point.x >= rect.left && point.x < rect.right && point.y >= rect.top && point.y < rect.bottom;
|
|
327
347
|
}
|
|
328
348
|
/**
|
|
329
349
|
* If val is non null, returns the result of val passed to action, else null
|
|
330
350
|
*/
|
|
331
351
|
export function mapNullable(val, action) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
352
|
+
if (val != null) {
|
|
353
|
+
const result = action(val);
|
|
354
|
+
if (result != null) {
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
339
359
|
}
|