123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293 |
- /*!
- * 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));
|