1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | (function() {
|
10 | |
11 |
|
12 |
|
13 |
|
14 | var numeral,
|
15 | _,
|
16 | VERSION = '2.0.0',
|
17 | formats = {},
|
18 | locales = {},
|
19 | defaults = {
|
20 | currentLocale: 'en',
|
21 | zeroFormat: null,
|
22 | nullFormat: null,
|
23 | defaultFormat: '0,0'
|
24 | },
|
25 | options = {
|
26 | currentLocale: defaults.currentLocale,
|
27 | zeroFormat: defaults.zeroFormat,
|
28 | nullFormat: defaults.nullFormat,
|
29 | defaultFormat: defaults.defaultFormat
|
30 | };
|
31 |
|
32 |
|
33 | |
34 |
|
35 |
|
36 |
|
37 |
|
38 | function Numeral(input, number) {
|
39 | this._input = input;
|
40 |
|
41 | this._value = number;
|
42 | }
|
43 |
|
44 | numeral = function(input) {
|
45 | var value,
|
46 | kind,
|
47 | unformatFunction,
|
48 | regexp;
|
49 |
|
50 | if (numeral.isNumeral(input)) {
|
51 | value = input.value();
|
52 | } else if (input === 0 || typeof input === 'undefined') {
|
53 | value = 0;
|
54 | } else if (input === null || _.isNaN(input)) {
|
55 | value = null;
|
56 | } else if (typeof input === 'string') {
|
57 | if (options.zeroFormat && input === options.zeroFormat) {
|
58 | value = 0;
|
59 | } else if (options.nullFormat && input === options.nullFormat || !input.replace(/[^0-9]+/g, '').length) {
|
60 | value = null;
|
61 | } else {
|
62 | for (kind in formats) {
|
63 | regexp = typeof formats[kind].regexps.unformat === 'function' ? formats[kind].regexps.unformat() : formats[kind].regexps.unformat;
|
64 |
|
65 | if (regexp && input.match(regexp)) {
|
66 | unformatFunction = formats[kind].unformat;
|
67 |
|
68 | break;
|
69 | }
|
70 | }
|
71 |
|
72 | unformatFunction = unformatFunction || numeral._.stringToNumber;
|
73 |
|
74 | value = unformatFunction(input);
|
75 | }
|
76 | } else {
|
77 | value = Number(input)|| null;
|
78 | }
|
79 |
|
80 | return new Numeral(input, value);
|
81 | };
|
82 |
|
83 |
|
84 | numeral.version = VERSION;
|
85 |
|
86 |
|
87 | numeral.isNumeral = function(obj) {
|
88 | return obj instanceof Numeral;
|
89 | };
|
90 |
|
91 |
|
92 | numeral._ = _ = {
|
93 |
|
94 | numberToFormat: function(value, format, roundingFunction) {
|
95 | var locale = locales[numeral.options.currentLocale],
|
96 | negP = false,
|
97 | signed = false,
|
98 | optDec = false,
|
99 | abbr = '',
|
100 | trillion = 1000000000000,
|
101 | billion = 1000000000,
|
102 | million = 1000000,
|
103 | thousand = 1000,
|
104 | abbrForce,
|
105 | abs,
|
106 | min,
|
107 | max,
|
108 | power,
|
109 | int,
|
110 | precision,
|
111 | thousands,
|
112 | decimal = '',
|
113 | neg = false;
|
114 |
|
115 |
|
116 | value = value || 0;
|
117 |
|
118 | abs = Math.abs(value);
|
119 |
|
120 |
|
121 |
|
122 | if (numeral._.includes(format, '(')) {
|
123 | negP = true;
|
124 | format = format.slice(1, -1);
|
125 | } else if (numeral._.includes(format, '+')) {
|
126 | signed = true;
|
127 | format = format.replace(/\+/g, '');
|
128 | }
|
129 |
|
130 |
|
131 | if (numeral._.includes(format, 'a')) {
|
132 | abbrForce = format.match(/a(k|m|b|t)?/);
|
133 |
|
134 | abbrForce = abbrForce ? abbrForce[1] : false;
|
135 |
|
136 |
|
137 | if (numeral._.includes(format, ' a')) {
|
138 | abbr = ' ';
|
139 | }
|
140 |
|
141 | format = format.replace(new RegExp(abbr + 'a[kmbt]?'), '');
|
142 |
|
143 | if (abs >= trillion && !abbrForce || abbrForce === 't') {
|
144 |
|
145 | abbr += locale.abbreviations.trillion;
|
146 | value = value / trillion;
|
147 | } else if (abs < trillion && abs >= billion && !abbrForce || abbrForce === 'b') {
|
148 |
|
149 | abbr += locale.abbreviations.billion;
|
150 | value = value / billion;
|
151 | } else if (abs < billion && abs >= million && !abbrForce || abbrForce === 'm') {
|
152 |
|
153 | abbr += locale.abbreviations.million;
|
154 | value = value / million;
|
155 | } else if (abs < million && abs >= thousand && !abbrForce || abbrForce === 'k') {
|
156 |
|
157 | abbr += locale.abbreviations.thousand;
|
158 | value = value / thousand;
|
159 | }
|
160 | }
|
161 |
|
162 |
|
163 | if (numeral._.includes(format, '[.]')) {
|
164 | optDec = true;
|
165 | format = format.replace('[.]', '.');
|
166 | }
|
167 |
|
168 | int = value.toString().split('.')[0];
|
169 | precision = format.split('.')[1];
|
170 | thousands = format.indexOf(',');
|
171 |
|
172 | if (precision) {
|
173 | if (numeral._.includes(precision, '[')) {
|
174 | precision = precision.replace(']', '');
|
175 | precision = precision.split('[');
|
176 | decimal = numeral._.toFixed(value, (precision[0].length + precision[1].length), roundingFunction, precision[1].length);
|
177 | } else {
|
178 | decimal = numeral._.toFixed(value, precision.length, roundingFunction);
|
179 | }
|
180 |
|
181 | int = decimal.split('.')[0];
|
182 |
|
183 | if (numeral._.includes(decimal, '.')) {
|
184 | decimal = locale.delimiters.decimal + decimal.split('.')[1];
|
185 | } else {
|
186 | decimal = '';
|
187 | }
|
188 |
|
189 | if (optDec && Number(decimal.slice(1)) === 0) {
|
190 | decimal = '';
|
191 | }
|
192 | } else {
|
193 | int = numeral._.toFixed(value, null, roundingFunction);
|
194 | }
|
195 |
|
196 |
|
197 | if (numeral._.includes(int, '-')) {
|
198 | int = int.slice(1);
|
199 | neg = true;
|
200 | }
|
201 |
|
202 | if (thousands > -1) {
|
203 | int = int.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + locale.delimiters.thousands);
|
204 | }
|
205 |
|
206 | if (format.indexOf('.') === 0) {
|
207 | int = '';
|
208 | }
|
209 |
|
210 | return (negP && neg ? '(' : '') + (!negP && neg ? '-' : '') + (!neg && signed ? '+' : '') + int + decimal + (abbr ? abbr : '') + (negP && neg ? ')' : '');
|
211 | },
|
212 |
|
213 | stringToNumber: function(string) {
|
214 | var locale = locales[options.currentLocale],
|
215 | stringOriginal = string,
|
216 | abbreviations = {
|
217 | thousand: 3,
|
218 | million: 6,
|
219 | billion: 9,
|
220 | trillion: 12
|
221 | },
|
222 | abbreviation,
|
223 | value,
|
224 | i,
|
225 | regexp;
|
226 |
|
227 | if (options.zeroFormat && string === options.zeroFormat) {
|
228 | value = 0;
|
229 | } else if (options.nullFormat && string === options.nullFormat || !string.replace(/[^0-9]+/g, '').length) {
|
230 | value = null;
|
231 | } else {
|
232 | value = 1;
|
233 |
|
234 | if (locale.delimiters.decimal !== '.') {
|
235 | string = string.replace(/\./g, '').replace(locale.delimiters.decimal, '.');
|
236 | }
|
237 |
|
238 | for (abbreviation in abbreviations) {
|
239 | regexp = new RegExp('[^a-zA-Z]' + locale.abbreviations[abbreviation] + '(?:\\)|(\\' + locale.currency.symbol + ')?(?:\\))?)?$');
|
240 |
|
241 | if (stringOriginal.match(regexp)) {
|
242 | value *= Math.pow(10, abbreviations[abbreviation]);
|
243 | break;
|
244 | }
|
245 | }
|
246 |
|
247 |
|
248 | value *= (string.split('-').length + Math.min(string.split('(').length - 1, string.split(')').length - 1)) % 2 ? 1 : -1;
|
249 |
|
250 |
|
251 | string = string.replace(/[^0-9\.]+/g, '');
|
252 |
|
253 | value *= Number(string);
|
254 | }
|
255 |
|
256 | return value;
|
257 | },
|
258 | isNaN: function(value) {
|
259 | return typeof value === 'number' && isNaN(value);
|
260 | },
|
261 | includes: function(string, search) {
|
262 | return string.indexOf(search) !== -1;
|
263 | },
|
264 | reduce: function(array, callback /*, initialValue*/) {
|
265 | if (this === null) {
|
266 | throw new TypeError('Array.prototype.reduce called on null or undefined');
|
267 | }
|
268 |
|
269 | if (typeof callback !== 'function') {
|
270 | throw new TypeError(callback + ' is not a function');
|
271 | }
|
272 |
|
273 | var t = Object(array),
|
274 | len = t.length >>> 0,
|
275 | k = 0,
|
276 | value;
|
277 |
|
278 | if (arguments.length === 3) {
|
279 | value = arguments[2];
|
280 | } else {
|
281 | while (k < len && !(k in t)) {
|
282 | k++;
|
283 | }
|
284 |
|
285 | if (k >= len) {
|
286 | throw new TypeError('Reduce of empty array with no initial value');
|
287 | }
|
288 |
|
289 | value = t[k++];
|
290 | }
|
291 | for (; k < len; k++) {
|
292 | if (k in t) {
|
293 | value = callback(value, t[k], k, t);
|
294 | }
|
295 | }
|
296 | return value;
|
297 | },
|
298 | |
299 |
|
300 |
|
301 |
|
302 |
|
303 | multiplier: function (x) {
|
304 | var parts = x.toString().split('.');
|
305 |
|
306 | return parts.length < 2 ? 1 : Math.pow(10, parts[1].length);
|
307 | },
|
308 | |
309 |
|
310 |
|
311 |
|
312 |
|
313 | correctionFactor: function () {
|
314 | var args = Array.prototype.slice.call(arguments);
|
315 |
|
316 | return args.reduce(function(accum, next) {
|
317 | var mn = _.multiplier(next);
|
318 | return accum > mn ? accum : mn;
|
319 | }, 1);
|
320 | },
|
321 | |
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 | toFixed: function(value, maxDecimals, roundingFunction, optionals) {
|
328 | var splitValue = value.toString().split('.'),
|
329 | minDecimals = maxDecimals - (optionals || 0),
|
330 | boundedPrecision,
|
331 | optionalsRegExp,
|
332 | power,
|
333 | output;
|
334 |
|
335 |
|
336 | if (splitValue.length === 2) {
|
337 | boundedPrecision = Math.min(Math.max(splitValue[1].length, minDecimals), maxDecimals);
|
338 | } else {
|
339 | boundedPrecision = minDecimals;
|
340 | }
|
341 |
|
342 | power = Math.pow(10, boundedPrecision);
|
343 |
|
344 |
|
345 |
|
346 | output = (roundingFunction(value * power) / power).toFixed(boundedPrecision);
|
347 |
|
348 | if (optionals > maxDecimals - boundedPrecision) {
|
349 | optionalsRegExp = new RegExp('\\.?0{1,' + (optionals - (maxDecimals - boundedPrecision)) + '}$');
|
350 | output = output.replace(optionalsRegExp, '');
|
351 | }
|
352 |
|
353 | return output;
|
354 | }
|
355 | };
|
356 |
|
357 |
|
358 | numeral.options = options;
|
359 |
|
360 |
|
361 | numeral.formats = formats;
|
362 |
|
363 |
|
364 | numeral.locales = locales;
|
365 |
|
366 |
|
367 |
|
368 |
|
369 | numeral.locale = function(key) {
|
370 | if (!key) {
|
371 | return options.currentLocale;
|
372 | }
|
373 |
|
374 | options.currentLocale = key.toLowerCase();
|
375 |
|
376 | return numeral;
|
377 | };
|
378 |
|
379 |
|
380 |
|
381 |
|
382 | numeral.localeData = function(key) {
|
383 | if (!key) {
|
384 | return locales[options.currentLocale];
|
385 | }
|
386 |
|
387 | key = key.toLowerCase();
|
388 |
|
389 | if (!locales[key]) {
|
390 | throw new Error('Unknown locale : ' + key);
|
391 | }
|
392 |
|
393 | return locales[key];
|
394 | };
|
395 |
|
396 | numeral.reset = function() {
|
397 | for (var property in defaults) {
|
398 | options[property] = defaults[property];
|
399 | }
|
400 | };
|
401 |
|
402 | numeral.zeroFormat = function(format) {
|
403 | options.zeroFormat = typeof(format) === 'string' ? format : null;
|
404 | };
|
405 |
|
406 | numeral.nullFormat = function (format) {
|
407 | options.nullFormat = typeof(format) === 'string' ? format : null;
|
408 | };
|
409 |
|
410 | numeral.defaultFormat = function(format) {
|
411 | options.defaultFormat = typeof(format) === 'string' ? format : '0.0';
|
412 | };
|
413 |
|
414 | numeral.register = function(type, name, format) {
|
415 | name = name.toLowerCase();
|
416 |
|
417 | if (this[type + 's'][name]) {
|
418 | throw new TypeError(name + ' ' + type + ' already registered.');
|
419 | }
|
420 |
|
421 | this[type + 's'][name] = format;
|
422 | };
|
423 |
|
424 |
|
425 | numeral.validate = function(val, culture) {
|
426 | var _decimalSep,
|
427 | _thousandSep,
|
428 | _currSymbol,
|
429 | _valArray,
|
430 | _abbrObj,
|
431 | _thousandRegEx,
|
432 | localeData,
|
433 | temp;
|
434 |
|
435 |
|
436 | if (typeof val !== 'string') {
|
437 | val += '';
|
438 |
|
439 | if (console.warn) {
|
440 | console.warn('Numeral.js: Value is not string. It has been co-erced to: ', val);
|
441 | }
|
442 | }
|
443 |
|
444 |
|
445 | val = val.trim();
|
446 |
|
447 |
|
448 | if (!!val.match(/^\d+$/)) {
|
449 | return true;
|
450 | }
|
451 |
|
452 |
|
453 | if (val === '') {
|
454 | return false;
|
455 | }
|
456 |
|
457 |
|
458 | try {
|
459 |
|
460 | localeData = numeral.localeData(culture);
|
461 | } catch (e) {
|
462 | localeData = numeral.localeData(numeral.locale());
|
463 | }
|
464 |
|
465 |
|
466 | _currSymbol = localeData.currency.symbol;
|
467 | _abbrObj = localeData.abbreviations;
|
468 | _decimalSep = localeData.delimiters.decimal;
|
469 | if (localeData.delimiters.thousands === '.') {
|
470 | _thousandSep = '\\.';
|
471 | } else {
|
472 | _thousandSep = localeData.delimiters.thousands;
|
473 | }
|
474 |
|
475 |
|
476 | temp = val.match(/^[^\d]+/);
|
477 | if (temp !== null) {
|
478 | val = val.substr(1);
|
479 | if (temp[0] !== _currSymbol) {
|
480 | return false;
|
481 | }
|
482 | }
|
483 |
|
484 |
|
485 | temp = val.match(/[^\d]+$/);
|
486 | if (temp !== null) {
|
487 | val = val.slice(0, -1);
|
488 | if (temp[0] !== _abbrObj.thousand && temp[0] !== _abbrObj.million && temp[0] !== _abbrObj.billion && temp[0] !== _abbrObj.trillion) {
|
489 | return false;
|
490 | }
|
491 | }
|
492 |
|
493 | _thousandRegEx = new RegExp(_thousandSep + '{2}');
|
494 |
|
495 | if (!val.match(/[^\d.,]/g)) {
|
496 | _valArray = val.split(_decimalSep);
|
497 | if (_valArray.length > 2) {
|
498 | return false;
|
499 | } else {
|
500 | if (_valArray.length < 2) {
|
501 | return ( !! _valArray[0].match(/^\d+.*\d$/) && !_valArray[0].match(_thousandRegEx));
|
502 | } else {
|
503 | if (_valArray[0].length === 1) {
|
504 | return ( !! _valArray[0].match(/^\d+$/) && !_valArray[0].match(_thousandRegEx) && !! _valArray[1].match(/^\d+$/));
|
505 | } else {
|
506 | return ( !! _valArray[0].match(/^\d+.*\d$/) && !_valArray[0].match(_thousandRegEx) && !! _valArray[1].match(/^\d+$/));
|
507 | }
|
508 | }
|
509 | }
|
510 | }
|
511 |
|
512 | return false;
|
513 | };
|
514 |
|
515 |
|
516 | |
517 |
|
518 |
|
519 |
|
520 | numeral.fn = Numeral.prototype = {
|
521 | clone: function() {
|
522 | return numeral(this);
|
523 | },
|
524 | format: function(inputString, roundingFunction) {
|
525 | var value = this._value,
|
526 | format = inputString || options.defaultFormat,
|
527 | kind,
|
528 | output,
|
529 | formatFunction;
|
530 |
|
531 |
|
532 | roundingFunction = roundingFunction || Math.round;
|
533 |
|
534 |
|
535 | if (value === 0 && options.zeroFormat !== null) {
|
536 | output = options.zeroFormat;
|
537 | } else if (value === null && options.nullFormat !== null) {
|
538 | output = options.nullFormat;
|
539 | } else {
|
540 | for (kind in formats) {
|
541 | if (format.match(formats[kind].regexps.format)) {
|
542 | formatFunction = formats[kind].format;
|
543 |
|
544 | break;
|
545 | }
|
546 | }
|
547 |
|
548 | formatFunction = formatFunction || numeral._.numberToFormat;
|
549 |
|
550 | output = formatFunction(value, format, roundingFunction);
|
551 | }
|
552 |
|
553 | return output;
|
554 | },
|
555 | value: function() {
|
556 | return this._value;
|
557 | },
|
558 | input: function() {
|
559 | return this._input;
|
560 | },
|
561 | set: function(value) {
|
562 | this._value = Number(value);
|
563 |
|
564 | return this;
|
565 | },
|
566 | add: function(value) {
|
567 | var corrFactor = _.correctionFactor.call(null, this._value, value);
|
568 |
|
569 | function cback(accum, curr, currI, O) {
|
570 | return accum + Math.round(corrFactor * curr);
|
571 | }
|
572 |
|
573 | this._value = _.reduce([this._value, value], cback, 0) / corrFactor;
|
574 |
|
575 | return this;
|
576 | },
|
577 | subtract: function(value) {
|
578 | var corrFactor = _.correctionFactor.call(null, this._value, value);
|
579 |
|
580 | function cback(accum, curr, currI, O) {
|
581 | return accum - Math.round(corrFactor * curr);
|
582 | }
|
583 |
|
584 | this._value = _.reduce([value], cback, Math.round(this._value * corrFactor)) / corrFactor;
|
585 |
|
586 | return this;
|
587 | },
|
588 | multiply: function(value) {
|
589 | function cback(accum, curr, currI, O) {
|
590 | var corrFactor = _.correctionFactor(accum, curr);
|
591 | return Math.round(accum * corrFactor) * Math.round(curr * corrFactor) / Math.round(corrFactor * corrFactor);
|
592 | }
|
593 |
|
594 | this._value = _.reduce([this._value, value], cback, 1);
|
595 |
|
596 | return this;
|
597 | },
|
598 | divide: function(value) {
|
599 | function cback(accum, curr, currI, O) {
|
600 | var corrFactor = _.correctionFactor(accum, curr);
|
601 | return Math.round(accum * corrFactor) / Math.round(curr * corrFactor);
|
602 | }
|
603 |
|
604 | this._value = _.reduce([this._value, value], cback);
|
605 |
|
606 | return this;
|
607 | },
|
608 | difference: function(value) {
|
609 | return Math.abs(numeral(this._value).subtract(value).value());
|
610 | }
|
611 | };
|
612 |
|
613 | |
614 |
|
615 |
|
616 |
|
617 | numeral.register('locale', 'en', {
|
618 | delimiters: {
|
619 | thousands: ',',
|
620 | decimal: '.'
|
621 | },
|
622 | abbreviations: {
|
623 | thousand: 'k',
|
624 | million: 'm',
|
625 | billion: 'b',
|
626 | trillion: 't'
|
627 | },
|
628 | ordinal: function(number) {
|
629 | var b = number % 10;
|
630 | return (~~(number % 100 / 10) === 1) ? 'th' :
|
631 | (b === 1) ? 'st' :
|
632 | (b === 2) ? 'nd' :
|
633 | (b === 3) ? 'rd' : 'th';
|
634 | },
|
635 | currency: {
|
636 | symbol: '$'
|
637 | }
|
638 | });
|
639 |
|
640 |
|
641 | |
642 |
|
643 |
|
644 |
|
645 |
|
646 | if (typeof module !== 'undefined' && module.exports) {
|
647 | module.exports = numeral;
|
648 | }
|
649 |
|
650 |
|
651 | if (typeof ender === 'undefined') {
|
652 |
|
653 |
|
654 |
|
655 | this['numeral'] = numeral;
|
656 | }
|
657 |
|
658 |
|
659 | if (typeof define === 'function' && define.amd) {
|
660 | define([], function() {
|
661 | return numeral;
|
662 | });
|
663 | }
|
664 | }).call(this);
|