import { ARRAY, BIT, BITS, BYTE, BYTES, EMPTY, EXPONENT, FUNCTION, IEC, INVALID_NUMBER, INVALID_ROUND, JEDEC, OBJECT, PERIOD, ROUND, S, SI, SI_KBIT, SI_KBYTE, SPACE, STRING, STRINGS, ZERO } from "./constants"; export function filesize (arg, { bits = false, pad = false, base = -1, round = 2, locale = EMPTY, localeOptions = {}, separator = EMPTY, spacer = SPACE, symbols = {}, standard = EMPTY, output = STRING, fullform = false, fullforms = [], exponent = -1, roundingMethod = ROUND, precision = 0 } = {}) { let e = exponent, num = Number(arg), result = [], val = 0, u = EMPTY; // Sync base & standard if (standard === SI) { base = 10; standard = JEDEC; } else if (standard === IEC || standard === JEDEC) { base = 2; } else if (base === 2) { standard = IEC; } else { base = 10; standard = JEDEC; } const ceil = base === 10 ? 1000 : 1024, full = fullform === true, neg = num < 0, roundingFunc = Math[roundingMethod]; if (typeof arg !== "bigint" && isNaN(arg)) { throw new TypeError(INVALID_NUMBER); } if (typeof roundingFunc !== FUNCTION) { throw new TypeError(INVALID_ROUND); } // Flipping a negative number to determine the size if (neg) { num = -num; } // Determining the exponent if (e === -1 || isNaN(e)) { e = Math.floor(Math.log(num) / Math.log(ceil)); if (e < 0) { e = 0; } } // Exceeding supported length, time to reduce & multiply if (e > 8) { if (precision > 0) { precision += 8 - e; } e = 8; } if (output === EXPONENT) { return e; } // Zero is now a special case because bytes divide by 1 if (num === 0) { result[0] = 0; u = result[1] = STRINGS.symbol[standard][bits ? BITS : BYTES][e]; } else { val = num / (base === 2 ? Math.pow(2, e * 10) : Math.pow(1000, e)); if (bits) { val = val * 8; if (val >= ceil && e < 8) { val = val / ceil; e++; } } const p = Math.pow(10, e > 0 ? round : 0); result[0] = roundingFunc(val * p) / p; if (result[0] === ceil && e < 8 && exponent === -1) { result[0] = 1; e++; } u = result[1] = base === 10 && e === 1 ? bits ? SI_KBIT : SI_KBYTE : STRINGS.symbol[standard][bits ? BITS : BYTES][e]; } // Decorating a 'diff' if (neg) { result[0] = -result[0]; } // Setting optional precision if (precision > 0) { result[0] = result[0].toPrecision(precision); } // Applying custom symbol result[1] = symbols[result[1]] || result[1]; if (locale === true) { result[0] = result[0].toLocaleString(); } else if (locale.length > 0) { result[0] = result[0].toLocaleString(locale, localeOptions); } else if (separator.length > 0) { result[0] = result[0].toString().replace(PERIOD, separator); } if (pad && Number.isInteger(result[0]) === false && round > 0) { const x = separator || PERIOD, tmp = result[0].toString().split(x), s = tmp[1] || EMPTY, l = s.length, n = round - l; result[0] = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`; } if (full) { result[1] = fullforms[e] ? fullforms[e] : STRINGS.fullform[standard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); } // Returning Array, Object, or String (default) return output === ARRAY ? result : output === OBJECT ? { value: result[0], symbol: result[1], exponent: e, unit: u } : result.join(spacer); } // Partial application for functional programming export function partial ({ bits = false, pad = false, base = -1, round = 2, locale = EMPTY, localeOptions = {}, separator = EMPTY, spacer = SPACE, symbols = {}, standard = EMPTY, output = STRING, fullform = false, fullforms = [], exponent = -1, roundingMethod = ROUND, precision = 0 } = {}) { return arg => filesize(arg, { bits, pad, base, round, locale, localeOptions, separator, spacer, symbols, standard, output, fullform, fullforms, exponent, roundingMethod, precision }); }