master
1
2 'use strict';
3
4 /***
5 * @package Number
6 * @dependency core
7 * @description Number formatting, rounding (with precision), and ranges. Aliases to Math methods.
8 *
9 ***/
10
11
12 function abbreviateNumber(num, roundTo, str, mid, limit, bytes) {
13 var fixed = num.toFixed(20),
14 decimalPlace = fixed.search(/\./),
15 numeralPlace = fixed.search(/[1-9]/),
16 significant = decimalPlace - numeralPlace,
17 unit, i, divisor;
18 if(significant > 0) {
19 significant -= 1;
20 }
21 i = max(min(floor(significant / 3), limit === false ? str.length : limit), -mid);
22 unit = str.charAt(i + mid - 1);
23 if(significant < -9) {
24 i = -3;
25 roundTo = abs(significant) - 9;
26 unit = str.slice(0,1);
27 }
28 divisor = bytes ? pow(2, 10 * i) : pow(10, i * 3);
29 return withPrecision(num / divisor, roundTo || 0).format() + unit.trim();
30 }
31
32
33 extend(number, false, true, {
34
35 /***
36 * @method Number.random([n1], [n2])
37 * @returns Number
38 * @short Returns a random integer between [n1] and [n2].
39 * @extra If only 1 number is passed, the other will be 0. If none are passed, the number will be either 0 or 1.
40 * @example
41 *
42 * Number.random(50, 100) -> ex. 85
43 * Number.random(50) -> ex. 27
44 * Number.random() -> ex. 0
45 *
46 ***/
47 'random': function(n1, n2) {
48 var minNum, maxNum;
49 if(arguments.length == 1) n2 = n1, n1 = 0;
50 minNum = min(n1 || 0, isUndefined(n2) ? 1 : n2);
51 maxNum = max(n1 || 0, isUndefined(n2) ? 1 : n2) + 1;
52 return floor((math.random() * (maxNum - minNum)) + minNum);
53 }
54
55 });
56
57 extend(number, true, true, {
58
59 /***
60 * @method log(<base> = Math.E)
61 * @returns Number
62 * @short Returns the logarithm of the number with base <base>, or natural logarithm of the number if <base> is undefined.
63 * @example
64 *
65 * (64).log(2) -> 6
66 * (9).log(3) -> 2
67 * (5).log() -> 1.6094379124341003
68 *
69 ***/
70
71 'log': function(base) {
72 return math.log(this) / (base ? math.log(base) : 1);
73 },
74
75 /***
76 * @method abbr([precision] = 0)
77 * @returns String
78 * @short Returns an abbreviated form of the number.
79 * @extra [precision] will round to the given precision.
80 * @example
81 *
82 * (1000).abbr() -> "1k"
83 * (1000000).abbr() -> "1m"
84 * (1280).abbr(1) -> "1.3k"
85 *
86 ***/
87 'abbr': function(precision) {
88 return abbreviateNumber(this, precision, 'kmbt', 0, 4);
89 },
90
91 /***
92 * @method metric([precision] = 0, [limit] = 1)
93 * @returns String
94 * @short Returns the number as a string in metric notation.
95 * @extra [precision] will round to the given precision. Both very large numbers and very small numbers are supported. [limit] is the upper limit for the units. The default is %1%, which is "kilo". If [limit] is %false%, the upper limit will be "exa". The lower limit is "nano", and cannot be changed.
96 * @example
97 *
98 * (1000).metric() -> "1k"
99 * (1000000).metric() -> "1,000k"
100 * (1000000).metric(0, false) -> "1M"
101 * (1249).metric(2) + 'g' -> "1.25kg"
102 * (0.025).metric() + 'm' -> "25mm"
103 *
104 ***/
105 'metric': function(precision, limit) {
106 return abbreviateNumber(this, precision, 'nμm kMGTPE', 4, isUndefined(limit) ? 1 : limit);
107 },
108
109 /***
110 * @method bytes([precision] = 0, [limit] = 4)
111 * @returns String
112 * @short Returns an abbreviated form of the number, considered to be "Bytes".
113 * @extra [precision] will round to the given precision. [limit] is the upper limit for the units. The default is %4%, which is "terabytes" (TB). If [limit] is %false%, the upper limit will be "exa".
114 * @example
115 *
116 * (1000).bytes() -> "1kB"
117 * (1000).bytes(2) -> "0.98kB"
118 * ((10).pow(20)).bytes() -> "90,949,470TB"
119 * ((10).pow(20)).bytes(0, false) -> "87EB"
120 *
121 ***/
122 'bytes': function(precision, limit) {
123 return abbreviateNumber(this, precision, 'kMGTPE', 0, isUndefined(limit) ? 4 : limit, true) + 'B';
124 },
125
126 /***
127 * @method isInteger()
128 * @returns Boolean
129 * @short Returns true if the number has no trailing decimal.
130 * @example
131 *
132 * (420).isInteger() -> true
133 * (4.5).isInteger() -> false
134 *
135 ***/
136 'isInteger': function() {
137 return this % 1 == 0;
138 },
139
140 /***
141 * @method isOdd()
142 * @returns Boolean
143 * @short Returns true if the number is odd.
144 * @example
145 *
146 * (3).isOdd() -> true
147 * (18).isOdd() -> false
148 *
149 ***/
150 'isOdd': function() {
151 return !isNaN(this) && !this.isMultipleOf(2);
152 },
153
154 /***
155 * @method isEven()
156 * @returns Boolean
157 * @short Returns true if the number is even.
158 * @example
159 *
160 * (6).isEven() -> true
161 * (17).isEven() -> false
162 *
163 ***/
164 'isEven': function() {
165 return this.isMultipleOf(2);
166 },
167
168 /***
169 * @method isMultipleOf(<num>)
170 * @returns Boolean
171 * @short Returns true if the number is a multiple of <num>.
172 * @example
173 *
174 * (6).isMultipleOf(2) -> true
175 * (17).isMultipleOf(2) -> false
176 * (32).isMultipleOf(4) -> true
177 * (34).isMultipleOf(4) -> false
178 *
179 ***/
180 'isMultipleOf': function(num) {
181 return this % num === 0;
182 },
183
184
185 /***
186 * @method format([place] = 0, [thousands] = ',', [decimal] = '.')
187 * @returns String
188 * @short Formats the number to a readable string.
189 * @extra If [place] is %undefined%, will automatically determine the place. [thousands] is the character used for the thousands separator. [decimal] is the character used for the decimal point.
190 * @example
191 *
192 * (56782).format() -> '56,782'
193 * (56782).format(2) -> '56,782.00'
194 * (4388.43).format(2, ' ') -> '4 388.43'
195 * (4388.43).format(2, '.', ',') -> '4.388,43'
196 *
197 ***/
198 'format': function(place, thousands, decimal) {
199 var i, str, split, integer, fraction, result = '';
200 if(isUndefined(thousands)) {
201 thousands = ',';
202 }
203 if(isUndefined(decimal)) {
204 decimal = '.';
205 }
206 str = (isNumber(place) ? withPrecision(this, place || 0).toFixed(max(place, 0)) : this.toString()).replace(/^-/, '');
207 split = str.split('.');
208 integer = split[0];
209 fraction = split[1];
210 for(i = integer.length; i > 0; i -= 3) {
211 if(i < integer.length) {
212 result = thousands + result;
213 }
214 result = integer.slice(max(0, i - 3), i) + result;
215 }
216 if(fraction) {
217 result += decimal + repeatString('0', (place || 0) - fraction.length) + fraction;
218 }
219 return (this < 0 ? '-' : '') + result;
220 },
221
222 /***
223 * @method hex([pad] = 1)
224 * @returns String
225 * @short Converts the number to hexidecimal.
226 * @extra [pad] will pad the resulting string to that many places.
227 * @example
228 *
229 * (255).hex() -> 'ff';
230 * (255).hex(4) -> '00ff';
231 * (23654).hex() -> '5c66';
232 *
233 ***/
234 'hex': function(pad) {
235 return this.pad(pad || 1, false, 16);
236 },
237
238 /***
239 * @method times(<fn>)
240 * @returns Number
241 * @short Calls <fn> a number of times equivalent to the number.
242 * @example
243 *
244 * (8).times(function(i) {
245 * // This function is called 8 times.
246 * });
247 *
248 ***/
249 'times': function(fn) {
250 if(fn) {
251 for(var i = 0; i < this; i++) {
252 fn.call(this, i);
253 }
254 }
255 return this.toNumber();
256 },
257
258 /***
259 * @method chr()
260 * @returns String
261 * @short Returns a string at the code point of the number.
262 * @example
263 *
264 * (65).chr() -> "A"
265 * (75).chr() -> "K"
266 *
267 ***/
268 'chr': function() {
269 return string.fromCharCode(this);
270 },
271
272 /***
273 * @method pad(<place> = 0, [sign] = false, [base] = 10)
274 * @returns String
275 * @short Pads a number with "0" to <place>.
276 * @extra [sign] allows you to force the sign as well (+05, etc). [base] can change the base for numeral conversion.
277 * @example
278 *
279 * (5).pad(2) -> '05'
280 * (-5).pad(4) -> '-0005'
281 * (82).pad(3, true) -> '+082'
282 *
283 ***/
284 'pad': function(place, sign, base) {
285 return padNumber(this, place, sign, base);
286 },
287
288 /***
289 * @method ordinalize()
290 * @returns String
291 * @short Returns an ordinalized (English) string, i.e. "1st", "2nd", etc.
292 * @example
293 *
294 * (1).ordinalize() -> '1st';
295 * (2).ordinalize() -> '2nd';
296 * (8).ordinalize() -> '8th';
297 *
298 ***/
299 'ordinalize': function() {
300 var suffix, num = abs(this), last = parseInt(num.toString().slice(-2));
301 return this + getOrdinalizedSuffix(last);
302 },
303
304 /***
305 * @method toNumber()
306 * @returns Number
307 * @short Returns a number. This is mostly for compatibility reasons.
308 * @example
309 *
310 * (420).toNumber() -> 420
311 *
312 ***/
313 'toNumber': function() {
314 return parseFloat(this, 10);
315 }
316
317 });
318
319 /***
320 * @method round(<precision> = 0)
321 * @returns Number
322 * @short Shortcut for %Math.round% that also allows a <precision>.
323 *
324 * @example
325 *
326 * (3.241).round() -> 3
327 * (-3.841).round() -> -4
328 * (3.241).round(2) -> 3.24
329 * (3748).round(-2) -> 3800
330 *
331 ***
332 * @method ceil(<precision> = 0)
333 * @returns Number
334 * @short Shortcut for %Math.ceil% that also allows a <precision>.
335 *
336 * @example
337 *
338 * (3.241).ceil() -> 4
339 * (-3.241).ceil() -> -3
340 * (3.241).ceil(2) -> 3.25
341 * (3748).ceil(-2) -> 3800
342 *
343 ***
344 * @method floor(<precision> = 0)
345 * @returns Number
346 * @short Shortcut for %Math.floor% that also allows a <precision>.
347 *
348 * @example
349 *
350 * (3.241).floor() -> 3
351 * (-3.841).floor() -> -4
352 * (3.241).floor(2) -> 3.24
353 * (3748).floor(-2) -> 3700
354 *
355 ***
356 * @method [math]()
357 * @returns Number
358 * @short Math related functions are mapped as shortcuts to numbers and are identical. Note that %Number#log% provides some special defaults.
359 *
360 * @set
361 * abs
362 * sin
363 * asin
364 * cos
365 * acos
366 * tan
367 * atan
368 * sqrt
369 * exp
370 * pow
371 *
372 * @example
373 *
374 * (3).pow(3) -> 27
375 * (-3).abs() -> 3
376 * (1024).sqrt() -> 32
377 *
378 ***/
379
380 function buildNumber() {
381 function createRoundingFunction(fn) {
382 return function (precision) {
383 return precision ? withPrecision(this, precision, fn) : fn(this);
384 }
385 }
386 extend(number, true, true, {
387 'ceil': createRoundingFunction(ceil),
388 'round': createRoundingFunction(round),
389 'floor': createRoundingFunction(floor)
390 });
391 extendSimilar(number, true, true, 'abs,pow,sin,asin,cos,acos,tan,atan,exp,pow,sqrt', function(methods, name) {
392 methods[name] = function(a, b) {
393 return math[name](this, a, b);
394 }
395 });
396 }
397
398 buildNumber();
399