UNPKG

14.8 kBJavaScriptView Raw
1
2// numeral.js
3// version : 1.3.3
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.3.3',
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 abbr = '',
206 bytes = '',
207 ord = '';
208
209 // see if we should use parentheses for negative number
210 if (format.indexOf('(') > -1) {
211 negP = true;
212 format = format.slice(1, -1);
213 }
214
215 // see if abbreviation is wanted
216 if (format.indexOf('a') > -1) {
217 // check for space before abbreviation
218 if (format.indexOf(' a') > -1) {
219 abbr = ' ';
220 format = format.replace(' a', '');
221 } else {
222 format = format.replace('a', '');
223 }
224
225 if (n._n > 1000000) {
226 abbr = abbr + languages[currentLanguage].abbreviations.million;
227 n._n = n._n / 1000000;
228 } else {
229 abbr = abbr + languages[currentLanguage].abbreviations.thousand;
230 n._n = n._n / 1000;
231 }
232 }
233
234 // see if we are formatting bytes
235 if (format.indexOf('b') > -1) {
236 // check for space before
237 if (format.indexOf(' b') > -1) {
238 bytes = ' ';
239 format = format.replace(' b', '');
240 } else {
241 format = format.replace('b', '');
242 }
243
244 var prefixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
245 min,
246 max;
247
248 for (var power = 0; power <= prefixes.length; power++) {
249 min = Math.pow(1024, power);
250 max = Math.pow(1024, power+1);
251
252 if (n._n > min && n._n < max) {
253 bytes = bytes + prefixes[power];
254 if (min > 0) {
255 n._n = n._n / min;
256 }
257 break;
258 }
259 }
260 }
261
262 // see if ordinal is wanted
263 if (format.indexOf('o') > -1) {
264 // check for space before
265 if (format.indexOf(' o') > -1) {
266 ord = ' ';
267 format = format.replace(' o', '');
268 } else {
269 format = format.replace('o', '');
270 }
271
272 ord = ord + languages[currentLanguage].ordinal(n._n);
273 }
274
275 var w = n._n.toString().split('.')[0],
276 precision = format.split('.')[1],
277 thousands = format.indexOf(','),
278 d = '',
279 neg = false;
280
281 if (precision) {
282 if (precision.indexOf('[') > -1) {
283 precision = precision.replace(']', '');
284 precision = precision.split('[');
285 d = toFixed(n._n, (precision[0].length + precision[1].length), precision[1].length);
286 } else {
287 d = toFixed(n._n, precision.length);
288 }
289
290 w = d.split('.')[0];
291
292 if (d.split('.')[1].length) {
293 d = languages[currentLanguage].delimiters.decimal + d.split('.')[1];
294 } else {
295 d = '';
296 }
297
298 } else {
299 w = toFixed(n._n, null);
300 }
301
302 // format number
303 if (w.indexOf('-') > -1) {
304 w = w.slice(1);
305 neg = true;
306 }
307
308 if (thousands > -1) {
309 w = w.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + languages[currentLanguage].delimiters.thousands);
310 }
311
312 if (format.indexOf('.') === 0) {
313 w = '';
314 }
315
316
317 return ((negP && neg) ? '(' : '') + ((!negP && neg) ? '-' : '') + w + d + ((ord) ? ord : '') + ((abbr) ? abbr : '') + ((bytes) ? bytes : '') + ((negP && neg) ? ')' : '');
318 }
319
320 /************************************
321 Top Level Functions
322 ************************************/
323
324 numeral = function (input) {
325 if (numeral.isNumeral(input)) {
326 input = input.value();
327 } else if (!Number(input)) {
328 input = 0;
329 }
330
331 return new Numeral(Number(input));
332 };
333
334 // compare numeral object
335 numeral.isNumeral = function (obj) {
336 return obj instanceof Numeral;
337 };
338
339 // version number
340 numeral.version = VERSION;
341
342 // compare numeral object
343 numeral.isNumeral = function (obj) {
344 return obj instanceof Numeral;
345 };
346
347 // This function will load languages and then set the global language. If
348 // no arguments are passed in, it will simply return the current global
349 // language key.
350 numeral.language = function (key, values) {
351 if (!key) {
352 return currentLanguage;
353 }
354
355 if (key && !values) {
356 currentLanguage = key;
357 }
358
359 if (values || !languages[key]) {
360 loadLanguage(key, values);
361 }
362
363 return languages;
364 };
365
366 numeral.language('en', {
367 delimiters: {
368 thousands: ',',
369 decimal: '.'
370 },
371 abbreviations: {
372 thousand: 'k',
373 million: 'm'
374 },
375 ordinal: function (number) {
376 var b = number % 10;
377 return (~~ (number % 100 / 10) === 1) ? 'th' :
378 (b === 1) ? 'st' :
379 (b === 2) ? 'nd' :
380 (b === 3) ? 'rd' : 'th';
381 },
382 currency: {
383 symbol: '$'
384 }
385 });
386
387 /************************************
388 Helpers
389 ************************************/
390
391 function loadLanguage(key, values) {
392 languages[key] = values;
393 }
394
395
396 /************************************
397 Numeral Prototype
398 ************************************/
399
400
401 numeral.fn = Numeral.prototype = {
402
403 clone : function () {
404 return numeral(this);
405 },
406
407 format : function (inputString) {
408 return formatNumeral(this, inputString ? inputString : numeral.defaultFormat);
409 },
410
411 unformat : function (inputString) {
412 return unformatNumeral(this, inputString ? inputString : numeral.defaultFormat);
413 },
414
415 value : function () {
416 return this._n;
417 },
418
419 set : function (value) {
420 this._n = Number(value);
421 return this;
422 },
423
424 add : function (value) {
425 this._n = this._n + Number(value);
426 return this;
427 },
428
429 subtract : function (value) {
430 this._n = this._n - Number(value);
431 return this;
432 },
433
434 multiply : function (value) {
435 this._n = this._n * Number(value);
436 return this;
437 },
438
439 divide : function (value) {
440 this._n = this._n / Number(value);
441 return this;
442 },
443
444 difference : function (value) {
445 var difference = this._n - Number(value);
446
447 if (difference < 0) {
448 difference = -difference;
449 }
450
451 return difference;
452 }
453
454 };
455
456 /************************************
457 Exposing Numeral
458 ************************************/
459
460 // Commenting out common js and global variable
461 // // CommonJS module is defined
462 if (hasModule) {
463 module.exports = numeral;
464 }
465 /*global ender:false */
466 if (typeof ender === 'undefined') {
467 // here, `this` means `window` in the browser, or `global` on the server
468 // add `numeral` as a global object via a string identifier,
469 // for Closure Compiler 'advanced' mode
470 this['numeral'] = numeral;
471 }
472
473 /*global define:false */
474 if (typeof define === 'function' && define.amd) {
475 define('numeral', [], function () {
476 return numeral;
477 });
478 }
479}).call(this);