bt-core-app 0.0.0

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.
Files changed (53) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/README.md +45 -0
  3. package/index.html +13 -0
  4. package/package.json +39 -0
  5. package/public/vite.svg +1 -0
  6. package/src/assets/vue.svg +1 -0
  7. package/src/components/BT-Btn.vue +40 -0
  8. package/src/components/BT-Col.vue +36 -0
  9. package/src/components/BT-Span.vue +21 -0
  10. package/src/components/Dialog-Confirm.vue +47 -0
  11. package/src/components/Dialog-Select-Date.vue +89 -0
  12. package/src/components/Dialog-Select.vue +140 -0
  13. package/src/components/Dialog-Text.vue +59 -0
  14. package/src/composables/actions-tracker.ts +99 -0
  15. package/src/composables/actions.ts +354 -0
  16. package/src/composables/api.ts +471 -0
  17. package/src/composables/auth.ts +382 -0
  18. package/src/composables/cosmetics.ts +178 -0
  19. package/src/composables/csv.ts +198 -0
  20. package/src/composables/dates.ts +79 -0
  21. package/src/composables/demo.ts +25 -0
  22. package/src/composables/dialogs.ts +115 -0
  23. package/src/composables/document-meta.ts +40 -0
  24. package/src/composables/draggable.ts +189 -0
  25. package/src/composables/filters.ts +256 -0
  26. package/src/composables/forage.ts +49 -0
  27. package/src/composables/helpers.ts +694 -0
  28. package/src/composables/id.ts +20 -0
  29. package/src/composables/list.ts +601 -0
  30. package/src/composables/navigation.ts +214 -0
  31. package/src/composables/presets.ts +20 -0
  32. package/src/composables/pwa.ts +89 -0
  33. package/src/composables/resizable.ts +382 -0
  34. package/src/composables/rules.ts +40 -0
  35. package/src/composables/stores.ts +797 -0
  36. package/src/composables/track.ts +55 -0
  37. package/src/composables/urls.ts +11 -0
  38. package/src/core.ts +92 -0
  39. package/src/index.ts +16 -0
  40. package/src/types.ts +13 -0
  41. package/src/useApi.ts +68 -0
  42. package/src/vite-env.d.ts +1 -0
  43. package/test/api.test.ts +84 -0
  44. package/test/forage.test.ts +31 -0
  45. package/test/helpers.test.ts +231 -0
  46. package/test/navigation.test.ts +99 -0
  47. package/test/stores-last-update.test.ts +138 -0
  48. package/test/stores-session.test.ts +118 -0
  49. package/test/track.test.ts +29 -0
  50. package/test/utils.ts +15 -0
  51. package/tsconfig.json +33 -0
  52. package/tsconfig.node.json +11 -0
  53. package/vite.config.ts +19 -0
@@ -0,0 +1,694 @@
1
+ import { firstBy } from 'thenby';
2
+ import { type MaybeRefOrGetter, toValue } from 'vue';
3
+
4
+
5
+ export function appendUrl(originalVal?: string, additionalVal?: string) {
6
+ let original = originalVal ?? ''
7
+ let additional = additionalVal ?? ''
8
+
9
+ if (original.endsWith('/')) {
10
+ do {
11
+ original = original.slice(0, original.length - 1)
12
+ } while (original.endsWith('/'));
13
+ }
14
+
15
+ if (additional.startsWith('/')) {
16
+ do {
17
+ additional = additional.slice(1, additional.length)
18
+ } while (additional.startsWith('/'));
19
+ }
20
+
21
+ return `${original}/${additional}`
22
+ }
23
+
24
+ export function extensionExists(elementId: string = 'blitzItExtensionExists') {
25
+ try {
26
+ var el = document.getElementById(elementId);
27
+ return el != null;
28
+ }
29
+ catch (ex) {
30
+ console.log(extractErrorDescription(ex));
31
+ return false;
32
+ }
33
+ }
34
+
35
+ //#region area and space
36
+
37
+ interface GeoCoordinate {
38
+ lat: number
39
+ lng: number
40
+ }
41
+
42
+ // /**
43
+ // * confirms if area is a certain size
44
+ // * @param boundary array of 4 x { lat: number, lng: number }
45
+ // * @param size
46
+ // * @returns
47
+ // */
48
+ // export function isAreaOfSize(boundary?: GeoCoordinate[], size?: number) {
49
+ // if (boundary == null || size == null || boundary.length != 4) {
50
+ // return false;
51
+ // }
52
+
53
+ // var middleLat = boundary[0].lat + size;
54
+ // var middleLng = boundary[0].lng - size;
55
+
56
+ // if ((boundary[1].lat + size) != middleLat) {
57
+ // return false;
58
+ // }
59
+ // if ((boundary[1].lng + size) != middleLng) {
60
+ // return false;
61
+ // }
62
+
63
+ // if ((boundary[2].lat - size) != middleLat) {
64
+ // return false;
65
+ // }
66
+ // if ((boundary[2].lng + size) != middleLng) {
67
+ // return false;
68
+ // }
69
+
70
+ // if ((boundary[3].lat - size) != middleLat) {
71
+ // return false;
72
+ // }
73
+ // if ((boundary[3].lng - size) != middleLng) {
74
+ // return false;
75
+ // }
76
+
77
+ // return true;
78
+ // }
79
+
80
+ /**
81
+ * get area around a certain location with a space of the given size
82
+ * @param location
83
+ * @param radius
84
+ * @returns
85
+ */
86
+ export function getAreaAround(location: GeoCoordinate, radius: number) {
87
+ return [
88
+ { lat: location.lat - radius, lng: location.lng + radius },
89
+ { lat: location.lat - radius, lng: location.lng - radius },
90
+ { lat: location.lat + radius, lng: location.lng - radius },
91
+ { lat: location.lat + radius, lng: location.lng + radius }
92
+ ];
93
+ }
94
+
95
+ /**get square area using the location as the far right line */
96
+ export function getAreaToLeft(location: GeoCoordinate, radius: number) {
97
+ return [
98
+ { lat: location.lat - (radius * 2), lng: location.lng + radius },
99
+ { lat: location.lat - (radius * 2), lng: location.lng - radius },
100
+ { lat: location.lat, lng: location.lng - radius },
101
+ { lat: location.lat, lng: location.lng + radius }
102
+ ];
103
+ }
104
+
105
+ /**get square area using the location as the far left line */
106
+ export function getAreaToRight(location: GeoCoordinate, radius: number) {
107
+ return [
108
+ { lat: location.lat, lng: location.lng + radius },
109
+ { lat: location.lat, lng: location.lng - radius },
110
+ { lat: location.lat + (radius * 2), lng: location.lng - radius },
111
+ { lat: location.lat + (radius * 2), lng: location.lng + radius }
112
+ ];
113
+ }
114
+
115
+ //#endregion
116
+
117
+ //#region locations
118
+
119
+ /**
120
+ *
121
+ * @param value converts location to a single string and standardizes state and road names, etc.
122
+ * @returns
123
+ */
124
+ export function getGoogleMapsLocationLine(value: any) {
125
+ var str = getLocationLine(value, true);
126
+
127
+ str = str.toLowerCase(); //str.replaceAll(' ', '').toLowerCase();
128
+ //replace values
129
+ str = str.replace(' victoria ', 'vic');
130
+ str = str.replace(' queensland ', 'qld');
131
+ str = str.replace(' new south wales ', 'nsw');
132
+ str = str.replace(' northern territory ', 'nt');
133
+ str = str.replace(' western australia ', 'wa');
134
+ str = str.replace(' tasmania ', 'tas');
135
+ str = str.replace(' south australia ', 'sa');
136
+ str = str.replace(' australian captial territory ', 'act');
137
+
138
+ str = str.replace(' & ', ' and ');
139
+ str = str.replace(' road', ' rd');
140
+ str = str.replace(' street', ' st');
141
+ str = str.replace(' lane', ' ln');
142
+ str = str.replace(' alley', ' aly');
143
+ str = str.replace(' arcade', ' arc');
144
+ str = str.replace(' boulevard', ' blvd');
145
+ str = str.replace(' court', ' ct');
146
+ str = str.replace(' cove', ' cv');
147
+ str = str.replace(' highway', ' hwy');
148
+
149
+ return str.replaceAll(' ', '');
150
+ }
151
+
152
+ export function getLocationLine(value: any, forGoogle: boolean = false) {
153
+ if (value == null) {
154
+ return '';
155
+ }
156
+
157
+ if (typeof value !== 'object') {
158
+ return value;
159
+ }
160
+
161
+ var rStr = '';
162
+
163
+ if (value.addressLineOne != null && !forGoogle) {
164
+ rStr = value.addressLineOne + ' ';
165
+ }
166
+ if (value.streetNumber != null) {
167
+ rStr = rStr + value.streetNumber + ' ';
168
+ }
169
+ if (value.streetName != null) {
170
+ rStr = rStr + value.streetName + ', ';
171
+ }
172
+ if (value.suburb != null) {
173
+ rStr = rStr + value.suburb + ' ';
174
+ }
175
+ if (value.state != null) {
176
+ rStr = rStr + value.state + ' ';
177
+ }
178
+ if (value.postcode != null) {
179
+ rStr = rStr + value.postcode;
180
+ }
181
+
182
+ return rStr;
183
+ }
184
+
185
+
186
+
187
+ //#endregion
188
+
189
+ //#region images
190
+
191
+ export function checkImage(url?: string, goodCallback?: any, badCallback?: any) {
192
+ if (!url)
193
+ return
194
+
195
+ var img = new Image();
196
+ img.onload = goodCallback;
197
+ img.onerror = badCallback;
198
+ img.src = url;
199
+ }
200
+
201
+ export async function getImageData(url?: string, throwErrorOnFail: boolean = true) {
202
+ return new Promise((resolve, reject) => {
203
+ if (url == null)
204
+ reject('no url given')
205
+
206
+ var img = new Image();
207
+ img.setAttribute('crossOrigin', 'anonymous');
208
+ img.onload = function () {
209
+ var canvas = document.createElement('canvas');
210
+ //@ts-ignore
211
+ canvas.width = this.width;
212
+ //@ts-ignore
213
+ canvas.height = this.height;
214
+
215
+ var ctx = canvas.getContext('2d');
216
+
217
+ //@ts-ignore
218
+ ctx?.drawImage(this, 0, 0);
219
+
220
+ resolve(canvas.toDataURL('image/png'));
221
+ };
222
+
223
+ img.onerror = function () {
224
+ console.log('errr');
225
+ if (throwErrorOnFail) {
226
+ reject('image could not be loaded for some reason');
227
+ }
228
+ else {
229
+ resolve(null);
230
+ }
231
+ };
232
+
233
+ if (url != null)
234
+ img.src = url;
235
+ })
236
+ }
237
+
238
+ //#endregion
239
+
240
+ //#region string and character
241
+
242
+ /**
243
+ *
244
+ * @param val Converts string from camel case to every word being capitalized and spaces between
245
+ * @returns
246
+ */
247
+ export function fromCamelCase(val?: string) {
248
+ if (!val)
249
+ return val
250
+
251
+ return val
252
+ .replace(/([A-Z])/g, ' $1')
253
+ .replace(/^./, (str) => {
254
+ return str.toUpperCase();
255
+ })
256
+ }
257
+
258
+ /**
259
+ * Converts props to camel casing
260
+ * @param value
261
+ * @returns
262
+ */
263
+ export function toCamelCase(value: any) { //for JSON parse
264
+ if (value != null && typeof value === 'object'){
265
+ for (var k in value) {
266
+ if (/^[A-Z]/.test(k) && Object.hasOwnProperty.call(value, k)) {
267
+ value[k.charAt(0).toLowerCase() + k.substring(1)] = value[k];
268
+ delete value[k];
269
+ }
270
+ }
271
+ }
272
+ return value;
273
+ }
274
+
275
+
276
+ export function capitalizeWords(val?: string) {
277
+ if (val == null)
278
+ return val
279
+
280
+ return val.replace(/\w\S*/g, (w) => (w.replace(/^\w/, (c) => c.toUpperCase())));
281
+ }
282
+
283
+ //#endregion
284
+
285
+ //#region weekday
286
+
287
+ export const weekdayPairs = [
288
+ { value: 1, short: 'sun', values: ['sun', 'sunday'] },
289
+ { value: 2, short: 'mon', values: ['mon', 'monday'] },
290
+ { value: 3, short: 'tue', values: ['tue', 'tues', 'tuesday'] },
291
+ { value: 4, short: 'wed', values: ['wed', 'wednesday'] },
292
+ { value: 5, short: 'thu', values: ['thu', 'thur', 'thurs', 'thursday'] },
293
+ { value: 6, short: 'fri', values: ['fri', 'friday'] },
294
+ { value: 7, short: 'sat', values: ['sat', 'saturday'] },
295
+ { value: 0, short: 'always', values: ['always', null, undefined] }
296
+ ]
297
+
298
+ /**returns the sort value of the weekday csv string
299
+ * returns minimum if csv list
300
+ */
301
+ export function weekdayValue(wkDay?: string) {
302
+ if (wkDay == null) {
303
+ return 0
304
+ }
305
+
306
+ const wkDaySplit = wkDay.replaceAll(' ', '').split(',').map(z => z.toLowerCase())
307
+ const valList = weekdayPairs.filter(x => x.values.some(v => wkDaySplit.some(s => v == s))).map(z => z.value)
308
+ if (valList.length == 0)
309
+ return 8
310
+
311
+ return Math.min(...valList)
312
+ }
313
+
314
+ /**returns the sort value of the weekday csv string
315
+ * returns minimum if csv list
316
+ */
317
+ export function weekdayShortName(wkDay?: string) {
318
+ if (wkDay == null) {
319
+ return wkDay
320
+ }
321
+
322
+ return wkDay.toLowerCase().replaceAll(' ', '').split(',').map(day => {
323
+ let pair = weekdayPairs.find(x => x.values.some(v => v == day))
324
+ return pair != null ? pair.short : ''
325
+ }).filter(z => z != null && z.length > 0).toString()
326
+
327
+ // const valList = weekdayPairs.filter(x => x.values.some(v => wkDaySplit.some(s => v == s))).map(z => z.short)
328
+ // if (valList.length == 0)
329
+ // return 8
330
+
331
+ // return Math.min(...valList)
332
+ }
333
+
334
+ /**whether the csv string contains the weekday
335
+ * returns true if either prop is undefined
336
+ */
337
+ export function containsWeekday(weekdays?: string, wkDay?: string) {
338
+ if (weekdays == null || wkDay == null) {
339
+ return true;
340
+ }
341
+
342
+ const wkDaySplit = weekdays.replaceAll(' ', '').split(',').map(z => z.toLowerCase())
343
+ const weekday = wkDay.replaceAll(' ', '').toLowerCase()
344
+ const pair = weekdayPairs.find(pair => pair.values.some(v => v == weekday))
345
+ return pair != null && wkDaySplit.some(s => s == pair.short || pair.values.some(v => v == s))
346
+ }
347
+
348
+ /**adds and sorts the weekday string */
349
+ export function addWeekday(weekdays?: string, day?: string) {
350
+ if (day == null) {
351
+ return weekdays;
352
+ }
353
+
354
+ let wDays = weekdays ?? ''
355
+ wDays = `${wDays},${day}`
356
+
357
+ let res = [...new Set(wDays.replaceAll(' ', '').toLowerCase().split(',').map(z => {
358
+ return weekdayPairs.find(x => x.values.some(v => v == z))
359
+ })
360
+ .filter(z => z != null)
361
+ .sort(firstBy(z => z?.value ?? 0))
362
+ .map(z => z?.short))].toString()
363
+
364
+ return res.length > 0 ? res : undefined
365
+ }
366
+
367
+ export function removeWeekday(weekdays?: string, day?: string) {
368
+ if (day == null || weekdays == null) {
369
+ return weekdays;
370
+ }
371
+
372
+ let wDays = weekdays ?? ''
373
+ let wDay = day.replaceAll(' ', '').toLowerCase()
374
+
375
+ let res = [...new Set(wDays.replaceAll(' ', '').toLowerCase().split(',').map(z => {
376
+ return weekdayPairs.find(x => x.values.some(v => v == z && v != wDay))
377
+ })
378
+ .filter(z => z != null)
379
+ .sort(firstBy(z => z?.value ?? 0))
380
+ .map(z => z?.short))].toString()
381
+
382
+ return res.length > 0 ? res : undefined
383
+ }
384
+
385
+ //#endregion
386
+
387
+ //region arrays
388
+
389
+ export function isArrayOfLength(val: any, l: number) {
390
+ return val != null && Array.isArray(val) && val.length == l;
391
+ }
392
+
393
+ export function isLengthyArray(val: any, greaterThan: number = 0) {
394
+ return val != null && Array.isArray(val) && val.length > greaterThan
395
+ }
396
+
397
+ //#endregion
398
+
399
+ //#region dates
400
+
401
+ export function isMinDate(d?: string) {
402
+ return '0001-01-01T00:00:00Z' == d;
403
+ }
404
+
405
+ export function getMinDate() {
406
+ return new Date('0001-01-01T00:00:00Z').getTime();
407
+ }
408
+
409
+ export function getMinDateString() {
410
+ return '0001-01-01T00:00:00Z'
411
+ }
412
+
413
+ //#end region
414
+
415
+ //#region math
416
+
417
+ /**
418
+ * rounds the given value to a certain number of decimal places
419
+ * @param v
420
+ * @param dPlaces
421
+ * @returns
422
+ */
423
+ export function roundTo(v: MaybeRefOrGetter<number>, dPlaces: number) {
424
+ const val = toValue(v)
425
+
426
+ let m = '1';
427
+ let i = 0;
428
+
429
+ if (i < dPlaces) {
430
+ do {
431
+ m = m + '0';
432
+ i += 1;
433
+ } while (i < dPlaces);
434
+ }
435
+
436
+ let d = Number.parseInt(m);
437
+
438
+ return Math.round(val * d) / d;
439
+ }
440
+
441
+ //#endregion
442
+
443
+ //#region csv
444
+
445
+ export function toggleCSV(value?: string, tag?: string) {
446
+ let rVal = value ?? ''
447
+ if (tag != null) {
448
+ if (csvContains(rVal, tag)) {
449
+ //remove
450
+ rVal = rVal.split(',').filter(x => x != tag).toString();
451
+ }
452
+ else {
453
+ //add
454
+ if (rVal != null) {
455
+ rVal = `${rVal},${tag}`;
456
+ }
457
+ else {
458
+ rVal = tag;
459
+ }
460
+ }
461
+ }
462
+
463
+ return rVal != null && rVal.length > 0 ? rVal : null
464
+ }
465
+
466
+ export function csvContains(value?: string, tag?: string) {
467
+ if (value == null || value.length == 0) {
468
+ return false;
469
+ }
470
+
471
+ if (!tag) {
472
+ return true;
473
+ }
474
+
475
+ var csvList = value.split(',');
476
+ var tagList = tag.split(',');
477
+
478
+ return csvList.some(x => tagList.some(y => y == x));
479
+ }
480
+
481
+ //#end region
482
+
483
+ /**copies object and all descendant properties */
484
+ export function copyDeep(aObject: any) {
485
+ if (!aObject) {
486
+ return aObject;
487
+ }
488
+ let v;
489
+ let bObject: any = Array.isArray(aObject) ? [] : {};
490
+ for (const k in aObject) {
491
+ v = aObject[k];
492
+ bObject[k] = (typeof v === 'object') ? copyDeep(v) : v;
493
+ }
494
+
495
+ return bObject;
496
+ }
497
+
498
+ /**copies object and returns copied object with descendant properties placed in alphabetical order */
499
+ export function copyItemByAlphabet(aObject: any) {
500
+ if (!aObject) {
501
+ return aObject;
502
+ }
503
+ return Object.keys(aObject)
504
+ .sort()
505
+ .reduce(function (acc: any, key) {
506
+ let v = aObject[key];
507
+ acc[key] = (typeof v === 'object' && v !== null) ? copyItemByAlphabet(v) : v;
508
+ return acc;
509
+ }, Array.isArray(aObject) ? [] : {});
510
+ }
511
+
512
+ /**whether string is contained somewhere in this value */
513
+ export function containsSearch(value?: string, str?: string) {
514
+ if (str == null) {
515
+ return true;
516
+ }
517
+ if (value == null) {
518
+ return false;
519
+ }
520
+
521
+ return value.toLowerCase().includes(str.toLowerCase());
522
+ }
523
+
524
+ // export function deepSelect(obj: any, propSelector: Function = (obj: any) => obj) {
525
+ // if (obj == null) {
526
+ // return []
527
+ // }
528
+
529
+ // if (Array.isArray(obj)) {
530
+ // let rr: any[] = []
531
+ // obj.forEach(e => {
532
+ // rr.push(e)
533
+ // const d = deepSelect(e, propSelector)
534
+ // if (isLengthyArray(d))
535
+ // rr.push(...d)
536
+ // })
537
+ // return rr
538
+ // }
539
+ // else {
540
+ // let arr = propSelector(obj)
541
+ // if (isLengthyArray(arr)) {
542
+ // let r = [...arr.reduce((a: any, b: any) => {
543
+ // a.push(b)
544
+ // const v = deepSelect(b, propSelector)
545
+ // if (isLengthyArray(v)) {
546
+ // a.push(...v)
547
+ // }
548
+ // return a
549
+ // }, [])]
550
+
551
+ // if (!Array.isArray(obj)) {
552
+ // r.unshift(obj)
553
+ // }
554
+
555
+ // return r
556
+ // }
557
+
558
+ // return []
559
+ // }
560
+ // }
561
+
562
+ /**must be an object. Returns a flat map of all items in the prop selector */
563
+ export function deepSelect(obj: any, propSelector: Function = (obj: any) => obj) {
564
+ if (obj == null) {
565
+ return []
566
+ }
567
+
568
+ const arr = Array.isArray(obj) ? obj : propSelector(obj)
569
+
570
+ if (!isLengthyArray(arr)) {
571
+ return []
572
+ }
573
+
574
+ return [...arr.reduce((a: any, b: any) => {
575
+ a.push(b)
576
+ const v = deepSelect(b, propSelector)
577
+ if (isLengthyArray(v)) {
578
+ a.push(...v)
579
+ }
580
+ return a
581
+ }, [])]
582
+ }
583
+
584
+ export function DataURIToBlob(dataURI: any) {
585
+ const splitDataURI = dataURI.split(',')
586
+ const byteString = splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1])
587
+ const mimeString = splitDataURI[0].split(':')[1].split(';')[0]
588
+
589
+ const ia = new Uint8Array(byteString.length)
590
+ for (let i = 0; i < byteString.length; i++)
591
+ ia[i] = byteString.charCodeAt(i)
592
+
593
+ return new Blob([ia], { type: mimeString })
594
+ }
595
+
596
+
597
+ export function extractErrorDescription(error: any) {
598
+ var msg = '';
599
+ if (error) {
600
+ if (error.message) {
601
+ msg = error.message;
602
+ }
603
+
604
+ if (error.response && error.response.data) {
605
+ if (error.response.data.errors) {
606
+ for (var i = 0; i < error.response.data.errors.length; i++) {
607
+ msg = msg + ' | ' + error.response.data.errors[i];
608
+ }
609
+ }
610
+ if (error.response.data.message) {
611
+ msg = msg + ' | ' + error.response.data.message;
612
+ }
613
+
614
+ msg = msg + ' | ' + JSON.stringify(error.response.data);
615
+ }
616
+
617
+ return msg;
618
+ }
619
+
620
+ return 'hmmm no error was supplied';
621
+ }
622
+
623
+ export function getRandomColor() {
624
+ const rColor = "#" + Math.floor(Math.random() * 16777215).toString(16);
625
+ if (rColor.length !== 7) {
626
+ return getRandomColor();
627
+ } else {
628
+ return rColor;
629
+ }
630
+ }
631
+
632
+ /**tests for whether string is contains in any of the given props of the given value */
633
+ export function hasSearch(value: any, str?: string, props?: string[]) {
634
+ if (str == null) {
635
+ return true;
636
+ }
637
+
638
+ if (value == null || props == null) {
639
+ return false;
640
+ }
641
+
642
+ for (let i = 0; i < props.length; i++) {
643
+ const propName = props[i];
644
+ var propVal = nestedValue(value, propName);
645
+ if (propVal != undefined) {
646
+ if (typeof(propVal) === 'string') {
647
+ if (containsSearch(propVal, str)) {
648
+ return true;
649
+ }
650
+ }
651
+ }
652
+ }
653
+
654
+ return false;
655
+ }
656
+
657
+ export function toCompareString(str?: string) {
658
+ if (str != null) {
659
+ return str.replaceAll(' ', '').toLowerCase();
660
+ }
661
+ else {
662
+ return null;
663
+ }
664
+ }
665
+
666
+ export function twiddleThumbs(mSec = 2000) {
667
+ return new Promise<void>((resolve) => {
668
+ setTimeout(() => resolve(), mSec);
669
+ })
670
+ }
671
+
672
+ export function nestedValue(obj: any, path?: string) {
673
+ if (obj == null || obj == undefined || !path) {
674
+ return null;
675
+ }
676
+
677
+ var props = path.split('.');
678
+ let propCnt = props.length;
679
+ var r = obj;
680
+
681
+ for (var i = 0; i < propCnt; i++) {
682
+ r = r[props[i]]
683
+ if (r == null) {
684
+ return null;
685
+ }
686
+ }
687
+
688
+ return r;
689
+ }
690
+
691
+ export function validEmail(email?: string) {
692
+ if (!email) return false
693
+ return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(email);
694
+ }
@@ -0,0 +1,20 @@
1
+ // Returns a random Universally Unique Identifier (UUID)
2
+ export function useId(pattern?: string) {
3
+ // Accept any desired pattern. If no pattern is provided
4
+ // default to a RFC4122 UUID pattern.
5
+ const _pattern = pattern ? pattern : 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
6
+
7
+ // Replace each character in the pattern
8
+ // leaving any non x|y character alone.
9
+ return _pattern.replace(/[xy]/g, replacePattern);
10
+ }
11
+
12
+ function replacePattern(c: string) {
13
+ // Random hexadecimal number
14
+ const r = (Math.random() * 16) | 0;
15
+
16
+ // If 'x' return hexadecimal number,
17
+ // if 'y' return [8-11] randomly
18
+ const v = c == 'x' ? r : (r & 0x3) | 0x8;
19
+ return v.toString(16);
20
+ }