@3dsource/utils 1.0.4 → 1.0.5

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 (131) hide show
  1. package/fesm2022/3dsource-utils.mjs +1793 -0
  2. package/fesm2022/3dsource-utils.mjs.map +1 -0
  3. package/index.d.ts +5 -0
  4. package/{src/lib/color/CMYKtoRGB.ts → lib/color/CMYKtoRGB.d.ts} +1 -11
  5. package/lib/color/HEXtoRGB.d.ts +5 -0
  6. package/lib/color/HSVtoRGB.d.ts +21 -0
  7. package/{src/lib/color/RGBtoCMYK.ts → lib/color/RGBtoCMYK.d.ts} +1 -31
  8. package/lib/color/RGBtoHEX.d.ts +1 -0
  9. package/lib/color/RGBtoHSV.d.ts +19 -0
  10. package/lib/color/hsv.d.ts +1 -0
  11. package/lib/color/max.d.ts +1 -0
  12. package/lib/color/min.d.ts +1 -0
  13. package/lib/color/overlay.d.ts +1 -0
  14. package/{src/lib/color/rgb.ts → lib/color/rgb.d.ts} +1 -3
  15. package/lib/color/sub.d.ts +1 -0
  16. package/lib/color/subtract.d.ts +1 -0
  17. package/lib/color/sum.d.ts +1 -0
  18. package/{src/lib/color/toRGB.ts → lib/color/toRGB.d.ts} +1 -7
  19. package/lib/color/toRGBA.d.ts +1 -0
  20. package/lib/constants/color-codes.constant.d.ts +9 -0
  21. package/lib/csv/CSV2Array.d.ts +1 -0
  22. package/lib/csv/CSV2Records.d.ts +1 -0
  23. package/lib/csv/ObjectToCSV.d.ts +1 -0
  24. package/lib/dev/dev3d.d.ts +1 -0
  25. package/lib/dev/logger.d.ts +11 -0
  26. package/lib/dev/timeToString.d.ts +5 -0
  27. package/lib/filenaming/cleanupFileName.d.ts +1 -0
  28. package/lib/filenaming/makePath.d.ts +1 -0
  29. package/lib/filenaming/normalizePath.d.ts +1 -0
  30. package/lib/geom/expandOverRectangle.d.ts +2 -0
  31. package/lib/geom/fitIntoRectangle.d.ts +2 -0
  32. package/{src/lib/geom/interfaces/area.interface.ts → lib/geom/interfaces/area.interface.d.ts} +1 -2
  33. package/{src/lib/geom/interfaces/rect.interface.ts → lib/geom/interfaces/rect.interface.d.ts} +2 -2
  34. package/lib/geom/interfaces/size.interface.d.ts +4 -0
  35. package/lib/geom/interfaces//321/201oords.interface.d.ts +4 -0
  36. package/lib/helpers/BatchLoader.d.ts +47 -0
  37. package/lib/helpers/KeyboardNumericCode.d.ts +103 -0
  38. package/lib/helpers/serialize.d.ts +1 -0
  39. package/lib/helpers/sleep.d.ts +1 -0
  40. package/lib/helpers/trimLastSlashFromUrl.d.ts +1 -0
  41. package/lib/image/SaveImage.d.ts +18 -0
  42. package/lib/image/getCanvasCached.d.ts +4 -0
  43. package/lib/image/getSnapshot.d.ts +2 -0
  44. package/lib/image/loadImage.d.ts +1 -0
  45. package/lib/interfaces/image-output.d.ts +7 -0
  46. package/{src/lib/interfaces/load-args-tmp.interface.ts → lib/interfaces/load-args-tmp.interface.d.ts} +1 -2
  47. package/lib/interfaces/load-args.interface.d.ts +15 -0
  48. package/{src/lib/math/baseSortedIndex.ts → lib/math/baseSortedIndex.d.ts} +1 -19
  49. package/{src/lib/math/calculateMedian.ts → lib/math/calculateMedian.d.ts} +1 -17
  50. package/{src/lib/math/circularIndex.ts → lib/math/circularIndex.d.ts} +1 -5
  51. package/lib/math/clampf.d.ts +8 -0
  52. package/lib/math/degrees.d.ts +2 -0
  53. package/{src/lib/math/floatCompare.ts → lib/math/floatCompare.d.ts} +3 -30
  54. package/{src/lib/math/inverseLerp.ts → lib/math/inverseLerp.d.ts} +1 -22
  55. package/lib/math/lerp.d.ts +7 -0
  56. package/{src/lib/mutex/Mutex.ts → lib/mutex/Mutex.d.ts} +8 -15
  57. package/{src/lib/mutex/Semaphore.ts → lib/mutex/Semaphore.d.ts} +7 -27
  58. package/lib/mutex/TaskRunner.d.ts +5 -0
  59. package/lib/predicates/BooleanPredictors.d.ts +27 -0
  60. package/{src/lib/predicates/textForSearch.ts → lib/predicates/textForSearch.d.ts} +1 -17
  61. package/{src/lib/predicates/where.ts → lib/predicates/where.d.ts} +2 -32
  62. package/lib/rxjs/leadingTrailingDebounceTime.d.ts +15 -0
  63. package/{src/lib/rxjs/smoothTransition.ts → lib/rxjs/smoothTransition.d.ts} +1 -19
  64. package/lib/rxjs/tapLog.d.ts +2 -0
  65. package/lib/strings/pad.d.ts +8 -0
  66. package/package.json +13 -2
  67. package/eslint.config.js +0 -37
  68. package/ng-package.json +0 -7
  69. package/src/lib/color/HEXtoRGB.ts +0 -9
  70. package/src/lib/color/HSVtoRGB.ts +0 -82
  71. package/src/lib/color/RGBtoHEX.ts +0 -17
  72. package/src/lib/color/RGBtoHSV.ts +0 -53
  73. package/src/lib/color/hsv.ts +0 -14
  74. package/src/lib/color/max.ts +0 -18
  75. package/src/lib/color/min.ts +0 -18
  76. package/src/lib/color/overlay.ts +0 -25
  77. package/src/lib/color/sub.ts +0 -18
  78. package/src/lib/color/subtract.ts +0 -27
  79. package/src/lib/color/sum.ts +0 -19
  80. package/src/lib/color/toRGBA.ts +0 -8
  81. package/src/lib/constants/color-codes.constant.ts +0 -9
  82. package/src/lib/csv/CSV2Array.ts +0 -66
  83. package/src/lib/csv/CSV2Records.ts +0 -56
  84. package/src/lib/csv/ObjectToCSV.ts +0 -21
  85. package/src/lib/dev/dev3d.ts +0 -1
  86. package/src/lib/dev/logger.ts +0 -94
  87. package/src/lib/dev/timeToString.ts +0 -16
  88. package/src/lib/filenaming/cleanupFileName.ts +0 -18
  89. package/src/lib/filenaming/makePath.ts +0 -5
  90. package/src/lib/filenaming/normalizePath.ts +0 -9
  91. package/src/lib/geom/expandOverRectangle.ts +0 -17
  92. package/src/lib/geom/fitIntoRectangle.ts +0 -43
  93. package/src/lib/geom/interfaces/size.interface.ts +0 -4
  94. package/src/lib/geom/interfaces//321/201oords.interface.ts +0 -4
  95. package/src/lib/helpers/BatchLoader.ts +0 -243
  96. package/src/lib/helpers/KeyboardNumericCode.ts +0 -118
  97. package/src/lib/helpers/serialize.ts +0 -11
  98. package/src/lib/helpers/sleep.ts +0 -3
  99. package/src/lib/helpers/trimLastSlashFromUrl.ts +0 -9
  100. package/src/lib/image/SaveImage.ts +0 -65
  101. package/src/lib/image/getCanvasCached.ts +0 -16
  102. package/src/lib/image/getSnapshot.ts +0 -99
  103. package/src/lib/image/loadImage.ts +0 -13
  104. package/src/lib/interfaces/image-output.ts +0 -8
  105. package/src/lib/interfaces/load-args.interface.ts +0 -15
  106. package/src/lib/math/clampf.ts +0 -14
  107. package/src/lib/math/degrees.ts +0 -7
  108. package/src/lib/math/lerp.ts +0 -12
  109. package/src/lib/mutex/TaskRunner.ts +0 -26
  110. package/src/lib/predicates/BooleanPredictors.ts +0 -47
  111. package/src/lib/rxjs/leadingTrailingDebounceTime.ts +0 -86
  112. package/src/lib/rxjs/tapLog.ts +0 -13
  113. package/src/lib/strings/pad.ts +0 -18
  114. package/tsconfig.lib.json +0 -13
  115. package/tsconfig.lib.prod.json +0 -11
  116. /package/{src/lib/color/index.ts → lib/color/index.d.ts} +0 -0
  117. /package/{src/lib/constants/index.ts → lib/constants/index.d.ts} +0 -0
  118. /package/{src/lib/csv/index.ts → lib/csv/index.d.ts} +0 -0
  119. /package/{src/lib/dev/index.ts → lib/dev/index.d.ts} +0 -0
  120. /package/{src/lib/filenaming/index.ts → lib/filenaming/index.d.ts} +0 -0
  121. /package/{src/lib/geom/index.ts → lib/geom/index.d.ts} +0 -0
  122. /package/{src/lib/geom/interfaces/index.ts → lib/geom/interfaces/index.d.ts} +0 -0
  123. /package/{src/lib/helpers/index.ts → lib/helpers/index.d.ts} +0 -0
  124. /package/{src/lib/image/index.ts → lib/image/index.d.ts} +0 -0
  125. /package/{src/lib/interfaces/index.ts → lib/interfaces/index.d.ts} +0 -0
  126. /package/{src/lib/math/index.ts → lib/math/index.d.ts} +0 -0
  127. /package/{src/lib/mutex/index.ts → lib/mutex/index.d.ts} +0 -0
  128. /package/{src/lib/predicates/index.ts → lib/predicates/index.d.ts} +0 -0
  129. /package/{src/lib/rxjs/index.ts → lib/rxjs/index.d.ts} +0 -0
  130. /package/{src/lib/strings/index.ts → lib/strings/index.d.ts} +0 -0
  131. /package/{src/public-api.ts → public-api.d.ts} +0 -0
@@ -0,0 +1,1793 @@
1
+ import { from, switchMap, finalize, asyncScheduler, Observable, timer, animationFrameScheduler, interval } from 'rxjs';
2
+ import { map, distinctUntilChanged, tap } from 'rxjs/operators';
3
+
4
+ /**
5
+ * RGB from each of the CMYK values to determine a return as an any[].
6
+ * CMYK values are as follows.
7
+ * C - a number between 0 and 255 representing cyan
8
+ * M - number between 0 and 255 representing magenta
9
+ * Y - number between 0 and 255 representing yellow
10
+ * K - number between 0 and 255 representing black
11
+ *
12
+ **/
13
+ function CMYKtoRGB(c, m, y, k) {
14
+ c = 255 - c;
15
+ m = 255 - m;
16
+ y = 255 - y;
17
+ k = 255 - k;
18
+ return [
19
+ ((255 - c) * (255 - k)) / 255,
20
+ ((255 - m) * (255 - k)) / 255,
21
+ ((255 - y) * (255 - k)) / 255,
22
+ ];
23
+ }
24
+
25
+ function HEXtoRGB(color) {
26
+ if (typeof color === 'string') {
27
+ color = parseInt(color.replace('#', ''), 16);
28
+ }
29
+ const r = (color >> 16) & 255;
30
+ const g = (color >> 8) & 255;
31
+ const b = color & 255;
32
+ return { r: r, g: g, b: b };
33
+ }
34
+
35
+ /**
36
+ * Values calculated from each RGB * RGB color value.
37
+ * @ Param r the red (R) indicating the number (0-255)
38
+ * @ Param g green (G) indicates the number (0-255)
39
+ * @ Param b blue (B) shows the number (0-255)
40
+ * @ Return obtained from the RGB color value for each indicating the number
41
+ **/
42
+ function rgb(r, g, b) {
43
+ return (r << 16) | (g << 8) | b;
44
+ }
45
+
46
+ /* eslint-disable prefer-const */
47
+ /**
48
+ * HSV from each of the RGB values to determine a return as an any[].
49
+ * RGB values are as follows.
50
+ * R - a number from 0 to 255
51
+ * G - a number from 0 to 255
52
+ * B - a number from 0 to 255
53
+ *
54
+ * HSV values are as follows.
55
+ * H - a number between 360-0
56
+ * S - number between 0 and 1.0
57
+ * V - number between 0 and 1.0
58
+ *
59
+ * H is replaced with equivalent numbers in the range of the 360-0 that is out of range.
60
+ * Cannot compute, including alpha.
61
+ *
62
+ * @ Param h hue (Hue) number that indicates (to 360-0)
63
+ * @ Param s the saturation (Saturation) shows the number (0.0 to 1.0)
64
+ * @ Param v lightness (Value) indicates the number (0.0 to 1.0)
65
+ * @ Return RGB values into an any[] of [R, G, B]
66
+ **/
67
+ function HSVtoRGB(h, s, v) {
68
+ let r = 0, g = 0, b = 0;
69
+ let i, x, y, z;
70
+ if (s < 0) {
71
+ s = 0;
72
+ }
73
+ if (s > 1) {
74
+ s = 1;
75
+ }
76
+ if (v < 0) {
77
+ v = 0;
78
+ }
79
+ if (v > 1) {
80
+ v = 1;
81
+ }
82
+ h = h % 360;
83
+ if (h < 0) {
84
+ h += 360;
85
+ }
86
+ h /= 60;
87
+ i = h >> 0;
88
+ x = v * (1 - s);
89
+ y = v * (1 - s * (h - i));
90
+ z = v * (1 - s * (1 - h + i));
91
+ switch (i) {
92
+ case 0:
93
+ r = v;
94
+ g = z;
95
+ b = x;
96
+ break;
97
+ case 1:
98
+ r = y;
99
+ g = v;
100
+ b = x;
101
+ break;
102
+ case 2:
103
+ r = x;
104
+ g = v;
105
+ b = z;
106
+ break;
107
+ case 3:
108
+ r = x;
109
+ g = y;
110
+ b = v;
111
+ break;
112
+ case 4:
113
+ r = z;
114
+ g = x;
115
+ b = v;
116
+ break;
117
+ case 5:
118
+ r = v;
119
+ g = x;
120
+ b = y;
121
+ break;
122
+ }
123
+ return [(r * 255) >> 0, (g * 255) >> 0, (b * 255) >> 0];
124
+ }
125
+
126
+ /* eslint-disable prefer-spread */
127
+ /**
128
+ * HSV calculated from the numbers of each RGB color value.
129
+ * @ Param h hue (Hue) number that indicates (to 360-0)
130
+ * @ Param s the saturation (Saturation) shows the number (0.0 to 1.0)
131
+ * @ Param v lightness (Value) indicates the number (0.0 to 1.0)
132
+ * @ Return obtained from the RGB color value for each indicating the number
133
+ **/
134
+ function hsv(h, s, v) {
135
+ return rgb.apply(null, HSVtoRGB(h, s, v));
136
+ }
137
+
138
+ /**
139
+ * RGB figures show (0x000000 0xFFFFFF up from) the
140
+ * R, G, B returns an any[] divided into a number from 0 to 255, respectively.
141
+ *
142
+ * @ Param rgb numbers show (0x000000 0xFFFFFF up from)
143
+ * @ Return any[] indicates the value of each color [R, G, B]
144
+ **/
145
+ function toRGB(rgb) {
146
+ const r = (rgb >> 16) & 0xff;
147
+ const g = (rgb >> 8) & 0xff;
148
+ const b = rgb & 0xff;
149
+ return [r, g, b];
150
+ }
151
+
152
+ /**
153
+ * Comparison (light).
154
+ * 2 RGB single number that indicates (0x000000 0xFFFFFF up from) to compare,
155
+ * RGB values combined with higher returns to their numbers.
156
+ * @ Param col1 RGB numbers show (0x000000 0xFFFFFF up from)
157
+ * @ Param col2 RGB numbers show (0x000000 0xFFFFFF up from)
158
+ * @ Return comparison (light) value
159
+ **/
160
+ function max(col1, col2) {
161
+ const c1 = toRGB(col1);
162
+ const c2 = toRGB(col2);
163
+ const r = Math.max(c1[0], c2[0]);
164
+ const g = Math.max(c1[1], c2[1]);
165
+ const b = Math.max(c1[2], c2[2]);
166
+ return (r << 16) | (g << 8) | b;
167
+ }
168
+
169
+ /**
170
+ * Comparison (dark).
171
+ * 2 RGB single numbers that indicate (0x000000 0xFFFFFF up from) to compare,
172
+ * RGB lower combined returns a numeric value for each number.
173
+ * @ Param col1 RGB numbers show (0x000000 0xFFFFFF up from)
174
+ * @ Param col2 RGB numbers show (0x000000 0xFFFFFF up from)
175
+ * @ Return comparison (dark) values
176
+ **/
177
+ function min(col1, col2) {
178
+ const c1 = toRGB(col1);
179
+ const c2 = toRGB(col2);
180
+ const r = Math.min(c1[0], c2[0]);
181
+ const g = Math.min(c1[1], c2[1]);
182
+ const b = Math.min(c1[2], c2[2]);
183
+ return (r << 16) | (g << 8) | b;
184
+ }
185
+
186
+ /**
187
+ *
188
+ There are two part of formula:
189
+ First part: If Lower Layer Value > 127.5, then do the following -
190
+ Value Unit = (255-Lower Layer Value)/127.5
191
+ Min Value = Lower Layer Value - (255-Lower Layer Value)
192
+ Overlay = (Upper Layer Value * Value Unit) + Min Value
193
+ Second part: If Lower Layer Value < 127.5, then do the following -
194
+ Value Unit=Lower Layer Value/127.5
195
+
196
+ Overlay = Upper Layer Value * Value Unit
197
+ * @param {number} col1
198
+ * @param {number} col2
199
+ * @return {number}
200
+ */
201
+ function overlay(col1, col2) {
202
+ const c1 = toRGB(col1);
203
+ const c2 = toRGB(col2);
204
+ const r = Math.max(c1[0], c2[0]);
205
+ const g = Math.max(c1[1], c2[1]);
206
+ const b = Math.max(c1[2], c2[2]);
207
+ return (r << 16) | (g << 8) | b;
208
+ }
209
+
210
+ /**
211
+ * RGB from the respective figures, HSV sequences in terms of returns.
212
+ * RGB values are as follows.
213
+ * R - a number from 0 to 255
214
+ * G - a number from 0 to 255
215
+ * B - a number from 0 to 255
216
+ *
217
+ * CMYK values are as follows.
218
+ * C - a number between 0 and 255 representing cyan
219
+ * M - number between 0 and 255 representing magenta
220
+ * Y - number between 0 and 255 representing yellow
221
+ * K - number between 0 and 255 representing black
222
+ *
223
+ * Cannot compute, including alpha.
224
+ * @ Param r the red (R) indicating the number (0x00 to 0xFF to)
225
+ * @ Param g green (G) indicates the number (0x00 to 0xFF to)
226
+ * @ Param b blue (B) shows the number (0x00 to 0xFF to)
227
+ * @ Return CMYK values into an any[] of [H, S, V]
228
+ **/
229
+ function RGBtoCMYK(r, g, b) {
230
+ let c = 0;
231
+ let m = 0;
232
+ let y = 0;
233
+ let k = 0;
234
+ c = 255 - r;
235
+ m = 255 - g;
236
+ y = 255 - b;
237
+ k = 255;
238
+ if (c < k) {
239
+ k = c;
240
+ }
241
+ if (m < k) {
242
+ k = m;
243
+ }
244
+ if (y < k) {
245
+ k = y;
246
+ }
247
+ if (k === 255) {
248
+ c = 0;
249
+ m = 0;
250
+ y = 0;
251
+ }
252
+ else {
253
+ c = Math.round((255 * (c - k)) / (255 - k));
254
+ m = Math.round((255 * (m - k)) / (255 - k));
255
+ y = Math.round((255 * (y - k)) / (255 - k));
256
+ }
257
+ return [c, m, y, k];
258
+ }
259
+
260
+ function RGBtoHEX(r, g, b) {
261
+ let redColor = r.toString(16);
262
+ let greenColor = g.toString(16);
263
+ let blackColor = b.toString(16);
264
+ if (redColor.length === 1) {
265
+ redColor = '0' + redColor;
266
+ }
267
+ if (greenColor.length === 1) {
268
+ greenColor = '0' + greenColor;
269
+ }
270
+ if (blackColor.length === 1) {
271
+ blackColor = '0' + blackColor;
272
+ }
273
+ return (redColor + greenColor + blackColor).toUpperCase();
274
+ }
275
+
276
+ /**
277
+ * RGB from the respective figures, HSV sequences in terms of returns.
278
+ * RGB values are as follows.
279
+ * R - a number from 0 to 255
280
+ * G - a number from 0 to 255
281
+ * B - a number from 0 to 255
282
+ *
283
+ * HSV values are as follows.
284
+ * H - a number between 360-0
285
+ * S - number between 0 and 1.0
286
+ * V - number between 0 and 1.0
287
+ *
288
+ * Cannot compute, including alpha.
289
+ * @ Param r the red (R) indicating the number (0x00 to 0xFF to)
290
+ * @ Param g green (G) indicates the number (0x00 to 0xFF to)
291
+ * @ Param b blue (B) shows the number (0x00 to 0xFF to)
292
+ * @ Return HSV values into an any[] of [H, S, V]
293
+ **/
294
+ function RGBtoHSV(r, g, b) {
295
+ r /= 255;
296
+ g /= 255;
297
+ b /= 255;
298
+ let h = 0, s = 0, v = 0;
299
+ let x, y;
300
+ if (r >= g)
301
+ x = r;
302
+ else
303
+ x = g;
304
+ if (b > x)
305
+ x = b;
306
+ if (r <= g)
307
+ y = r;
308
+ else
309
+ y = g;
310
+ if (b < y)
311
+ y = b;
312
+ v = x;
313
+ const c = x - y;
314
+ if (x === 0)
315
+ s = 0;
316
+ else
317
+ s = c / x;
318
+ if (s !== 0) {
319
+ if (r === x) {
320
+ h = (g - b) / c;
321
+ }
322
+ else {
323
+ if (g === x) {
324
+ h = 2 + (b - r) / c;
325
+ }
326
+ else {
327
+ if (b === x) {
328
+ h = 4 + (r - g) / c;
329
+ }
330
+ }
331
+ }
332
+ h = h * 60;
333
+ if (h < 0)
334
+ h = h + 360;
335
+ }
336
+ return [h, s, v];
337
+ }
338
+
339
+ /**
340
+ * Subtractive.
341
+ * 2 RGB single numbers that indicate (0x000000 0xFFFFFF up from) Return the value
342
+ * of the subtractive color.
343
+ * @ Param col1 RGB numbers show (0x000000 0xFFFFFF up from)
344
+ * @ Param col2 RGB numbers show (0x000000 0xFFFFFF up from)
345
+ * @ Return the subtractive
346
+ **/
347
+ function sub(col1, col2) {
348
+ const c1 = toRGB(col1);
349
+ const c2 = toRGB(col2);
350
+ const r = Math.max(c1[0] - c2[0], 0);
351
+ const g = Math.max(c1[1] - c2[1], 0);
352
+ const b = Math.max(c1[2] - c2[2], 0);
353
+ return (r << 16) | (g << 8) | b;
354
+ }
355
+
356
+ /**
357
+ * Subtraction.
358
+ * 2 RGB single number that indicates (0x000000 0xFFFFFF up from) is subtracted
359
+ * from the return numbers.
360
+ * @ Param col1 RGB numbers show (0x000000 0xFFFFFF up from)
361
+ * @ Param col2 RGB numbers show (0x000000 0xFFFFFF up from)
362
+ * @ Return value subtracted Blend
363
+ **/
364
+ function subtract(col1, col2) {
365
+ const colA = toRGB(col1);
366
+ const colB = toRGB(col2);
367
+ const r = Math.max(Math.max(colB[0] - (256 - colA[0]), colA[0] - (256 - colB[0])), 0);
368
+ const g = Math.max(Math.max(colB[1] - (256 - colA[1]), colA[1] - (256 - colB[1])), 0);
369
+ const b = Math.max(Math.max(colB[2] - (256 - colA[2]), colA[2] - (256 - colB[2])), 0);
370
+ return (r << 16) | (g << 8) | b;
371
+ }
372
+
373
+ /**
374
+ * Additive color.
375
+ * 2 RGB single numbers that indicate (0x000000 0xFFFFFF up from) Return the value
376
+ * of the additive mixture.
377
+ * @ Param col1 RGB numbers show (0x000000 0xFFFFFF up from)
378
+ * @ Param col2 RGB numbers show (0x000000 0xFFFFFF up from)
379
+ * @ Return the additive color
380
+ **/
381
+ function sum(col1, col2) {
382
+ const c1 = toRGB(col1);
383
+ const c2 = toRGB(col2);
384
+ const r = Math.min(c1[0] + c2[0], 255);
385
+ const g = Math.min(c1[1] + c2[1], 255);
386
+ const b = Math.min(c1[2] + c2[2], 255);
387
+ return (r << 16) | (g << 8) | b;
388
+ }
389
+
390
+ function toRGBA(rgba) {
391
+ const a = (rgba >> 24) & 0xff;
392
+ const r = (rgba >> 16) & 0xff;
393
+ const g = (rgba >> 8) & 0xff;
394
+ const b = rgba & 0xff;
395
+ return [r, g, b, a];
396
+ }
397
+
398
+ const COLOR_CODES = {
399
+ TO_UNREAL: ['#ffffff', '#005912', `UNREAL <-`],
400
+ TAP_LOG: ['#ffff00', '#1976d2', ' [TAPLOG]'],
401
+ FROM_UNREAL: ['#ffff00', '#898989', 'UNREAL ->'],
402
+ FROM_UNREAL_ERROR: ['#ffffff', '#ff0000', 'UNREAL ERROR ->'],
403
+ FROM_CIRRUS: ['#ffffff', '#ef702b', 'CIRRUS ->'],
404
+ FROM_CIRRUS_ERROR: ['#ffffff', '#ff0000', 'CIRRUS ->'],
405
+ TO_CIRRUS: ['#ffffff', '#3c9738', 'CIRRUS <-'],
406
+ };
407
+
408
+ function CSV2Array(strData, strDelimiter) {
409
+ // Check to see if the delimiter is defined. If not,
410
+ // then default to comma.
411
+ strDelimiter = strDelimiter || ',';
412
+ // Create a regular expression to parse the CSV values.
413
+ const objPattern = new RegExp(
414
+ // Delimiters.
415
+ '(\\' +
416
+ strDelimiter +
417
+ '|\\r?\\n|\\r|^)' +
418
+ // Quoted fields.
419
+ '(?:"([^"]*(?:""[^"]*)*)"|' +
420
+ // Standard fields.
421
+ '([^"\\' +
422
+ strDelimiter +
423
+ '\\r\\n]*))', 'gi');
424
+ // Create an array to hold our data. Give the array
425
+ // a default empty first row.
426
+ const arrData = [[]];
427
+ // Create an array to hold our individual pattern
428
+ // matching groups.
429
+ let arrMatches = null;
430
+ // Keep looping over the regular expression matches
431
+ // until we can no longer find a match.
432
+ while ((arrMatches = objPattern.exec(strData))) {
433
+ // Get the delimiter that was found.
434
+ const strMatchedDelimiter = arrMatches[1];
435
+ // Check to see if the given delimiter has a length
436
+ // (is not the start of string) and if it matches
437
+ // field delimiter. If idKey does not, then we know
438
+ // that this delimiter is a row delimiter.
439
+ if (strMatchedDelimiter.length && strMatchedDelimiter !== strDelimiter) {
440
+ // Since we have reached a new row of data,
441
+ // add an empty row to our data array.
442
+ arrData.push([]);
443
+ }
444
+ let strMatchedValue;
445
+ // Now that we have our delimiter out of the way,
446
+ // let's check to see which kind of value we
447
+ // captured (quoted or unquoted).
448
+ if (arrMatches[2]) {
449
+ // We found a quoted value. When we capture
450
+ // this value, unescape any double quotes.
451
+ strMatchedValue = arrMatches[2].replace(new RegExp('""', 'g'), '"');
452
+ }
453
+ else {
454
+ // We found a non-quoted value.
455
+ strMatchedValue = arrMatches[3];
456
+ }
457
+ // Now that we have our value string, let's add
458
+ // it to the data array.
459
+ arrData[arrData.length - 1].push(strMatchedValue);
460
+ }
461
+ // Return the parsed data.
462
+ return arrData;
463
+ }
464
+
465
+ const booleanRegex = /^(true|false)$/i;
466
+ const trueRegex = /^(true)$/i;
467
+ const jsonRegex = /([\d.]+)|(\[])|(\[.+])|({.+})/i;
468
+ const zipObject = (props, values) => props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {});
469
+ const toHead = (input) => {
470
+ return String(input).trim();
471
+ };
472
+ const toCorrectString = (input) => {
473
+ if (typeof input === 'string') {
474
+ if (input.match(jsonRegex)) {
475
+ // optimization for use try-catch only after regex match. much faster than without regex
476
+ try {
477
+ return JSON.parse(input);
478
+ }
479
+ catch {
480
+ /* empty */
481
+ }
482
+ }
483
+ if (input.match(booleanRegex)) {
484
+ return !!input.match(trueRegex);
485
+ }
486
+ return input.trim();
487
+ }
488
+ else if (typeof input === 'number') {
489
+ return input;
490
+ }
491
+ };
492
+ function CSV2Records(csv, useRowAsHead = 0, strDelimiter) {
493
+ const arr = CSV2Array(csv, strDelimiter);
494
+ const total = arr.length;
495
+ const keys = arr[useRowAsHead].map(toHead);
496
+ const data = [];
497
+ let tmpSKU = 0;
498
+ for (let i = useRowAsHead + 1; i < total; i++) {
499
+ const obj = zipObject(keys, arr[i].map(toCorrectString));
500
+ data.push(obj);
501
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
502
+ tmpSKU++;
503
+ }
504
+ return data.filter(Boolean);
505
+ }
506
+
507
+ function ObjectToCSV(records) {
508
+ if (records.length === 0) {
509
+ return;
510
+ }
511
+ const uniqueFields = Array.from(records.reduce((fields, record) => {
512
+ Object.keys(record).forEach((field) => fields.add(field));
513
+ return fields;
514
+ }, new Set()));
515
+ const csvHeader = uniqueFields.join(',') + '\n';
516
+ const csvRows = records
517
+ .map((record) => uniqueFields.map((field) => `"${record[field] || ''}"`).join(','))
518
+ .join('\n');
519
+ return csvHeader + csvRows;
520
+ }
521
+
522
+ const isLocalhost = !!window.location.href.match(/localhost/gi);
523
+
524
+ /**
525
+ * Adds required number of symbols before input
526
+ * @param input input string
527
+ * @param size amount of total symbols
528
+ * @param symbol filler
529
+ * @returns string
530
+ */
531
+ function pad(input, size, symbol = '0') {
532
+ let s = input + '';
533
+ while (s.length < size) {
534
+ s = symbol + s;
535
+ }
536
+ return s.slice(-size);
537
+ }
538
+
539
+ /**
540
+ * Create a string of the form 'HOURS.MINUTES.SECONDS.MILLISECONDS'.
541
+ */
542
+ function timeToString() {
543
+ const date = new Date();
544
+ return `${pad(date.getHours(), 2)}:${pad(date.getMinutes(), 2)}:${pad(date.getSeconds(), 2)}.${pad(date.getMilliseconds(), 3)}`;
545
+ }
546
+ function timeUTCToString() {
547
+ const date = new Date();
548
+ return `${pad(date.getUTCHours(), 2)}:${pad(date.getUTCMinutes(), 2)}:${pad(date.getUTCSeconds(), 2)}.${pad(date.getUTCMilliseconds(), 3)}`;
549
+ }
550
+
551
+ /**
552
+ * The Boolean object represents a truth value: true or false.
553
+ * @param value
554
+ * @constructor
555
+ * Returns:boolean
556
+ */
557
+ const Truthy = (value) => !!value && value !== 'false' && value !== 'undefined' && value !== 'null';
558
+ /**
559
+ * The Boolean object represents a INVERSE truth value: true or false.
560
+ * @param value
561
+ * @constructor
562
+ * Returns:boolean
563
+ */
564
+ const Falsy = (value) => !Truthy(value);
565
+ /**
566
+ * Checks if a value is empty.
567
+ *
568
+ * @param {any} value - The value to check.
569
+ * @return {boolean} Returns true if the value is empty, otherwise false.
570
+ */
571
+ const IsEmpty = (value) => {
572
+ return (value === undefined ||
573
+ value === null ||
574
+ (typeof value === 'object' && Object.keys(value).length === 0) ||
575
+ (typeof value === 'string' && value.trim().length === 0));
576
+ };
577
+ const NotEmpty = (value) => {
578
+ return !IsEmpty(value);
579
+ };
580
+
581
+ class Logger {
582
+ static { this.isDevMode = Truthy(localStorage.getItem('devMode')); }
583
+ static colored(color, background, ...args) {
584
+ if (!Logger.isDevMode) {
585
+ return;
586
+ }
587
+ const [first, ...rest] = args;
588
+ console.log.apply(console, [
589
+ `%c ${timeUTCToString()} ${first} `,
590
+ `color: ${color}; background: ${background};`,
591
+ ...rest,
592
+ ]);
593
+ }
594
+ static log(...args) {
595
+ if (!Logger.isDevMode) {
596
+ return;
597
+ }
598
+ console.log.apply(console, [
599
+ `%c ${timeUTCToString()} [ LOG ] `,
600
+ 'color: black; background: #80ff80;',
601
+ ...args,
602
+ ]);
603
+ }
604
+ static warn(...args) {
605
+ if (!Logger.isDevMode) {
606
+ return;
607
+ }
608
+ console.warn.apply(console, [
609
+ `%c ${timeUTCToString()} WARN `,
610
+ 'color: black; background: #ffd500;',
611
+ ...args,
612
+ ]);
613
+ }
614
+ static info(...args) {
615
+ if (!Logger.isDevMode) {
616
+ return;
617
+ }
618
+ console.log.apply(console, [
619
+ `%c ${timeUTCToString()} [ INFO ] `,
620
+ 'color: white; background: blue;',
621
+ ...args,
622
+ ]);
623
+ }
624
+ static error(...args) {
625
+ if (!Logger.isDevMode) {
626
+ return;
627
+ }
628
+ console.error.apply(console, [
629
+ `%c ${timeUTCToString()} ERROR `,
630
+ 'color: white; background: red;',
631
+ ...args,
632
+ ]);
633
+ }
634
+ static table(...args) {
635
+ if (!Logger.isDevMode) {
636
+ return;
637
+ }
638
+ // eslint-disable-next-line no-console
639
+ console.table.apply(console, [args]);
640
+ }
641
+ static time(...args) {
642
+ if (!Logger.isDevMode) {
643
+ return;
644
+ }
645
+ // eslint-disable-next-line no-console,prefer-spread
646
+ console.time.apply(console, args);
647
+ }
648
+ static timeEnd(...args) {
649
+ if (!Logger.isDevMode) {
650
+ return;
651
+ }
652
+ // eslint-disable-next-line no-console,prefer-spread
653
+ console.timeEnd.apply(console, args);
654
+ }
655
+ }
656
+
657
+ function cleanupFileName(value) {
658
+ if (!value) {
659
+ return value;
660
+ }
661
+ value = value
662
+ .toString()
663
+ .replace(/undefined/g, '')
664
+ .trim()
665
+ .replace(/\//g, '-')
666
+ .replace(/[-]+/g, '-')
667
+ .replace(/[^\w^.-]+/gi, '_')
668
+ .replace(/[\^]+/gi, '_');
669
+ value = value.split('_').filter(Boolean).join('_').toLowerCase();
670
+ return value;
671
+ }
672
+
673
+ function makePath(...value) {
674
+ return [...value].map((el) => cleanupFileName(el)).join('/');
675
+ }
676
+
677
+ function normalizePath(value) {
678
+ const re = new RegExp('[/\\\\]+', 'g');
679
+ return value
680
+ .split(re)
681
+ .map((el) => cleanupFileName(el))
682
+ .join('/');
683
+ }
684
+
685
+ function expandOverRectangle(objectRect, areaRect) {
686
+ const ratio = objectRect.w / objectRect.h;
687
+ const result = { x: 0, y: 0, w: 0, h: 0, scale: 1 };
688
+ if (areaRect.w / ratio < areaRect.h) {
689
+ result.w = areaRect.h * ratio;
690
+ result.h = areaRect.h;
691
+ }
692
+ else {
693
+ result.w = areaRect.w;
694
+ result.h = areaRect.w / ratio;
695
+ }
696
+ result.x = areaRect.x + (areaRect.w - result.w) / 2;
697
+ result.y = areaRect.y + (areaRect.h - result.h) / 2;
698
+ result.scale = result.w / objectRect.w;
699
+ return result;
700
+ }
701
+
702
+ function fitIntoRectangle(objectRect, areaRect, round = true, fixedRatio = null) {
703
+ const result = { x: 0, y: 0, w: 0, h: 0, scale: 1 };
704
+ const ratio = objectRect.h / objectRect.w;
705
+ if (objectRect.h > objectRect.w) {
706
+ result.h = areaRect.h;
707
+ result.w = areaRect.h / ratio;
708
+ if (result.w > areaRect.w) {
709
+ result.w = areaRect.w;
710
+ result.h = areaRect.w * ratio;
711
+ }
712
+ }
713
+ else {
714
+ result.w = areaRect.w;
715
+ result.h = areaRect.w * ratio;
716
+ if (result.h > areaRect.h) {
717
+ result.h = areaRect.h;
718
+ result.w = areaRect.h / ratio;
719
+ }
720
+ }
721
+ if (fixedRatio) {
722
+ result.w = objectRect.w * fixedRatio;
723
+ result.h = objectRect.h * fixedRatio;
724
+ }
725
+ const wOut = round ? Math.floor(result.w) : result.w;
726
+ const hOut = round ? Math.floor(result.h) : result.h;
727
+ return {
728
+ x: areaRect.x + (areaRect.w - wOut) / 2,
729
+ y: areaRect.y + (areaRect.h - hOut) / 2,
730
+ w: wOut,
731
+ h: hOut,
732
+ scale: wOut / objectRect.w,
733
+ };
734
+ }
735
+
736
+ const canvasCacheObj = {};
737
+ function getCanvasCached(id) {
738
+ if (!canvasCacheObj[id]) {
739
+ const canvas = document.createElement('canvas');
740
+ canvasCacheObj[id] = {
741
+ canvas,
742
+ ctx: canvas.getContext('2d'),
743
+ };
744
+ }
745
+ return canvasCacheObj[id];
746
+ }
747
+
748
+ function getSnapshot2(imageElement, maxWidth = 1024, output = 'image/png', quality = 0.9) {
749
+ const drawableImage = imageElement;
750
+ const canvasHelper = document.createElement('canvas');
751
+ const drawCTX = canvasHelper.getContext('2d');
752
+ const prop = fitIntoRectangle({
753
+ x: 0,
754
+ y: 0,
755
+ w: drawableImage.naturalWidth || drawableImage.width,
756
+ h: drawableImage.naturalHeight || drawableImage.height,
757
+ }, {
758
+ x: 0,
759
+ y: 0,
760
+ w: maxWidth,
761
+ h: drawableImage.naturalHeight || drawableImage.height,
762
+ });
763
+ const width = prop.w;
764
+ const height = prop.h;
765
+ canvasHelper.width = width;
766
+ canvasHelper.height = height;
767
+ drawCTX.save();
768
+ drawCTX.clearRect(0, 0, width, height);
769
+ drawCTX.drawImage(drawableImage, 0, 0, width, height);
770
+ drawCTX.restore();
771
+ if (output === 'canvas') {
772
+ return canvasHelper;
773
+ }
774
+ return canvasHelper.toDataURL(output, quality);
775
+ }
776
+ function getSnapshot(imageElement, maxWidth = 1024, output = 'image/png', quality = 0.9) {
777
+ const drawableImage = imageElement;
778
+ const canvasHelper = document.createElement('canvas');
779
+ const drawCTX = canvasHelper.getContext('2d');
780
+ const prop = fitIntoRectangle({
781
+ x: 0,
782
+ y: 0,
783
+ w: drawableImage.naturalWidth || drawableImage.width,
784
+ h: drawableImage.naturalHeight || drawableImage.height,
785
+ }, {
786
+ x: 0,
787
+ y: 0,
788
+ w: (drawableImage.naturalWidth || drawableImage.width) / 2,
789
+ h: drawableImage.naturalHeight || drawableImage.height,
790
+ });
791
+ const width = prop.w;
792
+ const height = prop.h;
793
+ canvasHelper.width = width;
794
+ canvasHelper.height = height;
795
+ drawCTX.save();
796
+ drawCTX.clearRect(0, 0, width, height);
797
+ drawCTX.drawImage(drawableImage, 0, 0, width, height);
798
+ drawCTX.restore();
799
+ if (width > maxWidth * 2) {
800
+ return getSnapshot(canvasHelper, maxWidth, output, quality);
801
+ }
802
+ return getSnapshot2(drawableImage, maxWidth, output, quality);
803
+ }
804
+
805
+ function loadImage(url) {
806
+ return new Promise((resolve, reject) => {
807
+ const img = new Image();
808
+ img.crossOrigin = 'anonymous';
809
+ img.onload = () => {
810
+ setTimeout(() => resolve(img), 1);
811
+ };
812
+ img.onerror = () => {
813
+ setTimeout(() => reject(null), 1);
814
+ };
815
+ img.src = url;
816
+ });
817
+ }
818
+
819
+ /**
820
+ * USE DownloadImage instead!
821
+ * @deprecated use DownloadImage instead!
822
+ * @param imageSource
823
+ * @param name
824
+ * @param width
825
+ * @param format
826
+ */
827
+ function SaveImage(imageSource, name, width, format) {
828
+ let out;
829
+ const image = imageSource;
830
+ if (typeof image !== 'string') {
831
+ out = getSnapshot(image, width, format);
832
+ }
833
+ else {
834
+ out = image;
835
+ }
836
+ if (navigator.msSaveBlob) {
837
+ // IE10+
838
+ try {
839
+ const blob = image.msToBlob();
840
+ return navigator.msSaveBlob(blob, name);
841
+ }
842
+ catch (e) {
843
+ console.warn(e);
844
+ }
845
+ }
846
+ else {
847
+ const uri = out;
848
+ const link = document.createElement('a');
849
+ link.download = name;
850
+ link.href = uri;
851
+ document.body.appendChild(link);
852
+ if (link.click) {
853
+ link.click();
854
+ }
855
+ else {
856
+ const event = document.createEvent('MouseEvents');
857
+ event.initMouseEvent('click', true, true, window);
858
+ link.dispatchEvent(event);
859
+ }
860
+ document.body.removeChild(link);
861
+ }
862
+ }
863
+ /**
864
+ * @param imageSource
865
+ * @param string name
866
+ * @param number width
867
+ * @param string format
868
+ */
869
+ const DownloadImage = SaveImage;
870
+
871
+ class BatchLoader {
872
+ constructor() {
873
+ this.maximumSlotsNumber = 1;
874
+ this.queue = [];
875
+ this.slots = [];
876
+ }
877
+ clear() {
878
+ this.queue.forEach((item) => {
879
+ try {
880
+ if (item.img) {
881
+ item.img.src = '';
882
+ }
883
+ }
884
+ catch (e) {
885
+ console.warn(e);
886
+ }
887
+ try {
888
+ item.xhr?.abort();
889
+ }
890
+ catch (e) {
891
+ console.warn(e);
892
+ }
893
+ });
894
+ this.slots.forEach((item) => {
895
+ try {
896
+ if (item.img) {
897
+ item.img.src = '';
898
+ }
899
+ }
900
+ catch (e) {
901
+ console.warn(e);
902
+ }
903
+ try {
904
+ item.xhr?.abort();
905
+ }
906
+ catch (e) {
907
+ console.warn(e);
908
+ }
909
+ });
910
+ this.slots.length = 0;
911
+ this.queue.length = 0;
912
+ }
913
+ /**
914
+ * new settings
915
+ * you must pass new settings
916
+ * @param sett
917
+ */
918
+ setSettings(sett) {
919
+ this.maximumSlotsNumber =
920
+ sett.maximumSlotsNumber !== undefined
921
+ ? sett.maximumSlotsNumber
922
+ : this.maximumSlotsNumber;
923
+ }
924
+ /**
925
+ * execute with a chosen file
926
+ * @param id
927
+ * @param func
928
+ */
929
+ withItem(id, func) {
930
+ let item = this.getById(id);
931
+ if (!item) {
932
+ return false;
933
+ }
934
+ item = func(item);
935
+ this.queue.sort(this.sortByOrder);
936
+ return true;
937
+ }
938
+ remove(id) {
939
+ this.queue = this.queue.filter((arg) => arg.id !== id);
940
+ return this.queue;
941
+ }
942
+ /**
943
+ * executes when all files are downloaded
944
+ * @param cb
945
+ */
946
+ whenDone(cb) {
947
+ this._whenDone = cb;
948
+ }
949
+ // order {number} lower will execute first, default *100*
950
+ load(args) {
951
+ const out = args
952
+ .filter((arg) => !!arg.path)
953
+ .map((arg) => ({
954
+ path: arg.path,
955
+ id: this.add(arg),
956
+ }));
957
+ this.queue.sort(this.sortByOrder);
958
+ this.checkSlots();
959
+ return out;
960
+ }
961
+ /**
962
+ * operates with one file
963
+ * @param item
964
+ * @private
965
+ */
966
+ add(item) {
967
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
968
+ const that = this;
969
+ const temp = {
970
+ id: item.id,
971
+ resolutionId: null,
972
+ useXhr: item.useXhr,
973
+ order: item.order !== undefined ? item.order : 100,
974
+ fallBack: item.fallBack,
975
+ path: item.path,
976
+ onLoadEnd: item.onLoadEnd,
977
+ onProgress: item.onProgress,
978
+ onLoadStart: item.onLoadStart,
979
+ extra: item.extra,
980
+ };
981
+ let retryCount = 1;
982
+ if (temp.useXhr) {
983
+ temp.xhr = this.getXmlHttp();
984
+ temp.xhr.onloadstart = (data) => item.onLoadStart(data);
985
+ temp.xhr.onprogress = (data) => item.onProgress(data);
986
+ temp.xhr.onreadystatechange = async function (data) {
987
+ if (this.readyState === 1 && this.status === 0) {
988
+ return;
989
+ }
990
+ if (this.readyState === 4 &&
991
+ (this.status === 404 || this.status === 0)) {
992
+ temp.error = true;
993
+ that.loadEnd(temp);
994
+ return;
995
+ }
996
+ if (this.readyState === 4 && this.status === 200) {
997
+ const urlCreator = window.URL;
998
+ const imageUrl = urlCreator.createObjectURL(data.target.response);
999
+ temp.img = await loadImage(imageUrl);
1000
+ that.loadEnd(temp);
1001
+ return;
1002
+ }
1003
+ };
1004
+ }
1005
+ else {
1006
+ temp.img = new Image();
1007
+ temp.img.crossOrigin = 'anonymous';
1008
+ temp.img.onload = () => {
1009
+ that.loadEnd(temp);
1010
+ };
1011
+ temp.img.onerror = () => {
1012
+ if (temp.fallBack && retryCount-- > 0) {
1013
+ console.warn('Fallback used =>', temp.path, '=>', temp.fallBack);
1014
+ if (temp.img) {
1015
+ temp.img.onload = null;
1016
+ temp.img.src = temp.fallBack;
1017
+ }
1018
+ that.loadEnd(temp);
1019
+ }
1020
+ else {
1021
+ temp.error = true;
1022
+ that.loadEnd(temp);
1023
+ }
1024
+ };
1025
+ }
1026
+ this.queue.push(temp);
1027
+ return temp.id;
1028
+ }
1029
+ sortByOrder(a, b) {
1030
+ return a.order > b.order ? 1 : a.order < b.order ? -1 : 0;
1031
+ }
1032
+ loadEnd(item) {
1033
+ if (item.onLoadEnd !== undefined) {
1034
+ for (let i = 0, len = this.slots.length; i < len; i += 1) {
1035
+ if (this.slots[i].id === item.id) {
1036
+ this.slots.splice(i, 1);
1037
+ break;
1038
+ }
1039
+ }
1040
+ item.onLoadEnd(item);
1041
+ }
1042
+ this.checkSlots();
1043
+ }
1044
+ checkSlots() {
1045
+ while ((this.slots.length < this.maximumSlotsNumber ||
1046
+ this.maximumSlotsNumber === -1) &&
1047
+ this.queue.length) {
1048
+ this.slots.push(this.queue.shift());
1049
+ const slot = this.slots[this.slots.length - 1];
1050
+ if (slot.useXhr) {
1051
+ slot.xhr?.open('GET', slot.path, true);
1052
+ slot.xhr?.send(null);
1053
+ }
1054
+ else {
1055
+ if (slot.img) {
1056
+ slot.img.src = slot.path;
1057
+ }
1058
+ }
1059
+ }
1060
+ if (this.slots.length === 0 &&
1061
+ this.queue.length === 0 &&
1062
+ this._whenDone !== undefined) {
1063
+ this._whenDone();
1064
+ }
1065
+ }
1066
+ /**
1067
+ * get Item By id for interaction with files in this.queue
1068
+ * @param id
1069
+ * @private
1070
+ */
1071
+ getById(id) {
1072
+ for (let i = 0, len = this.queue.length; i < len; i += 1) {
1073
+ if (this.queue[i].id === id) {
1074
+ return this.queue[i];
1075
+ }
1076
+ }
1077
+ return null;
1078
+ }
1079
+ getXmlHttp() {
1080
+ let xhr = null;
1081
+ try {
1082
+ xhr = new XMLHttpRequest();
1083
+ xhr.responseType = 'blob';
1084
+ }
1085
+ catch (e) {
1086
+ console.warn(e);
1087
+ }
1088
+ return xhr;
1089
+ }
1090
+ }
1091
+
1092
+ const KeyboardNumericCode = {
1093
+ Backspace: 8,
1094
+ Tab: 9,
1095
+ Numpad5: 12,
1096
+ NumpadEnter: 13,
1097
+ ShiftRight: 16,
1098
+ ControlRight: 17,
1099
+ ControlLeft: 17,
1100
+ AltRight: 18,
1101
+ Escape: 27,
1102
+ Space: 32,
1103
+ Numpad9: 33,
1104
+ Numpad3: 34,
1105
+ Numpad1: 35,
1106
+ Numpad7: 36,
1107
+ Numpad4: 37,
1108
+ Numpad8: 38,
1109
+ Numpad6: 39,
1110
+ Numpad2: 40,
1111
+ Numpad0: 45,
1112
+ NumpadDecimal: 46,
1113
+ Digit0: 48,
1114
+ Digit1: 49,
1115
+ Digit2: 50,
1116
+ Digit3: 51,
1117
+ Digit4: 52,
1118
+ Digit5: 53,
1119
+ Digit6: 54,
1120
+ Digit7: 55,
1121
+ Digit8: 56,
1122
+ Digit9: 57,
1123
+ KeyA: 65,
1124
+ KeyB: 66,
1125
+ KeyC: 67,
1126
+ KeyD: 68,
1127
+ KeyE: 69,
1128
+ KeyF: 70,
1129
+ KeyG: 71,
1130
+ KeyH: 72,
1131
+ KeyI: 73,
1132
+ KeyJ: 74,
1133
+ KeyK: 75,
1134
+ KeyL: 76,
1135
+ KeyM: 77,
1136
+ KeyN: 78,
1137
+ KeyO: 79,
1138
+ KeyP: 80,
1139
+ KeyQ: 81,
1140
+ KeyR: 82,
1141
+ KeyS: 83,
1142
+ KeyT: 84,
1143
+ KeyU: 85,
1144
+ KeyV: 86,
1145
+ KeyW: 87,
1146
+ KeyX: 88,
1147
+ KeyY: 89,
1148
+ KeyZ: 90,
1149
+ MetaLeft: 91,
1150
+ ContextMenu: 93,
1151
+ NumpadMultiply: 106,
1152
+ NumpadAdd: 107,
1153
+ NumpadSubtract: 109,
1154
+ NumpadDivide: 111,
1155
+ F1: 112,
1156
+ F2: 113,
1157
+ F3: 114,
1158
+ F4: 115,
1159
+ F5: 116,
1160
+ F6: 117,
1161
+ F7: 118,
1162
+ F8: 119,
1163
+ F9: 120,
1164
+ F10: 121,
1165
+ F11: 122,
1166
+ F12: 123,
1167
+ NumLock: 144,
1168
+ ScrollLock: 145,
1169
+ Semicolon: 186,
1170
+ Equal: 187,
1171
+ Comma: 188,
1172
+ Minus: 189,
1173
+ Period: 190,
1174
+ Slash: 191,
1175
+ Backquote: 192,
1176
+ BracketLeft: 219,
1177
+ Backslash: 220,
1178
+ BracketRight: 221,
1179
+ Quote: 222,
1180
+ IntlBackslash: 226,
1181
+ ArrowUp: 38,
1182
+ ArrowDown: 40,
1183
+ ArrowLeft: 37,
1184
+ ArrowRight: 39,
1185
+ };
1186
+ const InvertedKeyMapValues = {};
1187
+ //For use for InvertedKeyMap[32] will return 'Space' and so on
1188
+ const InvertedKeyMap = InvertedKeyMapValues;
1189
+ const KeyboardStringCode = {};
1190
+ Object.entries(KeyboardNumericCode).forEach(
1191
+ // @ts-expect-error @typescript-eslint/ban-ts-comment
1192
+ ([key, value]) => {
1193
+ InvertedKeyMapValues[value] = key;
1194
+ // @ts-expect-error @typescript-eslint/ban-ts-comment
1195
+ KeyboardStringCode[key] = key;
1196
+ });
1197
+
1198
+ function serialize(obj) {
1199
+ const str = [];
1200
+ for (const p in obj) {
1201
+ if (Object.prototype.hasOwnProperty.call(obj, p)) {
1202
+ str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
1203
+ }
1204
+ }
1205
+ return str.join('&');
1206
+ }
1207
+
1208
+ function sleep(time) {
1209
+ return new Promise((r) => setTimeout(r, time));
1210
+ }
1211
+
1212
+ function trimLastSlashFromUrl(baseUrl) {
1213
+ if (!baseUrl) {
1214
+ return null;
1215
+ }
1216
+ else if (baseUrl[baseUrl.length - 1] === '/') {
1217
+ return baseUrl.substring(0, baseUrl.length - 1);
1218
+ }
1219
+ return;
1220
+ }
1221
+
1222
+ const ImageOutput = {
1223
+ CANVAS: 'canvas',
1224
+ PNG: 'image/png',
1225
+ WEBP: 'image/webp',
1226
+ JPG: 'image/jpeg',
1227
+ };
1228
+
1229
+ /**
1230
+ * Calculates the index at which the `value` should be inserted into the `array` to maintain its
1231
+ * sorted order. This function performs a binary search with a complexity of O(log n), making it
1232
+ * efficient for large datasets. It assumes that the input array is sorted in ascending order and
1233
+ * contains comparable elements.
1234
+ *
1235
+ * Note: The function is designed to handle arrays with lengths up to a maximum of `HALF_MAX_ARRAY_LENGTH`
1236
+ * to ensure the search operation remains within JavaScript's maximum array index limit.
1237
+ *
1238
+ * @param {any[]} array The sorted array into which the value should be inserted. The array elements
1239
+ * should be comparable with the `value` using less-than and greater-than operations.
1240
+ * @param {number} value The value to insert into the array. This function assumes that `array` is sorted
1241
+ * in ascending order and will find the correct position for `value` accordingly.
1242
+ * @returns {number} The index at which the `value` should be inserted into `array` to maintain its sorted order.
1243
+ * If the `array` is empty or if `value` is less than or equal to all elements in `array`,
1244
+ * returns 0. If `value` is greater than all elements in `array`, returns the length of `array`.
1245
+ * @example
1246
+ * // returns 2
1247
+ * baseSortedIndex([1, 3, 4], 3);
1248
+ *
1249
+ * @example
1250
+ * // returns 1
1251
+ * baseSortedIndex([1, 2, 3], 2);
1252
+ */
1253
+ function baseSortedIndex(array, value) {
1254
+ const MAX_ARRAY_LENGTH = 4294967295;
1255
+ const HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
1256
+ let low = 0;
1257
+ let high = array?.length || low;
1258
+ if (high <= HALF_MAX_ARRAY_LENGTH) {
1259
+ while (low < high) {
1260
+ const mid = (low + high) >>> 1;
1261
+ const computed = array[mid];
1262
+ if (computed !== null && computed < value) {
1263
+ low = mid + 1;
1264
+ }
1265
+ else {
1266
+ high = mid;
1267
+ }
1268
+ }
1269
+ }
1270
+ return high;
1271
+ }
1272
+
1273
+ /**
1274
+ * Calculates the median of an array of numbers.
1275
+ *
1276
+ * The median is the middle value of a sorted list of numbers. If the list has an even number
1277
+ * of elements, the median is the average of the two middle numbers.
1278
+ *
1279
+ * @param numbers - An array of numbers for which the median will be calculated.
1280
+ * @returns The median value as a number, or `null` if the array is empty.
1281
+ *
1282
+ * @example
1283
+ * ```typescript
1284
+ * const numbers = [3, 1, 4, 2];
1285
+ * const median = calculateMedian(numbers);
1286
+ * console.log(median); // Output: 2.5
1287
+ * ```
1288
+ */
1289
+ function calculateMedian(numbers) {
1290
+ if (numbers.length === 0)
1291
+ return 0;
1292
+ // Sort the array in ascending order
1293
+ const sorted = [...numbers].sort((a, b) => a - b);
1294
+ const length = sorted.length;
1295
+ const middle = Math.floor(length / 2);
1296
+ // If the length is even, return the average of the two middle numbers
1297
+ if (length % 2 === 0) {
1298
+ return (sorted[middle - 1] + sorted[middle]) / 2;
1299
+ }
1300
+ // If the length is odd, return the middle number
1301
+ return sorted[middle];
1302
+ }
1303
+
1304
+ /**
1305
+ * Clamps value between min and max;
1306
+ * @param min minValue
1307
+ * @param max maxValue
1308
+ * @param value inputValue
1309
+ * @returns number
1310
+ */
1311
+ function clampf(min, max, value) {
1312
+ if (min > max) {
1313
+ return min;
1314
+ }
1315
+ return Math.min(max, Math.max(min, value));
1316
+ }
1317
+
1318
+ /**
1319
+ * Computes a circular index within a given range.
1320
+ *
1321
+ * This function ensures that the index wraps around within the range of 0 to `totalItems - 1`.
1322
+ * It uses the `clampf` function to clamp the result within the valid range.
1323
+ *
1324
+ * @param index - The current index to be wrapped.
1325
+ * @param totalItems - The total number of items in the range.
1326
+ * @returns The wrapped index within the range of 0 to `totalItems - 1`.
1327
+ *
1328
+ * @example
1329
+ * ```typescript
1330
+ * import { circularIndex } from './circularIndex';
1331
+ *
1332
+ * // Example 1: Basic usage
1333
+ * const index1 = circularIndex(5, 5);
1334
+ * console.log(index1); // Output: 0
1335
+ *
1336
+ * // Example 2: Index greater than totalItems
1337
+ * const index2 = circularIndex(6, 5);
1338
+ * console.log(index2); // Output: 1
1339
+ *
1340
+ * // Example 3: Negative index
1341
+ * const index3 = circularIndex(-2, 5);
1342
+ * console.log(index3); // Output: 3
1343
+ *
1344
+ * // Example 4: Large Negative index
1345
+ * const index4 = circularIndex(-6, 5);
1346
+ * console.log(index4); // Output: 4
1347
+ *
1348
+ * // Example 5: Index equal to totalItems
1349
+ * const index5 = circularIndex(1, 1);
1350
+ * console.log(index5); // Output: 0
1351
+ */
1352
+ function circularIndex(index, totalItems) {
1353
+ return clampf(0, totalItems - 1, (totalItems * 2 + index) % totalItems);
1354
+ }
1355
+
1356
+ function degreesToRadians(degrees) {
1357
+ return degrees * (Math.PI / 180);
1358
+ }
1359
+ function radiansToDegrees(radians) {
1360
+ return radians * (180 / Math.PI);
1361
+ }
1362
+
1363
+ /**
1364
+ * A small epsilon value used for floating-point comparisons.
1365
+ *
1366
+ * @remarks
1367
+ * The value of `EPSILON` is `0.01`. This can be overridden in the comparison functions if needed.
1368
+ */
1369
+ const EPSILON = 0.01;
1370
+ /**
1371
+ * Checks if floating-point number `A` is less than `B` considering a small margin of error.
1372
+ *
1373
+ * @param A - The first floating-point number to compare.
1374
+ * @param B - The second floating-point number to compare.
1375
+ * @param Epsilon - Optional custom epsilon value for comparison. Defaults to `EPSILON` if not provided.
1376
+ * @returns `true` if `A` is less than `B` considering the epsilon margin, otherwise `false`.
1377
+ *
1378
+ * @example
1379
+ * ```
1380
+ * const result = fpIsALessThanB(0.0034, 0.0066); // true
1381
+ * ```
1382
+ */
1383
+ function fpIsALessThanB(A, B, Epsilon) {
1384
+ Epsilon = Epsilon || EPSILON;
1385
+ return A - B < Epsilon && Math.abs(A - B) > Epsilon;
1386
+ }
1387
+ /**
1388
+ * Checks if floating-point number `A` is greater than `B` considering a small margin of error.
1389
+ *
1390
+ * @param A - The first floating-point number to compare.
1391
+ * @param B - The second floating-point number to compare.
1392
+ * @param Epsilon - Optional custom epsilon value for comparison. Defaults to `EPSILON` if not provided.
1393
+ * @returns `true` if `A` is greater than `B` considering the epsilon margin, otherwise `false`.
1394
+ *
1395
+ * @example
1396
+ * ```
1397
+ * const result = fpIsAGreaterThanB(0.0066, 0.0034); // true
1398
+ * ```
1399
+ */
1400
+ function fpIsAGreaterThanB(A, B, Epsilon) {
1401
+ Epsilon = Epsilon || EPSILON;
1402
+ return A - B > Epsilon && Math.abs(A - B) > Epsilon;
1403
+ }
1404
+ /**
1405
+ * Checks if floating-point number `A` is approximately the same as `B` considering a small margin of error.
1406
+ *
1407
+ * @param A - The first floating-point number to compare.
1408
+ * @param B - The second floating-point number to compare.
1409
+ * @param Epsilon - Optional custom epsilon value for comparison. Defaults to `EPSILON` if not provided.
1410
+ * @returns `true` if `A` is approximately equal to `B` considering the epsilon margin, otherwise `false`.
1411
+ *
1412
+ * @example
1413
+ * ```
1414
+ * const result = fpIsASameAsB(0.005, 0.0051); // true
1415
+ * ```
1416
+ */
1417
+ function fpIsASameAsB(A, B, Epsilon) {
1418
+ Epsilon = Epsilon || EPSILON;
1419
+ return Math.abs(A - B) < Epsilon;
1420
+ }
1421
+
1422
+ /**
1423
+ * Performs an inverse linear interpolation.
1424
+ *
1425
+ * Given a range defined by `min` and `max`, this function calculates the
1426
+ * interpolation factor (or percentage) of `value` within that range. Optionally,
1427
+ * it can clamp the result to be between 0 and 1 to ensure the return value is
1428
+ * within the range of a standard lerp operation.
1429
+ *
1430
+ * @param {number} min - The minimum value of the interpolation range.
1431
+ * @param {number} max - The maximum value of the interpolation range.
1432
+ * @param {number} value - The value to interpolate.
1433
+ * @param {boolean} [clampToNormal=false] - Whether to clamp the result between 0 and 1.
1434
+ * @returns {number} The interpolation factor of `value` between `min` and `max`.
1435
+ * If `clampToNormal` is true, the result is clamped between 0 and 1.
1436
+ * If the difference between `min` and `max` is 0, returns 1 to avoid division by zero.
1437
+ */
1438
+ function inverseLerp(min, max, value, clampToNormal = false) {
1439
+ if (clampToNormal) {
1440
+ value = Math.min(max, value);
1441
+ value = Math.max(min, value);
1442
+ }
1443
+ const difference = max - min;
1444
+ // Avoid JS division error
1445
+ if (difference === 0) {
1446
+ return 1;
1447
+ }
1448
+ const result = (value - min) / difference;
1449
+ if (clampToNormal) {
1450
+ return Math.min(1, Math.max(0, result));
1451
+ }
1452
+ return result;
1453
+ }
1454
+
1455
+ /**
1456
+ * @param min minimums
1457
+ * @param max maximums
1458
+ * @param value current float value in 0-1 range
1459
+ * @returns clamped value
1460
+ */
1461
+ function lerp(min, max, value) {
1462
+ value = clampf(0, 1, value);
1463
+ return value * (max - min) + min;
1464
+ }
1465
+
1466
+ /**
1467
+ * A Mutex class to handle mutual exclusion locks, ensuring that only one asynchronous task can execute a critical section at a time.
1468
+ *
1469
+ * @example
1470
+ * const mutex = new Mutex();
1471
+ *
1472
+ * async function task(name: string, duration: number) {
1473
+ * console.log(`${name} waiting to acquire mutex`);
1474
+ * const release = await mutex.acquire();
1475
+ * console.log(`${name} acquired mutex`);
1476
+ * await new Promise(resolve => setTimeout(resolve, duration));
1477
+ * console.log(`${name} releasing mutex`);
1478
+ * release();
1479
+ * }
1480
+ *
1481
+ * task('Task 1', 1000);
1482
+ * task('Task 2', 2000);
1483
+ * task('Task 3', 1500);
1484
+ * task('Task 4', 500);
1485
+ *
1486
+ * Outputs
1487
+ * [LOG]: "Task 1 waiting to acquire mutex"
1488
+ * [LOG]: "Task 2 waiting to acquire mutex"
1489
+ * [LOG]: "Task 3 waiting to acquire mutex"
1490
+ * [LOG]: "Task 4 waiting to acquire mutex"
1491
+ * [LOG]: "Task 1 acquired mutex"
1492
+ * [LOG]: "Task 1 releasing mutex"
1493
+ * [LOG]: "Task 2 acquired mutex"
1494
+ * [LOG]: "Task 2 releasing mutex"
1495
+ * [LOG]: "Task 3 acquired mutex"
1496
+ * [LOG]: "Task 3 releasing mutex"
1497
+ * [LOG]: "Task 4 acquired mutex"
1498
+ * [LOG]: "Task 4 releasing mutex"
1499
+ */
1500
+ class Mutex {
1501
+ constructor() {
1502
+ this.mutex = Promise.resolve();
1503
+ }
1504
+ /**
1505
+ * Acquires the mutex, returning a promise that resolves when the mutex is acquired.
1506
+ * The returned promise provides a release function that should be called to release the mutex.
1507
+ * @returns A promise that resolves with a release function.
1508
+ */
1509
+ async acquire() {
1510
+ let release;
1511
+ const previous = this.mutex;
1512
+ this.mutex = new Promise((resolve) => (release = resolve));
1513
+ await previous;
1514
+ return release;
1515
+ }
1516
+ }
1517
+
1518
+ /**
1519
+ * A Semaphore controls access to a resource that can handle a limited number of concurrent operations.
1520
+ *
1521
+ * @example
1522
+ * const semaphore = new Semaphore(2);
1523
+ *
1524
+ * async function task(name: string, duration: number) {
1525
+ * console.log(`${name} waiting to acquire semaphore`);
1526
+ * await semaphore.acquire();
1527
+ * console.log(`${name} acquired semaphore`);
1528
+ * await new Promise(resolve => setTimeout(resolve, duration));
1529
+ * console.log(`${name} releasing semaphore`);
1530
+ * semaphore.release();
1531
+ * }
1532
+ *
1533
+ * task('Task 1', 1000);
1534
+ * task('Task 2', 2000);
1535
+ * task('Task 3', 1500);
1536
+ * task('Task 4', 500);
1537
+ *
1538
+ * Outputs
1539
+ * [LOG]: "Task 1 waiting to acquire semaphore"
1540
+ * [LOG]: "Task 2 waiting to acquire semaphore"
1541
+ * [LOG]: "Task 3 waiting to acquire semaphore"
1542
+ * [LOG]: "Task 4 waiting to acquire semaphore"
1543
+ * [LOG]: "Task 1 acquired semaphore"
1544
+ * [LOG]: "Task 2 acquired semaphore"
1545
+ * [LOG]: "Task 1 releasing semaphore"
1546
+ * [LOG]: "Task 3 acquired semaphore"
1547
+ * [LOG]: "Task 2 releasing semaphore"
1548
+ * [LOG]: "Task 4 acquired semaphore"
1549
+ * [LOG]: "Task 4 releasing semaphore"
1550
+ * [LOG]: "Task 3 releasing semaphore"
1551
+ */
1552
+ class Semaphore {
1553
+ constructor(maxConcurrency) {
1554
+ this.maxConcurrency = maxConcurrency;
1555
+ this.tasks = [];
1556
+ this.currentCount = 0;
1557
+ }
1558
+ acquire() {
1559
+ return new Promise((resolve) => {
1560
+ if (this.currentCount < this.maxConcurrency) {
1561
+ this.currentCount++;
1562
+ resolve();
1563
+ }
1564
+ else {
1565
+ this.tasks.push(resolve);
1566
+ }
1567
+ });
1568
+ }
1569
+ release() {
1570
+ if (this.tasks.length > 0) {
1571
+ const nextTask = this.tasks.shift();
1572
+ if (nextTask) {
1573
+ nextTask();
1574
+ }
1575
+ }
1576
+ else if (this.currentCount > 0) {
1577
+ this.currentCount--;
1578
+ }
1579
+ }
1580
+ }
1581
+
1582
+ class TaskRunner {
1583
+ constructor() {
1584
+ this.mutex = new Mutex();
1585
+ }
1586
+ runTask(name, task) {
1587
+ // Log that the task is waiting to acquire the mutex
1588
+ Logger.log(`${name} waiting to acquire mutex`);
1589
+ return from(this.mutex.acquire()).pipe(switchMap((release) => {
1590
+ // Log that the mutex has been acquired
1591
+ Logger.log(`${name} acquired mutex`);
1592
+ return task().pipe(finalize(() => {
1593
+ Logger.log(`${name} releasing mutex`);
1594
+ release();
1595
+ }));
1596
+ }));
1597
+ }
1598
+ }
1599
+
1600
+ const cache = {};
1601
+ /**
1602
+ * Converts a given value to a string suitable for search comparisons.
1603
+ * The resulting string is in lowercase, with consecutive spaces replaced by underscores.
1604
+ * The function uses a cache to improve performance by storing and reusing the results of previous conversions.
1605
+ *
1606
+ * @param {any} value - The value to be converted to a search-friendly string.
1607
+ * It can be of any type, but it will be coerced to a string if not already one.
1608
+ *
1609
+ * @param {boolean} caseSensitive - A flag indicating whether the comparison should be case-sensitive.
1610
+ * If set to true, the function will perform case-sensitive comparisons, preserving the original casing of input strings.
1611
+ * If set to false (default), the function will convert the input strings to lowercase for case-insensitive comparisons
1612
+ *
1613
+ * @returns {string} The processed string, which is trimmed, converted to lowercase (unless caseSensitive is true),
1614
+ * and has all sequences of spaces replaced with underscores. This string is either
1615
+ * retrieved from the cache or freshly computed and then cached.
1616
+ *
1617
+ */
1618
+ function textForSearch(value, caseSensitive = false) {
1619
+ const key = `${value}_${caseSensitive}`;
1620
+ return (cache[key] ||
1621
+ (() => {
1622
+ let result = String(value).trim().replace(/ +/gi, '_');
1623
+ if (!caseSensitive) {
1624
+ result = result.toLowerCase();
1625
+ }
1626
+ cache[key] = result;
1627
+ return result;
1628
+ })());
1629
+ }
1630
+
1631
+ /**
1632
+ * Creates a predicate function to filter records from a collection based on specified criteria.
1633
+ *
1634
+ * The `where` function accepts a criteria object with key-value pairs, indicating the fields
1635
+ * and their expected values for filtering. It returns a predicate function that filters a collection
1636
+ * of records. This predicate checks if records match the criteria using a case-insensitive comparison,
1637
+ * making it particularly useful in conjunction with the `textForSearch` function, which standardizes
1638
+ * strings for such comparisons.
1639
+ *
1640
+ * Each criterion value is first processed with `textForSearch` to normalize spacing and case,
1641
+ * ensuring consistent and predictable filtering. The comparison between each record's field value
1642
+ * and the criterion value is thus case-insensitive and ignores extra spaces.
1643
+ *
1644
+ * @param {Partial<Record<K, T[K]>>} criteria - An object representing the filtering criteria.
1645
+ * Keys correspond to the record's fields to be filtered, and values are the expected values for those fields,
1646
+ * processed in a case-insensitive manner. Undefined values are ignored.
1647
+ *
1648
+ * @param {boolean} caseSensitive - A flag indicating whether the comparison should be case-sensitive.
1649
+ * If set to true, the function will perform case-sensitive comparisons, preserving the original casing of input strings.
1650
+ * If set to false (default), the function will convert the input strings to lowercase for case-insensitive comparisons
1651
+ *
1652
+ * @returns {(record: T) => boolean} A predicate function that takes a record of type `T`
1653
+ * and returns `true` if the record's field values match all criteria, otherwise `false`.
1654
+ *
1655
+ * @example
1656
+ * // Define a collection of items
1657
+ * const items = [
1658
+ * { name: "Apple", category: "Fruit" },
1659
+ * { name: "Carrot", category: "Vegetable" },
1660
+ * { name: "Banana", category: "Fruit" }
1661
+ * ];
1662
+ *
1663
+ * // Create a predicate to find fruits, using a case-insensitive comparison
1664
+ * const isFruit = where({ category: "fruit" }); // 'fruit' will match 'Fruit' in items
1665
+ *
1666
+ * // Filter the collection with the predicate
1667
+ * const fruits = items.filter(isFruit);
1668
+ * // Output: [{ name: "Apple", category: "Fruit" }, { name: "Banana", category: "Fruit" }]
1669
+ *
1670
+ * @typeparam T - Specifies the type of records in the collection to be filtered. It extends `object`,
1671
+ * ensuring the function is only used with object types. This generic approach allows for flexible usage
1672
+ * with various record structures.
1673
+ */
1674
+ function where(criteria, caseSensitive = false) {
1675
+ const preComputedEntries = Object.entries(criteria)
1676
+ .filter(([, value]) => value !== undefined)
1677
+ .map(([key, value]) => [key, textForSearch(value, caseSensitive)]);
1678
+ return (record) => {
1679
+ return (preComputedEntries.length > 0 &&
1680
+ preComputedEntries.every(([key, criterionValue]) => {
1681
+ return (textForSearch(record[key] ?? '', caseSensitive) ===
1682
+ criterionValue);
1683
+ }));
1684
+ };
1685
+ }
1686
+ function whereNot(criteria, caseSensitive = false) {
1687
+ const whereFunc = where(criteria, caseSensitive);
1688
+ return (record) => {
1689
+ return !whereFunc(record);
1690
+ };
1691
+ }
1692
+
1693
+ /**
1694
+ * Emits the most recent value emitted by the source Observable after a
1695
+ * specified time span has passed without another source emission.
1696
+ *
1697
+ * This is a combination of `debounceTime` and `auditTime` operators.
1698
+ *
1699
+ * The leading value is emitted immediately, and trailing values are debounced.
1700
+ *
1701
+ * @param dueTime The time to wait before emitting the last value.
1702
+ * @param scheduler The scheduler to use for the timeout.
1703
+ * @returns A function that returns an Observable that mirrors the source Observable, but applies the specified debouncing.
1704
+ */
1705
+ function leadingTrailingDebounceTime(dueTime, scheduler = asyncScheduler) {
1706
+ return (source) => new Observable((subscriber) => {
1707
+ let hasValue = false;
1708
+ let lastValue = null;
1709
+ let timerSubscription = null;
1710
+ let leading = true; // Controls whether we emit immediately.
1711
+ const clearTimer = () => {
1712
+ if (timerSubscription) {
1713
+ timerSubscription.unsubscribe();
1714
+ timerSubscription = null;
1715
+ }
1716
+ };
1717
+ const resetValues = () => {
1718
+ hasValue = false;
1719
+ lastValue = null;
1720
+ leading = true; // Reset so the next new value is immediate.
1721
+ };
1722
+ const emitLastValue = () => {
1723
+ if (hasValue && lastValue !== null) {
1724
+ subscriber.next(lastValue);
1725
+ resetValues();
1726
+ }
1727
+ };
1728
+ const sourceSubscription = source.subscribe({
1729
+ next: (value) => {
1730
+ // If we are "leading", emit immediately and switch leading off
1731
+ if (leading) {
1732
+ subscriber.next(value);
1733
+ leading = false;
1734
+ timerSubscription = timer(dueTime, scheduler).subscribe(() => {
1735
+ resetValues();
1736
+ clearTimer();
1737
+ });
1738
+ return;
1739
+ }
1740
+ // Otherwise, save the value and start (or restart) timer
1741
+ hasValue = true;
1742
+ lastValue = value;
1743
+ clearTimer();
1744
+ timerSubscription = timer(dueTime, scheduler).subscribe(() => {
1745
+ emitLastValue();
1746
+ });
1747
+ },
1748
+ error: (err) => {
1749
+ clearTimer();
1750
+ subscriber.error(err);
1751
+ },
1752
+ complete: () => {
1753
+ // If there is a pending emission, emit it before completing
1754
+ clearTimer();
1755
+ emitLastValue();
1756
+ subscriber.complete();
1757
+ },
1758
+ });
1759
+ // Cleanup logic
1760
+ return () => {
1761
+ clearTimer();
1762
+ sourceSubscription.unsubscribe();
1763
+ };
1764
+ });
1765
+ }
1766
+
1767
+ /**
1768
+ * Creates an observable that emits values transitioning smoothly from the start value to the end value over a specified duration.
1769
+ * The transition is performed using linear interpolation (lerp) and clamped to the range [0, 1].
1770
+ *
1771
+ * @param {number} start - The starting value of the transition.
1772
+ * @param {number} end - The ending value of the transition.
1773
+ * @param {number} duration - The duration of the transition in milliseconds.
1774
+ * @returns {Observable<number>} An observable that emits the interpolated values from start to end over the specified duration.
1775
+ */
1776
+ const smoothTransition = (start, end, duration) => {
1777
+ const startTime = animationFrameScheduler.now();
1778
+ return interval(0, animationFrameScheduler).pipe(map(() => {
1779
+ const elapsed = (animationFrameScheduler.now() - startTime) / duration;
1780
+ return clampf(0, 1, elapsed);
1781
+ }), distinctUntilChanged(), map((fraction) => lerp(start, end, fraction)));
1782
+ };
1783
+
1784
+ const tapLog = (text, ...args) => (source) => source.pipe(tap((data) => {
1785
+ Logger.colored(...COLOR_CODES.TAP_LOG, text, data, ...args);
1786
+ }));
1787
+
1788
+ /**
1789
+ * Generated bundle index. Do not edit.
1790
+ */
1791
+
1792
+ export { BatchLoader, CMYKtoRGB, COLOR_CODES, CSV2Array, CSV2Records, DownloadImage, Falsy, HEXtoRGB, HSVtoRGB, ImageOutput, InvertedKeyMap, IsEmpty, KeyboardNumericCode, KeyboardStringCode, Logger, Mutex, NotEmpty, ObjectToCSV, RGBtoCMYK, RGBtoHEX, RGBtoHSV, SaveImage, Semaphore, TaskRunner, Truthy, baseSortedIndex, calculateMedian, circularIndex, clampf, cleanupFileName, degreesToRadians, expandOverRectangle, fitIntoRectangle, fpIsAGreaterThanB, fpIsALessThanB, fpIsASameAsB, getCanvasCached, getSnapshot, hsv, inverseLerp, isLocalhost, leadingTrailingDebounceTime, lerp, loadImage, makePath, max, min, normalizePath, overlay, pad, radiansToDegrees, rgb, serialize, sleep, smoothTransition, sub, subtract, sum, tapLog, textForSearch, timeToString, timeUTCToString, toRGB, toRGBA, trimLastSlashFromUrl, where, whereNot };
1793
+ //# sourceMappingURL=3dsource-utils.mjs.map