|
| 1 | +/** |
| 2 | + * filesize |
| 3 | + * |
| 4 | + * @copyright 2021 Jason Mulligan <[email protected]> |
| 5 | + * @license BSD-3-Clause |
| 6 | + * @version 7.0.0 |
| 7 | + */ |
| 8 | +const b = /^(b|B)$/, |
| 9 | + symbol = { |
| 10 | + iec: { |
| 11 | + bits: ["b", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib"], |
| 12 | + bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] |
| 13 | + }, |
| 14 | + jedec: { |
| 15 | + bits: ["b", "Kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb"], |
| 16 | + bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] |
| 17 | + } |
| 18 | + }, |
| 19 | + fullform = { |
| 20 | + iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"], |
| 21 | + jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"] |
| 22 | + }, |
| 23 | + roundingFuncs = { |
| 24 | + floor: Math.floor, |
| 25 | + ceil: Math.ceil |
| 26 | + }; |
| 27 | + |
| 28 | +/** |
| 29 | + * filesize |
| 30 | + * |
| 31 | + * @method filesize |
| 32 | + * @param {Mixed} arg String, Int or Float to transform |
| 33 | + * @param {Object} descriptor [Optional] Flags |
| 34 | + * @return {String} Readable file size String |
| 35 | + */ |
| 36 | +function filesize (arg, descriptor = {}) { |
| 37 | + let result = [], |
| 38 | + val = 0, |
| 39 | + e, base, bits, ceil, full, fullforms, locale, localeOptions, neg, num, output, pad, round, u, unix, separator, spacer, standard, symbols, roundingFunc, precision; |
| 40 | + |
| 41 | + if (isNaN(arg)) { |
| 42 | + throw new TypeError("Invalid number"); |
| 43 | + } |
| 44 | + |
| 45 | + bits = descriptor.bits === true; |
| 46 | + unix = descriptor.unix === true; |
| 47 | + pad = descriptor.pad === true; |
| 48 | + base = descriptor.base || 2; |
| 49 | + round = descriptor.round !== void 0 ? descriptor.round : unix ? 1 : 2; |
| 50 | + locale = descriptor.locale !== void 0 ? descriptor.locale : ""; |
| 51 | + localeOptions = descriptor.localeOptions || {}; |
| 52 | + separator = descriptor.separator !== void 0 ? descriptor.separator : ""; |
| 53 | + spacer = descriptor.spacer !== void 0 ? descriptor.spacer : unix ? "" : " "; |
| 54 | + symbols = descriptor.symbols || {}; |
| 55 | + standard = base === 2 ? descriptor.standard || "jedec" : "jedec"; |
| 56 | + output = descriptor.output || "string"; |
| 57 | + full = descriptor.fullform === true; |
| 58 | + fullforms = descriptor.fullforms instanceof Array ? descriptor.fullforms : []; |
| 59 | + e = descriptor.exponent !== void 0 ? descriptor.exponent : -1; |
| 60 | + roundingFunc = roundingFuncs[descriptor.roundingMethod] || Math.round; |
| 61 | + num = Number(arg); |
| 62 | + neg = num < 0; |
| 63 | + ceil = base > 2 ? 1000 : 1024; |
| 64 | + precision = isNaN(descriptor.precision) === false ? parseInt(descriptor.precision, 10) : 0; |
| 65 | + |
| 66 | + // Flipping a negative number to determine the size |
| 67 | + if (neg) { |
| 68 | + num = -num; |
| 69 | + } |
| 70 | + |
| 71 | + // Determining the exponent |
| 72 | + if (e === -1 || isNaN(e)) { |
| 73 | + e = Math.floor(Math.log(num) / Math.log(ceil)); |
| 74 | + |
| 75 | + if (e < 0) { |
| 76 | + e = 0; |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + // Exceeding supported length, time to reduce & multiply |
| 81 | + if (e > 8) { |
| 82 | + if (precision > 0) { |
| 83 | + precision += 8 - e; |
| 84 | + } |
| 85 | + |
| 86 | + e = 8; |
| 87 | + } |
| 88 | + |
| 89 | + if (output === "exponent") { |
| 90 | + return e; |
| 91 | + } |
| 92 | + |
| 93 | + // Zero is now a special case because bytes divide by 1 |
| 94 | + if (num === 0) { |
| 95 | + result[0] = 0; |
| 96 | + u = result[1] = unix ? "" : symbol[standard][bits ? "bits" : "bytes"][e]; |
| 97 | + } else { |
| 98 | + val = num / (base === 2 ? Math.pow(2, e * 10) : Math.pow(1000, e)); |
| 99 | + |
| 100 | + if (bits) { |
| 101 | + val = val * 8; |
| 102 | + |
| 103 | + if (val >= ceil && e < 8) { |
| 104 | + val = val / ceil; |
| 105 | + e++; |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + const p = Math.pow(10, e > 0 ? round : 0); |
| 110 | + result[0] = roundingFunc(val * p) / p; |
| 111 | + |
| 112 | + if (result[0] === ceil && e < 8 && descriptor.exponent === void 0) { |
| 113 | + result[0] = 1; |
| 114 | + e++; |
| 115 | + } |
| 116 | + |
| 117 | + u = result[1] = base === 10 && e === 1 ? bits ? "kb" : "kB" : symbol[standard][bits ? "bits" : "bytes"][e]; |
| 118 | + |
| 119 | + if (unix) { |
| 120 | + result[1] = standard === "jedec" ? result[1].charAt(0) : e > 0 ? result[1].replace(/B$/, "") : result[1]; |
| 121 | + |
| 122 | + if (b.test(result[1])) { |
| 123 | + result[0] = Math.floor(result[0]); |
| 124 | + result[1] = ""; |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + |
| 129 | + // Decorating a 'diff' |
| 130 | + if (neg) { |
| 131 | + result[0] = -result[0]; |
| 132 | + } |
| 133 | + |
| 134 | + // Setting optional precision |
| 135 | + if (precision > 0) { |
| 136 | + result[0] = result[0].toPrecision(precision); |
| 137 | + } |
| 138 | + |
| 139 | + // Applying custom symbol |
| 140 | + result[1] = symbols[result[1]] || result[1]; |
| 141 | + |
| 142 | + if (locale === true) { |
| 143 | + result[0] = result[0].toLocaleString(); |
| 144 | + } else if (locale.length > 0) { |
| 145 | + result[0] = result[0].toLocaleString(locale, localeOptions); |
| 146 | + } else if (separator.length > 0) { |
| 147 | + result[0] = result[0].toString().replace(".", separator); |
| 148 | + } |
| 149 | + |
| 150 | + if (pad && Number.isInteger(result[0]) === false && round > 0) { |
| 151 | + const x = separator || ".", |
| 152 | + tmp = result[0].toString().split(x), |
| 153 | + s = tmp[1] || "", |
| 154 | + l = s.length, |
| 155 | + n = round - l; |
| 156 | + |
| 157 | + result[0] = `${tmp[0]}${x}${s.padEnd(l + n, "0")}`; |
| 158 | + } |
| 159 | + |
| 160 | + if (full) { |
| 161 | + result[1] = fullforms[e] ? fullforms[e] : fullform[standard][e] + (bits ? "bit" : "byte") + (result[0] === 1 ? "" : "s"); |
| 162 | + } |
| 163 | + |
| 164 | + // Returning Array, Object, or String (default) |
| 165 | + return output === "array" ? result : output === "object" ? {value: result[0], symbol: result[1], exponent: e, unit: u} : result.join(spacer); |
| 166 | +} |
| 167 | + |
| 168 | +// Partial application for functional programming |
| 169 | +filesize.partial = opt => arg => filesize(arg, opt); |
| 170 | + |
| 171 | +export default filesize; |
0 commit comments