@kwiz/common 1.0.128 → 1.0.132

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 (105) hide show
  1. package/.github/workflows/npm-publish.yml +24 -24
  2. package/LICENSE +21 -21
  3. package/lib/cjs/config.js +2 -0
  4. package/lib/cjs/config.js.map +1 -1
  5. package/lib/cjs/helpers/browser.js +8 -1
  6. package/lib/cjs/helpers/browser.js.map +1 -1
  7. package/lib/cjs/types/libs/msal.types.js +26 -26
  8. package/lib/cjs/utils/sharepoint.rest/user.js +11 -11
  9. package/lib/esm/config.js +2 -0
  10. package/lib/esm/config.js.map +1 -1
  11. package/lib/esm/helpers/browser.js +6 -0
  12. package/lib/esm/helpers/browser.js.map +1 -1
  13. package/lib/esm/types/libs/msal.types.js +26 -26
  14. package/lib/esm/utils/sharepoint.rest/user.js +11 -11
  15. package/lib/types/config.d.ts +2 -0
  16. package/lib/types/helpers/browser.d.ts +2 -0
  17. package/package.json +81 -81
  18. package/readme.md +17 -17
  19. package/src/_dependencies.ts +12 -12
  20. package/src/config.ts +19 -17
  21. package/src/helpers/Guid.ts +181 -181
  22. package/src/helpers/base64.ts +173 -173
  23. package/src/helpers/browser.test.js +13 -13
  24. package/src/helpers/browser.ts +1504 -1498
  25. package/src/helpers/browserinfo.ts +292 -292
  26. package/src/helpers/collections.base.test.js +25 -25
  27. package/src/helpers/collections.base.ts +437 -437
  28. package/src/helpers/collections.ts +107 -107
  29. package/src/helpers/color.ts +54 -54
  30. package/src/helpers/cookies.ts +59 -59
  31. package/src/helpers/date.test.js +119 -119
  32. package/src/helpers/date.ts +188 -188
  33. package/src/helpers/debug.ts +186 -186
  34. package/src/helpers/diagrams.ts +43 -43
  35. package/src/helpers/emails.ts +6 -6
  36. package/src/helpers/eval.ts +5 -5
  37. package/src/helpers/file.test.js +50 -50
  38. package/src/helpers/file.ts +63 -63
  39. package/src/helpers/flatted.ts +149 -149
  40. package/src/helpers/functions.ts +16 -16
  41. package/src/helpers/graph/calendar.types.ts +10 -10
  42. package/src/helpers/http.ts +69 -69
  43. package/src/helpers/images.ts +22 -22
  44. package/src/helpers/json.ts +44 -44
  45. package/src/helpers/md5.ts +189 -189
  46. package/src/helpers/objects.test.js +33 -33
  47. package/src/helpers/objects.ts +274 -274
  48. package/src/helpers/promises.test.js +37 -37
  49. package/src/helpers/promises.ts +165 -165
  50. package/src/helpers/random.ts +27 -27
  51. package/src/helpers/scheduler/scheduler.test.js +103 -103
  52. package/src/helpers/scheduler/scheduler.ts +131 -131
  53. package/src/helpers/sharepoint.ts +796 -796
  54. package/src/helpers/strings.test.js +122 -122
  55. package/src/helpers/strings.ts +337 -337
  56. package/src/helpers/typecheckers.test.js +34 -34
  57. package/src/helpers/typecheckers.ts +266 -266
  58. package/src/helpers/url.test.js +43 -43
  59. package/src/helpers/url.ts +207 -207
  60. package/src/helpers/urlhelper.ts +111 -111
  61. package/src/index.ts +6 -6
  62. package/src/types/auth.ts +62 -62
  63. package/src/types/common.types.ts +15 -15
  64. package/src/types/flatted.types.ts +59 -59
  65. package/src/types/globals.types.ts +6 -6
  66. package/src/types/graph/calendar.types.ts +80 -80
  67. package/src/types/knownscript.types.ts +18 -18
  68. package/src/types/libs/datajs.types.ts +28 -28
  69. package/src/types/libs/ics.types.ts +30 -30
  70. package/src/types/libs/msal.types.ts +57 -57
  71. package/src/types/locales.ts +125 -125
  72. package/src/types/localstoragecache.types.ts +8 -8
  73. package/src/types/location.types.ts +27 -27
  74. package/src/types/moment.ts +11 -11
  75. package/src/types/regex.types.ts +16 -16
  76. package/src/types/rest.types.ts +95 -95
  77. package/src/types/sharepoint.types.ts +1466 -1466
  78. package/src/types/sharepoint.utils.types.ts +306 -306
  79. package/src/utils/auth/common.ts +118 -118
  80. package/src/utils/auth/discovery.test.js +12 -12
  81. package/src/utils/auth/discovery.ts +132 -132
  82. package/src/utils/base64.ts +27 -27
  83. package/src/utils/consolelogger.ts +333 -333
  84. package/src/utils/date.ts +172 -172
  85. package/src/utils/emails.ts +24 -24
  86. package/src/utils/knownscript.ts +286 -286
  87. package/src/utils/localstoragecache.ts +446 -446
  88. package/src/utils/rest.ts +501 -501
  89. package/src/utils/script.ts +170 -170
  90. package/src/utils/sharepoint.rest/common.ts +159 -159
  91. package/src/utils/sharepoint.rest/date.ts +62 -62
  92. package/src/utils/sharepoint.rest/file.folder.ts +685 -685
  93. package/src/utils/sharepoint.rest/item.ts +547 -547
  94. package/src/utils/sharepoint.rest/list.ts +1572 -1572
  95. package/src/utils/sharepoint.rest/listutils/GetListItemsByCaml.ts +774 -774
  96. package/src/utils/sharepoint.rest/listutils/GetListItemsById.ts +275 -275
  97. package/src/utils/sharepoint.rest/listutils/common.ts +206 -206
  98. package/src/utils/sharepoint.rest/location.ts +141 -141
  99. package/src/utils/sharepoint.rest/navigation-links.ts +86 -86
  100. package/src/utils/sharepoint.rest/user-search.ts +252 -252
  101. package/src/utils/sharepoint.rest/user.ts +558 -558
  102. package/src/utils/sharepoint.rest/web.ts +1384 -1384
  103. package/src/utils/sod.ts +194 -194
  104. package/.madgerc +0 -3
  105. package/fix-folder-imports.js +0 -27
@@ -1,438 +1,438 @@
1
-
2
- /** this file will only use basic type checker and types, do not add functions that require
3
- * a reference to tother helpers
4
- */
5
- import { IDictionary } from "../types/common.types";
6
- import { getFromFullName, isBoolean, isDate, isFunction, isNotEmptyArray, isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined, isNumber, isString, isTypeofFullNameFunction } from "./typecheckers";
7
-
8
- /** this will support HtmlCollectionOf, Arrays, and any other types that have a length and indexer Issue 568 */
9
- export interface IndexedCollection<ElementType> {
10
- length: number;
11
- [index: number]: ElementType;
12
- }
13
-
14
- /** Finds an object in array based on a filter and moves it to the start of the array */
15
- export function moveToStart<T>(arr: T[], filter?: (item: T) => boolean) {
16
- let index = firstIndexOf<T>(arr, filter);
17
-
18
- if (index > 0) {
19
- let obj = arr[index];
20
- arr.splice(index, 1);
21
- arr.unshift(obj);
22
- }
23
- }
24
-
25
- /** Finds an object in array based on a filter and moves it to the end of the array */
26
- export function moveToEnd<T>(arr: T[], filter?: (item: T) => boolean) {
27
- let index = firstIndexOf<T>(arr, filter);
28
-
29
- if (index !== -1 && index !== arr.length - 1) {
30
- let obj = arr[index];
31
- arr.splice(index, 1);
32
- arr.push(obj);
33
- }
34
- }
35
-
36
- /** Get the first index of an object of an array, or -1 if the array is empty / null */
37
- // export function firstIndexOf<T extends Element>(arr: HTMLCollectionOf<T>, filter?: (item: T, index?: number) => boolean, startFrom?: number): number;
38
- // export function firstIndexOf<T>(arr: T[], filter?: (item: T, index?: number) => boolean, startFrom?: number): number;
39
- export function firstIndexOf<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => boolean, startFrom?: number): number {
40
- if (!isNullOrUndefined(arr) && arr.length > 0) {
41
- if (isFunction(filter)) {
42
- //use for loop so we can stop when it is found
43
- for (let i = startFrom > 0 ? startFrom : 0; i < arr.length; i++)
44
- if (filter(arr[i], i) === true)
45
- return i;
46
- }
47
- else return 0;
48
- }
49
-
50
- return -1;
51
- }
52
- /** Get the first object of an array, or null if the array is empty / null
53
- * If you pass a filter, it will find the first element that matches the filter and return it, stopping the loop when it is found
54
- * */
55
- export function firstOrNull<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => boolean): T {
56
- let index = firstIndexOf(arr, filter);
57
- return index < 0 ? null : arr[index];
58
- }
59
-
60
-
61
- /** Get the last index of an object of an array, or -1 if the array is empty / null */
62
- export function lastIndexOf<T>(arr: IndexedCollection<T>, filter?: (item: T) => boolean): number {
63
- if (!isNullOrUndefined(arr) && arr.length > 0) {
64
- if (isFunction(filter)) {
65
- //use for loop so we can stop when it is found
66
- for (let i = arr.length - 1; i >= 0; i--)
67
- if (filter(arr[i]) === true)
68
- return i;
69
- }
70
- else return arr.length - 1;
71
- }
72
-
73
- return -1;
74
- }
75
-
76
- /** get the last element or null */
77
- export function lastOrNull<T>(arr: IndexedCollection<T>, filter?: (item: T) => boolean): T {
78
- let index = lastIndexOf(arr as T[], filter);
79
- return index < 0 ? null : arr[index];
80
- }
81
-
82
- /** Get the first index of an object of an array, or -1 if the array is empty / null */
83
- export async function firstIndexOfAsync<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => Promise<boolean>, startFrom?: number): Promise<number> {
84
- if (!isNullOrUndefined(arr) && arr.length > 0) {
85
- if (isFunction(filter)) {
86
- //use for loop so we can stop when it is found
87
- for (let i = startFrom > 0 ? startFrom : 0; i < arr.length; i++)
88
- if ((await filter(arr[i], i)) === true)
89
- return i;
90
- }
91
- else return 0;
92
- }
93
-
94
- return -1;
95
- }
96
- /** Get the first object of an array, or null if the array is empty / null
97
- * If you pass a filter, it will find the first element that matches the filter and return it, stopping the loop when it is found
98
- * */
99
- export async function firstOrNullAsync<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => Promise<boolean>): Promise<T> {
100
- let index = await firstIndexOfAsync(arr, filter);
101
- return index < 0 ? null : arr[index];
102
- }
103
-
104
-
105
- /** Get the last index of an object of an array, or -1 if the array is empty / null */
106
- export async function lastIndexOfAsync<T>(arr: IndexedCollection<T>, filter?: (item: T) => Promise<boolean>): Promise<number> {
107
- if (!isNullOrUndefined(arr) && arr.length > 0) {
108
- if (isFunction(filter)) {
109
- //use for loop so we can stop when it is found
110
- for (let i = arr.length - 1; i >= 0; i--)
111
- if ((await filter(arr[i])) === true)
112
- return i;
113
- }
114
- else return arr.length - 1;
115
- }
116
-
117
- return -1;
118
- }
119
-
120
- /** get the last element or null */
121
- export async function lastOrNullAsync<T>(arr: IndexedCollection<T>, filter?: (item: T) => Promise<boolean>): Promise<T> {
122
- let index = await lastIndexOfAsync(arr, filter);
123
- return index < 0 ? null : arr[index];
124
- }
125
-
126
- /** Sorts an array of complex objects, use defaultPrimitiveGetValue for default functionality */
127
- export function sortArray<T>(arr: T[], getValue: (item: T) => number | string) {
128
- if (!isNullOrEmptyArray(arr)) {
129
- if (isTypeofFullNameFunction("Intl.Collator")) {
130
- //todo: should probably use the SharePoint locale isntead of 'undefined'
131
- let collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
132
- arr.sort((a, b) => {
133
- let va = getValue(a);
134
- let vb = getValue(b);
135
- return collator.compare(va as string, vb as string);
136
- });
137
- } else {
138
- arr.sort((a, b) => {
139
- let va = getValue(a);
140
- if (isString(va)) va = va.toLowerCase();
141
- let vb = getValue(b);
142
- if (isString(vb)) vb = vb.toLowerCase();
143
- return va === vb ? 0 : va > vb ? 1 : -1;
144
- });
145
- }
146
- }
147
- return arr;
148
- }
149
-
150
- /** removes null, undefined or "" elements from the array */
151
- export function filterEmptyEntries<T>(arr: T[]) {
152
- return arr.filter(val => !isNullOrEmptyString(val));
153
- }
154
-
155
-
156
- export function sortNumberArrayAsc(a: number, b: number): number {
157
- return a - b;
158
- }
159
- export function sortNumberArray(a: number, b: number): number {
160
- return b - a;
161
- }
162
- /** call a foreach on an object or an array, with an option to break when returning false */
163
- export function forEach<T>(obj: IDictionary<T> | Array<T> | { [key: number]: T; length: number; }, func: (propertyName: string, propertyValue: T, _args?: any) => void | boolean, args?: any) {
164
- if (obj && func && isFunction(func)) {
165
- if (Array.isArray(obj) || obj.constructor && getFromFullName("constructor.name", obj) === "Array") {
166
- for (let i = 0; i < (obj as Array<T>).length; i++) {
167
- let property = i;
168
- let value = obj[property];
169
- let result = func(property.toString(10), value, args);
170
- if (result === false) {
171
- break;
172
- }
173
- }
174
- }
175
- else {
176
- let keys = Object.keys(obj);
177
- for (let i = 0; i < keys.length; i++) {
178
- let property = keys[i];
179
- let value = obj[property];
180
- let result = func(property, value, args);
181
- if (result === false) {
182
- break;
183
- }
184
- }
185
- }
186
- }
187
- }
188
-
189
- export async function forEachAsync<ElmType, ResultType>(arr: Array<ElmType> | IDictionary<ElmType>, handler: (elm: ElmType, index: number) => Promise<ResultType>, options?: { parallel?: boolean; }) {
190
- if (!isNullOrUndefined(arr) && Object.keys(arr).length > 0) {
191
- let keys = Object.keys(arr);
192
- if (options && options.parallel) {
193
- let promises: Promise<ResultType>[] = [];
194
- keys.forEach((key, i) => {
195
- promises.push(handler(arr[key], i));
196
- });
197
- return Promise.all(promises);
198
- }
199
- else {
200
- let results: ResultType[] = [];
201
- for (let i = 0; i < keys.length; i++) {
202
- results.push(await handler(arr[keys[i]], i));
203
- }
204
- return results;
205
- }
206
- }
207
- else return []
208
- }
209
-
210
- export function sizeOf(obj: any) {
211
- if (Array.isArray(obj))
212
- return obj.length;
213
- return Object.keys(obj).length;
214
- }
215
-
216
- export function chunkArray<T>(array: T[], chunkSize: number) {
217
- let chunkedArray: T[][] = [];
218
- for (var i = 0; i < array.length; i += chunkSize) {
219
- chunkedArray.push(array.slice(i, i + chunkSize));
220
- }
221
- return chunkedArray;
222
- }
223
-
224
- /** Takes an array and transforms it into a hash. this will assign 1 item per key, assumig getKey will be unique per item. */
225
- export function toHash<T, Y = T>(arr: T[], getKey: (element: T) => string | number, filter?: (element: T) => boolean, transformValue?: (element: T) => Y): IDictionary<Y> {
226
- let hash: { [key: string | number]: Y; } = {};
227
- if (!isFunction(transformValue)) transformValue = v => v as any as Y;
228
- if (isNotEmptyArray(arr))
229
- arr.forEach(i => {
230
- if (!isFunction(filter) || filter(i))
231
- hash[getKey(i)] = transformValue(i);
232
- });
233
- return hash;
234
- }
235
-
236
- /** Returns an array from the values of the dictionary. */
237
- export function toArray<Source, Result = Source>(hash: { [key: string]: Source; }, filter?: (element: Source) => boolean, transform?: (key: string, element: Source) => Result): Result[] {
238
- let arr: Result[] = [];
239
- if (!isFunction(transform)) transform = (key, element) => element as any as Result;
240
-
241
- if (!isNullOrUndefined(hash))
242
- Object.keys(hash).forEach(key => {
243
- if (!isFunction(filter) || filter(hash[key]))
244
- arr.push(transform(key, hash[key]));
245
- });
246
- return arr;
247
- }
248
-
249
- /** returns a new dictionary, converting each entry in source using the transform function */
250
- export function convertDictionary<S, R>(source: IDictionary<S>, transform: (sourceItem: S) => R): IDictionary<R> {
251
- let result: IDictionary<R> = {};
252
- forEach(source, (key, value) => { result[key] = transform(value); });
253
- return result;
254
- }
255
-
256
- export function flattenArray<T>(array: (T | T[])[]) {
257
- return array.reduce((acc, val) => (acc as T[]).concat(val), []) as T[];
258
- }
259
-
260
- /** careful, does not work for date/complex objects. Use GetUniqueArrayInfo if you suspect you might have Date/complex objects. */
261
- export function makeUniqueArray<T>(arr: T[]) {
262
- return arr.filter((v, i, a) => a.indexOf(v) === i);
263
- }
264
-
265
- /** return an array of unique values, and the first index they were found, use defaultPrimitiveGetValue for default functionality */
266
- export function GetUniqueArrayInfo<T, V>(arr: T[], getValue: (item: T) => V) {
267
- var uniqueValues: { item: T; value: V; firstIndex: number; }[] = [];
268
- var uniqueArray: T[] = [];
269
- var foundValues: V[] = [];
270
- var hasDuplicates = false;
271
- var duplicateIndexes: number[] = [];
272
-
273
- if (isNotEmptyArray(arr)) {
274
- arr.forEach((item, index) => {
275
- let value = getValue(item);
276
- if (foundValues.includes(value)) {
277
- hasDuplicates = true;
278
- duplicateIndexes.push(index);
279
- }
280
- else {
281
- foundValues.push(value);
282
- uniqueValues.push({ item: item, value: value, firstIndex: index });
283
- uniqueArray.push(item);
284
- }
285
- });
286
- }
287
-
288
- return {
289
- /** true if duplicate values found */
290
- hasDuplicates: hasDuplicates,
291
- /** all duplicate item indexes */
292
- duplicateIndexes: duplicateIndexes,
293
- /** unique values and their info */
294
- uniqueValues: uniqueValues,
295
- /** the unique version of this array */
296
- uniqueArray: uniqueArray
297
- };
298
- }
299
-
300
- export interface IMultiLevelGroupItem<ItemType> {
301
- parentGroup: IMultiLevelGroup<ItemType>;
302
- }
303
- export interface IMultiLevelGroup<ItemType> {
304
- groupItems: (ItemType & IMultiLevelGroupItem<ItemType>)[];
305
- subGroups: IDictionary<IMultiLevelGroup<ItemType>>;
306
- depth: number;
307
- parentGroup: IMultiLevelGroup<ItemType>;
308
- /** would contain the path to the group, such as: 0_1_3 for first group, second sub group, 4th sub-sub group */
309
- key: string;
310
- index: number;
311
- title: string;
312
- /** Optional, add a prefix to the group. For example: "Priority > " to fullTitle will be "Priority > High" */
313
- groupPrefix?: string;
314
- /** title with decorations, such as prefix */
315
- fullTitle: string;
316
- }
317
- export type MultiLevelGroupItem<ItemType> = ItemType & IMultiLevelGroupItem<ItemType>;
318
- export type MultiLevelGroupOrItem<ItemType> = MultiLevelGroupItem<ItemType> | IMultiLevelGroup<ItemType>;
319
- /** returns true if the element is a group of items */
320
- export function IsMultiLevelGroup<ItemType>(groupOrItem: MultiLevelGroupOrItem<ItemType>): groupOrItem is IMultiLevelGroup<ItemType> {
321
- let asGroup = groupOrItem as IMultiLevelGroup<ItemType>;
322
- return !isNullOrUndefined(asGroup.subGroups) && Array.isArray(asGroup.groupItems) && isNumber(asGroup.index) && isNumber(asGroup.depth);
323
- }
324
-
325
- /** returns a flat array of groups>items ordered by groups */
326
- export function FlattenGroupItems<ItemType>(groups: IDictionary<IMultiLevelGroup<ItemType>>) {
327
- let flatItems: MultiLevelGroupOrItem<ItemType>[] = [];
328
- Object.keys(groups).forEach(groupName => {
329
- let group = groups[groupName];
330
- if (!isNullOrEmptyString(groupName))
331
- flatItems.push(group);
332
- let subGroups = Object.keys(group.subGroups);
333
- if (isNotEmptyArray(subGroups)) {
334
- flatItems.push(...FlattenGroupItems(group.subGroups));
335
- }
336
- else flatItems.push(...group.groupItems);
337
- });
338
-
339
- return flatItems;
340
- }
341
-
342
- /** split a collection by page size and return the info */
343
- export function GetPagedCollectionInfo<T>(collection: Array<T>, pageSize: number, currentPage?: number) {
344
- let pagedItems: (T[])[] = [];
345
-
346
- if (pageSize < 1) {
347
- pagedItems = [collection.slice()];
348
- }
349
- else {
350
- let copy = collection.slice();
351
- while (isNotEmptyArray(copy)) {
352
- pagedItems.push(copy.splice(0, pageSize));
353
- }
354
- }
355
-
356
- currentPage = isNumber(currentPage) && currentPage >= 0 && currentPage < pagedItems.length ? currentPage : 0;
357
- return {
358
- /** nubmer of pages */
359
- pages: pagedItems.length,
360
- /** page items, per page (Array of pages, each has an array of the page items) */
361
- pagedItems: pagedItems,
362
- /** the current page */
363
- currentPage: currentPage,
364
- /** the current page items */
365
- currentPageItems: pagedItems[currentPage] || [],
366
- /** has more than 1 page */
367
- hasPages: pagedItems.length > 1,
368
- allowPrev: currentPage > 0,
369
- allowNext: currentPage < pagedItems.length - 1
370
- };
371
- }
372
-
373
- /** use with sortArray or get unique array to handle premitive types or dates, with a JSON.stringify to all other values */
374
- export function defaultPrimitiveGetValue<T>(item: T) {
375
- return isNullOrUndefined(item)
376
- ? ""
377
- : isDate(item) ? item.getTime()
378
- : isBoolean(item)
379
- ? item === true ? 1 : 0
380
- : isNumber(item) || isString(item)
381
- ? item
382
- : JSON.stringify(item);
383
- }
384
-
385
- export function RemoveItemFromArr<T>(arr: T[], item: T) {
386
- let idx = arr.indexOf(item);
387
- if (idx >= 0)
388
- arr.splice(idx, 1);
389
- }
390
- export function PushNoDuplicate<T>(arr: T[], item: T) {
391
- if (!arr.includes(item)) arr.push(item);
392
- }
393
- /** fills an array with a value. Array.fill isn't available on SPFx. */
394
- export function ArrayFill<T>(arr: T[], value: T, onlyEmpty?: boolean) {
395
- for (let i = 0; i < arr.length; i++) {
396
- if (onlyEmpty !== true || isNullOrUndefined(arr[i]))
397
- arr[i] = value;
398
- }
399
- return arr;
400
- }
401
-
402
- /** give a name and a collection, and it will return a unique name availalbe, suffixing a _# to the name
403
- * example: file
404
- * return file, file_2, file_9 etc... whichever is availalbe first.
405
- */
406
- export function FindNextAvailableName(name: string, usedNames: string[], options?: {
407
- //check for specific letter case
408
- caseSensitive?: boolean;
409
- //append suffix when adding _# for example: file_1.docx, file_2.docx etc
410
- suffix?: string;
411
- }) {
412
- let nameForTest = name;
413
- if (options && options.caseSensitive !== true) {
414
- usedNames = usedNames.map(n => n.toLowerCase());
415
- nameForTest = name.toLowerCase();
416
- }
417
-
418
- let nameSuffix = options && options.suffix || "";
419
-
420
- let suffixIdx = 0;
421
- let suffixStr = "";
422
- while (usedNames.indexOf(`${nameForTest}${suffixStr}${nameSuffix}`) >= 0) {
423
- suffixIdx++;
424
- suffixStr = "_" + suffixIdx;
425
- }
426
-
427
- return `${name}${suffixStr}${nameSuffix}`;
428
- }
429
-
430
- //** returns an array of numbers from 0,1,2... */
431
- export function numbersArray<T extends number>(length: number, startFrom: number = 0) {
432
- //dvp build will fail without any type
433
- if (isNullOrUndefined(length) || length < 0) length = 0;
434
- let arr: number[] = Array.from((Array(length) as any).keys());
435
- return startFrom > 0
436
- ? arr.map(i => i + startFrom) as T[]
437
- : arr as T[];
1
+
2
+ /** this file will only use basic type checker and types, do not add functions that require
3
+ * a reference to tother helpers
4
+ */
5
+ import { IDictionary } from "../types/common.types";
6
+ import { getFromFullName, isBoolean, isDate, isFunction, isNotEmptyArray, isNullOrEmptyArray, isNullOrEmptyString, isNullOrUndefined, isNumber, isString, isTypeofFullNameFunction } from "./typecheckers";
7
+
8
+ /** this will support HtmlCollectionOf, Arrays, and any other types that have a length and indexer Issue 568 */
9
+ export interface IndexedCollection<ElementType> {
10
+ length: number;
11
+ [index: number]: ElementType;
12
+ }
13
+
14
+ /** Finds an object in array based on a filter and moves it to the start of the array */
15
+ export function moveToStart<T>(arr: T[], filter?: (item: T) => boolean) {
16
+ let index = firstIndexOf<T>(arr, filter);
17
+
18
+ if (index > 0) {
19
+ let obj = arr[index];
20
+ arr.splice(index, 1);
21
+ arr.unshift(obj);
22
+ }
23
+ }
24
+
25
+ /** Finds an object in array based on a filter and moves it to the end of the array */
26
+ export function moveToEnd<T>(arr: T[], filter?: (item: T) => boolean) {
27
+ let index = firstIndexOf<T>(arr, filter);
28
+
29
+ if (index !== -1 && index !== arr.length - 1) {
30
+ let obj = arr[index];
31
+ arr.splice(index, 1);
32
+ arr.push(obj);
33
+ }
34
+ }
35
+
36
+ /** Get the first index of an object of an array, or -1 if the array is empty / null */
37
+ // export function firstIndexOf<T extends Element>(arr: HTMLCollectionOf<T>, filter?: (item: T, index?: number) => boolean, startFrom?: number): number;
38
+ // export function firstIndexOf<T>(arr: T[], filter?: (item: T, index?: number) => boolean, startFrom?: number): number;
39
+ export function firstIndexOf<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => boolean, startFrom?: number): number {
40
+ if (!isNullOrUndefined(arr) && arr.length > 0) {
41
+ if (isFunction(filter)) {
42
+ //use for loop so we can stop when it is found
43
+ for (let i = startFrom > 0 ? startFrom : 0; i < arr.length; i++)
44
+ if (filter(arr[i], i) === true)
45
+ return i;
46
+ }
47
+ else return 0;
48
+ }
49
+
50
+ return -1;
51
+ }
52
+ /** Get the first object of an array, or null if the array is empty / null
53
+ * If you pass a filter, it will find the first element that matches the filter and return it, stopping the loop when it is found
54
+ * */
55
+ export function firstOrNull<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => boolean): T {
56
+ let index = firstIndexOf(arr, filter);
57
+ return index < 0 ? null : arr[index];
58
+ }
59
+
60
+
61
+ /** Get the last index of an object of an array, or -1 if the array is empty / null */
62
+ export function lastIndexOf<T>(arr: IndexedCollection<T>, filter?: (item: T) => boolean): number {
63
+ if (!isNullOrUndefined(arr) && arr.length > 0) {
64
+ if (isFunction(filter)) {
65
+ //use for loop so we can stop when it is found
66
+ for (let i = arr.length - 1; i >= 0; i--)
67
+ if (filter(arr[i]) === true)
68
+ return i;
69
+ }
70
+ else return arr.length - 1;
71
+ }
72
+
73
+ return -1;
74
+ }
75
+
76
+ /** get the last element or null */
77
+ export function lastOrNull<T>(arr: IndexedCollection<T>, filter?: (item: T) => boolean): T {
78
+ let index = lastIndexOf(arr as T[], filter);
79
+ return index < 0 ? null : arr[index];
80
+ }
81
+
82
+ /** Get the first index of an object of an array, or -1 if the array is empty / null */
83
+ export async function firstIndexOfAsync<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => Promise<boolean>, startFrom?: number): Promise<number> {
84
+ if (!isNullOrUndefined(arr) && arr.length > 0) {
85
+ if (isFunction(filter)) {
86
+ //use for loop so we can stop when it is found
87
+ for (let i = startFrom > 0 ? startFrom : 0; i < arr.length; i++)
88
+ if ((await filter(arr[i], i)) === true)
89
+ return i;
90
+ }
91
+ else return 0;
92
+ }
93
+
94
+ return -1;
95
+ }
96
+ /** Get the first object of an array, or null if the array is empty / null
97
+ * If you pass a filter, it will find the first element that matches the filter and return it, stopping the loop when it is found
98
+ * */
99
+ export async function firstOrNullAsync<T>(arr: IndexedCollection<T>, filter?: (item: T, index?: number) => Promise<boolean>): Promise<T> {
100
+ let index = await firstIndexOfAsync(arr, filter);
101
+ return index < 0 ? null : arr[index];
102
+ }
103
+
104
+
105
+ /** Get the last index of an object of an array, or -1 if the array is empty / null */
106
+ export async function lastIndexOfAsync<T>(arr: IndexedCollection<T>, filter?: (item: T) => Promise<boolean>): Promise<number> {
107
+ if (!isNullOrUndefined(arr) && arr.length > 0) {
108
+ if (isFunction(filter)) {
109
+ //use for loop so we can stop when it is found
110
+ for (let i = arr.length - 1; i >= 0; i--)
111
+ if ((await filter(arr[i])) === true)
112
+ return i;
113
+ }
114
+ else return arr.length - 1;
115
+ }
116
+
117
+ return -1;
118
+ }
119
+
120
+ /** get the last element or null */
121
+ export async function lastOrNullAsync<T>(arr: IndexedCollection<T>, filter?: (item: T) => Promise<boolean>): Promise<T> {
122
+ let index = await lastIndexOfAsync(arr, filter);
123
+ return index < 0 ? null : arr[index];
124
+ }
125
+
126
+ /** Sorts an array of complex objects, use defaultPrimitiveGetValue for default functionality */
127
+ export function sortArray<T>(arr: T[], getValue: (item: T) => number | string) {
128
+ if (!isNullOrEmptyArray(arr)) {
129
+ if (isTypeofFullNameFunction("Intl.Collator")) {
130
+ //todo: should probably use the SharePoint locale isntead of 'undefined'
131
+ let collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
132
+ arr.sort((a, b) => {
133
+ let va = getValue(a);
134
+ let vb = getValue(b);
135
+ return collator.compare(va as string, vb as string);
136
+ });
137
+ } else {
138
+ arr.sort((a, b) => {
139
+ let va = getValue(a);
140
+ if (isString(va)) va = va.toLowerCase();
141
+ let vb = getValue(b);
142
+ if (isString(vb)) vb = vb.toLowerCase();
143
+ return va === vb ? 0 : va > vb ? 1 : -1;
144
+ });
145
+ }
146
+ }
147
+ return arr;
148
+ }
149
+
150
+ /** removes null, undefined or "" elements from the array */
151
+ export function filterEmptyEntries<T>(arr: T[]) {
152
+ return arr.filter(val => !isNullOrEmptyString(val));
153
+ }
154
+
155
+
156
+ export function sortNumberArrayAsc(a: number, b: number): number {
157
+ return a - b;
158
+ }
159
+ export function sortNumberArray(a: number, b: number): number {
160
+ return b - a;
161
+ }
162
+ /** call a foreach on an object or an array, with an option to break when returning false */
163
+ export function forEach<T>(obj: IDictionary<T> | Array<T> | { [key: number]: T; length: number; }, func: (propertyName: string, propertyValue: T, _args?: any) => void | boolean, args?: any) {
164
+ if (obj && func && isFunction(func)) {
165
+ if (Array.isArray(obj) || obj.constructor && getFromFullName("constructor.name", obj) === "Array") {
166
+ for (let i = 0; i < (obj as Array<T>).length; i++) {
167
+ let property = i;
168
+ let value = obj[property];
169
+ let result = func(property.toString(10), value, args);
170
+ if (result === false) {
171
+ break;
172
+ }
173
+ }
174
+ }
175
+ else {
176
+ let keys = Object.keys(obj);
177
+ for (let i = 0; i < keys.length; i++) {
178
+ let property = keys[i];
179
+ let value = obj[property];
180
+ let result = func(property, value, args);
181
+ if (result === false) {
182
+ break;
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ export async function forEachAsync<ElmType, ResultType>(arr: Array<ElmType> | IDictionary<ElmType>, handler: (elm: ElmType, index: number) => Promise<ResultType>, options?: { parallel?: boolean; }) {
190
+ if (!isNullOrUndefined(arr) && Object.keys(arr).length > 0) {
191
+ let keys = Object.keys(arr);
192
+ if (options && options.parallel) {
193
+ let promises: Promise<ResultType>[] = [];
194
+ keys.forEach((key, i) => {
195
+ promises.push(handler(arr[key], i));
196
+ });
197
+ return Promise.all(promises);
198
+ }
199
+ else {
200
+ let results: ResultType[] = [];
201
+ for (let i = 0; i < keys.length; i++) {
202
+ results.push(await handler(arr[keys[i]], i));
203
+ }
204
+ return results;
205
+ }
206
+ }
207
+ else return []
208
+ }
209
+
210
+ export function sizeOf(obj: any) {
211
+ if (Array.isArray(obj))
212
+ return obj.length;
213
+ return Object.keys(obj).length;
214
+ }
215
+
216
+ export function chunkArray<T>(array: T[], chunkSize: number) {
217
+ let chunkedArray: T[][] = [];
218
+ for (var i = 0; i < array.length; i += chunkSize) {
219
+ chunkedArray.push(array.slice(i, i + chunkSize));
220
+ }
221
+ return chunkedArray;
222
+ }
223
+
224
+ /** Takes an array and transforms it into a hash. this will assign 1 item per key, assumig getKey will be unique per item. */
225
+ export function toHash<T, Y = T>(arr: T[], getKey: (element: T) => string | number, filter?: (element: T) => boolean, transformValue?: (element: T) => Y): IDictionary<Y> {
226
+ let hash: { [key: string | number]: Y; } = {};
227
+ if (!isFunction(transformValue)) transformValue = v => v as any as Y;
228
+ if (isNotEmptyArray(arr))
229
+ arr.forEach(i => {
230
+ if (!isFunction(filter) || filter(i))
231
+ hash[getKey(i)] = transformValue(i);
232
+ });
233
+ return hash;
234
+ }
235
+
236
+ /** Returns an array from the values of the dictionary. */
237
+ export function toArray<Source, Result = Source>(hash: { [key: string]: Source; }, filter?: (element: Source) => boolean, transform?: (key: string, element: Source) => Result): Result[] {
238
+ let arr: Result[] = [];
239
+ if (!isFunction(transform)) transform = (key, element) => element as any as Result;
240
+
241
+ if (!isNullOrUndefined(hash))
242
+ Object.keys(hash).forEach(key => {
243
+ if (!isFunction(filter) || filter(hash[key]))
244
+ arr.push(transform(key, hash[key]));
245
+ });
246
+ return arr;
247
+ }
248
+
249
+ /** returns a new dictionary, converting each entry in source using the transform function */
250
+ export function convertDictionary<S, R>(source: IDictionary<S>, transform: (sourceItem: S) => R): IDictionary<R> {
251
+ let result: IDictionary<R> = {};
252
+ forEach(source, (key, value) => { result[key] = transform(value); });
253
+ return result;
254
+ }
255
+
256
+ export function flattenArray<T>(array: (T | T[])[]) {
257
+ return array.reduce((acc, val) => (acc as T[]).concat(val), []) as T[];
258
+ }
259
+
260
+ /** careful, does not work for date/complex objects. Use GetUniqueArrayInfo if you suspect you might have Date/complex objects. */
261
+ export function makeUniqueArray<T>(arr: T[]) {
262
+ return arr.filter((v, i, a) => a.indexOf(v) === i);
263
+ }
264
+
265
+ /** return an array of unique values, and the first index they were found, use defaultPrimitiveGetValue for default functionality */
266
+ export function GetUniqueArrayInfo<T, V>(arr: T[], getValue: (item: T) => V) {
267
+ var uniqueValues: { item: T; value: V; firstIndex: number; }[] = [];
268
+ var uniqueArray: T[] = [];
269
+ var foundValues: V[] = [];
270
+ var hasDuplicates = false;
271
+ var duplicateIndexes: number[] = [];
272
+
273
+ if (isNotEmptyArray(arr)) {
274
+ arr.forEach((item, index) => {
275
+ let value = getValue(item);
276
+ if (foundValues.includes(value)) {
277
+ hasDuplicates = true;
278
+ duplicateIndexes.push(index);
279
+ }
280
+ else {
281
+ foundValues.push(value);
282
+ uniqueValues.push({ item: item, value: value, firstIndex: index });
283
+ uniqueArray.push(item);
284
+ }
285
+ });
286
+ }
287
+
288
+ return {
289
+ /** true if duplicate values found */
290
+ hasDuplicates: hasDuplicates,
291
+ /** all duplicate item indexes */
292
+ duplicateIndexes: duplicateIndexes,
293
+ /** unique values and their info */
294
+ uniqueValues: uniqueValues,
295
+ /** the unique version of this array */
296
+ uniqueArray: uniqueArray
297
+ };
298
+ }
299
+
300
+ export interface IMultiLevelGroupItem<ItemType> {
301
+ parentGroup: IMultiLevelGroup<ItemType>;
302
+ }
303
+ export interface IMultiLevelGroup<ItemType> {
304
+ groupItems: (ItemType & IMultiLevelGroupItem<ItemType>)[];
305
+ subGroups: IDictionary<IMultiLevelGroup<ItemType>>;
306
+ depth: number;
307
+ parentGroup: IMultiLevelGroup<ItemType>;
308
+ /** would contain the path to the group, such as: 0_1_3 for first group, second sub group, 4th sub-sub group */
309
+ key: string;
310
+ index: number;
311
+ title: string;
312
+ /** Optional, add a prefix to the group. For example: "Priority > " to fullTitle will be "Priority > High" */
313
+ groupPrefix?: string;
314
+ /** title with decorations, such as prefix */
315
+ fullTitle: string;
316
+ }
317
+ export type MultiLevelGroupItem<ItemType> = ItemType & IMultiLevelGroupItem<ItemType>;
318
+ export type MultiLevelGroupOrItem<ItemType> = MultiLevelGroupItem<ItemType> | IMultiLevelGroup<ItemType>;
319
+ /** returns true if the element is a group of items */
320
+ export function IsMultiLevelGroup<ItemType>(groupOrItem: MultiLevelGroupOrItem<ItemType>): groupOrItem is IMultiLevelGroup<ItemType> {
321
+ let asGroup = groupOrItem as IMultiLevelGroup<ItemType>;
322
+ return !isNullOrUndefined(asGroup.subGroups) && Array.isArray(asGroup.groupItems) && isNumber(asGroup.index) && isNumber(asGroup.depth);
323
+ }
324
+
325
+ /** returns a flat array of groups>items ordered by groups */
326
+ export function FlattenGroupItems<ItemType>(groups: IDictionary<IMultiLevelGroup<ItemType>>) {
327
+ let flatItems: MultiLevelGroupOrItem<ItemType>[] = [];
328
+ Object.keys(groups).forEach(groupName => {
329
+ let group = groups[groupName];
330
+ if (!isNullOrEmptyString(groupName))
331
+ flatItems.push(group);
332
+ let subGroups = Object.keys(group.subGroups);
333
+ if (isNotEmptyArray(subGroups)) {
334
+ flatItems.push(...FlattenGroupItems(group.subGroups));
335
+ }
336
+ else flatItems.push(...group.groupItems);
337
+ });
338
+
339
+ return flatItems;
340
+ }
341
+
342
+ /** split a collection by page size and return the info */
343
+ export function GetPagedCollectionInfo<T>(collection: Array<T>, pageSize: number, currentPage?: number) {
344
+ let pagedItems: (T[])[] = [];
345
+
346
+ if (pageSize < 1) {
347
+ pagedItems = [collection.slice()];
348
+ }
349
+ else {
350
+ let copy = collection.slice();
351
+ while (isNotEmptyArray(copy)) {
352
+ pagedItems.push(copy.splice(0, pageSize));
353
+ }
354
+ }
355
+
356
+ currentPage = isNumber(currentPage) && currentPage >= 0 && currentPage < pagedItems.length ? currentPage : 0;
357
+ return {
358
+ /** nubmer of pages */
359
+ pages: pagedItems.length,
360
+ /** page items, per page (Array of pages, each has an array of the page items) */
361
+ pagedItems: pagedItems,
362
+ /** the current page */
363
+ currentPage: currentPage,
364
+ /** the current page items */
365
+ currentPageItems: pagedItems[currentPage] || [],
366
+ /** has more than 1 page */
367
+ hasPages: pagedItems.length > 1,
368
+ allowPrev: currentPage > 0,
369
+ allowNext: currentPage < pagedItems.length - 1
370
+ };
371
+ }
372
+
373
+ /** use with sortArray or get unique array to handle premitive types or dates, with a JSON.stringify to all other values */
374
+ export function defaultPrimitiveGetValue<T>(item: T) {
375
+ return isNullOrUndefined(item)
376
+ ? ""
377
+ : isDate(item) ? item.getTime()
378
+ : isBoolean(item)
379
+ ? item === true ? 1 : 0
380
+ : isNumber(item) || isString(item)
381
+ ? item
382
+ : JSON.stringify(item);
383
+ }
384
+
385
+ export function RemoveItemFromArr<T>(arr: T[], item: T) {
386
+ let idx = arr.indexOf(item);
387
+ if (idx >= 0)
388
+ arr.splice(idx, 1);
389
+ }
390
+ export function PushNoDuplicate<T>(arr: T[], item: T) {
391
+ if (!arr.includes(item)) arr.push(item);
392
+ }
393
+ /** fills an array with a value. Array.fill isn't available on SPFx. */
394
+ export function ArrayFill<T>(arr: T[], value: T, onlyEmpty?: boolean) {
395
+ for (let i = 0; i < arr.length; i++) {
396
+ if (onlyEmpty !== true || isNullOrUndefined(arr[i]))
397
+ arr[i] = value;
398
+ }
399
+ return arr;
400
+ }
401
+
402
+ /** give a name and a collection, and it will return a unique name availalbe, suffixing a _# to the name
403
+ * example: file
404
+ * return file, file_2, file_9 etc... whichever is availalbe first.
405
+ */
406
+ export function FindNextAvailableName(name: string, usedNames: string[], options?: {
407
+ //check for specific letter case
408
+ caseSensitive?: boolean;
409
+ //append suffix when adding _# for example: file_1.docx, file_2.docx etc
410
+ suffix?: string;
411
+ }) {
412
+ let nameForTest = name;
413
+ if (options && options.caseSensitive !== true) {
414
+ usedNames = usedNames.map(n => n.toLowerCase());
415
+ nameForTest = name.toLowerCase();
416
+ }
417
+
418
+ let nameSuffix = options && options.suffix || "";
419
+
420
+ let suffixIdx = 0;
421
+ let suffixStr = "";
422
+ while (usedNames.indexOf(`${nameForTest}${suffixStr}${nameSuffix}`) >= 0) {
423
+ suffixIdx++;
424
+ suffixStr = "_" + suffixIdx;
425
+ }
426
+
427
+ return `${name}${suffixStr}${nameSuffix}`;
428
+ }
429
+
430
+ //** returns an array of numbers from 0,1,2... */
431
+ export function numbersArray<T extends number>(length: number, startFrom: number = 0) {
432
+ //dvp build will fail without any type
433
+ if (isNullOrUndefined(length) || length < 0) length = 0;
434
+ let arr: number[] = Array.from((Array(length) as any).keys());
435
+ return startFrom > 0
436
+ ? arr.map(i => i + startFrom) as T[]
437
+ : arr as T[];
438
438
  }