UNPKG

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