csv-sort 5.0.16 → 6.0.2

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.
@@ -1,2204 +0,0 @@
1
- /**
2
- * @name csv-sort
3
- * @fileoverview Sorts double-entry bookkeeping CSV coming from internet banking
4
- * @version 5.0.16
5
- * @author Roy Revelt, Codsen Ltd
6
- * @license MIT
7
- * {@link https://codsen.com/os/csv-sort/}
8
- */
9
-
10
- (function (global, factory) {
11
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
12
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
13
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.csvSort = {}));
14
- }(this, (function (exports) { 'use strict';
15
-
16
- /**
17
- * lodash (Custom Build) <https://lodash.com/>
18
- * Build: `lodash modularize exports="npm" -o ./`
19
- * Copyright jQuery Foundation and other contributors <https://jquery.org/>
20
- * Released under MIT license <https://lodash.com/license>
21
- * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
22
- * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
23
- */
24
- /**
25
- * A faster alternative to `Function#apply`, this function invokes `func`
26
- * with the `this` binding of `thisArg` and the arguments of `args`.
27
- *
28
- * @private
29
- * @param {Function} func The function to invoke.
30
- * @param {*} thisArg The `this` binding of `func`.
31
- * @param {Array} args The arguments to invoke `func` with.
32
- * @returns {*} Returns the result of `func`.
33
- */
34
- function apply(func, thisArg, args) {
35
- switch (args.length) {
36
- case 0: return func.call(thisArg);
37
- case 1: return func.call(thisArg, args[0]);
38
- case 2: return func.call(thisArg, args[0], args[1]);
39
- case 3: return func.call(thisArg, args[0], args[1], args[2]);
40
- }
41
- return func.apply(thisArg, args);
42
- }
43
-
44
- /**
45
- * A specialized version of `_.map` for arrays without support for iteratee
46
- * shorthands.
47
- *
48
- * @private
49
- * @param {Array} [array] The array to iterate over.
50
- * @param {Function} iteratee The function invoked per iteration.
51
- * @returns {Array} Returns the new mapped array.
52
- */
53
- function arrayMap(array, iteratee) {
54
- var index = -1,
55
- length = array ? array.length : 0,
56
- result = Array(length);
57
-
58
- while (++index < length) {
59
- result[index] = iteratee(array[index], index, array);
60
- }
61
- return result;
62
- }
63
-
64
- /**
65
- * The base implementation of `_.findIndex` and `_.findLastIndex` without
66
- * support for iteratee shorthands.
67
- *
68
- * @private
69
- * @param {Array} array The array to search.
70
- * @param {Function} predicate The function invoked per iteration.
71
- * @param {number} fromIndex The index to search from.
72
- * @param {boolean} [fromRight] Specify iterating from right to left.
73
- * @returns {number} Returns the index of the matched value, else `-1`.
74
- */
75
- function baseFindIndex$1(array, predicate, fromIndex, fromRight) {
76
- var length = array.length,
77
- index = fromIndex + (fromRight ? 1 : -1);
78
-
79
- while ((fromRight ? index-- : ++index < length)) {
80
- if (predicate(array[index], index, array)) {
81
- return index;
82
- }
83
- }
84
- return -1;
85
- }
86
-
87
- /**
88
- * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
89
- *
90
- * @private
91
- * @param {Array} array The array to search.
92
- * @param {*} value The value to search for.
93
- * @param {number} fromIndex The index to search from.
94
- * @returns {number} Returns the index of the matched value, else `-1`.
95
- */
96
- function baseIndexOf$1(array, value, fromIndex) {
97
- if (value !== value) {
98
- return baseFindIndex$1(array, baseIsNaN$1, fromIndex);
99
- }
100
- var index = fromIndex - 1,
101
- length = array.length;
102
-
103
- while (++index < length) {
104
- if (array[index] === value) {
105
- return index;
106
- }
107
- }
108
- return -1;
109
- }
110
-
111
- /**
112
- * This function is like `baseIndexOf` except that it accepts a comparator.
113
- *
114
- * @private
115
- * @param {Array} array The array to search.
116
- * @param {*} value The value to search for.
117
- * @param {number} fromIndex The index to search from.
118
- * @param {Function} comparator The comparator invoked per element.
119
- * @returns {number} Returns the index of the matched value, else `-1`.
120
- */
121
- function baseIndexOfWith(array, value, fromIndex, comparator) {
122
- var index = fromIndex - 1,
123
- length = array.length;
124
-
125
- while (++index < length) {
126
- if (comparator(array[index], value)) {
127
- return index;
128
- }
129
- }
130
- return -1;
131
- }
132
-
133
- /**
134
- * The base implementation of `_.isNaN` without support for number objects.
135
- *
136
- * @private
137
- * @param {*} value The value to check.
138
- * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
139
- */
140
- function baseIsNaN$1(value) {
141
- return value !== value;
142
- }
143
-
144
- /**
145
- * The base implementation of `_.unary` without support for storing metadata.
146
- *
147
- * @private
148
- * @param {Function} func The function to cap arguments for.
149
- * @returns {Function} Returns the new capped function.
150
- */
151
- function baseUnary(func) {
152
- return function(value) {
153
- return func(value);
154
- };
155
- }
156
-
157
- /** Used for built-in method references. */
158
- var arrayProto = Array.prototype;
159
-
160
- /** Built-in value references. */
161
- var splice = arrayProto.splice;
162
-
163
- /* Built-in method references for those with the same name as other `lodash` methods. */
164
- var nativeMax = Math.max;
165
-
166
- /**
167
- * The base implementation of `_.pullAllBy` without support for iteratee
168
- * shorthands.
169
- *
170
- * @private
171
- * @param {Array} array The array to modify.
172
- * @param {Array} values The values to remove.
173
- * @param {Function} [iteratee] The iteratee invoked per element.
174
- * @param {Function} [comparator] The comparator invoked per element.
175
- * @returns {Array} Returns `array`.
176
- */
177
- function basePullAll(array, values, iteratee, comparator) {
178
- var indexOf = comparator ? baseIndexOfWith : baseIndexOf$1,
179
- index = -1,
180
- length = values.length,
181
- seen = array;
182
-
183
- if (array === values) {
184
- values = copyArray(values);
185
- }
186
- if (iteratee) {
187
- seen = arrayMap(array, baseUnary(iteratee));
188
- }
189
- while (++index < length) {
190
- var fromIndex = 0,
191
- value = values[index],
192
- computed = iteratee ? iteratee(value) : value;
193
-
194
- while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
195
- if (seen !== array) {
196
- splice.call(seen, fromIndex, 1);
197
- }
198
- splice.call(array, fromIndex, 1);
199
- }
200
- }
201
- return array;
202
- }
203
-
204
- /**
205
- * The base implementation of `_.rest` which doesn't validate or coerce arguments.
206
- *
207
- * @private
208
- * @param {Function} func The function to apply a rest parameter to.
209
- * @param {number} [start=func.length-1] The start position of the rest parameter.
210
- * @returns {Function} Returns the new function.
211
- */
212
- function baseRest(func, start) {
213
- start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
214
- return function() {
215
- var args = arguments,
216
- index = -1,
217
- length = nativeMax(args.length - start, 0),
218
- array = Array(length);
219
-
220
- while (++index < length) {
221
- array[index] = args[start + index];
222
- }
223
- index = -1;
224
- var otherArgs = Array(start + 1);
225
- while (++index < start) {
226
- otherArgs[index] = args[index];
227
- }
228
- otherArgs[start] = array;
229
- return apply(func, this, otherArgs);
230
- };
231
- }
232
-
233
- /**
234
- * Copies the values of `source` to `array`.
235
- *
236
- * @private
237
- * @param {Array} source The array to copy values from.
238
- * @param {Array} [array=[]] The array to copy values to.
239
- * @returns {Array} Returns `array`.
240
- */
241
- function copyArray(source, array) {
242
- var index = -1,
243
- length = source.length;
244
-
245
- array || (array = Array(length));
246
- while (++index < length) {
247
- array[index] = source[index];
248
- }
249
- return array;
250
- }
251
-
252
- /**
253
- * Removes all given values from `array` using
254
- * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)
255
- * for equality comparisons.
256
- *
257
- * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
258
- * to remove elements from an array by predicate.
259
- *
260
- * @static
261
- * @memberOf _
262
- * @since 2.0.0
263
- * @category Array
264
- * @param {Array} array The array to modify.
265
- * @param {...*} [values] The values to remove.
266
- * @returns {Array} Returns `array`.
267
- * @example
268
- *
269
- * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
270
- *
271
- * _.pull(array, 'a', 'c');
272
- * console.log(array);
273
- * // => ['b', 'b']
274
- */
275
- var pull = baseRest(pullAll);
276
-
277
- /**
278
- * This method is like `_.pull` except that it accepts an array of values to remove.
279
- *
280
- * **Note:** Unlike `_.difference`, this method mutates `array`.
281
- *
282
- * @static
283
- * @memberOf _
284
- * @since 4.0.0
285
- * @category Array
286
- * @param {Array} array The array to modify.
287
- * @param {Array} values The values to remove.
288
- * @returns {Array} Returns `array`.
289
- * @example
290
- *
291
- * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
292
- *
293
- * _.pullAll(array, ['a', 'c']);
294
- * console.log(array);
295
- * // => ['b', 'b']
296
- */
297
- function pullAll(array, values) {
298
- return (array && array.length && values && values.length)
299
- ? basePullAll(array, values)
300
- : array;
301
- }
302
-
303
- var lodash_pull = pull;
304
-
305
- /**
306
- * @name ranges-sort
307
- * @fileoverview Sort string index ranges
308
- * @version 4.0.16
309
- * @author Roy Revelt, Codsen Ltd
310
- * @license MIT
311
- * {@link https://codsen.com/os/ranges-sort/}
312
- */
313
- const defaults$4 = {
314
- strictlyTwoElementsInRangeArrays: false,
315
- progressFn: null
316
- };
317
- function rSort(arrOfRanges, originalOptions) {
318
- if (!Array.isArray(arrOfRanges) || !arrOfRanges.length) {
319
- return arrOfRanges;
320
- }
321
- const opts = { ...defaults$4,
322
- ...originalOptions
323
- };
324
- let culpritsIndex;
325
- let culpritsLen;
326
- if (opts.strictlyTwoElementsInRangeArrays && !arrOfRanges.filter(range => range).every((rangeArr, indx) => {
327
- if (rangeArr.length !== 2) {
328
- culpritsIndex = indx;
329
- culpritsLen = rangeArr.length;
330
- return false;
331
- }
332
- return true;
333
- })) {
334
- throw new TypeError(`ranges-sort: [THROW_ID_03] The first argument should be an array and must consist of arrays which are natural number indexes representing TWO string index ranges. However, ${culpritsIndex}th range (${JSON.stringify(arrOfRanges[culpritsIndex], null, 4)}) has not two but ${culpritsLen} elements!`);
335
- }
336
- if (!arrOfRanges.filter(range => range).every((rangeArr, indx) => {
337
- if (!Number.isInteger(rangeArr[0]) || rangeArr[0] < 0 || !Number.isInteger(rangeArr[1]) || rangeArr[1] < 0) {
338
- culpritsIndex = indx;
339
- return false;
340
- }
341
- return true;
342
- })) {
343
- throw new TypeError(`ranges-sort: [THROW_ID_04] The first argument should be an array and must consist of arrays which are natural number indexes representing string index ranges. However, ${culpritsIndex}th range (${JSON.stringify(arrOfRanges[culpritsIndex], null, 4)}) does not consist of only natural numbers!`);
344
- }
345
- const maxPossibleIterations = arrOfRanges.filter(range => range).length ** 2;
346
- let counter = 0;
347
- return Array.from(arrOfRanges).filter(range => range).sort((range1, range2) => {
348
- if (opts.progressFn) {
349
- counter += 1;
350
- opts.progressFn(Math.floor(counter * 100 / maxPossibleIterations));
351
- }
352
- if (range1[0] === range2[0]) {
353
- if (range1[1] < range2[1]) {
354
- return -1;
355
- }
356
- if (range1[1] > range2[1]) {
357
- return 1;
358
- }
359
- return 0;
360
- }
361
- if (range1[0] < range2[0]) {
362
- return -1;
363
- }
364
- return 1;
365
- });
366
- }
367
-
368
- /**
369
- * @name ranges-merge
370
- * @fileoverview Merge and sort string index ranges
371
- * @version 7.0.16
372
- * @author Roy Revelt, Codsen Ltd
373
- * @license MIT
374
- * {@link https://codsen.com/os/ranges-merge/}
375
- */
376
- const defaults$3 = {
377
- mergeType: 1,
378
- progressFn: null,
379
- joinRangesThatTouchEdges: true
380
- };
381
- function rMerge(arrOfRanges, originalOpts) {
382
- function isObj(something) {
383
- return something && typeof something === "object" && !Array.isArray(something);
384
- }
385
- if (!Array.isArray(arrOfRanges) || !arrOfRanges.length) {
386
- return null;
387
- }
388
- let opts;
389
- if (originalOpts) {
390
- if (isObj(originalOpts)) {
391
- opts = { ...defaults$3,
392
- ...originalOpts
393
- };
394
- if (opts.progressFn && isObj(opts.progressFn) && !Object.keys(opts.progressFn).length) {
395
- opts.progressFn = null;
396
- } else if (opts.progressFn && typeof opts.progressFn !== "function") {
397
- throw new Error(`ranges-merge: [THROW_ID_01] opts.progressFn must be a function! It was given of a type: "${typeof opts.progressFn}", equal to ${JSON.stringify(opts.progressFn, null, 4)}`);
398
- }
399
- if (opts.mergeType && +opts.mergeType !== 1 && +opts.mergeType !== 2) {
400
- throw new Error(`ranges-merge: [THROW_ID_02] opts.mergeType was customised to a wrong thing! It was given of a type: "${typeof opts.mergeType}", equal to ${JSON.stringify(opts.mergeType, null, 4)}`);
401
- }
402
- if (typeof opts.joinRangesThatTouchEdges !== "boolean") {
403
- throw new Error(`ranges-merge: [THROW_ID_04] opts.joinRangesThatTouchEdges was customised to a wrong thing! It was given of a type: "${typeof opts.joinRangesThatTouchEdges}", equal to ${JSON.stringify(opts.joinRangesThatTouchEdges, null, 4)}`);
404
- }
405
- } else {
406
- throw new Error(`emlint: [THROW_ID_03] the second input argument must be a plain object. It was given as:\n${JSON.stringify(originalOpts, null, 4)} (type ${typeof originalOpts})`);
407
- }
408
- } else {
409
- opts = { ...defaults$3
410
- };
411
- }
412
- const filtered = arrOfRanges
413
- .filter(range => range).map(subarr => [...subarr]).filter(
414
- rangeArr => rangeArr[2] !== undefined || rangeArr[0] !== rangeArr[1]);
415
- let sortedRanges;
416
- let lastPercentageDone;
417
- let percentageDone;
418
- if (opts.progressFn) {
419
- sortedRanges = rSort(filtered, {
420
- progressFn: percentage => {
421
- percentageDone = Math.floor(percentage / 5);
422
- if (percentageDone !== lastPercentageDone) {
423
- lastPercentageDone = percentageDone;
424
- opts.progressFn(percentageDone);
425
- }
426
- }
427
- });
428
- } else {
429
- sortedRanges = rSort(filtered);
430
- }
431
- if (!sortedRanges) {
432
- return null;
433
- }
434
- const len = sortedRanges.length - 1;
435
- for (let i = len; i > 0; i--) {
436
- if (opts.progressFn) {
437
- percentageDone = Math.floor((1 - i / len) * 78) + 21;
438
- if (percentageDone !== lastPercentageDone && percentageDone > lastPercentageDone) {
439
- lastPercentageDone = percentageDone;
440
- opts.progressFn(percentageDone);
441
- }
442
- }
443
- if (sortedRanges[i][0] <= sortedRanges[i - 1][0] || !opts.joinRangesThatTouchEdges && sortedRanges[i][0] < sortedRanges[i - 1][1] || opts.joinRangesThatTouchEdges && sortedRanges[i][0] <= sortedRanges[i - 1][1]) {
444
- sortedRanges[i - 1][0] = Math.min(sortedRanges[i][0], sortedRanges[i - 1][0]);
445
- sortedRanges[i - 1][1] = Math.max(sortedRanges[i][1], sortedRanges[i - 1][1]);
446
- if (sortedRanges[i][2] !== undefined && (sortedRanges[i - 1][0] >= sortedRanges[i][0] || sortedRanges[i - 1][1] <= sortedRanges[i][1])) {
447
- if (sortedRanges[i - 1][2] !== null) {
448
- if (sortedRanges[i][2] === null && sortedRanges[i - 1][2] !== null) {
449
- sortedRanges[i - 1][2] = null;
450
- } else if (sortedRanges[i - 1][2] != null) {
451
- if (+opts.mergeType === 2 && sortedRanges[i - 1][0] === sortedRanges[i][0]) {
452
- sortedRanges[i - 1][2] = sortedRanges[i][2];
453
- } else {
454
- sortedRanges[i - 1][2] += sortedRanges[i][2];
455
- }
456
- } else {
457
- sortedRanges[i - 1][2] = sortedRanges[i][2];
458
- }
459
- }
460
- }
461
- sortedRanges.splice(i, 1);
462
- i = sortedRanges.length;
463
- }
464
- }
465
- return sortedRanges.length ? sortedRanges : null;
466
- }
467
-
468
- /**
469
- * @name ranges-apply
470
- * @fileoverview Take an array of string index ranges, delete/replace the string according to them
471
- * @version 5.0.16
472
- * @author Roy Revelt, Codsen Ltd
473
- * @license MIT
474
- * {@link https://codsen.com/os/ranges-apply/}
475
- */
476
- function rApply(str, originalRangesArr, progressFn) {
477
- let percentageDone = 0;
478
- let lastPercentageDone = 0;
479
- if (arguments.length === 0) {
480
- throw new Error("ranges-apply: [THROW_ID_01] inputs missing!");
481
- }
482
- if (typeof str !== "string") {
483
- throw new TypeError(`ranges-apply: [THROW_ID_02] first input argument must be a string! Currently it's: ${typeof str}, equal to: ${JSON.stringify(str, null, 4)}`);
484
- }
485
- if (originalRangesArr && !Array.isArray(originalRangesArr)) {
486
- throw new TypeError(`ranges-apply: [THROW_ID_03] second input argument must be an array (or null)! Currently it's: ${typeof originalRangesArr}, equal to: ${JSON.stringify(originalRangesArr, null, 4)}`);
487
- }
488
- if (progressFn && typeof progressFn !== "function") {
489
- throw new TypeError(`ranges-apply: [THROW_ID_04] the third input argument must be a function (or falsey)! Currently it's: ${typeof progressFn}, equal to: ${JSON.stringify(progressFn, null, 4)}`);
490
- }
491
- if (!originalRangesArr || !originalRangesArr.filter(range => range).length) {
492
- return str;
493
- }
494
- let rangesArr;
495
- if (Array.isArray(originalRangesArr) && Number.isInteger(originalRangesArr[0]) && Number.isInteger(originalRangesArr[1])) {
496
- rangesArr = [Array.from(originalRangesArr)];
497
- } else {
498
- rangesArr = Array.from(originalRangesArr);
499
- }
500
- const len = rangesArr.length;
501
- let counter = 0;
502
- rangesArr.filter(range => range).forEach((el, i) => {
503
- if (progressFn) {
504
- percentageDone = Math.floor(counter / len * 10);
505
- /* istanbul ignore else */
506
- if (percentageDone !== lastPercentageDone) {
507
- lastPercentageDone = percentageDone;
508
- progressFn(percentageDone);
509
- }
510
- }
511
- if (!Array.isArray(el)) {
512
- throw new TypeError(`ranges-apply: [THROW_ID_05] ranges array, second input arg., has ${i}th element not an array: ${JSON.stringify(el, null, 4)}, which is ${typeof el}`);
513
- }
514
- if (!Number.isInteger(el[0])) {
515
- if (!Number.isInteger(+el[0]) || +el[0] < 0) {
516
- throw new TypeError(`ranges-apply: [THROW_ID_06] ranges array, second input arg. has ${i}th element, array ${JSON.stringify(el, null, 0)}. Its first element is not an integer, string index, but ${typeof el[0]}, equal to: ${JSON.stringify(el[0], null, 4)}.`);
517
- } else {
518
- rangesArr[i][0] = +rangesArr[i][0];
519
- }
520
- }
521
- if (!Number.isInteger(el[1])) {
522
- if (!Number.isInteger(+el[1]) || +el[1] < 0) {
523
- throw new TypeError(`ranges-apply: [THROW_ID_07] ranges array, second input arg. has ${i}th element, array ${JSON.stringify(el, null, 0)}. Its second element is not an integer, string index, but ${typeof el[1]}, equal to: ${JSON.stringify(el[1], null, 4)}.`);
524
- } else {
525
- rangesArr[i][1] = +rangesArr[i][1];
526
- }
527
- }
528
- counter += 1;
529
- });
530
- const workingRanges = rMerge(rangesArr, {
531
- progressFn: perc => {
532
- if (progressFn) {
533
- percentageDone = 10 + Math.floor(perc / 10);
534
- /* istanbul ignore else */
535
- if (percentageDone !== lastPercentageDone) {
536
- lastPercentageDone = percentageDone;
537
- progressFn(percentageDone);
538
- }
539
- }
540
- }
541
- });
542
- const len2 = Array.isArray(workingRanges) ? workingRanges.length : 0;
543
- /* istanbul ignore else */
544
- if (len2 > 0) {
545
- const tails = str.slice(workingRanges[len2 - 1][1]);
546
- str = workingRanges.reduce((acc, _val, i, arr) => {
547
- if (progressFn) {
548
- percentageDone = 20 + Math.floor(i / len2 * 80);
549
- /* istanbul ignore else */
550
- if (percentageDone !== lastPercentageDone) {
551
- lastPercentageDone = percentageDone;
552
- progressFn(percentageDone);
553
- }
554
- }
555
- const beginning = i === 0 ? 0 : arr[i - 1][1];
556
- const ending = arr[i][0];
557
- return acc + str.slice(beginning, ending) + (arr[i][2] || "");
558
- }, "");
559
- str += tails;
560
- }
561
- return str;
562
- }
563
-
564
- /**
565
- * @name string-collapse-leading-whitespace
566
- * @fileoverview Collapse the leading and trailing whitespace of a string
567
- * @version 5.0.16
568
- * @author Roy Revelt, Codsen Ltd
569
- * @license MIT
570
- * {@link https://codsen.com/os/string-collapse-leading-whitespace/}
571
- */
572
- function collWhitespace(str, originallineBreakLimit = 1) {
573
- const rawNbsp = "\u00A0";
574
- function reverse(s) {
575
- return Array.from(s).reverse().join("");
576
- }
577
- function prep(whitespaceChunk, limit, trailing) {
578
- const firstBreakChar = trailing ? "\n" : "\r";
579
- const secondBreakChar = trailing ? "\r" : "\n";
580
- if (!whitespaceChunk) {
581
- return whitespaceChunk;
582
- }
583
- let crlfCount = 0;
584
- let res = "";
585
- for (let i = 0, len = whitespaceChunk.length; i < len; i++) {
586
- if (whitespaceChunk[i] === firstBreakChar || whitespaceChunk[i] === secondBreakChar && whitespaceChunk[i - 1] !== firstBreakChar) {
587
- crlfCount++;
588
- }
589
- if (`\r\n`.includes(whitespaceChunk[i]) || whitespaceChunk[i] === rawNbsp) {
590
- if (whitespaceChunk[i] === rawNbsp) {
591
- res += whitespaceChunk[i];
592
- } else if (whitespaceChunk[i] === firstBreakChar) {
593
- if (crlfCount <= limit) {
594
- res += whitespaceChunk[i];
595
- if (whitespaceChunk[i + 1] === secondBreakChar) {
596
- res += whitespaceChunk[i + 1];
597
- i++;
598
- }
599
- }
600
- } else if (whitespaceChunk[i] === secondBreakChar && (!whitespaceChunk[i - 1] || whitespaceChunk[i - 1] !== firstBreakChar) && crlfCount <= limit) {
601
- res += whitespaceChunk[i];
602
- }
603
- } else {
604
- if (!whitespaceChunk[i + 1] && !crlfCount) {
605
- res += " ";
606
- }
607
- }
608
- }
609
- return res;
610
- }
611
- if (typeof str === "string" && str.length) {
612
- let lineBreakLimit = 1;
613
- if (typeof +originallineBreakLimit === "number" && Number.isInteger(+originallineBreakLimit) && +originallineBreakLimit >= 0) {
614
- lineBreakLimit = +originallineBreakLimit;
615
- }
616
- let frontPart = "";
617
- let endPart = "";
618
- if (!str.trim()) {
619
- frontPart = str;
620
- } else if (!str[0].trim()) {
621
- for (let i = 0, len = str.length; i < len; i++) {
622
- if (str[i].trim()) {
623
- frontPart = str.slice(0, i);
624
- break;
625
- }
626
- }
627
- }
628
- if (str.trim() && (str.slice(-1).trim() === "" || str.slice(-1) === rawNbsp)) {
629
- for (let i = str.length; i--;) {
630
- if (str[i].trim()) {
631
- endPart = str.slice(i + 1);
632
- break;
633
- }
634
- }
635
- }
636
- return `${prep(frontPart, lineBreakLimit, false)}${str.trim()}${reverse(prep(reverse(endPart), lineBreakLimit, true))}`;
637
- }
638
- return str;
639
- }
640
-
641
- /**
642
- * @name ranges-push
643
- * @fileoverview Gather string index ranges
644
- * @version 5.0.16
645
- * @author Roy Revelt, Codsen Ltd
646
- * @license MIT
647
- * {@link https://codsen.com/os/ranges-push/}
648
- */
649
- function existy(x) {
650
- return x != null;
651
- }
652
- function isNum(something) {
653
- return Number.isInteger(something) && something >= 0;
654
- }
655
- function isStr(something) {
656
- return typeof something === "string";
657
- }
658
- const defaults$2 = {
659
- limitToBeAddedWhitespace: false,
660
- limitLinebreaksCount: 1,
661
- mergeType: 1
662
- };
663
- class Ranges {
664
- constructor(originalOpts) {
665
- const opts = { ...defaults$2,
666
- ...originalOpts
667
- };
668
- if (opts.mergeType && opts.mergeType !== 1 && opts.mergeType !== 2) {
669
- if (isStr(opts.mergeType) && opts.mergeType.trim() === "1") {
670
- opts.mergeType = 1;
671
- } else if (isStr(opts.mergeType) && opts.mergeType.trim() === "2") {
672
- opts.mergeType = 2;
673
- } else {
674
- throw new Error(`ranges-push: [THROW_ID_02] opts.mergeType was customised to a wrong thing! It was given of a type: "${typeof opts.mergeType}", equal to ${JSON.stringify(opts.mergeType, null, 4)}`);
675
- }
676
- }
677
- this.opts = opts;
678
- this.ranges = [];
679
- }
680
- add(originalFrom, originalTo, addVal) {
681
- if (originalFrom == null && originalTo == null) {
682
- return;
683
- }
684
- if (existy(originalFrom) && !existy(originalTo)) {
685
- if (Array.isArray(originalFrom)) {
686
- if (originalFrom.length) {
687
- if (originalFrom.some(el => Array.isArray(el))) {
688
- originalFrom.forEach(thing => {
689
- if (Array.isArray(thing)) {
690
- this.add(...thing);
691
- }
692
- });
693
- return;
694
- }
695
- if (originalFrom.length && isNum(+originalFrom[0]) && isNum(+originalFrom[1])) {
696
- this.add(...originalFrom);
697
- }
698
- }
699
- return;
700
- }
701
- throw new TypeError(`ranges-push/Ranges/add(): [THROW_ID_12] the first input argument, "from" is set (${JSON.stringify(originalFrom, null, 0)}) but second-one, "to" is not (${JSON.stringify(originalTo, null, 0)})`);
702
- } else if (!existy(originalFrom) && existy(originalTo)) {
703
- throw new TypeError(`ranges-push/Ranges/add(): [THROW_ID_13] the second input argument, "to" is set (${JSON.stringify(originalTo, null, 0)}) but first-one, "from" is not (${JSON.stringify(originalFrom, null, 0)})`);
704
- }
705
- const from = +originalFrom;
706
- const to = +originalTo;
707
- if (isNum(addVal)) {
708
- addVal = String(addVal);
709
- }
710
- if (isNum(from) && isNum(to)) {
711
- if (existy(addVal) && !isStr(addVal) && !isNum(addVal)) {
712
- throw new TypeError(`ranges-push/Ranges/add(): [THROW_ID_08] The third argument, the value to add, was given not as string but ${typeof addVal}, equal to:\n${JSON.stringify(addVal, null, 4)}`);
713
- }
714
- if (existy(this.ranges) && Array.isArray(this.last()) && from === this.last()[1]) {
715
- this.last()[1] = to;
716
- if (this.last()[2] === null || addVal === null) ;
717
- if (this.last()[2] !== null && existy(addVal)) {
718
- let calculatedVal = this.last()[2] && this.last()[2].length > 0 && (!this.opts || !this.opts.mergeType || this.opts.mergeType === 1) ? this.last()[2] + addVal : addVal;
719
- if (this.opts.limitToBeAddedWhitespace) {
720
- calculatedVal = collWhitespace(calculatedVal, this.opts.limitLinebreaksCount);
721
- }
722
- if (!(isStr(calculatedVal) && !calculatedVal.length)) {
723
- this.last()[2] = calculatedVal;
724
- }
725
- }
726
- } else {
727
- if (!this.ranges) {
728
- this.ranges = [];
729
- }
730
- const whatToPush = addVal !== undefined && !(isStr(addVal) && !addVal.length) ? [from, to, addVal && this.opts.limitToBeAddedWhitespace ? collWhitespace(addVal, this.opts.limitLinebreaksCount) : addVal] : [from, to];
731
- this.ranges.push(whatToPush);
732
- }
733
- } else {
734
- if (!(isNum(from) && from >= 0)) {
735
- throw new TypeError(`ranges-push/Ranges/add(): [THROW_ID_09] "from" value, the first input argument, must be a natural number or zero! Currently it's of a type "${typeof from}" equal to: ${JSON.stringify(from, null, 4)}`);
736
- } else {
737
- throw new TypeError(`ranges-push/Ranges/add(): [THROW_ID_10] "to" value, the second input argument, must be a natural number or zero! Currently it's of a type "${typeof to}" equal to: ${JSON.stringify(to, null, 4)}`);
738
- }
739
- }
740
- }
741
- push(originalFrom, originalTo, addVal) {
742
- this.add(originalFrom, originalTo, addVal);
743
- }
744
- current() {
745
- if (Array.isArray(this.ranges) && this.ranges.length) {
746
- this.ranges = rMerge(this.ranges, {
747
- mergeType: this.opts.mergeType
748
- });
749
- if (this.ranges && this.opts.limitToBeAddedWhitespace) {
750
- return this.ranges.map(val => {
751
- if (existy(val[2])) {
752
- return [val[0], val[1], collWhitespace(val[2], this.opts.limitLinebreaksCount)];
753
- }
754
- return val;
755
- });
756
- }
757
- return this.ranges;
758
- }
759
- return null;
760
- }
761
- wipe() {
762
- this.ranges = [];
763
- }
764
- replace(givenRanges) {
765
- if (Array.isArray(givenRanges) && givenRanges.length) {
766
- if (!(Array.isArray(givenRanges[0]) && isNum(givenRanges[0][0]))) {
767
- throw new Error(`ranges-push/Ranges/replace(): [THROW_ID_11] Single range was given but we expected array of arrays! The first element, ${JSON.stringify(givenRanges[0], null, 4)} should be an array and its first element should be an integer, a string index.`);
768
- } else {
769
- this.ranges = Array.from(givenRanges);
770
- }
771
- } else {
772
- this.ranges = [];
773
- }
774
- }
775
- last() {
776
- if (Array.isArray(this.ranges) && this.ranges.length) {
777
- return this.ranges[this.ranges.length - 1];
778
- }
779
- return null;
780
- }
781
- }
782
-
783
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
784
-
785
- /**
786
- * lodash (Custom Build) <https://lodash.com/>
787
- * Build: `lodash modularize exports="npm" -o ./`
788
- * Copyright jQuery Foundation and other contributors <https://jquery.org/>
789
- * Released under MIT license <https://lodash.com/license>
790
- * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
791
- * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
792
- */
793
-
794
- /** Used as references for various `Number` constants. */
795
- var INFINITY = 1 / 0;
796
-
797
- /** `Object#toString` result references. */
798
- var symbolTag = '[object Symbol]';
799
-
800
- /** Used to match leading and trailing whitespace. */
801
- var reTrim = /^\s+|\s+$/g;
802
-
803
- /** Used to compose unicode character classes. */
804
- var rsAstralRange = '\\ud800-\\udfff',
805
- rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23',
806
- rsComboSymbolsRange = '\\u20d0-\\u20f0',
807
- rsVarRange = '\\ufe0e\\ufe0f';
808
-
809
- /** Used to compose unicode capture groups. */
810
- var rsAstral = '[' + rsAstralRange + ']',
811
- rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',
812
- rsFitz = '\\ud83c[\\udffb-\\udfff]',
813
- rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
814
- rsNonAstral = '[^' + rsAstralRange + ']',
815
- rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
816
- rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
817
- rsZWJ = '\\u200d';
818
-
819
- /** Used to compose unicode regexes. */
820
- var reOptMod = rsModifier + '?',
821
- rsOptVar = '[' + rsVarRange + ']?',
822
- rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
823
- rsSeq = rsOptVar + reOptMod + rsOptJoin,
824
- rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
825
-
826
- /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
827
- var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
828
-
829
- /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
830
- var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
831
-
832
- /** Detect free variable `global` from Node.js. */
833
- var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
834
-
835
- /** Detect free variable `self`. */
836
- var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
837
-
838
- /** Used as a reference to the global object. */
839
- var root = freeGlobal || freeSelf || Function('return this')();
840
-
841
- /**
842
- * Converts an ASCII `string` to an array.
843
- *
844
- * @private
845
- * @param {string} string The string to convert.
846
- * @returns {Array} Returns the converted array.
847
- */
848
- function asciiToArray(string) {
849
- return string.split('');
850
- }
851
-
852
- /**
853
- * The base implementation of `_.findIndex` and `_.findLastIndex` without
854
- * support for iteratee shorthands.
855
- *
856
- * @private
857
- * @param {Array} array The array to inspect.
858
- * @param {Function} predicate The function invoked per iteration.
859
- * @param {number} fromIndex The index to search from.
860
- * @param {boolean} [fromRight] Specify iterating from right to left.
861
- * @returns {number} Returns the index of the matched value, else `-1`.
862
- */
863
- function baseFindIndex(array, predicate, fromIndex, fromRight) {
864
- var length = array.length,
865
- index = fromIndex + (fromRight ? 1 : -1);
866
-
867
- while ((fromRight ? index-- : ++index < length)) {
868
- if (predicate(array[index], index, array)) {
869
- return index;
870
- }
871
- }
872
- return -1;
873
- }
874
-
875
- /**
876
- * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
877
- *
878
- * @private
879
- * @param {Array} array The array to inspect.
880
- * @param {*} value The value to search for.
881
- * @param {number} fromIndex The index to search from.
882
- * @returns {number} Returns the index of the matched value, else `-1`.
883
- */
884
- function baseIndexOf(array, value, fromIndex) {
885
- if (value !== value) {
886
- return baseFindIndex(array, baseIsNaN, fromIndex);
887
- }
888
- var index = fromIndex - 1,
889
- length = array.length;
890
-
891
- while (++index < length) {
892
- if (array[index] === value) {
893
- return index;
894
- }
895
- }
896
- return -1;
897
- }
898
-
899
- /**
900
- * The base implementation of `_.isNaN` without support for number objects.
901
- *
902
- * @private
903
- * @param {*} value The value to check.
904
- * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
905
- */
906
- function baseIsNaN(value) {
907
- return value !== value;
908
- }
909
-
910
- /**
911
- * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
912
- * that is not found in the character symbols.
913
- *
914
- * @private
915
- * @param {Array} strSymbols The string symbols to inspect.
916
- * @param {Array} chrSymbols The character symbols to find.
917
- * @returns {number} Returns the index of the first unmatched string symbol.
918
- */
919
- function charsStartIndex(strSymbols, chrSymbols) {
920
- var index = -1,
921
- length = strSymbols.length;
922
-
923
- while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
924
- return index;
925
- }
926
-
927
- /**
928
- * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
929
- * that is not found in the character symbols.
930
- *
931
- * @private
932
- * @param {Array} strSymbols The string symbols to inspect.
933
- * @param {Array} chrSymbols The character symbols to find.
934
- * @returns {number} Returns the index of the last unmatched string symbol.
935
- */
936
- function charsEndIndex(strSymbols, chrSymbols) {
937
- var index = strSymbols.length;
938
-
939
- while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
940
- return index;
941
- }
942
-
943
- /**
944
- * Checks if `string` contains Unicode symbols.
945
- *
946
- * @private
947
- * @param {string} string The string to inspect.
948
- * @returns {boolean} Returns `true` if a symbol is found, else `false`.
949
- */
950
- function hasUnicode(string) {
951
- return reHasUnicode.test(string);
952
- }
953
-
954
- /**
955
- * Converts `string` to an array.
956
- *
957
- * @private
958
- * @param {string} string The string to convert.
959
- * @returns {Array} Returns the converted array.
960
- */
961
- function stringToArray(string) {
962
- return hasUnicode(string)
963
- ? unicodeToArray(string)
964
- : asciiToArray(string);
965
- }
966
-
967
- /**
968
- * Converts a Unicode `string` to an array.
969
- *
970
- * @private
971
- * @param {string} string The string to convert.
972
- * @returns {Array} Returns the converted array.
973
- */
974
- function unicodeToArray(string) {
975
- return string.match(reUnicode) || [];
976
- }
977
-
978
- /** Used for built-in method references. */
979
- var objectProto = Object.prototype;
980
-
981
- /**
982
- * Used to resolve the
983
- * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
984
- * of values.
985
- */
986
- var objectToString = objectProto.toString;
987
-
988
- /** Built-in value references. */
989
- var Symbol = root.Symbol;
990
-
991
- /** Used to convert symbols to primitives and strings. */
992
- var symbolProto = Symbol ? Symbol.prototype : undefined,
993
- symbolToString = symbolProto ? symbolProto.toString : undefined;
994
-
995
- /**
996
- * The base implementation of `_.slice` without an iteratee call guard.
997
- *
998
- * @private
999
- * @param {Array} array The array to slice.
1000
- * @param {number} [start=0] The start position.
1001
- * @param {number} [end=array.length] The end position.
1002
- * @returns {Array} Returns the slice of `array`.
1003
- */
1004
- function baseSlice(array, start, end) {
1005
- var index = -1,
1006
- length = array.length;
1007
-
1008
- if (start < 0) {
1009
- start = -start > length ? 0 : (length + start);
1010
- }
1011
- end = end > length ? length : end;
1012
- if (end < 0) {
1013
- end += length;
1014
- }
1015
- length = start > end ? 0 : ((end - start) >>> 0);
1016
- start >>>= 0;
1017
-
1018
- var result = Array(length);
1019
- while (++index < length) {
1020
- result[index] = array[index + start];
1021
- }
1022
- return result;
1023
- }
1024
-
1025
- /**
1026
- * The base implementation of `_.toString` which doesn't convert nullish
1027
- * values to empty strings.
1028
- *
1029
- * @private
1030
- * @param {*} value The value to process.
1031
- * @returns {string} Returns the string.
1032
- */
1033
- function baseToString(value) {
1034
- // Exit early for strings to avoid a performance hit in some environments.
1035
- if (typeof value == 'string') {
1036
- return value;
1037
- }
1038
- if (isSymbol(value)) {
1039
- return symbolToString ? symbolToString.call(value) : '';
1040
- }
1041
- var result = (value + '');
1042
- return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
1043
- }
1044
-
1045
- /**
1046
- * Casts `array` to a slice if it's needed.
1047
- *
1048
- * @private
1049
- * @param {Array} array The array to inspect.
1050
- * @param {number} start The start position.
1051
- * @param {number} [end=array.length] The end position.
1052
- * @returns {Array} Returns the cast slice.
1053
- */
1054
- function castSlice(array, start, end) {
1055
- var length = array.length;
1056
- end = end === undefined ? length : end;
1057
- return (!start && end >= length) ? array : baseSlice(array, start, end);
1058
- }
1059
-
1060
- /**
1061
- * Checks if `value` is object-like. A value is object-like if it's not `null`
1062
- * and has a `typeof` result of "object".
1063
- *
1064
- * @static
1065
- * @memberOf _
1066
- * @since 4.0.0
1067
- * @category Lang
1068
- * @param {*} value The value to check.
1069
- * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
1070
- * @example
1071
- *
1072
- * _.isObjectLike({});
1073
- * // => true
1074
- *
1075
- * _.isObjectLike([1, 2, 3]);
1076
- * // => true
1077
- *
1078
- * _.isObjectLike(_.noop);
1079
- * // => false
1080
- *
1081
- * _.isObjectLike(null);
1082
- * // => false
1083
- */
1084
- function isObjectLike(value) {
1085
- return !!value && typeof value == 'object';
1086
- }
1087
-
1088
- /**
1089
- * Checks if `value` is classified as a `Symbol` primitive or object.
1090
- *
1091
- * @static
1092
- * @memberOf _
1093
- * @since 4.0.0
1094
- * @category Lang
1095
- * @param {*} value The value to check.
1096
- * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
1097
- * @example
1098
- *
1099
- * _.isSymbol(Symbol.iterator);
1100
- * // => true
1101
- *
1102
- * _.isSymbol('abc');
1103
- * // => false
1104
- */
1105
- function isSymbol(value) {
1106
- return typeof value == 'symbol' ||
1107
- (isObjectLike(value) && objectToString.call(value) == symbolTag);
1108
- }
1109
-
1110
- /**
1111
- * Converts `value` to a string. An empty string is returned for `null`
1112
- * and `undefined` values. The sign of `-0` is preserved.
1113
- *
1114
- * @static
1115
- * @memberOf _
1116
- * @since 4.0.0
1117
- * @category Lang
1118
- * @param {*} value The value to process.
1119
- * @returns {string} Returns the string.
1120
- * @example
1121
- *
1122
- * _.toString(null);
1123
- * // => ''
1124
- *
1125
- * _.toString(-0);
1126
- * // => '-0'
1127
- *
1128
- * _.toString([1, 2, 3]);
1129
- * // => '1,2,3'
1130
- */
1131
- function toString(value) {
1132
- return value == null ? '' : baseToString(value);
1133
- }
1134
-
1135
- /**
1136
- * Removes leading and trailing whitespace or specified characters from `string`.
1137
- *
1138
- * @static
1139
- * @memberOf _
1140
- * @since 3.0.0
1141
- * @category String
1142
- * @param {string} [string=''] The string to trim.
1143
- * @param {string} [chars=whitespace] The characters to trim.
1144
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
1145
- * @returns {string} Returns the trimmed string.
1146
- * @example
1147
- *
1148
- * _.trim(' abc ');
1149
- * // => 'abc'
1150
- *
1151
- * _.trim('-_-abc-_-', '_-');
1152
- * // => 'abc'
1153
- *
1154
- * _.map([' foo ', ' bar '], _.trim);
1155
- * // => ['foo', 'bar']
1156
- */
1157
- function trim(string, chars, guard) {
1158
- string = toString(string);
1159
- if (string && (guard || chars === undefined)) {
1160
- return string.replace(reTrim, '');
1161
- }
1162
- if (!string || !(chars = baseToString(chars))) {
1163
- return string;
1164
- }
1165
- var strSymbols = stringToArray(string),
1166
- chrSymbols = stringToArray(chars),
1167
- start = charsStartIndex(strSymbols, chrSymbols),
1168
- end = charsEndIndex(strSymbols, chrSymbols) + 1;
1169
-
1170
- return castSlice(strSymbols, start, end).join('');
1171
- }
1172
-
1173
- var lodash_trim = trim;
1174
-
1175
- /**
1176
- * @name string-remove-thousand-separators
1177
- * @fileoverview Detects and removes thousand separators (dot/comma/quote/space) from string-type digits
1178
- * @version 5.0.16
1179
- * @author Roy Revelt, Codsen Ltd
1180
- * @license MIT
1181
- * {@link https://codsen.com/os/string-remove-thousand-separators/}
1182
- */
1183
- function remSep(str, originalOpts) {
1184
- let allOK = true;
1185
- const knownSeparatorsArray = [".", ",", "'", " "];
1186
- let firstSeparator;
1187
- if (typeof str !== "string") {
1188
- throw new TypeError(`string-remove-thousand-separators/remSep(): [THROW_ID_01] Input must be string! Currently it's: ${typeof str}, equal to:\n${JSON.stringify(str, null, 4)}`);
1189
- }
1190
- if (originalOpts && typeof originalOpts !== "object") {
1191
- throw new TypeError(`string-remove-thousand-separators/remSep(): [THROW_ID_02] Options object must be a plain object! Currently it's: ${typeof originalOpts}, equal to:\n${JSON.stringify(originalOpts, null, 4)}`);
1192
- }
1193
- const defaults = {
1194
- removeThousandSeparatorsFromNumbers: true,
1195
- padSingleDecimalPlaceNumbers: true,
1196
- forceUKStyle: false
1197
- };
1198
- const opts = { ...defaults,
1199
- ...originalOpts
1200
- };
1201
- const res = lodash_trim(str.trim(), '"');
1202
- if (res === "") {
1203
- return res;
1204
- }
1205
- const rangesToDelete = new Ranges();
1206
- for (let i = 0, len = res.length; i < len; i++) {
1207
- if (opts.removeThousandSeparatorsFromNumbers && res[i].trim() === "") {
1208
- rangesToDelete.add(i, i + 1);
1209
- }
1210
- if (opts.removeThousandSeparatorsFromNumbers && res[i] === "'") {
1211
- rangesToDelete.add(i, i + 1);
1212
- if (res[i + 1] === "'") {
1213
- allOK = false;
1214
- break;
1215
- }
1216
- }
1217
- if (knownSeparatorsArray.includes(res[i])) {
1218
- if (res[i + 1] !== undefined && /^\d*$/.test(res[i + 1])) {
1219
- if (res[i + 2] !== undefined) {
1220
- if (/^\d*$/.test(res[i + 2])) {
1221
- if (res[i + 3] !== undefined) {
1222
- if (/^\d*$/.test(res[i + 3])) {
1223
- if (res[i + 4] !== undefined && /^\d*$/.test(res[i + 4])) {
1224
- allOK = false;
1225
- break;
1226
- } else {
1227
- if (opts.removeThousandSeparatorsFromNumbers) {
1228
- rangesToDelete.add(i, i + 1);
1229
- }
1230
- if (!firstSeparator) {
1231
- firstSeparator = res[i];
1232
- } else if (res[i] !== firstSeparator) {
1233
- allOK = false;
1234
- break;
1235
- }
1236
- }
1237
- } else {
1238
- allOK = false;
1239
- break;
1240
- }
1241
- } else if (opts.removeThousandSeparatorsFromNumbers && opts.forceUKStyle && res[i] === ",") {
1242
- rangesToDelete.add(i, i + 1, ".");
1243
- }
1244
- } else {
1245
- allOK = false;
1246
- break;
1247
- }
1248
- } else {
1249
- if (opts.forceUKStyle && res[i] === ",") {
1250
- rangesToDelete.add(i, i + 1, ".");
1251
- }
1252
- if (opts.padSingleDecimalPlaceNumbers) {
1253
- rangesToDelete.add(i + 2, i + 2, "0");
1254
- }
1255
- }
1256
- }
1257
- } else if (!/^\d*$/.test(res[i])) {
1258
- allOK = false;
1259
- break;
1260
- }
1261
- }
1262
- if (allOK && rangesToDelete.current()) {
1263
- return rApply(res, rangesToDelete.current());
1264
- }
1265
- return res;
1266
- }
1267
-
1268
- /**
1269
- * @name csv-split-easy
1270
- * @fileoverview Splits the CSV string into array of arrays, each representing a row of columns
1271
- * @version 5.0.16
1272
- * @author Roy Revelt, Codsen Ltd
1273
- * @license MIT
1274
- * {@link https://codsen.com/os/csv-split-easy/}
1275
- */
1276
- const defaults$1 = {
1277
- removeThousandSeparatorsFromNumbers: true,
1278
- padSingleDecimalPlaceNumbers: true,
1279
- forceUKStyle: false
1280
- };
1281
- function splitEasy(str, originalOpts) {
1282
- let colStarts = 0;
1283
- let lineBreakStarts = 0;
1284
- let rowArray = [];
1285
- const resArray = [];
1286
- let ignoreCommasThatFollow = false;
1287
- let thisRowContainsOnlyEmptySpace = true;
1288
- if (originalOpts && typeof originalOpts !== "object") {
1289
- throw new Error(`csv-split-easy/split(): [THROW_ID_02] Options object must be a plain object! Currently it's of a type ${typeof originalOpts} equal to:\n${JSON.stringify(originalOpts, null, 4)}`);
1290
- }
1291
- const opts = { ...defaults$1,
1292
- ...originalOpts
1293
- };
1294
- if (typeof str !== "string") {
1295
- throw new TypeError(`csv-split-easy/split(): [THROW_ID_04] input must be string! Currently it's: ${typeof str}, equal to: ${JSON.stringify(str, null, 4)}`);
1296
- } else {
1297
- if (str === "") {
1298
- return [[""]];
1299
- }
1300
- str = str.trim();
1301
- }
1302
- for (let i = 0, len = str.length; i < len; i++) {
1303
- if (thisRowContainsOnlyEmptySpace && str[i] !== '"' && str[i] !== "," && str[i].trim() !== "") {
1304
- thisRowContainsOnlyEmptySpace = false;
1305
- }
1306
- if (str[i] === '"') {
1307
- if (ignoreCommasThatFollow && str[i + 1] === '"') {
1308
- i += 1;
1309
- } else if (ignoreCommasThatFollow) {
1310
- ignoreCommasThatFollow = false;
1311
- const newElem = str.slice(colStarts, i);
1312
- if (newElem.trim() !== "") {
1313
- thisRowContainsOnlyEmptySpace = false;
1314
- }
1315
- const processedElem = /""/.test(newElem) ? newElem.replace(/""/g, '"') : remSep(newElem, {
1316
- removeThousandSeparatorsFromNumbers: opts.removeThousandSeparatorsFromNumbers,
1317
- padSingleDecimalPlaceNumbers: opts.padSingleDecimalPlaceNumbers,
1318
- forceUKStyle: opts.forceUKStyle
1319
- });
1320
- rowArray.push(processedElem);
1321
- } else {
1322
- ignoreCommasThatFollow = true;
1323
- colStarts = i + 1;
1324
- }
1325
- }
1326
- else if (!ignoreCommasThatFollow && str[i] === ",") {
1327
- if (str[i - 1] !== '"' && !ignoreCommasThatFollow) {
1328
- const newElem = str.slice(colStarts, i);
1329
- if (newElem.trim() !== "") {
1330
- thisRowContainsOnlyEmptySpace = false;
1331
- }
1332
- rowArray.push(remSep(newElem,
1333
- {
1334
- removeThousandSeparatorsFromNumbers: opts.removeThousandSeparatorsFromNumbers,
1335
- padSingleDecimalPlaceNumbers: opts.padSingleDecimalPlaceNumbers,
1336
- forceUKStyle: opts.forceUKStyle
1337
- }));
1338
- }
1339
- colStarts = i + 1;
1340
- if (lineBreakStarts) {
1341
- lineBreakStarts = 0;
1342
- }
1343
- }
1344
- else if (str[i] === "\n" || str[i] === "\r") {
1345
- if (!lineBreakStarts) {
1346
- lineBreakStarts = i;
1347
- if (!ignoreCommasThatFollow && str[i - 1] !== '"') {
1348
- const newElem = str.slice(colStarts, i);
1349
- if (newElem.trim() !== "") {
1350
- thisRowContainsOnlyEmptySpace = false;
1351
- }
1352
- rowArray.push(remSep(newElem, {
1353
- removeThousandSeparatorsFromNumbers: opts.removeThousandSeparatorsFromNumbers,
1354
- padSingleDecimalPlaceNumbers: opts.padSingleDecimalPlaceNumbers,
1355
- forceUKStyle: opts.forceUKStyle
1356
- }));
1357
- }
1358
- if (!thisRowContainsOnlyEmptySpace) {
1359
- resArray.push(rowArray);
1360
- } else {
1361
- rowArray.length = 0;
1362
- }
1363
- thisRowContainsOnlyEmptySpace = true;
1364
- rowArray = [];
1365
- }
1366
- colStarts = i + 1;
1367
- }
1368
- else if (lineBreakStarts) {
1369
- lineBreakStarts = 0;
1370
- colStarts = i;
1371
- }
1372
- if (i + 1 === len) {
1373
- if (str[i] !== '"') {
1374
- const newElem = str.slice(colStarts, i + 1);
1375
- if (newElem.trim()) {
1376
- thisRowContainsOnlyEmptySpace = false;
1377
- }
1378
- rowArray.push(remSep(newElem, {
1379
- removeThousandSeparatorsFromNumbers: opts.removeThousandSeparatorsFromNumbers,
1380
- padSingleDecimalPlaceNumbers: opts.padSingleDecimalPlaceNumbers,
1381
- forceUKStyle: opts.forceUKStyle
1382
- }));
1383
- }
1384
- if (!thisRowContainsOnlyEmptySpace) {
1385
- resArray.push(rowArray);
1386
- } else {
1387
- rowArray = [];
1388
- }
1389
- thisRowContainsOnlyEmptySpace = true;
1390
- }
1391
- }
1392
- if (resArray.length === 0) {
1393
- return [[""]];
1394
- }
1395
- return resArray;
1396
- }
1397
-
1398
- /*!
1399
- * currency.js - v2.0.3
1400
- * http://scurker.github.io/currency.js
1401
- *
1402
- * Copyright (c) 2020 Jason Wilson
1403
- * Released under MIT license
1404
- */
1405
-
1406
- var defaults = {
1407
- symbol: '$',
1408
- separator: ',',
1409
- decimal: '.',
1410
- errorOnInvalid: false,
1411
- precision: 2,
1412
- pattern: '!#',
1413
- negativePattern: '-!#',
1414
- format: format,
1415
- fromCents: false
1416
- };
1417
-
1418
- var round = function round(v) {
1419
- return Math.round(v);
1420
- };
1421
-
1422
- var pow = function pow(p) {
1423
- return Math.pow(10, p);
1424
- };
1425
-
1426
- var rounding = function rounding(value, increment) {
1427
- return round(value / increment) * increment;
1428
- };
1429
-
1430
- var groupRegex = /(\d)(?=(\d{3})+\b)/g;
1431
- var vedicRegex = /(\d)(?=(\d\d)+\d\b)/g;
1432
- /**
1433
- * Create a new instance of currency.js
1434
- * @param {number|string|currency} value
1435
- * @param {object} [opts]
1436
- */
1437
-
1438
- function currency(value, opts) {
1439
- var that = this;
1440
-
1441
- if (!(that instanceof currency)) {
1442
- return new currency(value, opts);
1443
- }
1444
-
1445
- var settings = Object.assign({}, defaults, opts),
1446
- precision = pow(settings.precision),
1447
- v = parse(value, settings);
1448
- that.intValue = v;
1449
- that.value = v / precision; // Set default incremental value
1450
-
1451
- settings.increment = settings.increment || 1 / precision; // Support vedic numbering systems
1452
- // see: https://en.wikipedia.org/wiki/Indian_numbering_system
1453
-
1454
- if (settings.useVedic) {
1455
- settings.groups = vedicRegex;
1456
- } else {
1457
- settings.groups = groupRegex;
1458
- } // Intended for internal usage only - subject to change
1459
-
1460
-
1461
- this.s = settings;
1462
- this.p = precision;
1463
- }
1464
-
1465
- function parse(value, opts) {
1466
- var useRounding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1467
- var v = 0,
1468
- decimal = opts.decimal,
1469
- errorOnInvalid = opts.errorOnInvalid,
1470
- decimals = opts.precision,
1471
- fromCents = opts.fromCents,
1472
- precision = pow(decimals),
1473
- isNumber = typeof value === 'number',
1474
- isCurrency = value instanceof currency;
1475
-
1476
- if (isCurrency && fromCents) {
1477
- return value.intValue;
1478
- }
1479
-
1480
- if (isNumber || isCurrency) {
1481
- v = isCurrency ? value.value : value;
1482
- } else if (typeof value === 'string') {
1483
- var regex = new RegExp('[^-\\d' + decimal + ']', 'g'),
1484
- decimalString = new RegExp('\\' + decimal, 'g');
1485
- v = value.replace(/\((.*)\)/, '-$1') // allow negative e.g. (1.99)
1486
- .replace(regex, '') // replace any non numeric values
1487
- .replace(decimalString, '.'); // convert any decimal values
1488
-
1489
- v = v || 0;
1490
- } else {
1491
- if (errorOnInvalid) {
1492
- throw Error('Invalid Input');
1493
- }
1494
-
1495
- v = 0;
1496
- }
1497
-
1498
- if (!fromCents) {
1499
- v *= precision; // scale number to integer value
1500
-
1501
- v = v.toFixed(4); // Handle additional decimal for proper rounding.
1502
- }
1503
-
1504
- return useRounding ? round(v) : v;
1505
- }
1506
- /**
1507
- * Formats a currency object
1508
- * @param currency
1509
- * @param {object} [opts]
1510
- */
1511
-
1512
-
1513
- function format(currency, settings) {
1514
- var pattern = settings.pattern,
1515
- negativePattern = settings.negativePattern,
1516
- symbol = settings.symbol,
1517
- separator = settings.separator,
1518
- decimal = settings.decimal,
1519
- groups = settings.groups,
1520
- split = ('' + currency).replace(/^-/, '').split('.'),
1521
- dollars = split[0],
1522
- cents = split[1];
1523
- return (currency.value >= 0 ? pattern : negativePattern).replace('!', symbol).replace('#', dollars.replace(groups, '$1' + separator) + (cents ? decimal + cents : ''));
1524
- }
1525
-
1526
- currency.prototype = {
1527
- /**
1528
- * Adds values together.
1529
- * @param {number} number
1530
- * @returns {currency}
1531
- */
1532
- add: function add(number) {
1533
- var intValue = this.intValue,
1534
- _settings = this.s,
1535
- _precision = this.p;
1536
- return currency((intValue += parse(number, _settings)) / (_settings.fromCents ? 1 : _precision), _settings);
1537
- },
1538
-
1539
- /**
1540
- * Subtracts value.
1541
- * @param {number} number
1542
- * @returns {currency}
1543
- */
1544
- subtract: function subtract(number) {
1545
- var intValue = this.intValue,
1546
- _settings = this.s,
1547
- _precision = this.p;
1548
- return currency((intValue -= parse(number, _settings)) / (_settings.fromCents ? 1 : _precision), _settings);
1549
- },
1550
-
1551
- /**
1552
- * Multiplies values.
1553
- * @param {number} number
1554
- * @returns {currency}
1555
- */
1556
- multiply: function multiply(number) {
1557
- var intValue = this.intValue,
1558
- _settings = this.s;
1559
- return currency((intValue *= number) / (_settings.fromCents ? 1 : pow(_settings.precision)), _settings);
1560
- },
1561
-
1562
- /**
1563
- * Divides value.
1564
- * @param {number} number
1565
- * @returns {currency}
1566
- */
1567
- divide: function divide(number) {
1568
- var intValue = this.intValue,
1569
- _settings = this.s;
1570
- return currency(intValue /= parse(number, _settings, false), _settings);
1571
- },
1572
-
1573
- /**
1574
- * Takes the currency amount and distributes the values evenly. Any extra pennies
1575
- * left over from the distribution will be stacked onto the first set of entries.
1576
- * @param {number} count
1577
- * @returns {array}
1578
- */
1579
- distribute: function distribute(count) {
1580
- var intValue = this.intValue,
1581
- _precision = this.p,
1582
- _settings = this.s,
1583
- distribution = [],
1584
- split = Math[intValue >= 0 ? 'floor' : 'ceil'](intValue / count),
1585
- pennies = Math.abs(intValue - split * count),
1586
- precision = _settings.fromCents ? 1 : _precision;
1587
-
1588
- for (; count !== 0; count--) {
1589
- var item = currency(split / precision, _settings); // Add any left over pennies
1590
-
1591
- pennies-- > 0 && (item = item[intValue >= 0 ? 'add' : 'subtract'](1 / precision));
1592
- distribution.push(item);
1593
- }
1594
-
1595
- return distribution;
1596
- },
1597
-
1598
- /**
1599
- * Returns the dollar value.
1600
- * @returns {number}
1601
- */
1602
- dollars: function dollars() {
1603
- return ~~this.value;
1604
- },
1605
-
1606
- /**
1607
- * Returns the cent value.
1608
- * @returns {number}
1609
- */
1610
- cents: function cents() {
1611
- var intValue = this.intValue,
1612
- _precision = this.p;
1613
- return ~~(intValue % _precision);
1614
- },
1615
-
1616
- /**
1617
- * Formats the value as a string according to the formatting settings.
1618
- * @param {boolean} useSymbol - format with currency symbol
1619
- * @returns {string}
1620
- */
1621
- format: function format(options) {
1622
- var _settings = this.s;
1623
-
1624
- if (typeof options === 'function') {
1625
- return options(this, _settings);
1626
- }
1627
-
1628
- return _settings.format(this, Object.assign({}, _settings, options));
1629
- },
1630
-
1631
- /**
1632
- * Formats the value as a string according to the formatting settings.
1633
- * @returns {string}
1634
- */
1635
- toString: function toString() {
1636
- var intValue = this.intValue,
1637
- _precision = this.p,
1638
- _settings = this.s;
1639
- return rounding(intValue / _precision, _settings.increment).toFixed(_settings.precision);
1640
- },
1641
-
1642
- /**
1643
- * Value for JSON serialization.
1644
- * @returns {float}
1645
- */
1646
- toJSON: function toJSON() {
1647
- return this.value;
1648
- }
1649
- };
1650
-
1651
- function isNumeric(str) {
1652
- // if (typeof str === "number") {
1653
- // return true;
1654
- // }
1655
- // if (!String(str).trim()) {
1656
- if (!str.trim()) {
1657
- return false;
1658
- }
1659
- return Number(str) === Number(str);
1660
- }
1661
- const currencySigns = [
1662
- "د.إ",
1663
- "؋",
1664
- "L",
1665
- "֏",
1666
- "ƒ",
1667
- "Kz",
1668
- "$",
1669
- "ƒ",
1670
- "₼",
1671
- "KM",
1672
- "৳",
1673
- "лв",
1674
- ".د.ب",
1675
- "FBu",
1676
- "$b",
1677
- "R$",
1678
- "฿",
1679
- "Nu.",
1680
- "P",
1681
- "p.",
1682
- "BZ$",
1683
- "FC",
1684
- "CHF",
1685
- "¥",
1686
- "₡",
1687
- "₱",
1688
- "Kč",
1689
- "Fdj",
1690
- "kr",
1691
- "RD$",
1692
- "دج",
1693
- "kr",
1694
- "Nfk",
1695
- "Br",
1696
- "Ξ",
1697
- "€",
1698
- "₾",
1699
- "₵",
1700
- "GH₵",
1701
- "D",
1702
- "FG",
1703
- "Q",
1704
- "L",
1705
- "kn",
1706
- "G",
1707
- "Ft",
1708
- "Rp",
1709
- "₪",
1710
- "₹",
1711
- "ع.د",
1712
- "﷼",
1713
- "kr",
1714
- "J$",
1715
- "JD",
1716
- "¥",
1717
- "KSh",
1718
- "лв",
1719
- "៛",
1720
- "CF",
1721
- "₩",
1722
- "₩",
1723
- "KD",
1724
- "лв",
1725
- "₭",
1726
- "₨",
1727
- "M",
1728
- "Ł",
1729
- "Lt",
1730
- "Ls",
1731
- "LD",
1732
- "MAD",
1733
- "lei",
1734
- "Ar",
1735
- "ден",
1736
- "K",
1737
- "₮",
1738
- "MOP$",
1739
- "UM",
1740
- "₨",
1741
- "Rf",
1742
- "MK",
1743
- "RM",
1744
- "MT",
1745
- "₦",
1746
- "C$",
1747
- "kr",
1748
- "₨",
1749
- "﷼",
1750
- "B/.",
1751
- "S/.",
1752
- "K",
1753
- "₱",
1754
- "₨",
1755
- "zł",
1756
- "Gs",
1757
- "﷼",
1758
- "¥",
1759
- "lei",
1760
- "Дин.",
1761
- "₽",
1762
- "R₣",
1763
- "﷼",
1764
- "₨",
1765
- "ج.س.",
1766
- "kr",
1767
- "£",
1768
- "Le",
1769
- "S",
1770
- "Db",
1771
- "E",
1772
- "฿",
1773
- "SM",
1774
- "T",
1775
- "د.ت",
1776
- "T$",
1777
- "₤",
1778
- "₺",
1779
- "TT$",
1780
- "NT$",
1781
- "TSh",
1782
- "₴",
1783
- "USh",
1784
- "$U",
1785
- "лв",
1786
- "Bs",
1787
- "₫",
1788
- "VT",
1789
- "WS$",
1790
- "FCFA",
1791
- "Ƀ",
1792
- "CFA",
1793
- "₣",
1794
- "﷼",
1795
- "R",
1796
- "Z$",
1797
- ];
1798
- function findType(something) {
1799
- /* istanbul ignore next */
1800
- if (typeof something !== "string") {
1801
- throw new Error(`csv-sort/util/findType(): input must be string! Currently it's: ${typeof something}`);
1802
- }
1803
- if (isNumeric(something)) {
1804
- return "numeric";
1805
- }
1806
- /* istanbul ignore next */
1807
- if (currencySigns.some((singleSign) =>
1808
- // We remove all known currency symbols one by one from this input string.
1809
- // If at least one passes as numeric after the currency symbol-removing, it's numeric.
1810
- isNumeric(something.replace(singleSign, "").replace(/[,.]/g, "")))) {
1811
- return "numeric";
1812
- }
1813
- if (!something.trim()) {
1814
- return "empty";
1815
- }
1816
- return "text";
1817
- }
1818
-
1819
- /**
1820
- * Sorts double-entry bookkeeping CSV coming from internet banking
1821
- */
1822
- function sort(input) {
1823
- let msgContent = null;
1824
- let msgType = null;
1825
- // step 1.
1826
- // ===========================
1827
- // depends what was passed in,
1828
- if (typeof input !== "string") {
1829
- throw new TypeError(`csv-sort/csvSort(): [THROW_ID_01] The input is of a wrong type! We accept either string of array of arrays. We got instead: ${typeof input}, equal to:\n${JSON.stringify(input, null, 4)}`);
1830
- }
1831
- else if (!input.trim()) {
1832
- return { res: [[""]], msgContent, msgType };
1833
- }
1834
- let content = splitEasy(input);
1835
- // step 2.
1836
- // ===========================
1837
- // - iterate from the bottom
1838
- // - calculate schema as you go to save calculation rounds
1839
- // - first row can have different amount of columns
1840
- // - think about 2D trim feature
1841
- let schema = [];
1842
- let stateHeaderRowPresent = false;
1843
- let stateDataColumnRowLengthIsConsistent = true;
1844
- const stateColumnsContainingSameValueEverywhere = [];
1845
- // used for 2D trimming:
1846
- let indexAtWhichEmptyCellsStart = null;
1847
- for (let i = content.length - 1; i >= 0; i--) {
1848
- if (!schema.length) {
1849
- // prevention against last blank row:
1850
- /* istanbul ignore next */
1851
- if (content[i].length !== 1 || content[i][0] !== "") {
1852
- for (let y = 0, len = content[i].length; y < len; y++) {
1853
- schema.push(findType(content[i][y].trim()));
1854
- if (indexAtWhichEmptyCellsStart === null &&
1855
- findType(content[i][y].trim()) === "empty") {
1856
- indexAtWhichEmptyCellsStart = y;
1857
- }
1858
- if (indexAtWhichEmptyCellsStart !== null &&
1859
- findType(content[i][y].trim()) !== "empty") {
1860
- indexAtWhichEmptyCellsStart = null;
1861
- }
1862
- }
1863
- }
1864
- }
1865
- else {
1866
- if (i === 0) {
1867
- // Check is this header row.
1868
- // Header rows should consist of only text content.
1869
- // Let's iterate through all elements and find out.
1870
- stateHeaderRowPresent = content[i].every((el) => findType(el) === "text" || findType(el) === "empty");
1871
- // if schema was calculated (this means there's header row and at least one content row),
1872
- // find out if the column length in the header differs from schema's
1873
- // if (stateHeaderRowPresent && (schema.length !== content[i].length)) {
1874
- // }
1875
- }
1876
- /* istanbul ignore else */
1877
- if (!stateHeaderRowPresent && schema.length !== content[i].length) {
1878
- stateDataColumnRowLengthIsConsistent = false;
1879
- }
1880
- let perRowIndexAtWhichEmptyCellsStart = null;
1881
- for (let y = 0, len = content[i].length; y < len; y++) {
1882
- // trim
1883
- /* istanbul ignore else */
1884
- if (perRowIndexAtWhichEmptyCellsStart === null &&
1885
- findType(content[i][y].trim()) === "empty") {
1886
- perRowIndexAtWhichEmptyCellsStart = y;
1887
- }
1888
- /* istanbul ignore else */
1889
- if (perRowIndexAtWhichEmptyCellsStart !== null &&
1890
- findType(content[i][y].trim()) !== "empty") {
1891
- perRowIndexAtWhichEmptyCellsStart = null;
1892
- }
1893
- // checking schema
1894
- /* istanbul ignore else */
1895
- if (findType(content[i][y].trim()) !== schema[y] &&
1896
- !stateHeaderRowPresent) {
1897
- const toAdd = findType(content[i][y].trim());
1898
- /* istanbul ignore else */
1899
- if (Array.isArray(schema[y])) {
1900
- if (!schema[y].includes(toAdd)) {
1901
- schema[y].push(findType(content[i][y].trim()));
1902
- }
1903
- }
1904
- else if (schema[y] !== toAdd) {
1905
- const temp = schema[y];
1906
- schema[y] = [];
1907
- schema[y].push(temp);
1908
- schema[y].push(toAdd);
1909
- }
1910
- }
1911
- }
1912
- // when row has finished, get the perRowIndexAtWhichEmptyCellsStart
1913
- // that's to cover cases where last row got schema calculated, but it
1914
- // had more empty columns than the following rows:
1915
- //
1916
- // [8, 9, 0, 1, , ]
1917
- // [4, 5, 6, 7, , ] <<< perRowIndexAtWhichEmptyCellsStart would be 3 (indexes start at zero)
1918
- // [1, 2, 3, , , ] <<< indexAtWhichEmptyCellsStart would be here 2 (indexes start at zero)
1919
- //
1920
- // as a result, indexAtWhichEmptyCellsStart above would be assigned to 3, not 2
1921
- //
1922
- // That's still an achievement, we "trimmed" CSV by two places.
1923
- // I'm saying "trimmed", but we're not really trimming yet, we're only
1924
- // setting inner variable which we will later use to limit the traversal,
1925
- // so algorithm skips those empty columns.
1926
- //
1927
- /* istanbul ignore next */
1928
- if (indexAtWhichEmptyCellsStart !== null &&
1929
- perRowIndexAtWhichEmptyCellsStart !== null &&
1930
- perRowIndexAtWhichEmptyCellsStart > indexAtWhichEmptyCellsStart &&
1931
- (!stateHeaderRowPresent || (stateHeaderRowPresent && i !== 0))) {
1932
- indexAtWhichEmptyCellsStart = perRowIndexAtWhichEmptyCellsStart;
1933
- }
1934
- }
1935
- }
1936
- /* istanbul ignore else */
1937
- if (!indexAtWhichEmptyCellsStart) {
1938
- indexAtWhichEmptyCellsStart = schema.length;
1939
- }
1940
- // find out at which index non-empty columns start. This is effectively left-side trimming.
1941
- let nonEmptyColsStartAt = 0;
1942
- for (let i = 0, len = schema.length; i < len; i++) {
1943
- if (schema[i] === "empty") {
1944
- nonEmptyColsStartAt = i;
1945
- }
1946
- else {
1947
- break;
1948
- }
1949
- }
1950
- // if there are empty column in front, trim (via slice) both content and schema
1951
- /* istanbul ignore else */
1952
- if (nonEmptyColsStartAt !== 0) {
1953
- content = content.map((arr) => arr.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart));
1954
- schema = schema.slice(nonEmptyColsStartAt + 1, indexAtWhichEmptyCellsStart);
1955
- }
1956
- // step 3.
1957
- // ===========================
1958
- // CHALLENGE: without any assumptions, identify "current balance" and "debit",
1959
- // "credit" columns by analysing their values.
1960
- //
1961
- // - double entry accounting rows will have the "current balance" which will
1962
- // be strictly numeric, and will be present across all rows. These are the
1963
- // two first signs of a "current balance" column.
1964
- // - "current balance" should also match up with at least one field under it,
1965
- // if subracted/added the value from one field in its row
1966
- // swoop in traversing the schema array to get "numeric" columns:
1967
- // ----------------
1968
- const numericSchemaColumns = [];
1969
- let balanceColumnIndex;
1970
- schema.forEach((colType, i) => {
1971
- if (colType === "numeric") {
1972
- numericSchemaColumns.push(i);
1973
- }
1974
- });
1975
- const traverseUpToThisIndexAtTheTop = stateHeaderRowPresent ? 1 : 0;
1976
- if (numericSchemaColumns.length === 1) {
1977
- // Bob's your uncle, the only numeric column is your Balance column
1978
- balanceColumnIndex = numericSchemaColumns[0];
1979
- }
1980
- else if (numericSchemaColumns.length === 0) {
1981
- throw new Error('csv-sort/csvSort(): [THROW_ID_03] Your CSV file does not contain numeric-only columns and computer was not able to detect the "Balance" column!');
1982
- }
1983
- else {
1984
- // So (numericSchemaColumns > 0) and we'll have to do some work.
1985
- // Fine.
1986
- //
1987
- // Clone numericSchemaColumns array, remove columns that have the same value
1988
- // among consecutive rows.
1989
- // For example, accounting CSV's will have "Account number" repeated.
1990
- // Balance is never the same on two rows, otherwise what's the point of
1991
- // accounting if nothing happened?
1992
- // Traverse the CSV vertically on each column from numericSchemaColumns and
1993
- // find out `balanceColumnIndex`:
1994
- // ----------------
1995
- let potentialBalanceColumnIndexesList = Array.from(numericSchemaColumns);
1996
- // iterate through `potentialBalanceColumnIndexesList`
1997
- const deleteFromPotentialBalanceColumnIndexesList = [];
1998
- for (let i = 0, len = potentialBalanceColumnIndexesList.length; i < len; i++) {
1999
- // if any two rows are in sequence currently and they are equal, this column is out
2000
- const suspectedBalanceColumnsIndexNumber = potentialBalanceColumnIndexesList[i];
2001
- // we traverse column suspected to be "Balance" with index `index` vertically,
2002
- // from the top to bottom. Depending if there's heading row, we start at 0 or 1,
2003
- // which is set by `traverseUpToThisIndexAtTheTop`.
2004
- // We will look for two rows having the same value. If it's found that column is
2005
- // not "Balance":
2006
- // EASY ATTEMPT TO RULE-OUT NOT-BALANCE COLUMNS
2007
- let previousValue; // to check if two consecutive are the same
2008
- let lookForTwoEqualAndConsecutive = true;
2009
- let firstValue; // to check if all are the same
2010
- let lookForAllTheSame = true;
2011
- for (let rowNum = traverseUpToThisIndexAtTheTop, len2 = content.length; rowNum < len2; rowNum++) {
2012
- // 1. check for two consecutive equal values
2013
- /* istanbul ignore else */
2014
- if (lookForTwoEqualAndConsecutive) {
2015
- // deliberate == to catch undefined and null
2016
- if (previousValue == null) {
2017
- previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
2018
- }
2019
- else if (previousValue ===
2020
- content[rowNum][suspectedBalanceColumnsIndexNumber]) {
2021
- // potentialBalanceColumnIndexesList.splice(suspectedBalanceColumnsIndexNumber, 1)
2022
- // don't mutate the `potentialBalanceColumnIndexesList`, do it later.
2023
- // Let's compile TO-DELETE list instead:
2024
- deleteFromPotentialBalanceColumnIndexesList.push(suspectedBalanceColumnsIndexNumber);
2025
- lookForTwoEqualAndConsecutive = false;
2026
- }
2027
- else {
2028
- previousValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
2029
- }
2030
- }
2031
- // 2. also, tell if ALL values are the same:
2032
- /* istanbul ignore else */
2033
- if (lookForAllTheSame) {
2034
- // deliberate == to catch undefined and null
2035
- if (firstValue == null) {
2036
- firstValue = content[rowNum][suspectedBalanceColumnsIndexNumber];
2037
- }
2038
- else if (content[rowNum][suspectedBalanceColumnsIndexNumber] !== firstValue) {
2039
- lookForAllTheSame = false;
2040
- }
2041
- }
2042
- if (!lookForTwoEqualAndConsecutive) {
2043
- break;
2044
- }
2045
- }
2046
- /* istanbul ignore else */
2047
- if (lookForAllTheSame) {
2048
- stateColumnsContainingSameValueEverywhere.push(suspectedBalanceColumnsIndexNumber);
2049
- }
2050
- }
2051
- // now mutate the `potentialBalanceColumnIndexesList` using
2052
- // `deleteFromPotentialBalanceColumnIndexesList`:
2053
- potentialBalanceColumnIndexesList = lodash_pull(potentialBalanceColumnIndexesList, ...deleteFromPotentialBalanceColumnIndexesList);
2054
- /* istanbul ignore else */
2055
- if (potentialBalanceColumnIndexesList.length === 1) {
2056
- balanceColumnIndex = potentialBalanceColumnIndexesList[0];
2057
- }
2058
- else if (potentialBalanceColumnIndexesList.length === 0) {
2059
- throw new Error('csv-sort/csvSort(): [THROW_ID_04] The computer can\'t find the "Balance" column! It saw some numeric-only columns, but they all seem to have certain rows with the same values as rows right below/above them!');
2060
- }
2061
- else ;
2062
- // at this point 99% of normal-size, real-life bank account CSV's should have
2063
- // "Balance" column identified because there will be both "Credit" and "Debit"
2064
- // transaction rows which will be not exclusively numeric, but ["empty", "numeric"] type.
2065
- // Even Lloyds Business banking CSV's that output account numbers
2066
- // will have "Balance" column identified this stage.
2067
- }
2068
- if (!balanceColumnIndex) {
2069
- throw new Error("csv-sort/csvSort(): [THROW_ID_05] Sadly computer couldn't find its way in this CSV and had to stop working on it.");
2070
- }
2071
- // step 4.
2072
- // ===========================
2073
- // query the schema and find out potential Credit/Debit columns
2074
- // take schema, filter all indexes that are equal to or are arrays and have
2075
- // "numeric" among their values, then remove the index of "Balance" column:
2076
- const potentialCreditDebitColumns = lodash_pull(Array.from(schema.reduce((result, el, index) => {
2077
- if ((typeof el === "string" && el === "numeric") ||
2078
- (Array.isArray(el) && el.includes("numeric"))) {
2079
- result.push(index);
2080
- }
2081
- return result;
2082
- }, [])), balanceColumnIndex, ...stateColumnsContainingSameValueEverywhere);
2083
- // step 5.
2084
- // ===========================
2085
- const resContent = [];
2086
- // Now that we know the `balanceColumnIndex`, traverse the CSV rows again,
2087
- // assembling a new array
2088
- // step 5.1. Put the last row into the new array.
2089
- // ---------------------------------------------------------------------------
2090
- // Worst case scenario, if it doesn't match with anything, we'll throw in the end.
2091
- // For now, let's assume CSV is correct, only rows are mixed.
2092
- resContent.push(content[content.length - 1].slice(0, indexAtWhichEmptyCellsStart));
2093
- const usedUpRows = [];
2094
- const bottom = stateHeaderRowPresent ? 1 : 0;
2095
- for (let y = content.length - 2; y >= bottom; y--) {
2096
- // for each row above the last-one (which is already in place), we'll traverse
2097
- // all the rows above to find the match.
2098
- // go through all the rows and pick the right row which matches to the above:
2099
- for (let suspectedRowsIndex = content.length - 2; suspectedRowsIndex >= bottom; suspectedRowsIndex--) {
2100
- if (!usedUpRows.includes(suspectedRowsIndex)) {
2101
- // go through each of the suspected Credit/Debit columns:
2102
- let thisRowIsDone = false;
2103
- for (let suspectedColIndex = 0, len = potentialCreditDebitColumns.length; suspectedColIndex < len; suspectedColIndex++) {
2104
- let diffVal = null;
2105
- if (content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
2106
- diffVal = currency(content[suspectedRowsIndex][potentialCreditDebitColumns[suspectedColIndex]]);
2107
- }
2108
- let totalVal = null;
2109
- /* istanbul ignore else */
2110
- if (content[suspectedRowsIndex][balanceColumnIndex] !== "") {
2111
- totalVal = currency(content[suspectedRowsIndex][balanceColumnIndex]);
2112
- }
2113
- let topmostResContentBalance = null;
2114
- /* istanbul ignore else */
2115
- if (resContent[0][balanceColumnIndex] !== "") {
2116
- topmostResContentBalance = currency(resContent[0][balanceColumnIndex]).format();
2117
- }
2118
- let currentRowsDiffVal = null;
2119
- /* istanbul ignore else */
2120
- if (resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]] !== "") {
2121
- currentRowsDiffVal = currency(resContent[resContent.length - 1][potentialCreditDebitColumns[suspectedColIndex]]).format();
2122
- }
2123
- let lastResContentRowsBalance = null;
2124
- /* istanbul ignore else */
2125
- if (resContent[resContent.length - 1][balanceColumnIndex] !== "") {
2126
- lastResContentRowsBalance = currency(resContent[resContent.length - 1][balanceColumnIndex]);
2127
- }
2128
- /* istanbul ignore else */
2129
- if (diffVal &&
2130
- totalVal.add(diffVal).format() ===
2131
- topmostResContentBalance) {
2132
- // ADD THIS ROW ABOVE EVERYTHING
2133
- // add this row above the current HEAD in resContent lines array (index `0`)
2134
- resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
2135
- usedUpRows.push(suspectedRowsIndex);
2136
- thisRowIsDone = true;
2137
- break;
2138
- }
2139
- else if (diffVal &&
2140
- totalVal.subtract(diffVal).format() ===
2141
- topmostResContentBalance) {
2142
- // ADD THIS ROW ABOVE EVERYTHING
2143
- resContent.unshift(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
2144
- usedUpRows.push(suspectedRowsIndex);
2145
- thisRowIsDone = true;
2146
- break;
2147
- }
2148
- else if (currentRowsDiffVal &&
2149
- lastResContentRowsBalance
2150
- .add(currentRowsDiffVal)
2151
- .format() === totalVal.format()) {
2152
- // ADD THIS ROW BELOW EVERYTHING
2153
- resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
2154
- usedUpRows.push(suspectedRowsIndex);
2155
- thisRowIsDone = true;
2156
- break;
2157
- }
2158
- else if (currentRowsDiffVal &&
2159
- lastResContentRowsBalance
2160
- .subtract(currentRowsDiffVal)
2161
- .format() === totalVal.format()) {
2162
- // ADD THIS ROW BELOW EVERYTHING
2163
- resContent.push(content[suspectedRowsIndex].slice(0, indexAtWhichEmptyCellsStart));
2164
- usedUpRows.push(suspectedRowsIndex);
2165
- thisRowIsDone = true;
2166
- break;
2167
- }
2168
- }
2169
- /* istanbul ignore else */
2170
- if (thisRowIsDone) {
2171
- thisRowIsDone = false;
2172
- break;
2173
- }
2174
- }
2175
- }
2176
- }
2177
- // restore title row if present
2178
- /* istanbul ignore else */
2179
- if (stateHeaderRowPresent) {
2180
- // trim header row of trailing empty columns if they protrude outside of the (consistent row length) schema
2181
- if (stateDataColumnRowLengthIsConsistent &&
2182
- content[0].length > schema.length) {
2183
- content[0].length = schema.length;
2184
- }
2185
- // push header row on top of the results array:
2186
- resContent.unshift(content[0].slice(0, indexAtWhichEmptyCellsStart));
2187
- }
2188
- /* istanbul ignore else */
2189
- if (content.length - (stateHeaderRowPresent ? 2 : 1) !== usedUpRows.length) {
2190
- msgContent = "Not all rows were recognised!";
2191
- msgType = "alert";
2192
- }
2193
- return {
2194
- res: resContent,
2195
- msgContent,
2196
- msgType,
2197
- };
2198
- }
2199
-
2200
- exports.sort = sort;
2201
-
2202
- Object.defineProperty(exports, '__esModule', { value: true });
2203
-
2204
- })));