/*! * numeral.js * version : 1.4.9 * author : Adam Draper * license : MIT * http://adamwdraper.github.com/Numeral-js/ */ (function () { /************************************ Constants ************************************/ var numeral, VERSION = '1.4.9', // internal storage for language config files languages = {}, currentLanguage = 'en', zeroFormat = null, // check for nodeJS hasModule = (typeof module !== 'undefined' && module.exports); /************************************ Constructors ************************************/ // Numeral prototype object function Numeral (number) { this._n = number; } /** * Implementation of toFixed() that treats floats more like decimals * * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present * problems for accounting- and finance-related software. */ function toFixed (value, precision, optionals) { var power = Math.pow(10, precision), output; // Multiply up by precision, round accurately, then divide and use native toFixed(): output = (Math.round(value * power) / power).toFixed(precision); if (optionals) { var optionalsRegExp = new RegExp('0{1,' + optionals + '}$'); output = output.replace(optionalsRegExp, ''); } return output; } /************************************ Formatting ************************************/ // determine what type of formatting we need to do function formatNumeral (n, format) { var output; // figure out what kind of format we are dealing with if (format.indexOf('$') > -1) { // currency!!!!! output = formatCurrency(n, format); } else if (format.indexOf('%') > -1) { // percentage output = formatPercentage(n, format); } else if (format.indexOf(':') > -1) { // time output = formatTime(n, format); } else { // plain ol' numbers or bytes output = formatNumber(n, format); } // return string return output; } // revert to number function unformatNumeral (n, string) { if (string.indexOf(':') > -1) { n._n = unformatTime(string); } else { if (string === zeroFormat) { n._n = 0; } else { var stringOriginal = string; if (languages[currentLanguage].delimiters.decimal !== '.') { string = string.replace(/\./g,'').replace(languages[currentLanguage].delimiters.decimal, '.'); } // see if abbreviations are there so that we can multiply to the correct number var thousandRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.thousand + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'), millionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.million + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'), billionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.billion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'), trillionRegExp = new RegExp('[^a-zA-Z]' + languages[currentLanguage].abbreviations.trillion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'); // see if bytes are there so that we can multiply to the correct number var prefixes = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], bytesMultiplier = false; for (var power = 0; power <= prefixes.length; power++) { bytesMultiplier = (string.indexOf(prefixes[power]) > -1) ? Math.pow(1024, power + 1) : false; if (bytesMultiplier) { break; } } // do some math to create our number n._n = ((bytesMultiplier) ? bytesMultiplier : 1) * ((stringOriginal.match(thousandRegExp)) ? Math.pow(10, 3) : 1) * ((stringOriginal.match(millionRegExp)) ? Math.pow(10, 6) : 1) * ((stringOriginal.match(billionRegExp)) ? Math.pow(10, 9) : 1) * ((stringOriginal.match(trillionRegExp)) ? Math.pow(10, 12) : 1) * ((string.indexOf('%') > -1) ? 0.01 : 1) * (((string.split('-').length + Math.min(string.split('(').length-1, string.split(')').length-1)) % 2)? 1: -1) * Number(string.replace(/[^0-9\.]+/g, '')); // round if we are talking about bytes n._n = (bytesMultiplier) ? Math.ceil(n._n) : n._n; } } return n._n; } function formatCurrency (n, format) { var prependSymbol = (format.indexOf('$') <= 1) ? true : false; // remove $ for the moment var space = ''; // check for space before or after currency if (format.indexOf(' $') > -1) { space = ' '; format = format.replace(' $', ''); } else if (format.indexOf('$ ') > -1) { space = ' '; format = format.replace('$ ', ''); } else { format = format.replace('$', ''); } // format the number var output = formatNumeral(n, format); // position the symbol if (prependSymbol) { if (output.indexOf('(') > -1 || output.indexOf('-') > -1) { output = output.split(''); output.splice(1, 0, languages[currentLanguage].currency.symbol + space); output = output.join(''); } else { output = languages[currentLanguage].currency.symbol + space + output; } } else { if (output.indexOf(')') > -1) { output = output.split(''); output.splice(-1, 0, space + languages[currentLanguage].currency.symbol); output = output.join(''); } else { output = output + space + languages[currentLanguage].currency.symbol; } } return output; } function formatPercentage (n, format) { var space = ''; // check for space before % if (format.indexOf(' %') > -1) { space = ' '; format = format.replace(' %', ''); } else { format = format.replace('%', ''); } n._n = n._n * 100; var output = formatNumeral(n, format); if (output.indexOf(')') > -1 ) { output = output.split(''); output.splice(-1, 0, space + '%'); output = output.join(''); } else { output = output + space + '%'; } return output; } function formatTime (n, format) { var hours = Math.floor(n._n/60/60), minutes = Math.floor((n._n - (hours * 60 * 60))/60), seconds = Math.round(n._n - (hours * 60 * 60) - (minutes * 60)); return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds); } function unformatTime (string) { var timeArray = string.split(':'), seconds = 0; // turn hours and minutes into seconds and add them all up if (timeArray.length === 3) { // hours seconds = seconds + (Number(timeArray[0]) * 60 * 60); // minutes seconds = seconds + (Number(timeArray[1]) * 60); // seconds seconds = seconds + Number(timeArray[2]); } else if (timeArray.length === 2) { // minutes seconds = seconds + (Number(timeArray[0]) * 60); // seconds seconds = seconds + Number(timeArray[1]); } return Number(seconds); } function formatNumber (n, format) { var negP = false, optDec = false, abbr = '', bytes = '', ord = '', abs = Math.abs(n._n); // check if number is zero and a custom zero format has been set if (n._n === 0 && zeroFormat !== null) { return zeroFormat; } else { // see if we should use parentheses for negative number if (format.indexOf('(') > -1) { negP = true; format = format.slice(1, -1); } // see if abbreviation is wanted if (format.indexOf('a') > -1) { // check for space before abbreviation if (format.indexOf(' a') > -1) { abbr = ' '; format = format.replace(' a', ''); } else { format = format.replace('a', ''); } if (abs >= Math.pow(10, 12)) { // trillion abbr = abbr + languages[currentLanguage].abbreviations.trillion; n._n = n._n / Math.pow(10, 12); } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9)) { // billion abbr = abbr + languages[currentLanguage].abbreviations.billion; n._n = n._n / Math.pow(10, 9); } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6)) { // million abbr = abbr + languages[currentLanguage].abbreviations.million; n._n = n._n / Math.pow(10, 6); } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3)) { // thousand abbr = abbr + languages[currentLanguage].abbreviations.thousand; n._n = n._n / Math.pow(10, 3); } } // see if we are formatting bytes if (format.indexOf('b') > -1) { // check for space before if (format.indexOf(' b') > -1) { bytes = ' '; format = format.replace(' b', ''); } else { format = format.replace('b', ''); } var prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], min, max; for (var power = 0; power <= prefixes.length; power++) { min = Math.pow(1024, power); max = Math.pow(1024, power+1); if (n._n >= min && n._n < max) { bytes = bytes + prefixes[power]; if (min > 0) { n._n = n._n / min; } break; } } } // see if ordinal is wanted if (format.indexOf('o') > -1) { // check for space before if (format.indexOf(' o') > -1) { ord = ' '; format = format.replace(' o', ''); } else { format = format.replace('o', ''); } ord = ord + languages[currentLanguage].ordinal(n._n); } if (format.indexOf('[.]') > -1) { optDec = true; format = format.replace('[.]', '.'); } var w = n._n.toString().split('.')[0], precision = format.split('.')[1], thousands = format.indexOf(','), d = '', neg = false; if (precision) { if (precision.indexOf('[') > -1) { precision = precision.replace(']', ''); precision = precision.split('['); d = toFixed(n._n, (precision[0].length + precision[1].length), precision[1].length); } else { d = toFixed(n._n, precision.length); } w = d.split('.')[0]; if (d.split('.')[1].length) { d = languages[currentLanguage].delimiters.decimal + d.split('.')[1]; } else { d = ''; } if (optDec && Number(d.slice(1)) === 0) { d = ''; } } else { w = toFixed(n._n, null); } // format number if (w.indexOf('-') > -1) { w = w.slice(1); neg = true; } if (thousands > -1) { w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands); } if (format.indexOf('.') === 0) { w = ''; } return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : ''); } } /************************************ Top Level Functions ************************************/ numeral = function (input) { if (numeral.isNumeral(input)) { input = input.value(); } else if (input == 0 || typeof input == "undefined") { input = 0; } else if (!Number(input)) { input = numeral.fn.unformat(input); } return new Numeral(Number(input)); }; // version number numeral.version = VERSION; // compare numeral object numeral.isNumeral = function (obj) { return obj instanceof Numeral; }; // This function will load languages and then set the global language. If // no arguments are passed in, it will simply return the current global // language key. numeral.language = function (key, values) { if (!key) { return currentLanguage; } if (key && !values) { if(!languages[key]) { throw new Error('Unknown language : ' + key); } currentLanguage = key; } if (values || !languages[key]) { loadLanguage(key, values); } return numeral; }; numeral.language('en', { delimiters: { thousands: ',', decimal: '.' }, abbreviations: { thousand: 'k', million: 'm', billion: 'b', trillion: 't' }, ordinal: function (number) { var b = number % 10; return (~~ (number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; }, currency: { symbol: '$' } }); numeral.zeroFormat = function (format) { if (typeof(format) === 'string') { zeroFormat = format; } else { zeroFormat = null; } }; /************************************ Helpers ************************************/ function loadLanguage(key, values) { languages[key] = values; } /************************************ Numeral Prototype ************************************/ numeral.fn = Numeral.prototype = { clone : function () { return numeral(this); }, format : function (inputString) { return formatNumeral(this, inputString ? inputString : numeral.defaultFormat); }, unformat : function (inputString) { return unformatNumeral(this, inputString ? inputString : numeral.defaultFormat); }, value : function () { return this._n; }, valueOf : function () { return this._n; }, set : function (value) { this._n = Number(value); return this; }, add : function (value) { this._n = this._n + Number(value); return this; }, subtract : function (value) { this._n = this._n - Number(value); return this; }, multiply : function (value) { this._n = this._n * Number(value); return this; }, divide : function (value) { this._n = this._n / Number(value); return this; }, difference : function (value) { var difference = this._n - Number(value); if (difference < 0) { difference = -difference; } return difference; } }; /************************************ Exposing Numeral ************************************/ // CommonJS module is defined if (hasModule) { module.exports = numeral; } /*global ender:false */ if (typeof ender === 'undefined') { // here, `this` means `window` in the browser, or `global` on the server // add `numeral` as a global object via a string identifier, // for Closure Compiler 'advanced' mode this['numeral'] = numeral; } /*global define:false */ if (typeof define === 'function' && define.amd) { define([], function () { return numeral; }); } }).call(this);