1 | 'use strict';
|
---|
2 |
|
---|
3 | const BYTE_UNITS = [
|
---|
4 | 'B',
|
---|
5 | 'kB',
|
---|
6 | 'MB',
|
---|
7 | 'GB',
|
---|
8 | 'TB',
|
---|
9 | 'PB',
|
---|
10 | 'EB',
|
---|
11 | 'ZB',
|
---|
12 | 'YB'
|
---|
13 | ];
|
---|
14 |
|
---|
15 | const BIBYTE_UNITS = [
|
---|
16 | 'B',
|
---|
17 | 'kiB',
|
---|
18 | 'MiB',
|
---|
19 | 'GiB',
|
---|
20 | 'TiB',
|
---|
21 | 'PiB',
|
---|
22 | 'EiB',
|
---|
23 | 'ZiB',
|
---|
24 | 'YiB'
|
---|
25 | ];
|
---|
26 |
|
---|
27 | const BIT_UNITS = [
|
---|
28 | 'b',
|
---|
29 | 'kbit',
|
---|
30 | 'Mbit',
|
---|
31 | 'Gbit',
|
---|
32 | 'Tbit',
|
---|
33 | 'Pbit',
|
---|
34 | 'Ebit',
|
---|
35 | 'Zbit',
|
---|
36 | 'Ybit'
|
---|
37 | ];
|
---|
38 |
|
---|
39 | const BIBIT_UNITS = [
|
---|
40 | 'b',
|
---|
41 | 'kibit',
|
---|
42 | 'Mibit',
|
---|
43 | 'Gibit',
|
---|
44 | 'Tibit',
|
---|
45 | 'Pibit',
|
---|
46 | 'Eibit',
|
---|
47 | 'Zibit',
|
---|
48 | 'Yibit'
|
---|
49 | ];
|
---|
50 |
|
---|
51 | /*
|
---|
52 | Formats the given number using `Number#toLocaleString`.
|
---|
53 | - If locale is a string, the value is expected to be a locale-key (for example: `de`).
|
---|
54 | - If locale is true, the system default locale is used for translation.
|
---|
55 | - If no value for locale is specified, the number is returned unmodified.
|
---|
56 | */
|
---|
57 | const toLocaleString = (number, locale, options) => {
|
---|
58 | let result = number;
|
---|
59 | if (typeof locale === 'string' || Array.isArray(locale)) {
|
---|
60 | result = number.toLocaleString(locale, options);
|
---|
61 | } else if (locale === true || options !== undefined) {
|
---|
62 | result = number.toLocaleString(undefined, options);
|
---|
63 | }
|
---|
64 |
|
---|
65 | return result;
|
---|
66 | };
|
---|
67 |
|
---|
68 | module.exports = (number, options) => {
|
---|
69 | if (!Number.isFinite(number)) {
|
---|
70 | throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`);
|
---|
71 | }
|
---|
72 |
|
---|
73 | options = Object.assign({bits: false, binary: false}, options);
|
---|
74 |
|
---|
75 | const UNITS = options.bits ?
|
---|
76 | (options.binary ? BIBIT_UNITS : BIT_UNITS) :
|
---|
77 | (options.binary ? BIBYTE_UNITS : BYTE_UNITS);
|
---|
78 |
|
---|
79 | if (options.signed && number === 0) {
|
---|
80 | return ` 0 ${UNITS[0]}`;
|
---|
81 | }
|
---|
82 |
|
---|
83 | const isNegative = number < 0;
|
---|
84 | const prefix = isNegative ? '-' : (options.signed ? '+' : '');
|
---|
85 |
|
---|
86 | if (isNegative) {
|
---|
87 | number = -number;
|
---|
88 | }
|
---|
89 |
|
---|
90 | let localeOptions;
|
---|
91 |
|
---|
92 | if (options.minimumFractionDigits !== undefined) {
|
---|
93 | localeOptions = {minimumFractionDigits: options.minimumFractionDigits};
|
---|
94 | }
|
---|
95 |
|
---|
96 | if (options.maximumFractionDigits !== undefined) {
|
---|
97 | localeOptions = Object.assign({maximumFractionDigits: options.maximumFractionDigits}, localeOptions);
|
---|
98 | }
|
---|
99 |
|
---|
100 | if (number < 1) {
|
---|
101 | const numberString = toLocaleString(number, options.locale, localeOptions);
|
---|
102 | return prefix + numberString + ' ' + UNITS[0];
|
---|
103 | }
|
---|
104 |
|
---|
105 | const exponent = Math.min(Math.floor(options.binary ? Math.log(number) / Math.log(1024) : Math.log10(number) / 3), UNITS.length - 1);
|
---|
106 | // eslint-disable-next-line unicorn/prefer-exponentiation-operator
|
---|
107 | number /= Math.pow(options.binary ? 1024 : 1000, exponent);
|
---|
108 |
|
---|
109 | if (!localeOptions) {
|
---|
110 | number = number.toPrecision(3);
|
---|
111 | }
|
---|
112 |
|
---|
113 | const numberString = toLocaleString(Number(number), options.locale, localeOptions);
|
---|
114 |
|
---|
115 | const unit = UNITS[exponent];
|
---|
116 |
|
---|
117 | return prefix + numberString + ' ' + unit;
|
---|
118 | };
|
---|