master
1
2 'use strict';
3
4 /***
5 * @package ES5
6 * @description Shim methods that provide ES5 compatible functionality. This package can be excluded if you do not require legacy browser support (IE8 and below).
7 *
8 ***/
9
10
11 /***
12 * Object module
13 *
14 ***/
15
16 extend(object, false, false, {
17
18 'keys': function(obj) {
19 var keys = [];
20 if(!isObjectType(obj) && !isRegExp(obj) && !isFunction(obj)) {
21 throw new TypeError('Object required');
22 }
23 iterateOverObject(obj, function(key, value) {
24 keys.push(key);
25 });
26 return keys;
27 }
28
29 });
30
31
32 /***
33 * Array module
34 *
35 ***/
36
37 // ECMA5 methods
38
39 function arrayIndexOf(arr, search, fromIndex, increment) {
40 var length = arr.length,
41 fromRight = increment == -1,
42 start = fromRight ? length - 1 : 0,
43 index = toIntegerWithDefault(fromIndex, start);
44 if(index < 0) {
45 index = length + index;
46 }
47 if((!fromRight && index < 0) || (fromRight && index >= length)) {
48 index = start;
49 }
50 while((fromRight && index >= 0) || (!fromRight && index < length)) {
51 if(arr[index] === search) {
52 return index;
53 }
54 index += increment;
55 }
56 return -1;
57 }
58
59 function arrayReduce(arr, fn, initialValue, fromRight) {
60 var length = arr.length, count = 0, defined = isDefined(initialValue), result, index;
61 checkCallback(fn);
62 if(length == 0 && !defined) {
63 throw new TypeError('Reduce called on empty array with no initial value');
64 } else if(defined) {
65 result = initialValue;
66 } else {
67 result = arr[fromRight ? length - 1 : count];
68 count++;
69 }
70 while(count < length) {
71 index = fromRight ? length - count - 1 : count;
72 if(index in arr) {
73 result = fn(result, arr[index], index, arr);
74 }
75 count++;
76 }
77 return result;
78 }
79
80 function toIntegerWithDefault(i, d) {
81 if(isNaN(i)) {
82 return d;
83 } else {
84 return parseInt(i >> 0);
85 }
86 }
87
88 function checkFirstArgumentExists(args) {
89 if(args.length === 0) {
90 throw new TypeError('First argument must be defined');
91 }
92 }
93
94
95
96
97 extend(array, false, false, {
98
99 /***
100 *
101 * @method Array.isArray(<obj>)
102 * @returns Boolean
103 * @short Returns true if <obj> is an Array.
104 * @extra This method is provided for browsers that don't support it internally.
105 * @example
106 *
107 * Array.isArray(3) -> false
108 * Array.isArray(true) -> false
109 * Array.isArray('wasabi') -> false
110 * Array.isArray([1,2,3]) -> true
111 *
112 ***/
113 'isArray': function(obj) {
114 return isArray(obj);
115 }
116
117 });
118
119
120 extend(array, true, false, {
121
122 /***
123 * @method every(<f>, [scope])
124 * @returns Boolean
125 * @short Returns true if all elements in the array match <f>.
126 * @extra [scope] is the %this% object. %all% is provided an alias. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
127 * @example
128 *
129 + ['a','a','a'].every(function(n) {
130 * return n == 'a';
131 * });
132 * ['a','a','a'].every('a') -> true
133 * [{a:2},{a:2}].every({a:2}) -> true
134 ***/
135 'every': function(fn, scope) {
136 var length = this.length, index = 0;
137 checkFirstArgumentExists(arguments);
138 while(index < length) {
139 if(index in this && !fn.call(scope, this[index], index, this)) {
140 return false;
141 }
142 index++;
143 }
144 return true;
145 },
146
147 /***
148 * @method some(<f>, [scope])
149 * @returns Boolean
150 * @short Returns true if any element in the array matches <f>.
151 * @extra [scope] is the %this% object. %any% is provided as an alias. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
152 * @example
153 *
154 + ['a','b','c'].some(function(n) {
155 * return n == 'a';
156 * });
157 + ['a','b','c'].some(function(n) {
158 * return n == 'd';
159 * });
160 * ['a','b','c'].some('a') -> true
161 * [{a:2},{b:5}].some({a:2}) -> true
162 ***/
163 'some': function(fn, scope) {
164 var length = this.length, index = 0;
165 checkFirstArgumentExists(arguments);
166 while(index < length) {
167 if(index in this && fn.call(scope, this[index], index, this)) {
168 return true;
169 }
170 index++;
171 }
172 return false;
173 },
174
175 /***
176 * @method map(<map>, [scope])
177 * @returns Array
178 * @short Maps the array to another array containing the values that are the result of calling <map> on each element.
179 * @extra [scope] is the %this% object. When <map> is a function, it receives three arguments: the current element, the current index, and a reference to the array. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts a string, which is a shortcut for a function that gets that property (or invokes a function) on each element.
180 * @example
181 *
182 * [1,2,3].map(function(n) {
183 * return n * 3;
184 * }); -> [3,6,9]
185 * ['one','two','three'].map(function(n) {
186 * return n.length;
187 * }); -> [3,3,5]
188 * ['one','two','three'].map('length') -> [3,3,5]
189 *
190 ***/
191 'map': function(fn, scope) {
192 var scope = arguments[1], length = this.length, index = 0, result = new Array(length);
193 checkFirstArgumentExists(arguments);
194 while(index < length) {
195 if(index in this) {
196 result[index] = fn.call(scope, this[index], index, this);
197 }
198 index++;
199 }
200 return result;
201 },
202
203 /***
204 * @method filter(<f>, [scope])
205 * @returns Array
206 * @short Returns any elements in the array that match <f>.
207 * @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
208 * @example
209 *
210 + [1,2,3].filter(function(n) {
211 * return n > 1;
212 * });
213 * [1,2,2,4].filter(2) -> 2
214 *
215 ***/
216 'filter': function(fn) {
217 var scope = arguments[1];
218 var length = this.length, index = 0, result = [];
219 checkFirstArgumentExists(arguments);
220 while(index < length) {
221 if(index in this && fn.call(scope, this[index], index, this)) {
222 result.push(this[index]);
223 }
224 index++;
225 }
226 return result;
227 },
228
229 /***
230 * @method indexOf(<search>, [fromIndex])
231 * @returns Number
232 * @short Searches the array and returns the first index where <search> occurs, or -1 if the element is not found.
233 * @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>. It does not support enhanced functionality such as searching the contents against a regex, callback, or deep comparison of objects. For such functionality, use the %findIndex% method instead.
234 * @example
235 *
236 * [1,2,3].indexOf(3) -> 1
237 * [1,2,3].indexOf(7) -> -1
238 *
239 ***/
240 'indexOf': function(search) {
241 var fromIndex = arguments[1];
242 if(isString(this)) return this.indexOf(search, fromIndex);
243 return arrayIndexOf(this, search, fromIndex, 1);
244 },
245
246 /***
247 * @method lastIndexOf(<search>, [fromIndex])
248 * @returns Number
249 * @short Searches the array and returns the last index where <search> occurs, or -1 if the element is not found.
250 * @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>.
251 * @example
252 *
253 * [1,2,1].lastIndexOf(1) -> 2
254 * [1,2,1].lastIndexOf(7) -> -1
255 *
256 ***/
257 'lastIndexOf': function(search) {
258 var fromIndex = arguments[1];
259 if(isString(this)) return this.lastIndexOf(search, fromIndex);
260 return arrayIndexOf(this, search, fromIndex, -1);
261 },
262
263 /***
264 * @method forEach([fn], [scope])
265 * @returns Nothing
266 * @short Iterates over the array, calling [fn] on each loop.
267 * @extra This method is only provided for those browsers that do not support it natively. [scope] becomes the %this% object.
268 * @example
269 *
270 * ['a','b','c'].forEach(function(a) {
271 * // Called 3 times: 'a','b','c'
272 * });
273 *
274 ***/
275 'forEach': function(fn) {
276 var length = this.length, index = 0, scope = arguments[1];
277 checkCallback(fn);
278 while(index < length) {
279 if(index in this) {
280 fn.call(scope, this[index], index, this);
281 }
282 index++;
283 }
284 },
285
286 /***
287 * @method reduce(<fn>, [init])
288 * @returns Mixed
289 * @short Reduces the array to a single result.
290 * @extra If [init] is passed as a starting value, that value will be passed as the first argument to the callback. The second argument will be the first element in the array. From that point, the result of the callback will then be used as the first argument of the next iteration. This is often refered to as "accumulation", and [init] is often called an "accumulator". If [init] is not passed, then <fn> will be called n - 1 times, where n is the length of the array. In this case, on the first iteration only, the first argument will be the first element of the array, and the second argument will be the second. After that callbacks work as normal, using the result of the previous callback as the first argument of the next. This method is only provided for those browsers that do not support it natively.
291 *
292 * @example
293 *
294 + [1,2,3,4].reduce(function(a, b) {
295 * return a - b;
296 * });
297 + [1,2,3,4].reduce(function(a, b) {
298 * return a - b;
299 * }, 100);
300 *
301 ***/
302 'reduce': function(fn) {
303 return arrayReduce(this, fn, arguments[1]);
304 },
305
306 /***
307 * @method reduceRight([fn], [init])
308 * @returns Mixed
309 * @short Identical to %Array#reduce%, but operates on the elements in reverse order.
310 * @extra This method is only provided for those browsers that do not support it natively.
311 *
312 *
313 *
314 *
315 * @example
316 *
317 + [1,2,3,4].reduceRight(function(a, b) {
318 * return a - b;
319 * });
320 *
321 ***/
322 'reduceRight': function(fn) {
323 return arrayReduce(this, fn, arguments[1], true);
324 }
325
326
327 });
328
329
330
331
332 /***
333 * String module
334 *
335 ***/
336
337
338 function buildTrim() {
339 var support = getTrimmableCharacters().match(/^\s+$/);
340 try { string.prototype.trim.call([1]); } catch(e) { support = false; }
341 extend(string, true, !support, {
342
343 /***
344 * @method trim[Side]()
345 * @returns String
346 * @short Removes leading and/or trailing whitespace from the string.
347 * @extra Whitespace is defined as line breaks, tabs, and any character in the "Space, Separator" Unicode category, conforming to the the ES5 spec. The standard %trim% method is only added when not fully supported natively.
348 *
349 * @set
350 * trim
351 * trimLeft
352 * trimRight
353 *
354 * @example
355 *
356 * ' wasabi '.trim() -> 'wasabi'
357 * ' wasabi '.trimLeft() -> 'wasabi '
358 * ' wasabi '.trimRight() -> ' wasabi'
359 *
360 ***/
361 'trim': function() {
362 return this.toString().trimLeft().trimRight();
363 },
364
365 'trimLeft': function() {
366 return this.replace(regexp('^['+getTrimmableCharacters()+']+'), '');
367 },
368
369 'trimRight': function() {
370 return this.replace(regexp('['+getTrimmableCharacters()+']+$'), '');
371 }
372 });
373 }
374
375
376
377 /***
378 * Function module
379 *
380 ***/
381
382
383 extend(Function, true, false, {
384
385 /***
386 * @method bind(<scope>, [arg1], ...)
387 * @returns Function
388 * @short Binds <scope> as the %this% object for the function when it is called. Also allows currying an unlimited number of parameters.
389 * @extra "currying" means setting parameters ([arg1], [arg2], etc.) ahead of time so that they are passed when the function is called later. If you pass additional parameters when the function is actually called, they will be added will be added to the end of the curried parameters. This method is provided for browsers that don't support it internally.
390 * @example
391 *
392 + (function() {
393 * return this;
394 * }).bind('woof')(); -> returns 'woof'; function is bound with 'woof' as the this object.
395 * (function(a) {
396 * return a;
397 * }).bind(1, 2)(); -> returns 2; function is bound with 1 as the this object and 2 curried as the first parameter
398 * (function(a, b) {
399 * return a + b;
400 * }).bind(1, 2)(3); -> returns 5; function is bound with 1 as the this object, 2 curied as the first parameter and 3 passed as the second when calling the function
401 *
402 ***/
403 'bind': function(scope) {
404 var fn = this, args = multiArgs(arguments, null, 1), bound;
405 if(!isFunction(this)) {
406 throw new TypeError('Function.prototype.bind called on a non-function');
407 }
408 bound = function() {
409 return fn.apply(fn.prototype && this instanceof fn ? this : scope, args.concat(multiArgs(arguments)));
410 }
411 bound.prototype = this.prototype;
412 return bound;
413 }
414
415 });
416
417 /***
418 * Date module
419 *
420 ***/
421
422 /***
423 * @method toISOString()
424 * @returns String
425 * @short Formats the string to ISO8601 format.
426 * @extra This will always format as UTC time. Provided for browsers that do not support this method.
427 * @example
428 *
429 * Date.create().toISOString() -> ex. 2011-07-05 12:24:55.528Z
430 *
431 ***
432 * @method toJSON()
433 * @returns String
434 * @short Returns a JSON representation of the date.
435 * @extra This is effectively an alias for %toISOString%. Will always return the date in UTC time. Provided for browsers that do not support this method.
436 * @example
437 *
438 * Date.create().toJSON() -> ex. 2011-07-05 12:24:55.528Z
439 *
440 ***/
441
442 extend(date, false, false, {
443
444 /***
445 * @method Date.now()
446 * @returns String
447 * @short Returns the number of milliseconds since January 1st, 1970 00:00:00 (UTC time).
448 * @extra Provided for browsers that do not support this method.
449 * @example
450 *
451 * Date.now() -> ex. 1311938296231
452 *
453 ***/
454 'now': function() {
455 return new date().getTime();
456 }
457
458 });
459
460 function buildISOString() {
461 var d = new date(date.UTC(1999, 11, 31)), target = '1999-12-31T00:00:00.000Z';
462 var support = d.toISOString && d.toISOString() === target;
463 extendSimilar(date, true, !support, 'toISOString,toJSON', function(methods, name) {
464 methods[name] = function() {
465 return padNumber(this.getUTCFullYear(), 4) + '-' +
466 padNumber(this.getUTCMonth() + 1, 2) + '-' +
467 padNumber(this.getUTCDate(), 2) + 'T' +
468 padNumber(this.getUTCHours(), 2) + ':' +
469 padNumber(this.getUTCMinutes(), 2) + ':' +
470 padNumber(this.getUTCSeconds(), 2) + '.' +
471 padNumber(this.getUTCMilliseconds(), 3) + 'Z';
472 }
473 });
474 }
475
476 // Initialize
477 buildTrim();
478 buildISOString();
479