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.
- package/.vscode/extensions.json +3 -0
- package/README.md +45 -0
- package/index.html +13 -0
- package/package.json +39 -0
- package/public/vite.svg +1 -0
- package/src/assets/vue.svg +1 -0
- package/src/components/BT-Btn.vue +40 -0
- package/src/components/BT-Col.vue +36 -0
- package/src/components/BT-Span.vue +21 -0
- package/src/components/Dialog-Confirm.vue +47 -0
- package/src/components/Dialog-Select-Date.vue +89 -0
- package/src/components/Dialog-Select.vue +140 -0
- package/src/components/Dialog-Text.vue +59 -0
- package/src/composables/actions-tracker.ts +99 -0
- package/src/composables/actions.ts +354 -0
- package/src/composables/api.ts +471 -0
- package/src/composables/auth.ts +382 -0
- package/src/composables/cosmetics.ts +178 -0
- package/src/composables/csv.ts +198 -0
- package/src/composables/dates.ts +79 -0
- package/src/composables/demo.ts +25 -0
- package/src/composables/dialogs.ts +115 -0
- package/src/composables/document-meta.ts +40 -0
- package/src/composables/draggable.ts +189 -0
- package/src/composables/filters.ts +256 -0
- package/src/composables/forage.ts +49 -0
- package/src/composables/helpers.ts +694 -0
- package/src/composables/id.ts +20 -0
- package/src/composables/list.ts +601 -0
- package/src/composables/navigation.ts +214 -0
- package/src/composables/presets.ts +20 -0
- package/src/composables/pwa.ts +89 -0
- package/src/composables/resizable.ts +382 -0
- package/src/composables/rules.ts +40 -0
- package/src/composables/stores.ts +797 -0
- package/src/composables/track.ts +55 -0
- package/src/composables/urls.ts +11 -0
- package/src/core.ts +92 -0
- package/src/index.ts +16 -0
- package/src/types.ts +13 -0
- package/src/useApi.ts +68 -0
- package/src/vite-env.d.ts +1 -0
- package/test/api.test.ts +84 -0
- package/test/forage.test.ts +31 -0
- package/test/helpers.test.ts +231 -0
- package/test/navigation.test.ts +99 -0
- package/test/stores-last-update.test.ts +138 -0
- package/test/stores-session.test.ts +118 -0
- package/test/track.test.ts +29 -0
- package/test/utils.ts +15 -0
- package/tsconfig.json +33 -0
- package/tsconfig.node.json +11 -0
- 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
|
+
}
|