@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.
- package/Array.ts +1362 -0
- package/Async.ts +207 -0
- package/AsyncBuilder.ts +222 -0
- package/BigInt.ts +337 -0
- package/BitConverter.ts +165 -0
- package/Boolean.ts +22 -0
- package/CHANGELOG.md +15 -0
- package/Char.ts +222 -0
- package/Choice.ts +300 -0
- package/Date.ts +495 -0
- package/DateOffset.ts +324 -0
- package/DateOnly.ts +146 -0
- package/Decimal.ts +250 -0
- package/Double.ts +55 -0
- package/Encoding.ts +170 -0
- package/Event.ts +119 -0
- package/FSharp.Collections.ts +34 -0
- package/FSharp.Core.CompilerServices.ts +37 -0
- package/FSharp.Core.ts +86 -0
- package/Global.ts +37 -0
- package/Guid.ts +143 -0
- package/Int32.ts +156 -0
- package/List.ts +1417 -0
- package/Long.ts +49 -0
- package/MailboxProcessor.ts +125 -0
- package/Map.ts +1552 -0
- package/MapUtil.ts +120 -0
- package/MutableMap.ts +344 -0
- package/MutableSet.ts +248 -0
- package/Native.ts +11 -0
- package/Numeric.ts +80 -0
- package/Observable.ts +156 -0
- package/Option.ts +137 -0
- package/README.md +3 -0
- package/Random.ts +196 -0
- package/Range.ts +56 -0
- package/Reflection.ts +539 -0
- package/RegExp.ts +143 -0
- package/Result.ts +196 -0
- package/Seq.ts +1526 -0
- package/Seq2.ts +129 -0
- package/Set.ts +1955 -0
- package/String.ts +589 -0
- package/System.Collections.Generic.ts +380 -0
- package/System.Text.ts +137 -0
- package/SystemException.ts +7 -0
- package/TimeOnly.ts +131 -0
- package/TimeSpan.ts +194 -0
- package/Timer.ts +80 -0
- package/Types.ts +231 -0
- package/Unicode.13.0.0.ts +4 -0
- package/Uri.ts +206 -0
- package/Util.ts +928 -0
- package/lib/big.d.ts +338 -0
- package/lib/big.js +1054 -0
- package/package.json +24 -0
- 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
|
+
}
|