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