Skip to content
Snippets Groups Projects
index.js 3.53 KiB
Newer Older
  • Learn to ignore specific revisions
  • Jay's avatar
    Jay committed
    /*!
     * bytes
     * Copyright(c) 2012-2014 TJ Holowaychuk
     * Copyright(c) 2015 Jed Watson
     * MIT Licensed
     */
    
    'use strict';
    
    /**
     * Module exports.
     * @public
     */
    
    module.exports = bytes;
    module.exports.format = format;
    module.exports.parse = parse;
    
    /**
     * Module variables.
     * @private
     */
    
    var formatThousandsRegExp = /\B(?=(\d{3})+(?!\d))/g;
    
    var formatDecimalsRegExp = /(?:\.0*|(\.[^0]+)0+)$/;
    
    var map = {
      b:  1,
      kb: 1 << 10,
      mb: 1 << 20,
      gb: 1 << 30,
      tb: Math.pow(1024, 4),
      pb: Math.pow(1024, 5),
    };
    
    var parseRegExp = /^((-|\+)?(\d+(?:\.\d+)?)) *(kb|mb|gb|tb|pb)$/i;
    
    /**
     * Convert the given value in bytes into a string or parse to string to an integer in bytes.
     *
     * @param {string|number} value
     * @param {{
     *  case: [string],
     *  decimalPlaces: [number]
     *  fixedDecimals: [boolean]
     *  thousandsSeparator: [string]
     *  unitSeparator: [string]
     *  }} [options] bytes options.
     *
     * @returns {string|number|null}
     */
    
    function bytes(value, options) {
      if (typeof value === 'string') {
        return parse(value);
      }
    
      if (typeof value === 'number') {
        return format(value, options);
      }
    
      return null;
    }
    
    /**
     * Format the given value in bytes into a string.
     *
     * If the value is negative, it is kept as such. If it is a float,
     * it is rounded.
     *
     * @param {number} value
     * @param {object} [options]
     * @param {number} [options.decimalPlaces=2]
     * @param {number} [options.fixedDecimals=false]
     * @param {string} [options.thousandsSeparator=]
     * @param {string} [options.unit=]
     * @param {string} [options.unitSeparator=]
     *
     * @returns {string|null}
     * @public
     */
    
    function format(value, options) {
      if (!Number.isFinite(value)) {
        return null;
      }
    
      var mag = Math.abs(value);
      var thousandsSeparator = (options && options.thousandsSeparator) || '';
      var unitSeparator = (options && options.unitSeparator) || '';
      var decimalPlaces = (options && options.decimalPlaces !== undefined) ? options.decimalPlaces : 2;
      var fixedDecimals = Boolean(options && options.fixedDecimals);
      var unit = (options && options.unit) || '';
    
      if (!unit || !map[unit.toLowerCase()]) {
        if (mag >= map.pb) {
          unit = 'PB';
        } else if (mag >= map.tb) {
          unit = 'TB';
        } else if (mag >= map.gb) {
          unit = 'GB';
        } else if (mag >= map.mb) {
          unit = 'MB';
        } else if (mag >= map.kb) {
          unit = 'KB';
        } else {
          unit = 'B';
        }
      }
    
      var val = value / map[unit.toLowerCase()];
      var str = val.toFixed(decimalPlaces);
    
      if (!fixedDecimals) {
        str = str.replace(formatDecimalsRegExp, '$1');
      }
    
      if (thousandsSeparator) {
        str = str.split('.').map(function (s, i) {
          return i === 0
            ? s.replace(formatThousandsRegExp, thousandsSeparator)
            : s
        }).join('.');
      }
    
      return str + unitSeparator + unit;
    }
    
    /**
     * Parse the string value into an integer in bytes.
     *
     * If no unit is given, it is assumed the value is in bytes.
     *
     * @param {number|string} val
     *
     * @returns {number|null}
     * @public
     */
    
    function parse(val) {
      if (typeof val === 'number' && !isNaN(val)) {
        return val;
      }
    
      if (typeof val !== 'string') {
        return null;
      }
    
      // Test if the string passed is valid
      var results = parseRegExp.exec(val);
      var floatValue;
      var unit = 'b';
    
      if (!results) {
        // Nothing could be extracted from the given string
        floatValue = parseInt(val, 10);
        unit = 'b'
      } else {
        // Retrieve the value and the unit
        floatValue = parseFloat(results[1]);
        unit = results[4].toLowerCase();
      }
    
      if (isNaN(floatValue)) {
        return null;
      }
    
      return Math.floor(map[unit] * floatValue);
    }