UNPKG

17.1 kBJavaScriptView Raw
1// numeral.js
2// version : 1.4.5
3// author : Adam Draper
4// license : MIT
5// http://adamwdraper.github.com/Numeral-js/
6
7(function () {
8
9 /************************************
10 Constants
11 ************************************/
12
13 var numeral,
14 VERSION = '1.4.5',
15 // internal storage for language config files
16 languages = {},
17 currentLanguage = 'en',
18 zeroFormat = null,
19 // check for nodeJS
20 hasModule = (typeof module !== 'undefined' && module.exports);
21
22
23 /************************************
24 Constructors
25 ************************************/
26
27
28 // Numeral prototype object
29 function Numeral (number) {
30 this._n = number;
31 }
32
33 /**
34 * Implementation of toFixed() that treats floats more like decimals
35 *
36 * Fixes binary rounding issues (eg. (0.615).toFixed(2) === '0.61') that present
37 * problems for accounting- and finance-related software.
38 */
39 function toFixed (value, precision, optionals) {
40 var power = Math.pow(10, precision),
41 output;
42
43 // Multiply up by precision, round accurately, then divide and use native toFixed():
44 output = (Math.round(value * power) / power).toFixed(precision);
45
46 if (optionals) {
47 var optionalsRegExp = new RegExp('0{1,' + optionals + '}$');
48 output = output.replace(optionalsRegExp, '');
49 }
50
51 return output;
52 }
53
54 /************************************
55 Formatting
56 ************************************/
57
58 // determine what type of formatting we need to do
59 function formatNumeral (n, format) {
60 var output;
61
62 // figure out what kind of format we are dealing with
63 if (format.indexOf('$') > -1) { // currency!!!!!
64 output = formatCurrency(n, format);
65 } else if (format.indexOf('%') > -1) { // percentage
66 output = formatPercentage(n, format);
67 } else if (format.indexOf(':') > -1) { // time
68 output = formatTime(n, format);
69 } else { // plain ol' numbers or bytes
70 output = formatNumber(n, format);
71 }
72
73 // return string
74 return output;
75 }
76
77 // revert to number
78 function unformatNumeral (n, string) {
79 if (string.indexOf(':') > -1) {
80 n._n = unformatTime(string);
81 } else {
82 if (string === zeroFormat) {
83 n._n = 0;
84 } else {
85 var stringOriginal = string;
86 if (languages[currentLanguage].delimiters.decimal !== '.') {
87 string = string.replace(/\./g,'').replace(languages[currentLanguage].delimiters.decimal, '.');
88 }
89
90 // see if abbreviations are there so that we can multiply to the correct number
91 var thousandRegExp = new RegExp(languages[currentLanguage].abbreviations.thousand + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
92 millionRegExp = new RegExp(languages[currentLanguage].abbreviations.million + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
93 billionRegExp = new RegExp(languages[currentLanguage].abbreviations.billion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$'),
94 trillionRegExp = new RegExp(languages[currentLanguage].abbreviations.trillion + '(?:\\)|(\\' + languages[currentLanguage].currency.symbol + ')?(?:\\))?)?$');
95
96 // see if bytes are there so that we can multiply to the correct number
97 var prefixes = ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
98 bytesMultiplier = false;
99
100 for (var power = 0; power <= prefixes.length; power++) {
101 bytesMultiplier = (string.indexOf(prefixes[power]) > -1) ? Math.pow(1024, power + 1) : false;
102
103 if (bytesMultiplier) {
104 break;
105 }
106 }
107
108 // do some math to create our number
109 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) * Number(((string.indexOf('(') > -1) ? '-' : '') + string.replace(/[^0-9\.'-]+/g, ''));
110
111 // round if we are talking about bytes
112 n._n = (bytesMultiplier) ? Math.ceil(n._n) : n._n;
113 }
114 }
115 return n._n;
116 }
117
118 function formatCurrency (n, format) {
119 var prependSymbol = (format.indexOf('$') <= 1) ? true : false;
120
121 // remove $ for the moment
122 var space = '';
123
124 // check for space before or after currency
125 if (format.indexOf(' $') > -1) {
126 space = ' ';
127 format = format.replace(' $', '');
128 } else if (format.indexOf('$ ') > -1) {
129 space = ' ';
130 format = format.replace('$ ', '');
131 } else {
132 format = format.replace('$', '');
133 }
134
135 // format the number
136 var output = formatNumeral(n, format);
137
138 // position the symbol
139 if (prependSymbol) {
140 if (output.indexOf('(') > -1 || output.indexOf('-') > -1) {
141 output = output.split('');
142 output.splice(1, 0, languages[currentLanguage].currency.symbol + space);
143 output = output.join('');
144 } else {
145 output = languages[currentLanguage].currency.symbol + space + output;
146 }
147 } else {
148 if (output.indexOf(')') > -1) {
149 output = output.split('');
150 output.splice(-1, 0, space + languages[currentLanguage].currency.symbol);
151 output = output.join('');
152 } else {
153 output = output + space + languages[currentLanguage].currency.symbol;
154 }
155 }
156
157 return output;
158 }
159
160 function formatPercentage (n, format) {
161 var space = '';
162 // check for space before %
163 if (format.indexOf(' %') > -1) {
164 space = ' ';
165 format = format.replace(' %', '');
166 } else {
167 format = format.replace('%', '');
168 }
169
170 n._n = n._n * 100;
171 var output = formatNumeral(n, format);
172 if (output.indexOf(')') > -1 ) {
173 output = output.split('');
174 output.splice(-1, 0, space + '%');
175 output = output.join('');
176 } else {
177 output = output + space + '%';
178 }
179 return output;
180 }
181
182 function formatTime (n, format) {
183 var hours = Math.floor(n._n/60/60),
184 minutes = Math.floor((n._n - (hours * 60 * 60))/60),
185 seconds = Math.round(n._n - (hours * 60 * 60) - (minutes * 60));
186 return hours + ':' + ((minutes < 10) ? '0' + minutes : minutes) + ':' + ((seconds < 10) ? '0' + seconds : seconds);
187 }
188
189 function unformatTime (string) {
190 var timeArray = string.split(':'),
191 seconds = 0;
192 // turn hours and minutes into seconds and add them all up
193 if (timeArray.length === 3) {
194 // hours
195 seconds = seconds + (Number(timeArray[0]) * 60 * 60);
196 // minutes
197 seconds = seconds + (Number(timeArray[1]) * 60);
198 // seconds
199 seconds = seconds + Number(timeArray[2]);
200 } else if (timeArray.lenght === 2) {
201 // minutes
202 seconds = seconds + (Number(timeArray[0]) * 60);
203 // seconds
204 seconds = seconds + Number(timeArray[1]);
205 }
206 return Number(seconds);
207 }
208
209 function formatNumber (n, format) {
210 var negP = false,
211 optDec = false,
212 abbr = '',
213 bytes = '',
214 ord = '',
215 abs = Math.abs(n._n);
216
217 // check if number is zero and a custom zero format has been set
218 if (n._n === 0 && zeroFormat !== null) {
219 return zeroFormat;
220 } else {
221 // see if we should use parentheses for negative number
222 if (format.indexOf('(') > -1) {
223 negP = true;
224 format = format.slice(1, -1);
225 }
226
227 // see if abbreviation is wanted
228 if (format.indexOf('a') > -1) {
229 // check for space before abbreviation
230 if (format.indexOf(' a') > -1) {
231 abbr = ' ';
232 format = format.replace(' a', '');
233 } else {
234 format = format.replace('a', '');
235 }
236
237 if (abs >= Math.pow(10, 12)) {
238 // trillion
239 abbr = abbr + languages[currentLanguage].abbreviations.tillion;
240 n._n = n._n / Math.pow(10, 12);
241 } else if (abs < Math.pow(10, 12) && abs >= Math.pow(10, 9)) {
242 // billion
243 abbr = abbr + languages[currentLanguage].abbreviations.billion;
244 n._n = n._n / Math.pow(10, 9);
245 } else if (abs < Math.pow(10, 9) && abs >= Math.pow(10, 6)) {
246 // million
247 abbr = abbr + languages[currentLanguage].abbreviations.million;
248 n._n = n._n / Math.pow(10, 6);
249 } else if (abs < Math.pow(10, 6) && abs >= Math.pow(10, 3)) {
250 // thousand
251 abbr = abbr + languages[currentLanguage].abbreviations.thousand;
252 n._n = n._n / Math.pow(10, 3);
253 }
254 }
255
256 // see if we are formatting bytes
257 if (format.indexOf('b') > -1) {
258 // check for space before
259 if (format.indexOf(' b') > -1) {
260 bytes = ' ';
261 format = format.replace(' b', '');
262 } else {
263 format = format.replace('b', '');
264 }
265
266 var prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
267 min,
268 max;
269
270 for (var power = 0; power <= prefixes.length; power++) {
271 min = Math.pow(1024, power);
272 max = Math.pow(1024, power+1);
273
274 if (n._n >= min && n._n < max) {
275 bytes = bytes + prefixes[power];
276 if (min > 0) {
277 n._n = n._n / min;
278 }
279 break;
280 }
281 }
282 }
283
284 // see if ordinal is wanted
285 if (format.indexOf('o') > -1) {
286 // check for space before
287 if (format.indexOf(' o') > -1) {
288 ord = ' ';
289 format = format.replace(' o', '');
290 } else {
291 format = format.replace('o', '');
292 }
293
294 ord = ord + languages[currentLanguage].ordinal(n._n);
295 }
296
297 if (format.indexOf('[.]') > -1) {
298 optDec = true;
299 format = format.replace('[.]', '.');
300 }
301
302 var w = n._n.toString().split('.')[0],
303 precision = format.split('.')[1],
304 thousands = format.indexOf(','),
305 d = '',
306 neg = false;
307
308 if (precision) {
309 if (precision.indexOf('[') > -1) {
310 precision = precision.replace(']', '');
311 precision = precision.split('[');
312 d = toFixed(n._n, (precision[0].length + precision[1].length), precision[1].length);
313 } else {
314 d = toFixed(n._n, precision.length);
315 }
316
317 w = d.split('.')[0];
318
319 if (d.split('.')[1].length) {
320 d = languages[currentLanguage].delimiters.decimal + d.split('.')[1];
321 } else {
322 d = '';
323 }
324
325 if (optDec && Number(d) === 0) {
326 d = '';
327 }
328 } else {
329 w = toFixed(n._n, null);
330 }
331
332 // format number
333 if (w.indexOf('-') > -1) {
334 w = w.slice(1);
335 neg = true;
336 }
337
338 if (thousands > -1) {
339 w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands);
340 }
341
342 if (format.indexOf('.') === 0) {
343 w = '';
344 }
345
346 return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
347 }
348 }
349
350 /************************************
351 Top Level Functions
352 ************************************/
353
354 numeral = function (input) {
355 if (numeral.isNumeral(input)) {
356 input = input.value();
357 } else if (!Number(input)) {
358 input = 0;
359 }
360
361 return new Numeral(Number(input));
362 };
363
364 // version number
365 numeral.version = VERSION;
366
367 // compare numeral object
368 numeral.isNumeral = function (obj) {
369 return obj instanceof Numeral;
370 };
371
372 // This function will load languages and then set the global language. If
373 // no arguments are passed in, it will simply return the current global
374 // language key.
375 numeral.language = function (key, values) {
376 if (!key) {
377 return currentLanguage;
378 }
379
380 if (key && !values) {
381 currentLanguage = key;
382 }
383
384 if (values || !languages[key]) {
385 loadLanguage(key, values);
386 }
387
388 return numeral;
389 };
390
391 numeral.language('en', {
392 delimiters: {
393 thousands: ',',
394 decimal: '.'
395 },
396 abbreviations: {
397 thousand: 'k',
398 million: 'm',
399 billion: 'b',
400 trillion: 't'
401 },
402 ordinal: function (number) {
403 var b = number % 10;
404 return (~~ (number % 100 / 10) === 1) ? 'th' :
405 (b === 1) ? 'st' :
406 (b === 2) ? 'nd' :
407 (b === 3) ? 'rd' : 'th';
408 },
409 currency: {
410 symbol: '$'
411 }
412 });
413
414 numeral.zeroFormat = function (format) {
415 if (typeof(format) === 'string') {
416 zeroFormat = format;
417 } else {
418 zeroFormat = null;
419 }
420 };
421
422 /************************************
423 Helpers
424 ************************************/
425
426 function loadLanguage(key, values) {
427 languages[key] = values;
428 }
429
430
431 /************************************
432 Numeral Prototype
433 ************************************/
434
435
436 numeral.fn = Numeral.prototype = {
437
438 clone : function () {
439 return numeral(this);
440 },
441
442 format : function (inputString) {
443 return formatNumeral(this, inputString ? inputString : numeral.defaultFormat);
444 },
445
446 unformat : function (inputString) {
447 return unformatNumeral(this, inputString ? inputString : numeral.defaultFormat);
448 },
449
450 value : function () {
451 return this._n;
452 },
453
454 valueOf : function () {
455 return this._n;
456 },
457
458 set : function (value) {
459 this._n = Number(value);
460 return this;
461 },
462
463 add : function (value) {
464 this._n = this._n + Number(value);
465 return this;
466 },
467
468 subtract : function (value) {
469 this._n = this._n - Number(value);
470 return this;
471 },
472
473 multiply : function (value) {
474 this._n = this._n * Number(value);
475 return this;
476 },
477
478 divide : function (value) {
479 this._n = this._n / Number(value);
480 return this;
481 },
482
483 difference : function (value) {
484 var difference = this._n - Number(value);
485
486 if (difference < 0) {
487 difference = -difference;
488 }
489
490 return difference;
491 }
492
493 };
494
495 /************************************
496 Exposing Numeral
497 ************************************/
498
499 // CommonJS module is defined
500 if (hasModule) {
501 module.exports = numeral;
502 }
503
504 /*global ender:false */
505 if (typeof ender === 'undefined') {
506 // here, `this` means `window` in the browser, or `global` on the server
507 // add `numeral` as a global object via a string identifier,
508 // for Closure Compiler 'advanced' mode
509 this['numeral'] = numeral;
510 }
511
512 /*global define:false */
513 if (typeof define === 'function' && define.amd) {
514 define([], function () {
515 return numeral;
516 });
517 }
518}).call(this);