monocart-coverage-reports 2.5.9 → 2.6.0

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,484 +1,484 @@
1
- const Util = {
2
-
3
- // definition
4
- tagPattern: /(\s*)@([^@\s]+)(\s*)/g,
5
- lineBreakPattern: /\r\n|[\r\n\u2028\u2029]/gu,
6
-
7
- hasOwn: function(obj, key) {
8
- return Object.prototype.hasOwnProperty.call(obj, key);
9
- },
10
-
11
- isNull: function(input) {
12
- if (input === null || typeof input === 'undefined') {
13
- return true;
14
- }
15
- return false;
16
- },
17
-
18
- uid: function(len = 20, prefix = '') {
19
- const dict = '0123456789abcdefghijklmnopqrstuvwxyz';
20
- const dictLen = dict.length;
21
- let str = prefix;
22
- while (len--) {
23
- str += dict[Math.random() * dictLen | 0];
24
- }
25
- return str;
26
- },
27
-
28
- zero: function(s, l = 2) {
29
- s = `${s}`;
30
- return s.padStart(l, '0');
31
- },
32
-
33
- toNum: function(num, toInt) {
34
- if (typeof (num) !== 'number') {
35
- num = parseFloat(num);
36
- }
37
- if (isNaN(num)) {
38
- num = 0;
39
- }
40
- if (toInt) {
41
- num = Math.round(num);
42
- }
43
- return num;
44
- },
45
-
46
- isList: function(data) {
47
- if (data && data instanceof Array && data.length > 0) {
48
- return true;
49
- }
50
- return false;
51
- },
52
-
53
- toList: function(data, separator) {
54
- if (data instanceof Array) {
55
- return data;
56
- }
57
- if (typeof data === 'string' && (typeof separator === 'string' || separator instanceof RegExp)) {
58
- return data.split(separator).map((str) => str.trim()).filter((str) => str);
59
- }
60
- if (typeof data === 'undefined' || data === null) {
61
- return [];
62
- }
63
- return [data];
64
- },
65
-
66
- forEach: function(rootList, callback) {
67
- const isBreak = (res) => {
68
- return res === 'break' || res === false;
69
- };
70
- const forList = (list, parent) => {
71
- if (!Util.isList(list)) {
72
- return;
73
- }
74
- for (const item of list) {
75
- const result = callback(item, parent);
76
- if (isBreak(result)) {
77
- return result;
78
- }
79
- const subResult = forList(item.subs, item);
80
- if (isBreak(subResult)) {
81
- return subResult;
82
- }
83
- }
84
- };
85
- forList(rootList);
86
- },
87
-
88
- // \ to /
89
- formatPath: function(str) {
90
- if (str) {
91
- str = str.replace(/\\/g, '/');
92
- }
93
- return str;
94
- },
95
-
96
- getCurrentTrendInfo: (data) => {
97
-
98
- const {
99
- date, duration, summary
100
- } = data;
101
-
102
- const info = {
103
- date,
104
- duration
105
- };
106
-
107
- Object.keys(summary).forEach((k) => {
108
- const item = summary[k];
109
- info[k] = item.value;
110
- });
111
-
112
- return info;
113
- },
114
-
115
- isTagItem: (item) => {
116
- if (item.type === 'case' || (item.type === 'suite' && item.suiteType === 'describe')) {
117
- return true;
118
- }
119
- return false;
120
- },
121
-
122
- delay: function(ms) {
123
- return new Promise((resolve) => {
124
- if (ms) {
125
- setTimeout(resolve, ms);
126
- } else {
127
- setImmediate(resolve);
128
- }
129
- });
130
- },
131
-
132
- // =============================================================================
133
-
134
- getMetrics: (type, metrics) => {
135
- const istanbulMetrics = ['statements', 'branches', 'functions', 'lines'];
136
- const v8Metrics = ['bytes'].concat(istanbulMetrics);
137
- const allMetrics = type === 'v8' ? v8Metrics : istanbulMetrics;
138
- let list = allMetrics;
139
- if (Util.isList(metrics)) {
140
- const newList = list.filter((k) => metrics.includes(k));
141
- if (newList.length) {
142
- list = newList;
143
- }
144
- }
145
- return list;
146
- },
147
-
148
- generatePercentChart: function(percent) {
149
- return `<div style="--mcr-percent:${percent}%;" class="mcr-percent-chart"></div>`;
150
- },
151
-
152
- getStatus: (value, watermarks) => {
153
- if (!watermarks) {
154
- return 'unknown';
155
- }
156
- if (value < watermarks[0]) {
157
- return 'low';
158
- }
159
- if (value < watermarks[1]) {
160
- return 'medium';
161
- }
162
- return 'high';
163
- },
164
-
165
- isJsonType: (contentType) => {
166
- if (contentType) {
167
- if (contentType === 'application/json' || contentType === 'json') {
168
- return true;
169
- }
170
- }
171
- return false;
172
- },
173
-
174
- isMarkdownType: (contentType) => {
175
- if (contentType) {
176
- if (contentType === 'text/markdown' || contentType === 'markdown') {
177
- return true;
178
- }
179
- }
180
- return false;
181
- },
182
-
183
- isTextType: (contentType) => {
184
- if (contentType) {
185
- if (contentType.startsWith('text')) {
186
- return true;
187
- }
188
- if (Util.isMarkdownType(contentType)) {
189
- return true;
190
- }
191
- if (Util.isJsonType(contentType)) {
192
- return true;
193
- }
194
- }
195
- return false;
196
- },
197
-
198
- isBlank: (codeStr) => {
199
- if (typeof codeStr === 'string') {
200
- const blankBlock = /\S/;
201
- return !blankBlock.test(codeStr);
202
- }
203
- return false;
204
- },
205
-
206
- findInRanges: (startPos, endPos, rangeList, startKey = 'start', endKey = 'end') => {
207
- if (!Util.isList(rangeList)) {
208
- return;
209
- }
210
-
211
- const quickFindRange = (position, ranges) => {
212
- let start = 0;
213
- let end = ranges.length - 1;
214
- while (end - start > 1) {
215
- const i = Math.floor((start + end) * 0.5);
216
- const item = ranges[i];
217
- if (position < item[startKey]) {
218
- end = i;
219
- continue;
220
- }
221
- if (position > item[endKey]) {
222
- start = i;
223
- continue;
224
- }
225
- return ranges[i];
226
- }
227
- // last two items, less is start
228
- const endItem = ranges[end];
229
- if (position < endItem[startKey]) {
230
- return ranges[start];
231
- }
232
- return ranges[end];
233
- };
234
-
235
- const range = quickFindRange(startPos, rangeList);
236
- if (startPos >= range[startKey] && endPos <= range[endKey]) {
237
- return range;
238
- }
239
- },
240
-
241
- getRangeLines: (sLoc, eLoc) => {
242
-
243
- const lines = [];
244
-
245
- // invalid location order
246
- if (sLoc.line > eLoc.line) {
247
- return lines;
248
- }
249
-
250
- // nothing middle
251
- if (sLoc.line === eLoc.line && sLoc.column >= eLoc.column) {
252
- return lines;
253
- }
254
-
255
- const addStart = () => {
256
-
257
- // nothing start
258
- if (sLoc.column >= sLoc.length) {
259
- return;
260
- }
261
-
262
- const entire = sLoc.column <= sLoc.indent;
263
- lines.push({
264
- line: sLoc.line,
265
- entire,
266
- pieces: {
267
- start: sLoc.column,
268
- end: sLoc.length
269
- }
270
- });
271
- };
272
-
273
- const addMiddle = () => {
274
-
275
- const start = sLoc.column <= sLoc.indent;
276
- const end = Util.isBlank(eLoc.text.slice(eLoc.column));
277
- const entire = start && end;
278
- // same line
279
- lines.push({
280
- line: sLoc.line,
281
- entire,
282
- pieces: {
283
- start: sLoc.column,
284
- end: eLoc.column
285
- }
286
- });
287
- };
288
-
289
- const addEnd = () => {
290
-
291
- // nothing end
292
- if (eLoc.column === 0) {
293
- return;
294
- }
295
-
296
- // pieces in indent, ignored
297
- if (eLoc.column <= eLoc.indent) {
298
- return;
299
- }
300
-
301
- const rightText = eLoc.text.slice(eLoc.column);
302
-
303
- // right text is blank or only ";" (few cases)
304
- const entire = Util.isBlank(rightText) || rightText.trim() === ';';
305
- lines.push({
306
- line: eLoc.line,
307
- entire,
308
- pieces: {
309
- start: eLoc.indent,
310
- end: eLoc.column
311
- }
312
- });
313
- };
314
-
315
- // same line, single line
316
- if (sLoc.line === eLoc.line) {
317
-
318
- addMiddle();
319
-
320
- } else {
321
- // multiple lines
322
-
323
- addStart();
324
-
325
- // always entire for middle lines
326
- const lineStart = sLoc.line + 1;
327
- const lineEnd = eLoc.line;
328
- if (lineEnd > lineStart) {
329
- for (let i = lineStart; i < lineEnd; i++) {
330
- lines.push({
331
- line: i,
332
- entire: true
333
- // no pieces for entire line
334
- });
335
- }
336
- }
337
-
338
- // check end
339
- addEnd();
340
- }
341
-
342
- return lines;
343
- },
344
-
345
- // =============================================================================
346
- // svg
347
-
348
- dFixed: (num, fixed = 1) => {
349
- if (Number.isInteger(num)) {
350
- return num;
351
- }
352
- return Util.toNum(Util.toNum(num).toFixed(fixed));
353
- },
354
-
355
- pxFixed: (num) => {
356
- const floor = Math.floor(num);
357
- if (num < floor + 0.5) {
358
- return floor + 0.5;
359
- }
360
- return floor + 1.5;
361
- },
362
-
363
- point: (px, py) => {
364
- return `${Util.dFixed(px)},${Util.dFixed(py)}`;
365
- },
366
-
367
- capitalizeFirstLetter: (string) => {
368
- return string.charAt(0).toUpperCase() + string.slice(1);
369
- },
370
-
371
- // =============================================================================
372
- // formatter
373
-
374
- // number
375
- NF: function(v) {
376
- if (typeof v === 'number' && v) {
377
- return v.toLocaleString();
378
- }
379
- return v;
380
- },
381
-
382
- // percent
383
- PF: function(v, t = 1, digits = 1, unit = '%', space = '') {
384
- v = Util.toNum(v);
385
- t = Util.toNum(t);
386
- let per = 0;
387
- if (t) {
388
- per = v / t;
389
- }
390
- const perStr = (per * 100).toFixed(digits);
391
- if (unit) {
392
- return perStr + space + unit;
393
- }
394
- return parseFloat(perStr);
395
- },
396
-
397
- PSF: function(v, t = 1, digits = 1) {
398
- return Util.PF(v, t, digits, '%', ' ');
399
- },
400
-
401
- PNF: function(v, t = 1, digits = 1) {
402
- return Util.PF(v, t, digits, '');
403
- },
404
-
405
- // byte
406
- BF: function(v, places = 1, space = '') {
407
- v = Util.toNum(v, true);
408
- if (v === 0) {
409
- return `0${space}B`;
410
- }
411
- let prefix = '';
412
- if (v < 0) {
413
- v = Math.abs(v);
414
- prefix = '-';
415
- }
416
-
417
- const base = 1024;
418
- const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
419
- for (let i = 0, l = units.length; i < l; i++) {
420
- const min = Math.pow(base, i);
421
- const max = Math.pow(base, i + 1);
422
- if (v > min && v < max) {
423
- const unit = units[i];
424
- v = prefix + parseFloat((v / min).toFixed(places)) + space + unit;
425
- break;
426
- }
427
- }
428
- return v;
429
- },
430
-
431
- BSF: function(v, places = 1) {
432
- return Util.BF(v, places, ' ');
433
- },
434
-
435
- // time
436
- TF: function(v, space = '') {
437
- const ms = Util.toNum(v, true);
438
-
439
- if (ms < 1000) {
440
- return `${ms}${space}ms`;
441
- }
442
-
443
- if (ms < 60 * 1000) {
444
- const ss = Math.floor(ms / 1000);
445
- const sms = Math.round((ms - ss * 1000) / 100);
446
- if (sms) {
447
- return `${ss}.${sms}${space}s`;
448
- }
449
- return `${ss}${space}s`;
450
- }
451
-
452
- const s = Math.round(ms / 1000);
453
-
454
- const m = 60;
455
- const h = m * 60;
456
- const d = h * 24;
457
-
458
- if (s < h) {
459
- const minutes = Math.floor(s / m);
460
- const seconds = s - minutes * m;
461
- return `${minutes}${space}m ${seconds}${space}s`;
462
- }
463
-
464
- if (s < d) {
465
- const hours = Math.floor(s / h);
466
- const minutes = Math.floor((s - hours * h) / m);
467
- const seconds = s - hours * h - minutes * m;
468
- return `${hours}${space}h ${minutes}${space}m ${seconds}${space}s`;
469
- }
470
-
471
- const days = Math.floor(s / d);
472
- const hours = Math.floor((s - days * d) / h);
473
- const minutes = Math.floor((s - days * d - hours * h) / m);
474
- const seconds = s - days * d - hours * h - minutes * m;
475
- return `${days}${space}d ${hours}${space}h ${minutes}${space}m ${seconds}${space}s`;
476
- },
477
-
478
- TSF: function(v) {
479
- return Util.TF(v, ' ');
480
- }
481
- };
482
-
483
-
484
- module.exports = Util;
1
+ const Util = {
2
+
3
+ // definition
4
+ tagPattern: /(\s*)@([^@\s]+)(\s*)/g,
5
+ lineBreakPattern: /\r\n|[\r\n\u2028\u2029]/gu,
6
+
7
+ hasOwn: function(obj, key) {
8
+ return Object.prototype.hasOwnProperty.call(obj, key);
9
+ },
10
+
11
+ isNull: function(input) {
12
+ if (input === null || typeof input === 'undefined') {
13
+ return true;
14
+ }
15
+ return false;
16
+ },
17
+
18
+ uid: function(len = 20, prefix = '') {
19
+ const dict = '0123456789abcdefghijklmnopqrstuvwxyz';
20
+ const dictLen = dict.length;
21
+ let str = prefix;
22
+ while (len--) {
23
+ str += dict[Math.random() * dictLen | 0];
24
+ }
25
+ return str;
26
+ },
27
+
28
+ zero: function(s, l = 2) {
29
+ s = `${s}`;
30
+ return s.padStart(l, '0');
31
+ },
32
+
33
+ toNum: function(num, toInt) {
34
+ if (typeof (num) !== 'number') {
35
+ num = parseFloat(num);
36
+ }
37
+ if (isNaN(num)) {
38
+ num = 0;
39
+ }
40
+ if (toInt) {
41
+ num = Math.round(num);
42
+ }
43
+ return num;
44
+ },
45
+
46
+ isList: function(data) {
47
+ if (data && data instanceof Array && data.length > 0) {
48
+ return true;
49
+ }
50
+ return false;
51
+ },
52
+
53
+ toList: function(data, separator) {
54
+ if (data instanceof Array) {
55
+ return data;
56
+ }
57
+ if (typeof data === 'string' && (typeof separator === 'string' || separator instanceof RegExp)) {
58
+ return data.split(separator).map((str) => str.trim()).filter((str) => str);
59
+ }
60
+ if (typeof data === 'undefined' || data === null) {
61
+ return [];
62
+ }
63
+ return [data];
64
+ },
65
+
66
+ forEach: function(rootList, callback) {
67
+ const isBreak = (res) => {
68
+ return res === 'break' || res === false;
69
+ };
70
+ const forList = (list, parent) => {
71
+ if (!Util.isList(list)) {
72
+ return;
73
+ }
74
+ for (const item of list) {
75
+ const result = callback(item, parent);
76
+ if (isBreak(result)) {
77
+ return result;
78
+ }
79
+ const subResult = forList(item.subs, item);
80
+ if (isBreak(subResult)) {
81
+ return subResult;
82
+ }
83
+ }
84
+ };
85
+ forList(rootList);
86
+ },
87
+
88
+ // \ to /
89
+ formatPath: function(str) {
90
+ if (str) {
91
+ str = str.replace(/\\/g, '/');
92
+ }
93
+ return str;
94
+ },
95
+
96
+ getCurrentTrendInfo: (data) => {
97
+
98
+ const {
99
+ date, duration, summary
100
+ } = data;
101
+
102
+ const info = {
103
+ date,
104
+ duration
105
+ };
106
+
107
+ Object.keys(summary).forEach((k) => {
108
+ const item = summary[k];
109
+ info[k] = item.value;
110
+ });
111
+
112
+ return info;
113
+ },
114
+
115
+ isTagItem: (item) => {
116
+ if (item.type === 'case' || (item.type === 'suite' && item.suiteType === 'describe')) {
117
+ return true;
118
+ }
119
+ return false;
120
+ },
121
+
122
+ delay: function(ms) {
123
+ return new Promise((resolve) => {
124
+ if (ms) {
125
+ setTimeout(resolve, ms);
126
+ } else {
127
+ setImmediate(resolve);
128
+ }
129
+ });
130
+ },
131
+
132
+ // =============================================================================
133
+
134
+ getMetrics: (metrics, type) => {
135
+ const istanbulMetrics = ['statements', 'branches', 'functions', 'lines'];
136
+ const v8Metrics = ['bytes'].concat(istanbulMetrics);
137
+ const allMetrics = type === 'istanbul' ? istanbulMetrics : v8Metrics;
138
+ let list = allMetrics;
139
+ if (Util.isList(metrics)) {
140
+ const newList = list.filter((k) => metrics.includes(k));
141
+ if (newList.length) {
142
+ list = newList;
143
+ }
144
+ }
145
+ return list;
146
+ },
147
+
148
+ generatePercentChart: function(percent) {
149
+ return `<div style="--mcr-percent:${percent}%;" class="mcr-percent-chart"></div>`;
150
+ },
151
+
152
+ getStatus: (value, watermarks) => {
153
+ if (!watermarks) {
154
+ return 'unknown';
155
+ }
156
+ if (value < watermarks[0]) {
157
+ return 'low';
158
+ }
159
+ if (value < watermarks[1]) {
160
+ return 'medium';
161
+ }
162
+ return 'high';
163
+ },
164
+
165
+ isJsonType: (contentType) => {
166
+ if (contentType) {
167
+ if (contentType === 'application/json' || contentType === 'json') {
168
+ return true;
169
+ }
170
+ }
171
+ return false;
172
+ },
173
+
174
+ isMarkdownType: (contentType) => {
175
+ if (contentType) {
176
+ if (contentType === 'text/markdown' || contentType === 'markdown') {
177
+ return true;
178
+ }
179
+ }
180
+ return false;
181
+ },
182
+
183
+ isTextType: (contentType) => {
184
+ if (contentType) {
185
+ if (contentType.startsWith('text')) {
186
+ return true;
187
+ }
188
+ if (Util.isMarkdownType(contentType)) {
189
+ return true;
190
+ }
191
+ if (Util.isJsonType(contentType)) {
192
+ return true;
193
+ }
194
+ }
195
+ return false;
196
+ },
197
+
198
+ isBlank: (codeStr) => {
199
+ if (typeof codeStr === 'string') {
200
+ const blankBlock = /\S/;
201
+ return !blankBlock.test(codeStr);
202
+ }
203
+ return false;
204
+ },
205
+
206
+ findInRanges: (startPos, endPos, rangeList, startKey = 'start', endKey = 'end') => {
207
+ if (!Util.isList(rangeList)) {
208
+ return;
209
+ }
210
+
211
+ const quickFindRange = (position, ranges) => {
212
+ let start = 0;
213
+ let end = ranges.length - 1;
214
+ while (end - start > 1) {
215
+ const i = Math.floor((start + end) * 0.5);
216
+ const item = ranges[i];
217
+ if (position < item[startKey]) {
218
+ end = i;
219
+ continue;
220
+ }
221
+ if (position > item[endKey]) {
222
+ start = i;
223
+ continue;
224
+ }
225
+ return ranges[i];
226
+ }
227
+ // last two items, less is start
228
+ const endItem = ranges[end];
229
+ if (position < endItem[startKey]) {
230
+ return ranges[start];
231
+ }
232
+ return ranges[end];
233
+ };
234
+
235
+ const range = quickFindRange(startPos, rangeList);
236
+ if (startPos >= range[startKey] && endPos <= range[endKey]) {
237
+ return range;
238
+ }
239
+ },
240
+
241
+ getRangeLines: (sLoc, eLoc) => {
242
+
243
+ const lines = [];
244
+
245
+ // invalid location order
246
+ if (sLoc.line > eLoc.line) {
247
+ return lines;
248
+ }
249
+
250
+ // nothing middle
251
+ if (sLoc.line === eLoc.line && sLoc.column >= eLoc.column) {
252
+ return lines;
253
+ }
254
+
255
+ const addStart = () => {
256
+
257
+ // nothing start
258
+ if (sLoc.column >= sLoc.length) {
259
+ return;
260
+ }
261
+
262
+ const entire = sLoc.column <= sLoc.indent;
263
+ lines.push({
264
+ line: sLoc.line,
265
+ entire,
266
+ pieces: {
267
+ start: sLoc.column,
268
+ end: sLoc.length
269
+ }
270
+ });
271
+ };
272
+
273
+ const addMiddle = () => {
274
+
275
+ const start = sLoc.column <= sLoc.indent;
276
+ const end = Util.isBlank(eLoc.text.slice(eLoc.column));
277
+ const entire = start && end;
278
+ // same line
279
+ lines.push({
280
+ line: sLoc.line,
281
+ entire,
282
+ pieces: {
283
+ start: sLoc.column,
284
+ end: eLoc.column
285
+ }
286
+ });
287
+ };
288
+
289
+ const addEnd = () => {
290
+
291
+ // nothing end
292
+ if (eLoc.column === 0) {
293
+ return;
294
+ }
295
+
296
+ // pieces in indent, ignored
297
+ if (eLoc.column <= eLoc.indent) {
298
+ return;
299
+ }
300
+
301
+ const rightText = eLoc.text.slice(eLoc.column);
302
+
303
+ // right text is blank or only ";" (few cases)
304
+ const entire = Util.isBlank(rightText) || rightText.trim() === ';';
305
+ lines.push({
306
+ line: eLoc.line,
307
+ entire,
308
+ pieces: {
309
+ start: eLoc.indent,
310
+ end: eLoc.column
311
+ }
312
+ });
313
+ };
314
+
315
+ // same line, single line
316
+ if (sLoc.line === eLoc.line) {
317
+
318
+ addMiddle();
319
+
320
+ } else {
321
+ // multiple lines
322
+
323
+ addStart();
324
+
325
+ // always entire for middle lines
326
+ const lineStart = sLoc.line + 1;
327
+ const lineEnd = eLoc.line;
328
+ if (lineEnd > lineStart) {
329
+ for (let i = lineStart; i < lineEnd; i++) {
330
+ lines.push({
331
+ line: i,
332
+ entire: true
333
+ // no pieces for entire line
334
+ });
335
+ }
336
+ }
337
+
338
+ // check end
339
+ addEnd();
340
+ }
341
+
342
+ return lines;
343
+ },
344
+
345
+ // =============================================================================
346
+ // svg
347
+
348
+ dFixed: (num, fixed = 1) => {
349
+ if (Number.isInteger(num)) {
350
+ return num;
351
+ }
352
+ return Util.toNum(Util.toNum(num).toFixed(fixed));
353
+ },
354
+
355
+ pxFixed: (num) => {
356
+ const floor = Math.floor(num);
357
+ if (num < floor + 0.5) {
358
+ return floor + 0.5;
359
+ }
360
+ return floor + 1.5;
361
+ },
362
+
363
+ point: (px, py) => {
364
+ return `${Util.dFixed(px)},${Util.dFixed(py)}`;
365
+ },
366
+
367
+ capitalizeFirstLetter: (string) => {
368
+ return string.charAt(0).toUpperCase() + string.slice(1);
369
+ },
370
+
371
+ // =============================================================================
372
+ // formatter
373
+
374
+ // number
375
+ NF: function(v) {
376
+ if (typeof v === 'number' && v) {
377
+ return v.toLocaleString();
378
+ }
379
+ return v;
380
+ },
381
+
382
+ // percent
383
+ PF: function(v, t = 1, digits = 1, unit = '%', space = '') {
384
+ v = Util.toNum(v);
385
+ t = Util.toNum(t);
386
+ let per = 0;
387
+ if (t) {
388
+ per = v / t;
389
+ }
390
+ const perStr = (per * 100).toFixed(digits);
391
+ if (unit) {
392
+ return perStr + space + unit;
393
+ }
394
+ return parseFloat(perStr);
395
+ },
396
+
397
+ PSF: function(v, t = 1, digits = 1) {
398
+ return Util.PF(v, t, digits, '%', ' ');
399
+ },
400
+
401
+ PNF: function(v, t = 1, digits = 1) {
402
+ return Util.PF(v, t, digits, '');
403
+ },
404
+
405
+ // byte
406
+ BF: function(v, places = 1, space = '') {
407
+ v = Util.toNum(v, true);
408
+ if (v === 0) {
409
+ return `0${space}B`;
410
+ }
411
+ let prefix = '';
412
+ if (v < 0) {
413
+ v = Math.abs(v);
414
+ prefix = '-';
415
+ }
416
+
417
+ const base = 1024;
418
+ const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
419
+ for (let i = 0, l = units.length; i < l; i++) {
420
+ const min = Math.pow(base, i);
421
+ const max = Math.pow(base, i + 1);
422
+ if (v > min && v < max) {
423
+ const unit = units[i];
424
+ v = prefix + parseFloat((v / min).toFixed(places)) + space + unit;
425
+ break;
426
+ }
427
+ }
428
+ return v;
429
+ },
430
+
431
+ BSF: function(v, places = 1) {
432
+ return Util.BF(v, places, ' ');
433
+ },
434
+
435
+ // time
436
+ TF: function(v, space = '') {
437
+ const ms = Util.toNum(v, true);
438
+
439
+ if (ms < 1000) {
440
+ return `${ms}${space}ms`;
441
+ }
442
+
443
+ if (ms < 60 * 1000) {
444
+ const ss = Math.floor(ms / 1000);
445
+ const sms = Math.round((ms - ss * 1000) / 100);
446
+ if (sms) {
447
+ return `${ss}.${sms}${space}s`;
448
+ }
449
+ return `${ss}${space}s`;
450
+ }
451
+
452
+ const s = Math.round(ms / 1000);
453
+
454
+ const m = 60;
455
+ const h = m * 60;
456
+ const d = h * 24;
457
+
458
+ if (s < h) {
459
+ const minutes = Math.floor(s / m);
460
+ const seconds = s - minutes * m;
461
+ return `${minutes}${space}m ${seconds}${space}s`;
462
+ }
463
+
464
+ if (s < d) {
465
+ const hours = Math.floor(s / h);
466
+ const minutes = Math.floor((s - hours * h) / m);
467
+ const seconds = s - hours * h - minutes * m;
468
+ return `${hours}${space}h ${minutes}${space}m ${seconds}${space}s`;
469
+ }
470
+
471
+ const days = Math.floor(s / d);
472
+ const hours = Math.floor((s - days * d) / h);
473
+ const minutes = Math.floor((s - days * d - hours * h) / m);
474
+ const seconds = s - days * d - hours * h - minutes * m;
475
+ return `${days}${space}d ${hours}${space}h ${minutes}${space}m ${seconds}${space}s`;
476
+ },
477
+
478
+ TSF: function(v) {
479
+ return Util.TF(v, ' ');
480
+ }
481
+ };
482
+
483
+
484
+ module.exports = Util;