|
|
/*! * numbro.js * version : 1.11.0 * author : Företagsplatsen AB * license : MIT * http://www.foretagsplatsen.se
*/
(function () { 'use strict';
/************************************ Constants ************************************/
var numbro, VERSION = '1.11.0', binarySuffixes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'], decimalSuffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], bytes = { general: { scale: 1024, suffixes: decimalSuffixes, marker: 'bd' }, binary: { scale: 1024, suffixes: binarySuffixes, marker: 'b' }, decimal: { scale: 1000, suffixes: decimalSuffixes, marker: 'd' } }, // general must be before the others because it reuses their characters!
byteFormatOrder = [ bytes.general, bytes.binary, bytes.decimal ], // internal storage for culture config files
cultures = {}, // Todo: Remove in 2.0.0
languages = cultures, currentCulture = 'en-US', zeroFormat = null, defaultFormat = '0,0', defaultCurrencyFormat = '0$', // check for nodeJS
hasModule = (typeof module !== 'undefined' && module.exports), // default culture
enUS = { 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: '$', position: 'prefix' }, defaults: { currencyFormat: ',0000 a' }, formats: { fourDigits: '0000 a', fullWithTwoDecimals: '$ ,0.00', fullWithTwoDecimalsNoCurrency: ',0.00' } };
/************************************ Constructors ************************************/
// Numbro prototype object
function Numbro(number) { this._value = number; }
function numberLength(number) { if (number === 0) { return 1; } return Math.floor(Math.log(Math.abs(number)) / Math.LN10) + 1; }
function zeroes(count) { var i, ret = '';
for (i = 0; i < count; i++) { ret += '0'; }
return ret; } /** * Implementation of toFixed() for numbers with exponents * This function may return negative representations for zero values e.g. "-0.0" */ function toFixedLargeSmall(value, precision) { var mantissa, beforeDec, afterDec, exponent, prefix, endStr, zerosStr, str;
str = value.toString();
mantissa = str.split('e')[0]; exponent = str.split('e')[1];
beforeDec = mantissa.split('.')[0]; afterDec = mantissa.split('.')[1] || '';
if (+exponent > 0) { // exponent is positive - add zeros after the numbers
str = beforeDec + afterDec + zeroes(exponent - afterDec.length); } else { // exponent is negative
if (+beforeDec < 0) { prefix = '-0'; } else { prefix = '0'; }
// tack on the decimal point if needed
if (precision > 0) { prefix += '.'; }
zerosStr = zeroes((-1 * exponent) - 1); // substring off the end to satisfy the precision
endStr = (zerosStr + Math.abs(beforeDec) + afterDec).substr(0, precision); str = prefix + endStr; }
// only add percision 0's if the exponent is positive
if (+exponent > 0 && precision > 0) { str += '.' + zeroes(precision); }
return str; }
/** * 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. * * Also removes negative signs for zero-formatted numbers. e.g. -0.01 w/ precision 1 -> 0.0 */ function toFixed(value, precision, roundingFunction, optionals) { var power = Math.pow(10, precision), optionalsRegExp, output;
if (value.toString().indexOf('e') > -1) { // toFixed returns scientific notation for numbers above 1e21 and below 1e-7
output = toFixedLargeSmall(value, precision); // remove the leading negative sign if it exists and should not be present (e.g. -0.00)
if (output.charAt(0) === '-' && +output >= 0) { output = output.substr(1); // chop off the '-'
} } else { // Multiply up by precision, round accurately, then divide and use native toFixed():
output = (roundingFunction(value + 'e+' + precision) / power).toFixed(precision); }
if (optionals) { optionalsRegExp = new RegExp('0{1,' + optionals + '}$'); output = output.replace(optionalsRegExp, ''); }
return output; }
/************************************ Formatting ************************************/
// determine what type of formatting we need to do
function formatNumbro(n, format, roundingFunction) { var output, escapedFormat = format.replace(/\{[^\{\}]*\}/g, '');
// figure out what kind of format we are dealing with
if (escapedFormat.indexOf('$') > -1) { // currency!!!!!
output = formatCurrency(n, cultures[currentCulture].currency.symbol, format, roundingFunction); } else if (escapedFormat.indexOf('%') > -1) { // percentage
output = formatPercentage(n, format, roundingFunction); } else if (escapedFormat.indexOf(':') > -1) { // time
output = formatTime(n, format); } else { // plain ol' numbers or bytes
output = formatNumber(n._value, format, roundingFunction); }
// return string
return output; }
// revert to number
function unformatNumbro(n, string) { var stringOriginal = string, thousandRegExp, millionRegExp, billionRegExp, trillionRegExp, bytesMultiplier = false, power;
if (string.indexOf(':') > -1) { n._value = unformatTime(string); } else { if (string === zeroFormat) { n._value = 0; } else { if (cultures[currentCulture].delimiters.decimal !== '.') { string = string.replace(/\./g, '').replace(cultures[currentCulture].delimiters.decimal, '.'); }
// see if abbreviations are there so that we can multiply to the correct number
thousandRegExp = new RegExp('[^a-zA-Z]' + cultures[currentCulture].abbreviations.thousand + '(?:\\)|(\\' + cultures[currentCulture].currency.symbol + ')?(?:\\))?)?$'); millionRegExp = new RegExp('[^a-zA-Z]' + cultures[currentCulture].abbreviations.million + '(?:\\)|(\\' + cultures[currentCulture].currency.symbol + ')?(?:\\))?)?$'); billionRegExp = new RegExp('[^a-zA-Z]' + cultures[currentCulture].abbreviations.billion + '(?:\\)|(\\' + cultures[currentCulture].currency.symbol + ')?(?:\\))?)?$'); trillionRegExp = new RegExp('[^a-zA-Z]' + cultures[currentCulture].abbreviations.trillion + '(?:\\)|(\\' + cultures[currentCulture].currency.symbol + ')?(?:\\))?)?$');
// see if bytes are there so that we can multiply to the correct number
for (power = 1; power < binarySuffixes.length && !bytesMultiplier; ++power) { if (string.indexOf(binarySuffixes[power]) > -1) { bytesMultiplier = Math.pow(1024, power); } else if (string.indexOf(decimalSuffixes[power]) > -1) { bytesMultiplier = Math.pow(1000, power); } }
var str = string.replace(/[^0-9\.]+/g, ''); if (str === '') { // An empty string is not a number.
n._value = NaN;
} else { // do some math to create our number
n._value = ((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(str);
// round if we are talking about bytes
n._value = (bytesMultiplier) ? Math.ceil(n._value) : n._value; } } } return n._value; }
function formatCurrency(n, currencySymbol, originalFormat, roundingFunction) { var format = originalFormat, symbolIndex = format.indexOf('$'), openParenIndex = format.indexOf('('), plusSignIndex = format.indexOf('+'), minusSignIndex = format.indexOf('-'), space = '', decimalSeparator = '', spliceIndex, output;
if(format.indexOf('$') === -1){ // Use defaults instead of the format provided
if (cultures[currentCulture].currency.position === 'infix') { decimalSeparator = currencySymbol; if (cultures[currentCulture].currency.spaceSeparated) { decimalSeparator = ' ' + decimalSeparator + ' '; } } else if (cultures[currentCulture].currency.spaceSeparated) { space = ' '; } } else { // 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
output = formatNumber(n._value, format, roundingFunction, decimalSeparator);
if (originalFormat.indexOf('$') === -1) { // Use defaults instead of the format provided
switch (cultures[currentCulture].currency.position) { case 'postfix': if (output.indexOf(')') > -1) { output = output.split(''); output.splice(-1, 0, space + currencySymbol); output = output.join(''); } else { output = output + space + currencySymbol; } break; case 'infix': break; case 'prefix': if (output.indexOf('(') > -1 || output.indexOf('-') > -1) { output = output.split(''); spliceIndex = Math.max(openParenIndex, minusSignIndex) + 1;
output.splice(spliceIndex, 0, currencySymbol + space); output = output.join(''); } else { output = currencySymbol + space + output; } break; default: throw Error('Currency position should be among ["prefix", "infix", "postfix"]'); } } else { // position the symbol
if (symbolIndex <= 1) { if (output.indexOf('(') > -1 || output.indexOf('+') > -1 || output.indexOf('-') > -1) { output = output.split(''); spliceIndex = 1; if (symbolIndex < openParenIndex || symbolIndex < plusSignIndex || symbolIndex < minusSignIndex) { // the symbol appears before the "(", "+" or "-"
spliceIndex = 0; } output.splice(spliceIndex, 0, currencySymbol + space); output = output.join(''); } else { output = currencySymbol + space + output; } } else { if (output.indexOf(')') > -1) { output = output.split(''); output.splice(-1, 0, space + currencySymbol); output = output.join(''); } else { output = output + space + currencySymbol; } } }
return output; }
function formatForeignCurrency(n, foreignCurrencySymbol, originalFormat, roundingFunction) { return formatCurrency(n, foreignCurrencySymbol, originalFormat, roundingFunction); }
function formatPercentage(n, format, roundingFunction) { var space = '', output, value = n._value * 100;
// check for space before %
if (format.indexOf(' %') > -1) { space = ' '; format = format.replace(' %', ''); } else { format = format.replace('%', ''); }
output = formatNumber(value, format, roundingFunction);
if (output.indexOf(')') > -1) { output = output.split(''); output.splice(-1, 0, space + '%'); output = output.join(''); } else { output = output + space + '%'; }
return output; }
function formatTime(n) { var hours = Math.floor(n._value / 60 / 60), minutes = Math.floor((n._value - (hours * 60 * 60)) / 60), seconds = Math.round(n._value - (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 formatByteUnits (value, suffixes, scale) { var suffix = suffixes[0], power, min, max, abs = Math.abs(value);
if (abs >= scale) { for (power = 1; power < suffixes.length; ++power) { min = Math.pow(scale, power); max = Math.pow(scale, power + 1);
if (abs >= min && abs < max) { suffix = suffixes[power]; value = value / min; break; } }
// values greater than or equal to [scale] YB never set the suffix
if (suffix === suffixes[0]) { value = value / Math.pow(scale, suffixes.length - 1); suffix = suffixes[suffixes.length - 1]; } }
return { value: value, suffix: suffix }; }
function formatNumber (value, format, roundingFunction, sep) { var negP = false, signed = false, optDec = false, abbr = '', abbrK = false, // force abbreviation to thousands
abbrM = false, // force abbreviation to millions
abbrB = false, // force abbreviation to billions
abbrT = false, // force abbreviation to trillions
abbrForce = false, // force abbreviation
bytes = '', byteFormat, units, ord = '', abs = Math.abs(value), totalLength, length, minimumPrecision, pow, w, intPrecision, precision, prefix, postfix, thousands, d = '', forcedNeg = false, neg = false, indexOpenP, indexMinus, paren = '', minlen, i;
// check if number is zero and a custom zero format has been set
if (value === 0 && zeroFormat !== null) { return zeroFormat; }
if (!isFinite(value)) { return '' + value; }
if (format.indexOf('{') === 0) { var end = format.indexOf('}'); if (end === -1) { throw Error('Format should also contain a "}"'); } prefix = format.slice(1, end); format = format.slice(end + 1); } else { prefix = ''; }
if (format.indexOf('}') === format.length - 1 && format.length) { var start = format.indexOf('{'); if (start === -1) { throw Error('Format should also contain a "{"'); } postfix = format.slice(start + 1, -1); format = format.slice(0, start + 1); } else { postfix = ''; }
// check for min length
var info; if (format.indexOf('.') === -1) { info = format.match(/([0-9]+).*/); } else { info = format.match(/([0-9]+)\..*/); } minlen = info === null ? -1 : info[1].length;
// see if we should use parentheses for negative number or if we should prefix with a sign
// if both are present we default to parentheses
if (format.indexOf('-') !== -1) { forcedNeg = true; } if (format.indexOf('(') > -1) { negP = true; format = format.slice(1, -1); } else if (format.indexOf('+') > -1) { signed = true; format = format.replace(/\+/g, ''); }
// see if abbreviation is wanted
if (format.indexOf('a') > -1) { intPrecision = format.split('.')[0].match(/[0-9]+/g) || ['0']; intPrecision = parseInt(intPrecision[0], 10);
// check if abbreviation is specified
abbrK = format.indexOf('aK') >= 0; abbrM = format.indexOf('aM') >= 0; abbrB = format.indexOf('aB') >= 0; abbrT = format.indexOf('aT') >= 0; abbrForce = abbrK || abbrM || abbrB || abbrT;
// check for space before abbreviation
if (format.indexOf(' a') > -1) { abbr = ' '; format = format.replace(' a', ''); } else { format = format.replace('a', ''); }
totalLength = numberLength(value); minimumPrecision = totalLength % 3; minimumPrecision = minimumPrecision === 0 ? 3 : minimumPrecision;
if (intPrecision && abs !== 0) { pow = 3 * ~~((Math.min(intPrecision, totalLength) - minimumPrecision) / 3); abs = abs / Math.pow(10, pow); }
if (totalLength !== intPrecision) { if (abs >= Math.pow(10, 12) && !abbrForce || abbrT) { // trillion
abbr = abbr + cultures[currentCulture].abbreviations.trillion; value = value / Math.pow(10, 12); } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9) && !abbrForce || abbrB) { // billion
abbr = abbr + cultures[currentCulture].abbreviations.billion; value = value / Math.pow(10, 9); } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6) && !abbrForce || abbrM) { // million
abbr = abbr + cultures[currentCulture].abbreviations.million; value = value / Math.pow(10, 6); } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3) && !abbrForce || abbrK) { // thousand
abbr = abbr + cultures[currentCulture].abbreviations.thousand; value = value / Math.pow(10, 3); } }
length = numberLength(value); if (intPrecision && length < intPrecision && format.indexOf('.') === -1) { format += '[.]'; format += zeroes(intPrecision - length); } }
// see if we are formatting
// binary-decimal bytes (1024 MB), binary bytes (1024 MiB), or decimal bytes (1000 MB)
for (i = 0; i < byteFormatOrder.length; ++i) { byteFormat = byteFormatOrder[i];
if (format.indexOf(byteFormat.marker) > -1) { // check for space before
if (format.indexOf(' ' + byteFormat.marker) >-1) { bytes = ' '; }
// remove the marker (with the space if it had one)
format = format.replace(bytes + byteFormat.marker, '');
units = formatByteUnits(value, byteFormat.suffixes, byteFormat.scale);
value = units.value; bytes = bytes + units.suffix;
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', ''); }
if (cultures[currentCulture].ordinal) { ord = ord + cultures[currentCulture].ordinal(value); } }
if (format.indexOf('[.]') > -1) { optDec = true; format = format.replace('[.]', '.'); }
precision = format.split('.')[1]; thousands = format.indexOf(',');
if (precision) { var dSplit = [];
if (precision.indexOf('*') !== -1) { d = value.toString(); dSplit = d.split('.'); if (dSplit.length > 1) { d = toFixed(value, dSplit[1].length, roundingFunction); } } else { if (precision.indexOf('[') > -1) { precision = precision.replace(']', ''); precision = precision.split('['); d = toFixed(value, (precision[0].length + precision[1].length), roundingFunction, precision[1].length); } else { d = toFixed(value, precision.length, roundingFunction); } }
dSplit = d.split('.'); w = dSplit[0];
if (dSplit.length > 1 && dSplit[1].length) { var p = sep ? abbr + sep : cultures[currentCulture].delimiters.decimal; d = p + dSplit[1]; } else { d = ''; }
if (optDec && Number(d.slice(1)) === 0) { d = ''; } } else { w = toFixed(value, 0, roundingFunction); }
// format number
if (w.indexOf('-') > -1) { w = w.slice(1); neg = true; }
if (w.length < minlen) { w = zeroes(minlen - w.length) + w; }
if (thousands > -1) { w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + cultures[currentCulture].delimiters.thousands); }
if (format.indexOf('.') === 0) { w = ''; }
indexOpenP = format.indexOf('('); indexMinus = format.indexOf('-');
if (indexOpenP < indexMinus) { paren = ((negP && neg) ? '(' : '') + (((forcedNeg && neg) || (!negP && neg)) ? '-' : ''); } else { paren = (((forcedNeg && neg) || (!negP && neg)) ? '-' : '') + ((negP && neg) ? '(' : ''); }
return prefix + paren + ((!neg && signed && value !== 0) ? '+' : '') + w + d + ((ord) ? ord : '') + ((abbr && !sep) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '') + postfix; }
/************************************ Top Level Functions ************************************/
numbro = function(input) { if (numbro.isNumbro(input)) { input = input.value(); } else if (typeof input === 'string' || typeof input === 'number') { input = numbro.fn.unformat(input); } else { input = NaN; }
return new Numbro(Number(input)); };
// version number
numbro.version = VERSION;
// compare numbro object
numbro.isNumbro = function(obj) { return obj instanceof Numbro; };
/** * This function allow the user to set a new language with a fallback if * the language does not exist. If no fallback language is provided, * it fallbacks to english. * * @deprecated Since in version 1.6.0. It will be deleted in version 2.0 * `setCulture` should be used instead. */ numbro.setLanguage = function(newLanguage, fallbackLanguage) { console.warn('`setLanguage` is deprecated since version 1.6.0. Use `setCulture` instead'); var key = newLanguage, prefix = newLanguage.split('-')[0], matchingLanguage = null; if (!languages[key]) { Object.keys(languages).forEach(function(language) { if (!matchingLanguage && language.split('-')[0] === prefix) { matchingLanguage = language; } }); key = matchingLanguage || fallbackLanguage || 'en-US'; } chooseCulture(key); };
/** * This function allow the user to set a new culture with a fallback if * the culture does not exist. If no fallback culture is provided, * it falls back to "en-US". */ numbro.setCulture = function(newCulture, fallbackCulture) { var key = newCulture, suffix = newCulture.split('-')[1], matchingCulture = null; if (!cultures[key]) { if (suffix) { Object.keys(cultures).forEach(function(language) { if (!matchingCulture && language.split('-')[1] === suffix) { matchingCulture = language; } }); }
key = matchingCulture || fallbackCulture || 'en-US'; } chooseCulture(key); };
/** * 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. * * @deprecated Since in version 1.6.0. It will be deleted in version 2.0 * `culture` should be used instead. */ numbro.language = function(key, values) { console.warn('`language` is deprecated since version 1.6.0. Use `culture` instead');
if (!key) { return currentCulture; }
if (key && !values) { if (!languages[key]) { throw new Error('Unknown language : ' + key); } chooseCulture(key); }
if (values || !languages[key]) { setCulture(key, values); }
return numbro; };
/** * This function will load cultures and then set the global culture. If * no arguments are passed in, it will simply return the current global * culture code. */ numbro.culture = function(code, values) { if (!code) { return currentCulture; }
if (code && !values) { if (!cultures[code]) { throw new Error('Unknown culture : ' + code); } chooseCulture(code); }
if (values || !cultures[code]) { setCulture(code, values); }
return numbro; };
/** * This function provides access to the loaded language data. If * no arguments are passed in, it will simply return the current * global language object. * * @deprecated Since in version 1.6.0. It will be deleted in version 2.0 * `culture` should be used instead. */ numbro.languageData = function(key) { console.warn('`languageData` is deprecated since version 1.6.0. Use `cultureData` instead');
if (!key) { return languages[currentCulture]; }
if (!languages[key]) { throw new Error('Unknown language : ' + key); }
return languages[key]; };
/** * This function provides access to the loaded culture data. If * no arguments are passed in, it will simply return the current * global culture object. */ numbro.cultureData = function(code) { if (!code) { return cultures[currentCulture]; }
if (!cultures[code]) { throw new Error('Unknown culture : ' + code); }
return cultures[code]; };
numbro.culture('en-US', enUS);
/** * @deprecated Since in version 1.6.0. It will be deleted in version 2.0 * `cultures` should be used instead. */ numbro.languages = function() { console.warn('`languages` is deprecated since version 1.6.0. Use `cultures` instead');
return languages; };
numbro.cultures = function() { return cultures; };
numbro.zeroFormat = function(format) { zeroFormat = typeof(format) === 'string' ? format : null; };
numbro.defaultFormat = function(format) { defaultFormat = typeof(format) === 'string' ? format : '0.0'; };
numbro.defaultCurrencyFormat = function (format) { defaultCurrencyFormat = typeof(format) === 'string' ? format : '0$'; };
numbro.validate = function(val, culture) {
var _decimalSep, _thousandSep, _currSymbol, _valArray, _abbrObj, _thousandRegEx, cultureData, temp;
//coerce val to string
if (typeof val !== 'string') { val += ''; if (console.warn) { console.warn('Numbro.js: Value is not string. It has been co-erced to: ', val); } }
//trim whitespaces from either sides
val = val.trim();
//replace the initial '+' or '-' sign if present
val = val.replace(/^[+-]?/, '');
//if val is just digits return true
if ( !! val.match(/^\d+$/)) { return true; }
//if val is empty return false
if (val === '') { return false; }
//get the decimal and thousands separator from numbro.cultureData
try { //check if the culture is understood by numbro. if not, default it to current culture
cultureData = numbro.cultureData(culture); } catch (e) { cultureData = numbro.cultureData(numbro.culture()); }
//setup the delimiters and currency symbol based on culture
_currSymbol = cultureData.currency.symbol; _abbrObj = cultureData.abbreviations; _decimalSep = cultureData.delimiters.decimal; if (cultureData.delimiters.thousands === '.') { _thousandSep = '\\.'; } else { _thousandSep = cultureData.delimiters.thousands; }
// validating currency symbol
temp = val.match(/^[^\d\.\,]+/); if (temp !== null) { val = val.substr(1); if (temp[0] !== _currSymbol) { return false; } }
//validating abbreviation symbol
temp = val.match(/[^\d]+$/); if (temp !== null) { val = val.slice(0, -1); if (temp[0] !== _abbrObj.thousand && temp[0] !== _abbrObj.million && temp[0] !== _abbrObj.billion && temp[0] !== _abbrObj.trillion) { return false; } }
_thousandRegEx = new RegExp(_thousandSep + '{2}');
if (!val.match(/[^\d.,]/g)) { _valArray = val.split(_decimalSep); if (_valArray.length > 2) { return false; } else { if (_valArray.length < 2) { return ( !! _valArray[0].match(/^\d+.*\d$/) && !_valArray[0].match(_thousandRegEx)); } else { if (_valArray[0] === '') { // for values without leading zero eg. .984
return (!_valArray[0].match(_thousandRegEx) && !!_valArray[1].match(/^\d+$/));
} else if (_valArray[0].length === 1) { return ( !! _valArray[0].match(/^\d+$/) && !_valArray[0].match(_thousandRegEx) && !! _valArray[1].match(/^\d+$/)); } else { return ( !! _valArray[0].match(/^\d+.*\d$/) && !_valArray[0].match(_thousandRegEx) && !! _valArray[1].match(/^\d+$/)); } } } }
return false; };
/** * * @deprecated Since in version 1.6.0. It will be deleted in version 2.0 * `loadCulturesInNode` should be used instead. */ numbro.loadLanguagesInNode = function() { console.warn('`loadLanguagesInNode` is deprecated since version 1.6.0. Use `loadCulturesInNode` instead');
numbro.loadCulturesInNode(); };
numbro.loadCulturesInNode = function() { // TODO: Rename the folder in 2.0.0
var cultures = require('./languages');
for(var langLocaleCode in cultures) { if(langLocaleCode) { numbro.culture(langLocaleCode, cultures[langLocaleCode]); } } };
/************************************ Helpers ************************************/
function setCulture(code, values) { cultures[code] = values; }
function chooseCulture(code) { currentCulture = code; var defaults = cultures[code].defaults; if (defaults && defaults.format) { numbro.defaultFormat(defaults.format); } if (defaults && defaults.currencyFormat) { numbro.defaultCurrencyFormat(defaults.currencyFormat); } }
function inNodejsRuntime() { return (typeof process !== 'undefined') && (process.browser === undefined) && process.title && ( process.title.indexOf('node') !== -1 || process.title.indexOf('meteor-tool') > 0 || process.title === 'grunt' || process.title === 'gulp' ) && (typeof require !== 'undefined'); }
/************************************ Floating-point helpers ************************************/
// The floating-point helper functions and implementation
// borrows heavily from sinful.js: http://guipn.github.io/sinful.js/
/** * Array.prototype.reduce for browsers that don't support it * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Compatibility
*/ if ('function' !== typeof Array.prototype.reduce) { Array.prototype.reduce = function(callback, optInitialValue) {
if (null === this || 'undefined' === typeof this) { // At the moment all modern browsers, that support strict mode, have
// native implementation of Array.prototype.reduce. For instance, IE8
// does not support strict mode, so this check is actually useless.
throw new TypeError('Array.prototype.reduce called on null or undefined'); }
if ('function' !== typeof callback) { throw new TypeError(callback + ' is not a function'); }
var index, value, length = this.length >>> 0, isValueSet = false;
if (1 < arguments.length) { value = optInitialValue; isValueSet = true; }
for (index = 0; length > index; ++index) { if (this.hasOwnProperty(index)) { if (isValueSet) { value = callback(value, this[index], index, this); } else { value = this[index]; isValueSet = true; } } }
if (!isValueSet) { throw new TypeError('Reduce of empty array with no initial value'); }
return value; }; }
/** * Computes the multiplier necessary to make x >= 1, * effectively eliminating miscalculations caused by * finite precision. */ function multiplier(x) { var parts = x.toString().split('.'); if (parts.length < 2) { return 1; } return Math.pow(10, parts[1].length); }
/** * Given a variable number of arguments, returns the maximum * multiplier that must be used to normalize an operation involving * all of them. */ function correctionFactor() { var args = Array.prototype.slice.call(arguments); return args.reduce(function(prev, next) { var mp = multiplier(prev), mn = multiplier(next); return mp > mn ? mp : mn; }, -Infinity); }
/************************************ Numbro Prototype ************************************/
numbro.fn = Numbro.prototype = {
clone: function() { return numbro(this); },
format: function(inputString, roundingFunction) { return formatNumbro(this, inputString ? inputString : defaultFormat, (roundingFunction !== undefined) ? roundingFunction : Math.round ); },
formatCurrency: function(inputString, roundingFunction) { return formatCurrency(this, cultures[currentCulture].currency.symbol, inputString ? inputString : defaultCurrencyFormat, (roundingFunction !== undefined) ? roundingFunction : Math.round ); },
formatForeignCurrency: function(currencySymbol, inputString, roundingFunction) { return formatForeignCurrency(this, currencySymbol, inputString ? inputString : defaultCurrencyFormat, (roundingFunction !== undefined) ? roundingFunction : Math.round ); },
unformat: function(inputString) { if (typeof inputString === 'number') { return inputString; } else if (typeof inputString === 'string') { var result = unformatNumbro(this, inputString);
// Any unparseable string (represented as NaN in the result) is
// converted into undefined.
return isNaN(result) ? undefined : result; } else { return undefined; } },
binaryByteUnits: function() { return formatByteUnits(this._value, bytes.binary.suffixes, bytes.binary.scale).suffix; },
byteUnits: function() { return formatByteUnits(this._value, bytes.general.suffixes, bytes.general.scale).suffix; },
decimalByteUnits: function() { return formatByteUnits(this._value, bytes.decimal.suffixes, bytes.decimal.scale).suffix; },
value: function() { return this._value; },
valueOf: function() { return this._value; },
set: function(value) { this._value = Number(value); return this; },
add: function(value) { var corrFactor = correctionFactor.call(null, this._value, value);
function cback(accum, curr) { return accum + corrFactor * curr; } this._value = [this._value, value].reduce(cback, 0) / corrFactor; return this; },
subtract: function(value) { var corrFactor = correctionFactor.call(null, this._value, value);
function cback(accum, curr) { return accum - corrFactor * curr; } this._value = [value].reduce(cback, this._value * corrFactor) / corrFactor; return this; },
multiply: function(value) { function cback(accum, curr) { var corrFactor = correctionFactor(accum, curr), result = accum * corrFactor; result *= curr * corrFactor; result /= corrFactor * corrFactor; return result; } this._value = [this._value, value].reduce(cback, 1); return this; },
divide: function(value) { function cback(accum, curr) { var corrFactor = correctionFactor(accum, curr); return (accum * corrFactor) / (curr * corrFactor); } this._value = [this._value, value].reduce(cback); return this; },
difference: function(value) { return Math.abs(numbro(this._value).subtract(value).value()); }
};
/************************************ Exposing Numbro ************************************/
if (inNodejsRuntime()) { //Todo: Rename the folder in 2.0.0
numbro.loadCulturesInNode(); }
// CommonJS module is defined
if (hasModule) { module.exports = numbro; } else { /*global ender:false */ if (typeof ender === 'undefined') { // here, `this` means `window` in the browser, or `global` on the server
// add `numbro` as a global object via a string identifier,
// for Closure Compiler 'advanced' mode
this.numbro = numbro; }
/*global define:false */ if (typeof define === 'function' && define.amd) { define([], function() { return numbro; }); } }
}.call(typeof window === 'undefined' ? this : window));
|