1 (function () {
  2   var testArray = function (o) {
  3     return Object.prototype.toString.apply(o) === '[object Array]';
  4   };
  5 
  6 // Functions that are used in assembling nativelib.
  7 
  8 /**
  9  * @namespace Functions for dealing with Objects.
 10  */
 11 Obj = {
 12   /**
 13    * Adds all of the passed properties if they are not already
 14    * defined.
 15    *
 16    * @example
 17    * var o = { a: 1 };
 18    * Object.merge(o, {
 19    *   a: 2,
 20    *   b: 2
 21    * });
 22    * o; // { a: 1, b: 2 }
 23    *
 24    * @name merge
 25    * @methodOf Obj
 26    * @param {Object} o The object to modify (in-place).
 27    * @param {Object} props The properties to write.
 28    * @param {Boolean} clobber true to overwrite defined properties
 29    * @return {Object} The modified object.
 30    */
 31   merge: function (o, props, clobber) {
 32     for (var p in props) {
 33       if (props.hasOwnProperty(p) && (!o[p] || clobber)) {
 34         o[p] = props[p];
 35       }
 36     }
 37     return o;
 38   }
 39 };
 40 
 41 Obj.merge(Obj, {
 42   /**
 43    * Maps each property to its value.
 44    *
 45    * @example
 46    * var o = { a: 1 };
 47    * Object.alias(o, {
 48    *   a: 'b'
 49    * }; // { a: 1, b: 1 }
 50    * Object.alias(o, {
 51    *   b: ['c', 'd']
 52    * }; // { a: 1, b: 1, c: 1, d: 1 }
 53    *
 54    * @name alias
 55    * @methodOf Obj
 56    * @param {Object} o The object to modify (in-place).
 57    * @param {Object} map The properties to map.
 58    * @return {Object} The modified object.
 59    */
 60   alias: function (o, map) {
 61     for (var p in map) {
 62       if (map.hasOwnProperty(p) && !o[map[p]]) {
 63         if (testArray(map[p])) {
 64           for (var i = 0, len = map[p].length; i < len; i++) {
 65             o[map[p][i]] = o[p];
 66           }
 67         } else {
 68           o[map[p]] = o[p];
 69         }
 70       }
 71     }
 72     return o;
 73   },
 74 
 75   /**
 76    * Copies each property of the provided object into a new object,
 77    * creating a shallow copy.
 78    *
 79    * @name copy
 80    * @methodOf Obj
 81    * @param {Object} o The object to copy.
 82    * @return {Object} The copied object.
 83    */
 84   copy: function (o) {
 85     return Obj.merge({}, o);
 86   },
 87 
 88   /**
 89    * Creates a new object from an array of property-value pairs.
 90    *
 91    * @example
 92    * Object.create([['a', 1], ['b', 2]]); // { a: 1, b: 2 }
 93    *
 94    * @name create
 95    * @methodOf Obj
 96    * @param {Array} pairs The pair array.
 97    * @return {Object} The created object.
 98    */
 99   create: function (pairs) {
100     var o = {};
101     for (var i = 0; i < pairs.length; i++) {
102       o[pairs[i][0]] = pairs[i][1];
103     }
104     return o;
105   },
106 
107   /**
108    * Compares the value of each property in each object.
109    *
110    * @name equals
111    * @methodOf Obj
112    * @param {Object} o One object to compare.
113    * @param {Object} o2 The object to compare it with.
114    * @return {Boolean} true if the properties are equal, false if not.
115    */
116   equals: function (o, o2) {
117     var p;
118     for (p in o2) {
119       if (o2.hasOwnProperty(p) && !Obj.valuesEqual(o[p], o2[p])) {
120         return false;
121       }
122     }
123     for (p in o) {
124       if (o.hasOwnProperty(p) && !Obj.valuesEqual(o[p], o2[p])) {
125         return false;
126       }
127     }
128     return true;
129   },
130 
131   /**
132    * Creates a new object containing only the properties that return
133    * true when passed to the provided function.
134    *
135    * @name filter
136    * @methodOf Obj
137    * @param {Object} o The object to filter.
138    * @param {Function} fn The filtering function.
139    * @param {Object} ctx The function's execution context.
140    * @return {Object} The filtered object.
141    */
142   filter: function (o, fn, ctx) {
143     var filtered = {};
144     for (var p in o) {
145       if (o.hasOwnProperty(p) && fn.call(ctx || o, o[p], p, o)) {
146         filtered[p] = o[p];
147       }
148     }
149     return filtered;
150   },
151 
152   /**
153    * The same as filter, but only returning the first match.
154    *
155    * @name filterOne
156    * @methodOf Obj
157    * @param {Object} o The object to filter.
158    * @param {Function} fn The filtering function.
159    * @param {Object} ctx The function's execution context.
160    * @return The first filtered item, if found.
161    */
162   filterOne: function (o, fn, ctx) {
163     var filtered = {};
164     for (var p in o) {
165       if (o.hasOwnProperty(p) && fn.call(ctx || o, o[p], p, o)) {
166         filtered[p] = o[p];
167         return filtered;
168       }
169     }
170   },
171 
172   /**
173    * Returns an object matching the specified path, if it exists.
174    *
175    * @name fromPath
176    * @methodOf Obj
177    * @param {String} path The period '.' separated object path.
178    * @param {Object} root The root path (default is window).
179    * @return {Object} The object, if found.
180    */
181   fromPath: function (path, root) {
182     return path.split('.').reduce(function (a, b) {
183       return a ? a[b] : undefined;
184     }, root || window);
185   },
186 
187   /**
188    * Calls the property, if present, on each property in the passed
189    * object, returning an object with the results at each property. If
190    * the named property is not present on a given item, the result
191    * property will be undefined.
192    *
193    * @name invoke
194    * @methodOf Obj
195    * @param {Object} o The object
196    * @param {String} name The property name to call.
197    * @param {Array} args An argument list that will be provided with
198    * each call.
199    * @param {Object} ctx The function's execution context.
200    * @return {Object} The return value of each call.
201    */
202   invoke: function (o, name, args, ctx) {
203     var invoked = {};
204     for (var p in o) {
205       if (o.hasOwnProperty(p)) {
206         invoked[p] = (name in o[p] ?
207           o[p][name].apply(ctx || o[p], args || []) : undefined);
208       }
209     }
210     return invoked;
211   },
212 
213   /**
214    * Creates an array containing the property names of an object.
215    *
216    * @name keys
217    * @methodOf Obj
218    * @param {Object} o The object
219    * @return {Array} The object's keys
220    */
221   keys: function (o) {
222     var keys = [];
223     for (var p in o) {
224       if (o.hasOwnProperty(p)) {
225         keys.push(p);
226       }
227     }
228     return keys;
229   },
230 
231   /**
232    * Runs the passed function for each property in the object.
233    *
234    * @name forEach
235    * @methodOf Obj
236    * @param {Object} o The object to iterate.
237    * @param {Function} fn The function to call.
238    * @param {Object} ctx The function's execution context.
239    */
240   forEach: function (o, fn, ctx) {
241     for (var p in o) {
242       if (o.hasOwnProperty(p)) {
243         fn.call(ctx || o, o[p], p, o);
244       }
245     }
246   },
247 
248   /**
249    * Creates an array containing the values of a function applied to
250    * each property in the object.
251    *
252    * @example
253    * Object.map({ Rice: 'Jerry', Clark: 'Gary' }, function (v, p, o) {
254    *   return p + ', ' + v;
255    * }; // { Rice: 'Rice, Jerry', Clark: 'Clark, Gary' }
256    *
257    * @name map
258    * @methodOf Obj
259    * @param {Object} o The object to iterate.
260    * @param {Function} fn The map function.
261    * @param {Object} ctx The function's execution context, called with (value,
262    * property, object).
263    * @return {Object} The mapped object.
264    */
265   map: function (o, fn, ctx) {
266     var mapped = {};
267     for (var p in o) {
268       if (o.hasOwnProperty(p)) {
269         mapped[p] = fn.call(ctx || o, o[p], o, o);
270       }
271     }
272     return mapped;
273   },
274 
275   /**
276    * Creates a nested object to the depth specified, not overwriting
277    * existing properties.
278    *
279    * @name namespace
280    * @methodOf Obj
281    * @param {String} path The period '.' separated object path.
282    * @return {Object} The resulting object.
283    */
284   namespace: function (path) {
285     return path.split('.').reduce(function (o, part) {
286       return o[part] || (o[part] = {});
287     }, window);
288   },
289 
290   /**
291    * Checks if the passed item is an object.
292    *
293    * @name test
294    * @methodOf Obj
295    * @param {Object} o The item to check.
296    * @return {Boolean} true if it is an object, false if not.
297    */
298   test: function (o) {
299     return Object.prototype.toString.call(o) === '[object Object]';
300   },
301 
302   /**
303    * Returns each property's value.
304    *
305    * @name values
306    * @methodOf Obj
307    * @param {Object} o The object to use.
308    * @return {Array} An array of values.
309    */
310   values: function (o) {
311     var vals = [];
312     for (var p in o) {
313       if (o.hasOwnProperty(p)) {
314         vals.push(o[p]);
315       }
316     }
317     return vals;
318   },
319 
320   /**
321    * Checks if the two passed objects are equal, performing deep
322    * checks for Objects and Arrays.
323    *
324    * @name valuesEqual
325    * @methodOf Obj
326    * @param {Object} a The item to check.
327    * @param {Object} b The item to check it against.
328    * @return {Boolean} true if the values are equal, false if not.
329    */
330   valuesEqual: function (a, b) {
331     if (testArray(a)) {
332       return testArray(b) && a.equals(b);
333     } else if (Obj.test(a)) {
334       return Obj.test(b) && Obj.equals(a, b);
335     } else {
336       return a === b;
337     }
338   }
339 });
340 
341 Obj.alias(Obj, {
342   /**
343    * Alias of filter.
344    *
345    * @name select
346    * @methodOf Obj
347    */
348   filter: 'select',
349 
350   /**
351    * Alias of filterOne.
352    *
353    * @name detect
354    * @methodOf Obj
355    */
356   filterOne: 'detect',
357 
358   /**
359    * Alias of forEach.
360    *
361    * @name each
362    * @methodOf Obj
363    */
364   forEach: 'each',
365 
366   /**
367    * Alias of namespace
368    *
369    * @name ns
370    * @methodOf Obj
371    */
372   namespace: 'ns'
373 });
374 
375 Obj.merge(Array.prototype, {
376   /**
377    * Checks if all items in this array pass the provided test.
378    *
379    * @name all
380    * @methodOf Array#
381    * @param {Function} fn The filtering function.
382    * @param {Object} ctx The function's execution context.
383    * @return {Boolean} If each item passes.
384    */
385   all: function (fn, ctx) {
386     for (var i = 0, len = this.length; i < len; i++) {
387       if (!fn.call(ctx || this, this[i], i, this)) {
388         return false;
389       }
390     }
391     return true;
392   },
393 
394   /**
395    * Creates a copy of this array.
396    *
397    * @name copy
398    * @methodOf Array#
399    * @return {Array} The copy.
400    */
401   copy: function () {
402     return this.slice(0);
403   },
404 
405   /**
406    * Checks that the passed array equals this one at each index.
407    *
408    * @name equals
409    * @methodOf Array#
410    * @param {Array} a The array to check
411    * @return {Boolean} true if equal, false if not
412    */
413   equals: function (a) {
414     if (a.length !== this.length) {
415       return false;
416     }
417     for (var i = 0, len = a.length; i < len; i++) {
418       if (!Obj.valuesEqual(this[i], a[i])) {
419         return false;
420       }
421     }
422     return true;
423   },
424 
425   /**
426    * Creates an array containing only the elements in this one for
427    * which the filtering function returns true.
428    *
429    * @name filter
430    * @methodOf Array#
431    * @param {Function} fn The filtering function.
432    * @param {Object} ctx The function's execution context.
433    * @return {Array} The filtered array.
434    */
435   filter: function (fn, ctx) {
436     var filtered = [];
437     for (var i = 0, len = this.length; i < len; i++) {
438       if (fn.call(ctx || this, this[i], i, this)) {
439         filtered.push(this[i]);
440       }
441     }
442     return filtered;
443   },
444 
445   /**
446    * Creates a flattened array, merging down all sub-arrays.
447    *
448    * @name flatten
449    * @methodOf Array#
450    * @return {Array} The flattened array.
451    */
452   flatten: function () {
453     var flattened = [];
454     for (var i = 0, len = this.length; i < len; i++) {
455       if (testArray(this[i])) {
456         flattened = flattened.concat(this[i].flatten());
457       } else {
458         flattened.push(this[i]);
459       }
460     }
461     return flattened;
462   },
463 
464   /**
465    * The same as filter, but only returning the first match.
466    *
467    * @name filterOne
468    * @methodOf Array#
469    * @param {Function} fn The filtering function.
470    * @param {Object} ctx The function's execution context.
471    * @return {Object} The first filtered item, if found.
472    */
473   filterOne: function (fn, ctx) {
474     for (var i = 0, len = this.length; i < len; i++) {
475       if (fn.call(ctx || this, this[i], i, this)) {
476         return this[i];
477       }
478     }
479   },
480 
481   /**
482    * Gets the array's first item.
483    *
484    * @name first
485    * @methodOf Array#
486    * @return {Object} The first item.
487    */
488   first: function () {
489     return this[0];
490   },
491 
492   /**
493    * Gets a propertie's value for each object in the array.
494    *
495    * @name eachProperty
496    * @methodOf Array#
497    * @param {String} p The property value to retrieve.
498    * @param {Function} fn true if the property is a function and should be
499    * called.
500    * @param {Object} ctx The function's execution context.
501    * @return {Array} Each property's value.
502    */
503   eachProperty: function (p, fn, ctx) {
504     var vals = [];
505     for (var i = 0, len = this.length; i < len; i++) {
506       vals.push(fn ? this[i][p].apply(ctx || this[i][p]) : this[i][p]);
507     }
508     return vals;
509   },
510 
511   /**
512    * Runs the provided function for each item in this array.
513    *
514    * @name forEach
515    * @methodOf Array#
516    * @param {Function} fn The function to run.
517    * @param {Object} ctx The function's execution context.
518    */
519   forEach: function (fn, ctx) {
520     for (var i = 0, len = this.length; i < len; i++) {
521       fn.call(ctx || this, this[i], i, this);
522     }
523   },
524 
525   /**
526    * Checks if the array includes an item identical to the one passed.
527    *
528    * @name includes
529    * @methodOf Array#
530    * @param {Object} item The item to check.
531    * @return {Boolean} true if the item was found, false if not.
532    */
533   includes: function (item) {
534     for (var i = 0, len = this.length; i < len; i++) {
535       if (this[i] === item) {
536         return true;
537       }
538     }
539     return false;
540   },
541 
542   /**
543    * Gets all but the last item of the array.
544    *
545    * @name init
546    * @methodOf Array#
547    * @return {Array} The array's init.
548    */
549   init: function () {
550     return this.slice(0, this.length - 1);
551   },
552 
553   /**
554    * Calls the property, if present, on each item in this array,
555    * returning a list of results. If the named property is not present
556    * on a given item, undefined will pushed on the result list.
557    *
558    * @name invoke
559    * @methodOf Array#
560    * @param {String} name The property name to call.
561    * @param {Array} args An argument list that will be provided with
562    * each call.
563    * @param {Object} ctx The function's execution context.
564    * @return {Array} The return value of each call.
565    */
566   invoke: function (name, args, ctx) {
567     var results = [];
568     for (var i = 0, len = this.length; i < len; i++) {
569       results.push(name in this[i] ?
570         this[i][name].apply(ctx || this[i], args || []) : undefined);
571     }
572     return results;
573   },
574 
575   /**
576    * Gets the last item of the array.
577    *
578    * @name last
579    * @methodOf Array#
580    * @return {Object} The array's last item.
581    */
582   last: function () {
583     return this[this.length - 1];
584   },
585 
586   /**
587    * Creates an array containing the values of a function applied to
588    * each item in the array.
589    *
590    * @example
591    * [1, 2, 3].map(function (n, i, a) {
592    *   return n * i;
593    * }; // [0, 2, 6]
594    *
595    * @name map
596    * @methodOf Array#
597    * @param {Function} fn The map function
598    * @param {Object} ctx The function's execution context, called with (value,
599    * index, array)
600    * @return {Array} The mapped array
601    */
602   map: function (fn, ctx) {
603     var mapped = [];
604     for (var i = 0, len = this.length; i < len; i++) {
605       mapped.push(fn.call(ctx || this, this[i], i, this));
606     }
607     return mapped;
608   },
609 
610   /**
611    * Splits this array in two based on the return value of the
612    * partitioning function. The first array contains all positives and
613    * the second all negatives.
614    *
615    * @name partition
616    * @methodOf Array#
617    * @param {Function} fn The partition function.
618    * @param {Object} ctx The function's execution context.
619    * @return {Array} The array of paritions.
620    */
621   partition: function (fn, ctx) {
622     var parts = [[], []];
623     for (var i = 0, len = this.length; i < len; i++) {
624       parts[fn.call(ctx || this, this[i], i, this) ? 0 : 1].push(this[i]);
625     }
626     return parts;
627   },
628 
629   /**
630    * Processes the items of an array from left to right, applying the
631    * reducing function to each item.
632    *
633    * @name reduce
634    * @methodOf Array#
635    * @param {Function} fn The function that is called with each iteration. This
636    * function is provided with the following arguments:
637    * - The result of the previous iteration
638    * - The current item
639    * - The current item's index
640    * - The array
641    * @param {Object} init A value to be used as the previous value in the first
642    * iteration.
643    * @param {Object} ctx The context in which to apply the function.
644    * @return {Object} The reduced value.
645    */
646   reduce: function (fn, init, ctx) {
647     var i = 0, len = this.length, prev = init || this[i++];
648     while (i < len) {
649       prev = fn.apply(ctx || this, [prev, this[i], i++, this]);
650     }
651     return prev;
652   },
653 
654   /**
655    * Removes all items matching the passed object.
656    *
657    * @name remove
658    * @methodOf Array#
659    * @param {Object} o The object to remove.
660    * @return {Boolean} true if the object was removed, false if not.
661    */
662   remove: function (o) {
663     var removed = false;
664     for (var i = 0, len = this.length; i < len; i++) {
665       if (this[i] === o) {
666         removed = true;
667         this.splice(i--, 1);
668       }
669     }
670     return removed;
671   },
672 
673   /**
674    * Checks if any item in this array passes the provided test.
675    *
676    * @name some
677    * @methodOf Array#
678    * @param {Function} fn The filtering function.
679    * @param {Object} ctx The function's execution context.
680    * @return {Boolean} If any item passes.
681    */
682   some: function (fn, ctx) {
683     for (var i = 0, len = this.length; i < len; i++) {
684       if (fn.call(ctx || this, this[i], i, this)) {
685         return true;
686       }
687     }
688     return false;
689   },
690 
691   /**
692    * Returns an array containing all items not identical to those in the
693    * passed array.
694    *
695    * @name subtract
696    * @methodOf Array#
697    * @param {Array} a The array of of objects to filter.
698    * @param {Boolean} v true to check value rather than identity.
699    * @return {Array} The array with none of the passed objects.
700    */
701   subtract: function (a, v) {
702     var subtracted = [];
703 
704     if (v) {
705       for (var i = 0, len = this.length; i < len; i++) {
706         for (var j = 0, len2 = a.length; j < len2; j++) {
707           if (this[i] != a[j]) {
708             subtracted.push(this[i]);
709           }
710         }
711       }
712     } else {
713       for (var i = 0, len = this.length; i < len; i++) {
714         for (var j = 0, len2 = a.length; j < len2; j++) {
715           if (this[i] !== a[j]) {
716             subtracted.push(this[i]);
717           }
718         }
719       }
720     }
721 
722     return subtracted;
723   },
724 
725   /**
726    * Returns an array containing only items with unique identity.
727    *
728    * @name unique
729    * @methodOf Array#
730    * @return {Array} The array with no duplicate objects.
731    */
732   unique: function () {
733     var uniq = [];
734     for (var i = 0, len = this.length; i < len; i++) {
735       if (!uniq.includes(this[i])) {
736         uniq.push(this[i]);
737       }
738     }
739     return uniq;
740   },
741 
742   /**
743    * Creates an array containing arrays of each item at each index.
744    *
745    * @example
746    * [1, 2, 3].zip([4, 5, 6]); // [[1, 4], [2, 5], [3, 6]]
747    *
748    * @name zip
749    * @methodOf Array#
750    * @param {Array} arr The array to zip.
751    * @return {Array} The zipped array.
752    */
753   zip: function (arr) {
754     var zipped = [];
755     var len = this.length > arr.length ? this.length : arr.length;
756     for (var i = 0; i < len; i++) {
757       zipped.push([this[i], arr[i]]);
758     }
759     return zipped;
760   }
761 });
762 
763 Obj.merge(Array, {
764   /**
765    * Creates an array from an array-like object.
766    *
767    * @name create
768    * @methodOf Array
769    * @param {Object} o The source object
770    * @return {Array} The new array
771    */
772   create: function (o) {
773     var arr;
774     if (testArray(o)) {
775       arr = o;
776     } else if (o.toArray) {
777       arr = o.toArray();
778     } else {
779       arr = [];
780       for (var i = 0, len = o.length || 0; i < len; i++) {
781         arr.push(o[i]);
782       }
783     }
784     return arr;
785   },
786 
787   /**
788    * Checks if the object is an array.
789    *
790    * @name test
791    * @methodOf Array
792    * @param {Object} o The object to check.
793    * @return {Boolean} true if it is an array, false if not.
794    */
795   test: testArray
796 });
797 
798 Obj.alias(Array.prototype, {
799   /**
800    * Alias of eachProperty.
801    *
802    * @name pluck
803    * @methodOf Array#
804    */
805   eachProperty: 'pluck',
806 
807   /**
808    * Alias of filter.
809    *
810    * @name select
811    * @methodOf Array#
812    */
813   filter: 'select',
814 
815   /**
816    * Alias of filterOne.
817    *
818    * @name detect
819    * @methodOf Array#
820    */
821   filterOne: 'detect',
822 
823   /**
824    * Alias of forEach.
825    *
826    * @name each
827    * @methodOf Array#
828    */
829   forEach: 'each'
830 });
831 
832 Obj.merge(Function.prototype, {
833   /**
834    * Binds an execution context and an optional argument list to this
835    * function.
836    *
837    * @name bind
838    * @methodOf Function#
839    * @param {Object} ctx The bound execution context.
840    * @param {Array} args The bound arguments array.
841    * @return {Function} The bound function.
842    */
843   bind: function (ctx, args) {
844     var fn = this;
845     return function () {
846       return fn.apply(ctx, args || []);
847     };
848   },
849 
850   /**
851    * Bind a number of arguments to this function, not specifying
852    * execution context.
853    *
854    * @name curry
855    * @methodOf Function#
856    * @param {Arguments} list An argument list to bind.
857    * @return {Function} The bound function.
858    */
859   curry: function () {
860     var fn = this, args = Array.create(arguments);
861     return function () {
862       return fn.apply(this, args.concat(Array.create(arguments)));
863     };
864   },
865 
866   /**
867    * Calls the function after the specified timeout.
868    *
869    * @name delay
870    * @methodOf Function#
871    * @param {Number} ms The number of milliseconds to delay.
872    * @param {Object} ctx The function's execution context.
873    * @param {Array} args An arguments array to pass to the function.
874    * @return {Number} The timeout's ID.
875    */
876   delay: function (ms, ctx, args) {
877     var fn = this;
878     return setTimeout(function () {
879       fn.apply(ctx || this, args || []);
880     }, ms);
881   },
882 
883   repeat: function (n, ctx) {
884     var fn = this;
885     return function (x) {
886       return n.range(1).reduce(function (p) {
887         return fn.call(ctx || this, p);
888       }, x);
889     }
890   }
891 });
892 
893 Obj.merge(Function, {
894   /**
895    * Returns an empty (typically placeholder) function.
896    *
897    * @name empty
898    * @methodOf Function
899    * @return {Function} The empty function.
900    */
901   empty: function () {
902     return function () {};
903   },
904 
905   /**
906    * Checks if the provided object is a Function.
907    *
908    * @name test
909    * @methodOf Function
910    * @param {Object} o The object to check.
911    * @return {Boolean} true if it is a Function, false if not.
912    */
913   test: function (o) {
914     return Object.prototype.toString.apply(o) === '[object Function]';
915   }
916 });
917 
918 Obj.alias(Function.prototype, {
919   /**
920    * Alias of curry.
921    *
922    * @name partial
923    * @methodOf Function#
924    */
925   curry: 'partial'
926 });
927 
928 Obj.merge(Math, {
929   /**
930    * Calculates the average (mean) of a list of numbers.
931    *
932    * @name average
933    * @methodOf Math
934    * @param {Array/Arguments} list An array or argument list of numbers.
935    * @return {Number} Their average.
936    */
937   average: function (list) {
938     var nums;
939     if (testArray(list)) {
940        nums = list;
941     } else {
942       nums = Array.create(arguments);
943     }
944     return Math.sum(nums) / nums.length;
945   },
946 
947   /**
948    * Creates a range between two numbers.
949    *
950    * @name range
951    * @methodOf Math
952    * @param {Number} a The start of the range.
953    * @param {Number} b The end of the range.
954    * @return {Array} The range.
955    */
956   range: function (a, b) {
957     // Switch a and b so that the second is greater
958     if (a > b) { var _b = b; b = a; a = _b; }
959     var range = [];
960     while (a <= b && range.push(a++)) {}
961     return range;
962   },
963 
964   /**
965    * Calculates the sum of a list of numbers.
966    *
967    * @name sum
968    * @methodOf Math
969    * @param {Array/Arguments} list An array or argument list of numbers.
970    * @return {Number} Their sum.
971    */
972   sum: function (list) {
973     var nums;
974     if (testArray(list)) {
975       nums = list;
976     } else {
977       nums = Array.create(arguments);
978     }
979     return nums.reduce(function (a, b) {
980       return a + b;
981     });
982   }
983 });
984 
985 Obj.alias(Math, {
986   /**
987    * Alias of average.
988    *
989    * @name mean
990    * @methodOf Math
991    */
992   average: 'mean'
993 });
994 
995 Obj.merge(Number.prototype, {
996   /**
997    * Calls the passed function 'n' times, where 'n' is this Number.
998    *
999    * @name times
1000    * @methodOf Number#
1001    * @param {Function} fn The function to call.
1002    * @param {Object} ctx The execution context for the function.
1003    */
1004   times: function (fn, ctx) {
1005     for (var i = 1; i <= this; i++) {
1006       fn.call(ctx || this, i);
1007     }
1008   }
1009 });
1010 
1011 // Make all Math methods callable from a Number
1012 Obj.merge(Number.prototype, Obj.create([
1013   'abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor',
1014   'log', 'max', 'min', 'pow', 'random', 'round', 'sin', 'sqrt', 'tan'
1015 ].concat(Obj.keys(Math)).map(function (fn) {
1016   return [fn, function () {
1017     return Math[fn].apply(this, [this].concat(Array.prototype.slice.apply(arguments)));
1018   }];
1019 })));
1020 
1021 Obj.merge(Number, {
1022   /**
1023    * Checks if the passed object is a Number.
1024    *
1025    * @name test
1026    * @methodOf Number
1027    * @param {Object} o The object to check.
1028    * @return {Boolean} true if it is a number, false if not.
1029    */
1030   test: function (o) {
1031     return typeof o === 'number';
1032   }
1033 });
1034 
1035 Obj.alias(Number.prototype, {
1036   /**
1037    * Alias of range.
1038    *
1039    * @name to
1040    * @methodOf Number#
1041    */
1042   range: 'to'
1043 });
1044 
1045 Obj.merge(String.prototype, {
1046   /**
1047    * Capitalizes the string's first letter.
1048    *
1049    * @name capitalize
1050    * @methodOf String#
1051    * @return {String} The capitalized string.
1052    */
1053   capitalize: function () {
1054     return this.substr(0, 1).toUpperCase() + this.slice(1);
1055   },
1056 
1057   /**
1058    * Checks if this string ends with the passed string.
1059    *
1060    * @name endsWith
1061    * @methodOf String#
1062    * @param {String} str The string to check.
1063    * @return {Boolean} true if it matches, false if not.
1064    */
1065   endsWith: function (str) {
1066     return this.slice(this.length - str.length) === str;
1067   },
1068 
1069   /**
1070    * Checks if this string has non-whitespace characters.
1071    *
1072    * @name empty
1073    * @methodOf String#
1074    * @return {Boolean} true if empty, false, if not.
1075    */
1076   empty: function () {
1077     return this.strip().length < 1;
1078   },
1079 
1080   /**
1081    * Creates a formatted string.
1082    *
1083    * @example
1084    * '{0}-{1}-{2}'.format('a', 'b', 'c'); // 'a-b-c'
1085    * '{1}-{1}-{3}'.format('a', 'b', 'c', 'd'); // 'b-b-d'
1086    *
1087    * @name format
1088    * @methodOf String#
1089    * @param {Arguments} list An argument list of ordered replacements.
1090    * @return {String} The formatted string.
1091    */
1092   format: function () {
1093     var replacements = arguments;
1094     return this.replace(/\{(\d+)\}/g, function (match, group) {
1095       return replacements[Number(group)];
1096     });
1097   },
1098 
1099   /**
1100    * Concatenates this string n times.
1101    *
1102    * @name mult
1103    * @methodOf String#
1104    * @param {Number} n The number of times to concatenate the string.
1105    * @return {String} The multipled string.
1106    */
1107   mult: function (n) {
1108     var str = '';
1109     for (var i = 0; i < n; i++) {
1110       str += this;
1111     }
1112     return str;
1113   },
1114 
1115   /**
1116    * sprintf implementation: works for basic replacement but
1117    * precision, width, and flags are not implemented fully.
1118    *
1119    * @name sprintf
1120    * @methodOf String#
1121    * @param {Arguments} An argument list of ordered replacements.
1122    * @return {String} The formatted string.
1123    */
1124   sprintf: function () {
1125     var args = Array.prototype.slice.call(arguments);
1126 
1127     return this.replace(String.sprintfRegex, function (match,
1128       mainGroup, flag, width, precisionMatch, precision,
1129       specifier, chars) {
1130         // Special cases that use no argument
1131         if (specifier === '%') {
1132           return '%';
1133         }
1134         if (specifier === 'n') {
1135           return chars;
1136         }
1137         if (width === '*') {
1138           width = args.shift();
1139         }
1140         if (precisionMatch && precision === '') {
1141           precision = 0;
1142         } else if (precision === '*') {
1143           precision = args.shift();
1144         }
1145 
1146         var replace = args.shift();
1147 
1148         switch (specifier) {
1149           case 'c':
1150           case 's':
1151             replace = String(replace);
1152             break;
1153 
1154           case 'd':
1155           case 'i':
1156           case 'u':
1157             replace = Number(replace).floor();
1158             break;
1159 
1160           case 'e':
1161             replace = Number(replace).toExponential();
1162             break;
1163 
1164           case 'E':
1165             replace = Number(replace).toExponential().toUpperCase();
1166             break;
1167 
1168           case 'f':
1169             replace = Number(replace);
1170             break;
1171 
1172           case 'g':
1173           if (Number(replace).toExponential().length <=
1174              String(Number(replace)).length) {
1175               replace = Number(replace).toExponential();
1176             } else {
1177               replace = Number(replace);
1178             }
1179             break;
1180 
1181           case 'G':
1182             if (Number(replace).toExponential().length <=
1183                String(Number(replace)).length) {
1184               replace = Number(replace).toExponential().toUpperCase();
1185             } else {
1186               replace = Number(replace);
1187             }
1188             break;
1189 
1190           case 'o':
1191             replace = Number(replace).toString(8);
1192             break;
1193 
1194           case 'x':
1195             replace = Number(replace).toString(16);
1196             break;
1197 
1198           case 'X':
1199             replace = Number(replace).toString(16).toUpperCase();
1200             break;
1201 
1202           default:
1203             break;
1204         }
1205 
1206         return replace;
1207       }
1208     );
1209   },
1210 
1211   /**
1212    * Checks if this string starts with the passed string.
1213    *
1214    * @name startsWith
1215    * @methodOf String#
1216    * @param {String} str The string to check.
1217    * @return {Boolean} true if it matches, false if not.
1218    */
1219   startsWith: function (str) {
1220     return this.slice(0, str.length) === str;
1221   },
1222 
1223   /**
1224    * Strips all whitespace characters from each side of the string.
1225    *
1226    * @name strip
1227    * @methodOf String#
1228    * @return {String} The stripped string.
1229    */
1230   strip: function () {
1231     return this.replace(/^\s+/g, '').replace(/\s+$/, '');
1232   },
1233 
1234   /**
1235    * Splits the string into words.
1236    *
1237    * @name words
1238    * @methodOf String#
1239    * @return {Array} An array of words.
1240    */
1241   words: function () {
1242     return this.split(/\s+/);
1243   }
1244 });
1245 
1246 Obj.merge(String, {
1247   sprintfRegex: new RegExp(
1248     '%' + // % Prefix
1249     '(' +
1250       '([-+ #0])?'       + // Flags
1251       '(\\d+|\\*)?'      + // Width
1252       '(\\.(\\d+|\\*))?'   + // Precision
1253       '([cdieEfgGosuxXpn%])' + // Specifier
1254     ')',
1255   'g'),
1256 
1257   /**
1258    * Checks if the provided object is a string.
1259    *
1260    * @name test
1261    * @methodOf String
1262    * @param {Object} o The object to check.
1263    * @return {Boolean} true if it is a string, false if not.
1264    */
1265   test: function (o) {
1266     return typeof o === 'string';
1267   }
1268 });
1269 
1270 Obj.alias(String.prototype, {
1271   /**
1272    * Alias of mult.
1273    *
1274    * @name x
1275    * @methodOf String#
1276    */
1277   mult: 'x'
1278 });
1279 
1280 })();
1281