@fable-org/fable-library-ts 1.0.0-beta-001

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 (57) hide show
  1. package/Array.ts +1362 -0
  2. package/Async.ts +207 -0
  3. package/AsyncBuilder.ts +222 -0
  4. package/BigInt.ts +337 -0
  5. package/BitConverter.ts +165 -0
  6. package/Boolean.ts +22 -0
  7. package/CHANGELOG.md +15 -0
  8. package/Char.ts +222 -0
  9. package/Choice.ts +300 -0
  10. package/Date.ts +495 -0
  11. package/DateOffset.ts +324 -0
  12. package/DateOnly.ts +146 -0
  13. package/Decimal.ts +250 -0
  14. package/Double.ts +55 -0
  15. package/Encoding.ts +170 -0
  16. package/Event.ts +119 -0
  17. package/FSharp.Collections.ts +34 -0
  18. package/FSharp.Core.CompilerServices.ts +37 -0
  19. package/FSharp.Core.ts +86 -0
  20. package/Global.ts +37 -0
  21. package/Guid.ts +143 -0
  22. package/Int32.ts +156 -0
  23. package/List.ts +1417 -0
  24. package/Long.ts +49 -0
  25. package/MailboxProcessor.ts +125 -0
  26. package/Map.ts +1552 -0
  27. package/MapUtil.ts +120 -0
  28. package/MutableMap.ts +344 -0
  29. package/MutableSet.ts +248 -0
  30. package/Native.ts +11 -0
  31. package/Numeric.ts +80 -0
  32. package/Observable.ts +156 -0
  33. package/Option.ts +137 -0
  34. package/README.md +3 -0
  35. package/Random.ts +196 -0
  36. package/Range.ts +56 -0
  37. package/Reflection.ts +539 -0
  38. package/RegExp.ts +143 -0
  39. package/Result.ts +196 -0
  40. package/Seq.ts +1526 -0
  41. package/Seq2.ts +129 -0
  42. package/Set.ts +1955 -0
  43. package/String.ts +589 -0
  44. package/System.Collections.Generic.ts +380 -0
  45. package/System.Text.ts +137 -0
  46. package/SystemException.ts +7 -0
  47. package/TimeOnly.ts +131 -0
  48. package/TimeSpan.ts +194 -0
  49. package/Timer.ts +80 -0
  50. package/Types.ts +231 -0
  51. package/Unicode.13.0.0.ts +4 -0
  52. package/Uri.ts +206 -0
  53. package/Util.ts +928 -0
  54. package/lib/big.d.ts +338 -0
  55. package/lib/big.js +1054 -0
  56. package/package.json +24 -0
  57. package/tsconfig.json +103 -0
package/String.ts ADDED
@@ -0,0 +1,589 @@
1
+ import { toString as dateToString } from "./Date.js";
2
+ import { compare as numericCompare, isNumeric, multiply, Numeric, toExponential, toFixed, toHex, toPrecision } from "./Numeric.js";
3
+ import { escape } from "./RegExp.js";
4
+ import { toString } from "./Types.js";
5
+
6
+ const fsFormatRegExp = /(^|[^%])%([0+\- ]*)(\*|\d+)?(?:\.(\d+))?(\w)/g;
7
+ const interpolateRegExp = /(?:(^|[^%])%([0+\- ]*)(\d+)?(?:\.(\d+))?(\w))?%P\(\)/g;
8
+ const formatRegExp = /\{(\d+)(,-?\d+)?(?:\:([a-zA-Z])(\d{0,2})|\:(.+?))?\}/g;
9
+
10
+ function isLessThan(x: Numeric, y: number) {
11
+ return numericCompare(x, y) < 0;
12
+ }
13
+
14
+ const enum StringComparison {
15
+ CurrentCulture = 0,
16
+ CurrentCultureIgnoreCase = 1,
17
+ InvariantCulture = 2,
18
+ InvariantCultureIgnoreCase = 3,
19
+ Ordinal = 4,
20
+ OrdinalIgnoreCase = 5,
21
+ }
22
+
23
+ function cmp(x: string, y: string, ic: boolean | StringComparison) {
24
+ function isIgnoreCase(i: boolean | StringComparison) {
25
+ return i === true ||
26
+ i === StringComparison.CurrentCultureIgnoreCase ||
27
+ i === StringComparison.InvariantCultureIgnoreCase ||
28
+ i === StringComparison.OrdinalIgnoreCase;
29
+ }
30
+ function isOrdinal(i: boolean | StringComparison) {
31
+ return i === StringComparison.Ordinal ||
32
+ i === StringComparison.OrdinalIgnoreCase;
33
+ }
34
+ if (x == null) { return y == null ? 0 : -1; }
35
+ if (y == null) { return 1; } // everything is bigger than null
36
+
37
+ if (isOrdinal(ic)) {
38
+ if (isIgnoreCase(ic)) { x = x.toLowerCase(); y = y.toLowerCase(); }
39
+ return (x === y) ? 0 : (x < y ? -1 : 1);
40
+ } else {
41
+ if (isIgnoreCase(ic)) { x = x.toLocaleLowerCase(); y = y.toLocaleLowerCase(); }
42
+ return x.localeCompare(y);
43
+ }
44
+ }
45
+
46
+ export function compare(...args: any[]): number {
47
+ switch (args.length) {
48
+ case 2: return cmp(args[0], args[1], false);
49
+ case 3: return cmp(args[0], args[1], args[2]);
50
+ case 4: return cmp(args[0], args[1], args[2] === true);
51
+ case 5: return cmp(args[0].substr(args[1], args[4]), args[2].substr(args[3], args[4]), false);
52
+ case 6: return cmp(args[0].substr(args[1], args[4]), args[2].substr(args[3], args[4]), args[5]);
53
+ case 7: return cmp(args[0].substr(args[1], args[4]), args[2].substr(args[3], args[4]), args[5] === true);
54
+ default: throw new Error("String.compare: Unsupported number of parameters");
55
+ }
56
+ }
57
+
58
+ export function compareOrdinal(x: string, y: string) {
59
+ return cmp(x, y, StringComparison.Ordinal);
60
+ }
61
+
62
+ export function compareTo(x: string, y: string) {
63
+ return cmp(x, y, StringComparison.CurrentCulture);
64
+ }
65
+
66
+ export function startsWith(str: string, pattern: string, ic: number) {
67
+ if (str.length >= pattern.length) {
68
+ return cmp(str.substr(0, pattern.length), pattern, ic) === 0;
69
+ }
70
+ return false;
71
+ }
72
+
73
+ export function indexOfAny(str: string, anyOf: string[], ...args: number[]) {
74
+ if (str == null || str === "") {
75
+ return -1;
76
+ }
77
+ const startIndex = (args.length > 0) ? args[0] : 0;
78
+ if (startIndex < 0) {
79
+ throw new Error("Start index cannot be negative");
80
+ }
81
+ const length = (args.length > 1) ? args[1] : str.length - startIndex;
82
+ if (length < 0) {
83
+ throw new Error("Length cannot be negative");
84
+ }
85
+ if (startIndex + length > str.length) {
86
+ throw new Error("Invalid startIndex and length");
87
+ }
88
+ const endIndex = startIndex + length
89
+ const anyOfAsStr = "".concat.apply("", anyOf);
90
+ for (let i=startIndex; i<endIndex; i++) {
91
+ if (anyOfAsStr.indexOf(str[i]) > -1) {
92
+ return i;
93
+ }
94
+ }
95
+ return -1;
96
+ }
97
+
98
+ export type IPrintfFormatContinuation =
99
+ (f: (x: string) => any) => any;
100
+
101
+ export interface IPrintfFormat {
102
+ input: string;
103
+ cont: IPrintfFormatContinuation;
104
+ }
105
+
106
+ export function printf(input: string): IPrintfFormat {
107
+ return {
108
+ input,
109
+ cont: fsFormat(input) as IPrintfFormatContinuation,
110
+ };
111
+ }
112
+
113
+ export function interpolate(str: string, values: any[]): string {
114
+ let valIdx = 0;
115
+ let strIdx = 0;
116
+ let result = "";
117
+ interpolateRegExp.lastIndex = 0;
118
+ let match = interpolateRegExp.exec(str);
119
+ while (match) {
120
+ // The first group corresponds to the no-escape char (^|[^%]), the actual pattern starts in the next char
121
+ // Note: we don't use negative lookbehind because some browsers don't support it yet
122
+ const matchIndex = match.index + (match[1] || "").length;
123
+ result += str.substring(strIdx, matchIndex).replace(/%%/g, "%");
124
+ const [, , flags, padLength, precision, format] = match;
125
+ // Save interpolateRegExp.lastIndex before running formatReplacement because the values
126
+ // may also involve interpolation and make use of interpolateRegExp (see #3078)
127
+ strIdx = interpolateRegExp.lastIndex;
128
+ result += formatReplacement(values[valIdx++], flags, padLength, precision, format);
129
+ // Move interpolateRegExp.lastIndex one char behind to make sure we match the no-escape char next time
130
+ interpolateRegExp.lastIndex = strIdx - 1;
131
+ match = interpolateRegExp.exec(str);
132
+ }
133
+ result += str.substring(strIdx).replace(/%%/g, "%");
134
+ return result;
135
+ }
136
+
137
+ function continuePrint(cont: (x: string) => any, arg: IPrintfFormat | string) {
138
+ return typeof arg === "string" ? cont(arg) : arg.cont(cont);
139
+ }
140
+
141
+ export function toConsole(arg: IPrintfFormat | string) {
142
+ // Don't remove the lambda here, see #1357
143
+ return continuePrint((x: string) => console.log(x), arg);
144
+ }
145
+
146
+ export function toConsoleError(arg: IPrintfFormat | string) {
147
+ return continuePrint((x: string) => console.error(x), arg);
148
+ }
149
+
150
+ export function toText(arg: IPrintfFormat | string) {
151
+ return continuePrint((x: string) => x, arg);
152
+ }
153
+
154
+ export function toFail(arg: IPrintfFormat | string) {
155
+ return continuePrint((x: string) => {
156
+ throw new Error(x);
157
+ }, arg);
158
+ }
159
+
160
+ function formatReplacement(rep: any, flags: any, padLength: any, precision: any, format: any) {
161
+ let sign = "";
162
+ flags = flags || "";
163
+ format = format || "";
164
+ if (isNumeric(rep)) {
165
+ if (format.toLowerCase() !== "x") {
166
+ if (isLessThan(rep, 0)) {
167
+ rep = multiply(rep, -1);
168
+ sign = "-";
169
+ } else {
170
+ if (flags.indexOf(" ") >= 0) {
171
+ sign = " ";
172
+ } else if (flags.indexOf("+") >= 0) {
173
+ sign = "+";
174
+ }
175
+ }
176
+ }
177
+ precision = precision == null ? null : parseInt(precision, 10);
178
+ switch (format) {
179
+ case "f": case "F":
180
+ precision = precision != null ? precision : 6;
181
+ rep = toFixed(rep, precision);
182
+ break;
183
+ case "g": case "G":
184
+ rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
185
+ break;
186
+ case "e": case "E":
187
+ rep = precision != null ? toExponential(rep, precision) : toExponential(rep);
188
+ break;
189
+ case "x":
190
+ rep = toHex(rep);
191
+ break;
192
+ case "X":
193
+ rep = toHex(rep).toUpperCase();
194
+ break;
195
+ default: // AOid
196
+ rep = String(rep);
197
+ break;
198
+ }
199
+ } else if (rep instanceof Date) {
200
+ rep = dateToString(rep);
201
+ } else {
202
+ rep = toString(rep);
203
+ }
204
+ padLength = typeof padLength === "number" ? padLength : parseInt(padLength, 10);
205
+ if (!isNaN(padLength)) {
206
+ const zeroFlag = flags.indexOf("0") >= 0; // Use '0' for left padding
207
+ const minusFlag = flags.indexOf("-") >= 0; // Right padding
208
+ const ch = minusFlag || !zeroFlag ? " " : "0";
209
+ if (ch === "0") {
210
+ rep = pad(rep, padLength - sign.length, ch, minusFlag);
211
+ rep = sign + rep;
212
+ } else {
213
+ rep = pad(sign + rep, padLength, ch, minusFlag);
214
+ }
215
+ } else {
216
+ rep = sign + rep;
217
+ }
218
+ return rep;
219
+ }
220
+
221
+ function createPrinter(cont: (...args: any[]) => any, _strParts: string[], _matches: RegExpExecArray[], _result = "", padArg = -1) {
222
+ return (...args: any[]) => {
223
+ // Make copies of the values passed by reference because the function can be used multiple times
224
+ let result = _result;
225
+ const strParts = _strParts.slice();
226
+ const matches = _matches.slice();
227
+ for (const arg of args) {
228
+ const [, , flags, _padLength, precision, format] = matches[0];
229
+ let padLength: any = _padLength;
230
+ if (padArg >= 0) {
231
+ padLength = padArg;
232
+ padArg = -1;
233
+ }
234
+ else if (padLength === "*") {
235
+ if (arg < 0) {
236
+ throw new Error("Non-negative number required");
237
+ }
238
+ padArg = arg;
239
+ continue;
240
+ }
241
+
242
+ result += strParts[0];
243
+ result += formatReplacement(arg, flags, padLength, precision, format);
244
+
245
+ strParts.splice(0, 1);
246
+ matches.splice(0, 1);
247
+ }
248
+
249
+ if (matches.length === 0) {
250
+ result += strParts[0];
251
+ return cont(result);
252
+ } else {
253
+ return createPrinter(cont, strParts, matches, result, padArg);
254
+ }
255
+ };
256
+ }
257
+
258
+ export function fsFormat(str: string) {
259
+ return (cont: (...args: any[]) => any) => {
260
+ fsFormatRegExp.lastIndex = 0;
261
+ const strParts: string[] = [];
262
+ const matches: RegExpExecArray[] = [];
263
+ let strIdx = 0;
264
+ let match = fsFormatRegExp.exec(str);
265
+ while (match) {
266
+ // The first group corresponds to the no-escape char (^|[^%]), the actual pattern starts in the next char
267
+ // Note: we don't use negative lookbehind because some browsers don't support it yet
268
+ const matchIndex = match.index + (match[1] || "").length;
269
+ strParts.push(str.substring(strIdx, matchIndex).replace(/%%/g, "%"));
270
+ matches.push(match)
271
+ strIdx = fsFormatRegExp.lastIndex;
272
+ // Likewise we need to move fsFormatRegExp.lastIndex one char behind to make sure we match the no-escape char next time
273
+ fsFormatRegExp.lastIndex -= 1;
274
+ match = fsFormatRegExp.exec(str);
275
+ }
276
+ if (strParts.length === 0) {
277
+ return cont(str.replace(/%%/g, "%"));
278
+ } else {
279
+ strParts.push(str.substring(strIdx).replace(/%%/g, "%"))
280
+ return createPrinter(cont, strParts, matches);
281
+ }
282
+ };
283
+ }
284
+
285
+ export function format(str: string | object, ...args: any[]) {
286
+ let str2: string;
287
+ if (typeof str === "object") {
288
+ // Called with culture info
289
+ str2 = String(args[0]);
290
+ args.shift();
291
+ } else {
292
+ str2 = str;
293
+ }
294
+
295
+ return str2.replace(formatRegExp, (_, idx: number, padLength, format, precision, pattern) => {
296
+ if (idx < 0 || idx >= args.length) {
297
+ throw new Error("Index must be greater or equal to zero and less than the arguments' length.")
298
+ }
299
+ let rep = args[idx];
300
+ if (isNumeric(rep)) {
301
+ precision = precision == null ? null : parseInt(precision, 10);
302
+ switch (format) {
303
+ case "f": case "F":
304
+ precision = precision != null ? precision : 2;
305
+ rep = toFixed(rep, precision);
306
+ break;
307
+ case "g": case "G":
308
+ rep = precision != null ? toPrecision(rep, precision) : toPrecision(rep);
309
+ break;
310
+ case "e": case "E":
311
+ rep = precision != null ? toExponential(rep, precision) : toExponential(rep);
312
+ break;
313
+ case "p": case "P":
314
+ precision = precision != null ? precision : 2;
315
+ rep = toFixed(multiply(rep, 100), precision) + " %";
316
+ break;
317
+ case "d": case "D":
318
+ rep = precision != null ? padLeft(String(rep), precision, "0") : String(rep);
319
+ break;
320
+ case "x": case "X":
321
+ rep = precision != null ? padLeft(toHex(rep), precision, "0") : toHex(rep);
322
+ if (format === "X") { rep = rep.toUpperCase(); }
323
+ break;
324
+ default:
325
+ if (pattern) {
326
+ let sign = "";
327
+ rep = (pattern as string).replace(/([0#,]+)(\.[0#]+)?/, (_, intPart: string, decimalPart: string) => {
328
+ if (isLessThan(rep, 0)) {
329
+ rep = multiply(rep, -1);
330
+ sign = "-";
331
+ }
332
+ decimalPart = decimalPart == null ? "" : decimalPart.substring(1);
333
+ rep = toFixed(rep, Math.max(decimalPart.length, 0));
334
+
335
+ let [repInt, repDecimal] = (rep as string).split(".");
336
+ repDecimal ||= "";
337
+
338
+ const leftZeroes = intPart.replace(/,/g, "").replace(/^#+/, "").length;
339
+ repInt = padLeft(repInt, leftZeroes, "0");
340
+
341
+ const rightZeros = decimalPart.replace(/#+$/, "").length;
342
+ if (rightZeros > repDecimal.length) {
343
+ repDecimal = padRight(repDecimal, rightZeros, "0");
344
+ } else if (rightZeros < repDecimal.length) {
345
+ repDecimal = repDecimal.substring(0, rightZeros) + repDecimal.substring(rightZeros).replace(/0+$/, "");
346
+ }
347
+
348
+ // Thousands separator
349
+ if (intPart.indexOf(",") > 0) {
350
+ const i = repInt.length % 3;
351
+ const thousandGroups = Math.floor(repInt.length / 3);
352
+ let thousands = i > 0 ? repInt.substr(0, i) + (thousandGroups > 0 ? "," : "") : "";
353
+ for (let j = 0; j < thousandGroups; j++) {
354
+ thousands += repInt.substr(i + j * 3, 3) + (j < thousandGroups - 1 ? "," : "");
355
+ }
356
+ repInt = thousands;
357
+ }
358
+
359
+ return repDecimal.length > 0 ? repInt + "." + repDecimal : repInt;
360
+ });
361
+ rep = sign + rep;
362
+ }
363
+ }
364
+ } else if (rep instanceof Date) {
365
+ rep = dateToString(rep, pattern || format);
366
+ } else {
367
+ rep = toString(rep)
368
+ }
369
+ padLength = parseInt((padLength || " ").substring(1), 10);
370
+ if (!isNaN(padLength)) {
371
+ rep = pad(String(rep), Math.abs(padLength), " ", padLength < 0);
372
+ }
373
+ return rep;
374
+ });
375
+ }
376
+
377
+ export function endsWith(str: string, search: string) {
378
+ const idx = str.lastIndexOf(search);
379
+ return idx >= 0 && idx === str.length - search.length;
380
+ }
381
+
382
+ export function initialize(n: number, f: (i: number) => string) {
383
+ if (n < 0) {
384
+ throw new Error("String length must be non-negative");
385
+ }
386
+ const xs = new Array(n);
387
+ for (let i = 0; i < n; i++) {
388
+ xs[i] = f(i);
389
+ }
390
+ return xs.join("");
391
+ }
392
+
393
+ export function insert(str: string, startIndex: number, value: string) {
394
+ if (startIndex < 0 || startIndex > str.length) {
395
+ throw new Error("startIndex is negative or greater than the length of this instance.");
396
+ }
397
+ return str.substring(0, startIndex) + value + str.substring(startIndex);
398
+ }
399
+
400
+ export function isNullOrEmpty(str: string | any) {
401
+ return typeof str !== "string" || str.length === 0;
402
+ }
403
+
404
+ export function isNullOrWhiteSpace(str: string | any) {
405
+ return typeof str !== "string" || /^\s*$/.test(str);
406
+ }
407
+
408
+ export function concat(...xs: any[]): string {
409
+ return xs.map((x) => String(x)).join("");
410
+ }
411
+
412
+ export function join<T>(delimiter: string, xs: Iterable<T>): string {
413
+ if (Array.isArray(xs)) {
414
+ return xs.join(delimiter);
415
+ } else {
416
+ return Array.from(xs).join(delimiter);
417
+ }
418
+ }
419
+
420
+ export function joinWithIndices(delimiter: string, xs: string[], startIndex: number, count: number) {
421
+ const endIndexPlusOne = startIndex + count;
422
+ if (endIndexPlusOne > xs.length) {
423
+ throw new Error("Index and count must refer to a location within the buffer.");
424
+ }
425
+ return xs.slice(startIndex, endIndexPlusOne).join(delimiter);
426
+ }
427
+
428
+ function notSupported(name: string): never {
429
+ throw new Error("The environment doesn't support '" + name + "', please use a polyfill.");
430
+ }
431
+
432
+ export function toBase64String(inArray: ArrayLike<number>) {
433
+ let str = "";
434
+ for (let i = 0; i < inArray.length; i++) {
435
+ str += String.fromCharCode(inArray[i]);
436
+ }
437
+ return typeof btoa === "function" ? btoa(str) : notSupported("btoa");
438
+ }
439
+
440
+ export function fromBase64String(b64Encoded: string): number[] {
441
+ const binary = typeof atob === "function" ? atob(b64Encoded) : notSupported("atob");
442
+ const bytes = new Uint8Array(binary.length);
443
+ for (let i = 0; i < binary.length; i++) {
444
+ bytes[i] = binary.charCodeAt(i);
445
+ }
446
+ return bytes as any as number[];
447
+ }
448
+
449
+ function pad(str: string, len: number, ch?: string, isRight?: boolean) {
450
+ ch = ch || " ";
451
+ len = len - str.length;
452
+ for (let i = 0; i < len; i++) {
453
+ str = isRight ? str + ch : ch + str;
454
+ }
455
+ return str;
456
+ }
457
+
458
+ export function padLeft(str: string, len: number, ch?: string) {
459
+ return pad(str, len, ch);
460
+ }
461
+
462
+ export function padRight(str: string, len: number, ch?: string) {
463
+ return pad(str, len, ch, true);
464
+ }
465
+
466
+ export function remove(str: string, startIndex: number, count?: number) {
467
+ if (startIndex >= str.length) {
468
+ throw new Error("startIndex must be less than length of string");
469
+ }
470
+ if (typeof count === "number" && (startIndex + count) > str.length) {
471
+ throw new Error("Index and count must refer to a location within the string.");
472
+ }
473
+ return str.slice(0, startIndex) + (typeof count === "number" ? str.substr(startIndex + count) : "");
474
+ }
475
+
476
+ export function replace(str: string, search: string, replace: string) {
477
+ return str.replace(new RegExp(escape(search), "g"), replace);
478
+ }
479
+
480
+ export function replicate(n: number, x: string) {
481
+ return initialize(n, () => x);
482
+ }
483
+
484
+ export function getCharAtIndex(input: string, index: number) {
485
+ if (index < 0 || index >= input.length) {
486
+ throw new Error("Index was outside the bounds of the array.");
487
+ }
488
+ return input[index];
489
+ }
490
+
491
+ export function split(str: string, splitters: string[], count?: number, options?: number) {
492
+ count = typeof count === "number" ? count : undefined;
493
+ options = typeof options === "number" ? options : 0;
494
+ if (count && count < 0) {
495
+ throw new Error("Count cannot be less than zero");
496
+ }
497
+ if (count === 0) {
498
+ return [];
499
+ }
500
+
501
+ const removeEmpty = (options & 1) === 1;
502
+ const trim = (options & 2) === 2;
503
+
504
+ splitters = splitters || [];
505
+ splitters = splitters.filter(x => x).map(escape);
506
+ splitters = splitters.length > 0 ? splitters : ["\\s"];
507
+
508
+ const splits: string[] = [];
509
+ const reg = new RegExp(splitters.join("|"), "g");
510
+
511
+ let findSplits = true;
512
+ let i = 0;
513
+ do {
514
+ const match = reg.exec(str);
515
+
516
+ if (match === null) {
517
+ const candidate = trim ? str.substring(i).trim() : str.substring(i);
518
+ if (!removeEmpty || candidate.length > 0) {
519
+ splits.push(candidate);
520
+ }
521
+ findSplits = false;
522
+ } else {
523
+ const candidate = trim ? str.substring(i, match.index).trim() : str.substring(i, match.index);
524
+ if (!removeEmpty || candidate.length > 0) {
525
+ if (count != null && splits.length + 1 === count) {
526
+ splits.push(trim ? str.substring(i).trim() : str.substring(i));
527
+ findSplits = false;
528
+ } else {
529
+ splits.push(candidate);
530
+ }
531
+ }
532
+
533
+ i = reg.lastIndex;
534
+ }
535
+ } while (findSplits)
536
+
537
+ return splits;
538
+ }
539
+
540
+ export function trim(str: string, ...chars: string[]) {
541
+ if (chars.length === 0) {
542
+ return str.trim();
543
+ }
544
+ const pattern = "[" + escape(chars.join("")) + "]+";
545
+ return str.replace(new RegExp("^" + pattern), "").replace(new RegExp(pattern + "$"), "");
546
+ }
547
+
548
+ export function trimStart(str: string, ...chars: string[]) {
549
+ return chars.length === 0
550
+ ? (str as any).trimStart()
551
+ : str.replace(new RegExp("^[" + escape(chars.join("")) + "]+"), "");
552
+ }
553
+
554
+ export function trimEnd(str: string, ...chars: string[]) {
555
+ return chars.length === 0
556
+ ? (str as any).trimEnd()
557
+ : str.replace(new RegExp("[" + escape(chars.join("")) + "]+$"), "");
558
+ }
559
+
560
+ export function filter(pred: (char: string) => boolean, x: string) {
561
+ return x.split("").filter((c) => pred(c)).join("");
562
+ }
563
+
564
+ export function substring(str: string, startIndex: number, length?: number) {
565
+ if ((startIndex + (length || 0) > str.length)) {
566
+ throw new Error("Invalid startIndex and/or length");
567
+ }
568
+ return length != null ? str.substr(startIndex, length) : str.substr(startIndex);
569
+ }
570
+
571
+ interface FormattableString {
572
+ strs: TemplateStringsArray,
573
+ args: any[],
574
+ fmts?: string[]
575
+ }
576
+
577
+ export function fmt(strs: TemplateStringsArray, ...args: any[]): FormattableString {
578
+ return ({ strs, args });
579
+ }
580
+
581
+ export function fmtWith(fmts: string[]) {
582
+ return (strs: TemplateStringsArray, ...args: any[]) => ({ strs, args, fmts } as FormattableString);
583
+ }
584
+
585
+ export function getFormat(s: FormattableString) {
586
+ return s.fmts
587
+ ? s.strs.reduce((acc, newPart, index) => acc + `{${String(index - 1) + s.fmts![index - 1]}}` + newPart)
588
+ : s.strs.reduce((acc, newPart, index) => acc + `{${index - 1}}` + newPart);
589
+ }