PageSourceSearch

https://www.ongate.com/Scripts/Views/sugar.js

js ongate.com collected 2026-10-06 02:09:50 UTC 466,344 bytes, 13,407 lines download raw bytes

1/*
2*  Sugar v2.0.2
3*
4*  Freely distributable and licensed under the MIT-style license.
5*  Copyright (c) Andrew Plummer
6*  https://sugarjs.com/
7*
8* ---------------------------- */
9(function() {
10    'use strict';
11
12    /***
13    * @module Core
14    * @description Core functionality including the ability to define methods and
15    *              extend onto natives.
16    *
17    ***/
18
19    // The global to export.
20    var Sugar;
21
22    // The name of Sugar in the global namespace.
23    var SUGAR_GLOBAL = 'Sugar';
24
25    // Natives available on initialization. Letting Object go first to ensure its
26    // global is set by the time the rest are checking for chainable Object methods.
27    var NATIVE_NAMES = 'Object Number String Array Date RegExp Function';
28
29    // Static method flag
30    var STATIC = 0x1;
31
32    // Instance method flag
33    var INSTANCE = 0x2;
34
35    // IE8 has a broken defineProperty but no defineProperties so this saves a try/catch.
36    var PROPERTY_DESCRIPTOR_SUPPORT = !!(Object.defineProperty && Object.defineProperties);
37
38    // The global context. Rhino uses a different "global" keyword so
39    // do an extra check to be sure that it's actually the global context.
40    var globalContext = typeof global !== 'undefined' && global.Object === Object ? global : this;
41
42    // Is the environment node?
43    var hasExports = typeof module !== 'undefined' && module.exports;
44
45    // Whether object instance methods can be mapped to the prototype.
46    var allowObjectPrototype = false;
47
48    // A map from Array to SugarArray.
49    var namespacesByName = {};
50
51    // A map from [object Object] to namespace.
52    var namespacesByClassString = {};
53
54    // Defining properties.
55    var defineProperty = PROPERTY_DESCRIPTOR_SUPPORT ? Object.defineProperty : definePropertyShim;
56
57    // A default chainable class for unknown types.
58    var DefaultChainable = getNewChainableClass('Chainable');
59
60
61    // Global methods
62
63    function setupGlobal() {
64        Sugar = globalContext[SUGAR_GLOBAL];
65        if (Sugar) {
66            // Reuse already defined Sugar global object.
67            return;
68        }
69        Sugar = function(arg) {
70            forEachProperty(Sugar, function(sugarNamespace, name) {
71                // Although only the only enumerable properties on the global
72                // object are Sugar namespaces, environments that can't set
73                // non-enumerable properties will step through the utility methods
74                // as well here, so use this check to only allow true namespaces.
75                if (hasOwn(namespacesByName, name)) {
76                    sugarNamespace.extend(arg);
77                }
78            });
79            return Sugar;
80        };
81        if (hasExports) {
82            module.exports = Sugar;
83        } else {
84            try {
85                globalContext[SUGAR_GLOBAL] = Sugar;
86            } catch (e) {
87                // Contexts such as QML have a read-only global context.
88            }
89        }
90        forEachProperty(NATIVE_NAMES.split(' '), function(name) {
91            createNamespace(name);
92        });
93        setGlobalProperties();
94    }
95
96    /***
97    * @method createNamespace(name)
98    * @returns SugarNamespace
99    * @namespace Sugar
100    * @short Creates a new Sugar namespace.
101    * @extra This method is for plugin developers who want to define methods to be
102    *        used with natives that Sugar does not handle by default. The new
103    *        namespace will appear on the `Sugar` global with all the methods of
104    *        normal namespaces, including the ability to define new methods. When
105    *        extended, any defined methods will be mapped to `name` in the global
106    *        context.
107    *
108    * @example
109    *
110    *   Sugar.createNamespace('Boolean');
111    *
112    * @param {string} name - The namespace name.
113    *
114    ***/
115    function createNamespace(name) {
116
117        // Is the current namespace Object?
118        var isObject = name === 'Object';
119
120        // A Sugar namespace is also a chainable class: Sugar.Array, etc.
121        var sugarNamespace = getNewChainableClass(name, true);
122
123        /***
124        * @method extend([opts])
125        * @returns Sugar
126        * @namespace Sugar
127        * @short Extends Sugar defined methods onto natives.
128        * @extra This method can be called on individual namespaces like
129        *        `Sugar.Array` or on the `Sugar` global itself, in which case
130        *        [opts] will be forwarded to each `extend` call. For more,
131        *        see `extending`.
132        *
133        * @options
134        *
135        *   methods           An array of method names to explicitly extend.
136        *
137        *   except            An array of method names or global namespaces (`Array`,
138        *                     `String`) to explicitly exclude. Namespaces should be the
139        *                     actual global objects, not strings.
140        *
141        *   namespaces        An array of global namespaces (`Array`, `String`) to
142        *                     explicitly extend. Namespaces should be the actual
143        *                     global objects, not strings.
144        *
145        *   enhance           A shortcut to disallow all "enhance" flags at once
146        *                     (flags listed below). For more, see `enhanced methods`.
147        *                     Default is `true`.
148        *
149        *   enhanceString     A boolean allowing String enhancements. Default is `true`.
150        *
151        *   enhanceArray      A boolean allowing Array enhancements. Default is `true`.
152        *
153        *   objectPrototype   A boolean allowing Sugar to extend Object.prototype
154        *                     with instance methods. This option is off by default
155        *                     and should generally not be used except with caution.
156        *                     For more, see `object methods`.
157        *
158        * @example
159        *
160        *   Sugar.Array.extend();
161        *   Sugar.extend();
162        *
163        * @option {Array<string>} [methods]
164        * @option {Array<string|NativeConstructor>} [except]
165        * @option {Array<NativeConstructor>} [namespaces]
166        * @option {boolean} [enhance]
167        * @option {boolean} [enhanceString]
168        * @option {boolean} [enhanceArray]
169        * @option {boolean} [objectPrototype]
170        * @param {ExtendOptions} [opts]
171        *
172        ***
173        * @method extend([opts])
174        * @returns SugarNamespace
175        * @namespace SugarNamespace
176        * @short Extends Sugar defined methods for a specific namespace onto natives.
177        * @param {ExtendOptions} [opts]
178        *
179        ***/
180        var extend = function(opts) {
181
182            var nativeClass = globalContext[name], nativeProto = nativeClass.prototype;
183            var staticMethods = {}, instanceMethods = {}, methodsByName;
184
185            function objectRestricted(name, target) {
186                return isObject && target === nativeProto &&
187               (!allowObjectPrototype || name === 'get' || name === 'set');
188            }
189
190            function arrayOptionExists(field, val) {
191                var arr = opts[field];
192                if (arr) {
193                    for (var i = 0, el; el = arr[i]; i++) {
194                        if (el === val) {
195                            return true;
196                        }
197                    }
198                }
199                return false;
200            }
201
202            function arrayOptionExcludes(field, val) {
203                return opts[field] && !arrayOptionExists(field, val);
204            }
205
206            function disallowedByFlags(methodName, target, flags) {
207                // Disallowing methods by flag currently only applies if methods already
208                // exist to avoid enhancing native methods, as aliases should still be
209                // extended (i.e. Array#all should still be extended even if Array#every
210                // is being disallowed by a flag).
211                if (!target[methodName] || !flags) {
212                    return false;
213                }
214                for (var i = 0; i < flags.length; i++) {
215                    if (opts[flags[i]] === false) {
216                        return true;
217                    }
218                }
219            }
220
221            function namespaceIsExcepted() {
222                return arrayOptionExists('except', nativeClass) ||
223               arrayOptionExcludes('namespaces', nativeClass);
224            }
225
226            function methodIsExcepted(methodName) {
227                return arrayOptionExists('except', methodName);
228            }
229
230            function canExtend(methodName, method, target) {
231                return !objectRestricted(methodName, target) &&
232               !disallowedByFlags(methodName, target, method.flags) &&
233               !methodIsExcepted(methodName);
234            }
235
236            opts = opts || {};
237            methodsByName = opts.methods;
238
239            if (namespaceIsExcepted()) {
240                return;
241            } else if (isObject && typeof opts.objectPrototype === 'boolean') {
242                // Store "objectPrototype" flag for future reference.
243                allowObjectPrototype = opts.objectPrototype;
244            }
245
246            forEachProperty(methodsByName || sugarNamespace, function(method, methodName) {
247                if (methodsByName) {
248                    // If we have method names passed in an array,
249                    // then we need to flip the key and value here
250                    // and find the method in the Sugar namespace.
251                    methodName = method;
252                    method = sugarNamespace[methodName];
253                }
254                if (hasOwn(method, 'instance') && canExtend(methodName, method, nativeProto)) {
255                    instanceMethods[methodName] = method.instance;
256                }
257                if (hasOwn(method, 'static') && canExtend(methodName, method, nativeClass)) {
258                    staticMethods[methodName] = method;
259                }
260            });
261
262            // Accessing the extend target each time instead of holding a reference as
263            // it may have been overwritten (for example Date by Sinon). Also need to
264            // access through the global to allow extension of user-defined namespaces.
265            extendNative(nativeClass, staticMethods);
266            extendNative(nativeProto, instanceMethods);
267
268            if (!methodsByName) {
269                // If there are no method names passed, then
270                // all methods in the namespace will be extended
271                // to the native. This includes all future defined
272                // methods, so add a flag here to check later.
273                setProperty(sugarNamespace, 'active', true);
274            }
275            return sugarNamespace;
276        };
277
278        function defineWithOptionCollect(methodName, instance, args) {
279            setProperty(sugarNamespace, methodName, function(arg1, arg2, arg3) {
280                var opts = collectDefineOptions(arg1, arg2, arg3);
281                defineMethods(sugarNamespace, opts.methods, instance, args, opts.last);
282                return sugarNamespace;
283            });
284        }
285
286        /***
287        * @method defineStatic(methods)
288        * @returns SugarNamespace
289        * @namespace SugarNamespace
290        * @short Defines static methods on the namespace that can later be extended
291        *        onto the native globals.
292        * @extra Accepts either a single object mapping names to functions, or name
293        *        and function as two arguments. If `extend` was previously called
294        *        with no arguments, the method will be immediately mapped to its
295        *        native when defined.
296        *
297        * @example
298        *
299        *   Sugar.Number.defineStatic({
300        *     isOdd: function (num) {
301        *       return num % 2 === 1;
302        *     }
303        *   });
304        *
305        * @signature defineStatic(methodName, methodFn)
306        * @param {Object} methods - Methods to be defined.
307        * @param {string} methodName - Name of a single method to be defined.
308        * @param {Function} methodFn - Function body of a single method to be defined.
309        ***/
310        defineWithOptionCollect('defineStatic', STATIC);
311
312        /***
313        * @method defineInstance(methods)
314        * @returns SugarNamespace
315        * @namespace SugarNamespace
316        * @short Defines methods on the namespace that can later be extended as
317        *        instance methods onto the native prototype.
318        * @extra Accepts either a single object mapping names to functions, or name
319        *        and function as two arguments. All functions should accept the
320        *        native for which they are mapped as their first argument, and should
321        *        never refer to `this`. If `extend` was previously called with no
322        *        arguments, the method will be immediately mapped to its native when
323        *        defined.
324        *
325        *        Methods cannot accept more than 4 arguments in addition to the
326        *        native (5 arguments total). Any additional arguments will not be
327        *        mapped. If the method needs to accept unlimited arguments, use
328        *        `defineInstanceWithArguments`. Otherwise if more options are
329        *        required, use an options object instead.
330        *
331        * @example
332        *
333        *   Sugar.Number.defineInstance({
334        *     square: function (num) {
335        *       return num * num;
336        *     }
337        *   });
338        *
339        * @signature defineInstance(methodName, methodFn)
340        * @param {Object} methods - Methods to be defined.
341        * @param {string} methodName - Name of a single method to be defined.
342        * @param {Function} methodFn - Function body of a single method to be defined.
343        ***/
344        defineWithOptionCollect('defineInstance', INSTANCE);
345
346        /***
347        * @method defineInstanceAndStatic(methods)
348        * @returns SugarNamespace
349        * @namespace SugarNamespace
350        * @short A shortcut to define both static and instance methods on the namespace.
351        * @extra This method is intended for use with `Object` instance methods. Sugar
352        *        will not map any methods to `Object.prototype` by default, so defining
353        *        instance methods as static helps facilitate their proper use.
354        *
355        * @example
356        *
357        *   Sugar.Object.defineInstanceAndStatic({
358        *     isAwesome: function (obj) {
359        *       // check if obj is awesome!
360        *     }
361        *   });
362        *
363        * @signature defineInstanceAndStatic(methodName, methodFn)
364        * @param {Object} methods - Methods to be defined.
365        * @param {string} methodName - Name of a single method to be defined.
366        * @param {Function} methodFn - Function body of a single method to be defined.
367        ***/
368        defineWithOptionCollect('defineInstanceAndStatic', INSTANCE | STATIC);
369
370
371        /***
372        * @method defineStaticWithArguments(methods)
373        * @returns SugarNamespace
374        * @namespace SugarNamespace
375        * @short Defines static methods that collect arguments.
376        * @extra This method is identical to `defineStatic`, except that when defined
377        *        methods are called, they will collect any arguments past `n - 1`,
378        *        where `n` is the number of arguments that the method accepts.
379        *        Collected arguments will be passed to the method in an array
380        *        as the last argument defined on the function.
381        *
382        * @example
383        *
384        *   Sugar.Number.defineStaticWithArguments({
385        *     addAll: function (num, args) {
386        *       for (var i = 0; i < args.length; i++) {
387        *         num += args[i];
388        *       }
389        *       return num;
390        *     }
391        *   });
392        *
393        * @signature defineStaticWithArguments(methodName, methodFn)
394        * @param {Object} methods - Methods to be defined.
395        * @param {string} methodName - Name of a single method to be defined.
396        * @param {Function} methodFn - Function body of a single method to be defined.
397        ***/
398        defineWithOptionCollect('defineStaticWithArguments', STATIC, true);
399
400        /***
401        * @method defineInstanceWithArguments(methods)
402        * @returns SugarNamespace
403        * @namespace SugarNamespace
404        * @short Defines instance methods that collect arguments.
405        * @extra This method is identical to `defineInstance`, except that when
406        *        defined methods are called, they will collect any arguments past
407        *        `n - 1`, where `n` is the number of arguments that the method
408        *        accepts. Collected arguments will be passed to the method as the
409        *        last argument defined on the function.
410        *
411        * @example
412        *
413        *   Sugar.Number.defineInstanceWithArguments({
414        *     addAll: function (num, args) {
415        *       for (var i = 0; i < args.length; i++) {
416        *         num += args[i];
417        *       }
418        *       return num;
419        *     }
420        *   });
421        *
422        * @signature defineInstanceWithArguments(methodName, methodFn)
423        * @param {Object} methods - Methods to be defined.
424        * @param {string} methodName - Name of a single method to be defined.
425        * @param {Function} methodFn - Function body of a single method to be defined.
426        ***/
427        defineWithOptionCollect('defineInstanceWithArguments', INSTANCE, true);
428
429        /***
430        * @method defineStaticPolyfill(methods)
431        * @returns SugarNamespace
432        * @namespace SugarNamespace
433        * @short Defines static methods that are mapped onto the native if they do
434        *        not already exist.
435        * @extra Intended only for use creating polyfills that follow the ECMAScript
436        *        spec. Accepts either a single object mapping names to functions, or
437        *        name and function as two arguments.
438        *
439        * @example
440        *
441        *   Sugar.Object.defineStaticPolyfill({
442        *     keys: function (obj) {
443        *       // get keys!
444        *     }
445        *   });
446        *
447        * @signature defineStaticPolyfill(methodName, methodFn)
448        * @param {Object} methods - Methods to be defined.
449        * @param {string} methodName - Name of a single method to be defined.
450        * @param {Function} methodFn - Function body of a single method to be defined.
451        ***/
452        setProperty(sugarNamespace, 'defineStaticPolyfill', function(arg1, arg2, arg3) {
453            var opts = collectDefineOptions(arg1, arg2, arg3);
454            extendNative(globalContext[name], opts.methods, true, opts.last);
455            return sugarNamespace;
456        });
457
458        /***
459        * @method defineInstancePolyfill(methods)
460        * @returns SugarNamespace
461        * @namespace SugarNamespace
462        * @short Defines instance methods that are mapped onto the native prototype
463        *        if they do not already exist.
464        * @extra Intended only for use creating polyfills that follow the ECMAScript
465        *        spec. Accepts either a single object mapping names to functions, or
466        *        name and function as two arguments. This method differs from
467        *        `defineInstance` as there is no static signature (as the method
468        *        is mapped as-is to the native), so it should refer to its `this`
469        *        object.
470        *
471        * @example
472        *
473        *   Sugar.Array.defineInstancePolyfill({
474        *     indexOf: function (arr, el) {
475        *       // index finding code here!
476        *     }
477        *   });
478        *
479        * @signature defineInstancePolyfill(methodName, methodFn)
480        * @param {Object} methods - Methods to be defined.
481        * @param {string} methodName - Name of a single method to be defined.
482        * @param {Function} methodFn - Function body of a single method to be defined.
483        ***/
484        setProperty(sugarNamespace, 'defineInstancePolyfill', function(arg1, arg2, arg3) {
485            var opts = collectDefineOptions(arg1, arg2, arg3);
486            extendNative(globalContext[name].prototype, opts.methods, true, opts.last);
487            // Map instance polyfills to chainable as well.
488            forEachProperty(opts.methods, function(fn, methodName) {
489                defineChainableMethod(sugarNamespace, methodName, fn);
490            });
491            return sugarNamespace;
492        });
493
494        /***
495        * @method alias(toName, from)
496        * @returns SugarNamespace
497        * @namespace SugarNamespace
498        * @short Aliases one Sugar method to another.
499        *
500        * @example
501        *
502        *   Sugar.Array.alias('all', 'every');
503        *
504        * @signature alias(toName, fn)
505        * @param {string} toName - Name for new method.
506        * @param {string|Function} from - Method to alias, or string shortcut.
507        ***/
508        setProperty(sugarNamespace, 'alias', function(name, source) {
509            var method = typeof source === 'string' ? sugarNamespace[source] : source;
510            setMethod(sugarNamespace, name, method);
511            return sugarNamespace;
512        });
513
514        // Each namespace can extend only itself through its .extend method.
515        setProperty(sugarNamespace, 'extend', extend);
516
517        // Cache the class to namespace relationship for later use.
518        namespacesByName[name] = sugarNamespace;
519        namespacesByClassString['[object ' + name + ']'] = sugarNamespace;
520
521        mapNativeToChainable(name);
522        mapObjectChainablesToNamespace(sugarNamespace);
523
524
525        // Export
526        return Sugar[name] = sugarNamespace;
527    }
528
529    function setGlobalProperties() {
530        setProperty(Sugar, 'extend', Sugar);
531        setProperty(Sugar, 'toString', toString);
532        setProperty(Sugar, 'createNamespace', createNamespace);
533
534        setProperty(Sugar, 'util', {
535            'hasOwn': hasOwn,
536            'getOwn': getOwn,
537            'setProperty': setProperty,
538            'classToString': classToString,
539            'defineProperty': defineProperty,
540            'forEachProperty': forEachProperty,
541            'mapNativeToChainable': mapNativeToChainable
542        });
543    }
544
545    function toString() {
546        return SUGAR_GLOBAL;
547    }
548
549
550    // Defining Methods
551
552    function defineMethods(sugarNamespace, methods, type, args, flags) {
553        forEachProperty(methods, function(method, methodName) {
554            var instanceMethod, staticMethod = method;
555            if (args) {
556                staticMethod = wrapMethodWithArguments(method);
557            }
558            if (flags) {
559                staticMethod.flags = flags;
560            }
561
562            // A method may define its own custom implementation, so
563            // make sure that's not the case before creating one.
564            if (type & INSTANCE && !method.instance) {
565                instanceMethod = wrapInstanceMethod(method, args);
566                setProperty(staticMethod, 'instance', instanceMethod);
567            }
568
569            if (type & STATIC) {
570                setProperty(staticMethod, 'static', true);
571            }
572
573            setMethod(sugarNamespace, methodName, staticMethod);
574
575            if (sugarNamespace.active) {
576                // If the namespace has been activated (.extend has been called),
577                // then map this method as well.
578                sugarNamespace.extend(methodName);
579            }
580        });
581    }
582
583    function collectDefineOptions(arg1, arg2, arg3) {
584        var methods, last;
585        if (typeof arg1 === 'string') {
586            methods = {};
587            methods[arg1] = arg2;
588            last = arg3;
589        } else {
590            methods = arg1;
591            last = arg2;
592        }
593        return {
594            last: last,
595            methods: methods
596        };
597    }
598
599    function wrapInstanceMethod(fn, args) {
600        return args ? wrapMethodWithArguments(fn, true) : wrapInstanceMethodFixed(fn);
601    }
602
603    function wrapMethodWithArguments(fn, instance) {
604        // Functions accepting enumerated arguments will always have "args" as the
605        // last argument, so subtract one from the function length to get the point
606        // at which to start collecting arguments. If this is an instance method on
607        // a prototype, then "this" will be pushed into the arguments array so start
608        // collecting 1 argument earlier.
609        var startCollect = fn.length - 1 - (instance ? 1 : 0);
610        return function() {
611            var args = [], collectedArgs = [], len;
612            if (instance) {
613                args.push(this);
614            }
615            len = Math.max(arguments.length, startCollect);
616            // Optimized: no leaking arguments
617            for (var i = 0; i < len; i++) {
618                if (i < startCollect) {
619                    args.push(arguments[i]);
620                } else {
621                    collectedArgs.push(arguments[i]);
622                }
623            }
624            args.push(collectedArgs);
625            return fn.apply(this, args);
626        };
627    }
628
629    function wrapInstanceMethodFixed(fn) {
630        switch (fn.length) {
631            // Wrapped instance methods will always be passed the instance 
632            // as the first argument, but requiring the argument to be defined 
633            // may cause confusion here, so return the same wrapped function regardless. 
634            case 0:
635            case 1:
636                return function() {
637                    return fn(this);
638                };
639            case 2:
640                return function(a) {
641                    return fn(this, a);
642                };
643            case 3:
644                return function(a, b) {
645                    return fn(this, a, b);
646                };
647            case 4:
648                return function(a, b, c) {
649                    return fn(this, a, b, c);
650                };
651            case 5:
652                return function(a, b, c, d) {
653                    return fn(this, a, b, c, d);
654                };
655        }
656    }
657
658    // Method helpers
659
660    function extendNative(target, source, polyfill, override) {
661        forEachProperty(source, function(method, name) {
662            if (polyfill && !override && target[name]) {
663                // Method exists, so bail.
664                return;
665            }
666            setProperty(target, name, method);
667        });
668    }
669
670    function setMethod(sugarNamespace, methodName, method) {
671        sugarNamespace[methodName] = method;
672        if (method.instance) {
673            defineChainableMethod(sugarNamespace, methodName, method.instance, true);
674        }
675    }
676
677
678    // Chainables
679
680    function getNewChainableClass(name) {
681        var fn = function SugarChainable(obj, arg) {
682            if (!(this instanceof fn)) {
683                return new fn(obj, arg);
684            }
685            if (this.constructor !== fn) {
686                // Allow modules to define their own constructors.
687                obj = this.constructor.apply(obj, arguments);
688            }
689            this.raw = obj;
690        };
691        setProperty(fn, 'toString', function() {
692            return SUGAR_GLOBAL + name;
693        });
694        setProperty(fn.prototype, 'valueOf', function() {
695            return this.raw;
696        });
697        return fn;
698    }
699
700    function defineChainableMethod(sugarNamespace, methodName, fn) {
701        var wrapped = wrapWithChainableResult(fn), existing, collision, dcp;
702        dcp = DefaultChainable.prototype;
703        existing = dcp[methodName];
704
705        // If the method was previously defined on the default chainable, then a
706        // collision exists, so set the method to a disambiguation function that will
707        // lazily evaluate the object and find it's associated chainable. An extra
708        // check is required to avoid false positives from Object inherited methods.
709        collision = existing && existing !== Object.prototype[methodName];
710
711        // The disambiguation function is only required once.
712        if (!existing || !existing.disambiguate) {
713            dcp[methodName] = collision ? disambiguateMethod(methodName) : wrapped;
714        }
715
716        // The target chainable always receives the wrapped method. Additionally,
717        // if the target chainable is Sugar.Object, then map the wrapped method
718        // to all other namespaces as well if they do not define their own method
719        // of the same name. This way, a Sugar.Number will have methods like
720        // isEqual that can be called on any object without having to traverse up
721        // the prototype chain and perform disambiguation, which costs cycles.
722        // Note that the "if" block below actually does nothing on init as Object
723        // goes first and no other namespaces exist yet. However it needs to be
724        // here as Object instance methods defined later also need to be mapped
725        // back onto existing namespaces.
726        sugarNamespace.prototype[methodName] = wrapped;
727        if (sugarNamespace === Sugar.Object) {
728            mapObjectChainableToAllNamespaces(methodName, wrapped);
729        }
730    }
731
732    function mapObjectChainablesToNamespace(sugarNamespace) {
733        forEachProperty(Sugar.Object && Sugar.Object.prototype, function(val, methodName) {
734            if (typeof val === 'function') {
735                setObjectChainableOnNamespace(sugarNamespace, methodName, val);
736            }
737        });
738    }
739
740    function mapObjectChainableToAllNamespaces(methodName, fn) {
741        forEachProperty(namespacesByName, function(sugarNamespace) {
742            setObjectChainableOnNamespace(sugarNamespace, methodName, fn);
743        });
744    }
745
746    function setObjectChainableOnNamespace(sugarNamespace, methodName, fn) {
747        var proto = sugarNamespace.prototype;
748        if (!hasOwn(proto, methodName)) {
749            proto[methodName] = fn;
750        }
751    }
752
753    function wrapWithChainableResult(fn) {
754        return function() {
755            return new DefaultChainable(fn.apply(this.raw, arguments));
756        };
757    }
758
759    function disambiguateMethod(methodName) {
760        var fn = function() {
761            var raw = this.raw, sugarNamespace, fn;
762            if (raw != null) {
763                // Find the Sugar namespace for this unknown.
764                sugarNamespace = namespacesByClassString[classToString(raw)];
765            }
766            if (!sugarNamespace) {
767                // If no sugarNamespace can be resolved, then default
768                // back to Sugar.Object so that undefined and other
769                // non-supported types can still have basic object
770                // methods called on them, such as type checks.
771                sugarNamespace = Sugar.Object;
772            }
773
774            fn = new sugarNamespace(raw)[methodName];
775
776            if (fn.disambiguate) {
777                // If the method about to be called on this chainable is
778                // itself a disambiguation method, then throw an error to
779                // prevent infinite recursion.
780                throw new TypeError('Cannot resolve namespace for ' + raw);
781            }
782
783            return fn.apply(this, arguments);
784        };
785        fn.disambiguate = true;
786        return fn;
787    }
788
789    function mapNativeToChainable(name, methodNames) {
790        var sugarNamespace = namespacesByName[name],
791        nativeProto = globalContext[name].prototype;
792
793        if (!methodNames && ownPropertyNames) {
794            methodNames = ownPropertyNames(nativeProto);
795        }
796
797        forEachProperty(methodNames, function(methodName) {
798            if (nativeMethodProhibited(methodName)) {
799                // Sugar chainables have their own constructors as well as "valueOf"
800                // methods, so exclude them here. The __proto__ argument should be trapped
801                // by the function check below, however simply accessing this property on
802                // Object.prototype causes QML to segfault, so pre-emptively excluding it.
803                return;
804            }
805            try {
806                var fn = nativeProto[methodName];
807                if (typeof fn !== 'function') {
808                    // Bail on anything not a function.
809                    return;
810                }
811            } catch (e) {
812                // Function.prototype has properties that
813                // will throw errors when accessed.
814                return;
815            }
816            defineChainableMethod(sugarNamespace, methodName, fn);
817        });
818    }
819
820    function nativeMethodProhibited(methodName) {
821        return methodName === 'constructor' ||
822           methodName === 'valueOf' ||
823           methodName === '__proto__';
824    }
825
826
827    // Util
828
829    // Internal references
830    var ownPropertyNames = Object.getOwnPropertyNames,
831      internalToString = Object.prototype.toString,
832      internalHasOwnProperty = Object.prototype.hasOwnProperty;
833
834    // Defining this as a variable here as the ES5 module
835    // overwrites it to patch DONTENUM.
836    var forEachProperty = function(obj, fn) {
837        for (var key in obj) {
838            if (!hasOwn(obj, key)) continue;
839            if (fn.call(obj, obj[key], key, obj) === false) break;
840        }
841    };
842
843    function definePropertyShim(obj, prop, descriptor) {
844        obj[prop] = descriptor.value;
845    }
846
847    function setProperty(target, name, value, enumerable) {
848        defineProperty(target, name, {
849            value: value,
850            enumerable: !!enumerable,
851            configurable: true,
852            writable: true
853        });
854    }
855
856    // PERF: Attempts to speed this method up get very Heisenbergy. Quickly
857    // returning based on typeof works for primitives, but slows down object
858    // types. Even === checks on null and undefined (no typeof) will end up
859    // basically breaking even. This seems to be as fast as it can go.
860    function classToString(obj) {
861        return internalToString.call(obj);
862    }
863
864    function hasOwn(obj, prop) {
865        return !!obj && internalHasOwnProperty.call(obj, prop);
866    }
867
868    function getOwn(obj, prop) {
869        if (hasOwn(obj, prop)) {
870            return obj[prop];
871        }
872    }
873
874    setupGlobal();
875
876    /***
877    * @module Common
878    * @description Internal utility and common methods.
879    ***/
880
881    // Flag allowing native methods to be enhanced
882    var ENHANCEMENTS_FLAG = 'enhance';
883
884    // For type checking, etc. Excludes object as this is more nuanced.
885    var NATIVE_TYPES = 'Boolean Number String Date RegExp Function Array Error Set Map';
886
887    // Do strings have no keys?
888    var NO_KEYS_IN_STRING_OBJECTS = !('0' in Object('a'));
889
890    // Prefix for private properties
891    var PRIVATE_PROP_PREFIX = '_sugar_';
892
893    // Matches 1..2 style ranges in properties
894    var PROPERTY_RANGE_REG = /^(.*?)\[([-\d]*)\.\.([-\d]*)\](.*)$/;
895
896    // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
897    var TRIM_CHARS = '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF';
898
899    // Regex for matching a formatted string
900    var STRING_FORMAT_REG = /([{}])\1|\{([^}]*)\}|(%)%|(%(\w*))/g;
901
902    // Common chars
903    var HALF_WIDTH_ZERO = 0x30,
904      FULL_WIDTH_ZERO = 0xff10,
905      HALF_WIDTH_PERIOD = '.',
906      FULL_WIDTH_PERIOD = '.',
907      HALF_WIDTH_COMMA = ',',
908      OPEN_BRACE = '{',
909      CLOSE_BRACE = '}';
910
911    // Namespace aliases
912    var sugarObject = Sugar.Object,
913      sugarArray = Sugar.Array,
914      sugarDate = Sugar.Date,
915      sugarString = Sugar.String,
916      sugarNumber = Sugar.Number,
917      sugarFunction = Sugar.Function,
918      sugarRegExp = Sugar.RegExp;
919
920    // Class checks
921    var isSerializable,
922      isBoolean, isNumber, isString,
923      isDate, isRegExp, isFunction,
924      isArray, isSet, isMap, isError;
925
926    function buildClassChecks() {
927
928        var knownTypes = {};
929
930        function addCoreTypes() {
931
932            var names = spaceSplit(NATIVE_TYPES);
933
934            isBoolean = buildPrimitiveClassCheck(names[0]);
935            isNumber = buildPrimitiveClassCheck(names[1]);
936            isString = buildPrimitiveClassCheck(names[2]);
937
938            isDate = buildClassCheck(names[3]);
939            isRegExp = buildClassCheck(names[4]);
940
941            // Wanted to enhance performance here by using simply "typeof"
942            // but Firefox has two major issues that make this impossible,
943            // one fixed, the other not, so perform a full class check here.
944            //
945            // 1. Regexes can be typeof "function" in FF < 3
946            //    https://bugzilla.mozilla.org/show_bug.cgi?id=61911 (fixed)
947            //
948            // 2. HTMLEmbedElement and HTMLObjectElement are be typeof "function"
949            //    https://bugzilla.mozilla.org/show_bug.cgi?id=268945 (won't fix)
950            isFunction = buildClassCheck(names[5]);
951
952
953            isArray = Array.isArray || buildClassCheck(names[6]);
954            isError = buildClassCheck(names[7]);
955
956            isSet = buildClassCheck(names[8], typeof Set !== 'undefined' && Set);
957            isMap = buildClassCheck(names[9], typeof Map !== 'undefined' && Map);
958
959            // Add core types as known so that they can be checked by value below,
960            // notably excluding Functions and adding Arguments and Error.
961            addKnownType('Arguments');
962            addKnownType(names[0]);
963            addKnownType(names[1]);
964            addKnownType(names[2]);
965            addKnownType(names[3]);
966            addKnownType(names[4]);
967            addKnownType(names[6]);
968
969        }
970
971        function addArrayTypes() {
972            var types = 'Int8 Uint8 Uint8Clamped Int16 Uint16 Int32 Uint32 Float32 Float64';
973            forEach(spaceSplit(types), function(str) {
974                addKnownType(str + 'Array');
975            });
976        }
977
978        function addKnownType(className) {
979            var str = '[object ' + className + ']';
980            knownTypes[str] = true;
981        }
982
983        function isKnownType(className) {
984            return knownTypes[className];
985        }
986
987        function buildClassCheck(className, globalObject) {
988            if (globalObject && isClass(new globalObject, 'Object')) {
989                return getConstructorClassCheck(globalObject);
990            } else {
991                return getToStringClassCheck(className);
992            }
993        }
994
995        function getConstructorClassCheck(obj) {
996            var ctorStr = String(obj);
997            return function(obj) {
998                return String(obj.constructor) === ctorStr;
999            };
1000        }
1001
1002        function getToStringClassCheck(className) {
1003            return function(obj, str) {
1004                // perf: Returning up front on instanceof appears to be slower.
1005                return isClass(obj, className, str);
1006            };
1007        }
1008
1009        function buildPrimitiveClassCheck(className) {
1010            var type = className.toLowerCase();
1011            return function(obj) {
1012                var t = typeof obj;
1013                return t === type || t === 'object' && isClass(obj, className);
1014            };
1015        }
1016
1017        addCoreTypes();
1018        addArrayTypes();
1019
1020        isSerializable = function(obj, className) {
1021            // Only known objects can be serialized. This notably excludes functions,
1022            // host objects, Symbols (which are matched by reference), and instances
1023            // of classes. The latter can arguably be matched by value, but
1024            // distinguishing between these and host objects -- which should never be
1025            // compared by value -- is very tricky so not dealing with it here.
1026            className = className || classToString(obj);
1027            return isKnownType(className) || isPlainObject(obj, className);
1028        };
1029
1030    }
1031
1032    function isClass(obj, className, str) {
1033        if (!str) {
1034            str = classToString(obj);
1035        }
1036        return str === '[object ' + className + ']';
1037    }
1038
1039    // Wrapping the core's "define" methods to
1040    // save a few bytes in the minified script.
1041    function wrapNamespace(method) {
1042        return function(sugarNamespace, arg1, arg2) {
1043            sugarNamespace[method](arg1, arg2);
1044        };
1045    }
1046
1047    // Method define aliases
1048    var alias = wrapNamespace('alias'),
1049      defineStatic = wrapNamespace('defineStatic'),
1050      defineInstance = wrapNamespace('defineInstance'),
1051      defineStaticPolyfill = wrapNamespace('defineStaticPolyfill'),
1052      defineInstancePolyfill = wrapNamespace('defineInstancePolyfill'),
1053      defineInstanceAndStatic = wrapNamespace('defineInstanceAndStatic'),
1054      defineInstanceWithArguments = wrapNamespace('defineInstanceWithArguments');
1055
1056    function defineInstanceSimilar(sugarNamespace, set, fn, flags) {
1057        defineInstance(sugarNamespace, collectSimilarMethods(set, fn), flags);
1058    }
1059
1060    function defineInstanceAndStaticSimilar(sugarNamespace, set, fn, flags) {
1061        defineInstanceAndStatic(sugarNamespace, collectSimilarMethods(set, fn), flags);
1062    }
1063
1064    function collectSimilarMethods(set, fn) {
1065        var methods = {};
1066        if (isString(set)) {
1067            set = spaceSplit(set);
1068        }
1069        forEach(set, function(el, i) {
1070            fn(methods, el, i);
1071        });
1072        return methods;
1073    }
1074
1075    // This song and dance is to fix methods to a different length
1076    // from what they actually accept in order to stay in line with
1077    // spec. Additionally passing argument length, as some methods
1078    // throw assertion errors based on this (undefined check is not
1079    // enough). Fortunately for now spec is such that passing 3
1080    // actual arguments covers all requirements. Note that passing
1081    // the argument length also forces the compiler to not rewrite
1082    // length of the compiled function.
1083    function fixArgumentLength(fn) {
1084        var staticFn = function(a) {
1085            var args = arguments;
1086            return fn(a, args[1], args[2], args.length - 1);
1087        };
1088        staticFn.instance = function(b) {
1089            var args = arguments;
1090            return fn(this, b, args[1], args.length);
1091        };
1092        return staticFn;
1093    }
1094
1095    function defineAccessor(namespace, name, fn) {
1096        setProperty(namespace, name, fn);
1097    }
1098
1099    function defineOptionsAccessor(namespace, defaults) {
1100        var obj = simpleClone(defaults);
1101
1102        function getOption(name) {
1103            return obj[name];
1104        }
1105
1106        function setOption(arg1, arg2) {
1107            var options;
1108            if (arguments.length === 1) {
1109                options = arg1;
1110            } else {
1111                options = {};
1112                options[arg1] = arg2;
1113            }
1114            forEachProperty(options, function(val, name) {
1115                if (val === null) {
1116                    val = defaults[name];
1117                }
1118                obj[name] = val;
1119            });
1120        }
1121
1122        defineAccessor(namespace, 'getOption', getOption);
1123        defineAccessor(namespace, 'setOption', setOption);
1124        return getOption;
1125    }
1126
1127    // For methods defined directly on the prototype like Range
1128    function defineOnPrototype(ctor, methods) {
1129        var proto = ctor.prototype;
1130        forEachProperty(methods, function(val, key) {
1131            proto[key] = val;
1132        });
1133    }
1134
1135    // Argument helpers
1136
1137    function assertArgument(exists) {
1138        if (!exists) {
1139            throw new TypeError('Argument required');
1140        }
1141    }
1142
1143    function assertCallable(obj) {
1144        if (!isFunction(obj)) {
1145            throw new TypeError('Function is not callable');
1146        }
1147    }
1148
1149    function assertArray(obj) {
1150        if (!isArray(obj)) {
1151            throw new TypeError('Array required');
1152        }
1153    }
1154
1155    function assertWritable(obj) {
1156        if (isPrimitive(obj)) {
1157            // If strict mode is active then primitives will throw an
1158            // error when attempting to write properties. We can't be
1159            // sure if strict mode is available, so pre-emptively
1160            // throw an error here to ensure consistent behavior.
1161            throw new TypeError('Property cannot be written');
1162        }
1163    }
1164
1165    // Coerces an object to a positive integer.
1166    // Does not allow Infinity.
1167    function coercePositiveInteger(n) {
1168        n = +n || 0;
1169        if (n < 0 || !isNumber(n) || !isFinite(n)) {
1170            throw new RangeError('Invalid number');
1171        }
1172        return trunc(n);
1173    }
1174
1175
1176    // General helpers
1177
1178    function isDefined(o) {
1179        return o !== undefined;
1180    }
1181
1182    function isUndefined(o) {
1183        return o === undefined;
1184    }
1185
1186    function privatePropertyAccessor(key) {
1187        var privateKey = PRIVATE_PROP_PREFIX + key;
1188        return function(obj, val) {
1189            if (arguments.length > 1) {
1190                setProperty(obj, privateKey, val);
1191                return obj;
1192            }
1193            return obj[privateKey];
1194        };
1195    }
1196
1197    function setChainableConstructor(sugarNamespace, createFn) {
1198        sugarNamespace.prototype.constructor = function() {
1199            return createFn.apply(this, arguments);
1200        };
1201    }
1202
1203    // Fuzzy matching helpers
1204
1205    function getMatcher(f) {
1206        if (!isPrimitive(f)) {
1207            var className = classToString(f);
1208            if (isRegExp(f, className)) {
1209                return regexMatcher(f);
1210            } else if (isDate(f, className)) {
1211                return dateMatcher(f);
1212            } else if (isFunction(f, className)) {
1213                return functionMatcher(f);
1214            } else if (isPlainObject(f, className)) {
1215                return fuzzyMatcher(f);
1216            }
1217        }
1218        // Default is standard isEqual
1219        return defaultMatcher(f);
1220    }
1221
1222    function fuzzyMatcher(obj) {
1223        var matchers = {};
1224        return function(el, i, arr) {
1225            var matched = true;
1226            if (!isObjectType(el)) {
1227                return false;
1228            }
1229            forEachProperty(obj, function(val, key) {
1230                matchers[key] = getOwn(matchers, key) || getMatcher(val);
1231                if (matchers[key].call(arr, el[key], i, arr) === false) {
1232                    matched = false;
1233                }
1234                return matched;
1235            });
1236            return matched;
1237        };
1238    }
1239
1240    function defaultMatcher(f) {
1241        return function(el) {
1242            return isEqual(el, f);
1243        };
1244    }
1245
1246    function regexMatcher(reg) {
1247        reg = RegExp(reg);
1248        return function(el) {
1249            return reg.test(el);
1250        };
1251    }
1252
1253    function dateMatcher(d) {
1254        var ms = d.getTime();
1255        return function(el) {
1256            return !!(el && el.getTime) && el.getTime() === ms;
1257        };
1258    }
1259
1260    function functionMatcher(fn) {
1261        return function(el, i, arr) {
1262            // Return true up front if match by reference
1263            return el === fn || fn.call(arr, el, i, arr);
1264        };
1265    }
1266
1267    // Object helpers
1268
1269    function getKeys(obj) {
1270        return Object.keys(obj);
1271    }
1272
1273    function deepHasProperty(obj, key, any) {
1274        return handleDeepProperty(obj, key, any, true);
1275    }
1276
1277    function deepGetProperty(obj, key, any) {
1278        return handleDeepProperty(obj, key, any, false);
1279    }
1280
1281    function deepSetProperty(obj, key, val) {
1282        handleDeepProperty(obj, key, false, false, true, false, val);
1283        return obj;
1284    }
1285
1286    function handleDeepProperty(obj, key, any, has, fill, fillLast, val) {
1287        var ns, bs, ps, cbi, set, isLast, isPush, isIndex, nextIsIndex, exists;
1288        ns = obj || undefined;
1289        if (key == null) return;
1290
1291        if (isObjectType(key)) {
1292            // Allow array and array-like accessors
1293            bs = [key];
1294        } else {
1295            key = String(key);
1296            if (key.indexOf('..') !== -1) {
1297                return handleArrayIndexRange(obj, key, any, val);
1298            }
1299            bs = key.split('[');
1300        }
1301
1302        set = isDefined(val);
1303
1304        for (var i = 0, blen = bs.length; i < blen; i++) {
1305            ps = bs[i];
1306
1307            if (isString(ps)) {
1308                ps = periodSplit(ps);
1309            }
1310
1311            for (var j = 0, plen = ps.length; j < plen; j++) {
1312                key = ps[j];
1313
1314                // Is this the last key?
1315                isLast = i === blen - 1 && j === plen - 1;
1316
1317                // Index of the closing ]
1318                cbi = key.indexOf(']');
1319
1320                // Is the key an array index?
1321                isIndex = cbi !== -1;
1322
1323                // Is this array push syntax "[]"?
1324                isPush = set && cbi === 0;
1325
1326                // If the bracket split was successful and this is the last element
1327                // in the dot split, then we know the next key will be an array index.
1328                nextIsIndex = blen > 1 && j === plen - 1;
1329
1330                if (isPush) {
1331                    // Set the index to the end of the array
1332                    key = ns.length;
1333                } else if (isIndex) {
1334                    // Remove the closing ]
1335                    key = key.slice(0, -1);
1336                }
1337
1338                // If the array index is less than 0, then
1339                // add its length to allow negative indexes.
1340                if (isIndex && key < 0) {
1341                    key = +key + ns.length;
1342                }
1343
1344                // Bracket keys may look like users[5] or just [5], so the leading
1345                // characters are optional. We can enter the namespace if this is the
1346                // 2nd part, if there is only 1 part, or if there is an explicit key.
1347                if (i || key || blen === 1) {
1348
1349                    exists = any ? key in ns : hasOwn(ns, key);
1350
1351                    // Non-existent namespaces are only filled if they are intermediate
1352                    // (not at the end) or explicitly filling the last.
1353                    if (fill && (!isLast || fillLast) && !exists) {
1354                        // For our purposes, last only needs to be an array.
1355                        ns = ns[key] = nextIsIndex || (fillLast && isLast) ? [] : {};
1356                        continue;
1357                    }
1358
1359                    if (has) {
1360                        if (isLast || !exists) {
1361                            return exists;
1362                        }
1363                    } else if (set && isLast) {
1364                        assertWritable(ns);
1365                        ns[key] = val;
1366                    }
1367
1368                    ns = exists ? ns[key] : undefined;
1369                }
1370
1371            }
1372        }
1373        return ns;
1374    }
1375
1376    // Get object property with support for 0..1 style range notation.
1377    function handleArrayIndexRange(obj, key, any, val) {
1378        var match, start, end, leading, trailing, arr, set;
1379        match = key.match(PROPERTY_RANGE_REG);
1380        if (!match) {
1381            return;
1382        }
1383
1384        set = isDefined(val);
1385        leading = match[1];
1386
1387        if (leading) {
1388            arr = handleDeepProperty(obj, leading, any, false, set ? true : false, true);
1389        } else {
1390            arr = obj;
1391        }
1392
1393        assertArray(arr);
1394
1395        trailing = match[4];
1396        start = match[2] ? +match[2] : 0;
1397        end = match[3] ? +match[3] : arr.length;
1398
1399        // A range of 0..1 is inclusive, so we need to add 1 to the end. If this
1400        // pushes the index from -1 to 0, then set it to the full length of the
1401        // array, otherwise it will return nothing.
1402        end = end === -1 ? arr.length : end + 1;
1403
1404        if (set) {
1405            for (var i = start; i < end; i++) {
1406                handleDeepProperty(arr, i + trailing, any, false, true, false, val);
1407            }
1408        } else {
1409            arr = arr.slice(start, end);
1410
1411            // If there are trailing properties, then they need to be mapped for each
1412            // element in the array.
1413            if (trailing) {
1414                if (trailing.charAt(0) === HALF_WIDTH_PERIOD) {
1415                    // Need to chomp the period if one is trailing after the range. We
1416                    // can't do this at the regex level because it will be required if
1417                    // we're setting the value as it needs to be concatentated together
1418                    // with the array index to be set.
1419                    trailing = trailing.slice(1);
1420                }
1421                return arr.map(function(el) {
1422                    return handleDeepProperty(el, trailing);
1423                });
1424            }
1425        }
1426        return arr;
1427    }
1428
1429    function getOwnKey(obj, key) {
1430        if (hasOwn(obj, key)) {
1431            return key;
1432        }
1433    }
1434
1435    function hasProperty(obj, prop) {
1436        return !isPrimitive(obj) && prop in obj;
1437    }
1438
1439    function isObjectType(obj, type) {
1440        return !!obj && (type || typeof obj) === 'object';
1441    }
1442
1443    function isPrimitive(obj, type) {
1444        type = type || typeof obj;
1445        return obj == null || type === 'string' || type === 'number' || type === 'boolean';
1446    }
1447
1448    function isPlainObject(obj, className) {
1449        return isObjectType(obj) &&
1450           isClass(obj, 'Object', className) &&
1451           hasValidPlainObjectPrototype(obj) &&
1452           hasOwnEnumeratedProperties(obj);
1453    }
1454
1455    function hasValidPlainObjectPrototype(obj) {
1456        var hasToString = 'toString' in obj;
1457        var hasConstructor = 'constructor' in obj;
1458        // An object created with Object.create(null) has no methods in the
1459        // prototype chain, so check if any are missing. The additional hasToString
1460        // check is for false positives on some host objects in old IE which have
1461        // toString but no constructor. If the object has an inherited constructor,
1462        // then check if it is Object (the "isPrototypeOf" tapdance here is a more
1463        // robust way of ensuring this if the global has been hijacked). Note that
1464        // accessing the constructor directly (without "in" or "hasOwnProperty")
1465        // will throw a permissions error in IE8 on cross-domain windows.
1466        return (!hasConstructor && !hasToString) ||
1467            (hasConstructor && !hasOwn(obj, 'constructor') &&
1468             hasOwn(obj.constructor.prototype, 'isPrototypeOf'));
1469    }
1470
1471    function hasOwnEnumeratedProperties(obj) {
1472        // Plain objects are generally defined as having enumerated properties
1473        // all their own, however in early IE environments without defineProperty,
1474        // there may also be enumerated methods in the prototype chain, so check
1475        // for both of these cases.
1476        var objectProto = Object.prototype;
1477        for (var key in obj) {
1478            var val = obj[key];
1479            if (!hasOwn(obj, key) && val !== objectProto[key]) {
1480                return false;
1481            }
1482        }
1483        return true;
1484    }
1485
1486    function simpleRepeat(n, fn) {
1487        for (var i = 0; i < n; i++) {
1488            fn(i);
1489        }
1490    }
1491
1492    function simpleClone(obj) {
1493        return simpleMerge({}, obj);
1494    }
1495
1496    function simpleMerge(target, source) {
1497        forEachProperty(source, function(val, key) {
1498            target[key] = val;
1499        });
1500        return target;
1501    }
1502
1503    // Make primtives types like strings into objects.
1504    function coercePrimitiveToObject(obj) {
1505        if (isPrimitive(obj)) {
1506            obj = Object(obj);
1507        }
1508        if (NO_KEYS_IN_STRING_OBJECTS && isString(obj)) {
1509            forceStringCoercion(obj);
1510        }
1511        return obj;
1512    }
1513
1514    // Force strings to have their indexes set in
1515    // environments that don't do this automatically.
1516    function forceStringCoercion(obj) {
1517        var i = 0, chr;
1518        while (chr = obj.charAt(i)) {
1519            obj[i++] = chr;
1520        }
1521    }
1522
1523    // Equality helpers
1524
1525    function isEqual(a, b, stack) {
1526        var aClass, bClass;
1527        if (a === b) {
1528            // Return quickly up front when matched by reference,
1529            // but be careful about 0 !== -0.
1530            return a !== 0 || 1 / a === 1 / b;
1531        }
1532        aClass = classToString(a);
1533        bClass = classToString(b);
1534        if (aClass !== bClass) {
1535            return false;
1536        }
1537
1538        if (isSerializable(a, aClass) && isSerializable(b, bClass)) {
1539            return objectIsEqual(a, b, aClass, stack);
1540        } else if (isSet(a, aClass) && isSet(b, bClass)) {
1541            return a.size === b.size && isEqual(setToArray(a), setToArray(b), stack);
1542        } else if (isMap(a, aClass) && isMap(b, bClass)) {
1543            return a.size === b.size && isEqual(mapToArray(a), mapToArray(b), stack);
1544        } else if (isError(a, aClass) && isError(b, bClass)) {
1545            return a.toString() === b.toString();
1546        }
1547
1548        return false;
1549    }
1550
1551    function objectIsEqual(a, b, aClass, stack) {
1552        var aType = typeof a, bType = typeof b, propsEqual, count;
1553        if (aType !== bType) {
1554            return false;
1555        }
1556        if (isObjectType(a.valueOf())) {
1557            if (a.length !== b.length) {
1558                // perf: Quickly returning up front for arrays.
1559                return false;
1560            }
1561            count = 0;
1562            propsEqual = true;
1563            iterateWithCyclicCheck(a, false, stack, function(key, val, cyc, stack) {
1564                if (!cyc && (!(key in b) || !isEqual(val, b[key], stack))) {
1565                    propsEqual = false;
1566                }
1567                count++;
1568                return propsEqual;
1569            });
1570            if (!propsEqual || count !== getKeys(b).length) {
1571                return false;
1572            }
1573        }
1574        // Stringifying the value handles NaN, wrapped primitives, dates, and errors in one go.
1575        return a.valueOf().toString() === b.valueOf().toString();
1576    }
1577
1578    // Serializes an object in a way that will provide a token unique
1579    // to the type, class, and value of an object. Host objects, class
1580    // instances etc, are not serializable, and are held in an array
1581    // of references that will return the index as a unique identifier
1582    // for the object. This array is passed from outside so that the
1583    // calling function can decide when to dispose of this array.
1584    function serializeInternal(obj, refs, stack) {
1585        var type = typeof obj, className, value, ref;
1586
1587        // Return quickly for primitives to save cycles
1588        if (isPrimitive(obj, type) && !isRealNaN(obj)) {
1589            return type + obj;
1590        }
1591
1592        className = classToString(obj);
1593
1594        if (!isSerializable(obj, className)) {
1595            ref = indexOf(refs, obj);
1596            if (ref === -1) {
1597                ref = refs.length;
1598                refs.push(obj);
1599            }
1600            return ref;
1601        } else if (isObjectType(obj)) {
1602            value = serializeDeep(obj, refs, stack) + obj.toString();
1603        } else if (1 / obj === -Infinity) {
1604            value = '-0';
1605        } else if (obj.valueOf) {
1606            value = obj.valueOf();
1607        }
1608        return type + className + value;
1609    }
1610
1611    function serializeDeep(obj, refs, stack) {
1612        var result = '';
1613        iterateWithCyclicCheck(obj, true, stack, function(key, val, cyc, stack) {
1614            result += cyc ? 'CYC' : key + serializeInternal(val, refs, stack);
1615        });
1616        return result;
1617    }
1618
1619    function iterateWithCyclicCheck(obj, sortedKeys, stack, fn) {
1620
1621        function next(val, key) {
1622            var cyc = false;
1623
1624            // Allowing a step into the structure before triggering this check to save
1625            // cycles on standard JSON structures and also to try as hard as possible to
1626            // catch basic properties that may have been modified.
1627            if (stack.length > 1) {
1628                var i = stack.length;
1629                while (i--) {
1630                    if (stack[i] === val) {
1631                        cyc = true;
1632                    }
1633                }
1634            }
1635
1636            stack.push(val);
1637            fn(key, val, cyc, stack);
1638            stack.pop();
1639        }
1640
1641        function iterateWithSortedKeys() {
1642            // Sorted keys is required for serialization, where object order
1643            // does not matter but stringified order does.
1644            var arr = getKeys(obj).sort(), key;
1645            for (var i = 0; i < arr.length; i++) {
1646                key = arr[i];
1647                next(obj[key], arr[i]);
1648            }
1649        }
1650
1651        // This method for checking for cyclic structures was egregiously stolen from
1652        // the ingenious method by @kitcambridge from the Underscore script:
1653        // https://github.com/documentcloud/underscore/issues/240
1654        if (!stack) {
1655            stack = [];
1656        }
1657
1658        if (sortedKeys) {
1659            iterateWithSortedKeys();
1660        } else {
1661            forEachProperty(obj, next);
1662        }
1663    }
1664
1665
1666    // Array helpers
1667
1668    function isArrayIndex(n) {
1669        return n >>> 0 == n && n != 0xFFFFFFFF;
1670    }
1671
1672    function iterateOverSparseArray(arr, fn, fromIndex, loop) {
1673        var indexes = getSparseArrayIndexes(arr, fromIndex, loop), index;
1674        for (var i = 0, len = indexes.length; i < len; i++) {
1675            index = indexes[i];
1676            fn.call(arr, arr[index], index, arr);
1677        }
1678        return arr;
1679    }
1680
1681    // It's unclear whether or not sparse arrays qualify as "simple enumerables".
1682    // If they are not, however, the wrapping function will be deoptimized, so
1683    // isolate here (also to share between es5 and array modules).
1684    function getSparseArrayIndexes(arr, fromIndex, loop, fromRight) {
1685        var indexes = [], i;
1686        for (i in arr) {
1687            if (isArrayIndex(i) && (loop || (fromRight ? i <= fromIndex : i >= fromIndex))) {
1688                indexes.push(+i);
1689            }
1690        }
1691        indexes.sort(function(a, b) {
1692            var aLoop = a > fromIndex;
1693            var bLoop = b > fromIndex;
1694            if (aLoop !== bLoop) {
1695                return aLoop ? -1 : 1;
1696            }
1697            return a - b;
1698        });
1699        return indexes;
1700    }
1701
1702    function getEntriesForIndexes(obj, find, loop, isString) {
1703        var result, length = obj.length;
1704        if (!isArray(find)) {
1705            return entryAtIndex(obj, find, length, loop, isString);
1706        }
1707        result = new Array(find.length);
1708        forEach(find, function(index, i) {
1709            result[i] = entryAtIndex(obj, index, length, loop, isString);
1710        });
1711        return result;
1712    }
1713
1714    function getNormalizedIndex(index, length, loop) {
1715        if (index && loop) {
1716            index = index % length;
1717        }
1718        if (index < 0) index = length + index;
1719        return index;
1720    }
1721
1722    function entryAtIndex(obj, index, length, loop, isString) {
1723        index = getNormalizedIndex(index, length, loop);
1724        return isString ? obj.charAt(index) : obj[index];
1725    }
1726
1727    function mapWithShortcuts(el, f, context, mapArgs) {
1728        if (!f) {
1729            return el;
1730        } else if (f.apply) {
1731            return f.apply(context, mapArgs || []);
1732        } else if (isArray(f)) {
1733            return f.map(function(m) {
1734                return mapWithShortcuts(el, m, context, mapArgs);
1735            });
1736        } else if (isFunction(el[f])) {
1737            return el[f].call(el);
1738        } else {
1739            return deepGetProperty(el, f);
1740        }
1741    }
1742
1743    function spaceSplit(str) {
1744        return str.split(' ');
1745    }
1746
1747    function commaSplit(str) {
1748        return str.split(HALF_WIDTH_COMMA);
1749    }
1750
1751    function periodSplit(str) {
1752        return str.split(HALF_WIDTH_PERIOD);
1753    }
1754
1755    function forEach(arr, fn) {
1756        for (var i = 0, len = arr.length; i < len; i++) {
1757            if (!(i in arr)) {
1758                return iterateOverSparseArray(arr, fn, i);
1759            }
1760            fn(arr[i], i);
1761        }
1762    }
1763
1764    function filter(arr, fn) {
1765        var result = [];
1766        for (var i = 0, len = arr.length; i < len; i++) {
1767            var el = arr[i];
1768            if (i in arr && fn(el, i)) {
1769                result.push(el);
1770            }
1771        }
1772        return result;
1773    }
1774
1775    function map(arr, fn) {
1776        // perf: Not using fixed array len here as it may be sparse.
1777        var result = [];
1778        for (var i = 0, len = arr.length; i < len; i++) {
1779            if (i in arr) {
1780                result.push(fn(arr[i], i));
1781            }
1782        }
1783        return result;
1784    }
1785
1786    function indexOf(arr, el) {
1787        for (var i = 0, len = arr.length; i < len; i++) {
1788            if (i in arr && arr[i] === el) return i;
1789        }
1790        return -1;
1791    }
1792
1793    // Number helpers
1794
1795    var trunc = Math.trunc || function(n) {
1796        if (n === 0 || !isFinite(n)) return n;
1797        return n < 0 ? ceil(n) : floor(n);
1798    };
1799
1800    function isRealNaN(obj) {
1801        // This is only true of NaN
1802        return obj != null && obj !== obj;
1803    }
1804
1805    function withPrecision(val, precision, fn) {
1806        var multiplier = pow(10, abs(precision || 0));
1807        fn = fn || round;
1808        if (precision < 0) multiplier = 1 / multiplier;
1809        return fn(val * multiplier) / multiplier;
1810    }
1811
1812    function padNumber(num, place, sign, base, replacement) {
1813        var str = abs(num).toString(base || 10);
1814        str = repeatString(replacement || '0', place - str.replace(/\.\d+/, '').length) + str;
1815        if (sign || num < 0) {
1816            str = (num < 0 ? '-' : '+') + str;
1817        }
1818        return str;
1819    }
1820
1821    function getOrdinalSuffix(num) {
1822        if (num >= 11 && num <= 13) {
1823            return 'th';
1824        } else {
1825            switch (num % 10) {
1826                case 1: return 'st';
1827                case 2: return 'nd';
1828                case 3: return 'rd';
1829                default: return 'th';
1830            }
1831        }
1832    }
1833
1834    // Fullwidth number helpers
1835    var fullWidthNumberReg, fullWidthNumberMap, fullWidthNumbers;
1836
1837    function buildFullWidthNumber() {
1838        var fwp = FULL_WIDTH_PERIOD, hwp = HALF_WIDTH_PERIOD, hwc = HALF_WIDTH_COMMA, fwn = '';
1839        fullWidthNumberMap = {};
1840        for (var i = 0, digit; i <= 9; i++) {
1841            digit = chr(i + FULL_WIDTH_ZERO);
1842            fwn += digit;
1843            fullWidthNumberMap[digit] = chr(i + HALF_WIDTH_ZERO);
1844        }
1845        fullWidthNumberMap[hwc] = '';
1846        fullWidthNumberMap[fwp] = hwp;
1847        // Mapping this to itself to capture it easily
1848        // in stringToNumber to detect decimals later.
1849        fullWidthNumberMap[hwp] = hwp;
1850        fullWidthNumberReg = allCharsReg(fwn + fwp + hwc + hwp);
1851        fullWidthNumbers = fwn;
1852    }
1853
1854    // Takes into account full-width characters, commas, and decimals.
1855    function stringToNumber(str, base) {
1856        var sanitized, isDecimal;
1857        sanitized = str.replace(fullWidthNumberReg, function(chr) {
1858            var replacement = getOwn(fullWidthNumberMap, chr);
1859            if (replacement === HALF_WIDTH_PERIOD) {
1860                isDecimal = true;
1861            }
1862            return replacement;
1863        });
1864        return isDecimal ? parseFloat(sanitized) : parseInt(sanitized, base || 10);
1865    }
1866
1867    // Math aliases
1868    var abs = Math.abs,
1869      pow = Math.pow,
1870      min = Math.min,
1871      max = Math.max,
1872      ceil = Math.ceil,
1873      floor = Math.floor,
1874      round = Math.round;
1875
1876
1877    // String helpers
1878
1879    var chr = String.fromCharCode;
1880
1881    function trim(str) {
1882        return str.trim();
1883    }
1884
1885    function repeatString(str, num) {
1886        var result = '';
1887        str = str.toString();
1888        while (num > 0) {
1889            if (num & 1) {
1890                result += str;
1891            }
1892            if (num >>= 1) {
1893                str += str;
1894            }
1895        }
1896        return result;
1897    }
1898
1899    function simpleCapitalize(str) {
1900        return str.charAt(0).toUpperCase() + str.slice(1);
1901    }
1902
1903    function createFormatMatcher(bracketMatcher, percentMatcher, precheck) {
1904
1905        var reg = STRING_FORMAT_REG;
1906        var compileMemoized = memoizeFunction(compile);
1907
1908        function getToken(format, match) {
1909            var get, token, literal, fn;
1910            var bKey = match[2];
1911            var pLit = match[3];
1912            var pKey = match[5];
1913            if (match[4] && percentMatcher) {
1914                token = pKey;
1915                get = percentMatcher;
1916            } else if (bKey) {
1917                token = bKey;
1918                get = bracketMatcher;
1919            } else if (pLit && percentMatcher) {
1920                literal = pLit;
1921            } else {
1922                literal = match[1] || match[0];
1923            }
1924            if (get) {
1925                assertPassesPrecheck(precheck, bKey, pKey);
1926                fn = function(obj, opt) {
1927                    return get(obj, token, opt);
1928                };
1929            }
1930            format.push(fn || getLiteral(literal));
1931        }
1932
1933        function getSubstring(format, str, start, end) {
1934            if (end > start) {
1935                var sub = str.slice(start, end);
1936                assertNoUnmatched(sub, OPEN_BRACE);
1937                assertNoUnmatched(sub, CLOSE_BRACE);
1938                format.push(function() {
1939                    return sub;
1940                });
1941            }
1942        }
1943
1944        function getLiteral(str) {
1945            return function() {
1946                return str;
1947            };
1948        }
1949
1950        function assertPassesPrecheck(precheck, bt, pt) {
1951            if (precheck && !precheck(bt, pt)) {
1952                throw new TypeError('Invalid token ' + (bt || pt) + ' in format string');
1953            }
1954        }
1955
1956        function assertNoUnmatched(str, chr) {
1957            if (str.indexOf(chr) !== -1) {
1958                throw new TypeError('Unmatched ' + chr + ' in format string');
1959            }
1960        }
1961
1962        function compile(str) {
1963            var format = [], lastIndex = 0, match;
1964            reg.lastIndex = 0;
1965            while (match = reg.exec(str)) {
1966                getSubstring(format, str, lastIndex, match.index);
1967                getToken(format, match);
1968                lastIndex = reg.lastIndex;
1969            }
1970            getSubstring(format, str, lastIndex, str.length);
1971            return format;
1972        }
1973
1974        return function(str, obj, opt) {
1975            var format = compileMemoized(str), result = '';
1976            for (var i = 0; i < format.length; i++) {
1977                result += format[i](obj, opt);
1978            }
1979            return result;
1980        };
1981    }
1982
1983    // Inflection helper
1984
1985    var Inflections = {};
1986
1987    function getAcronym(str) {
1988        return Inflections.acronyms && Inflections.acronyms.find(str);
1989    }
1990
1991    function getHumanWord(str) {
1992        return Inflections.human && Inflections.human.find(str);
1993    }
1994
1995    function runHumanRules(str) {
1996        return Inflections.human && Inflections.human.runRules(str) || str;
1997    }
1998
1999    // RegExp helpers
2000
2001    function allCharsReg(src) {
2002        return RegExp('[' + src + ']', 'g');
2003    }
2004
2005    function getRegExpFlags(reg, add) {
2006        var flags = '';
2007        add = add || '';
2008        function checkFlag(prop, flag) {
2009            if (prop || add.indexOf(flag) > -1) {
2010                flags += flag;
2011            }
2012        }
2013        checkFlag(reg.global, 'g');
2014        checkFlag(reg.ignoreCase, 'i');
2015        checkFlag(reg.multiline, 'm');
2016        checkFlag(reg.sticky, 'y');
2017        return flags;
2018    }
2019
2020    function escapeRegExp(str) {
2021        if (!isString(str)) str = String(str);
2022        return str.replace(/([\\\/\'*+?|()\[\]{}.^$-])/g, '\\$1');
2023    }
2024
2025    // Date helpers
2026
2027    var _utc = privatePropertyAccessor('utc');
2028
2029    function callDateGet(d, method) {
2030        return d['get' + (_utc(d) ? 'UTC' : '') + method]();
2031    }
2032
2033    function callDateSet(d, method, value, safe) {
2034        // "Safe" denotes not setting the date if the value is the same as what is
2035        // currently set. In theory this should be a noop, however it will cause
2036        // timezone shifts when in the middle of a DST fallback. This is unavoidable
2037        // as the notation itself is ambiguous (i.e. there are two "1:00ams" on
2038        // November 1st, 2015 in northern hemisphere timezones that follow DST),
2039        // however when advancing or rewinding dates this can throw off calculations
2040        // so avoiding this unintentional shifting on an opt-in basis.
2041        if (safe && value === callDateGet(d, method, value)) {
2042            return;
2043        }
2044        d['set' + (_utc(d) ? 'UTC' : '') + method](value);
2045    }
2046
2047    // Memoization helpers
2048
2049    var INTERNAL_MEMOIZE_LIMIT = 1000;
2050
2051    // Note that attemps to consolidate this with Function#memoize
2052    // ended up clunky as that is also serializing arguments. Separating
2053    // these implementations turned out to be simpler.
2054    function memoizeFunction(fn) {
2055        var memo = {}, counter = 0;
2056
2057        return function(key) {
2058            if (hasOwn(memo, key)) {
2059                return memo[key];
2060            }
2061            if (counter === INTERNAL_MEMOIZE_LIMIT) {
2062                memo = {};
2063                counter = 0;
2064            }
2065            counter++;
2066            return memo[key] = fn(key);
2067        };
2068    }
2069
2070    // ES6 helpers
2071
2072    function setToArray(set) {
2073        var arr = new Array(set.size), i = 0;
2074        set.forEach(function(val) {
2075            arr[i++] = val;
2076        });
2077        return arr;
2078    }
2079
2080    function mapToArray(map) {
2081        var arr = new Array(map.size), i = 0;
2082        map.forEach(function(val, key) {
2083            arr[i++] = [key, val];
2084        });
2085        return arr;
2086    }
2087
2088    buildClassChecks();
2089    buildFullWidthNumber();
2090
2091    /***
2092    * @module ES6
2093    * @description Polyfills that provide basic ES6 compatibility. This module
2094    *              provides the base for Sugar functionality, but is not a full
2095    *              polyfill suite.
2096    *
2097    ***/
2098
2099
2100    /*** @namespace String ***/
2101
2102    function getCoercedStringSubject(obj) {
2103        if (obj == null) {
2104            throw new TypeError('String required.');
2105        }
2106        return String(obj);
2107    }
2108
2109    function getCoercedSearchString(obj) {
2110        if (isRegExp(obj)) {
2111            throw new TypeError();
2112        }
2113        return String(obj);
2114    }
2115
2116    defineInstancePolyfill(sugarString, {
2117
2118        /***
2119        * @method includes(search, [pos] = 0)
2120        * @returns Boolean
2121        * @polyfill ES6
2122        * @short Returns true if `search` is contained within the string.
2123        * @extra Search begins at [pos], which defaults to the beginning of the
2124        *        string. Sugar enhances this method to allow matching a regex.
2125        *
2126        * @example
2127        *
2128        *   'jumpy'.includes('py')      -> true
2129        *   'broken'.includes('ken', 3) -> true
2130        *   'broken'.includes('bro', 3) -> false
2131        *
2132        ***/
2133        'includes': function(searchString) {
2134            // Force compiler to respect argument length.
2135            var argLen = arguments.length, pos = arguments[1];
2136            var str = getCoercedStringSubject(this);
2137            searchString = getCoercedSearchString(searchString);
2138            return str.indexOf(searchString, pos) !== -1;
2139        },
2140
2141        /***
2142        * @method startsWith(search, [pos] = 0)
2143        * @returns Boolean
2144        * @polyfill ES6
2145        * @short Returns true if the string starts with substring `search`.
2146        * @extra Search begins at [pos], which defaults to the entire string length.
2147        *
2148        * @example
2149        *
2150        *   'hello'.startsWith('hell')   -> true
2151        *   'hello'.startsWith('HELL')   -> false
2152        *   'hello'.startsWith('ell', 1) -> true
2153        *
2154        ***/
2155        'startsWith': function(searchString) {
2156            // Force compiler to respect argument length.
2157            var argLen = arguments.length, position = arguments[1];
2158            var str, start, pos, len, searchLength;
2159            str = getCoercedStringSubject(this);
2160            searchString = getCoercedSearchString(searchString);
2161            pos = +position || 0;
2162            len = str.length;
2163            start = min(max(pos, 0), len);
2164            searchLength = searchString.length;
2165            if (searchLength + start > len) {
2166                return false;
2167            }
2168            if (str.substr(start, searchLength) === searchString) {
2169                return true;
2170            }
2171            return false;
2172        },
2173
2174        /***
2175        * @method endsWith(search, [pos] = length)
2176        * @returns Boolean
2177        * @polyfill ES6
2178        * @short Returns true if the string ends with substring `search`.
2179        * @extra Search ends at [pos], which defaults to the entire string length.
2180        *
2181        * @example
2182        *
2183        *   'jumpy'.endsWith('py')    -> true
2184        *   'jumpy'.endsWith('MPY')   -> false
2185        *   'jumpy'.endsWith('mp', 4) -> false
2186        *
2187        ***/
2188        'endsWith': function(searchString) {
2189            // Force compiler to respect argument length.
2190            var argLen = arguments.length, endPosition = arguments[1];
2191            var str, start, end, pos, len, searchLength;
2192            str = getCoercedStringSubject(this);
2193            searchString = getCoercedSearchString(searchString);
2194            len = str.length;
2195            pos = len;
2196            if (isDefined(endPosition)) {
2197                pos = +endPosition || 0;
2198            }
2199            end = min(max(pos, 0), len);
2200            searchLength = searchString.length;
2201            start = end - searchLength;
2202            if (start < 0) {
2203                return false;
2204            }
2205            if (str.substr(start, searchLength) === searchString) {
2206                return true;
2207            }
2208            return false;
2209        },
2210
2211        /***
2212        * @method repeat([num] = 0)
2213        * @returns String
2214        * @polyfill ES6
2215        * @short Returns the string repeated [num] times.
2216        *
2217        * @example
2218        *
2219        *   'jumpy'.repeat(2) -> 'jumpyjumpy'
2220        *   'a'.repeat(5)     -> 'aaaaa'
2221        *   'a'.repeat(0)     -> ''
2222        *
2223        ***/
2224        'repeat': function(num) {
2225            num = coercePositiveInteger(num);
2226            return repeatString(this, num);
2227        }
2228
2229    });
2230
2231
2232    /*** @namespace Number ***/
2233
2234    defineStaticPolyfill(sugarNumber, {
2235
2236        /***
2237        * @method isNaN(value)
2238        * @returns Boolean
2239        * @polyfill ES6
2240        * @static
2241        * @short Returns true only if the number is `NaN`.
2242        * @extra This is differs from the global `isNaN`, which returns true for
2243        *        anything that is not a number.
2244        *
2245        * @example
2246        *
2247        *   Number.isNaN(NaN) -> true
2248        *   Number.isNaN('n') -> false
2249        *
2250        ***/
2251        'isNaN': function(obj) {
2252            return isRealNaN(obj);
2253        }
2254
2255    });
2256
2257
2258    /*** @namespace Array ***/
2259
2260    function getCoercedObject(obj) {
2261        if (obj == null) {
2262            throw new TypeError('Object required.');
2263        }
2264        return coercePrimitiveToObject(obj);
2265    }
2266
2267    defineStaticPolyfill(sugarArray, {
2268
2269        /***
2270        * @method from(a, [map], [context])
2271        * @returns Mixed
2272        * @polyfill ES6
2273        * @static
2274        * @short Creates an array from an array-like object.
2275        * @extra If a function is passed for [map], it will be map each element of
2276        *        the array. [context] is the `this` object if passed.
2277        *
2278        * @callback map
2279        *
2280        *   el   The element of the current iteration.
2281        *   i    The index of the current iteration.
2282        *   arr  A reference to the array.
2283        *
2284        * @example
2285        *
2286        *   Array.from({0:'a',1:'b',length:2}); -> ['a','b']
2287        *
2288        ***/
2289        'from': function(a) {
2290            // Force compiler to respect argument length.
2291            var argLen = arguments.length, map = arguments[1], context = arguments[2];
2292            var len, arr;
2293            if (isDefined(map)) {
2294                assertCallable(map);
2295            }
2296            a = getCoercedObject(a);
2297            len = trunc(max(0, a.length || 0));
2298            if (!isArrayIndex(len)) {
2299                throw new RangeError('Invalid array length');
2300            }
2301            if (isFunction(this)) {
2302                arr = new this(len);
2303                arr.length = len;
2304            } else {
2305                arr = new Array(len);
2306            }
2307            for (var i = 0; i < len; i++) {
2308                setProperty(arr, i, isDefined(map) ? map.call(context, a[i], i) : a[i], true);
2309            }
2310            return arr;
2311        }
2312
2313    });
2314
2315    defineInstancePolyfill(sugarArray, {
2316
2317        'find': function(f) {
2318            // Force compiler to respect argument length.
2319            var argLen = arguments.length, context = arguments[1];
2320            assertCallable(f);
2321            for (var i = 0, len = this.length; i < len; i++) {
2322                if (f.call(context, this[i], i, this)) {
2323                    return this[i];
2324                }
2325            }
2326        },
2327
2328        'findIndex': function(f) {
2329            // Force compiler to respect argument length.
2330            var argLen = arguments.length, context = arguments[1];
2331            assertCallable(f);
2332            for (var i = 0, len = this.length; i < len; i++) {
2333                if (f.call(context, this[i], i, this)) {
2334                    return i;
2335                }
2336            }
2337            return -1;
2338        }
2339
2340    });
2341
2342    /***
2343    * @module ES7
2344    * @description Polyfills that provide basic ES7 compatibility. This module
2345    *              provides the base for Sugar functionality, but is not a full
2346    *              polyfill suite.
2347    *
2348    ***/
2349
2350
2351    /*** @namespace Array ***/
2352
2353    function sameValueZero(a, b) {
2354        if (isRealNaN(a)) {
2355            return isRealNaN(b);
2356        }
2357        return a === b ? a !== 0 || 1 / a === 1 / b : false;
2358    }
2359
2360    defineInstancePolyfill(sugarArray, {
2361
2362        /***
2363        * @method includes(search, [fromIndex] = 0)
2364        * @returns Boolean
2365        * @polyfill ES7
2366        * @short Returns true if `search` is contained within the array.
2367        * @extra Search begins at [fromIndex], which defaults to the beginning of the
2368        *        array.
2369        *
2370        * @example
2371        *
2372        *   [1,2,3].includes(2)    -> true
2373        *   [1,2,3].includes(4)    -> false
2374        *   [1,2,3].includes(2, 3) -> false
2375        *
2376        ***/
2377        'includes': function(search) {
2378            // Force compiler to respect argument length.
2379            var argLen = arguments.length, fromIndex = arguments[1];
2380            var arr = this, len;
2381            if (isString(arr)) return arr.includes(search, fromIndex);
2382            fromIndex = fromIndex ? fromIndex.valueOf() : 0;
2383            len = arr.length;
2384            if (fromIndex < 0) {
2385                fromIndex = max(0, fromIndex + len);
2386            }
2387            for (var i = fromIndex; i < len; i++) {
2388                if (sameValueZero(search, arr[i])) {
2389                    return true;
2390                }
2391            }
2392            return false;
2393        }
2394
2395    });
2396
2397    /***
2398    * @module Date
2399    * @description Date parsing and formatting, relative formats, number shortcuts,
2400    *              and locale support with default English locales.
2401    *
2402    ***/
2403
2404    var DATE_OPTIONS = {
2405        'newDateInternal': defaultNewDate
2406    };
2407
2408    var LOCALE_ARRAY_FIELDS = [
2409    'months', 'weekdays', 'units', 'numerals', 'placeholders',
2410    'articles', 'tokens', 'timeMarkers', 'ampm', 'timeSuffixes',
2411    'parse', 'timeParse', 'timeFrontParse', 'modifiers'
2412  ];
2413
2414    // Regex for stripping Timezone Abbreviations
2415    var TIMEZONE_ABBREVIATION_REG = /(\w{3})[()\s\d]*$/;
2416
2417    // One minute in milliseconds
2418    var MINUTES = 60 * 1000;
2419
2420    // Date unit indexes
2421    var HOURS_INDEX = 3,
2422      DAY_INDEX = 4,
2423      WEEK_INDEX = 5,
2424      MONTH_INDEX = 6,
2425      YEAR_INDEX = 7;
2426
2427    // ISO Defaults
2428    var ISO_FIRST_DAY_OF_WEEK = 1,
2429      ISO_FIRST_DAY_OF_WEEK_YEAR = 4;
2430
2431    var ParsingTokens = {
2432        'yyyy': {
2433            param: 'year',
2434            src: '\\d{4}'
2435        },
2436        'MM': {
2437            param: 'month',
2438            src: '[01]?\\d'
2439        },
2440        'dd': {
2441            param: 'date',
2442            src: '[0123]?\\d'
2443        },
2444        'hh': {
2445            param: 'hour',
2446            src: '[0-2]?\\d'
2447        },
2448        'mm': {
2449            param: 'minute',
2450            src: '[0-5]\\d'
2451        },
2452        'ss': {
2453            param: 'second',
2454            src: '[0-5]\\d(?:[,.]\\d+)?'
2455        },
2456        'yy': {
2457            param: 'year',
2458            src: '\\d{2}'
2459        },
2460        'y': {
2461            param: 'year',
2462            src: '\\d'
2463        },
2464        'yearSign': {
2465            src: '[+-]',
2466            sign: true
2467        },
2468        'tzHour': {
2469            src: '[0-1]\\d'
2470        },
2471        'tzMinute': {
2472            src: '[0-5]\\d'
2473        },
2474        'tzSign': {
2475            src: '[+−-]',
2476            sign: true
2477        },
2478        'ihh': {
2479            param: 'hour',
2480            src: '[0-2]?\\d(?:[,.]\\d+)?'
2481        },
2482        'imm': {
2483            param: 'minute',
2484            src: '[0-5]\\d(?:[,.]\\d+)?'
2485        },
2486        'GMT': {
2487            param: 'utc',
2488            src: 'GMT',
2489            val: 1
2490        },
2491        'Z': {
2492            param: 'utc',
2493            src: 'Z',
2494            val: 1
2495        },
2496        'timestamp': {
2497            src: '\\d+'
2498        }
2499    };
2500
2501    var LocalizedParsingTokens = {
2502        'year': {
2503            base: 'yyyy',
2504            requiresSuffix: true
2505        },
2506        'month': {
2507            base: 'MM',
2508            requiresSuffix: true
2509        },
2510        'date': {
2511            base: 'dd',
2512            requiresSuffix: true
2513        },
2514        'hour': {
2515            base: 'hh',
2516            requiresSuffixOr: ':'
2517        },
2518        'minute': {
2519            base: 'mm'
2520        },
2521        'second': {
2522            base: 'ss'
2523        },
2524        'num': {
2525            src: '\\d+',
2526            requiresNumerals: true
2527        }
2528    };
2529
2530    var CoreParsingFormats = [
2531    {
2532        // 12-1978
2533        // 08-1978 (MDY)
2534        src: '{MM}[-.\\/]{yyyy}'
2535    },
2536    {
2537        // 12/08/1978
2538        // 08/12/1978 (MDY)
2539        time: true,
2540        src: '{dd}[-.\\/]{MM}(?:[-.\\/]{yyyy|yy|y})?',
2541        mdy: '{MM}[-.\\/]{dd}(?:[-.\\/]{yyyy|yy|y})?'
2542    },
2543    {
2544        // 1975-08-25
2545        time: true,
2546        src: '{yyyy}[-.\\/]{MM}(?:[-.\\/]{dd})?'
2547    },
2548    {
2549        // .NET JSON
2550        src: '\\\\/Date\\({timestamp}
2550(?:[+-]\\d{4,4})?\\)\\\\/'
2551    },
2552    {
2553        // ISO-8601
2554        src: '{yearSign?}{yyyy}(?:-?{MM}(?:-?{dd}(?:T{ihh}(?::?{imm}(?::?{ss})?)?)?)?)?{tzOffset?}'
2555    }
2556  ];
2557
2558    var CoreOutputFormats = {
2559        'ISO8601': '{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{SSS}{Z}',
2560        'RFC1123': '{Dow}, {dd} {Mon} {yyyy} {HH}:{mm}:{ss} {ZZ}',
2561        'RFC1036': '{Weekday}, {dd}-{Mon}-{yy} {HH}:{mm}:{ss} {ZZ}'
2562    };
2563
2564    var FormatTokensBase = [
2565    {
2566        ldml: 'Dow',
2567        strf: 'a',
2568        lowerToken: 'dow',
2569        get: function(d, localeCode) {
2570            return localeManager.get(localeCode).getWeekdayName(getWeekday(d), 2);
2571        }
2572    },
2573    {
2574        ldml: 'Weekday',
2575        strf: 'A',
2576        lowerToken: 'weekday',
2577        allowAlternates: true,
2578        get: function(d, localeCode, alternate) {
2579            return localeManager.get(localeCode).getWeekdayName(getWeekday(d), alternate);
2580        }
2581    },
2582    {
2583        ldml: 'Mon',
2584        strf: 'b h',
2585        lowerToken: 'mon',
2586        get: function(d, localeCode) {
2587            return localeManager.get(localeCode).getMonthName(getMonth(d), 2);
2588        }
2589    },
2590    {
2591        ldml: 'Month',
2592        strf: 'B',
2593        lowerToken: 'month',
2594        allowAlternates: true,
2595        get: function(d, localeCode, alternate) {
2596            return localeManager.get(localeCode).getMonthName(getMonth(d), alternate);
2597        }
2598    },
2599    {
2600        strf: 'C',
2601        get: function(d) {
2602            return getYear(d).toString().slice(0, 2);
2603        }
2604    },
2605    {
2606        ldml: 'd date day',
2607        strf: 'd',
2608        strfPadding: 2,
2609        ldmlPaddedToken: 'dd',
2610        ordinalToken: 'do',
2611        get: function(d) {
2612            return getDate(d);
2613        }
2614    },
2615    {
2616        strf: 'e',
2617        get: function(d) {
2618            return padNumber(getDate(d), 2, false, 10, ' ');
2619        }
2620    },
2621    {
2622        ldml: 'H 24hr',
2623        strf: 'H',
2624        strfPadding: 2,
2625        ldmlPaddedToken: 'HH',
2626        get: function(d) {
2627            return getHours(d);
2628        }
2629    },
2630    {
2631        ldml: 'h hours 12hr',
2632        strf: 'I',
2633        strfPadding: 2,
2634        ldmlPaddedToken: 'hh',
2635        get: function(d) {
2636            return getHours(d) % 12 || 12;
2637        }
2638    },
2639    {
2640        ldml: 'D',
2641        strf: 'j',
2642        strfPadding: 3,
2643        ldmlPaddedToken: 'DDD',
2644        get: function(d) {
2645            var s = setUnitAndLowerToEdge(cloneDate(d), MONTH_INDEX);
2646            return getDaysSince(d, s) + 1;
2647        }
2648    },
2649    {
2650        ldml: 'M',
2651        strf: 'm',
2652        strfPadding: 2,
2653        ordinalToken: 'Mo',
2654        ldmlPaddedToken: 'MM',
2655        get: function(d) {
2656            return getMonth(d) + 1;
2657        }
2658    },
2659    {
2660        ldml: 'm minutes',
2661        strf: 'M',
2662        strfPadding: 2,
2663        ldmlPaddedToken: 'mm',
2664        get: function(d) {
2665            return callDateGet(d, 'Minutes');
2666        }
2667    },
2668    {
2669        ldml: 'Q',
2670        get: function(d) {
2671            return ceil((getMonth(d) + 1) / 3);
2672        }
2673    },
2674    {
2675        ldml: 'TT',
2676        strf: 'p',
2677        get: function(d, localeCode) {
2678            return getMeridiemToken(d, localeCode);
2679        }
2680    },
2681    {
2682        ldml: 'tt',
2683        strf: 'P',
2684        get: function(d, localeCode) {
2685            return getMeridiemToken(d, localeCode).toLowerCase();
2686        }
2687    },
2688    {
2689        ldml: 'T',
2690        lowerToken: 't',
2691        get: function(d, localeCode) {
2692            return getMeridiemToken(d, localeCode).charAt(0);
2693        }
2694    },
2695    {
2696        ldml: 's seconds',
2697        strf: 'S',
2698        strfPadding: 2,
2699        ldmlPaddedToken: 'ss',
2700        get: function(d) {
2701            return callDateGet(d, 'Seconds');
2702        }
2703    },
2704    {
2705        ldml: 'S ms',
2706        strfPadding: 3,
2707        ldmlPaddedToken: 'SSS',
2708        get: function(d) {
2709            return callDateGet(d, 'Milliseconds');
2710        }
2711    },
2712    {
2713        ldml: 'e',
2714        strf: 'u',
2715        ordinalToken: 'eo',
2716        get: function(d) {
2717            return getWeekday(d) || 7;
2718        }
2719    },
2720    {
2721        strf: 'U',
2722        strfPadding: 2,
2723        get: function(d) {
2724            // Sunday first, 0-53
2725            return getWeekNumber(d, false, 0);
2726        }
2727    },
2728    {
2729        ldml: 'W',
2730        strf: 'V',
2731        strfPadding: 2,
2732        ordinalToken: 'Wo',
2733        ldmlPaddedToken: 'WW',
2734        get: function(d) {
2735            // Monday first, 1-53 (ISO8601)
2736            return getWeekNumber(d, true);
2737        }
2738    },
2739    {
2740        strf: 'w',
2741        get: function(d) {
2742            return getWeekday(d);
2743        }
2744    },
2745    {
2746        ldml: 'w',
2747        ordinalToken: 'wo',
2748        ldmlPaddedToken: 'ww',
2749        get: function(d, localeCode) {
2750            // Locale dependent, 1-53
2751            var loc = localeManager.get(localeCode),
2752            dow = loc.getFirstDayOfWeek(localeCode),
2753            doy = loc.getFirstDayOfWeekYear(localeCode);
2754            return getWeekNumber(d, true, dow, doy);
2755        }
2756    },
2757    {
2758        strf: 'W',
2759        strfPadding: 2,
2760        get: function(d) {
2761            // Monday first, 0-53
2762            return getWeekNumber(d, false);
2763        }
2764    },
2765    {
2766        ldmlPaddedToken: 'gggg',
2767        ldmlTwoDigitToken: 'gg',
2768        get: function(d, localeCode) {
2769            return getWeekYear(d, localeCode);
2770        }
2771    },
2772    {
2773        strf: 'G',
2774        strfPadding: 4,
2775        strfTwoDigitToken: 'g',
2776        ldmlPaddedToken: 'GGGG',
2777        ldmlTwoDigitToken: 'GG',
2778        get: function(d, localeCode) {
2779            return getWeekYear(d, localeCode, true);
2780        }
2781    },
2782    {
2783        ldml: 'year',
2784        ldmlPaddedToken: 'yyyy',
2785        ldmlTwoDigitToken: 'yy',
2786        strf: 'Y',
2787        strfPadding: 4,
2788        strfTwoDigitToken: 'y',
2789        get: function(d) {
2790            return getYear(d);
2791        }
2792    },
2793    {
2794        ldml: 'ZZ',
2795        strf: 'z',
2796        get: function(d) {
2797            return getUTCOffset(d);
2798        }
2799    },
2800    {
2801        ldml: 'X',
2802        get: function(d) {
2803            return trunc(d.getTime() / 1000);
2804        }
2805    },
2806    {
2807        ldml: 'x',
2808        get: function(d) {
2809            return d.getTime();
2810        }
2811    },
2812    {
2813        ldml: 'Z',
2814        get: function(d) {
2815            return getUTCOffset(d, true);
2816        }
2817    },
2818    {
2819        ldml: 'z',
2820        strf: 'Z',
2821        get: function(d) {
2822            // Note that this is not accurate in all browsing environments!
2823            // https://github.com/moment/moment/issues/162
2824            // It will continue to be supported for Node and usage with the
2825            // understanding that it may be blank.
2826            var match = d.toString().match(TIMEZONE_ABBREVIATION_REG);
2827            return match ? match[1] : '';
2828        }
2829    },
2830    {
2831        strf: 'D',
2832        alias: '%m/%d/%y'
2833    },
2834    {
2835        strf: 'F',
2836        alias: '%Y-%m-%d'
2837    },
2838    {
2839        strf: 'r',
2840        alias: '%I:%M:%S %p'
2841    },
2842    {
2843        strf: 'R',
2844        alias: '%H:%M'
2845    },
2846    {
2847        strf: 'T',
2848        alias: '%H:%M:%S'
2849    },
2850    {
2851        strf: 'x',
2852        alias: '{short}'
2853    },
2854    {
2855        strf: 'X',
2856        alias: '{time}'
2857    },
2858    {
2859        strf: 'c',
2860        alias: '{stamp}'
2861    }
2862  ];
2863
2864    var DateUnits = [
2865    {
2866        name: 'millisecond',
2867        method: 'Milliseconds',
2868        multiplier: 1,
2869        start: 0,
2870        end: 999
2871    },
2872    {
2873        name: 'second',
2874        method: 'Seconds',
2875        multiplier: 1000,
2876        start: 0,
2877        end: 59
2878    },
2879    {
2880        name: 'minute',
2881        method: 'Minutes',
2882        multiplier: 60 * 1000,
2883        start: 0,
2884        end: 59
2885    },
2886    {
2887        name: 'hour',
2888        method: 'Hours',
2889        multiplier: 60 * 60 * 1000,
2890        start: 0,
2891        end: 23
2892    },
2893    {
2894        name: 'day',
2895        alias: 'date',
2896        method: 'Date',
2897        ambiguous: true,
2898        multiplier: 24 * 60 * 60 * 1000,
2899        start: 1,
2900        end: function(d) {
2901            return getDaysInMonth(d);
2902        }
2903    },
2904    {
2905        name: 'week',
2906        method: 'ISOWeek',
2907        ambiguous: true,
2908        multiplier: 7 * 24 * 60 * 60 * 1000
2909    },
2910    {
2911        name: 'month',
2912        method: 'Month',
2913        ambiguous: true,
2914        multiplier: 30.4375 * 24 * 60 * 60 * 1000,
2915        start: 0,
2916        end: 11
2917    },
2918    {
2919        name: 'year',
2920        method: 'FullYear',
2921        ambiguous: true,
2922        multiplier: 365.25 * 24 * 60 * 60 * 1000,
2923        start: 0
2924    }
2925  ];
2926
2927    /***
2928    * @method getOption(name)
2929    * @returns Mixed
2930    * @accessor
2931    * @short Gets an option used interally by Date.
2932    * @example
2933    *
2934    *   Sugar.Date.getOption('newDateInternal');
2935    *
2936    * @param {string} name
2937    *
2938    ***
2939    * @method setOption(name, value)
2940    * @accessor
2941    * @short Sets an option used interally by Date.
2942    * @extra If `value` is `null`, the default value will be restored.
2943    * @options
2944    *
2945    *   newDateInternal   Sugar's internal date constructor. Date methods often
2946    *                     construct a `new Date()` internally as a reference point
2947    *                     (`isToday`, relative formats like `tomorrow`, etc). This
2948    *                     can be overridden if you need it to be something else.
2949    *                     Most commonly, this allows you to return a shifted date
2950    *                     to simulate a specific timezone, as dates in Javascript
2951    *                     are always local.
2952    *
2953    * @example
2954    *
2955    *   Sugar.Date.setOption('newDateInternal', function() {
2956    *     var d = new Date(), offset;
2957    *     offset = (d.getTimezoneOffset() - 600) * 60 * 1000; // Hawaii time!
2958    *     d.setTime(d.getTime() + offset);
2959    *     return d;
2960    *   });
2961    *
2962    * @signature setOption(options)
2963    * @param {DateOptions} options
2964    * @param {string} name
2965    * @param {any} value
2966    * @option {Function} newDateInternal
2967    *
2968    ***/
2969    var _dateOptions = defineOptionsAccessor(sugarDate, DATE_OPTIONS);
2970
2971    function setDateChainableConstructor() {
2972        setChainableConstructor(sugarDate, createDate);
2973    }
2974
2975    // General helpers
2976
2977    function getNewDate() {
2978        return _dateOptions('newDateInternal')();
2979    }
2980
2981    function defaultNewDate() {
2982        return new Date;
2983    }
2984
2985    function cloneDate(d) {
2986        // Rhino environments have a bug where new Date(d) truncates
2987        // milliseconds so need to call getTime() here.
2988        var clone = new Date(d.getTime());
2989        _utc(clone, !!_utc(d));
2990        return clone;
2991    }
2992
2993    function dateIsValid(d) {
2994        return !isNaN(d.getTime());
2995    }
2996
2997    function assertDateIsValid(d) {
2998        if (!dateIsValid(d)) {
2999            throw new TypeError('Date is not valid');
3000        }
3001    }
3002
3003    function getHours(d) {
3004        return callDateGet(d, 'Hours');
3005    }
3006
3007    function getWeekday(d) {
3008        return callDateGet(d, 'Day');
3009    }
3010
3011    function getDate(d) {
3012        return callDateGet(d, 'Date');
3013    }
3014
3015    function getMonth(d) {
3016        return callDateGet(d, 'Month');
3017    }
3018
3019    function getYear(d) {
3020        return callDateGet(d, 'FullYear');
3021    }
3022
3023    function setDate(d, val) {
3024        callDateSet(d, 'Date', val);
3025    }
3026
3027    function setMonth(d, val) {
3028        callDateSet(d, 'Month', val);
3029    }
3030
3031    function setYear(d, val) {
3032        callDateSet(d, 'FullYear', val);
3033    }
3034
3035    function getDaysInMonth(d) {
3036        return 32 - callDateGet(new Date(getYear(d), getMonth(d), 32), 'Date');
3037    }
3038
3039    function setWeekday(d, dow, dir) {
3040        if (!isNumber(dow)) return;
3041        var currentWeekday = getWeekday(d);
3042        if (dir) {
3043            // Allow a "direction" parameter to determine whether a weekday can
3044            // be set beyond the current weekday in either direction.
3045            var ndir = dir > 0 ? 1 : -1;
3046            var offset = dow % 7 - currentWeekday;
3047            if (offset && offset / abs(offset) !== ndir) {
3048                dow += 7 * ndir;
3049            }
3050        }
3051        setDate(d, getDate(d) + dow - currentWeekday);
3052        return d.getTime();
3053    }
3054
3055    // Normal callDateSet method with ability
3056    // to handle ISOWeek setting as well.
3057    function callDateSetWithWeek(d, method, value, safe) {
3058        if (method === 'ISOWeek') {
3059            setISOWeekNumber(d, value);
3060        } else {
3061            callDateSet(d, method, value, safe);
3062        }
3063    }
3064
3065    // UTC helpers
3066
3067    function isUTC(d) {
3068        return !!_utc(d) || tzOffset(d) === 0;
3069    }
3070
3071    function getUTCOffset(d, iso) {
3072        var offset = _utc(d) ? 0 : tzOffset(d), hours, mins, colon;
3073        colon = iso === true ? ':' : '';
3074        if (!offset && iso) return 'Z';
3075        hours = padNumber(trunc(-offset / 60), 2, true);
3076        mins = padNumber(abs(offset % 60), 2);
3077        return hours + colon + mins;
3078    }
3079
3080    function tzOffset(d) {
3081        return d.getTimezoneOffset();
3082    }
3083
3084    // Argument helpers
3085
3086    function collectDateArguments(args, allowDuration) {
3087        var arg1 = args[0], arg2 = args[1];
3088        if (allowDuration && isString(arg1)) {
3089            arg1 = getDateParamsFromString(arg1);
3090        } else if (isNumber(arg1) && isNumber(arg2)) {
3091            arg1 = collectDateParamsFromArguments(args);
3092            arg2 = null;
3093        } else {
3094            if (isObjectType(arg1)) {
3095                arg1 = simpleClone(arg1);
3096            }
3097        }
3098        return [arg1, arg2];
3099    }
3100
3101    function collectDateParamsFromArguments(args) {
3102        var params = {}, index = 0;
3103        walkUnitDown(YEAR_INDEX, function(unit) {
3104            var arg = args[index++];
3105            if (isDefined(arg)) {
3106                params[unit.name] = arg;
3107            }
3108        });
3109        return params;
3110    }
3111
3112    function getDateParamsFromString(str) {
3113        var match, num, params = {};
3114        match = str.match(/^(-?\d*[\d.]\d*)?\s?(\w+?)s?$/i);
3115        if (match) {
3116            if (isUndefined(num)) {
3117                num = +match[1];
3118                if (isNaN(num)) {
3119                    num = 1;
3120                }
3121            }
3122            params[match[2].toLowerCase()] = num;
3123        }
3124        return params;
3125    }
3126
3127    // Iteration helpers
3128
3129    // Years -> Milliseconds
3130    function iterateOverDateUnits(fn, startIndex, endIndex) {
3131        endIndex = endIndex || 0;
3132        if (isUndefined(startIndex)) {
3133            startIndex = YEAR_INDEX;
3134        }
3135        for (var index = startIndex; index >= endIndex; index--) {
3136            if (fn(DateUnits[index], index) === false) {
3137                break;
3138            }
3139        }
3140    }
3141
3142    // Years -> Milliseconds using getLower/Higher methods
3143    function walkUnitDown(unitIndex, fn) {
3144        while (unitIndex >= 0) {
3145            if (fn(DateUnits[unitIndex], unitIndex) === false) {
3146                break;
3147            }
3148            unitIndex = getLowerUnitIndex(unitIndex);
3149        }
3150    }
3151
3152    // Moving lower from specific unit
3153    function getLowerUnitIndex(index) {
3154        if (index === MONTH_INDEX) {
3155            return DAY_INDEX;
3156        } else if (index === WEEK_INDEX) {
3157            return HOURS_INDEX;
3158        }
3159        return index - 1;
3160    }
3161
3162    // Moving higher from specific unit
3163    function getHigherUnitIndex(index) {
3164        return index === DAY_INDEX ? MONTH_INDEX : index + 1;
3165    }
3166
3167    // Years -> Milliseconds checking all date params including "weekday"
3168    function iterateOverDateParams(params, fn, startIndex, endIndex) {
3169
3170        function run(name, unit, i) {
3171            var val = getDateParam(params, name);
3172            if (isDefined(val)) {
3173                fn(name, val, unit, i);
3174            }
3175        }
3176
3177        iterateOverDateUnits(function(unit, i) {
3178            var result = run(unit.name, unit, i);
3179            if (result !== false && i === DAY_INDEX) {
3180                // Check for "weekday", which has a distinct meaning
3181                // in the context of setting a date, but has the same
3182                // meaning as "day" as a unit of time.
3183                result = run('weekday', unit, i);
3184            }
3185            return result;
3186        }, startIndex, endIndex);
3187
3188    }
3189
3190    // Years -> Days
3191    function iterateOverHigherDateParams(params, fn) {
3192        iterateOverDateParams(params, fn, YEAR_INDEX, DAY_INDEX);
3193    }
3194
3195    // Advancing helpers
3196
3197    function advanceDate(d, unit, num, reset) {
3198        var set = {};
3199        set[unit] = num;
3200        return updateDate(d, set, reset, 1);
3201    }
3202
3203    function advanceDateWithArgs(d, args, dir) {
3204        args = collectDateArguments(args, true);
3205        return updateDate(d, args[0], args[1], dir);
3206    }
3207
3208    // Edge helpers
3209
3210    function resetTime(d) {
3211        return setUnitAndLowerToEdge(d, HOURS_INDEX);
3212    }
3213
3214    function resetLowerUnits(d, unitIndex) {
3215        return setUnitAndLowerToEdge(d, getLowerUnitIndex(unitIndex));
3216    }
3217
3218    function moveToBeginningOfWeek(d, firstDayOfWeek) {
3219        setWeekday(d, floor((getWeekday(d) - firstDayOfWeek) / 7) * 7 + firstDayOfWeek);
3220        return d;
3221    }
3222
3223    function moveToEndOfWeek(d, firstDayOfWeek) {
3224        var target = firstDayOfWeek - 1;
3225        setWeekday(d, ceil((getWeekday(d) - target) / 7) * 7 + target);
3226        return d;
3227    }
3228
3229    function moveToBeginningOfUnit(d, unitIndex, localeCode) {
3230        if (unitIndex === WEEK_INDEX) {
3231            moveToBeginningOfWeek(d, localeManager.get(localeCode).getFirstDayOfWeek());
3232        }
3233        return setUnitAndLowerToEdge(d, getLowerUnitIndex(unitIndex));
3234    }
3235
3236    function moveToEndOfUnit(d, unitIndex, localeCode, stopIndex) {
3237        if (unitIndex === WEEK_INDEX) {
3238            moveToEndOfWeek(d, localeManager.get(localeCode).getFirstDayOfWeek());
3239        }
3240        return setUnitAndLowerToEdge(d, getLowerUnitIndex(unitIndex), stopIndex, true);
3241    }
3242
3243    function setUnitAndLowerToEdge(d, startIndex, stopIndex, end) {
3244        walkUnitDown(startIndex, function(unit, i) {
3245            var val = end ? unit.end : unit.start;
3246            if (isFunction(val)) {
3247                val = val(d);
3248            }
3249            callDateSet(d, unit.method, val);
3250            return !isDefined(stopIndex) || i > stopIndex;
3251        });
3252        return d;
3253    }
3254
3255    // Param helpers
3256
3257    function getDateParamKey(params, key) {
3258        return getOwnKey(params, key) ||
3259           getOwnKey(params, key + 's') ||
3260           (key === 'day' && getOwnKey(params, 'date'));
3261    }
3262
3263    function getDateParam(params, key) {
3264        return getOwn(params, getDateParamKey(params, key));
3265    }
3266
3267    function deleteDateParam(params, key) {
3268        delete params[getDateParamKey(params, key)];
3269    }
3270
3271    function getUnitIndexForParamName(name) {
3272        var params = {}, unitIndex;
3273        params[name] = 1;
3274        iterateOverDateParams(params, function(name, val, unit, i) {
3275            unitIndex = i;
3276            return false;
3277        });
3278        return unitIndex;
3279    }
3280
3281    // Time distance helpers
3282
3283    function getDaysSince(d1, d2) {
3284        return getTimeDistanceForUnit(d1, d2, DateUnits[DAY_INDEX]);
3285    }
3286
3287    function getTimeDistanceForUnit(d1, d2, unit) {
3288        var fwd = d2 > d1, num, tmp;
3289        if (!fwd) {
3290            tmp = d2;
3291            d2 = d1;
3292            d1 = tmp;
3293        }
3294        num = d2 - d1;
3295        if (unit.multiplier > 1) {
3296            num = trunc(num / unit.multiplier);
3297        }
3298        // For higher order with potential ambiguity, use the numeric calculation
3299        // as a starting point, then iterate until we pass the target date.
3300        if (unit.ambiguous) {
3301            d1 = cloneDate(d1);
3302            if (num) {
3303                advanceDate(d1, unit.name, num);
3304            }
3305            while (d1 < d2) {
3306                advanceDate(d1, unit.name, 1);
3307                if (d1 > d2) {
3308                    break;
3309                }
3310                num += 1;
3311            }
3312        }
3313        return fwd ? -num : num;
3314    }
3315
3316    // Parsing helpers
3317
3318    function getParsingTokenValue(token, str) {
3319        var val;
3320        if (token.val) {
3321            val = token.val;
3322        } else if (token.sign) {
3323            val = str === '+' ? 1 : -1;
3324        } else if (token.bool) {
3325            val = !!val;
3326        } else {
3327            val = +str.replace(/,/, '.');
3328        }
3329        if (token.param === 'month') {
3330            val -= 1;
3331        }
3332        return val;
3333    }
3334
3335    function getYearFromAbbreviation(str, d, prefer) {
3336        // Following IETF here, adding 1900 or 2000 depending on the last two digits.
3337        // Note that this makes no accordance for what should happen after 2050, but
3338        // intentionally ignoring this for now. https://www.ietf.org/rfc/rfc2822.txt
3339        var val = +str, delta;
3340        val += val < 50 ? 2000 : 1900;
3341        if (prefer) {
3342            delta = val - getYear(d);
3343            if (delta / abs(delta) !== prefer) {
3344                val += prefer * 100;
3345            }
3346        }
3347        return val;
3348    }
3349
3350    // Week number helpers
3351
3352    function setISOWeekNumber(d, num) {
3353        if (isNumber(num)) {
3354            // Intentionally avoiding updateDate here to prevent circular dependencies.
3355            var isoWeek = cloneDate(d), dow = getWeekday(d);
3356            moveToFirstDayOfWeekYear(isoWeek, ISO_FIRST_DAY_OF_WEEK, ISO_FIRST_DAY_OF_WEEK_YEAR);
3357            setDate(isoWeek, getDate(isoWeek) + 7 * (num - 1));
3358            setYear(d, getYear(isoWeek));
3359            setMonth(d, getMonth(isoWeek));
3360            setDate(d, getDate(isoWeek));
3361            setWeekday(d, dow || 7);
3362        }
3363        return d.getTime();
3364    }
3365
3366    function getWeekNumber(d, allowPrevious, firstDayOfWeek, firstDayOfWeekYear) {
3367        var isoWeek, n = 0;
3368        if (isUndefined(firstDayOfWeek)) {
3369            firstDayOfWeek = ISO_FIRST_DAY_OF_WEEK;
3370        }
3371        if (isUndefined(firstDayOfWeekYear)) {
3372            firstDayOfWeekYear = ISO_FIRST_DAY_OF_WEEK_YEAR;
3373        }
3374        // Moving to the end of the week allows for forward year traversal, ie
3375        // Dec 29 2014 is actually week 01 of 2015.
3376        isoWeek = moveToEndOfWeek(cloneDate(d), firstDayOfWeek);
3377        moveToFirstDayOfWeekYear(isoWeek, firstDayOfWeek, firstDayOfWeekYear);
3378        if (allowPrevious && d < isoWeek) {
3379            // If the date is still before the start of the year, then it should be
3380            // the last week of the previous year, ie Jan 1 2016 is actually week 53
3381            // of 2015, so move to the beginning of the week to traverse the year.
3382            isoWeek = moveToBeginningOfWeek(cloneDate(d), firstDayOfWeek);
3383            moveToFirstDayOfWeekYear(isoWeek, firstDayOfWeek, firstDayOfWeekYear);
3384        }
3385        while (isoWeek <= d) {
3386            // Doing a very simple walk to get the week number.
3387            setDate(isoWeek, getDate(isoWeek) + 7);
3388            n++;
3389        }
3390        return n;
3391    }
3392
3393    // Week year helpers
3394
3395    function getWeekYear(d, localeCode, iso) {
3396        var year, month, firstDayOfWeek, firstDayOfWeekYear, week, loc;
3397        year = getYear(d);
3398        month = getMonth(d);
3399        if (month === 0 || month === 11) {
3400            if (!iso) {
3401                loc = localeManager.get(localeCode);
3402                firstDayOfWeek = loc.getFirstDayOfWeek(localeCode);
3403                firstDayOfWeekYear = loc.getFirstDayOfWeekYear(localeCode);
3404            }
3405            week = getWeekNumber(d, false, firstDayOfWeek, firstDayOfWeekYear);
3406            if (month === 0 && week === 0) {
3407                year -= 1;
3408            } else if (month === 11 && week === 1) {
3409                year += 1;
3410            }
3411        }
3412        return year;
3413    }
3414
3415    function moveToFirstDayOfWeekYear(d, firstDayOfWeek, firstDayOfWeekYear) {
3416        setUnitAndLowerToEdge(d, MONTH_INDEX);
3417        setDate(d, firstDayOfWeekYear);
3418        moveToBeginningOfWeek(d, firstDayOfWeek);
3419    }
3420
3421    // Relative helpers
3422
3423    function dateRelative(d, dRelative, arg1, arg2) {
3424        var adu, format, type, localeCode, fn;
3425        assertDateIsValid(d);
3426        if (isFunction(arg1)) {
3427            fn = arg1;
3428        } else {
3429            localeCode = arg1;
3430            fn = arg2;
3431        }
3432        adu = getAdjustedUnitForDate(d, dRelative);
3433        if (fn) {
3434            format = fn.apply(d, adu.concat(localeManager.get(localeCode)));
3435            if (format) {
3436                return dateFormat(d, format, localeCode);
3437            }
3438        }
3439        // Adjust up if time is in ms, as this doesn't
3440        // look very good for a standard relative date.
3441        if (adu[1] === 0) {
3442            adu[1] = 1;
3443            adu[0] = 1;
3444        }
3445        if (dRelative) {
3446            type = 'duration';
3447        } else if (adu[2] > 0) {
3448            type = 'future';
3449        } else {
3450            type = 'past';
3451        }
3452        return localeManager.get(localeCode).getRelativeFormat(adu, type);
3453    }
3454
3455    // Gets an "adjusted date unit" which is a way of representing
3456    // the largest possible meaningful unit. In other words, if passed
3457    // 3600000, this will return an array which represents "1 hour".
3458    function getAdjustedUnit(ms, fn) {
3459        var unitIndex = 0, value = 0;
3460        iterateOverDateUnits(function(unit, i) {
3461            value = abs(fn(unit));
3462            if (value >= 1) {
3463                unitIndex = i;
3464                return false;
3465            }
3466        });
3467        return [value, unitIndex, ms];
3468    }
3469
3470    // Gets the adjusted unit based on simple division by
3471    // date unit multiplier.
3472    function getAdjustedUnitForNumber(ms) {
3473        return getAdjustedUnit(ms, function(unit) {
3474            return trunc(withPrecision(ms / unit.multiplier, 1));
3475        });
3476    }
3477
3478    // Gets the adjusted unit using the unitsFromNow methods,
3479    // which use internal date methods that neatly avoid vaguely
3480    // defined units of time (days in month, leap years, etc).
3481    // Reserving dRelative to allow another date to be relative to.
3482    function getAdjustedUnitForDate(d, dRelative) {
3483        var ms;
3484        if (!dRelative) {
3485            dRelative = getNewDate();
3486            if (d > dRelative) {
3487                // If our date is greater than the one that we got from getNewDate, it
3488                // means that we are finding the unit for a date that is in the future
3489                // relative to now. However, often the incoming date was created in
3490                // the same cycle as our comparison, but our "now" date will have been
3491                // created an instant after it, creating situations where "5 minutes from
3492                // now" becomes "4 minutes from now" in the same tick. To prevent this,
3493                // subtract a buffer of 10ms to compensate.
3494                dRelative = new Date(dRelative.getTime() - 10);
3495            }
3496        }
3497        ms = d - dRelative;
3498        return getAdjustedUnit(ms, function(u) {
3499            return abs(getTimeDistanceForUnit(d, dRelative, u));
3500        });
3501    }
3502
3503    // Foramtting helpers
3504
3505    // Formatting tokens
3506    var ldmlTokens, strfTokens;
3507
3508    function dateFormat(d, format, localeCode) {
3509        assertDateIsValid(d);
3510        format = CoreOutputFormats[format] || format || '{long}';
3511        return dateFormatMatcher(format, d, localeCode);
3512    }
3513
3514    function getMeridiemToken(d, localeCode) {
3515        var hours = getHours(d);
3516        return localeManager.get(localeCode).ampm[trunc(hours / 12)] || '';
3517    }
3518
3519    function buildDateFormatTokens() {
3520
3521        function addFormats(target, tokens, fn) {
3522            if (tokens) {
3523                forEach(spaceSplit(tokens), function(token) {
3524                    target[token] = fn;
3525                });
3526            }
3527        }
3528
3529        function buildLowercase(get) {
3530            return function(d, localeCode) {
3531                return get(d, localeCode).toLowerCase();
3532            };
3533        }
3534
3535        function buildOrdinal(get) {
3536            return function(d, localeCode) {
3537                var n = get(d, localeCode);
3538                return n + localeManager.get(localeCode).getOrdinal(n);
3539            };
3540        }
3541
3542        function buildPadded(get, padding) {
3543            return function(d, localeCode) {
3544                return padNumber(get(d, localeCode), padding);
3545            };
3546        }
3547
3548        function buildTwoDigits(get) {
3549            return function(d, localeCode) {
3550                return get(d, localeCode) % 100;
3551            };
3552        }
3553
3554        function buildAlias(alias) {
3555            return function(d, localeCode) {
3556                return dateFormatMatcher(alias, d, localeCode);
3557            };
3558        }
3559
3560        function buildAlternates(f) {
3561            for (var n = 1; n <= 5; n++) {
3562                buildAlternate(f, n);
3563            }
3564        }
3565
3566        function buildAlternate(f, n) {
3567            var alternate = function(d, localeCode) {
3568                return f.get(d, localeCode, n);
3569            };
3570            addFormats(ldmlTokens, f.ldml + n, alternate);
3571            if (f.lowerToken) {
3572                ldmlTokens[f.lowerToken + n] = buildLowercase(alternate);
3573            }
3574        }
3575
3576        function getIdentityFormat(name) {
3577            return function(d, localeCode) {
3578                var loc = localeManager.get(localeCode);
3579                return dateFormatMatcher(loc[name], d, localeCode);
3580            };
3581        }
3582
3583        ldmlTokens = {};
3584        strfTokens = {};
3585
3586        forEach(FormatTokensBase, function(f) {
3587            var get = f.get, getPadded;
3588            if (f.lowerToken) {
3589                ldmlTokens[f.lowerToken] = buildLowercase(get);
3590            }
3591            if (f.ordinalToken) {
3592                ldmlTokens[f.ordinalToken] = buildOrdinal(get, f);
3593            }
3594            if (f.ldmlPaddedToken) {
3595                ldmlTokens[f.ldmlPaddedToken] = buildPadded(get, f.ldmlPaddedToken.length);
3596            }
3597            if (f.ldmlTwoDigitToken) {
3598                ldmlTokens[f.ldmlTwoDigitToken] = buildPadded(buildTwoDigits(get), 2);
3599            }
3600            if (f.strfTwoDigitToken) {
3601                strfTokens[f.strfTwoDigitToken] = buildPadded(buildTwoDigits(get), 2);
3602            }
3603            if (f.strfPadding) {
3604                getPadded = buildPadded(get, f.strfPadding);
3605            }
3606            if (f.alias) {
3607                get = buildAlias(f.alias);
3608            }
3609            if (f.allowAlternates) {
3610                buildAlternates(f);
3611            }
3612            addFormats(ldmlTokens, f.ldml, get);
3613            addFormats(strfTokens, f.strf, getPadded || get);
3614        });
3615
3616        forEachProperty(CoreOutputFormats, function(src, name) {
3617            addFormats(ldmlTokens, name, buildAlias(src));
3618        });
3619
3620        defineInstanceSimilar(sugarDate, 'short medium long full', function(methods, name) {
3621            var fn = getIdentityFormat(name);
3622            addFormats(ldmlTokens, name, fn);
3623            methods[name] = fn;
3624        });
3625
3626        addFormats(ldmlTokens, 'time', getIdentityFormat('time'));
3627        addFormats(ldmlTokens, 'stamp', getIdentityFormat('stamp'));
3628    }
3629
3630    // Format matcher
3631
3632    var dateFormatMatcher;
3633
3634    function buildDateFormatMatcher() {
3635
3636        function getLdml(d, token, localeCode) {
3637            return getOwn(ldmlTokens, token)(d, localeCode);
3638        }
3639
3640        function getStrf(d, token, localeCode) {
3641            return getOwn(strfTokens, token)(d, localeCode);
3642        }
3643
3644        function checkDateToken(ldml, strf) {
3645            return hasOwn(ldmlTokens, ldml) || hasOwn(strfTokens, strf);
3646        }
3647
3648        // Format matcher for LDML or STRF tokens.
3649        dateFormatMatcher = createFormatMatcher(getLdml, getStrf, checkDateToken);
3650    }
3651
3652    // Comparison helpers
3653
3654    function fullCompareDate(date, d, margin) {
3655        var tmp;
3656        if (!dateIsValid(date)) return;
3657        if (isString(d)) {
3658            d = trim(d).toLowerCase();
3659            switch (true) {
3660                case d === 'future': return date.getTime() > getNewDate().getTime();
3661                case d === 'past': return date.getTime() < getNewDate().getTime();
3662                case d === 'today': return compareDay(date);
3663                case d === 'tomorrow': return compareDay(date, 1);
3664                case d === 'yesterday': return compareDay(date, -1);
3665                case d === 'weekday': return getWeekday(date) > 0 && getWeekday(date) < 6;
3666                case d === 'weekend': return getWeekday(date) === 0 || getWeekday(date) === 6;
3667
3668                case (isDefined(tmp = English.weekdayMap[d])):
3669                    return getWeekday(date) === tmp;
3670                case (isDefined(tmp = English.monthMap[d])):
3671                    return getMonth(date) === tmp;
3672            }
3673        }
3674        return compareDate(date, d, margin);
3675    }
3676
3677    function compareDate(date, d, margin, localeCode, options) {
3678        var loMargin = 0, hiMargin = 0, timezoneShift, compareEdges, override, min, max, p, t;
3679
3680        function getTimezoneShift() {
3681            // If there is any specificity in the date then we're implicitly not
3682            // checking absolute time, so ignore timezone shifts.
3683            if (p.set && p.set.specificity) {
3684                return 0;
3685            }
3686            return (tzOffset(p.date) - tzOffset(date)) * MINUTES;
3687        }
3688
3689        function addSpecificUnit() {
3690            var unit = DateUnits[p.set.specificity];
3691            return advanceDate(cloneDate(p.date), unit.name, 1).getTime() - 1;
3692        }
3693
3694        if (_utc(date)) {
3695            options = options || {};
3696            options.fromUTC = true;
3697            options.setUTC = true;
3698        }
3699
3700        p = getExtendedDate(null, d, options, true);
3701
3702        if (margin > 0) {
3703            loMargin = hiMargin = margin;
3704            override = true;
3705        }
3706        if (!dateIsValid(p.date)) return false;
3707        if (p.set && p.set.specificity) {
3708            if (isDefined(p.set.edge) || isDefined(p.set.shift)) {
3709                compareEdges = true;
3710                moveToBeginningOfUnit(p.date, p.set.specificity, localeCode);
3711            }
3712            if (compareEdges || p.set.specificity === MONTH_INDEX) {
3713                max = moveToEndOfUnit(cloneDate(p.date), p.set.specificity, localeCode).getTime();
3714            } else {
3715                max = addSpecificUnit();
3716            }
3717            if (!override && isDefined(p.set.sign) && p.set.specificity) {
3718                // If the time is relative, there can occasionally be an disparity between
3719                // the relative date and "now", which it is being compared to, so set an
3720                // extra margin to account for this.
3721                loMargin = 50;
3722                hiMargin = -50;
3723            }
3724        }
3725        t = date.getTime();
3726        min = p.date.getTime();
3727        max = max || min;
3728        timezoneShift = getTimezoneShift();
3729        if (timezoneShift) {
3730            min -= timezoneShift;
3731            max -= timezoneShift;
3732        }
3733        return t >= (min - loMargin) && t <= (max + hiMargin);
3734    }
3735
3736    function compareDay(d, shift) {
3737        var comp = getNewDate();
3738        if (shift) {
3739            setDate(comp, getDate(comp) + shift);
3740        }
3741        return getYear(d) === getYear(comp) &&
3742           getMonth(d) === getMonth(comp) &&
3743           getDate(d) === getDate(comp);
3744    }
3745
3746    // Create helpers
3747
3748    function createDate(d, options, forceClone) {
3749        return getExtendedDate(null, d, options, forceClone).date;
3750    }
3751
3752    function createDateWithContext(contextDate, d, options, forceClone) {
3753        return getExtendedDate(contextDate, d, options, forceClone).date;
3754    }
3755
3756    function getExtendedDate(contextDate, d, opt, forceClone) {
3757
3758        var date, set, loc, options, afterCallbacks, relative, weekdayDir;
3759
3760        afterCallbacks = [];
3761        options = getDateOptions(opt);
3762
3763        function getDateOptions(opt) {
3764            var options = isString(opt) ? { locale: opt} : opt || {};
3765            options.prefer = +!!getOwn(options, 'future') - +!!getOwn(options, 'past');
3766            return options;
3767        }
3768
3769        function getFormatParams(match, dif) {
3770            var set = getOwn(options, 'params') || {};
3771            forEach(dif.to, function(field, i) {
3772                var str = match[i + 1], token, val;
3773                if (!str) return;
3774                if (field === 'yy' || field === 'y') {
3775                    field = 'year';
3776                    val = getYearFromAbbreviation(str, date, getOwn(options, 'prefer'));
3777                } else if (token = getOwn(ParsingTokens, field)) {
3778                    field = token.param || field;
3779                    val = getParsingTokenValue(token, str);
3780                } else {
3781                    val = loc.getTokenValue(field, str);
3782                }
3783                set[field] = val;
3784            });
3785            return set;
3786        }
3787
3788        // Clone date will set the utc flag, but it will
3789        // be overriden later, so set option flags instead.
3790        function cloneDateByFlag(d, clone) {
3791            if (_utc(d) && !isDefined(getOwn(options, 'fromUTC'))) {
3792                options.fromUTC = true;
3793            }
3794            if (_utc(d) && !isDefined(getOwn(options, 'setUTC'))) {
3795                options.setUTC = true;
3796            }
3797            if (clone) {
3798                d = new Date(d.getTime());
3799            }
3800            return d;
3801        }
3802
3803        function afterDateSet(fn) {
3804            afterCallbacks.push(fn);
3805        }
3806
3807        function fireCallbacks() {
3808            forEach(afterCallbacks, function(fn) {
3809                fn.call();
3810            });
3811        }
3812
3813        function parseStringDate(str) {
3814
3815            str = str.toLowerCase();
3816
3817            // The act of getting the locale will initialize
3818            // if it is missing and add the required formats.
3819            loc = localeManager.get(getOwn(options, 'locale'));
3820
3821            for (var i = 0, dif, match; dif = loc.compiledFormats[i]; i++) {
3822                match = str.match(dif.reg);
3823                if (match) {
3824
3825                    // Note that caching the format will modify the compiledFormats array
3826                    // which is not a good idea to do inside its for loop, however we
3827                    // know at this point that we have a matched format and that we will
3828                    // break out below, so simpler to do it here.
3829                    loc.cacheFormat(dif, i);
3830
3831                    set = getFormatParams(match, dif);
3832
3833                    if (isDefined(set.timestamp)) {
3834                        str = set.timestamp;
3835                        set = null;
3836                        break;
3837                    }
3838
3839                    if (isDefined(set.ampm)) {
3840                        handleAmpm(set.ampm);
3841                    }
3842
3843                    if (set.utc || isDefined(set.tzHour)) {
3844                        handleTimezoneOffset(set.tzHour, set.tzMinute, set.tzSign);
3845                    }
3846
3847                    if (isDefined(set.shift) && isUndefined(set.unit)) {
3848                        // "next january", "next monday", etc
3849                        handleUnitlessShift();
3850                    }
3851
3852                    if (isDefined(set.num) && isUndefined(set.unit)) {
3853                        // "the second of January", etc
3854                        handleUnitlessNum(set.num);
3855                    }
3856
3857                    if (set.midday) {
3858                        // "noon" and "midnight"
3859                        handleMidday(set.midday);
3860                    }
3861
3862                    if (isDefined(set.day)) {
3863                        // Relative day localizations such as "today" and "tomorrow".
3864                        handleRelativeDay(set.day);
3865                    }
3866
3867                    if (isDefined(set.unit)) {
3868                        // "3 days ago", etc
3869                        handleRelativeUnit(set.unit);
3870                    }
3871
3872                    if (set.edge) {
3873                        // "the end of January", etc
3874                        handleEdge(set.edge, set);
3875                    }
3876
3877                    if (set.yearSign) {
3878                        set.year *= set.yearSign;
3879                    }
3880
3881                    break;
3882                }
3883            }
3884
3885            if (!set) {
3886                // Fall back to native parsing
3887                date = new Date(str);
3888                if (getOwn(options, 'fromUTC')) {
3889                    // Falling back to system date here which cannot be parsed as UTC,
3890                    // so if we're forcing UTC then simply add the offset.
3891                    date.setTime(date.getTime() + (tzOffset(date) * MINUTES));
3892                }
3893            } else if (relative) {
3894                updateDate(date, set, false, 1);
3895            } else {
3896                if (_utc(date)) {
3897                    // UTC times can traverse into other days or even months,
3898                    // so preemtively reset the time here to prevent this.
3899                    resetTime(date);
3900                }
3901                updateDate(date, set, true, 0, getOwn(options, 'prefer'), weekdayDir);
3902            }
3903            fireCallbacks();
3904            return date;
3905        }
3906
3907        function handleAmpm(ampm) {
3908            if (ampm === 1 && set.hour < 12) {
3909                // If the time is 1pm-11pm advance the time by 12 hours.
3910                set.hour += 12;
3911            } else if (ampm === 0 && set.hour === 12) {
3912                // If it is 12:00am then set the hour to 0.
3913                set.hour = 0;
3914            }
3915        }
3916
3917        function handleTimezoneOffset(tzHour, tzMinute, tzSign) {
3918            // Adjust for timezone offset
3919            _utc(date, true);
3920            var offset = (tzSign || 1) * ((tzHour || 0) * 60 + (tzMinute || 0));
3921            if (offset) {
3922                set.minute = (set.minute || 0) - offset;
3923            }
3924        }
3925
3926        function handleUnitlessShift() {
3927            if (isDefined(set.month)) {
3928                // "next January"
3929                set.unit = YEAR_INDEX;
3930            } else if (isDefined(set.weekday)) {
3931                // "next Monday"
3932                set.unit = WEEK_INDEX;
3933            }
3934        }
3935
3936        function handleUnitlessNum(num) {
3937            if (isDefined(set.weekday)) {
3938                // "The second Tuesday of March"
3939                setOrdinalWeekday(num);
3940            } else if (isDefined(set.month)) {
3941                // "The second of March"
3942                set.date = set.num;
3943            }
3944        }
3945
3946        function handleMidday(hour) {
3947            set.hour = hour % 24;
3948            if (hour > 23) {
3949                // If the date has hours past 24, we need to prevent it from traversing
3950                // into a new day as that would make it being part of a new week in
3951                // ambiguous dates such as "Monday".
3952                afterDateSet(function() {
3953                    advanceDate(date, 'date', trunc(hour / 24));
3954                });
3955            }
3956        }
3957
3958        function handleRelativeDay() {
3959            resetTime(date);
3960            if (isUndefined(set.unit)) {
3961                set.unit = DAY_INDEX;
3962                set.num = set.day;
3963                delete set.day;
3964            }
3965        }
3966
3967        function handleRelativeUnit(unitIndex) {
3968            var num = isDefined(set.num) ? set.num : 1;
3969
3970            // If a weekday is defined, there are 3 possible formats being applied:
3971            //
3972            // 1. "the day after monday": unit is days
3973            // 2. "next monday": short for "next week monday", unit is weeks
3974            // 3. "the 2nd monday of next month": unit is months
3975            //
3976            // In the first case, we need to set the weekday up front, as the day is
3977            // relative to it. The second case also needs to be handled up front for
3978            // formats like "next monday at midnight" which will have its weekday reset
3979            // if not set up front. The last case will set up the params necessary to
3980            // shift the weekday and allow separateAbsoluteUnits below to handle setting
3981            // it after the date has been shifted.
3982            if (isDefined(set.weekday)) {
3983                if (unitIndex === MONTH_INDEX) {
3984                    setOrdinalWeekday(num);
3985                    num = 1;
3986                } else {
3987                    updateDate(date, { weekday: set.weekday }, true);
3988                    delete set.weekday;
3989                }
3990            }
3991
3992            if (set.half) {
3993                // Allow localized "half" as a standalone colloquialism. Purposely avoiding
3994                // the locale number system to reduce complexity. The units "month" and
3995                // "week" are purposely excluded in the English date formats below, as
3996                // "half a week" and "half a month" are meaningless as exact dates.
3997                num *= set.half;
3998            }
3999
4000            if (isDefined(set.shift)) {
4001                // Shift and unit, ie "next month", "last week", etc.
4002                num *= set.shift;
4003            } else if (set.sign) {
4004                // Unit and sign, ie "months ago", "weeks from now", etc.
4005                num *= set.sign;
4006            }
4007
4008            if (isDefined(set.day)) {
4009                // "the day after tomorrow"
4010                num += set.day;
4011                delete set.day;
4012            }
4013
4014            // Formats like "the 15th of last month" or "6:30pm of next week"
4015            // contain absolute units in addition to relative ones, so separate
4016            // them here, remove them from the params, and set up a callback to
4017            // set them after the relative ones have been set.
4018            separateAbsoluteUnits(unitIndex);
4019
4020            // Finally shift the unit.
4021            set[English.units[unitIndex]] = num;
4022            relative = true;
4023        }
4024
4025        function handleEdge(edge, params) {
4026            var edgeIndex = params.unit, weekdayOfMonth;
4027            if (!edgeIndex) {
4028                // If we have "the end of January", then we need to find the unit index.
4029                iterateOverHigherDateParams(params, function(unitName, val, unit, i) {
4030                    if (unitName === 'weekday' && isDefined(params.month)) {
4031                        // If both a month and weekday exist, then we have a format like
4032                        // "the last tuesday in November, 2012", where the "last" is still
4033                        // relative to the end of the month, so prevent the unit "weekday"
4034                        // from taking over.
4035                        return;
4036                    }
4037                    edgeIndex = i;
4038                });
4039            }
4040            if (edgeIndex === MONTH_INDEX && isDefined(params.weekday)) {
4041                // If a weekday in a month exists (as described above),
4042                // then set it up to be set after the date has been shifted.
4043                weekdayOfMonth = params.weekday;
4044                delete params.weekday;
4045            }
4046            afterDateSet(function() {
4047                var stopIndex;
4048                // "edge" values that are at the very edge are "2" so the beginning of the
4049                // year is -2 and the end of the year is 2. Conversely, the "last day" is
4050                // actually 00:00am so it is 1. -1 is reserved but unused for now.
4051                if (edge < 0) {
4052                    moveToBeginningOfUnit(date, edgeIndex, getOwn(options, 'locale'));
4053                } else if (edge > 0) {
4054                    if (edge === 1) {
4055                        stopIndex = DAY_INDEX;
4056                        moveToBeginningOfUnit(date, DAY_INDEX);
4057                    }
4058                    moveToEndOfUnit(date, edgeIndex, getOwn(options, 'locale'), stopIndex);
4059                }
4060                if (isDefined(weekdayOfMonth)) {
4061                    setWeekday(date, weekdayOfMonth, -edge);
4062                    resetTime(date);
4063                }
4064            });
4065            if (edgeIndex === MONTH_INDEX) {
4066                params.specificity = DAY_INDEX;
4067            } else {
4068                params.specificity = edgeIndex - 1;
4069            }
4070        }
4071
4072        function setOrdinalWeekday(num) {
4073            // If we have "the 2nd Tuesday of June", then pass the "weekdayDir"
4074            // flag along to updateDate so that the date does not accidentally traverse
4075            // into the previous month. This needs to be independent of the "prefer"
4076            // flag because we are only ensuring that the weekday is in the future, not
4077            // the entire date.
4078            set.weekday = 7 * (num - 1) + set.weekday;
4079            set.date = 1;
4080            weekdayDir = 1;
4081        }
4082
4083        function separateAbsoluteUnits(unitIndex) {
4084            var params;
4085
4086            iterateOverDateParams(set, function(name, val, unit, i) {
4087                // If there is a time unit set that is more specific than
4088                // the matched unit we have a string like "5:30am in 2 minutes",
4089                // which is meaningless, so invalidate the date...
4090                if (i >= unitIndex) {
4091                    date.setTime(NaN);
4092                    return false;
4093                } else if (i < unitIndex) {
4094                    // ...otherwise set the params to set the absolute date
4095                    // as a callback after the relative date has been set.
4096                    params = params || {};
4097                    params[name] = val;
4098                    deleteDateParam(set, name);
4099                }
4100            });
4101            if (params) {
4102                afterDateSet(function() {
4103                    updateDate(date, params, true, false, getOwn(options, 'prefer'), weekdayDir);
4104                });
4105                if (set.edge) {
4106                    // "the end of March of next year"
4107                    handleEdge(set.edge, params);
4108                    delete set.edge;
4109                }
4110            }
4111        }
4112
4113        if (contextDate && d) {
4114            // If a context date is passed ("get" and "unitsFromNow"),
4115            // then use it as the starting point.
4116            date = cloneDateByFlag(contextDate, true);
4117        } else {
4118            date = getNewDate();
4119        }
4120
4121        _utc(date, getOwn(options, 'fromUTC'));
4122
4123        if (isString(d)) {
4124            date = parseStringDate(d);
4125        } else if (isDate(d)) {
4126            date = cloneDateByFlag(d, hasOwn(options, 'clone') || forceClone);
4127        } else if (isObjectType(d)) {
4128            set = simpleClone(d);
4129            updateDate(date, set, true);
4130        } else if (isNumber(d) || d === null) {
4131            date.setTime(d);
4132        }
4133        // A date created by parsing a string presumes that the format *itself* is
4134        // UTC, but not that the date, once created, should be manipulated as such. In
4135        // other words, if you are creating a date object from a server time
4136        // "2012-11-15T12:00:00Z", in the majority of cases you are using it to create
4137        // a date that will, after creation, be manipulated as local, so reset the utc
4138        // flag here unless "setUTC" is also set.
4139        _utc(date, !!getOwn(options, 'setUTC'));
4140        return {
4141            set: set,
4142            date: date
4143        };
4144    }
4145
4146    function updateDate(d, params, reset, advance, prefer, weekdayDir) {
4147        var upperUnitIndex;
4148
4149        function setUpperUnit(unitName, unitIndex) {
4150            if (prefer && !upperUnitIndex) {
4151                if (unitName === 'weekday') {
4152                    upperUnitIndex = WEEK_INDEX;
4153                } else {
4154                    upperUnitIndex = getHigherUnitIndex(unitIndex);
4155                }
4156            }
4157        }
4158
4159        function setSpecificity(unitIndex) {
4160            // Other functions may preemptively set the specificity before arriving
4161            // here so concede to them if they have already set more specific units.
4162            if (unitIndex > params.specificity) {
4163                return;
4164            }
4165            params.specificity = unitIndex;
4166        }
4167
4168        function canDisambiguate() {
4169            if (!upperUnitIndex || upperUnitIndex > YEAR_INDEX) {
4170                return;
4171            }
4172            switch (prefer) {
4173                case -1: return d > getNewDate();
4174                case 1: return d < getNewDate();
4175            }
4176        }
4177
4178        function disambiguateHigherUnit() {
4179            var unit = DateUnits[upperUnitIndex];
4180            advance = prefer;
4181            setUnit(unit.name, 1, unit, upperUnitIndex);
4182        }
4183
4184        function handleFraction(unit, unitIndex, fraction) {
4185            if (unitIndex) {
4186                var lowerUnit = DateUnits[getLowerUnitIndex(unitIndex)];
4187                var val = round(unit.multiplier / lowerUnit.multiplier * fraction);
4188                params[lowerUnit.name] = val;
4189            }
4190        }
4191
4192        function monthHasShifted(d, targetMonth) {
4193            if (targetMonth < 0) {
4194                targetMonth = targetMonth % 12 + 12;
4195            }
4196            return targetMonth % 12 !== getMonth(d);
4197        }
4198
4199        function setUnit(unitName, value, unit, unitIndex) {
4200            var method = unit.method, checkMonth, fraction;
4201
4202            setUpperUnit(unitName, unitIndex);
4203            setSpecificity(unitIndex);
4204
4205            fraction = value % 1;
4206            if (fraction) {
4207                handleFraction(unit, unitIndex, fraction);
4208                value = trunc(value);
4209            }
4210
4211            if (unitName === 'weekday') {
4212                if (!advance) {
4213                    // Weekdays are always considered absolute units so simply set them
4214                    // here even if it is an "advance" operation. This is to help avoid
4215                    // ambiguous meanings in "advance" as well as to neatly allow formats
4216                    // like "Wednesday of next week" without more complex logic.
4217                    setWeekday(d, value, weekdayDir);
4218                }
4219                return;
4220            }
4221            checkMonth = unitIndex === MONTH_INDEX && getDate(d) > 28;
4222
4223            // If we are advancing or rewinding, then we need we need to set the
4224            // absolute time if the unit is "hours" or less. This is due to the fact
4225            // that setting by method is ambiguous during DST shifts. For example,
4226            // 1:00am on November 1st 2015 occurs twice in North American timezones
4227            // with DST, the second time being after the clocks are rolled back at
4228            // 2:00am. When springing forward this is automatically handled as there
4229            // is no 2:00am so the date automatically jumps to 3:00am. However, when
4230            // rolling back, setHours(2) will always choose the first "2am" even if
4231            // the date is currently set to the second, causing unintended jumps.
4232            // This ambiguity is unavoidable when setting dates as the notation is
4233            // ambiguous. However when advancing, we clearly want the resulting date
4234            // to be an acutal hour ahead, which can only be accomplished by setting
4235            // the absolute time. Conversely, any unit higher than "hours" MUST use
4236            // the internal set methods, as they are ambiguous as absolute units of
4237            // time. Years may be 365 or 366 days depending on leap years, months are
4238            // all over the place, and even days may be 23-25 hours depending on DST
4239            // shifts. Finally, note that the kind of jumping described above will
4240            // occur when calling ANY "set" method on the date and will occur even if
4241            // the value being set is identical to the one currently set (i.e.
4242            // setHours(2) on a date at 2am may not be a noop). This is precarious,
4243            // so avoiding this situation in callDateSet by checking up front that
4244            // the value is not the same before setting.
4245            if (advance && !unit.ambiguous) {
4246                d.setTime(d.getTime() + (value * advance * unit.multiplier));
4247                return;
4248            } else if (advance) {
4249                if (unitIndex === WEEK_INDEX) {
4250                    value *= 7;
4251                    method = DateUnits[DAY_INDEX].method;
4252                }
4253                value = (value * advance) + callDateGet(d, method);
4254            }
4255            callDateSetWithWeek(d, method, value, advance);
4256            if (checkMonth && monthHasShifted(d, value)) {
4257                // As we are setting the units in reverse order, there is a chance that
4258                // our date may accidentally traverse into a new month, such as setting
4259                // { month: 1, date 15 } on January 31st. Check for this here and reset
4260                // the date to the last day of the previous month if this has happened.
4261                setDate(d, 0);
4262            }
4263        }
4264
4265        if (isNumber(params) && advance) {
4266            // If param is a number and advancing, the number is in milliseconds.
4267            params = { millisecond: params };
4268        } else if (isNumber(params)) {
4269            // Otherwise just set the timestamp and return.
4270            d.setTime(params);
4271            return d;
4272        }
4273
4274        iterateOverDateParams(params, setUnit);
4275
4276        if (reset && params.specificity) {
4277            resetLowerUnits(d, params.specificity);
4278        }
4279
4280        // If past or future is preferred, then the process of "disambiguation" will
4281        // ensure that an ambiguous time/date ("4pm", "thursday", "June", etc.) will
4282        // be in the past or future. Weeks are only considered ambiguous if there is
4283        // a weekday, i.e. "thursday" is an ambiguous week, but "the 4th" is an
4284        // ambiguous month.
4285        if (canDisambiguate()) {
4286            disambiguateHigherUnit();
4287        }
4288        return d;
4289    }
4290
4291    // Locales
4292
4293    // Locale helpers
4294    var English, localeManager;
4295
4296    function getEnglishVariant(v) {
4297        return simpleMerge(simpleClone(EnglishLocaleBaseDefinition), v);
4298    }
4299
4300    function arrayToRegAlternates(arr) {
4301        var joined = arr.join('');
4302        if (!arr || !arr.length) {
4303            return '';
4304        }
4305        if (joined.length === arr.length) {
4306            return '[' + joined + ']';
4307        }
4308        // map handles sparse arrays so no need to compact the array here.
4309        return map(arr, escapeRegExp).join('|');
4310    }
4311
4312    function getRegNonCapturing(src, opt) {
4313        if (src.length > 1) {
4314            src = '(?:' + src + ')';
4315        }
4316        if (opt) {
4317            src += '?';
4318        }
4319        return src;
4320    }
4321
4322    function getParsingTokenWithSuffix(field, src, suffix) {
4323        var token = LocalizedParsingTokens[field];
4324        if (token.requiresSuffix) {
4325            src = getRegNonCapturing(src + getRegNonCapturing(suffix));
4326        } else if (token.requiresSuffixOr) {
4327            src += getRegNonCapturing(token.requiresSuffixOr + '|' + suffix);
4328        } else {
4329            src += getRegNonCapturing(suffix, true);
4330        }
4331        return src;
4332    }
4333
4334    function getArrayWithOffset(arr, n, alternate, offset) {
4335        var val;
4336        if (alternate > 1) {
4337            val = arr[n + (alternate - 1) * offset];
4338        }
4339        return val || arr[n];
4340    }
4341
4342    function buildLocales() {
4343
4344        function LocaleManager(loc) {
4345            this.locales = {};
4346            this.add(loc);
4347        }
4348
4349        LocaleManager.prototype = {
4350
4351            get: function(code, fallback) {
4352                var loc = this.locales[code];
4353                if (!loc && LazyLoadedLocales[code]) {
4354                    loc = this.add(code, LazyLoadedLocales[code]);
4355                } else if (!loc && code) {
4356                    loc = this.locales[code.slice(0, 2)];
4357                }
4358                return loc || fallback === false ? loc : this.current;
4359            },
4360
4361            getAll: function() {
4362                return this.locales;
4363            },
4364
4365            set: function(code) {
4366                var loc = this.get(code, false);
4367                if (!loc) {
4368                    throw new TypeError('Invalid Locale: ' + code);
4369                }
4370                return this.current = loc;
4371            },
4372
4373            add: function(code, def) {
4374                if (!def) {
4375                    def = code;
4376                    code = def.code;
4377                } else {
4378                    def.code = code;
4379                }
4380                var loc = def.compiledFormats ? def : getNewLocale(def);
4381                this.locales[code] = loc;
4382                if (!this.current) {
4383                    this.current = loc;
4384                }
4385                return loc;
4386            },
4387
4388            remove: function(code) {
4389                if (this.current.code === code) {
4390                    this.current = this.get('en');
4391                }
4392                return delete this.locales[code];
4393            }
4394
4395        };
4396
4397        // Sorry about this guys...
4398        English = getNewLocale(AmericanEnglishDefinition);
4399        localeManager = new LocaleManager(English);
4400    }
4401
4402    function getNewLocale(def) {
4403
4404        function Locale(def) {
4405            this.init(def);
4406        }
4407
4408        Locale.prototype = {
4409
4410            getMonthName: function(n, alternate) {
4411                if (this.monthSuffix) {
4412                    return (n + 1) + this.monthSuffix;
4413                }
4414                return getArrayWithOffset(this.months, n, alternate, 12);
4415            },
4416
4417            getWeekdayName: function(n, alternate) {
4418                return getArrayWithOffset(this.weekdays, n, alternate, 7);
4419            },
4420
4421            getTokenValue: function(field, str) {
4422                var map = this[field + 'Map'], val;
4423                if (map) {
4424                    val = map[str];
4425                }
4426                if (isUndefined(val)) {
4427                    val = this.getNumber(str);
4428                    if (field === 'month') {
4429                        // Months are the only numeric date field
4430                        // whose value is not the same as its number.
4431                        val -= 1;
4432                    }
4433                }
4434                return val;
4435            },
4436
4437            getNumber: function(str) {
4438                var num = this.numeralMap[str];
4439                if (isDefined(num)) {
4440                    return num;
4441                }
4442                // The unary plus operator here show better performance and handles
4443                // every format that parseFloat does with the exception of trailing
4444                // characters, which are guaranteed not to be in our string at this point.
4445                num = +str.replace(/,/, '.');
4446                if (!isNaN(num)) {
4447                    return num;
4448                }
4449                num = this.getNumeralValue(str);
4450                if (!isNaN(num)) {
4451                    this.numeralMap[str] = num;
4452                    return num;
4453                }
4454                return num;
4455            },
4456
4457            getNumeralValue: function(str) {
4458                var place = 1, num = 0, lastWasPlace, isPlace, numeral, digit, arr;
4459                // Note that "numerals" that need to be converted through this method are
4460                // all considered to be single characters in order to handle CJK. This
4461                // method is by no means unique to CJK, but the complexity of handling
4462                // inflections in non-CJK languages adds too much overhead for not enough
4463                // value, so avoiding for now.
4464                arr = str.split('');
4465                for (var i = arr.length - 1; numeral = arr[i]; i--) {
4466                    digit = getOwn(this.numeralMap, numeral);
4467                    if (isUndefined(digit)) {
4468                        digit = getOwn(fullWidthNumberMap, numeral) || 0;
4469                    }
4470                    isPlace = digit > 0 && digit % 10 === 0;
4471                    if (isPlace) {
4472                        if (lastWasPlace) {
4473                            num += place;
4474                        }
4475                        if (i) {
4476                            place = digit;
4477                        } else {
4478                            num += digit;
4479                        }
4480                    } else {
4481                        num += digit * place;
4482                        place *= 10;
4483                    }
4484                    lastWasPlace = isPlace;
4485                }
4486                return num;
4487            },
4488
4489            getOrdinal: function(n) {
4490                var suffix = this.ordinalSuffix;
4491                return suffix || getOrdinalSuffix(n);
4492            },
4493
4494            getRelativeFormat: function(adu, type) {
4495                return this.convertAdjustedToFormat(adu, type);
4496            },
4497
4498            getDuration: function(ms) {
4499                return this.convertAdjustedToFormat(getAdjustedUnitForNumber(max(0, ms)), 'duration');
4500            },
4501
4502            getFirstDayOfWeek: function() {
4503                var val = this.firstDayOfWeek;
4504                return isDefined(val) ? val : ISO_FIRST_DAY_OF_WEEK;
4505            },
4506
4507            getFirstDayOfWeekYear: function() {
4508                return this.firstDayOfWeekYear || ISO_FIRST_DAY_OF_WEEK_YEAR;
4509            },
4510
4511            convertAdjustedToFormat: function(adu, type) {
4512                var sign, unit, mult,
4513            num = adu[0],
4514            u = adu[1],
4515            ms = adu[2],
4516            format = this[type] || this.relative;
4517                if (isFunction(format)) {
4518                    return format.call(this, num, u, ms, type);
4519                }
4520                mult = !this.plural || num === 1 ? 0 : 1;
4521                unit = this.units[mult * 8 + u] || this.units[u];
4522                sign = this[ms > 0 ? 'fromNow' : 'ago'];
4523                return format.replace(/\{(.*?)\}/g, function(full, match) {
4524                    switch (match) {
4525                        case 'num': return num;
4526                        case 'unit': return unit;
4527                        case 'sign': return sign;
4528                    }
4529                });
4530            },
4531
4532            cacheFormat: function(dif, i) {
4533                this.compiledFormats.splice(i, 1);
4534                this.compiledFormats.unshift(dif);
4535            },
4536
4537            addFormat: function(src, to) {
4538                var loc = this;
4539
4540                function getTokenSrc(str) {
4541                    var suffix, src, val,
4542              opt = str.match(/\?$/),
4543              nc = str.match(/^(\d+)\??$/),
4544              slice = str.match(/(\d)(?:-(\d))?/),
4545              key = str.replace(/[^a-z]+$/i, '');
4546
4547                    // Allowing alias tokens such as {time}
4548                    if (val = getOwn(loc.parsingAliases, key)) {
4549                        src = replaceParsingTokens(val);
4550                        if (opt) {
4551                            src = getRegNonCapturing(src, true);
4552                        }
4553                        return src;
4554                    }
4555
4556                    if (nc) {
4557                        src = loc.tokens[nc[1]];
4558                    } else if (val = getOwn(ParsingTokens, key)) {
4559                        src = val.src;
4560                    } else {
4561                        val = getOwn(loc.parsingTokens, key) || getOwn(loc, key);
4562
4563                        // Both the "months" array and the "month" parsing token can be accessed
4564                        // by either {month} or {months}, falling back as necessary, however
4565                        // regardless of whether or not a fallback occurs, the final field to
4566                        // be passed to addRawFormat must be normalized as singular.
4567                        key = key.replace(/s$/, '');
4568
4569                        if (!val) {
4570                            val = getOwn(loc.parsingTokens, key) || getOwn(loc, key + 's');
4571                        }
4572
4573                        if (isString(val)) {
4574                            src = val;
4575                            suffix = loc[key + 'Suffix'];
4576                        } else {
4577                            if (slice) {
4578                                val = filter(val, function(m, i) {
4579                                    var mod = i % (loc.units ? 8 : val.length);
4580                                    return mod >= slice[1] && mod <= (slice[2] || slice[1]);
4581                                });
4582                            }
4583                            src = arrayToRegAlternates(val);
4584                        }
4585                    }
4586                    if (!src) {
4587                        return '';
4588                    }
4589                    if (nc) {
4590                        // Non-capturing tokens like {0}
4591                        src = getRegNonCapturing(src);
4592                    } else {
4593                        // Capturing group and add to parsed tokens
4594                        to.push(key);
4595                        src = '(' + src + ')';
4596                    }
4597                    if (suffix) {
4598                        // Date/time suffixes such as those in CJK
4599                        src = getParsingTokenWithSuffix(key, src, suffix);
4600                    }
4601                    if (opt) {
4602                        src += '?';
4603                    }
4604                    return src;
4605                }
4606
4607                function replaceParsingTokens(str) {
4608
4609                    // Make spaces optional
4610                    str = str.replace(/ /g, ' ?');
4611
4612                    return str.replace(/\{([^,]+?)\}/g, function(match, token) {
4613                        var tokens = token.split('|'), src;
4614                        if (tokens.length > 1) {
4615                            src = getRegNonCapturing(map(tokens, getTokenSrc).join('|'));
4616                        } else {
4617                            src = getTokenSrc(token);
4618                        }
4619                        return src;
4620                    });
4621                }
4622
4623                if (!to) {
4624                    to = [];
4625                    src = replaceParsingTokens(src);
4626                }
4627
4628                loc.addRawFormat(src, to);
4629            },
4630
4631            addRawFormat: function(format, to) {
4632                this.compiledFormats.unshift({
4633                    reg: RegExp('^ *' + format + ' *$', 'i'),
4634                    to: to
4635                });
4636            },
4637
4638            init: function(def) {
4639                var loc = this;
4640
4641                // -- Initialization helpers
4642
4643                function initFormats() {
4644                    loc.compiledFormats = [];
4645                    loc.parsingAliases = {};
4646                    loc.parsingTokens = {};
4647                }
4648
4649                function initDefinition() {
4650                    simpleMerge(loc, def);
4651                }
4652
4653                function initArrayFields() {
4654                    forEach(LOCALE_ARRAY_FIELDS, function(name) {
4655                        var val = loc[name];
4656                        if (isString(val)) {
4657                            loc[name] = commaSplit(val);
4658                        } else if (!val) {
4659                            loc[name] = [];
4660                        }
4661                    });
4662                }
4663
4664                // -- Value array build helpers
4665
4666                function buildValueArray(name, mod, map, fn) {
4667                    var field = name, all = [], setMap;
4668                    if (!loc[field]) {
4669                        field += 's';
4670                    }
4671                    if (!map) {
4672                        map = {};
4673                        setMap = true;
4674                    }
4675                    forAllAlternates(field, function(alt, j, i) {
4676                        var idx = j * mod + i, val;
4677                        val = fn ? fn(i) : i;
4678                        map[alt] = val;
4679                        map[alt.toLowerCase()] = val;
4680                        all[idx] = alt;
4681                    });
4682                    loc[field] = all;
4683                    if (setMap) {
4684                        loc[name + 'Map'] = map;
4685                    }
4686                }
4687
4688                function forAllAlternates(field, fn) {
4689                    forEach(loc[field], function(str, i) {
4690                        forEachAlternate(str, function(alt, j) {
4691                            fn(alt, j, i);
4692                        });
4693                    });
4694                }
4695
4696                function forEachAlternate(str, fn) {
4697                    var arr = map(str.split('+'), function(split) {
4698                        return split.replace(/(.+):(.+)$/, function(full, base, suffixes) {
4699                            return map(suffixes.split('|'), function(suffix) {
4700                                return base + suffix;
4701                            }).join('|');
4702                        });
4703                    }).join('|');
4704                    forEach(arr.split('|'), fn);
4705                }
4706
4707                function buildNumerals() {
4708                    var map = {};
4709                    buildValueArray('numeral', 10, map);
4710                    buildValueArray('article', 1, map, function() {
4711                        return 1;
4712                    });
4713                    buildValueArray('placeholder', 4, map, function(n) {
4714                        return pow(10, n + 1);
4715                    });
4716                    loc.numeralMap = map;
4717                }
4718
4719                function buildTimeFormats() {
4720                    loc.parsingAliases['time'] = getTimeFormat();
4721                    loc.parsingAliases['tzOffset'] = getTZOffsetFormat();
4722                }
4723
4724                function getTimeFormat() {
4725                    var src;
4726                    if (loc.ampmFront) {
4727                        // "ampmFront" exists mostly for CJK locales, which also presume that
4728                        // time suffixes exist, allowing this to be a simpler regex.
4729                        src = '{ampm?} {hour} (?:{minute} (?::?{second})?)?';
4730                    } else if (loc.ampm.length) {
4731                        src = '{hour}(?:[.:]{minute}(?:[.:]{second})? {ampm?}| {ampm})';
4732                    } else {
4733                        src = '{hour}(?:[.:]{minute}(?:[.:]{second})?)';
4734                    }
4735                    return src;
4736                }
4737
4738                function getTZOffsetFormat() {
4739                    return '(?:{Z}|{GMT?}(?:{tzSign}{tzHour}(?::?{tzMinute}(?: \\([\\w\\s]+\\))?)?)?)?';
4740                }
4741
4742                function buildParsingTokens() {
4743                    forEachProperty(LocalizedParsingTokens, function(token, name) {
4744                        var src, arr;
4745                        src = token.base ? ParsingTokens[token.base].src : token.src;
4746                        if (token.requiresNumerals || loc.numeralUnits) {
4747                            src += getNumeralSrc();
4748                        }
4749                        arr = loc[name + 's'];
4750                        if (arr && arr.length) {
4751                            src += '|' + arrayToRegAlternates(arr);
4752                        }
4753                        loc.parsingTokens[name] = src;
4754                    });
4755                }
4756
4757                function getNumeralSrc() {
4758                    var all, src = '';
4759                    all = loc.numerals.concat(loc.placeholders).concat(loc.articles);
4760                    if (loc.allowsFullWidth) {
4761                        all = all.concat(fullWidthNumbers.split(''));
4762                    }
4763                    if (all.length) {
4764                        src = '|(?:' + arrayToRegAlternates(all) + ')+';
4765                    }
4766                    return src;
4767                }
4768
4769                function buildTimeSuffixes() {
4770                    iterateOverDateUnits(function(unit, i) {
4771                        var token = loc.timeSuffixes[i];
4772                        if (token) {
4773                            loc[(unit.alias || unit.name) + 'Suffix'] = token;
4774                        }
4775                    });
4776                }
4777
4778                function buildModifiers() {
4779                    forEach(loc.modifiers, function(modifier) {
4780                        var name = modifier.name, mapKey = name + 'Map', map;
4781                        map = loc[mapKey] || {};
4782                        forEachAlternate(modifier.src, function(alt, j) {
4783                            var token = getOwn(loc.parsingTokens, name), val = modifier.value;
4784                            map[alt] = val;
4785                            loc.parsingTokens[name] = token ? token + '|' + alt : alt;
4786                            if (modifier.name === 'sign' && j === 0) {
4787                                // Hooking in here to set the first "fromNow" or "ago" modifier
4788                                // directly on the locale, so that it can be reused in the
4789                                // relative format.
4790                                loc[val === 1 ? 'fromNow' : 'ago'] = alt;
4791                            }
4792                        });
4793                        loc[mapKey] = map;
4794                    });
4795                }
4796
4797                // -- Format adding helpers
4798
4799                function addCoreFormats() {
4800                    forEach(CoreParsingFormats, function(df) {
4801                        var src = df.src;
4802                        if (df.mdy && loc.mdy) {
4803                            // Use the mm/dd/yyyy variant if it
4804                            // exists and the locale requires it
4805                            src = df.mdy;
4806                        }
4807                        if (df.time) {
4808                            // Core formats that allow time require the time
4809                            // reg on both sides, so add both versions here.
4810                            loc.addFormat(getFormatWithTime(src, true));
4811                            loc.addFormat(getFormatWithTime(src));
4812                        } else {
4813                            loc.addFormat(src);
4814                        }
4815                    });
4816                    loc.addFormat('{time}');
4817                }
4818
4819                function addLocaleFormats() {
4820                    addFormatSet('parse');
4821                    addFormatSet('timeParse', true);
4822                    addFormatSet('timeFrontParse', true, true);
4823                }
4824
4825                function addFormatSet(field, allowTime, timeFront) {
4826                    forEach(loc[field], function(format) {
4827                        if (allowTime) {
4828                            format = getFormatWithTime(format, timeFront);
4829                        }
4830                        loc.addFormat(format);
4831                    });
4832                }
4833
4834                function getFormatWithTime(baseFormat, timeBefore) {
4835                    if (timeBefore) {
4836                        return getTimeBefore() + baseFormat;
4837                    }
4838                    return baseFormat + getTimeAfter();
4839                }
4840
4841                function getTimeBefore() {
4842                    return getRegNonCapturing('{time}[,\\s\\u3000]', true);
4843                }
4844
4845                function getTimeAfter() {
4846                    var markers = ',?[\\s\\u3000]', localized;
4847                    localized = arrayToRegAlternates(loc.timeMarkers);
4848                    if (localized) {
4849                        markers += '| (?:' + localized + ') ';
4850                    }
4851                    markers = getRegNonCapturing(markers, loc.timeMarkerOptional);
4852                    return getRegNonCapturing(markers + '{time}', true);
4853                }
4854
4855                initFormats();
4856                initDefinition();
4857                initArrayFields();
4858
4859                buildValueArray('month', 12);
4860                buildValueArray('weekday', 7);
4861                buildValueArray('unit', 8);
4862                buildValueArray('ampm', 2);
4863
4864                buildNumerals();
4865                buildTimeFormats();
4866                buildParsingTokens();
4867                buildTimeSuffixes();
4868                buildModifiers();
4869
4870                // The order of these formats is important. Order is reversed so formats
4871                // that are initialized later will take precedence. Generally, this means
4872                // that more specific formats should come later.
4873                addCoreFormats();
4874                addLocaleFormats();
4875
4876            }
4877
4878        };
4879
4880        return new Locale(def);
4881    }
4882
4883
4884    /***
4885    * @method [units]Since(d, [options])
4886    * @returns Number
4887    * @short Returns the time since [d].
4888    * @extra [d] will accept a date object, timestamp, or string. If not specified,
4889    *        [d] is assumed to be now. `unitsAgo` is provided as an alias to make
4890    *        this more readable when [d] is assumed to be the current date.
4891    *        [options] can be an object or a locale code as a string. See `create`
4892    *        for more.
4893    *
4894    * @set
4895    *   millisecondsSince
4896    *   secondsSince
4897    *   minutesSince
4898    *   hoursSince
4899    *   daysSince
4900    *   weeksSince
4901    *   monthsSince
4902    *   yearsSince
4903    *
4904    * @example
4905    *
4906    *   new Date().millisecondsSince('1 hour ago') -> 3,600,000
4907    *   new Date().daysSince('1 week ago')         -> 7
4908    *   new Date().yearsSince('15 years ago')      -> 15
4909    *   lastYear.yearsAgo()                 -> 1
4910    *
4911    * @param {string|number|Date} d
4912    * @param {DateCreateOptions} options
4913    *
4914    ***
4915    * @method [units]Ago()
4916    * @returns Number
4917    * @short Returns the time ago in the appropriate unit.
4918    *
4919    * @set
4920    *   millisecondsAgo
4921    *   secondsAgo
4922    *   minutesAgo
4923    *   hoursAgo
4924    *   daysAgo
4925    *   weeksAgo
4926    *   monthsAgo
4927    *   yearsAgo
4928    *
4929    * @example
4930    *
4931    *   lastYear.millisecondsAgo() -> 3,600,000
4932    *   lastYear.daysAgo()         -> 7
4933    *   lastYear.yearsAgo()        -> 15
4934    *
4935    ***
4936    * @method [units]Until([d], [options])
4937    * @returns Number
4938    * @short Returns the time until [d].
4939    * @extra [d] will accept a date object, timestamp, or string. If not specified,
4940    *        [d] is assumed to be now. `unitsFromNow` is provided as an alias to
4941    *        make this more readable when [d] is assumed to be the current date.
4942    *        [options] can be an object or a locale code as a string. See `create`
4943    *        for more.
4944    *
4945    *
4946    * @set
4947    *   millisecondsUntil
4948    *   secondsUntil
4949    *   minutesUntil
4950    *   hoursUntil
4951    *   daysUntil
4952    *   weeksUntil
4953    *   monthsUntil
4954    *   yearsUntil
4955    *
4956    * @example
4957    *
4958    *   new Date().millisecondsUntil('1 hour from now') -> 3,600,000
4959    *   new Date().daysUntil('1 week from now')         -> 7
4960    *   new Date().yearsUntil('15 years from now')      -> 15
4961    *   nextYear.yearsFromNow()                  -> 1
4962    *
4963    * @param {string|number|Date} d
4964    * @param {DateCreateOptions} options
4965    *
4966    ***
4967    * @method [units]FromNow()
4968    * @returns Number
4969    * @short Returns the time from now in the appropriate unit.
4970    *
4971    * @set
4972    *   millisecondsFromNow
4973    *   secondsFromNow
4974    *   minutesFromNow
4975    *   hoursFromNow
4976    *   daysFromNow
4977    *   weeksFromNow
4978    *   monthsFromNow
4979    *   yearsFromNow
4980    *
4981    * @example
4982    *
4983    *   nextYear.millisecondsFromNow() -> 3,600,000
4984    *   nextYear.daysFromNow()         -> 7
4985    *   nextYear.yearsFromNow()        -> 15
4986    *
4987    ***
4988    * @method add[Units](n, [reset] = false)
4989    * @returns Date
4990    * @short Adds `n` units to the date. If [reset] is true, all lower units will
4991    *        be reset.
4992    * @extra This method modifies the date! Note that in the case of `addMonths`,
4993    *        the date may fall on a date that doesn't exist (i.e. February 30). In
4994    *        this case the date will be shifted to the last day of the month. Don't
4995    *        use `addMonths` if you need precision.
4996    *
4997    * @set
4998    *   addMilliseconds
4999    *   addSeconds
5000    *   addMinutes
5001    *   addHours
5002    *   addDays
5003    *   addWeeks
5004    *   addMonths
5005    *   addYears
5006    *
5007    * @example
5008    *
5009    *   new Date().addYears(5)        -> current time + 5 years
5010    *   new Date().addDays(5)         -> current time + 5 days
5011    *   new Date().addDays(5, true)   -> current time + 5 days (time reset)
5012    *
5013    * @param {number} n
5014    * @param {boolean} [reset]
5015    *
5016    ***
5017    * @method isLast[Unit]([localeCode])
5018    * @returns Boolean
5019    * @short Returns true if the date is last week, month, or year.
5020    * @extra This method takes an optional locale code for `isLastWeek`, which is
5021    *        locale dependent. The default locale code is `en`, which places
5022    *        Sunday at the beginning of the week. You can pass `en-GB` as a quick
5023    *        way to force Monday as the beginning of the week.
5024    *
5025    * @set
5026    *   isLastWeek
5027    *   isLastMonth
5028    *   isLastYear
5029    *
5030    * @example
5031    *
5032    *   yesterday.isLastWeek()  -> true or false?
5033    *   yesterday.isLastMonth() -> probably not...
5034    *   yesterday.isLastYear()  -> even less likely...
5035    *
5036    * @param {string} [localeCode]
5037    *
5038    ***
5039    * @method isThis[Unit]([localeCode])
5040    * @returns Boolean
5041    * @short Returns true if the date is this week, month, or year.
5042    * @extra This method takes an optional locale code for `isThisWeek`, which is
5043    *        locale dependent. The default locale code is `en`, which places
5044    *        Sunday at the beginning of the week. You can pass `en-GB` as a quick
5045    *        way to force Monday as the beginning of the week.
5046    *
5047    * @set
5048    *   isThisWeek
5049    *   isThisMonth
5050    *   isThisYear
5051    *
5052    * @example
5053    *
5054    *   tomorrow.isThisWeek()  -> true or false?
5055    *   tomorrow.isThisMonth() -> probably...
5056    *   tomorrow.isThisYear()  -> signs point to yes...
5057    *
5058    * @param {string} [localeCode]
5059    *
5060    ***
5061    * @method isNext[Unit]([localeCode])
5062    * @returns Boolean
5063    * @short Returns true if the date is next week, month, or year.
5064    * @extra This method takes an optional locale code for `isNextWeek`, which is
5065    *        locale dependent. The default locale code is `en`, which places
5066    *        Sunday at the beginning of the week. You can pass `en-GB` as a quick
5067    *        way to force Monday as the beginning of the week.
5068    *
5069    * @set
5070    *   isNextWeek
5071    *   isNextMonth
5072    *   isNextYear
5073    *
5074    * @example
5075    *
5076    *   tomorrow.isNextWeek()  -> true or false?
5077    *   tomorrow.isNextMonth() -> probably not...
5078    *   tomorrow.isNextYear()  -> even less likely...
5079    *
5080    * @param {string} [localeCode]
5081    *
5082    ***
5083    * @method beginningOf[Unit]([localeCode])
5084    * @returns Date
5085    * @short Sets the date to the beginning of the appropriate unit.
5086    * @extra This method modifies the date! A locale code can be passed for
5087    *        `beginningOfWeek`, which is locale dependent. If consistency is
5088    *        needed, use `beginningOfISOWeek` instead.
5089    *
5090    * @set
5091    *   beginningOfDay
5092    *   beginningOfWeek
5093    *   beginningOfMonth
5094    *   beginningOfYear
5095    *
5096    * @example
5097    *
5098    *   new Date().beginningOfDay()   -> the beginning of today (resets the time)
5099    *   new Date().beginningOfWeek()  -> the beginning of the week
5100    *   new Date().beginningOfMonth() -> the beginning of the month
5101    *   new Date().beginningOfYear()  -> the beginning of the year
5102    *
5103    * @param {string} [localeCode]
5104    *
5105    ***
5106    * @method endOf[Unit]([localeCode])
5107    * @returns Date
5108    * @short Sets the date to the end of the appropriate unit.
5109    * @extra This method modifies the date! A locale code can be passed for
5110    *        `endOfWeek`, which is locale dependent. If consistency is needed, use
5111    *        `endOfISOWeek` instead.
5112    *
5113    * @set
5114    *   endOfDay
5115    *   endOfWeek
5116    *   endOfMonth
5117    *   endOfYear
5118    *
5119    * @example
5120    *
5121    *   new Date().endOfDay()   -> the end of today (sets the time to 23:59:59.999)
5122    *   new Date().endOfWeek()  -> the end of the week
5123    *   new Date().endOfMonth() -> the end of the month
5124    *   new Date().endOfYear()  -> the end of the year
5125    *
5126    * @param {string} [localeCode]
5127    *
5128    ***/
5129    function buildDateUnitMethods() {
5130
5131        defineInstanceSimilar(sugarDate, DateUnits, function(methods, unit, index) {
5132            var name = unit.name, caps = simpleCapitalize(name);
5133
5134            if (index > DAY_INDEX) {
5135                forEach(['Last', 'This', 'Next'], function(shift) {
5136                    methods['is' + shift + caps] = function(d, localeCode) {
5137                        return compareDate(d, shift + ' ' + name, 0, localeCode, { locale: 'en' });
5138                    };
5139                });
5140            }
5141            if (index > HOURS_INDEX) {
5142                methods['beginningOf' + caps] = function(d, localeCode) {
5143                    return moveToBeginningOfUnit(d, index, localeCode);
5144                };
5145                methods['endOf' + caps] = function(d, localeCode) {
5146                    return moveToEndOfUnit(d, index, localeCode);
5147                };
5148            }
5149
5150            methods['add' + caps + 's'] = function(d, num, reset) {
5151                return advanceDate(d, name, num, reset);
5152            };
5153
5154            var since = function(date, d, options) {
5155                return getTimeDistanceForUnit(date, createDateWithContext(date, d, options, true), unit);
5156            };
5157            var until = function(date, d, options) {
5158                return getTimeDistanceForUnit(createDateWithContext(date, d, options, true), date, unit);
5159            };
5160
5161            methods[name + 'sAgo'] = methods[name + 'sUntil'] = until;
5162            methods[name + 'sSince'] = methods[name + 'sFromNow'] = since;
5163
5164        });
5165
5166    }
5167
5168    /***
5169    * @method is[Day]()
5170    * @returns Boolean
5171    * @short Returns true if the date falls on the specified day.
5172    *
5173    * @set
5174    *   isToday
5175    *   isYesterday
5176    *   isTomorrow
5177    *   isWeekday
5178    *   isWeekend
5179    *   isSunday
5180    *   isMonday
5181    *   isTuesday
5182    *   isWednesday
5183    *   isThursday
5184    *   isFriday
5185    *   isSaturday
5186    *
5187    * @example
5188    *
5189    *   tomorrow.isToday() -> false
5190    *   thursday.isTomorrow() -> ?
5191    *   yesterday.isWednesday() -> ?
5192    *   today.isWeekend() -> ?
5193    *
5194    ***
5195    * @method isFuture()
5196    * @returns Boolean
5197    * @short Returns true if the date is in the future.
5198    *
5199    * @example
5200    *
5201    *   lastWeek.isFuture() -> false
5202    *   nextWeek.isFuture() -> true
5203    *
5204    ***
5205    * @method isPast()
5206    * @returns Boolean
5207    * @short Returns true if the date is in the past.
5208    *
5209    * @example
5210    *
5211    *   lastWeek.isPast() -> true
5212    *   nextWeek.isPast() -> false
5213    *
5214    ***/
5215    function buildRelativeAliases() {
5216        var special = spaceSplit('Today Yesterday Tomorrow Weekday Weekend Future Past');
5217        var weekdays = English.weekdays.slice(0, 7);
5218        var months = English.months.slice(0, 12);
5219        var together = special.concat(weekdays).concat(months);
5220        defineInstanceSimilar(sugarDate, together, function(methods, name) {
5221            methods['is' + name] = function(d) {
5222                return fullCompareDate(d, name);
5223            };
5224        });
5225    }
5226
5227    defineStatic(sugarDate, {
5228
5229        /***
5230        * @method create(d, [options])
5231        * @returns Date
5232        * @static
5233        * @short Alternate date constructor which accepts text formats, a timestamp,
5234        *        objects, or another date.
5235        * @extra If no argument is given, the date is assumed to be now. The second
5236        *        argument can either be an options object or a locale code as a
5237        *        shortcut. For more, see `date parsing`.
5238        *
5239        * @options
5240        *
5241        *   locale     A locale code to parse the date in. This can also be passed as
5242        *              the second argument to this method. Default is the current
5243        *              locale, which is `en` if none is set.
5244        *
5245        *   past       If `true`, ambiguous dates like `Sunday` will be parsed as
5246        *              `last Sunday`. Note that non-ambiguous dates are not
5247        *              guaranteed to be in the past.
5248        *              Default is `false`.
5249        *
5250        *   future     If `true`, ambiguous dates like `Sunday` will be parsed as
5251        *              `next Sunday`. Note that non-ambiguous dates are not
5252        *              guaranteed to be in the future.
5253        *              Default is `false`.
5254        *
5255        *   fromUTC    If `true`, dates with no timezone notation will be parsed as
5256        *              UTC (no timezone offset). This is useful for server
5257        *              timestamps, etc. Note that this flag is not required if the
5258        *              timezone is specified in the string, either as an explicit
5259        *              value (ex. +0900 or -09:00) or "Z", which is UTC time.
5260        *
5261        *   setUTC     If `true`, this will set a flag on the date that tells Sugar
5262        *              to internally use UTC methods like `getUTCHours` when handling
5263        *              it. This flag is the same as calling the `setUTC` method on
5264        *              the date after parsing is complete. Note that this is
5265        *              different from `fromUTC`, which parses a string as UTC, but
5266        *              does not set this flag.
5267        *
5268        *   clone      If `true` and `d` is a date, it will be cloned.
5269        *
5270        *   params     An optional object that is populated with properties that are
5271        *              parsed from string input. This option is useful when parsed
5272        *              properties need to be retained.
5273        *
5274        * @example
5275        *
5276        *   Date.create('July')                      -> July of this year
5277        *   Date.create('1776')                      -> 1776
5278        *   Date.create('today')                     -> today
5279        *   Date.create('Wednesday')                 -> This wednesday
5280        *   Date.create('next Friday')               -> Next friday
5281        *   Date.create('July 4, 1776')              -> July 4, 1776
5282        *   Date.create(-446806800000)               -> November 5, 1955
5283        *   Date.create('1776年07月04日', 'ja')      -> July 4, 1776
5284        *   Date.create('August', {past: true})      -> August of this or last year
5285        *   Date.create('August', {future: true})    -> August of this or next year
5286        *   Date.create('Thursday', {fromUTC: true}) -> Thursday at 12:00am UTC time
5287        *
5288        * @param {string|number|Date} d
5289        * @param {DateCreateOptions} [options]
5290        *
5291        * @option {string} [locale]
5292        * @option {boolean} [past]
5293        * @option {boolean} [future]
5294        * @option {boolean} [fromUTC]
5295        * @option {boolean} [setUTC]
5296        * @option {boolean} [clone]
5297        * @option {Object} [params]
5298        *
5299        ***/
5300        'create': function(d, options) {
5301            return createDate(d, options);
5302        },
5303
5304        /***
5305        * @method getLocale([localeCode] = current)
5306        * @returns Locale
5307        * @static
5308        * @short Gets the locale object for the given code, or the current locale.
5309        * @extra The locale object has various properties that dictate how dates are
5310        *        parsed and formatted for that locale. The locale object is exposed
5311        *        here mostly for introspection - it should be uncommon to need to
5312        *        maniplate the object itself. For more, see `date locales`.
5313        *
5314        * @example
5315        *
5316        *   Date.getLocale()     -> Returns the current locale
5317        *   Date.getLocale('en') -> Returns the EN locale
5318        *
5319        * @param {string} [localeCode]
5320        *
5321        ***/
5322        'getLocale': function(code) {
5323            return localeManager.get(code, !code);
5324        },
5325
5326        /***
5327        * @method getAllLocales()
5328        * @returns Array<Locale>
5329        * @static
5330        * @short Returns all available locales as an object.
5331        * @extra For more, see `date locales`.
5332        * @example
5333        *
5334        *   Date.getAllLocales()
5335        *
5336        ***/
5337        'getAllLocales': function() {
5338            return localeManager.getAll();
5339        },
5340
5341        /***
5342        * @method getAllLocaleCodes()
5343        * @returns string[]
5344        * @static
5345        * @short Returns all available locale codes as an array of str
5345ings.
5346        * @extra For more, see `date locales`.
5347        * @example
5348        *
5349        *   Date.getAllLocaleCodes()
5350        *
5351        ***/
5352        'getAllLocaleCodes': function() {
5353            return getKeys(localeManager.getAll());
5354        },
5355
5356        /***
5357        * @method setLocale(code)
5358        * @returns Locale
5359        * @static
5360        * @short Sets the current locale to be used with dates.
5361        * @extra Sugar has native support for 17 major locales. In addition, you can
5362        *        define a new locale with `addLocale`. For more, see `date locales`.
5363        * @example
5364        *
5365        *   Date.setLocale('en')
5366        *
5367        * @param {string} code
5368        *
5369        ***/
5370        'setLocale': function(code) {
5371            return localeManager.set(code);
5372        },
5373
5374        /***
5375        * @method addLocale(code, def)
5376        * @returns Locale
5377        * @static
5378        * @short Adds a locale definition to the locales understood by Sugar.
5379        * @extra This method should only be required for adding locale definitions
5380        *        that don't already exist. For more, see `date locales`.
5381        * @example
5382        *
5383        *   Date.addLocale('eo', {})
5384        *
5385        * @param {string} code
5386        * @param {Object} def
5387        *
5388        ***/
5389        'addLocale': function(code, set) {
5390            return localeManager.add(code, set);
5391        },
5392
5393        /***
5394        * @method removeLocale(code)
5395        * @returns Locale
5396        * @static
5397        * @short Deletes the the locale by `code` from Sugar's known locales.
5398        * @extra For more, see `date locales`.
5399        * @example
5400        *
5401        *   Date.removeLocale('foo')
5402        *
5403        * @param {string} code
5404        *
5405        ***/
5406        'removeLocale': function(code) {
5407            return localeManager.remove(code);
5408        }
5409
5410    });
5411
5412    defineInstanceWithArguments(sugarDate, {
5413
5414        /***
5415        * @method set(set, [reset] = false)
5416        * @returns Date
5417        * @short Sets the date object.
5418        * @extra This method accepts multiple formats including a single number as
5419        *        a timestamp, an object, or enumerated arguments. If [reset] is
5420        *        `true`, any units more specific than those passed will be reset.
5421        *
5422        * @example
5423        *
5424        *   new Date().set({year:2011,month:11,day:31}) -> December 31, 2011
5425        *   new Date().set(2011, 11, 31)                -> December 31, 2011
5426        *   new Date().set(86400000)                    -> 1 day after Jan 1, 1970
5427        *   new Date().set({year:2004,month:6}, true)   -> June 1, 2004, 00:00:00.000
5428        *
5429        * @signature set(milliseconds)
5430        * @signature set(year, month, [day], [hour], [minute], [second], [millliseconds])
5431        * @param {Object} set
5432        * @param {boolean} [reset]
5433        * @param {number} year
5434        * @param {number} month
5435        * @param {number} [day]
5436        * @param {number} [hour]
5437        * @param {number} [minute]
5438        * @param {number} [second]
5439        * @param {number} [milliseconds]
5440        *
5441        ***/
5442        'set': function(d, args) {
5443            args = collectDateArguments(args);
5444            return updateDate(d, args[0], args[1]);
5445        },
5446
5447        /***
5448        * @method advance(set, [reset] = false)
5449        * @returns Date
5450        * @short Shifts the date forward.
5451        * @extra `set` accepts multiple formats including an object, a string in the
5452        *        format "3 days", a single number as milliseconds, or enumerated
5453        *        parameters (as with the Date constructor). If [reset] is `true`, any
5454        *        units more specific than those passed will be reset. This method
5455        *        modifies the date!
5456        *
5457        * @example
5458        *
5459        *   new Date().advance({ year: 2 }) -> 2 years in the future
5460        *   new Date().advance('2 hours')   -> 2 hours in the future
5461        *   new Date().advance(0, 2, 3)     -> 2 months 3 days in the future
5462        *   new Date().advance(86400000)    -> 1 day in the future
5463        *
5464        * @signature advance(milliseconds)
5465        * @signature advance(year, month, [day], [hour], [minute], [second], [millliseconds])
5466        * @param {string|Object} set
5467        * @param {boolean} [reset]
5468        * @param {number} year
5469        * @param {number} month
5470        * @param {number} [day]
5471        * @param {number} [hour]
5472        * @param {number} [minute]
5473        * @param {number} [second]
5474        * @param {number} [milliseconds]
5475        *
5476        ***/
5477        'advance': function(d, args) {
5478            return advanceDateWithArgs(d, args, 1);
5479        },
5480
5481        /***
5482        * @method rewind(set, [reset] = false)
5483        * @returns Date
5484        * @short Shifts the date backward.
5485        * @extra [set] accepts multiple formats including an object, a string in the
5486        *        format "3 days", a single number as milliseconds, or enumerated
5487        *        parameters (as with the Date constructor). If [reset] is `true`, any
5488        *        units more specific than those passed will be reset. This method
5489        *        modifies the date!
5490        *
5491        * @example
5492        *
5493        *   new Date().rewind({ year: 2 }) -> 2 years in the past
5494        *   new Date().rewind('2 weeks')   -> 2 weeks in the past
5495        *   new Date().rewind(0, 2, 3)     -> 2 months 3 days in the past
5496        *   new Date().rewind(86400000)    -> 1 day in the past
5497        *
5498        * @signature advance(milliseconds)
5499        * @signature advance(year, month, [day], [hour], [minute], [second], [millliseconds])
5500        * @param {string|Object} set
5501        * @param {boolean} [reset]
5502        * @param {number} year
5503        * @param {number} month
5504        * @param {number} [day]
5505        * @param {number} [hour]
5506        * @param {number} [minute]
5507        * @param {number} [second]
5508        * @param {number} [milliseconds]
5509        *
5510        ***/
5511        'rewind': function(d, args) {
5512            return advanceDateWithArgs(d, args, -1);
5513        }
5514
5515    });
5516
5517    defineInstance(sugarDate, {
5518
5519        /***
5520        * @method get(d, [options])
5521        * @returns Date
5522        * @short Gets a new date using the current one as a starting point.
5523        * @extra This method is identical to `Date.create`, except that relative
5524        *        formats like `next month` are relative to the date instance rather
5525        *        than the current date. Accepts a locale code as a string in place
5526        *        of [options]. See `create` for more.
5527        *
5528        * @example
5529        *
5530        *   nextYear.get('monday') -> monday of the week exactly 1 year from now
5531        *   millenium.get('2 years before') -> 2 years before Jan 1, 2000.
5532        *
5533        * @param {string|number|Date} d
5534        * @param {DateCreateOptions} options
5535        *
5536        ***/
5537        'get': function(date, d, options) {
5538            return createDateWithContext(date, d, options);
5539        },
5540
5541        /***
5542        * @method setWeekday(dow)
5543        * @short Sets the weekday of the date, starting with Sunday at `0`.
5544        * @extra This method modifies the date!
5545        *
5546        * @example
5547        *
5548        *   d = new Date(); d.setWeekday(1); d; -> Monday of this week
5549        *   d = new Date(); d.setWeekday(6); d; -> Saturday of this week
5550        *
5551        * @param {number} dow
5552        *
5553        ***/
5554        'setWeekday': function(date, dow) {
5555            return setWeekday(date, dow);
5556        },
5557
5558        /***
5559        * @method setISOWeek(num)
5560        * @short Sets the week (of the year) as defined by the ISO8601 standard.
5561        * @extra Note that this standard places Sunday at the end of the week (day 7).
5562        *        This method modifies the date!
5563        *
5564        * @example
5565        *
5566        *   d = new Date(); d.setISOWeek(15); d; -> 15th week of the year
5567        *
5568        * @param {number} num
5569        *
5570        ***/
5571        'setISOWeek': function(date, num) {
5572            return setISOWeekNumber(date, num);
5573        },
5574
5575        /***
5576        * @method getISOWeek()
5577        * @returns Number
5578        * @short Gets the date's week (of the year) as defined by the ISO8601 standard.
5579        * @extra Note that this standard places Sunday at the end of the week (day 7).
5580        *        If `utc` is set on the date, the week will be according to UTC time.
5581        *
5582        * @example
5583        *
5584        *   new Date().getISOWeek() -> today's week of the year
5585        *
5586        ***/
5587        'getISOWeek': function(date) {
5588            return getWeekNumber(date, true);
5589        },
5590
5591        /***
5592        * @method beginningOfISOWeek()
5593        * @returns Date
5594        * @short Set the date to the beginning of week as defined by ISO8601.
5595        * @extra Note that this standard places Monday at the start of the week.
5596        *        This method modifies the date!
5597        *
5598        * @example
5599        *
5600        *   new Date().beginningOfISOWeek() -> Monday
5601        *
5602        ***/
5603        'beginningOfISOWeek': function(date) {
5604            var day = getWeekday(date);
5605            if (day === 0) {
5606                day = -6;
5607            } else if (day !== 1) {
5608                day = 1;
5609            }
5610            setWeekday(date, day);
5611            return resetTime(date);
5612        },
5613
5614        /***
5615        * @method endOfISOWeek()
5616        * @returns Date
5617        * @short Set the date to the end of week as defined by this ISO8601 standard.
5618        * @extra Note that this standard places Sunday at the end of the week.
5619        *        This method modifies the date!
5620        *
5621        * @example
5622        *
5623        *   new Date().endOfISOWeek() -> Sunday
5624        *
5625        ***/
5626        'endOfISOWeek': function(date) {
5627            if (getWeekday(date) !== 0) {
5628                setWeekday(date, 7);
5629            }
5630            return moveToEndOfUnit(date, DAY_INDEX);
5631        },
5632
5633        /***
5634        * @method getUTCOffset([iso] = false)
5635        * @returns String
5636        * @short Returns a string representation of the offset from UTC time. If [iso]
5637        *        is true the offset will be in ISO8601 format.
5638        *
5639        * @example
5640        *
5641        *   new Date().getUTCOffset()     -> "+0900"
5642        *   new Date().getUTCOffset(true) -> "+09:00"
5643        *
5644        * @param {boolean} iso
5645        *
5646        ***/
5647        'getUTCOffset': function(date, iso) {
5648            return getUTCOffset(date, iso);
5649        },
5650
5651        /***
5652        * @method setUTC([on] = false)
5653        * @returns Date
5654        * @short Controls a flag on the date that tells Sugar to internally use UTC
5655        *        methods like `getUTCHours`.
5656        * @extra This flag is most commonly used for output in UTC time with the
5657        *        `format` method. Note that this flag only governs which methods are
5658        *        called internally – date native methods like `setHours` will still
5659        *        return local non-UTC values. This method will modify the date!
5660        *
5661        * @example
5662        *
5663        *   new Date().setUTC(true).long()  -> formatted with UTC methods
5664        *   new Date().setUTC(false).long() -> formatted without UTC methods
5665        *
5666        * @param {boolean} on
5667        *
5668        ***/
5669        'setUTC': function(date, on) {
5670            return _utc(date, on);
5671        },
5672
5673        /***
5674        * @method isUTC()
5675        * @returns Boolean
5676        * @short Returns true if the date has no timezone offset.
5677        * @extra This will also return true for dates whose internal utc flag is set
5678        *        with `setUTC`. Even if the utc flag is set, `getTimezoneOffset`
5679        *        will always report the same thing as Javascript always reports that
5680        *        based on the environment's locale.
5681        *
5682        * @example
5683        *
5684        *   new Date().isUTC() -> true or false (depends on the local offset)
5685        *   new Date().setUTC(true).isUTC() -> true
5686        *
5687        ***/
5688        'isUTC': function(date) {
5689            return isUTC(date);
5690        },
5691
5692        /***
5693        * @method isValid()
5694        * @returns Boolean
5695        * @short Returns true if the date is valid.
5696        *
5697        * @example
5698        *
5699        *   new Date().isValid()         -> true
5700        *   new Date('flexor').isValid() -> false
5701        *
5702        ***/
5703        'isValid': function(date) {
5704            return dateIsValid(date);
5705        },
5706
5707        /***
5708        * @method isAfter(d, [margin] = 0)
5709        * @returns Boolean
5710        * @short Returns true if the date is after `d`.
5711        * @extra [margin] is to allow extra margin of error in ms. `d` will accept
5712        *        a date object, timestamp, or string. If not specified, `d` is
5713        *        assumed to be now. See `create` for formats.
5714        *
5715        * @example
5716        *
5717        *   today.isAfter('tomorrow')  -> false
5718        *   today.isAfter('yesterday') -> true
5719        *
5720        * @param {string|number|Date} d
5721        * @param {number} [margin]
5722        *
5723        ***/
5724        'isAfter': function(date, d, margin) {
5725            return date.getTime() > createDate(d).getTime() - (margin || 0);
5726        },
5727
5728        /***
5729        * @method isBefore(d, [margin] = 0)
5730        * @returns Boolean
5731        * @short Returns true if the date is before `d`.
5732        * @extra [margin] is to allow extra margin of error in ms. `d` will accept
5733        *        a date object, timestamp, or text format. If not specified, `d` is
5734        *        assumed to be now. See `create` for formats.
5735        *
5736        * @example
5737        *
5738        *   today.isBefore('tomorrow')  -> true
5739        *   today.isBefore('yesterday') -> false
5740        *
5741        * @param {string|number|Date} d
5742        * @param {number} [margin]
5743        *
5744        ***/
5745        'isBefore': function(date, d, margin) {
5746            return date.getTime() < createDate(d).getTime() + (margin || 0);
5747        },
5748
5749        /***
5750        * @method isBetween(d1, d2, [margin] = 0)
5751        * @returns Boolean
5752        * @short Returns true if the date is later or equal to `d1` and before or
5753        *        equal to `d2`.
5754        * @extra [margin] is to allow extra margin of error in ms. `d1` and `d2` will
5755        *        accept a date object, timestamp, or text format. If not specified,
5756        *        they are assumed to be now.  See `create` for formats.
5757        *
5758        * @example
5759        *
5760        *   new Date().isBetween('yesterday', 'tomorrow')    -> true
5761        *   new Date().isBetween('last year', '2 years ago') -> false
5762        *
5763        * @param {string|number|Date} d1
5764        * @param {string|number|Date} d2
5765        * @param {number} [margin]
5766        *
5767        ***/
5768        'isBetween': function(date, d1, d2, margin) {
5769            var t = date.getTime();
5770            var t1 = createDate(d1).getTime();
5771            var t2 = createDate(d2).getTime();
5772            var lo = min(t1, t2);
5773            var hi = max(t1, t2);
5774            margin = margin || 0;
5775            return (lo - margin <= t) && (hi + margin >= t);
5776        },
5777
5778        /***
5779        * @method isLeapYear()
5780        * @returns Boolean
5781        * @short Returns true if the date is a leap year.
5782        *
5783        * @example
5784        *
5785        *   millenium.isLeapYear() -> true
5786        *
5787        ***/
5788        'isLeapYear': function(date) {
5789            var year = getYear(date);
5790            return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
5791        },
5792
5793        /***
5794        * @method daysInMonth()
5795        * @returns Number
5796        * @short Returns the number of days in the date's month.
5797        *
5798        * @example
5799        *
5800        *   may.daysInMonth() -> 31
5801        *   feb.daysInMonth() -> 28 or 29
5802        *
5803        ***/
5804        'daysInMonth': function(date) {
5805            return getDaysInMonth(date);
5806        },
5807
5808        /***
5809        * @method format([f], [localeCode] = currentLocaleCode)
5810        * @returns String
5811        * @short Returns the date as a string using the format `f`.
5812        * @extra `f` is a string that contains tokens in either LDML format using
5813        *        curly braces, or "strftime" format using a percent sign. If `f` is
5814        *        not specified, the locale specific `{long}` format is used. [localeCode]
5815        *        is a locale code to use (if not specified the current locale is
5816        *        used). For more, see `date formatting`.
5817        *
5818        * @example
5819        *
5820        *   new Date().format()                        -> ex. February 13, 2012 11:21 AM
5821        *   new Date().format('{Weekday} {d} {Month}') -> ex. Monday July 4
5822        *   new Date().format('{hh}:{mm}')             -> ex. 15:57
5823        *   new Date().format('%H:%M')                 -> ex. 15:57
5824        *   new Date().format('{12hr}:{mm}{tt}')       -> ex. 3:57pm
5825        *   new Date().format('ISO8601')               -> ex. 2011-07-05 12:24:55.528Z
5826        *   new Date().format('{Weekday}', 'ja')       -> ex. 先週
5827        *
5828        * @param {string} f
5829        * @param {string} [localeCode]
5830        *
5831        ***
5832        * @method short([localeCode] = currentLocaleCode)
5833        * @returns String
5834        * @short Outputs the date in the short format for the current locale.
5835        * @extra [localeCode] overrides the current locale code if passed.
5836        *
5837        * @example
5838        *
5839        *   new Date().short()     -> ex. 02/13/2016
5840        *   new Date().short('fi') -> ex. 13.2.2016
5841        *
5842        * @param {string} [localeCode]
5843        *
5844        ***
5845        * @method medium([localeCode] = currentLocaleCode)
5846        * @returns String
5847        * @short Outputs the date in the medium format for the current locale.
5848        * @extra [localeCode] overrides the current locale code if passed.
5849        *
5850        * @example
5851        *
5852        *   new Date().medium()     -> ex. February 13, 2016
5853        *   new Date().medium('ja') -> ex. 2016年2月13日
5854        *
5855        * @param {string} [localeCode]
5856        *
5857        ***
5858        * @method long([localeCode] = currentLocaleCode)
5859        * @returns String
5860        * @short Outputs the date in the long format for the current locale.
5861        * @extra [localeCode] overrides the current locale code if passed.
5862        *
5863        * @example
5864        *
5865        *   new Date().long()     -> ex. February 13, 2016 6:22 PM
5866        *   new Date().long('es') -> ex. 13 de febrero de 2016 18:22
5867        *
5868        * @param {string} [localeCode]
5869        *
5870        ***
5871        * @method full([localeCode] = currentLocaleCode)
5872        * @returns String
5873        * @short Outputs the date in the full format for the current locale.
5874        * @extra [localeCode] overrides the current locale code if passed.
5875        *
5876        * @example
5877        *
5878        *   new Date().full()     -> ex. Saturday, February 13, 2016 6:23 PM
5879        *   new Date().full('ru') -> ex. суббота, 13 февраля 2016 г., 18:23
5880        *
5881        * @param {string} [localeCode]
5882        *
5883        ***/
5884        'format': function(date, f, localeCode) {
5885            return dateFormat(date, f, localeCode);
5886        },
5887
5888        /***
5889        * @method relative([localeCode] = currentLocaleCode, [fn])
5890        * @returns String
5891        * @short Returns the date in a text format relative to the current time,
5892        *        such as "5 minutes ago".
5893        * @extra [fn] is a function that can be passed to provide more granular
5894        *        control over the resulting string. Its return value will be passed
5895        *        to `format`. If nothing is returned, the relative format will be
5896        *        used. [fn] may be passed as the first argument in place of [locale].
5897        *        For more about formats, see `date formatting`.
5898        *
5899        * @callback relativeFn
5900        *
5901        *   num   The offset number in `unit`.
5902        *   unit  A numeric representation of the unit that `num` is in, starting at
5903        *         0 for ms.
5904        *   ms    The absolute offset in milliseconds.
5905        *   loc   The locale object, either specified by [locale] or default.
5906        *
5907        * @example
5908        *
5909        *   hourAgo.relative() -> 1 hour ago
5910        *   jan.relative()     -> ex. 5 months ago
5911        *   jan.relative('ja') -> 3ヶ月前
5912        *   jan.relative(function(num, unit, ms, loc) {
5913        *     // Return an absolute date for anything over 6 months.
5914        *     if(unit == 6 && num > 6 || unit > 6) {
5915        *       return '{Month} {d}, {yyyy}';
5916        *     }
5917        *   }); -> ex. 5 months ago
5918        *
5919        * @signature relative([fn])
5920        * @param {string} [localeCode]
5921        * @param {relativeFn} [fn]
5922        * @callbackParam {number} num
5923        * @callbackParam {number} unit
5924        * @callbackParam {number} ms
5925        * @callbackParam {Locale} loc
5926        * @callbackReturns {string} relativeFn
5927        *
5928        ***/
5929        'relative': function(date, localeCode, fn) {
5930            return dateRelative(date, null, localeCode, fn);
5931        },
5932
5933        /***
5934        * @method relativeTo(d, [localeCode] = currentLocaleCode)
5935        * @returns String
5936        * @short Returns the date in a text format relative to `d`, such as
5937        *        "5 minutes".
5938        * @extra `d` will accept a date object, timestamp, or string. [localeCode]
5939        *        applies to the method output, not `d`.
5940        *
5941        * @example
5942        *
5943        *   jan.relativeTo(jul)                 -> 5 months
5944        *   yesterday.relativeTo('today', 'ja') -> 一日
5945        *
5946        * @param {string|number|Date} d
5947        * @param {string} code
5948        *
5949        *
5950        ***/
5951        'relativeTo': function(date, d, localeCode) {
5952            return dateRelative(date, createDate(d), localeCode);
5953        },
5954
5955        /***
5956        * @method is(d, [margin] = 0)
5957        * @returns Boolean
5958        * @short Returns true if the date matches `d`.
5959        * @extra `d` will accept a date object, timestamp, or text format. In the
5960        *        case of objects and text formats, `is` will additionally compare
5961        *        based on the precision implied in the input. In the case of text
5962        *        formats `d` will use the currently set locale. [margin] allows an
5963        *        extra margin of error in milliseconds. See `create` for formats.
5964        *
5965        * @example
5966        *
5967        *   new Date().is('July')               -> true or false?
5968        *   new Date().is('1776')               -> false
5969        *   new Date().is('today')              -> true
5970        *   new Date().is('weekday')            -> true or false?
5971        *   new Date().is('July 4, 1776')       -> false
5972        *   new Date().is(-6106093200000)       -> false
5973        *   new Date().is(new Date(1776, 6, 4)) -> false
5974        *
5975        * @param {string|number|Date} d
5976        * @param {number} [margin]
5977        *
5978        ***/
5979        'is': function(date, d, margin) {
5980            return fullCompareDate(date, d, margin);
5981        },
5982
5983        /***
5984        * @method reset([unit] = 'day', [localeCode] = currentLocaleCode)
5985        * @returns Date
5986        * @short Resets the date to the beginning of [unit].
5987        * @extra This method effectively resets all smaller units, pushing the date
5988        *        to the beginning of [unit]. Default is `day`, which effectively
5989        *        resets the time. [localeCode] is provided for resetting weeks, which
5990        *        is locale dependent. This method modifies the date!
5991        *
5992        * @example
5993        *
5994        *   new Date().reset('day')   -> Beginning of the day
5995        *   new Date().reset('month') -> Beginning of the month
5996        *
5997        * @param {string} [unit]
5998        * @param {string} [localeCode]
5999        *
6000        ***/
6001        'reset': function(date, unit, localeCode) {
6002            var unitIndex = unit ? getUnitIndexForParamName(unit) : DAY_INDEX;
6003            moveToBeginningOfUnit(date, unitIndex, localeCode);
6004            return date;
6005        },
6006
6007        /***
6008        * @method clone()
6009        * @returns Date
6010        * @short Clones the date.
6011        * @extra Note that the UTC flag will be preserved if set. This flag is
6012        *        set via the `setUTC` method or an option on `Date.create`.
6013        *
6014        * @example
6015        *
6016        *   new Date().clone() -> Copy of now
6017        *
6018        ***/
6019        'clone': function(date) {
6020            return cloneDate(date);
6021        },
6022
6023        /***
6024        * @method iso()
6025        * @alias toISOString
6026        *
6027        ***/
6028        'iso': function(date) {
6029            return date.toISOString();
6030        },
6031
6032        /***
6033        * @method getWeekday()
6034        * @returns Number
6035        * @short Alias for `getDay`.
6036        *
6037        * @example
6038        *
6039        *   new Date().getWeekday();    -> (ex.) 3
6040        *
6041        ***/
6042        'getWeekday': function(date) {
6043            return getWeekday(date);
6044        },
6045
6046        /***
6047        * @method getUTCWeekday()
6048        * @returns Number
6049        * @short Alias for `getUTCDay`.
6050        *
6051        * @example
6052        *
6053        *   new Date().getUTCWeekday(); -> (ex.) 3
6054        *
6055        ***/
6056        'getUTCWeekday': function(date) {
6057            return date.getUTCDay();
6058        }
6059
6060    });
6061
6062
6063    /*** @namespace Number ***/
6064
6065    /***
6066    * @method [dateUnit]()
6067    * @returns Number
6068    * @short Takes the number as a unit of time and converts to milliseconds.
6069    * @extra Method names can be singular or plural.  Note that as "a month" is
6070    *        ambiguous as a unit of time, `months` will be equivalent to 30.4375
6071    *        days, the average number in a month. Be careful using `months` if you
6072    *        need exact precision.
6073    *
6074    * @set
6075    *   millisecond
6076    *   milliseconds
6077    *   second
6078    *   seconds
6079    *   minute
6080    *   minutes
6081    *   hour
6082    *   hours
6083    *   day
6084    *   days
6085    *   week
6086    *   weeks
6087    *   month
6088    *   months
6089    *   year
6090    *   years
6091    *
6092    * @example
6093    *
6094    *   (5).milliseconds() -> 5
6095    *   (10).hours()       -> 36000000
6096    *   (1).day()          -> 86400000
6097    *
6098    ***
6099    * @method [dateUnit]Before(d, [options])
6100    * @returns Date
6101    * @short Returns a date that is `n` units before [d], where `n` is the number.
6102    * @extra [d] will accept a date object, timestamp, or text format. Note that
6103    *        "months" is ambiguous as a unit of time. If the target date falls on a
6104    *        day that does not exist (i.e. August 31 -> February 31), the date will
6105    *        be shifted to the last day of the month. Be careful using
6106    *        `monthsBefore` if you need exact precision. [options] can be an object
6107    *        or a locale code as a string. See `create` for more.
6108    *
6109    *
6110    * @set
6111    *   millisecondBefore
6112    *   millisecondsBefore
6113    *   secondBefore
6114    *   secondsBefore
6115    *   minuteBefore
6116    *   minutesBefore
6117    *   hourBefore
6118    *   hoursBefore
6119    *   dayBefore
6120    *   daysBefore
6121    *   weekBefore
6122    *   weeksBefore
6123    *   monthBefore
6124    *   monthsBefore
6125    *   yearBefore
6126    *   yearsBefore
6127    *
6128    * @example
6129    *
6130    *   (5).daysBefore('tuesday')          -> 5 days before tuesday of this week
6131    *   (1).yearBefore('January 23, 1997') -> January 23, 1996
6132    *
6133    * @param {string|number|Date} d
6134    * @param {DateCreateOptions} options
6135    *
6136    ***
6137    * @method [dateUnit]Ago()
6138    * @returns Date
6139    * @short Returns a date that is `n` units ago.
6140    * @extra Note that "months" is ambiguous as a unit of time. If the target date
6141    *        falls on a day that does not exist (i.e. August 31 -> February 31), the
6142    *        date will be shifted to the last day of the month. Be careful using
6143    *        `monthsAgo` if you need exact precision.
6144    *
6145    * @set
6146    *   millisecondAgo
6147    *   millisecondsAgo
6148    *   secondAgo
6149    *   secondsAgo
6150    *   minuteAgo
6151    *   minutesAgo
6152    *   hourAgo
6153    *   hoursAgo
6154    *   dayAgo
6155    *   daysAgo
6156    *   weekAgo
6157    *   weeksAgo
6158    *   monthAgo
6159    *   monthsAgo
6160    *   yearAgo
6161    *   yearsAgo
6162    *
6163    * @example
6164    *
6165    *   (5).weeksAgo() -> 5 weeks ago
6166    *   (1).yearAgo()  -> January 23, 1996
6167    *
6168    ***
6169    * @method [dateUnit]After(d, [options])
6170    * @returns Date
6171    * @short Returns a date `n` units after [d], where `n` is the number.
6172    * @extra [d] will accept a date object, timestamp, or text format. Note that
6173    *        "months" is ambiguous as a unit of time. If the target date falls on a
6174    *        day that does not exist (i.e. August 31 -> February 31), the date will
6175    *        be shifted to the last day of the month. Be careful using
6176    *        `monthsAfter` if you need exact precision. [options] can be an object
6177    *        or a locale code as a string. See `create` for more.
6178    *
6179    * @set
6180    *   millisecondAfter
6181    *   millisecondsAfter
6182    *   secondAfter
6183    *   secondsAfter
6184    *   minuteAfter
6185    *   minutesAfter
6186    *   hourAfter
6187    *   hoursAfter
6188    *   dayAfter
6189    *   daysAfter
6190    *   weekAfter
6191    *   weeksAfter
6192    *   monthAfter
6193    *   monthsAfter
6194    *   yearAfter
6195    *   yearsAfter
6196    *
6197    * @example
6198    *
6199    *   (5).daysAfter('tuesday')          -> 5 days after tuesday of this week
6200    *   (1).yearAfter('January 23, 1997') -> January 23, 1998
6201    *
6202    * @param {string|number|Date} d
6203    * @param {DateCreateOptions} options
6204    *
6205    ***
6206    * @method [dateUnit]FromNow()
6207    * @returns Date
6208    * @short Returns a date `n` units from now.
6209    * @extra Note that "months" is ambiguous as a unit of time. If the target date
6210    *        falls on a day that does not exist (i.e. August 31 -> February 31), the
6211    *        date will be shifted to the last day of the month. Be careful using
6212    *        `monthsFromNow` if you need exact precision.
6213    *
6214    * @set
6215    *   millisecondFromNow
6216    *   millisecondsFromNow
6217    *   secondFromNow
6218    *   secondsFromNow
6219    *   minuteFromNow
6220    *   minutesFromNow
6221    *   hourFromNow
6222    *   hoursFromNow
6223    *   dayFromNow
6224    *   daysFromNow
6225    *   weekFromNow
6226    *   weeksFromNow
6227    *   monthFromNow
6228    *   monthsFromNow
6229    *   yearFromNow
6230    *   yearsFromNow
6231    *
6232    * @example
6233    *
6234    *   (5).weeksFromNow() -> 5 weeks ago
6235    *   (1).yearFromNow()  -> January 23, 1998
6236    *
6237    ***/
6238    function buildNumberUnitMethods() {
6239        defineInstanceSimilar(sugarNumber, DateUnits, function(methods, unit) {
6240            var name = unit.name, base, after, before;
6241            base = function(n) {
6242                return round(n * unit.multiplier);
6243            };
6244            after = function(n, d, options) {
6245                return advanceDate(createDate(d, options, true), name, n);
6246            };
6247            before = function(n, d, options) {
6248                return advanceDate(createDate(d, options, true), name, -n);
6249            };
6250            methods[name] = base;
6251            methods[name + 's'] = base;
6252            methods[name + 'Before'] = before;
6253            methods[name + 'sBefore'] = before;
6254            methods[name + 'Ago'] = before;
6255            methods[name + 'sAgo'] = before;
6256            methods[name + 'After'] = after;
6257            methods[name + 'sAfter'] = after;
6258            methods[name + 'FromNow'] = after;
6259            methods[name + 'sFromNow'] = after;
6260        });
6261    }
6262
6263    defineInstance(sugarNumber, {
6264
6265        /***
6266        * @method duration([localeCode] = currentLocaleCode)
6267        * @returns String
6268        * @short Takes the number as milliseconds and returns a localized string.
6269        * @extra This method is the same as `Date#relative` without the localized
6270        *        equivalent of "from now" or "ago". [localeCode] can be passed as the
6271        *        first (and only) parameter. Note that this method is only available
6272        *        when the dates module is included.
6273        *
6274        * @example
6275        *
6276        *   (500).duration() -> '500 milliseconds'
6277        *   (1200).duration() -> '1 second'
6278        *   (75).minutes().duration() -> '1 hour'
6279        *   (75).minutes().duration('es') -> '1 hora'
6280        *
6281        * @param {string} [localeCode]
6282        *
6283        ***/
6284        'duration': function(n, localeCode) {
6285            return localeManager.get(localeCode).getDuration(n);
6286        }
6287
6288    });
6289
6290
6291    var EnglishLocaleBaseDefinition = {
6292        'code': 'en',
6293        'plural': true,
6294        'timeMarkers': 'at',
6295        'ampm': 'AM|A.M.|a,PM|P.M.|p',
6296        'units': 'millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s',
6297        'months': 'Jan:uary|,Feb:ruary|,Mar:ch|,Apr:il|,May,Jun:e|,Jul:y|,Aug:ust|,Sep:tember|t|,Oct:ober|,Nov:ember|,Dec:ember|',
6298        'weekdays': 'Sun:day|,Mon:day|,Tue:sday|,Wed:nesday|,Thu:rsday|,Fri:day|,Sat:urday|+weekend',
6299        'numerals': 'zero,one|first,two|second,three|third,four:|th,five|fifth,six:|th,seven:|th,eight:|h,nin:e|th,ten:|th',
6300        'articles': 'a,an,the',
6301        'tokens': 'the,st|nd|rd|th,of|in,a|an,on',
6302        'time': '{H}:{mm}',
6303        'past': '{num} {unit} {sign}',
6304        'future': '{num} {unit} {sign}',
6305        'duration': '{num} {unit}',
6306        'modifiers': [
6307      { 'name': 'half', 'src': 'half', 'value': .5 },
6308      { 'name': 'midday', 'src': 'noon', 'value': 12 },
6309      { 'name': 'midday', 'src': 'midnight', 'value': 24 },
6310      { 'name': 'day', 'src': 'yesterday', 'value': -1 },
6311      { 'name': 'day', 'src': 'today|tonight', 'value': 0 },
6312      { 'name': 'day', 'src': 'tomorrow', 'value': 1 },
6313      { 'name': 'sign', 'src': 'ago|before', 'value': -1 },
6314      { 'name': 'sign', 'src': 'from now|after|from|in|later', 'value': 1 },
6315      { 'name': 'edge', 'src': 'first day|first|beginning', 'value': -2 },
6316      { 'name': 'edge', 'src': 'last day', 'value': 1 },
6317      { 'name': 'edge', 'src': 'end|last', 'value': 2 },
6318      { 'name': 'shift', 'src': 'last', 'value': -1 },
6319      { 'name': 'shift', 'src': 'the|this', 'value': 0 },
6320      { 'name': 'shift', 'src': 'next', 'value': 1 }
6321    ],
6322        'parse': [
6323      '(?:just)? now',
6324      '{shift} {unit:5-7}',
6325      "{months?} (?:{year}|'{yy})",
6326      '{midday} {4?} {day|weekday}',
6327      '{months},?(?:[-.\\/\\s]{year})?',
6328      '{edge} of (?:day)? {day|weekday}',
6329      '{0} {num}{1?} {weekday} {2} {months},? {year?}',
6330      '{shift?} {day?} {weekday?} {timeMarker?} {midday}',
6331      '{sign?} {3?} {half} {3?} {unit:3-4|unit:7} {sign?}',
6332      '{0?} {edge} {weekday?} {2} {shift?} {unit:4-7?} {months?},? {year?}'
6333    ],
6334        'timeParse': [
6335      '{day|weekday}',
6336      '{shift} {unit:5?} {weekday}',
6337      '{0?} {date}{1?} {2?} {months?}',
6338      '{weekday} {2?} {shift} {unit:5}',
6339      '{0?} {num} {2?} {months}\\.?,? {year?}',
6340      '{num?} {unit:4-5} {sign} {day|weekday}',
6341      '{year}[-.\\/\\s]{months}[-.\\/\\s]{date}',
6342      '{0|months} {date?}{1?} of {shift} {unit:6-7}',
6343      '{0?} {num}{1?} {weekday} of {shift} {unit:6}',
6344      "{date}[-.\\/\\s]{months}[-.\\/\\s](?:{year}|'?{yy}
6344)",
6345      "{weekday?}\\.?,? {months}\\.?,? {date}{1?},? (?:{year}|'{yy})?"
6346    ],
6347        'timeFrontParse': [
6348      '{sign} {num} {unit}',
6349      '{num} {unit} {sign}',
6350      '{4?} {day|weekday}'
6351    ]
6352    };
6353
6354    var AmericanEnglishDefinition = getEnglishVariant({
6355        'mdy': true,
6356        'firstDayOfWeek': 0,
6357        'firstDayOfWeekYear': 1,
6358        'short': '{MM}/{dd}/{yyyy}',
6359        'medium': '{Month} {d}, {yyyy}',
6360        'long': '{Month} {d}, {yyyy} {time}',
6361        'full': '{Weekday}, {Month} {d}, {yyyy} {time}',
6362        'stamp': '{Dow} {Mon} {d} {yyyy} {time}',
6363        'time': '{h}:{mm} {TT}'
6364    });
6365
6366    var BritishEnglishDefinition = getEnglishVariant({
6367        'short': '{dd}/{MM}/{yyyy}',
6368        'medium': '{d} {Month} {yyyy}',
6369        'long': '{d} {Month} {yyyy} {H}:{mm}',
6370        'full': '{Weekday}, {d} {Month}, {yyyy} {time}',
6371        'stamp': '{Dow} {d} {Mon} {yyyy} {time}'
6372    });
6373
6374    var CanadianEnglishDefinition = getEnglishVariant({
6375        'short': '{yyyy}-{MM}-{dd}',
6376        'medium': '{d} {Month}, {yyyy}',
6377        'long': '{d} {Month}, {yyyy} {H}:{mm}',
6378        'full': '{Weekday}, {d} {Month}, {yyyy} {time}',
6379        'stamp': '{Dow} {d} {Mon} {yyyy} {time}'
6380    });
6381
6382    var LazyLoadedLocales = {
6383        'en-US': AmericanEnglishDefinition,
6384        'en-GB': BritishEnglishDefinition,
6385        'en-AU': BritishEnglishDefinition,
6386        'en-CA': CanadianEnglishDefinition
6387    };
6388
6389    buildLocales();
6390    buildDateFormatTokens();
6391    buildDateFormatMatcher();
6392    buildDateUnitMethods();
6393    buildNumberUnitMethods();
6394    buildRelativeAliases();
6395    setDateChainableConstructor();
6396
6397    /***
6398    * @module String
6399    * @description String manupulation, encoding, truncation, and formatting, and more.
6400    *
6401    ***/
6402
6403    // Flag allowing native string methods to be enhanced
6404    var STRING_ENHANCEMENTS_FLAG = 'enhanceString';
6405
6406    // Matches non-punctuation characters except apostrophe for capitalization.
6407    var CAPITALIZE_REG = /[^\u0000-\u0040\u005B-\u0060\u007B-\u007F]+('s)?/g;
6408
6409    // Regex matching camelCase.
6410    var CAMELIZE_REG = /(^|_)([^_]+)/g;
6411
6412    // Regex matching any HTML entity.
6413    var HTML_ENTITY_REG = /&#?(x)?([\w\d]{0,5});/gi;
6414
6415    // Very basic HTML escaping regex.
6416    var HTML_ESCAPE_REG = /[&<>]/g;
6417
6418    // Special HTML entities.
6419    var HTMLFromEntityMap = {
6420        'lt': '<',
6421        'gt': '>',
6422        'amp': '&',
6423        'nbsp': ' ',
6424        'quot': '"',
6425        'apos': "'"
6426    };
6427
6428    var HTMLToEntityMap;
6429
6430    // Words that should not be capitalized in titles
6431    var DOWNCASED_WORDS = [
6432    'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at',
6433    'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over',
6434    'with', 'for'
6435  ];
6436
6437    // HTML tags that do not have inner content.
6438    var HTML_VOID_ELEMENTS = [
6439    'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img',
6440    'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'
6441  ];
6442
6443    var LEFT_TRIM_REG = RegExp('^[' + TRIM_CHARS + ']+');
6444    var RIGHT_TRIM_REG = RegExp('[' + TRIM_CHARS + ']+$');
6445    var TRUNC_REG = RegExp('(?=[' + TRIM_CHARS + '])');
6446
6447    // Reference to native String#includes to enhance later.
6448    var nativeIncludes = String.prototype.includes;
6449
6450    // Base64
6451    var encodeBase64, decodeBase64;
6452
6453    // Format matcher for String#format.
6454    var stringFormatMatcher = createFormatMatcher(deepGetProperty);
6455
6456    function padString(num, padding) {
6457        return repeatString(isDefined(padding) ? padding : ' ', num);
6458    }
6459
6460    function truncateString(str, length, from, ellipsis, split) {
6461        var str1, str2, len1, len2;
6462        if (str.length <= length) {
6463            return str.toString();
6464        }
6465        ellipsis = isUndefined(ellipsis) ? '...' : ellipsis;
6466        switch (from) {
6467            case 'left':
6468                str2 = split ? truncateOnWord(str, length, true) : str.slice(str.length - length);
6469                return ellipsis + str2;
6470            case 'middle':
6471                len1 = ceil(length / 2);
6472                len2 = floor(length / 2);
6473                str1 = split ? truncateOnWord(str, len1) : str.slice(0, len1);
6474                str2 = split ? truncateOnWord(str, len2, true) : str.slice(str.length - len2);
6475                return str1 + ellipsis + str2;
6476            default:
6477                str1 = split ? truncateOnWord(str, length) : str.slice(0, length);
6478                return str1 + ellipsis;
6479        }
6480    }
6481
6482    function stringEach(str, search, fn) {
6483        var chunks, chunk, reg, result = [];
6484        if (isFunction(search)) {
6485            fn = search;
6486            reg = /[\s\S]/g;
6487        } else if (!search) {
6488            reg = /[\s\S]/g;
6489        } else if (isString(search)) {
6490            reg = RegExp(escapeRegExp(search), 'gi');
6491        } else if (isRegExp(search)) {
6492            reg = RegExp(search.source, getRegExpFlags(search, 'g'));
6493        }
6494        // Getting the entire array of chunks up front as we need to
6495        // pass this into the callback function as an argument.
6496        chunks = runGlobalMatch(str, reg);
6497
6498        if (chunks) {
6499            for (var i = 0, len = chunks.length, r; i < len; i++) {
6500                chunk = chunks[i];
6501                result[i] = chunk;
6502                if (fn) {
6503                    r = fn.call(str, chunk, i, chunks);
6504                    if (r === false) {
6505                        break;
6506                    } else if (isDefined(r)) {
6507                        result[i] = r;
6508                    }
6509                }
6510            }
6511        }
6512        return result;
6513    }
6514
6515    // "match" in < IE9 has enumable properties that will confuse for..in
6516    // loops, so ensure that the match is a normal array by manually running
6517    // "exec". Note that this method is also slightly more performant.
6518    function runGlobalMatch(str, reg) {
6519        var result = [], match, lastLastIndex;
6520        while ((match = reg.exec(str)) != null) {
6521            if (reg.lastIndex === lastLastIndex) {
6522                reg.lastIndex += 1;
6523            } else {
6524                result.push(match[0]);
6525            }
6526            lastLastIndex = reg.lastIndex;
6527        }
6528        return result;
6529    }
6530
6531    function eachWord(str, fn) {
6532        return stringEach(trim(str), /\S+/g, fn);
6533    }
6534
6535    function stringCodes(str, fn) {
6536        var codes = new Array(str.length), i, len;
6537        for (i = 0, len = str.length; i < len; i++) {
6538            var code = str.charCodeAt(i);
6539            codes[i] = code;
6540            if (fn) {
6541                fn.call(str, code, i, str);
6542            }
6543        }
6544        return codes;
6545    }
6546
6547    function stringUnderscore(str) {
6548        var areg = Inflections.acronyms && Inflections.acronyms.reg;
6549        return str
6550      .replace(/[-\s]+/g, '_')
6551      .replace(areg, function(acronym, index) {
6552          return (index > 0 ? '_' : '') + acronym.toLowerCase();
6553      })
6554      .replace(/([A-Z\d]+)([A-Z][a-z])/g, '$1_$2')
6555      .replace(/([a-z\d])([A-Z])/g, '$1_$2')
6556      .toLowerCase();
6557    }
6558
6559    function stringCamelize(str, upper) {
6560        str = stringUnderscore(str);
6561        return str.replace(CAMELIZE_REG, function(match, pre, word, index) {
6562            var cap = upper !== false || index > 0, acronym;
6563            acronym = getAcronym(word);
6564            if (acronym && cap) {
6565                return acronym;
6566            }
6567            return cap ? stringCapitalize(word, true) : word;
6568        });
6569    }
6570
6571    function stringSpacify(str) {
6572        return stringUnderscore(str).replace(/_/g, ' ');
6573    }
6574
6575    function stringCapitalize(str, downcase, all) {
6576        if (downcase) {
6577            str = str.toLowerCase();
6578        }
6579        return all ? str.replace(CAPITALIZE_REG, simpleCapitalize) : simpleCapitalize(str);
6580    }
6581
6582    function stringTitleize(str) {
6583        var fullStopPunctuation = /[.:;!]$/, lastHadPunctuation;
6584        str = runHumanRules(str);
6585        str = stringSpacify(str);
6586        return eachWord(str, function(word, index, words) {
6587            word = getHumanWord(word) || word;
6588            word = getAcronym(word) || word;
6589            var hasPunctuation, isFirstOrLast;
6590            var first = index == 0, last = index == words.length - 1;
6591            hasPunctuation = fullStopPunctuation.test(word);
6592            isFirstOrLast = first || last || hasPunctuation || lastHadPunctuation;
6593            lastHadPunctuation = hasPunctuation;
6594            if (isFirstOrLast || indexOf(DOWNCASED_WORDS, word) === -1) {
6595                return stringCapitalize(word, false, true);
6596            } else {
6597                return word;
6598            }
6599        }).join(' ');
6600    }
6601
6602    function stringParameterize(str, separator) {
6603        if (separator === undefined) separator = '-';
6604        str = str.replace(/[^a-z0-9\-_]+/gi, separator);
6605        if (separator) {
6606            var reg = RegExp('^{s}+|{s}+$|({s}){s}+'.split('{s}').join(escapeRegExp(separator)), 'g');
6607            str = str.replace(reg, '$1');
6608        }
6609        return encodeURI(str.toLowerCase());
6610    }
6611
6612    function reverseString(str) {
6613        return str.split('').reverse().join('');
6614    }
6615
6616    function truncateOnWord(str, limit, fromLeft) {
6617        if (fromLeft) {
6618            return reverseString(truncateOnWord(reverseString(str), limit));
6619        }
6620        var words = str.split(TRUNC_REG);
6621        var count = 0;
6622        return filter(words, function(word) {
6623            count += word.length;
6624            return count <= limit;
6625        }).join('');
6626    }
6627
6628    function unescapeHTML(str) {
6629        return str.replace(HTML_ENTITY_REG, function(full, hex, code) {
6630            var special = HTMLFromEntityMap[code];
6631            return special || chr(hex ? parseInt(code, 16) : +code);
6632        });
6633    }
6634
6635    function tagIsVoid(tag) {
6636        return indexOf(HTML_VOID_ELEMENTS, tag.toLowerCase()) !== -1;
6637    }
6638
6639    function stringReplaceAll(str, f, replace) {
6640        var i = 0, tokens;
6641        if (isString(f)) {
6642            f = RegExp(escapeRegExp(f), 'g');
6643        } else if (f && !f.global) {
6644            f = RegExp(f.source, getRegExpFlags(f, 'g'));
6645        }
6646        if (!replace) {
6647            replace = '';
6648        } else {
6649            tokens = replace;
6650            replace = function() {
6651                var t = tokens[i++];
6652                return t != null ? t : '';
6653            };
6654        }
6655        return str.replace(f, replace);
6656    }
6657
6658    function replaceTags(str, find, replacement, strip) {
6659        var tags = isString(find) ? [find] : find, reg, src;
6660        tags = map(tags || [], function(t) {
6661            return escapeRegExp(t);
6662        }).join('|');
6663        src = tags.replace('all', '') || '[^\\s>]+';
6664        src = '<(\\/)?(' + src + ')(\\s+[^<>]*?)?\\s*(\\/)?>';
6665        reg = RegExp(src, 'gi');
6666        return runTagReplacements(str.toString(), reg, strip, replacement);
6667    }
6668
6669    function runTagReplacements(str, reg, strip, replacement, fullString) {
6670
6671        var match;
6672        var result = '';
6673        var currentIndex = 0;
6674        var openTagName;
6675        var openTagAttributes;
6676        var openTagCount = 0;
6677
6678        function processTag(index, tagName, attributes, tagLength, isVoid) {
6679            var content = str.slice(currentIndex, index), s = '', r = '';
6680            if (isString(replacement)) {
6681                r = replacement;
6682            } else if (replacement) {
6683                r = replacement.call(fullString, tagName, content, attributes, fullString) || '';
6684            }
6685            if (strip) {
6686                s = r;
6687            } else {
6688                content = r;
6689            }
6690            if (content) {
6691                content = runTagReplacements(content, reg, strip, replacement, fullString);
6692            }
6693            result += s + content + (isVoid ? '' : s);
6694            currentIndex = index + (tagLength || 0);
6695        }
6696
6697        fullString = fullString || str;
6698        reg = RegExp(reg.source, 'gi');
6699
6700        while (match = reg.exec(str)) {
6701
6702            var tagName = match[2];
6703            var attributes = (match[3] || '').slice(1);
6704            var isClosingTag = !!match[1];
6705            var isSelfClosing = !!match[4];
6706            var tagLength = match[0].length;
6707            var isVoid = tagIsVoid(tagName);
6708            var isOpeningTag = !isClosingTag && !isSelfClosing && !isVoid;
6709            var isSameAsCurrent = tagName === openTagName;
6710
6711            if (!openTagName) {
6712                result += str.slice(currentIndex, match.index);
6713                currentIndex = match.index;
6714            }
6715
6716            if (isOpeningTag) {
6717                if (!openTagName) {
6718                    openTagName = tagName;
6719                    openTagAttributes = attributes;
6720                    openTagCount++;
6721                    currentIndex += tagLength;
6722                } else if (isSameAsCurrent) {
6723                    openTagCount++;
6724                }
6725            } else if (isClosingTag && isSameAsCurrent) {
6726                openTagCount--;
6727                if (openTagCount === 0) {
6728                    processTag(match.index, openTagName, openTagAttributes, tagLength, isVoid);
6729                    openTagName = null;
6730                    openTagAttributes = null;
6731                }
6732            } else if (!openTagName) {
6733                processTag(match.index, tagName, attributes, tagLength, isVoid);
6734            }
6735        }
6736        if (openTagName) {
6737            processTag(str.length, openTagName, openTagAttributes);
6738        }
6739        result += str.slice(currentIndex);
6740        return result;
6741    }
6742
6743    function numberOrIndex(str, n, from) {
6744        if (isString(n)) {
6745            n = str.indexOf(n);
6746            if (n === -1) {
6747                n = from ? str.length : 0;
6748            }
6749        }
6750        return n;
6751    }
6752
6753    function buildBase64() {
6754        var encodeAscii, decodeAscii;
6755
6756        function catchEncodingError(fn) {
6757            return function(str) {
6758                try {
6759                    return fn(str);
6760                } catch (e) {
6761                    return '';
6762                }
6763            };
6764        }
6765
6766        if (typeof Buffer !== 'undefined') {
6767            encodeBase64 = function(str) {
6768                return new Buffer(str).toString('base64');
6769            };
6770            decodeBase64 = function(str) {
6771                return new Buffer(str, 'base64').toString('utf8');
6772            };
6773            return;
6774        }
6775        if (typeof btoa !== 'undefined') {
6776            encodeAscii = catchEncodingError(btoa);
6777            decodeAscii = catchEncodingError(atob);
6778        } else {
6779            var key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
6779+/=';
6780            var base64reg = /[^A-Za-z0-9\+\/\=]/g;
6781            encodeAscii = function(str) {
6782                var output = '';
6783                var chr1, chr2, chr3;
6784                var enc1, enc2, enc3, enc4;
6785                var i = 0;
6786                do {
6787                    chr1 = str.charCodeAt(i++);
6788                    chr2 = str.charCodeAt(i++);
6789                    chr3 = str.charCodeAt(i++);
6790                    enc1 = chr1 >> 2;
6791                    enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
6792                    enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
6793                    enc4 = chr3 & 63;
6794                    if (isNaN(chr2)) {
6795                        enc3 = enc4 = 64;
6796                    } else if (isNaN(chr3)) {
6797                        enc4 = 64;
6798                    }
6799                    output += key.charAt(enc1);
6800                    output += key.charAt(enc2);
6801                    output += key.charAt(enc3);
6802                    output += key.charAt(enc4);
6803                    chr1 = chr2 = chr3 = '';
6804                    enc1 = enc2 = enc3 = enc4 = '';
6805                } while (i < str.length);
6806                return output;
6807            };
6808            decodeAscii = function(input) {
6809                var output = '';
6810                var chr1, chr2, chr3;
6811                var enc1, enc2, enc3, enc4;
6812                var i = 0;
6813                if (input.match(base64reg)) {
6814                    return '';
6815                }
6816                input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
6817                do {
6818                    enc1 = key.indexOf(input.charAt(i++));
6819                    enc2 = key.indexOf(input.charAt(i++));
6820                    enc3 = key.indexOf(input.charAt(i++));
6821                    enc4 = key.indexOf(input.charAt(i++));
6822                    chr1 = (enc1 << 2) | (enc2 >> 4);
6823                    chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
6824                    chr3 = ((enc3 & 3) << 6) | enc4;
6825                    output = output + chr(chr1);
6826                    if (enc3 != 64) {
6827                        output = output + chr(chr2);
6828                    }
6829                    if (enc4 != 64) {
6830                        output = output + chr(chr3);
6831                    }
6832                    chr1 = chr2 = chr3 = '';
6833                    enc1 = enc2 = enc3 = enc4 = '';
6834                } while (i < input.length);
6835                return output;
6836            };
6837        }
6838        encodeBase64 = function(str) {
6839            return encodeAscii(unescape(encodeURIComponent(str)));
6840        };
6841        decodeBase64 = function(str) {
6842            return decodeURIComponent(escape(decodeAscii(str)));
6843        };
6844    }
6845
6846    function buildEntities() {
6847        HTMLToEntityMap = {};
6848        forEachProperty(HTMLFromEntityMap, function(val, key) {
6849            HTMLToEntityMap[val] = '&' + key + ';';
6850        });
6851    }
6852
6853    function callIncludesWithRegexSupport(str, search, position) {
6854        if (!isRegExp(search)) {
6855            return nativeIncludes.call(str, search, position);
6856        }
6857        if (position) {
6858            str = str.slice(position);
6859        }
6860        return search.test(str);
6861    }
6862
6863    defineInstance(sugarString, {
6864
6865        // Enhancment to String#includes to allow a regex.
6866        'includes': fixArgumentLength(callIncludesWithRegexSupport)
6867
6868    }, [ENHANCEMENTS_FLAG, STRING_ENHANCEMENTS_FLAG]);
6869
6870    defineInstance(sugarString, {
6871
6872        /***
6873        * @method at(index, [loop] = false)
6874        * @returns Mixed
6875        * @short Gets the character(s) at a given index.
6876        * @extra When [loop] is true, overshooting the end of the string will begin
6877        *        counting from the other end. `index` may be negative. If `index` is
6878        *        an array, multiple elements will be returned.
6879        * @example
6880        *
6881        *   'jumpy'.at(0)             -> 'j'
6882        *   'jumpy'.at(2)             -> 'm'
6883        *   'jumpy'.at(5)             -> ''
6884        *   'jumpy'.at(5, true)       -> 'j'
6885        *   'jumpy'.at(-1)            -> 'y'
6886        *   'lucky charms'.at([2, 4]) -> ['u','k']
6887        *
6888        * @param {number|Array<number>} index
6889        * @param {boolean} [loop]
6890        *
6891        ***/
6892        'at': function(str, index, loop) {
6893            return getEntriesForIndexes(str, index, loop, true);
6894        },
6895
6896        /***
6897        * @method escapeURL([param] = false)
6898        * @returns String
6899        * @short Escapes characters in a string to make a valid URL.
6900        * @extra If [param] is true, it will also escape valid URL characters. Use
6901        *        this when the entire string is meant for use in a query string.
6902        *
6903        * @example
6904        *
6905        *   'a, b, and c'.escapeURL() -> 'a,%20b,%20and%20c'
6906        *   'http://foo.com/'.escapeURL(true) -> 'http%3A%2F%2Ffoo.com%2F'
6907        *
6908        * @param {boolean} [param]
6909        *
6910        ***/
6911        'escapeURL': function(str, param) {
6912            return param ? encodeURIComponent(str) : encodeURI(str);
6913        },
6914
6915        /***
6916        * @method unescapeURL([partial] = false)
6917        * @returns String
6918        * @short Restores escaped characters in a URL escaped string.
6919        * @extra If [partial] is true, it will only unescape non-valid URL tokens,
6920        *        and is included here for completeness, but should be rarely needed.
6921        *
6922        * @example
6923        *
6924        *   'http%3A%2F%2Ffoo.com%2F'.unescapeURL()     -> 'http://foo.com/'
6925        *   'http%3A%2F%2Ffoo.com%2F'.unescapeURL(true) -> 'http%3A%2F%2Ffoo.com%2F'
6926        *
6927        * @param {boolean} [partial]
6928        *
6929        ***/
6930        'unescapeURL': function(str, param) {
6931            return param ? decodeURI(str) : decodeURIComponent(str);
6932        },
6933
6934        /***
6935        * @method escapeHTML()
6936        * @returns String
6937        * @short Converts HTML characters to their entity equivalents.
6938        *
6939        * @example
6940        *
6941        *   '<p>some text</p>'.escapeHTML() -> '&lt;p&gt;some text&lt;/p&gt;'
6942        *   'one & two'.escapeHTML()        -> 'one &amp; two'
6943        *
6944        ***/
6945        'escapeHTML': function(str) {
6946            return str.replace(HTML_ESCAPE_REG, function(chr) {
6947                return getOwn(HTMLToEntityMap, chr);
6948            });
6949        },
6950
6951        /***
6952        * @method unescapeHTML()
6953        * @returns String
6954        * @short Restores escaped HTML characters.
6955        *
6956        * @example
6957        *
6958        *   '&lt;p&gt;some text&lt;/p&gt;'.unescapeHTML() -> '<p>some text</p>'
6959        *   'one &amp; two'.unescapeHTML()                -> 'one & two'
6960        *
6961        ***/
6962        'unescapeHTML': function(str) {
6963            return unescapeHTML(str);
6964        },
6965
6966        /***
6967        * @method stripTags([tag] = 'all', [replace])
6968        * @returns String
6969        * @short Strips HTML tags from the string.
6970        * @extra [tag] may be an array of tags or 'all', in which case all tags will
6971        *        be stripped. [replace] will replace what was stripped, and may be a
6972        *        string or a function to handle replacements. If this function returns
6973        *        a string, then it will be used for the replacement. If it returns
6974        *        `undefined`, the tags will be stripped normally.
6975        *
6976        * @callback tagReplaceFn
6977        *
6978        *   tag     The tag name.
6979        *   inner   The tag content.
6980        *   attr    The attributes on the tag, if any, as a string.
6981        *   outer   The entire matched tag string.
6982        *
6983        * @example
6984        *
6985        *   '<p>just <b>some</b> text</p>'.stripTags()    -> 'just some text'
6986        *   '<p>just <b>some</b> text</p>'.stripTags('p') -> 'just <b>some</b> text'
6987        *   '<p>hi!</p>'.stripTags('p', function(all, content) {
6988        *     return '|';
6989        *   }); -> '|hi!|'
6990        *
6991        * @param {string} tag
6992        * @param {string|tagReplaceFn} replace
6993        * @callbackParam {string} tag
6994        * @callbackParam {string} inner
6995        * @callbackParam {string} attr
6996        * @callbackParam {string} outer
6997        * @callbackReturns {string} tagReplaceFn
6998        *
6999        ***/
7000        'stripTags': function(str, tag, replace) {
7001            return replaceTags(str, tag, replace, true);
7002        },
7003
7004        /***
7005        * @method removeTags([tag] = 'all', [replace])
7006        * @returns String
7007        * @short Removes HTML tags and their contents from the string.
7008        * @extra [tag] may be an array of tags or 'all', in which case all tags will
7009        *        be removed. [replace] will replace what was removed, and may be a
7010        *        string or a function to handle replacements. If this function returns
7011        *        a string, then it will be used for the replacement. If it returns
7012        *        `undefined`, the tags will be removed normally.
7013        *
7014        * @callback tagReplaceFn
7015        *
7016        *   tag     The tag name.
7017        *   inner   The tag content.
7018        *   attr    The attributes on the tag, if any, as a string.
7019        *   outer   The entire matched tag string.
7020        *
7021        * @example
7022        *
7023        *   '<p>just <b>some</b> text</p>'.removeTags()    -> ''
7024        *   '<p>just <b>some</b> text</p>'.removeTags('b') -> '<p>just text</p>'
7025        *   '<p>hi!</p>'.removeTags('p', function(all, content) {
7026        *     return 'bye!';
7027        *   }); -> 'bye!'
7028        *
7029        * @param {string} tag
7030        * @param {string|tagReplaceFn} replace
7031        * @callbackParam {string} tag
7032        * @callbackParam {string} inner
7033        * @callbackParam {string} attr
7034        * @callbackParam {string} outer
7035        * @callbackReturns {string} tagReplaceFn
7036        *
7037        ***/
7038        'removeTags': function(str, tag, replace) {
7039            return replaceTags(str, tag, replace, false);
7040        },
7041
7042        /***
7043        * @method encodeBase64()
7044        * @returns String
7045        * @short Encodes the string into base64 encoding.
7046        * @extra This method wraps native methods when available, and uses a custom
7047        *        implementation when not available. It can also handle Unicode
7048        *        string encodings.
7049        *
7050        * @example
7051        *
7052        *   'gonna get encoded!'.encodeBase64()  -> 'Z29ubmEgZ2V0IGVuY29kZWQh'
7053        *   'http://twitter.com/'.encodeBase64() -> 'aHR0cDovL3R3aXR0ZXIuY29tLw=='
7054        *
7055        ***/
7056        'encodeBase64': function(str) {
7057            return encodeBase64(str);
7058        },
7059
7060        /***
7061        * @method decodeBase64()
7062        * @returns String
7063        * @short Decodes the string from base64 encoding.
7064        * @extra This method wraps native methods when available, and uses a custom
7065        *        implementation when not available. It can also handle Unicode string
7066        *        encodings.
7067        *
7068        * @example
7069        *
7070        *   'aHR0cDovL3R3aXR0ZXIuY29tLw=='.decodeBase64() -> 'http://twitter.com/'
7071        *   'anVzdCBnb3QgZGVjb2RlZA=='.decodeBase64()     -> 'just got decoded!'
7072        *
7073        ***/
7074        'decodeBase64': function(str) {
7075            return decodeBase64(str);
7076        },
7077
7078        /***
7079        * @method forEach([search], [callback])
7080        * @returns Array
7081        * @short Runs callback [fn] against every character in the string, or every
7082        *        every occurence of [search] if it is provided.
7083        * @extra Returns an array of matches. [search] may be either a string or
7084        *        regex, and defaults to every character in the string. If [fn]
7085        *        returns false at any time it will break out of the loop.
7086        *
7087        * @callback stringEachFn
7088        *
7089        *   match  The current match.
7090        *   i      The current index.
7091        *   arr    An array of all matches.
7092        *
7093        * @example
7094        *
7095        *   'jumpy'.forEach(log)     -> ['j','u','m','p','y']
7096        *   'jumpy'.forEach(/[r-z]/) -> ['u','y']
7097        *   'jumpy'.forEach(/mp/)    -> ['mp']
7098        *   'jumpy'.forEach(/[r-z]/, function(m) {
7099        *     // Called twice: "u", "y"
7100        *   });
7101        *
7102        * @signature forEach(callback)
7103        * @param {string|RegExp} [search]
7104        * @param {stringEachFn} [callback]
7105        * @callbackParam {string} match
7106        * @callbackParam {number} i
7107        * @callbackParam {Array<string>} arr
7108        *
7109        ***/
7110        'forEach': function(str, search, fn) {
7111            return stringEach(str, search, fn);
7112        },
7113
7114        /***
7115        * @method chars([callback])
7116        * @returns Array
7117        * @short Runs [fn] against each character in the string, and returns an array.
7118        *
7119        * @callback eachCharFn
7120        *
7121        *   char  The current character.
7122        *   i     The current index.
7123        *   arr   An array of all characters.
7124        *
7125        * @example
7126        *
7127        *   'jumpy'.chars() -> ['j','u','m','p','y']
7128        *   'jumpy'.chars(function(c) {
7129        *     // Called 5 times: "j","u","m","p","y"
7130        *   });
7131        *
7132        * @param {eachCharFn} [callback]
7133        * @callbackParam {string} char
7134        * @callbackParam {number} i
7135        * @callbackParam {Array<string>} arr
7136        *
7137        ***/
7138        'chars': function(str, search, fn) {
7139            return stringEach(str, search, fn);
7140        },
7141
7142        /***
7143        * @method words([callback])
7144        * @returns Array
7145        * @short Runs [fn] against each word in the string, and returns an array.
7146        * @extra A "word" is defined as any sequence of non-whitespace characters.
7147        *
7148        * @callback eachWordFn
7149        *
7150        *   word  The current word.
7151        *   i     The current index.
7152        *   arr   An array of all words.
7153        *
7154        * @example
7155        *
7156        *   'broken wear'.words() -> ['broken','wear']
7157        *   'broken wear'.words(function(w) {
7158        *     // Called twice: "broken", "wear"
7159        *   });
7160        *
7161        * @param {eachWordFn} [callback]
7162        * @callbackParam {string} word
7163        * @callbackParam {number} i
7164        * @callbackParam {Array<string>} arr
7165        *
7166        ***/
7167        'words': function(str, fn) {
7168            return stringEach(trim(str), /\S+/g, fn);
7169        },
7170
7171        /***
7172        * @method lines([callback])
7173        * @returns Array
7174        * @short Runs [fn] against each line in the string, and returns an array.
7175        *
7176        * @callback eachLineFn
7177        *
7178        *   line  The current line.
7179        *   i     The current index.
7180        *   arr   An array of all lines.
7181        *
7182        * @example
7183        *
7184        *   lineText.lines() -> array of lines
7185        *   lineText.lines(function(l) {
7186        *     // Called once per line
7187        *   });
7188        *
7189        * @param {eachLineFn} [callback]
7190        * @callbackParam {string} line
7191        * @callbackParam {number} i
7192        * @callbackParam {Array<string>} arr
7193        *
7194        ***/
7195        'lines': function(str, fn) {
7196            return stringEach(trim(str), /^.*$/gm, fn);
7197        },
7198
7199        /***
7200        * @method codes([callback])
7201        * @returns Array
7202        * @short Runs callback [fn] against each character code in the string.
7203        *        Returns an array of character codes.
7204        *
7205        * @callback eachCodeFn
7206        *
7207        *   code  The current character code.
7208        *   i     The current index.
7209        *   str   The string being operated on.
7210        *
7211        * @example
7212        *
7213        *   'jumpy'.codes() -> [106,117,109,112,121]
7214        *   'jumpy'.codes(function(c) {
7215        *     // Called 5 times: 106, 117, 109, 112, 121
7216        *   });
7217        *
7218        * @param {eachCodeFn} [callback]
7219        * @callbackParam {number} code
7220        * @callbackParam {number} i
7221        * @callbackParam {string} str
7222        *
7223        ***/
7224        'codes': function(str, fn) {
7225            return stringCodes(str, fn);
7226        },
7227
7228        /***
7229        * @method shift(n)
7230        * @returns Array
7231        * @short Shifts each character in the string `n` places in the character map.
7232        *
7233        * @example
7234        *
7235        *   'a'.shift(1)  -> 'b'
7236        *   'ク'.shift(1) -> 'グ'
7237        *
7238        * @param {number} n
7239        *
7240        ***/
7241        'shift': function(str, n) {
7242            var result = '';
7243            n = n || 0;
7244            stringCodes(str, function(c) {
7245                result += chr(c + n);
7246            });
7247            return result;
7248        },
7249
7250        /***
7251        * @method isBlank()
7252        * @returns Boolean
7253        * @short Returns true if the string has length 0 or contains only whitespace.
7254        *
7255        * @example
7256        *
7257        *   ''.isBlank()      -> true
7258        *   '   '.isBlank()   -> true
7259        *   'noway'.isBlank() -> false
7260        *
7261        ***/
7262        'isBlank': function(str) {
7263            return trim(str).length === 0;
7264        },
7265
7266        /***
7267        * @method isEmpty()
7268        * @returns Boolean
7269        * @short Returns true if the string has length 0.
7270        *
7271        * @example
7272        *
7273        *   ''.isEmpty()  -> true
7274        *   'a'.isBlank() -> false
7275        *   ' '.isBlank() -> false
7276        *
7277        ***/
7278        'isEmpty': function(str) {
7279            return str.length === 0;
7280        },
7281
7282        /***
7283        * @method insert(str, [index] = length)
7284        * @returns String
7285        * @short Adds `str` at [index]. Allows negative values.
7286        *
7287        * @example
7288        *
7289        *   'dopamine'.insert('e', 3)       -> dopeamine
7290        *   'spelling eror'.insert('r', -3) -> spelling error
7291        *
7292        * @param {string} str
7293        * @param {number} [index]
7294        *
7295        ***/
7296        'insert': function(str, substr, index) {
7297            index = isUndefined(index) ? str.length : index;
7298            return str.slice(0, index) + substr + str.slice(index);
7299        },
7300
7301        /***
7302        * @method remove(f)
7303        * @returns String
7304        * @short Removes the first occurrence of `f` in the string.
7305        * @extra `f` can be a either case-sensitive string or a regex. In either case
7306        *        only the first match will be removed. To remove multiple occurrences,
7307        *        use `removeAll`.
7308        *
7309        * @example
7310        *
7311        *   'schfifty five'.remove('f')      -> 'schifty five'
7312        *   'schfifty five'.remove(/[a-f]/g) -> 'shfifty five'
7313        *
7314        * @param {string|RegExp} f
7315        *
7316        ***/
7317        'remove': function(str, f) {
7318            return str.replace(f, '');
7319        },
7320
7321        /***
7322        * @method removeAll(f)
7323        * @returns String
7324        * @short Removes any occurences of `f` in the string.
7325        * @extra `f` can be either a case-sensitive string or a regex. In either case
7326        *        all matches will be removed. To remove only a single occurence, use
7327        *        `remove`.
7328        *
7329        * @example
7330        *
7331        *   'schfifty five'.removeAll('f')     -> 'schity ive'
7332        *   'schfifty five'.removeAll(/[a-f]/) -> 'shity iv'
7333        *
7334        * @param {string|RegExp} f
7335        *
7336        ***/
7337        'removeAll': function(str, f) {
7338            return stringReplaceAll(str, f);
7339        },
7340
7341        /***
7342        * @method reverse()
7343        * @returns String
7344        * @short Reverses the string.
7345        *
7346        * @example
7347        *
7348        *   'jumpy'.reverse()        -> 'ypmuj'
7349        *   'lucky charms'.reverse() -> 'smrahc ykcul'
7350        *
7351        ***/
7352        'reverse': function(str) {
7353            return reverseString(str);
7354        },
7355
7356        /***
7357        * @method compact()
7358        * @returns String
7359        * @short Compacts whitespace in the string to a single space and trims the ends.
7360        *
7361        * @example
7362        *
7363        *   'too \n much \n space'.compact() -> 'too much space'
7364        *   'enough \n '.compact()           -> 'enought'
7365        *
7366        ***/
7367        'compact': function(str) {
7368            return trim(str).replace(/([\r\n\s ])+/g, function(match, whitespace) {
7369                return whitespace === ' ' ? whitespace : ' ';
7370            });
7371        },
7372
7373        /***
7374        * @method from([index] = 0)
7375        * @returns String
7376        * @short Returns a section of the string starting from [index].
7377        *
7378        * @example
7379        *
7380        *   'lucky charms'.from()   -> 'lucky charms'
7381        *   'lucky charms'.from(7)  -> 'harms'
7382        *
7383        * @param {number} [index]
7384        *
7385        ***/
7386        'from': function(str, from) {
7387            return str.slice(numberOrIndex(str, from, true));
7388        },
7389
7390        /***
7391        * @method to([index] = end)
7392        * @returns String
7393        * @short Returns a section of the string ending at [index].
7394        *
7395        * @example
7396        *
7397        *   'lucky charms'.to()   -> 'lucky charms'
7398        *   'lucky charms'.to(7)  -> 'lucky ch'
7399        *
7400        * @param {number} [index]
7401        *
7402        ***/
7403        'to': function(str, to) {
7404            if (isUndefined(to)) to = str.length;
7405            return str.slice(0, numberOrIndex(str, to));
7406        },
7407
7408        /***
7409        * @method dasherize()
7410        * @returns String
7411        * @short Converts underscores and camel casing to hypens.
7412        *
7413        * @example
7414        *
7415        *   'a_farewell_to_arms'.dasherize() -> 'a-farewell-to-arms'
7416        *   'capsLock'.dasherize()           -> 'caps-lock'
7417        *
7418        ***/
7419        'dasherize': function(str) {
7420            return stringUnderscore(str).replace(/_/g, '-');
7421        },
7422
7423        /***
7424        * @method underscore()
7425        * @returns String
7426        * @short Converts hyphens and camel casing to underscores.
7427        *
7428        * @example
7429        *
7430        *   'a-farewell-to-arms'.underscore() -> 'a_farewell_to_arms'
7431        *   'capsLock'.underscore()           -> 'caps_lock'
7432        *
7433        ***/
7434        'underscore': function(str) {
7435            return stringUnderscore(str);
7436        },
7437
7438        /***
7439        * @method camelize([upper] = true)
7440        * @returns String
7441        * @short Converts underscores and hyphens to camel case.
7442        * @extra If [upper] is true, the string will be UpperCamelCase. If the
7443        *        inflections module is included, acronyms can also be defined that
7444        *        will be used when camelizing.
7445        *
7446        * @example
7447        *
7448        *   'caps_lock'.camelize()              -> 'CapsLock'
7449        *   'moz-border-radius'.camelize()      -> 'MozBorderRadius'
7450        *   'moz-border-radius'.camelize(false) -> 'mozBorderRadius'
7451        *   'http-method'.camelize()            -> 'HTTPMethod'
7452        *
7453        * @param {boolean} [upper]
7454        *
7455        ***/
7456        'camelize': function(str, upper) {
7457            return stringCamelize(str, upper);
7458        },
7459
7460        /***
7461        * @method spacify()
7462        * @returns String
7463        * @short Converts camelcase, underscores, and hyphens to spaces.
7464        *
7465        * @example
7466        *
7467        *   'camelCase'.spacify()                         -> 'camel case'
7468        *   'an-ugly-string'.spacify()                    -> 'an ugly string'
7469        *   'oh-no_youDid-not'.spacify().capitalize(true) -> 'something else'
7470        *
7471        ***/
7472        'spacify': function(str) {
7473            return stringSpacify(str);
7474        },
7475
7476        /***
7477        * @method titleize()
7478        * @returns String
7479        * @short Creates a title version of the string.
7480        * @extra Capitalizes all the words and replaces some characters in the string
7481        *        to create a nicer looking title. String#titleize is meant for
7482        *        creating pretty output.
7483        *
7484        * @example
7485        *
7486        *   'man from the boondocks'.titleize() -> 'Man from the Boondocks'
7487        *   'x-men: apocalypse'.titleize() -> 'X Men: Apocalypse'
7488        *   'TheManWithoutAPast'.titleize() -> 'The Man Without a Past'
7489        *   'raiders_of_the_lost_ark'.titleize() -> 'Raiders of the Lost Ark'
7490        *
7491        ***/
7492        'titleize': function(str) {
7493            return stringTitleize(str);
7494        },
7495
7496        /***
7497        * @method parameterize()
7498        * @returns String
7499        * @short Replaces special characters in a string so that it may be used as
7500        *        part of a pretty URL.
7501        *
7502        * @example
7503        *
7504        *   'hell, no!'.parameterize() -> 'hell-no'
7505        *
7506        ***/
7507        'parameterize': function(str, separator) {
7508            return stringParameterize(str, separator);
7509        },
7510
7511        /***
7512        * @method truncate(length, [from] = 'right', [ellipsis] = '...')
7513        * @returns String
7514        * @short Truncates a string.
7515        * @extra [from] can be `'right'`, `'left'`, or `'middle'`. If the string is
7516        *        shorter than `length`, [ellipsis] will not be added.
7517        *
7518        * @example
7519        *
7520        *   'sittin on the dock'.truncate(10)           -> 'sittin on ...'
7521        *   'sittin on the dock'.truncate(10, 'left')   -> '...n the dock'
7522        *   'sittin on the dock'.truncate(10, 'middle') -> 'sitti... dock'
7523        *
7524        * @param {number} length
7525        * @param {string} [from]
7526        * @param {string} [ellipsis]
7527        *
7528        ***/
7529        'truncate': function(str, length, from, ellipsis) {
7530            return truncateString(str, length, from, ellipsis);
7531        },
7532
7533        /***
7534        * @method truncateOnWord(length, [from] = 'right', [ellipsis] = '...')
7535        * @returns String
7536        * @short Truncates a string without splitting up words.
7537        * @extra [from] can be `'right'`, `'left'`, or `'middle'`. If the string is
7538        *        shorter than `length`, [ellipsis] will not be added. A "word" is
7539        *        defined as any sequence of non-whitespace characters.
7540        *
7541        * @example
7542        *
7543        *   'here we go'.truncateOnWord(5)         -> 'here...'
7544        *   'here we go'.truncateOnWord(5, 'left') -> '...we go'
7545        *
7546        * @param {number} length
7547        * @param {string} [from]
7548        * @param {string} [ellipsis]
7549        *
7550        ***/
7551        'truncateOnWord': function(str, length, from, ellipsis) {
7552            return truncateString(str, length, from, ellipsis, true);
7553        },
7554
7555        /***
7556        * @method pad(num, [padding] = ' ')
7557        * @returns String
7558        * @short Pads the string out with [padding] to be exactly `num` characters.
7559        *
7560        * @example
7561        *
7562        *   'wasabi'.pad(8)      -> ' wasabi '
7563        *   'wasabi'.pad(8, '-') -> '-wasabi-'
7564        *
7565        * @param {number} num
7566        * @param {string} [padding]
7567        *
7568        ***/
7569        'pad': function(str, num, padding) {
7570            var half, front, back;
7571            num = coercePositiveInteger(num);
7572            half = max(0, num - str.length) / 2;
7573            front = floor(half);
7574            back = ceil(half);
7575            return padString(front, padding) + str + padString(back, padding);
7576        },
7577
7578        /***
7579        * @method padLeft(num, [padding] = ' ')
7580        * @returns String
7581        * @short Pads the string out from the left with [padding] to be exactly
7582        *        `num` characters.
7583        *
7584        * @example
7585        *
7586        *   'wasabi'.padLeft(8)      -> '  wasabi'
7587        *   'wasabi'.padLeft(8, '-') -> '--wasabi'
7588        *
7589        * @param {number} num
7590        * @param {string} [padding]
7591        *
7592        ***/
7593        'padLeft': function(str, num, padding) {
7594            num = coercePositiveInteger(num);
7595            return padString(max(0, num - str.length), padding) + str;
7596        },
7597
7598        /***
7599        * @method padRight(num, [padding] = ' ')
7600        * @returns String
7601        * @short Pads the string out from the right with [padding] to be exactly
7602        *        `num` characters.
7603        *
7604        * @example
7605        *
7606        *   'wasabi'.padRight(8)      -> 'wasabi  '
7607        *   'wasabi'.padRight(8, '-') -> 'wasabi--'
7608        *
7609        * @param {number} num
7610        * @param {string} [padding]
7611        *
7612        ***/
7613        'padRight': function(str, num, padding) {
7614            num = coercePositiveInteger(num);
7615            return str + padString(max(0, num - str.length), padding);
7616        },
7617
7618        /***
7619        * @method first([n] = 1)
7620        * @returns String
7621        * @short Returns the first [n] characters of the string.
7622        *
7623        * @example
7624        *
7625        *   'lucky charms'.first()  -> 'l'
7626        *   'lucky charms'.first(3) -> 'luc'
7627        *
7628        * @param {number} [n]
7629        *
7630        ***/
7631        'first': function(str, num) {
7632            if (isUndefined(num)) num = 1;
7633            return str.substr(0, num);
7634        },
7635
7636        /***
7637        * @method last([n] = 1)
7638        * @returns String
7639        * @short Returns the last [n] characters of the string.
7640        *
7641        * @example
7642        *
7643        *   'lucky charms'.last()  -> 's'
7644        *   'lucky charms'.last(3) -> 'rms'
7645        *
7646        * @param {number} [n]
7647        *
7648        ***/
7649        'last': function(str, num) {
7650            if (isUndefined(num)) num = 1;
7651            var start = str.length - num < 0 ? 0 : str.length - num;
7652            return str.substr(start);
7653        },
7654
7655        /***
7656        * @method toNumber([base] = 10)
7657        * @returns Number
7658        * @short Converts the string into a number.
7659        * @extra Any value with a "." fill be converted to a floating point value,
7660        *        otherwise an integer.
7661        *
7662        * @example
7663        *
7664        *   '153'.toNumber()    -> 153
7665        *   '12,000'.toNumber() -> 12000
7666        *   '10px'.toNumber()   -> 10
7667        *   'ff'.toNumber(16)   -> 255
7668        *
7669        * @param {number} [base]
7670        *
7671        ***/
7672        'toNumber': function(str, base) {
7673            return stringToNumber(str, base);
7674        },
7675
7676        /***
7677        * @method capitalize([lower] = false, [all] = false)
7678        * @returns String
7679        * @short Capitalizes the first character of the string.
7680        * @extra If [lower] is true, the remainder of the string will be downcased.
7681        *        If [all] is true, all words in the string will be capitalized.
7682        *
7683        * @example
7684        *
7685        *   'hello'.capitalize()           -> 'Hello'
7686        *   'HELLO'.capitalize(true)       -> 'Hello'
7687        *   'hello kitty'.capitalize()     -> 'Hello kitty'
7688        *   'hEllO kItTy'.capitalize(true, true) -> 'Hello Kitty'
7689        *
7690        * @param {boolean} [lower]
7691        * @param {boolean} [all]
7692        *
7693        ***/
7694        'capitalize': function(str, lower, all) {
7695            return stringCapitalize(str, lower, all);
7696        },
7697
7698        /***
7699        * @method trimLeft()
7700        * @returns String
7701        * @short Removes leading whitespace from the string.
7702        * @extra Whitespace is defined as line breaks, tabs, and any character in the
7703        *        "Space, Separator" Unicode category, conforming to the the ES5 `trim`
7704        *        spec.
7705        *
7706        * @example
7707        *
7708        *   '   wasabi   '.trimLeft()  -> 'wasabi   '
7709        *
7710        ***/
7711        'trimLeft': function(str) {
7712            return str.replace(LEFT_TRIM_REG, '');
7713        },
7714
7715        /***
7716        * @method trimRight()
7717        * @returns String
7718        * @short Removes trailing whitespace from the string.
7719        * @extra Whitespace is defined as line breaks, tabs, and any character in the
7720        *        "Space, Separator" Unicode category, conforming to the the ES5 `trim`
7721        *        spec.
7722        *
7723        * @example
7724        *
7725        *   '   wasabi   '.trimRight() -> '   wasabi'
7726        *
7727        ***/
7728        'trimRight': function(str) {
7729            return str.replace(RIGHT_TRIM_REG, '');
7730        }
7731
7732    });
7733
7734    defineInstanceWithArguments(sugarString, {
7735
7736        /***
7737        * @method replaceAll(f, [str1], [str2], ...)
7738        * @returns String
7739        * @short Replaces all occurences of `f` with arguments passed.
7740        * @extra This method is intended to be a quick way to perform multiple string
7741        *        replacements quickly when the replacement token differs depending on
7742        *        position. `f` can be either a case-sensitive string or a regex.
7743        *        In either case all matches will be replaced.
7744        *
7745        * @example
7746        *
7747        *   '-x -y -z'.replaceAll('-', 1, 2, 3)               -> '1x 2y 3z'
7748        *   'one and two'.replaceAll(/one|two/, '1st', '2nd') -> '1st and 2nd'
7749        *
7750        * @param {string|RegExp} f
7751        * @param {string} [str1]
7752        * @param {string} [str2]
7753        *
7754        ***/
7755        'replaceAll': function(str, f, args) {
7756            return stringReplaceAll(str, f, args);
7757        },
7758
7759        /***
7760        * @method format(obj1, [obj2], ...)
7761        * @returns String
7762        * @short Replaces `{}` tokens in the string with arguments or properties.
7763        * @extra Tokens support `deep properties`. If a single object is passed, its
7764        *        properties can be accessed by keywords such as `{name}`. If multiple
7765        *        objects or a non-object are passed, they can be accessed by the
7766        *        argument position like `{0}`. Literal braces in the string can be
7767        *        escaped by repeating them.
7768        *
7769        * @example
7770        *
7771        *   'Welcome, {name}.'.format({ name: 'Bill' }) -> 'Welcome, Bill.'
7772        *   'You are {0} years old today.'.format(5)    -> 'You are 5 years old today.'
7773        *   '{0.name} and {1.name}'.format(users)       -> logs first two users' names
7774        *   '${currencies.usd.balance}'.format(Harry)   -> "$500"
7775        *   '{{Hello}}'.format('Hello')                 -> "{Hello}"
7776        *
7777        * @param {any} [obj1]
7778        * @param {any} [obj2]
7779        *
7780        ***/
7781        'format': function(str, args) {
7782            var arg1 = args[0] && args[0].valueOf();
7783            // Unwrap if a single object is passed in.
7784            if (args.length === 1 && isObjectType(arg1)) {
7785                args = arg1;
7786            }
7787            return stringFormatMatcher(str, args);
7788        }
7789
7790    });
7791
7792    buildBase64();
7793    buildEntities();
7794
7795    /***
7796    * @module Array
7797    * @description Array manipulation and traversal, alphanumeric sorting and collation.
7798    *
7799    ***/
7800
7801    var HALF_WIDTH_NINE = 0x39;
7802    var FULL_WIDTH_NINE = 0xff19;
7803
7804    // Undefined array elements in < IE8 will not be visited by concat
7805    // and so will not be copied. This means that non-sparse arrays will
7806    // become sparse, so detect for this here.
7807    var HAS_CONCAT_BUG = !('0' in [].concat(undefined).concat());
7808
7809    var ARRAY_OPTIONS = {
7810        'sortIgnore': null,
7811        'sortNatural': true,
7812        'sortIgnoreCase': true,
7813        'sortOrder': getSortOrder(),
7814        'sortCollate': collateStrings,
7815        'sortEquivalents': getSortEquivalents()
7816    };
7817
7818    /***
7819    * @method getOption(name)
7820    * @returns Mixed
7821    * @accessor
7822    * @short Gets an option used interally by Array.
7823    * @extra Options listed below. Current options are for sorting strings with
7824    *        `sortBy`.
7825    *
7826    * @example
7827    *
7828    *   Sugar.Array.getOption('sortNatural')
7829    *
7830    * @param {string} name
7831    *
7832    ***
7833    * @method setOption(name, value)
7834    * @accessor
7835    * @short Sets an option used interally by Array.
7836    * @extra Options listed below. Current options are for sorting strings with
7837    *        `sortBy`. If `value` is `null`, the default value will be restored.
7838    *
7839    * @options
7840    *
7841    *   sortIgnore        A regex to ignore when sorting. An example usage of this
7842    *                     option would be to ignore numbers in a list to instead
7843    *                     sort by the first text that appears. Default is `null`.
7844    *
7845    *   sortIgnoreCase    A boolean that ignores case when sorting.
7846    *                     Default is `true`.
7847    *
7848    *   sortNatural       A boolean that turns on natural sorting. "Natural" means
7849    *                     that numerals like "10" will be sorted after "9" instead
7850    *                     of after "1". Default is `true`.
7851    *
7852    *   sortOrder         A string of characters to use as the base sort order. The
7853    *                     default is an order natural to most major world languages.
7854    *
7855    *   sortEquivalents   A table of equivalent characters used when sorting. The
7856    *                     default produces a natural sort order for most world
7857    *                     languages, however can be modified for others. For
7858    *                     example, setting "ä" and "ö" to `null` in the table would
7859    *                     produce a Scandanavian sort order. Note that setting this
7860    *                     option to `null` will restore the default table, but any
7861    *                     mutations made to that table will persist.
7862    *
7863    *   sortCollate       The collation function used when sorting strings. The
7864    *                     default function produces a natural sort order that can
7865    *                     be customized with the other "sort" options. Overriding
7866    *                     the function directly here will also override these
7867    *                     options.
7868    *
7869    * @example
7870    *
7871    *   Sugar.Array.setOption('sortIgnore', /^\d+\./)
7872    *   Sugar.Array.setOption('sortIgnoreCase', false)
7873    *
7874    * @signature setOption(options)
7875    * @param {ArrayOptions} options
7876    * @param {string} name
7877    * @param {any} value
7878    * @option {RegExp} [sortIgnore]
7879    * @option {boolean} [sortIgnoreCase]
7880    * @option {boolean} [sortNatural]
7881    * @option {string} [sortOrder]
7882    * @option {Object} [sortEquivalents]
7883    * @option {Function} [sortCollate]
7884    *
7885    ***/
7886    var _arrayOptions = defineOptionsAccessor(sugarArray, ARRAY_OPTIONS);
7887
7888
7889    function setArrayChainableConstructor() {
7890        setChainableConstructor(sugarArray, arrayCreate);
7891    }
7892
7893    function isArrayOrInherited(obj) {
7894        return obj && obj.constructor && isArray(obj.constructor.prototype);
7895    }
7896
7897    function arrayCreate(obj, clone) {
7898        var arr;
7899        if (isArrayOrInherited(obj)) {
7900            arr = clone ? arrayClone(obj) : obj;
7901        } else if (isObjectType(obj) || isString(obj)) {
7902            arr = Array.from(obj);
7903        } else if (isDefined(obj)) {
7904            arr = [obj];
7905        }
7906        return arr || [];
7907    }
7908
7909    function arrayClone(arr) {
7910        var clone = new Array(arr.length);
7911        forEach(arr, function(el, i) {
7912            clone[i] = el;
7913        });
7914        return clone;
7915    }
7916
7917    function arrayConcat(arr1, arr2) {
7918        if (HAS_CONCAT_BUG) {
7919            return arraySafeConcat(arr1, arr2);
7920        }
7921        return arr1.concat(arr2);
7922    }
7923
7924    // Avoids issues with [undefined] in < IE9
7925    function arrayWrap(obj) {
7926        var arr = [];
7927        arr.push(obj);
7928        return arr;
7929    }
7930
7931    // Avoids issues with concat in < IE8
7932    function arraySafeConcat(arr, arg) {
7933        var result = arrayClone(arr), len = result.length, arr2;
7934        arr2 = isArray(arg) ? arg : [arg];
7935        result.length += arr2.length;
7936        forEach(arr2, function(el, i) {
7937            result[len + i] = el;
7938        });
7939        return result;
7940    }
7941
7942
7943    function arrayAppend(arr, el, index) {
7944        var spliceArgs;
7945        index = +index;
7946        if (isNaN(index)) {
7947            index = arr.length;
7948        }
7949        spliceArgs = [index, 0];
7950        if (isDefined(el)) {
7951            spliceArgs = spliceArgs.concat(el);
7952        }
7953        arr.splice.apply(arr, spliceArgs);
7954        return arr;
7955    }
7956
7957    function arrayRemove(arr, f) {
7958        var matcher = getMatcher(f), i = 0;
7959        while (i < arr.length) {
7960            if (matcher(arr[i], i, arr)) {
7961                arr.splice(i, 1);
7962            } else {
7963                i++;
7964            }
7965        }
7966        return arr;
7967    }
7968
7969    function arrayExclude(arr, f) {
7970        var result = [], matcher = getMatcher(f);
7971        for (var i = 0; i < arr.length; i++) {
7972            if (!matcher(arr[i], i, arr)) {
7973                result.push(arr[i]);
7974            }
7975        }
7976        return result;
7977    }
7978
7979    function arrayUnique(arr, map) {
7980        var result = [], obj = {}, refs = [];
7981        forEach(arr, function(el, i) {
7982            var transformed = map ? mapWithShortcuts(el, map, arr, [el, i, arr]) : el;
7983            var key = serializeInternal(transformed, refs);
7984            if (!hasOwn(obj, key)) {
7985                result.push(el);
7986                obj[key] = true;
7987            }
7988        });
7989        return result;
7990    }
7991
7992    function arrayFlatten(arr, level, current) {
7993        var result = [];
7994        level = level || Infinity;
7995        current = current || 0;
7996        forEach(arr, function(el) {
7997            if (isArray(el) && current < level) {
7998                result = result.concat(arrayFlatten(el, level, current + 1));
7999            } else {
8000                result.push(el);
8001            }
8002        });
8003        return result;
8004    }
8005
8006    function arrayCompact(arr, all) {
8007        return filter(arr, function(el) {
8008            return el || (!all && el != null && el.valueOf() === el.valueOf());
8009        });
8010    }
8011
8012    function arrayShuffle(arr) {
8013        arr = arrayClone(arr);
8014        var i = arr.length, j, x;
8015        while (i) {
8016            j = (Math.random() * i) | 0;
8017            x = arr[--i];
8018            arr[i] = arr[j];
8019            arr[j] = x;
8020        }
8021        return arr;
8022    }
8023
8024    function arrayGroupBy(arr, map, fn) {
8025        var result = {}, key;
8026        forEach(arr, function(el, i) {
8027            key = mapWithShortcuts(el, map, arr, [el, i, arr]);
8028            if (!hasOwn(result, key)) {
8029                result[key] = [];
8030            }
8031            result[key].push(el);
8032        });
8033        if (fn) {
8034            forEachProperty(result, fn);
8035        }
8036        return result;
8037    }
8038
8039    function arrayIntersectOrSubtract(arr1, arr2, subtract) {
8040        var result = [], obj = {}, refs = [];
8041        if (!isArray(arr2)) {
8042            arr2 = arrayWrap(arr2);
8043        }
8044        forEach(arr2, function(el) {
8045            obj[serializeInternal(el, refs)] = true;
8046        });
8047        forEach(arr1, function(el) {
8048            var key = serializeInternal(el, refs);
8049            if (hasOwn(obj, key) !== subtract) {
8050                delete obj[key];
8051                result.push(el);
8052            }
8053        });
8054        return result;
8055    }
8056
8057    // Collation helpers
8058
8059    function compareValue(aVal, bVal) {
8060        var cmp, i, collate;
8061        if (isString(aVal) && isString(bVal)) {
8062            collate = _arrayOptions('sortCollate');
8063            return collate(aVal, bVal);
8064        } else if (isArray(aVal) && isArray(bVal)) {
8065            if (aVal.length < bVal.length) {
8066                return -1;
8067            } else if (aVal.length > bVal.length) {
8068                return 1;
8069            } else {
8070                for (i = 0; i < aVal.length; i++) {
8071                    cmp = compareValue(aVal[i], bVal[i]);
8072                    if (cmp !== 0) {
8073                        return cmp;
8074                    }
8075                }
8076                return 0;
8077            }
8078        }
8079        return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
8080    }
8081
8082    function codeIsNumeral(code) {
8083        return (code >= HALF_WIDTH_ZERO && code <= HALF_WIDTH_NINE) ||
8084           (code >= FULL_WIDTH_ZERO && code <= FULL_WIDTH_NINE);
8085    }
8086
8087    function collateStrings(a, b) {
8088        var aValue, bValue, aChar, bChar, aEquiv, bEquiv, index = 0, tiebreaker = 0;
8089
8090        var sortOrder = _arrayOptions('sortOrder');
8091        var sortIgnore = _arrayOptions('sortIgnore');
8092        var sortNatural = _arrayOptions('sortNatural');
8093        var sortIgnoreCase = _arrayOptions('sortIgnoreCase');
8094        var sortEquivalents = _arrayOptions('sortEquivalents');
8095
8096        a = getCollationReadyString(a, sortIgnore, sortIgnoreCase);
8097        b = getCollationReadyString(b, sortIgnore, sortIgnoreCase);
8098
8099        do {
8100
8101            aChar = getCollationCharacter(a, index, sortEquivalents);
8102            bChar = getCollationCharacter(b, index, sortEquivalents);
8103            aValue = getSortOrderIndex(aChar, sortOrder);
8104            bValue = getSortOrderIndex(bChar, sortOrder);
8105
8106            if (aValue === -1 || bValue === -1) {
8107                aValue = a.charCodeAt(index) || null;
8108                bValue = b.charCodeAt(index) || null;
8109                if (sortNatural && codeIsNumeral(aValue) && codeIsNumeral(bValue)) {
8110                    aValue = stringToNumber(a.slice(index));
8111                    bValue = stringToNumber(b.slice(index));
8112                }
8113            } else {
8114                aEquiv = aChar !== a.charAt(index);
8115                bEquiv = bChar !== b.charAt(index);
8116                if (aEquiv !== bEquiv && tiebreaker === 0) {
8117                    tiebreaker = aEquiv - bEquiv;
8118                }
8119            }
8120            index += 1;
8121        } while (aValue != null && bValue != null && aValue === bValue);
8122        if (aValue === bValue) return tiebreaker;
8123        return aValue - bValue;
8124    }
8125
8126    function getCollationReadyString(str, sortIgnore, sortIgnoreCase) {
8127        if (!isString(str)) str = String(str);
8128        if (sortIgnoreCase) {
8129            str = str.toLowerCase();
8130        }
8131        if (sortIgnore) {
8132            str = str.replace(sortIgnore, '');
8133        }
8134        return str;
8135    }
8136
8137    function getCollationCharacter(str, index, sortEquivalents) {
8138        var chr = str.charAt(index);
8139        return getOwn(sortEquivalents, chr) || chr;
8140    }
8141
8142    function getSortOrderIndex(chr, sortOrder) {
8143        if (!chr) {
8144            return null;
8145        } else {
8146            return sortOrder.indexOf(chr);
8147        }
8148    }
8149
8150    function getSortOrder() {
8151        var order = 'AÁÀÂÃĄBCĆČÇDĎÐEÉÈĚÊËĘFGĞHıIÍÌİÎÏJKLŁMNŃŇÑOÓÒÔPQRŘSŚŠŞTŤUÚÙŮÛÜVWXYÝZŹŻŽÞÆŒØÕÅÄÖ';
8152        return map(order.split(''), function(str) {
8153            return str + str.toLowerCase();
8154        }).join('');
8155    }
8156
8157    function getSortEquivalents() {
8158        var equivalents = {};
8159        forEach(spaceSplit('AÁÀÂÃÄ CÇ EÉÈÊË IÍÌİÎÏ OÓÒÔÕÖ Sß UÚÙÛÜ'), function(set) {
8160            var first = set.charAt(0);
8161            forEach(set.slice(1).split(''), function(chr) {
8162                equivalents[chr] = first;
8163                equivalents[chr.toLowerCase()] = first.toLowerCase();
8164            });
8165        });
8166        return equivalents;
8167    }
8168
8169    defineStatic(sugarArray, {
8170
8171        /***
8172        *
8173        * @method create([obj], [clone] = false)
8174        * @returns Array
8175        * @static
8176        * @short Creates an array from an unknown object.
8177        * @extra This method is similar to native `Array.from` but is faster when
8178        *        `obj` is already an array. When [clone] is true, the array will be
8179        *        shallow cloned. Additionally, it will not fail on `undefined`,
8180        *        `null`, or numbers, producing an empty array in the case of
8181        *        `undefined` and wrapping `obj` otherwise.
8182        *
8183        * @example
8184        *
8185        *   Array.create()          -> []
8186        *   Array.create(8)         -> [8]
8187        *   Array.create('abc')     -> ['a','b','c']
8188        *   Array.create([1,2,3])   -> [1, 2, 3]
8189        *   Array.create(undefined) -> []
8190        *
8191        * @param {number|ArrayLike<T>} [obj]
8192        * @param {boolean} [clone]
8193        *
8194        ***/
8195        'create': function(obj, clone) {
8196            return arrayCreate(obj, clone);
8197        },
8198
8199        /***
8200        *
8201        * @method construct(n, map)
8202        * @returns Array
8203        * @static
8204        * @short Constructs an array of `n` length from the values of `map`.
8205        * @extra This function is essentially a shortcut for using `Array.from` with
8206        *        `new Array(n)`.
8207        *
8208        * @callback indexMapFn
8209        *
8210        *   i   The index of the current iteration.
8211        *
8212        * @example
8213        *
8214        *   Array.construct(4, function(i) {
8215        *     return i * i;
8216        *   }); -> [0, 1, 4]
8217        *
8218        * @param {number} n
8219        * @param {indexMapFn} map
8220        * @callbackParam {number} i
8221        * @callbackReturns {any} indexMapFn
8222        *
8223        ***/
8224        'construct': function(n, fn) {
8225            n = coercePositiveInteger(n);
8226            return Array.from(new Array(n), function(el, i) {
8227                return fn && fn(i);
8228            });
8229        }
8230
8231    });
8232
8233    defineInstance(sugarArray, {
8234
8235        /***
8236        * @method isEmpty()
8237        * @returns Boolean
8238        * @short Returns true if the array has a length of zero.
8239        *
8240        * @example
8241        *
8242        *   [].isEmpty()    -> true
8243        *   ['a'].isEmpty() -> false
8244        *
8245        ***/
8246        'isEmpty': function(arr) {
8247            return arr.length === 0;
8248        },
8249
8250        /***
8251        * @method isEqual(arr)
8252        * @returns Boolean
8253        * @short Returns true if the array is equal to `arr`.
8254        * @extra Objects in the array are considered equal if they are not obserably
8255        *        distinguishable. This method is an instance alias for
8256        *        `Object.isEqual()`.
8257        *
8258        * @example
8259        *
8260        *   ['a','b'].isEqual(['a','b']) -> true
8261        *   ['a','b'].isEqual(['a','c']) -> false
8262        *   [{a:'a'}].isEqual([{a:'a'}]) -> true
8263        *   [5].isEqual([Object(5)])     -> false
8264        *
8265        * @param {Array} arr
8266        *
8267        ***/
8268        'isEqual': function(a, b) {
8269            return isEqual(a, b);
8270        },
8271
8272        /***
8273        * @method clone()
8274        * @returns Array
8275        * @short Creates a shallow clone of the array.
8276        *
8277        * @example
8278        *
8279        *   [1,2,3].clone() -> [1,2,3]
8280        *
8281        ***/
8282        'clone': function(arr) {
8283            return arrayClone(arr);
8284        },
8285
8286        /***
8287        * @method at(index, [loop] = false)
8288        * @returns ArrayElement
8289        * @short Gets the element(s) at `index`.
8290        * @extra When [loop] is true, overshooting the end of the array will begin
8291        *        counting from the other end. `index` may be negative. If `index` is
8292        *        an array, multiple elements will be returned.
8293        *
8294        * @example
8295        *
8296        *   [1,2,3].at(0)       -> 1
8297        *   [1,2,3].at(2)       -> 3
8298        *   [1,2,3].at(4)       -> undefined
8299        *   [1,2,3].at(4, true) -> 2
8300        *   [1,2,3].at(-1)      -> 3
8301        *   [1,2,3].at([0, 1])  -> [1, 2]
8302        *
8303        * @param {number|number[]} index
8304        * @param {boolean} [loop]
8305        *
8306        ***/
8307        'at': function(arr, index, loop) {
8308            return getEntriesForIndexes(arr, index, loop);
8309        },
8310
8311        /***
8312        * @method add(item, [index])
8313        * @returns Array
8314        * @short Adds `item` to the array and returns the result as a new array.
8315        * @extra If `item` is also an array, it will be concatenated instead of
8316        *        inserted. [index] will control where `item` is added. Use `append`
8317        *        to modify the original array.
8318        *
8319        * @example
8320        *
8321        *   [1,2,3,4].add(5)       -> [1,2,3,4,5]
8322        *   [1,2,3,4].add(8, 1)    -> [1,8,2,3,4]
8323        *   [1,2,3,4].add([5,6,7]) -> [1,2,3,4,5,6,7]
8324        *
8325        * @param {ArrayElement|Array} item
8326        * @param {number} [index]
8327        *
8328        ***/
8329        'add': function(arr, item, index) {
8330            return arrayAppend(arrayClone(arr), item, index);
8331        },
8332
8333        /***
8334        * @method subtract(item)
8335        * @returns Array
8336        * @short Subtracts `item` from the array and returns the result as a new array.
8337        * @extra If `item` is also an array, all elements in it will be removed. In
8338        *        addition to primitives, this method will also deep-check objects for
8339        *        equality.
8340        *
8341        * @example
8342        *
8343        *   [1,3,5].subtract([5,7,9])     -> [1,3]
8344        *   ['a','b'].subtract(['b','c']) -> ['a']
8345        *   [1,2,3].subtract(2)           -> [1,3]
8346        *
8347        * @param {ArrayElement|Array} item
8348        *
8349        ***/
8350        'subtract': function(arr, item) {
8351            return arrayIntersectOrSubtract(arr, item, true);
8352        },
8353
8354        /***
8355        * @method append(item, [index])
8356        * @returns Array
8357        * @short Appends `item` to the array.
8358        * @extra If `item` is also an array, it will be concatenated instead of
8359        *        inserted. This method modifies the array! Use `add` to create a new
8360        *        array. Additionally, `insert` is provided as an alias that reads
8361        *        better when using an index.
8362        *
8363        * @example
8364        *
8365        *   [1,2,3,4].append(5)       -> [1,2,3,4,5]
8366        *   [1,2,3,4].append([5,6,7]) -> [1,2,3,4,5,6,7]
8367        *   [1,2,3,4].append(8, 1)    -> [1,8,2,3,4]
8368        *
8369        * @param {ArrayElement|Array} item
8370        * @param {number} index
8371        *
8372        ***/
8373        'append': function(arr, item, index) {
8374            return arrayAppend(arr, item, index);
8375        },
8376
8377        /***
8378        * @method removeAt(start, [end])
8379        * @returns Array
8380        * @short Removes element at `start`. If [end] is specified, removes the range
8381        *        between `start` and [end]. This method will modify the array!
8382        *
8383        * @example
8384        *
8385        *   ['a','b','c'].removeAt(0) -> ['b','c']
8386        *   [1,2,3,4].removeAt(1, 2)  -> [1, 4]
8387        *
8388        * @param {number} start
8389        * @param {number} [end]
8390        *
8391        ***/
8392        'removeAt': function(arr, start, end) {
8393            if (isUndefined(start)) return arr;
8394            if (isUndefined(end)) end = start;
8395            arr.splice(start, end - start + 1);
8396            return arr;
8397        },
8398
8399        /***
8400        * @method unique([map])
8401        * @returns Array
8402        * @short Removes all duplicate elements in the array.
8403        * @extra [map] may be a function returning the value to be uniqued or a
8404        *        string acting as a shortcut. This is most commonly used when you
8405        *        only need to check a single field that can ensure the object's
8406        *        uniqueness (such as an `id` field). If [map] is not passed, then
8407        *        objects will be deep checked for equality. Supports
8408        *        `deep properties`.
8409        *
8410        * @callback mapFn
8411        *
8412        *   el   The element of the current iteration.
8413        *   i    The index of the current iteration.
8414        *   arr  A reference to the array.
8415        *
8416        * @example
8417        *
8418        *   [1,2,2,3].unique()            -> [1,2,3]
8419        *   [{a:'a'},{a:'a'}].unique()    -> [{a:'a'}]
8420        *
8421        *   users.unique(function(user) {
8422        *     return user.id;
8423        *   }); -> users array uniqued by id
8424        *
8425        *   users.unique('id')            -> users array uniqued by id
8426        *
8427        * @param {string|mapFn} map
8428        * @callbackParam {ArrayElement} el
8429        * @callbackParam {number} i
8430        * @callbackParam {Array} arr
8431        * @callbackReturns {NewArrayElement} mapFn
8432        *
8433        ***/
8434        'unique': function(arr, map) {
8435            return arrayUnique(arr, map);
8436        },
8437
8438        /***
8439        * @method flatten([limit] = Infinity)
8440        * @returns Array
8441        * @short Returns a flattened, one-dimensional copy of the array.
8442        * @extra You can optionally specify a [limit], which will only flatten to
8443        *        that depth.
8444        *
8445        * @example
8446        *
8447        *   [[1], 2, [3]].flatten() -> [1,2,3]
8448        *   [[1],[],2,3].flatten()  -> [1,2,3]
8449        *
8450        * @param {number} [limit]
8451        *
8452        ***/
8453        'flatten': function(arr, limit) {
8454            return arrayFlatten(arr, limit);
8455        },
8456
8457        /***
8458        * @method first([num] = 1)
8459        * @returns Mixed
8460        * @short Returns the first element(s) in the array.
8461        * @extra When `num` is passed, returns the first `num` elements in the array.
8462        *
8463        * @example
8464        *
8465        *   [1,2,3].first()  -> 1
8466        *   [1,2,3].first(2) -> [1,2]
8467        *
8468        * @param {number} [num]
8469        *
8470        ***/
8471        'first': function(arr, num) {
8472            if (isUndefined(num)) return arr[0];
8473            if (num < 0) num = 0;
8474            return arr.slice(0, num);
8475        },
8476
8477        /***
8478        * @method last([num] = 1)
8479        * @returns Mixed
8480        * @short Returns the last element(s) in the array.
8481        * @extra When `num` is passed, returns the last `num` elements in the array.
8482        *
8483        * @example
8484        *
8485        *   [1,2,3].last()  -> 3
8486        *   [1,2,3].last(2) -> [2,3]
8487        *
8488        * @param {number} [num]
8489        *
8490        ***/
8491        'last': function(arr, num) {
8492            if (isUndefined(num)) return arr[arr.length - 1];
8493            var start = arr.length - num < 0 ? 0 : arr.length - num;
8494            return arr.slice(start);
8495        },
8496
8497        /***
8498        * @method from(index)
8499        * @returns Array
8500        * @short Returns a slice of the array from `index`.
8501        *
8502        * @example
8503        *
8504        *   ['a','b','c'].from(1) -> ['b','c']
8505        *   ['a','b','c'].from(2) -> ['c']
8506        *
8507        * @param {number} [index]
8508        *
8509        ***/
8510        'from': function(arr, num) {
8511            return arr.slice(num);
8512        },
8513
8514        /***
8515        * @method to(index)
8516        * @returns Array
8517        * @short Returns a slice of the array up to `index`.
8518        *
8519        * @example
8520        *
8521        *   ['a','b','c'].to(1) -> ['a']
8522        *   ['a','b','c'].to(2) -> ['a','b']
8523        *
8524        * @param {number} [index]
8525        *
8526        ***/
8527        'to': function(arr, num) {
8528            if (isUndefined(num)) num = arr.length;
8529            return arr.slice(0, num);
8530        },
8531
8532        /***
8533        * @method compact([all] = false)
8534        * @returns Array
8535        * @short Removes all instances of `undefined`, `null`, and `NaN` from the array.
8536        * @extra If [all] is `true`, all "falsy" elements will be removed. This
8537        *        includes empty strings, `0`, and `false`.
8538        *
8539        * @example
8540        *
8541        *   [1,null,2,undefined,3].compact() -> [1,2,3]
8542        *   [1,'',2,false,3].compact()       -> [1,'',2,false,3]
8543        *   [1,'',2,false,3].compact(true)   -> [1,2,3]
8544        *   [null, [null, 'bye']].compact()  -> ['hi', [null, 'bye']]
8545        *
8546        * @param {boolean} [all]
8547        *
8548        ***/
8549        'compact': function(arr, all) {
8550            return arrayCompact(arr, all);
8551        },
8552
8553        /***
8554        * @method groupBy(map, [fn])
8555        * @returns Object
8556        * @short Groups the array by `map`.
8557        * @extra Will return an object whose keys are the mapped from `map`, which
8558        *        may be a mapping function, or a string acting as a shortcut. `map`
8559        *        supports `deep properties`. Optionally calls [fn] for each group.
8560        *
8561        * @callback mapFn
8562        *
8563        *   el   The element of the current iteration.
8564        *   i    The index of the current iteration.
8565        *   arr  A reference to the array.
8566        *
8567        * @callback groupFn
8568        *
8569        *   arr  The current group as an array.
8570        *   key  The unique key of the current group.
8571        *   obj  A reference to the object.
8572        *
8573        * @example
8574        *
8575        *   ['a','aa','aaa'].groupBy('length') -> { 1: ['a'], 2: ['aa'], 3: ['aaa'] }
8576        *
8577        *   users.groupBy(function(n) {
8578        *     return n.age;
8579        *   }); -> users array grouped by age
8580        *
8581        *   users.groupBy('age', function(age, users) {
8582        *     // iterates each grouping
8583        *   });
8584        *
8585        * @param {string|mapFn} map
8586        * @param {groupFn} fn
8587        * @callbackParam {ArrayElement} el
8588        * @callbackParam {number} i
8589        * @callbackParam {Array} arr
8590        * @callbackParam {string} key
8591        * @callbackParam {Object} obj
8592        * @callbackReturns {NewArrayElement} mapFn
8593        *
8594        ***/
8595        'groupBy': function(arr, map, fn) {
8596            return arrayGroupBy(arr, map, fn);
8597        },
8598
8599        /***
8600        * @method inGroups(num, [padding])
8601        * @returns Array
8602        * @short Groups the array into `num` arrays.
8603        * @extra If specified, [padding] will be added to the last array to be of
8604        *        equal length.
8605        *
8606        * @example
8607        *
8608        *   [1,2,3,4,5,6,7].inGroups(3)    -> [[1,2,3],[4,5,6],[7]]
8609        *   [1,2,3,4,5,6,7].inGroups(3, 0) -> [[1,2,3],[4,5,6],[7,0,0]]
8610        *
8611        * @param {number} num
8612        * @param {any} [padding]
8613        *
8614        ***/
8615        'inGroups': function(arr, num, padding) {
8616            var pad = isDefined(padding);
8617            var result = new Array(num);
8618            var divisor = ceil(arr.length / num);
8619            simpleRepeat(num, function(i) {
8620                var index = i * divisor;
8621                var group = arr.slice(index, index + divisor);
8622                if (pad && group.length < divisor) {
8623                    simpleRepeat(divisor - group.length, function() {
8624                        group.push(padding);
8625                    });
8626                }
8627                result[i] = group;
8628            });
8629            return result;
8630        },
8631
8632        /***
8633        * @method inGroupsOf(num, [padding] = null)
8634        * @returns Array
8635        * @short Groups the array into arrays of `num` elements each.
8636        * @extra [padding] will be added to the last array to be of equal length.
8637        *
8638        * @example
8639        *
8640        *   [1,2,3,4,5,6,7].inGroupsOf(4)    -> [ [1,2,3,4], [5,6,7] ]
8641        *   [1,2,3,4,5,6,7].inGroupsOf(4, 0) -> [ [1,2,3,4], [5,6,7,0] ]
8642        *
8643        * @param {number} num
8644        * @param {any} [padding]
8645        *
8646        ***/
8647        'inGroupsOf': function(arr, num, padding) {
8648            var result = [], len = arr.length, group;
8649            if (len === 0 || num === 0) return arr;
8650            if (isUndefined(num)) num = 1;
8651            if (isUndefined(padding)) padding = null;
8652            simpleRepeat(ceil(len / num), function(i) {
8653                group = arr.slice(num * i, num * i + num);
8654                while (group.length < num) {
8655                    group.push(padding);
8656                }
8657                result.push(group);
8658            });
8659            return result;
8660        },
8661
8662        /***
8663        * @method shuffle()
8664        * @returns Array
8665        * @short Returns a copy of the array with the elements randomized.
8666        * @extra Uses Fisher-Yates algorithm.
8667        *
8668        * @example
8669        *
8670        *   [1,2,3,4].shuffle()  -> [?,?,?,?]
8671        *
8672        ***/
8673        'shuffle': function(arr) {
8674            return arrayShuffle(arr);
8675        },
8676
8677        /***
8678        * @method sample([num] = 1, [remove] = false)
8679        * @returns Mixed
8680        * @short Returns a random element from the array.
8681        * @extra If [num] is passed, will return an array of [num] elements. If
8682        *        [remove] is true, sampled elements will also be removed from the
8683        *        array. [remove] can also be passed in place of [num].
8684        *
8685        * @example
8686        *
8687        *   [1,2,3,4,5].sample()  -> // Random element
8688        *   [1,2,3,4,5].sample(1) -> // Array of 1 random element
8689        *   [1,2,3,4,5].sample(3) -> // Array of 3 random elements
8690        *
8691        * @param {number} [num]
8692        * @param {boolean} [remove]
8693        *
8694        ***/
8695        'sample': function(arr, arg1, arg2) {
8696            var result = [], num, remove, single;
8697            if (isBoolean(arg1)) {
8698                remove = arg1;
8699            } else {
8700                num = arg1;
8701                remove = arg2;
8702            }
8703            if (isUndefined(num)) {
8704                num = 1;
8705                single = true;
8706            }
8707            if (!remove) {
8708                arr = arrayClone(arr);
8709            }
8710            num = min(num, arr.length);
8711            for (var i = 0, index; i < num; i++) {
8712                index = trunc(Math.random() * arr.length);
8713                result.push(arr[index]);
8714                arr.splice(index, 1);
8715            }
8716            return single ? result[0] : result;
8717        },
8718
8719        /***
8720        * @method sortBy([map], [desc] = false)
8721        * @returns Array
8722        * @short Enhanced sorting function that will sort the array by `map`.
8723        * @extra `map` may be a function, a string acting as a shortcut, an array
8724        *        (comparison by multiple values), or blank (direct comparison of
8725        *        array values). `map` supports `deep properties`. [desc] will sort
8726        *        the array in descending order. When the field being sorted on is
8727        *        a string, the resulting order will be determined by an internal
8728        *        collation algorithm that is optimized for major Western languages,
8729        *        but can be customized using sorting accessors such as `sortIgnore`.
8730        *        This method will modify the array!
8731        *
8732        * @callback sortMapFn
8733        *
8734        *   el   An array element.
8735        *
8736        * @example
8737        *
8738        *   ['world','a','new'].sortBy('length')       -> ['a','new','world']
8739        *   ['world','a','new'].sortBy('length', true) -> ['world','new','a']
8740        *   users.sortBy(function(n) {
8741        *     return n.age;
8742        *   }); -> users array sorted by age
8743        *   users.sortBy('age') -> users array sorted by age
8744        *
8745        * @param {string|sortMapFn} [map]
8746        * @param {boolean} [desc]
8747        * @callbackParam {ArrayElement} el
8748        * @callbackReturns {NewArrayElement} sortMapFn
8749        *
8750        ***/
8751        'sortBy': function(arr, map, desc) {
8752            arr.sort(function(a, b) {
8753                var aProperty = mapWithShortcuts(a, map, arr, [a]);
8754                var bProperty = mapWithShortcuts(b, map, arr, [b]);
8755                return compareValue(aProperty, bProperty) * (desc ? -1 : 1);
8756            });
8757            return arr;
8758        },
8759
8760        /***
8761        * @method remove(search)
8762        * @returns Array
8763        * @short Removes any element in the array that matches `search`.
8764        * @extra This method will modify the array! Use `exclude` for a
8765        *        non-destructive alias. This method implements `enhanced matching`.
8766        *
8767        * @callback searchFn
8768        *
8769        *   el   The element of the current iteration.
8770        *   i    The index of the current iteration.
8771        *   arr  A reference to the array.
8772        *
8773        * @example
8774        *
8775        *   [1,2,3].remove(3)         -> [1,2]
8776        *   ['a','b','c'].remove(/b/) -> ['a','c']
8777        *   [{a:1},{b:2}].remove(function(n) {
8778        *     return n['a'] == 1;
8779        *   }); -> [{b:2}]
8780        *
8781        * @param {ArrayElement|searchFn} search
8782        * @callbackParam {ArrayElement} el
8783        * @callbackParam {number} i
8784        * @callbackParam {Array} arr
8785        * @callbackReturns {boolean} searchFn
8786        *
8787        ***/
8788        'remove': function(arr, f) {
8789            return arrayRemove(arr, f);
8790        },
8791
8792        /***
8793        * @method exclude(search)
8794        * @returns Array
8795        * @short Returns a new array with every element that does not match `search`.
8796        * @extra This method can be thought of as the inverse of `Array#filter`. It
8797        *        will not modify the original array, Use `remove` to modify the
8798        *        array in place. Implements `enhanced matching`.
8799        *
8800        * @callback searchFn
8801        *
8802        *   el   The element of the current iteration.
8803        *   i    The index of the current iteration.
8804        *   arr  A reference to the array.
8805        *
8806        * @example
8807        *
8808        *   [1,2,3].exclude(3)         -> [1,2]
8809        *   ['a','b','c'].exclude(/b/) -> ['a','c']
8810        *   [{a:1},{b:2}].exclude(function(n) {
8811        *     return n['a'] == 1;
8812        *   }); -> [{b:2}]
8813        *
8814        * @param {ArrayElement|searchFn} search
8815        * @callbackParam {ArrayElement} el
8816        * @callbackParam {number} i
8817        * @callbackParam {Array} arr
8818        * @callbackReturns {boolean} searchFn
8819        *
8820        ***/
8821        'exclude': function(arr, f) {
8822            return arrayExclude(arr, f);
8823        },
8824
8825        /***
8826        * @method union(arr)
8827        * @returns Array
8828        * @short Returns a new array containing elements in both arrays with
8829        *        duplicates removed.
8830        * @extra In addition to primitives, this method will also deep-check objects
8831        *        for equality.
8832        *
8833        * @example
8834        *
8835        *   [1,3,5].union([5,7,9])     -> [1,3,5,7,9]
8836        *   ['a','b'].union(['b','c']) -> ['a','b','c']
8837        *
8838        * @param {Array} arr
8839        *
8840        ***/
8841        'union': function(arr1, arr2) {
8842            return arrayUnique(arrayConcat(arr1, arr2));
8843        },
8844
8845        /***
8846        * @method intersect(arr)
8847        * @returns Array
8848        * @short Returns a new array containing any elements that both arrays have in
8849        *        common.
8850        * @extra In addition to primitives, this method will also deep-check objects
8851        *        for equality.
8852        *
8853        * @example
8854        *
8855        *   [1,3,5].intersect([5,7,9])     -> [5]
8856        *   ['a','b'].intersect(['b','c']) -> ['b']
8857        *
8858        * @param {Array} arr
8859        *
8860        ***/
8861        'intersect': function(arr1, arr2) {
8862            return arrayIntersectOrSubtract(arr1, arr2, false);
8863        }
8864
8865    });
8866
8867    defineInstanceWithArguments(sugarArray, {
8868
8869        /***
8870        * @method zip([arr1], [arr2], ...)
8871        * @returns Array
8872        * @short Merges multiple arrays together.
8873        * @extra This method "zips up" smaller arrays into one large whose elements
8874        *        are "all elements at index 0", "all elements at index 1", etc.
8875        *        Useful when you have associated data that is split over separated
8876        *        arrays. If the arrays passed have more elements than the original
8877        *        array, they will be discarded. If they have fewer elements, the
8878        *        missing elements will filled with `null`.
8879        *
8880        * @example
8881        *
8882        *   [1,2,3].zip([4,5,6]) -> [[1,2], [3,4], [5,6]]
8883        *
8884        * @param {Array} arr1
8885        * @param {Array} arr2
8886        *
8887        ***/
8888        'zip': function(arr, args) {
8889            return map(arr, function(el, i) {
8890                return [el].concat(map(args, function(k) {
8891                    return (i in k) ? k[i] : null;
8892                }));
8893            });
8894        }
8895
8896    });
8897
8898    /***
8899    * @method insert(item, [index])
8900    * @returns Array
8901    * @short Appends `item` to the array at [index].
8902    * @extra This method is simply a more readable alias for `append` when passing
8903    *        an index. If `el` is an array it will be joined. This method modifies
8904    *        the array! Use `add` as a non-destructive alias.
8905    *
8906    * @example
8907    *
8908    *   [1,3,4,5].insert(2, 1)     -> [1,2,3,4,5]
8909    *   [1,4,5,6].insert([2,3], 1) -> [1,2,3,4,5,6]
8910    *
8911    * @param {ArrayElement|Array} item
8912    * @param {number} [index]
8913    *
8914    ***/
8915    alias(sugarArray, 'insert', 'append');
8916
8917    setArrayChainableConstructor();
8918
8919    /***
8920    * @module Object
8921    * @description Object creation, manipulation, comparison, type checking, and more.
8922    *
8923    * Much thanks to kangax for his informative aricle about how problems with
8924    * instanceof and constructor: http://bit.ly/1Qds27W
8925    *
8926    ***/
8927
8928    // Matches bracket-style query strings like user[name]
8929    var DEEP_QUERY_STRING_REG = /^(.+?)(\[.*\])$/;
8930
8931    // Matches any character not allowed in a decimal number.
8932    var NON_DECIMAL_REG = /[^\d.-]/;
8933
8934    // Native methods for merging by descriptor when available.
8935    var getOwnPropertyNames = Object.getOwnPropertyNames;
8936    var getOwnPropertySymbols = Object.getOwnPropertySymbols;
8937    var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
8938
8939    // Basic Helpers
8940
8941    function isArguments(obj, className) {
8942        className = className || classToString(obj);
8943        // .callee exists on Arguments objects in < IE8
8944        return hasProperty(obj, 'length') && (className === '[object Arguments]' || !!obj.callee);
8945    }
8946
8947    // Query Strings | Creating
8948
8949    function toQueryStringWithOptions(obj, opts) {
8950        opts = opts || {};
8951        if (isUndefined(opts.separator)) {
8952            opts.separator = '_';
8953        }
8954        return toQueryString(obj, opts.deep, opts.transform, opts.prefix || '', opts.separator);
8955    }
8956
8957    function toQueryString(obj, deep, transform, prefix, separator) {
8958        if (isArray(obj)) {
8959            return collectArrayAsQueryString(obj, deep, transform, prefix, separator);
8960        } else if (isObjectType(obj) && obj.toString === internalToString) {
8961            return collectObjectAsQueryString(obj, deep, transform, prefix, separator);
8962        } else if (prefix) {
8963            return getURIComponentValue(obj, prefix, transform);
8964        }
8965        return '';
8966    }
8967
8968    function collectArrayAsQueryString(arr, deep, transform, prefix, separator) {
8969        var el, qc, key, result = [];
8970        // Intentionally treating sparse arrays as dense here by avoiding map,
8971        // otherwise indexes will shift during the process of serialization.
8972        for (var i = 0, len = arr.length; i < len; i++) {
8973            el = arr[i];
8974            key = prefix + (prefix && deep ? '[]' : '');
8975            if (!key && !isObjectType(el)) {
8976                // If there is no key, then the values of the array should be
8977                // considered as null keys, so use them instead;
8978                qc = sanitizeURIComponent(el);
8979            } else {
8980                qc = toQueryString(el, deep, transform, key, separator);
8981            }
8982            result.push(qc);
8983        }
8984        return result.join('&');
8985    }
8986
8987    function collectObjectAsQueryString(obj, deep, transform, prefix, separator) {
8988        var result = [];
8989        forEachProperty(obj, function(val, key) {
8990            var fullKey;
8991            if (prefix && deep) {
8992                fullKey = prefix + '[' + key + ']';
8993            } else if (prefix) {
8994                fullKey = prefix + separator + key;
8995            } else {
8996                fullKey = key;
8997            }
8998            result.push(toQueryString(val, deep, transform, fullKey, separator));
8999        });
9000        return result.join('&');
9001    }
9002
9003    function getURIComponentValue(obj, prefix, transform) {
9004        var value;
9005        if (transform) {
9006            value = transform(obj, prefix);
9007        } else if (isDate(obj)) {
9008            value = obj.getTime();
9009        } else {
9010            value = obj;
9011        }
9012        return sanitizeURIComponent(prefix) + '=' + sanitizeURIComponent(value);
9013    }
9014
9015    function sanitizeURIComponent(obj) {
9016        // undefined, null, and NaN are represented as a blank string,
9017        // while false and 0 are stringified.
9018        return !obj && obj !== false && obj !== 0 ? '' : encodeURIComponent(obj);
9019    }
9020
9021
9022    // Query Strings | Parsing
9023
9024    function fromQueryStringWithOptions(obj, opts) {
9025        var str = String(obj || '').replace(/^.*?\?/, ''), result = {}, auto;
9026        opts = opts || {};
9027        if (str) {
9028            forEach(str.split('&'), function(p) {
9029                var split = p.split('=');
9030                var key = decodeURIComponent(split[0]);
9031                var val = split.length === 2 ? decodeURIComponent(split[1]) : '';
9032                auto = opts.auto !== false;
9033                parseQueryComponent(result, key, val, opts.deep, auto, opts.separator, opts.transform);
9034            });
9035        }
9036        return result;
9037    }
9038
9039    function parseQueryComponent(obj, key, val, deep, auto, separator, transform) {
9040        var match;
9041        if (separator) {
9042            key = mapQuerySeparatorToKeys(key, separator);
9043            deep = true;
9044        }
9045        if (deep === true && (match = key.match(DEEP_QUERY_STRING_REG))) {
9046            parseDeepQueryComponent(obj, match, val, deep, auto, separator, transform);
9047        } else {
9048            setQueryProperty(obj, key, val, auto, transform);
9049        }
9050    }
9051
9052    function parseDeepQueryComponent(obj, match, val, deep, auto, separator, transform) {
9053        var key = match[1];
9054        var inner = match[2].slice(1, -1).split('][');
9055        forEach(inner, function(k) {
9056            if (!hasOwn(obj, key)) {
9057                obj[key] = k ? {} : [];
9058            }
9059            obj = getOwn(obj, key);
9060            key = k ? k : obj.length.toString();
9061        });
9062        setQueryProperty(obj, key, val, auto, transform);
9063    }
9064
9065    function mapQuerySeparatorToKeys(key, separator) {
9066        var split = key.split(separator), result = split[0];
9067        for (var i = 1, len = split.length; i < len; i++) {
9068            result += '[' + split[i] + ']';
9069        }
9070        return result;
9071    }
9072
9073    function setQueryProperty(obj, key, val, auto, transform) {
9074        var fnValue;
9075        if (transform) {
9076            fnValue = transform(val, key, obj);
9077        }
9078        if (isDefined(fnValue)) {
9079            val = fnValue;
9080        } else if (auto) {
9081            val = getQueryValueAuto(obj, key, val);
9082        }
9083        obj[key] = val;
9084    }
9085
9086    function getQueryValueAuto(obj, key, val) {
9087        if (!val) {
9088            return null;
9089        } else if (val === 'true') {
9090            return true;
9091        } else if (val === 'false') {
9092            return false;
9093        }
9094        var num = +val;
9095        if (!isNaN(num) && stringIsDecimal(val)) {
9096            return num;
9097        }
9098        var existing = getOwn(obj, key);
9099        if (val && existing) {
9100            return isArray(existing) ? existing.concat(val) : [existing, val];
9101        }
9102        return val;
9103    }
9104
9105    function stringIsDecimal(str) {
9106        return str !== '' && !NON_DECIMAL_REG.test(str);
9107    }
9108
9109
9110    // Object Merging
9111
9112    function mergeWithOptions(target, source, opts) {
9113        opts = opts || {};
9114        return objectMerge(target, source, opts.deep, opts.resolve, opts.hidden, opts.descriptor);
9115    }
9116
9117    function defaults(target, sources, opts) {
9118        opts = opts || {};
9119        opts.resolve = opts.resolve || false;
9120        return mergeAll(target, sources, opts);
9121    }
9122
9123    function mergeAll(target, sources, opts) {
9124        if (!isArray(sources)) {
9125            sources = [sources];
9126        }
9127        forEach(sources, function(source) {
9128            return mergeWithOptions(target, source, opts);
9129        });
9130        return target;
9131    }
9132
9133    function iterateOverProperties(hidden, obj, fn) {
9134        if (getOwnPropertyNames && hidden) {
9135            iterateOverKeys(getOwnPropertyNames, obj, fn, hidden);
9136        } else {
9137            forEachProperty(obj, fn);
9138        }
9139        if (getOwnPropertySymbols) {
9140            iterateOverKeys(getOwnPropertySymbols, obj, fn, hidden);
9141        }
9142    }
9143
9144    // "keys" may include symbols
9145    function iterateOverKeys(getFn, obj, fn, hidden) {
9146        var keys = getFn(obj), desc;
9147        for (var i = 0, key; key = keys[i]; i++) {
9148            desc = getOwnPropertyDescriptor(obj, key);
9149            if (desc.enumerable || hidden) {
9150                fn(obj[key], key);
9151            }
9152        }
9153    }
9154
9155    function mergeByPropertyDescriptor(target, source, prop, sourceVal) {
9156        var descriptor = getOwnPropertyDescriptor(source, prop);
9157        if (isDefined(descriptor.value)) {
9158            descriptor.value = sourceVal;
9159        }
9160        defineProperty(target, prop, descriptor);
9161    }
9162
9163    function objectMerge(target, source, deep, resolve, hidden, descriptor) {
9164        var resolveByFunction = isFunction(resolve), resolveConflicts = resolve !== false;
9165
9166        if (isUndefined(target)) {
9167            target = getNewObjectForMerge(source);
9168        } else if (resolveConflicts && isDate(target) && isDate(source)) {
9169            // A date's timestamp is a property that can only be reached through its
9170            // methods, so actively set it up front if both are dates.
9171            target.setTime(source.getTime());
9172        }
9173
9174        if (isPrimitive(target)) {
9175            // Will not merge into a primitive type, so simply override.
9176            return source;
9177        }
9178
9179        // If the source object is a primitive
9180        // type then coerce it into an object.
9181        if (isPrimitive(source)) {
9182            source = coercePrimitiveToObject(source);
9183        }
9184
9185        iterateOverProperties(hidden, source, function(val, key) {
9186            var sourceVal, targetVal, resolved, goDeep, result;
9187
9188            sourceVal = source[key];
9189
9190            // We are iterating over properties of the source, so hasOwnProperty on
9191            // it is guaranteed to always be true. However, the target may happen to
9192            // have properties in its prototype chain that should not be considered
9193            // as conflicts.
9194            targetVal = getOwn(target, key);
9195
9196            if (resolveByFunction) {
9197                result = resolve(key, targetVal, sourceVal, target, source);
9198                if (isUndefined(result)) {
9199                    // Result is undefined so do not merge this property.
9200                    return;
9201                } else if (isDefined(result) && result !== Sugar) {
9202                    // If the source returns anything except undefined, then the conflict
9203                    // has been resolved, so don't continue traversing into the object. If
9204                    // the returned value is the Sugar global object, then allowing Sugar
9205                    // to resolve the conflict, so continue on.
9206                    sourceVal = result;
9207                    resolved = true;
9208                }
9209            } else if (isUndefined(sourceVal)) {
9210                // Will not merge undefined.
9211                return;
9212            }
9213
9214            // Regex properties are read-only, so intentionally disallowing deep
9215            // merging for now. Instead merge by reference even if deep.
9216            goDeep = !resolved && deep && isObjectType(sourceVal) && !isRegExp(sourceVal);
9217
9218            if (!goDeep && !resolveConflicts && isDefined(targetVal)) {
9219                return;
9220            }
9221
9222            if (goDeep) {
9223                sourceVal = objectMerge(targetVal, sourceVal, deep, resolve, hidden, descriptor);
9224            }
9225
9226            // getOwnPropertyNames is standing in as
9227            // a test for property descriptor support
9228            if (getOwnPropertyNames && descriptor) {
9229                mergeByPropertyDescriptor(target, source, key, sourceVal);
9230            } else {
9231                target[key] = sourceVal;
9232            }
9233
9234        });
9235        return target;
9236    }
9237
9238    function getNewObjectForMerge(source) {
9239        var klass = classToString(source);
9240        // Primitive types, dates, and regexes have no "empty" state. If they exist
9241        // at all, then they have an associated value. As we are only creating new
9242        // objects when they don't exist in the target, these values can come alone
9243        // for the ride when created.
9244        if (isArray(source, klass)) {
9245            return [];
9246        } else if (isPlainObject(source, klass)) {
9247            return {};
9248        } else if (isDate(source, klass)) {
9249            return new Date(source.getTime());
9250        } else if (isRegExp(source, klass)) {
9251            return RegExp(source.source, getRegExpFlags(source));
9252        } else if (isPrimitive(source && source.valueOf())) {
9253            return source;
9254        }
9255        // If the object is not of a known type, then simply merging its
9256        // properties into a plain object will result in something different
9257        // (it will not respond to instanceof operator etc). Similarly we don't
9258        // want to call a constructor here as we can't know for sure what the
9259        // original constructor was called with (Events etc), so throw an
9260        // error here instead. Non-standard types can be handled if either they
9261        // already exist and simply have their properties merged, if the merge
9262        // is not deep so their references will simply be copied over, or if a
9263        // resolve function is used to assist the merge.
9264        throw new TypeError('Must be a basic data type');
9265    }
9266
9267    function clone(source, deep) {
9268        var target = getNewObjectForMerge(source);
9269        return objectMerge(target, source, deep, true, true, true);
9270    }
9271
9272
9273    // Keys/Values
9274
9275    function objectSize(obj) {
9276        return getKeysWithObjectCoercion(obj).length;
9277    }
9278
9279    function getKeysWithObjectCoercion(obj) {
9280        return getKeys(coercePrimitiveToObject(obj));
9281    }
9282
9283    function getValues(obj) {
9284        var values = [];
9285        forEachProperty(obj, function(val) {
9286            values.push(val);
9287        });
9288        return values;
9289    }
9290
9291    function tap(obj, arg) {
9292        var fn = arg;
9293        if (!isFunction(arg)) {
9294            fn = function() {
9295                if (arg) obj[arg]();
9296            };
9297        }
9298        fn.call(obj, obj);
9299        return obj;
9300    }
9301
9302    // Select/Reject
9303
9304    function objectSelect(obj, f) {
9305        return selectFromObject(obj, f, true);
9306    }
9307
9308    function objectReject(obj, f) {
9309        return selectFromObject(obj, f, false);
9310    }
9311
9312    function selectFromObject(obj, f, select) {
9313        var match, result = {};
9314        f = [].concat(f);
9315        forEachProperty(obj, function(val, key) {
9316            match = false;
9317            for (var i = 0; i < f.length; i++) {
9318                if (matchInObject(f[i], key)) {
9319                    match = true;
9320                }
9321            }
9322            if (match === select) {
9323                result[key] = val;
9324            }
9325        });
9326        return result;
9327    }
9328
9329    function matchInObject(match, key) {
9330        if (isRegExp(match)) {
9331            return match.test(key);
9332        } else if (isObjectType(match)) {
9333            return key in match;
9334        } else {
9335            return key === String(match);
9336        }
9337    }
9338
9339    // Remove/Exclude
9340
9341    function objectRemove(obj, f) {
9342        var matcher = getMatcher(f);
9343        forEachProperty(obj, function(val, key) {
9344            if (matcher(val, key, obj)) {
9345                delete obj[key];
9346            }
9347        });
9348        return obj;
9349    }
9350
9351    function objectExclude(obj, f) {
9352        var result = {};
9353        var matcher = getMatcher(f);
9354        forEachProperty(obj, function(val, key) {
9355            if (!matcher(val, key, obj)) {
9356                result[key] = val;
9357            }
9358        });
9359        return result;
9360    }
9361
9362    function objectIntersectOrSubtract(obj1, obj2, subtract) {
9363        if (!isObjectType(obj1)) {
9364            return subtract ? obj1 : {};
9365        }
9366        obj2 = coercePrimitiveToObject(obj2);
9367        function resolve(key, val, val1) {
9368            var exists = key in obj2 && isEqual(val1, obj2[key]);
9369            if (exists !== subtract) {
9370                return val1;
9371            }
9372        }
9373        return objectMerge({}, obj1, false, resolve);
9374    }
9375
9376    /***
9377    * @method is[Type]()
9378    * @returns Boolean
9379    * @short Returns true if the object is an object of that type.
9380    *
9381    * @set
9382    *   isArray
9383    *   isBoolean
9384    *   isDate
9385    *   isError
9386    *   isFunction
9387    *   isMap
9388    *   isNumber
9389    *   isRegExp
9390    *   isSet
9391    *   isString
9392    *
9393    * @example
9394    *
9395    *   Object.isArray([3]) -> true
9396    *   Object.isNumber(3)  -> true
9397    *   Object.isString(8)  -> false
9398    *
9399    ***/
9400    function buildClassCheckMethods() {
9401        var checks = [isBoolean, isNumber, isString, isDate, isRegExp, isFunction, isArray, isError, isSet, isMap];
9402        defineInstanceAndStaticSimilar(sugarObject, NATIVE_TYPES, function(methods, name, i) {
9403            methods['is' + name] = checks[i];
9404        });
9405    }
9406
9407    defineStatic(sugarObject, {
9408
9409        /***
9410        * @method fromQueryString(str, [options])
9411        * @returns Object
9412        * @static
9413        * @short Converts the query string of a URL into an object.
9414        * @extra Options can be passed with [options] for more control over the result.
9415        *
9416        * @options
9417        *
9418        *   deep        If the string contains "deep" syntax `foo[]`, this will
9419        *               be automatically converted to an array. (Default `false`)
9420        *
9421        *   auto        If `true`, booleans `"true"` and `"false"`, numbers, and arrays
9422        *               (repeated keys) will be automatically cast to native
9423        *               values. (Default `true`)
9424        *
9425        *   transform   A function whose return value becomes the final value. If
9426        *               the function returns `undefined`, then the original value
9427        *               will be used. This allows the function to intercept only
9428        *               certain keys or values. (Default `undefined`)
9429        *
9430        *   separator   If passed, keys will be split on this string to extract
9431        *               deep values. (Default `''`)
9432        *
9433        * @callback queryStringTransformFn
9434        *
9435        *   key   The key component of the query string (before `=`).
9436        *   val   The value component of the query string (after `=`).
9437        *   obj   A reference to the object being built.
9438        *
9439        * @example
9440        *
9441        *   Object.fromQueryString('a=1&b=2')                 -> {a:1,b:2}
9442        *   Object.fromQueryString('a[]=1&a[]=2',{deep:true}) -> {a:['1','2']}
9443        *   Object.fromQueryString('a_b=c',{separator:'_'})   -> {a:{b:'c'}}
9444        *   Object.fromQueryString('id=123', {transform:idToNumber});
9445        *
9446        * @param {string} str
9447        * @param {QueryStringParseOptions} options
9448        * @callbackParam {string} key
9449        * @callbackParam {Property} val
9450        * @callbackParam {Object} obj
9451        * @callbackReturns {NewProperty} queryStringTransformFn
9452        * @option {boolean} [deep]
9453        * @option {boolean} [auto]
9454        * @option {string} [separator]
9455        * @option {queryStringTransformFn} [transform]
9456        *
9457        ***/
9458        'fromQueryString': function(obj, options) {
9459            return fromQueryStringWithOptions(obj, options);
9460        }
9461
9462    });
9463
9464    defineInstanceAndStatic(sugarObject, {
9465
9466        /***
9467        * @method has(key, [inherited] = false)
9468        * @returns Boolean
9469        * @short Checks if the object has property `key`.
9470        * @extra Supports `deep properties`. If [inherited] is `true`,
9471        *        properties defined in the prototype chain will also return `true`.
9472        *        The default of `false` for this argument makes this method suited
9473        *        to working with objects as data stores by default.
9474        *
9475        * @example
9476        *
9477        *   Object.has(usersByName, 'Harry')     -> true
9478        *   Object.has(data, 'users[1].profile') -> true
9479        *   Object.has([], 'forEach')            -> false
9480        *   Object.has([], 'forEach', true)      -> true
9481        *
9482        * @param {string} key
9483        * @param {boolean} [inherited]
9484        *
9485        ***/
9486        'has': function(obj, key, any) {
9487            return deepHasProperty(obj, key, any);
9488        },
9489
9490        /***
9491        * @method get(key, [inherited] = false)
9492        * @returns Mixed
9493        * @short Gets a property of the object.
9494        * @extra Supports `deep properties`. If [inherited] is `true`,
9495        *        properties defined in the prototype chain will also be returned.
9496        *        The default of `false` for this argument makes this method suited
9497        *        to working with objects as data stores by default.
9498        *
9499        * @example
9500        *
9501        *   Object.get(Harry, 'name');           -> 'Harry'
9502        *   Object.get(Harry, 'profile.likes');  -> Harry's likes
9503        *   Object.get(data, 'users[3].name')    -> User 3's name
9504        *   Object.get(data, 'users[1..2]')      -> Users 1 and 2
9505        *   Object.get(data, 'users[1..2].name') -> Names of users 1 and 2
9506        *   Object.get(data, 'users[-2..-1]')    -> Last 2 users
9507        *
9508        * @param {string} key
9509        * @param {boolean} [inherited]
9510        *
9511        ***/
9512        'get': function(obj, key, any) {
9513            return deepGetProperty(obj, key, any);
9514        },
9515
9516        /***
9517        * @method set(key, val)
9518        * @returns Object
9519        * @short Sets a property on the object.
9520        * @extra Using a dot or square bracket in `key` is considered "deep" syntax,
9521        *        and will attempt to traverse into the object to set the property,
9522        *        creating properties that do not exist along the way. If the missing
9523        *        property is referenced using square brackets, an empty array will be
9524        *        created, otherwise an empty object. A special `[]` carries the
9525        *        meaning of "the last index + 1", and will effectively push `val`
9526        *        onto the end of the array. Lastly, a `..` separator inside the
9527        *        brackets is "range" notation, and will set properties on all
9528        *        elements in the specified range. Range members may be negative,
9529        *        which will be offset from the end of the array.
9530        *
9531        * @example
9532        *
9533        *   Object.set({}, 'name', 'Harry');         -> {name:'Harry'}
9534        *   Object.set({}, 'user.name', 'Harry');    -> {user:{name:'Harry'}}
9535        *   Object.set({}, 'users[].name', 'Bob')    -> {users:[{name:'Bob'}}
9536        *   Object.set({}, 'users[1].name','Bob')    -> {users:[undefined, {name:'Bob'}]}
9537        *   Object.set({}, 'users[0..1].name','Bob') -> {users:[{name:'Bob'},{name:'Bob'}]}
9538        *
9539        * @param {string} key
9540        * @param {Property} val
9541        *
9542        ***/
9543        'set': function(obj, key, val) {
9544            return deepSetProperty(obj, key, val);
9545        },
9546
9547        /***
9548        * @method size()
9549        * @returns Number
9550        * @short Returns the number of properties in the object.
9551        *
9552        * @example
9553        *
9554        *   Object.size({foo:'bar'}) -> 1
9555        *
9556        ***/
9557        'size': function(obj) {
9558            return objectSize(obj);
9559        },
9560
9561        /***
9562        * @method isEmpty()
9563        * @returns Boolean
9564        * @short Returns true if the number of properties in the object is zero.
9565        *
9566        * @example
9567        *
9568        *   Object.isEmpty({})    -> true
9569        *   Object.isEmpty({a:1}) -> false
9570        *
9571        ***/
9572        'isEmpty': function(obj) {
9573            return objectSize(obj) === 0;
9574        },
9575
9576        /***
9577        * @method toQueryString([options])
9578        * @returns Object
9579        * @short Converts the object into a query string.
9580        * @extra Accepts deep objects and arrays. [options] can be passed for more
9581        *        control over the result.
9582        *
9583        * @options
9584        *
9585        *   deep        If `true`, non-standard "deep" syntax `foo[]` will be
9586        *               used for output. Note that `separator` will be ignored,
9587        *               as this option overrides shallow syntax. (Default `false`)
9588        *
9589        *   prefix      If passed, this string will be prefixed to all keys,
9590        *               separated by the `separator`. (Default `''`).
9591        *
9592        *   transform   A function whose return value becomes the final value
9593        *               in the string. (Default `undefined`)
9594        *
9595        *   separator   A string that is used to separate keys, either for deep
9596        *               objects, or when `prefix` is passed.(Default `_`).
9597        *
9598        * @callback queryStringTransformFn
9599        *
9600        *   key  The key of the current iteration.
9601        *   val  The value of the current iteration.
9602        *   obj  A reference to the object.
9603        *
9604        * @example
9605        *
9606        *   Object.toQueryString({foo:'bar'})                  -> 'foo=bar'
9607        *   Object.toQueryString({foo:['a','b']})              -> 'foo=a&foo=b'
9608        *   Object.toQueryString({foo:['a','b']}, {deep:true}) -> 'foo[]=a&foo[]=b'
9609        *
9610        * @param {Object} obj
9611        * @param {QueryStringOptions} [options]
9612        * @callbackParam {string} key
9613        * @callbackParam {Property} val
9614        * @callbackParam {Object} obj
9615        * @callbackReturns {NewProperty} queryStringTransformFn
9616        * @option {boolean} [deep]
9617        * @option {string} [prefix]
9618        * @option {string} [separator]
9619        * @option {queryStringTransformFn} [transform]
9620        *
9621        ***/
9622        'toQueryString': function(obj, options) {
9623            return toQueryStringWithOptions(obj, options);
9624        },
9625
9626        /***
9627        * @method isEqual(obj)
9628        * @returns Boolean
9629        * @short Returns true if `obj` is equivalent to the object.
9630        * @extra If both objects are built-in types, they will be considered
9631        *        equivalent if they are not "observably distinguishable". This means
9632        *        that primitives and object types, `0` and `-0`, and sparse and
9633        *        dense arrays are all not equal. Functions and non-built-ins like
9634        *        instances of user-defined classes and host objects like Element and
9635        *        Event are strictly compared `===`, and will only be equal if they
9636        *        are the same reference. Plain objects as well as Arrays will be
9637        *        traversed into and deeply checked by their non-inherited, enumerable
9638        *        properties. Other allowed types include Typed Arrays, Sets, Maps,
9639        *        Arguments, Dates, Regexes, and Errors.
9640        *
9641        * @example
9642        *
9643        *   Object.isEqual({a:2}, {a:2})         -> true
9644        *   Object.isEqual({a:2}, {a:3})         -> false
9645        *   Object.isEqual(5, Object(5))         -> false
9646        *   Object.isEqual(Object(5), Object(5)) -> true
9647        *   Object.isEqual(NaN, NaN)             -> false
9648        *
9649        * @param {Object} obj
9650        *
9651        ***/
9652        'isEqual': function(obj1, obj2) {
9653            return isEqual(obj1, obj2);
9654        },
9655
9656        /***
9657        * @method merge(source, [options])
9658        * @returns Object
9659        * @short Merges properties from `source` into the object.
9660        * @extra This method will modify the object! Use `add` for a non-destructive
9661        *        alias.
9662        *
9663        * @options
9664        *
9665        *   deep         If `true` deep properties are merged recursively.
9666        *                (Default `false`)
9667        *
9668        *   hidden       If `true`, non-enumerable properties will be merged as well.
9669        *                (Default `false`)
9670        *
9671        *   descriptor   If `true`, properties will be merged by property descriptor.
9672        *                Use this option to merge getters or setters, or to preserve
9673        *                `enumerable`, `configurable`, etc.
9674        *                (Default `false`)
9675        *
9676        *   resolve      Determines which property wins in the case of conflicts.
9677        *                If `true`, `source` wins. If `false`, the original property wins.
9678        *                If a function is passed, its return value will decide the result.
9679        *                Any non-undefined return value will resolve the conflict
9680        *                for that property (will not continue if `deep`). Returning
9681        *                `undefined` will do nothing (no merge). Finally, returning
9682        *                the global object `Sugar` will allow Sugar to handle the
9683        *                merge as normal. (Default `true`)
9684        *
9685        * @callback resolveFn
9686        *
9687        *   key        The key of the current iteration.
9688        *   targetVal  The current value for the key in the target.
9689        *   sourceVal  The current value for the key in `source`.
9690        *   target     The target object.
9691        *   source     The source object.
9692        *
9693        * @example
9694        *
9695        *   Object.merge({one:1},{two:2})                 -> {one:1,two:2}
9696        *   Object.merge({one:1},{one:9,two:2})           -> {one:9,two:2}
9697        *   Object.merge({x:{a:1}},{x:{b:2}},{deep:true}) -> {x:{a:1,b:2}}
9698        *   Object.merge({a:1},{a:2},{resolve:mergeAdd})  -> {a:3}
9699        *
9700        * @param {Object} source
9701        * @param {ObjectMergeOptions} [options]
9702        * @callbackParam {string} key
9703        * @callbackParam {Property} targetVal
9704        * @callbackParam {Property} sourceVal
9705        * @callbackParam {Object} target
9706        * @callbackParam {Object} source
9707        * @callbackReturns {boolean} resolveFn
9708        * @option {boolean} [deep]
9709        * @option {boolean} [hidden]
9710        * @option {boolean} [descriptor]
9711        * @option {boolean|resolveFn} [resolve]
9712        *
9713        ***/
9714        'merge': function(target, source, opts) {
9715            return mergeWithOptions(target, source, opts);
9716        },
9717
9718        /***
9719        * @method add(obj, [options])
9720        * @returns Object
9721        * @short Adds properties in `obj` and returns a new object.
9722        * @extra This method will not modify the original object. See `merge` for options.
9723        *
9724        * @example
9725        *
9726        *   Object.add({one:1},{two:2})                 -> {one:1,two:2}
9727        *   Object.add({one:1},{one:9,two:2})           -> {one:9,two:2}
9728        *   Object.add({x:{a:1}},{x:{b:2}},{deep:true}) -> {x:{a:1,b:2}}
9729        *   Object.add({a:1},{a:2},{resolve:mergeAdd})  -> {a:3}
9730        *
9731        * @param {Object} obj
9732        * @param {ObjectMergeOptions} [options]
9733        *
9734        ***/
9735        'add': function(obj1, obj2, opts) {
9736            return mergeWithOptions(clone(obj1), obj2, opts);
9737        },
9738
9739        /***
9740        * @method mergeAll(sources, [options])
9741        * @returns Object
9742        * @short Merges properties from an array of `sources`.
9743        * @extra This method will modify the object! Use `addAll` for a non-destructive
9744        *        alias. See `merge` for options.
9745        *
9746        * @example
9747        *
9748        *   Object.mergeAll({one:1},[{two:2},{three:3}]) -> {one:1,two:2,three:3}
9749        *   Object.mergeAll({x:{a:1}},[{x:{b:2}},{x:{c:3}}],{deep:true}) -> {x:{a:1,b:2,c:3}}
9750        *
9751        * @param {Array<Object>} sources
9752        * @param {ObjectMergeOptions} [options]
9753        *
9754        ***/
9755        'mergeAll': function(target, sources, opts) {
9756            return mergeAll(target, sources, opts);
9757        },
9758
9759        /***
9760        * @method addAll(sources, [options])
9761        * @returns Object
9762        * @short Adds properties from an array of `sources` and returns a new object.
9763        * @extra This method will not modify the object. See `merge` for options.
9764        *
9765        * @example
9766        *
9767        *   Object.addAll({one:1},[{two:2},{three:3}]) -> {one:1,two:2,three:3}
9768        *   Object.addAll({x:{a:1}},[{x:{b:2}},{x:{c:3}}],{deep:true}) -> {x:{a:1,b:2,c:3}}
9769        *
9770        * @param {Array<Object>} sources
9771        * @param {ObjectMergeOptions} [options]
9772        *
9773        ***/
9774        'addAll': function(obj, sources, opts) {
9775            return mergeAll(clone(obj), sources, opts);
9776        },
9777
9778        /***
9779        * @method defaults(sources, [options])
9780        * @returns Object
9781        * @short Merges properties from one or multiple `sources` while preserving
9782        *        the object's defined properties.
9783        * @extra This method modifies the object! See `merge` for options.
9784        *
9785        * @example
9786        *
9787        *   Object.defaults({one:1},[{one:9},{two:2}])                   -> {one:1,two:2}
9788        *   Object.defaults({x:{a:1}},[{x:{a:9}},{x:{b:2}}],{deep:true}) -> {x:{a:1,b:2}}
9789        *
9790        * @param {Array<Object>} sources
9791        * @param {ObjectMergeOptions} [options]
9792        *
9793        ***/
9794        'defaults': function(target, sources, opts) {
9795            return defaults(target, sources, opts);
9796        },
9797
9798        /***
9799        * @method intersect(obj)
9800        * @returns Object
9801        * @short Returns a new object whose properties are those that the object has
9802        *        in common both with `obj`.
9803        * @extra If both key and value do not match, then the property will not be included.
9804        *
9805        * @example
9806        *
9807        *   Object.intersect({a:'a'},{b:'b'}) -> {}
9808        *   Object.intersect({a:'a'},{a:'b'}) -> {}
9809        *   Object.intersect({a:'a',b:'b'},{b:'b',z:'z'}) -> {b:'b'}
9810        *
9811        * @param {Object} obj
9812        *
9813        ***/
9814        'intersect': function(obj1, obj2) {
9815            return objectIntersectOrSubtract(obj1, obj2, false);
9816        },
9817
9818        /***
9819        * @method subtract(obj)
9820        * @returns Object
9821        * @short Returns a clone of the object with any properties shared with `obj` excluded.
9822        * @extra If both key and value do not match, then the property will not be excluded.
9823        *
9824        * @example
9825        *
9826        *   Object.subtract({a:'a',b:'b'},{b:'b'}) -> {a:'a'}
9827        *   Object.subtract({a:'a',b:'b'},{a:'b'}) -> {a:'a',b:'b'}
9828        *
9829        * @param {Object} obj
9830        *
9831        ***/
9832        'subtract': function(obj1, obj2) {
9833            return objectIntersectOrSubtract(obj1, obj2, true);
9834        },
9835
9836        /***
9837        * @method clone([deep] = false)
9838        * @returns Object
9839        * @short Creates a clone of the object.
9840        * @extra Default is a shallow clone, unless [deep] is true.
9841        *
9842        * @example
9843        *
9844        *   Object.clone({foo:'bar'})       -> creates shallow clone
9845        *   Object.clone({foo:'bar'}, true) -> creates a deep clone
9846        *
9847        * @param {boolean} [deep]
9848        *
9849        ***/
9850        'clone': function(obj, deep) {
9851            return clone(obj, deep);
9852        },
9853
9854        /***
9855        * @method values()
9856        * @returns Array
9857        * @short Returns an array containing the values in the object.
9858        * @extra Values are in no particular order. Does not include inherited or
9859        *        non-enumerable properties.
9860        *
9861        * @example
9862        *
9863        *   Object.values({a:'a',b:'b'}) -> ['a','b']
9864        *
9865        ***/
9866        'values': function(obj) {
9867            return getValues(obj);
9868        },
9869
9870        /***
9871        * @method invert([multi] = false)
9872        * @returns Object
9873        * @short Creates a new object with the keys and values swapped.
9874        * @extra If [multi] is true, values will be an array of all keys, othewise
9875        *        collisions will be overwritten.
9876        *
9877        * @example
9878        *
9879        *   Object.invert({foo:'bar'})     -> {bar:'foo'}
9880        *   Object.invert({a:1,b:1}, true) -> {1:['a','b']}
9881        *
9882        * @param {boolean} [multi]
9883        *
9884        ***/
9885        'invert': function(obj, multi) {
9886            var result = {};
9887            multi = multi === true;
9888            forEachProperty(obj, function(val, key) {
9889                if (hasOwn(result, val) && multi) {
9890                    result[val].push(key);
9891                } else if (multi) {
9892                    result[val] = [key];
9893                } else {
9894                    result[val] = key;
9895                }
9896            });
9897            return result;
9898        },
9899
9900        /***
9901        * @method tap(fn)
9902        * @returns Object
9903        * @short Runs `fn` and returns the object.
9904        * @extra A string can also be used as a shortcut to a method. This method is
9905        *        designed to run an intermediary function that "taps into" a method
9906        *        chain. As such, it is fairly useless as a static method. However it
9907        *        can be quite useful when combined with chainables.
9908        *
9909        * @callback tapFn
9910        *
9911        *   obj  A reference to the object.
9912        *
9913        * @example
9914        *
9915        *   Sugar.Array([1,4,9]).map(Math.sqrt).tap('pop') -> [1,2]
9916        *   Sugar.Object({a:'a'}).tap(logArgs).merge({b:'b'})  -> {a:'a',b:'b'}
9917        *
9918        * @param {tapFn} fn
9919        * @callbackParam {Object} obj
9920        * @callbackReturns {any} tapFn
9921        *
9922        ***/
9923        'tap': function(obj, arg) {
9924            return tap(obj, arg);
9925        },
9926
9927        /***
9928        * @method isArguments()
9929        * @returns Boolean
9930        * @short Returns true if the object is an arguments object.
9931        *
9932        * @example
9933        *
9934        *   Object.isArguments([1]) -> false
9935        *
9936        ***/
9937        'isArguments': function(obj) {
9938            return isArguments(obj);
9939        },
9940
9941        /***
9942        * @method isObject()
9943        * @returns Boolean
9944        * @short Returns true if the object is a "plain" object.
9945        * @extra Plain objects do not include instances of classes or "host" objects,
9946        *        such as Elements, Events, etc.
9947        *
9948        * @example
9949        *
9950        *   Object.isObject({ broken:'wear' }) -> true
9951        *
9952        ***/
9953        'isObject': function(obj) {
9954            return isPlainObject(obj);
9955        },
9956
9957        /***
9958        * @method remove(search)
9959        * @returns Object
9960        * @short Deletes all properties in the object matching `search`.
9961        * @extra This method will modify the object!. Implements `enhanced matching`.
9962        *
9963        * @callback searchFn
9964        *
9965        *   key  The key of the current iteration.
9966        *   val  The value of the current iteration.
9967        *   obj  A reference to the object.
9968        *
9969        * @example
9970        *
9971        *   Object.remove({a:'a',b:'b'}, 'a');           -> {b:'b'}
9972        *   Object.remove({a:'a',b:'b',z:'z'}, /[a-f]/); -> {z:'z'}
9973        *
9974        * @param {Property|searchFn} search
9975        * @callbackParam {Property} val
9976        * @callbackParam {string} key
9977        * @callbackParam {Object} obj
9978        * @callbackReturns {boolean} searchFn
9979        *
9980        ***/
9981        'remove': function(obj, f) {
9982            return objectRemove(obj, f);
9983        },
9984
9985        /***
9986        * @method exclude(search)
9987        * @returns Object
9988        * @short Returns a new object with all properties matching `search` removed.
9989        * @extra This is a non-destructive version of `remove` and will not modify
9990        *        the object. Implements `enhanced matching`.
9991        *
9992        * @callback searchFn
9993        *
9994        *   key  The key of the current iteration.
9995        *   val  The value of the current iteration.
9996        *   obj  A reference to the object.
9997        *
9998        * @example
9999        *
10000        *   Object.exclude({a:'a',b:'b'}, 'a');           -> {b:'b'}
10001        *   Object.exclude({a:'a',b:'b',z:'z'}, /[a-f]/); -> {z:'z'}
10002        *
10003        * @param {Property|searchFn} search
10004        * @callbackParam {Property} val
10005        * @callbackParam {string} key
10006        * @callbackParam {Object} obj
10007        * @callbackReturns {boolean} searchFn
10008        *
10009        ***/
10010        'exclude': function(obj, f) {
10011            return objectExclude(obj, f);
10012        },
10013
10014        /***
10015        * @method select(find)
10016        * @returns Object
10017        * @short Builds a new object containing the keys specified in `find`.
10018        * @extra When `find` is a string, a single key will be selected. Arrays or
10019        *        objects match multiple keys, and a regex will match keys by regex.
10020        *
10021        * @example
10022        *
10023        *   Object.select({a:1,b:2}, 'a')           -> {a:1}
10024        *   Object.select({a:1,b:2}, ['a', 'b'])    -> {a:1,b:2}
10025        *   Object.select({a:1,b:2}, /[a-z]/)       -> {a:1,b:2}
10026        *   Object.select({a:1,b:2}, {a:'a',b:'b'}) -> {a:1,b:2}
10027        *
10028        * @param {string|RegExp|Array<string>|Object} find
10029        *
10030        ***/
10031        'select': function(obj, f) {
10032            return objectSelect(obj, f);
10033        },
10034
10035        /***
10036        * @method reject(find)
10037        * @returns Object
10038        * @short Builds a new object containing all keys except those in `find`.
10039        * @extra When `find` is a string, a single key will be rejected. Arrays or
10040        *        objects match multiple keys, and a regex will match keys by regex.
10041        *
10042        * @example
10043        *
10044        *   Object.reject({a:1,b:2}, 'a')        -> {b:2}
10045        *   Object.reject({a:1,b:2}, /[a-z]/)    -> {}
10046        *   Object.reject({a:1,b:2}, {a:'a'})    -> {b:2}
10047        *   Object.reject({a:1,b:2}, ['a', 'b']) -> {}
10048        *
10049        * @param {string|RegExp|Array<string>|Object} find
10050        *
10051        ***/
10052        'reject': function(obj, f) {
10053            return objectReject(obj, f);
10054        }
10055
10056    });
10057
10058    // TODO: why is this here?
10059    defineInstance(sugarObject, {
10060
10061        /***
10062        * @method keys()
10063        * @returns Array
10064        * @polyfill ES5
10065        * @short Returns an array containing the keys of all of the non-inherited,
10066        *        enumerable properties of the object.
10067        *
10068        * @example
10069        *
10070        *   Object.keys({a:'a',b:'b'}) -> ['a','b']
10071        *
10072        ***/
10073        'keys': function(obj) {
10074            return getKeys(obj);
10075        }
10076
10077    });
10078
10079    buildClassCheckMethods();
10080
10081    /***
10082    * @module Enumerable
10083    * @description Counting, mapping, and finding methods on both arrays and objects.
10084    *
10085    ***/
10086
10087    function sum(obj, map) {
10088        var sum = 0;
10089        enumerateWithMapping(obj, map, function(val) {
10090            sum += val;
10091        });
10092        return sum;
10093    }
10094
10095    function average(obj, map) {
10096        var sum = 0, count = 0;
10097        enumerateWithMapping(obj, map, function(val) {
10098            sum += val;
10099            count++;
10100        });
10101        // Prevent divide by 0
10102        return sum / (count || 1);
10103    }
10104
10105    function median(obj, map) {
10106        var result = [], middle, len;
10107        enumerateWithMapping(obj, map, function(val) {
10108            result.push(val);
10109        });
10110        len = result.length;
10111        if (!len) return 0;
10112        result.sort(function(a, b) {
10113            // IE7 will throw errors on non-numbers!
10114            return (a || 0) - (b || 0);
10115        });
10116        middle = trunc(len / 2);
10117        return len % 2 ? result[middle] : (result[middle - 1] + result[middle]) / 2;
10118    }
10119
10120    function getMinOrMax(obj, arg1, arg2, max, asObject) {
10121        var result = [], pushVal, edge, all, map;
10122        if (isBoolean(arg1)) {
10123            all = arg1;
10124            map = arg2;
10125        } else {
10126            map = arg1;
10127        }
10128        enumerateWithMapping(obj, map, function(val, key) {
10129            if (isUndefined(val)) {
10130                throw new TypeError('Cannot compare with undefined');
10131            }
10132            pushVal = asObject ? key : obj[key];
10133            if (val === edge) {
10134                result.push(pushVal);
10135            } else if (isUndefined(edge) || (max && val > edge) || (!max && val < edge)) {
10136                result = [pushVal];
10137                edge = val;
10138            }
10139        });
10140        return getReducedMinMaxResult(result, obj, all, asObject);
10141    }
10142
10143    function getLeastOrMost(obj, arg1, arg2, most, asObject) {
10144        var group = {}, refs = [], minMaxResult, result, all, map;
10145        if (isBoolean(arg1)) {
10146            all = arg1;
10147            map = arg2;
10148        } else {
10149            map = arg1;
10150        }
10151        enumerateWithMapping(obj, map, function(val, key) {
10152            var groupKey = serializeInternal(val, refs);
10153            var arr = getOwn(group, groupKey) || [];
10154            arr.push(asObject ? key : obj[key]);
10155            group[groupKey] = arr;
10156        });
10157        minMaxResult = getMinOrMax(group, !!all, 'length', most, true);
10158        if (all) {
10159            result = [];
10160            // Flatten result
10161            forEachProperty(minMaxResult, function(val) {
10162                result = result.concat(val);
10163            });
10164        } else {
10165            result = getOwn(group, minMaxResult);
10166        }
10167        return getReducedMinMaxResult(result, obj, all, asObject);
10168    }
10169
10170
10171    // Support
10172
10173    function getReducedMinMaxResult(result, obj, all, asObject) {
10174        if (asObject && all) {
10175            // The method has returned an array of keys so use this array
10176            // to build up the resulting object in the form we want it in.
10177            return result.reduce(function(o, key) {
10178                o[key] = obj[key];
10179                return o;
10180            }, {});
10181        } else if (result && !all) {
10182            result = result[0];
10183        }
10184        return result;
10185    }
10186
10187    function enumerateWithMapping(obj, map, fn) {
10188        var arrayIndexes = isArray(obj);
10189        forEachProperty(obj, function(val, key) {
10190            if (arrayIndexes) {
10191                if (!isArrayIndex(key)) {
10192                    return;
10193                }
10194                key = +key;
10195            }
10196            var mapped = mapWithShortcuts(val, map, obj, [val, key, obj]);
10197            fn(mapped, key);
10198        });
10199    }
10200
10201    /*** @namespace Array ***/
10202
10203    // Flag allowing native array methods to be enhanced
10204    var ARRAY_ENHANCEMENTS_FLAG = 'enhanceArray';
10205
10206    // Enhanced map function
10207    var enhancedMap = buildEnhancedMapping('map');
10208
10209    // Enhanced matcher methods
10210    var enhancedFind = buildEnhancedMatching('find'),
10211      enhancedSome = buildEnhancedMatching('some'),
10212      enhancedEvery = buildEnhancedMatching('every'),
10213      enhancedFilter = buildEnhancedMatching('filter'),
10214      enhancedFindIndex = buildEnhancedMatching('findIndex');
10215
10216    function arrayNone() {
10217        return !enhancedSome.apply(this, arguments);
10218    }
10219
10220    function arrayCount(arr, f) {
10221        if (isUndefined(f)) {
10222            return arr.length;
10223        }
10224        return enhancedFilter.apply(this, arguments).length;
10225    }
10226
10227    // Enhanced methods
10228
10229    function buildEnhancedMapping(name) {
10230        return wrapNativeArrayMethod(name, enhancedMapping);
10231    }
10232
10233
10234    function buildEnhancedMatching(name) {
10235        return wrapNativeArrayMethod(name, enhancedMatching);
10236    }
10237
10238    function enhancedMapping(map, context) {
10239        if (isFunction(map)) {
10240            return map;
10241        } else if (map) {
10242            return function(el, i, arr) {
10243                return mapWithShortcuts(el, map, context, [el, i, arr]);
10244            };
10245        }
10246    }
10247
10248    function enhancedMatching(f) {
10249        var matcher;
10250        if (isFunction(f)) {
10251            return f;
10252        }
10253        matcher = getMatcher(f);
10254        return function(el, i, arr) {
10255            return matcher(el, i, arr);
10256        };
10257    }
10258
10259    function wrapNativeArrayMethod(methodName, wrapper) {
10260        var nativeFn = Array.prototype[methodName];
10261        return function(arr, f, context, argsLen) {
10262            var args = new Array(2);
10263            assertArgument(argsLen > 0);
10264            args[0] = wrapper(f, context);
10265            args[1] = context;
10266            return nativeFn.apply(arr, args);
10267        };
10268    }
10269
10270
10271    /***
10272    * @method [fn]FromIndex(startIndex, [loop], ...)
10273    * @returns Mixed
10274    * @short Runs native array functions beginning from `startIndex`.
10275    * @extra If [loop] is `true`, once the end of the array has been reached,
10276    *        iteration will continue from the start of the array up to
10277    *        `startIndex - 1`. If [loop] is false it can be omitted. Standard
10278    *        arguments are then passed which will be forwarded to the native
10279    *        methods. When available, methods are always `enhanced`. This includes
10280    *        `deep properties` for `map`, and `enhanced matching` for `some`,
10281    *        `every`, `filter`, `find`, and `findIndex`. Note also that
10282    *        `forEachFromIndex` is optimized for sparse arrays and may be faster
10283    *        than native `forEach`.
10284    *
10285    * @set
10286    *   mapFromIndex
10287    *   forEachFromIndex
10288    *   filterFromIndex
10289    *   someFromIndex
10290    *   everyFromIndex
10291    *   reduceFromIndex
10292    *   reduceRightFromIndex
10293    *   findFromIndex
10294    *   findIndexFromIndex
10295    *
10296    * @example
10297    *
10298    *   users.mapFromIndex(2, 'name');
10299    *   users.mapFromIndex(2, true, 'name');
10300    *   names.forEachFromIndex(10, log);
10301    *   names.everyFromIndex(15, /^[A-F]/);
10302    *
10303    * @signature [fn]FromIndex(startIndex, ...)
10304    * @param {number} startIndex
10305    * @param {boolean} loop
10306    *
10307    ***/
10308    function buildFromIndexMethods() {
10309
10310        var methods = {
10311            'forEach': {
10312                base: forEachAsNative
10313            },
10314            'map': {
10315                wrapper: enhancedMapping
10316            },
10317            'some every': {
10318                wrapper: enhancedMatching
10319            },
10320            'findIndex': {
10321                wrapper: enhancedMatching,
10322                result: indexResult
10323            },
10324            'reduce': {
10325                apply: applyReduce
10326            },
10327            'filter find': {
10328                wrapper: enhancedMatching
10329            },
10330            'reduceRight': {
10331                apply: applyReduce,
10332                slice: sliceArrayFromRight,
10333                clamp: clampStartIndexFromRight
10334            }
10335        };
10336
10337        forEachProperty(methods, function(opts, key) {
10338            forEach(spaceSplit(key), function(baseName) {
10339                var methodName = baseName + 'FromIndex';
10340                var fn = createFromIndexWithOptions(baseName, opts);
10341                defineInstanceWithArguments(sugarArray, methodName, fn);
10342            });
10343        });
10344
10345        function forEachAsNative(fn) {
10346            forEach(this, fn);
10347        }
10348
10349        // Methods like filter and find have a direct association between the value
10350        // returned by the callback and the element of the current iteration. This
10351        // means that when looping, array elements must match the actual index for
10352        // which they are being called, so the array must be sliced. This is not the
10353        // case for methods like forEach and map, which either do not use return
10354        // values or use them in a way that simply getting the element at a shifted
10355        // index will not affect the final return value. However, these methods will
10356        // still fail on sparse arrays, so always slicing them here. For example, if
10357        // "forEachFromIndex" were to be called on [1,,2] from index 1, although the
10358        // actual index 1 would itself would be skipped, when the array loops back to
10359        // index 0, shifting it by adding 1 would result in the element for that
10360        // iteration being undefined. For shifting to work, all gaps in the array
10361        // between the actual index and the shifted index would have to be accounted
10362        // for. This is infeasible and is easily solved by simply slicing the actual
10363        // array instead so that gaps align. Note also that in the case of forEach,
10364        // we are using the internal function which handles sparse arrays in a way
10365        // that does not increment the index, and so is highly optimized compared to
10366        // the others here, which are simply going through the native implementation.
10367        function sliceArrayFromLeft(arr, startIndex, loop) {
10368            var result = arr;
10369            if (startIndex) {
10370                result = arr.slice(startIndex);
10371                if (loop) {
10372                    result = result.concat(arr.slice(0, startIndex));
10373                }
10374            }
10375            return result;
10376        }
10377
10378        // When iterating from the right, indexes are effectively shifted by 1.
10379        // For example, iterating from the right from index 2 in an array of 3
10380        // should also include the last element in the array. This matches the
10381        // "lastIndexOf" method which also iterates from the right.
10382        function sliceArrayFromRight(arr, startIndex, loop) {
10383            if (!loop) {
10384                startIndex += 1;
10385                arr = arr.slice(0, max(0, startIndex));
10386            }
10387            return arr;
10388        }
10389
10390        function clampStartIndex(startIndex, len) {
10391            return min(len, max(0, startIndex));
10392        }
10393
10394        // As indexes are shifted by 1 when starting from the right, clamping has to
10395        // go down to -1 to accommodate the full range of the sliced array.
10396        function clampStartIndexFromRight(startIndex, len) {
10397            return min(len, max(-1, startIndex));
10398        }
10399
10400        function applyReduce(arr, startIndex, fn, context, len, loop) {
10401            return function(acc, val, i) {
10402                i = getNormalizedIndex(i + startIndex, len, loop);
10403                return fn.call(arr, acc, val, i, arr);
10404            };
10405        }
10406
10407        function applyEach(arr, startIndex, fn, context, len, loop) {
10408            return function(el, i) {
10409                i = getNormalizedIndex(i + startIndex, len, loop);
10410                return fn.call(context, arr[i], i, arr);
10411            };
10412        }
10413
10414        function indexResult(result, startIndex, len) {
10415            if (result !== -1) {
10416                result = (result + startIndex) % len;
10417            }
10418            return result;
10419        }
10420
10421        function createFromIndexWithOptions(methodName, opts) {
10422
10423            var baseFn = opts.base || Array.prototype[methodName],
10424          applyCallback = opts.apply || applyEach,
10425          sliceArray = opts.slice || sliceArrayFromLeft,
10426          clampIndex = opts.clamp || clampStartIndex,
10427          getResult = opts.result,
10428          wrapper = opts.wrapper;
10429
10430            return function(arr, startIndex, args) {
10431                var callArgs = [], argIndex = 0, lastArg, result, len, loop, fn;
10432                len = arr.length;
10433                if (isBoolean(args[0])) {
10434                    loop = args[argIndex++];
10435                }
10436                fn = args[argIndex++];
10437                lastArg = args[argIndex];
10438                if (startIndex < 0) {
10439                    startIndex += len;
10440                }
10441                startIndex = clampIndex(startIndex, len);
10442                assertArgument(args.length);
10443                fn = wrapper ? wrapper(fn, lastArg) : fn;
10444                callArgs.push(applyCallback(arr, startIndex, fn, lastArg, len, loop));
10445                if (lastArg) {
10446                    callArgs.push(lastArg);
10447                }
10448                result = baseFn.apply(sliceArray(arr, startIndex, loop), callArgs);
10449                if (getResult) {
10450                    result = getResult(result, startIndex, len);
10451                }
10452                return result;
10453            };
10454        }
10455    }
10456
10457    defineInstance(sugarArray, {
10458
10459        /***
10460        * @method map(map, [context])
10461        * @returns New Array
10462        * @polyfill ES5
10463        * @short Maps the array to another array whose elements are the values
10464        *        returned by the `map` callback.
10465        * @extra [context] is the `this` object. Sugar enhances this method to accept
10466        *        a string for `map`, which is a shortcut for a function that gets
10467        *        a property or invokes a function on each element.
10468        *        Supports `deep properties`.
10469        *
10470        * @callback mapFn
10471        *
10472        *   el   The element of the current iteration.
10473        *   i    The index of the current iteration.
10474        *   arr  A reference to the array.
10475        *
10476        * @example
10477        *
10478        *   [1,2,3].map(function(n) {
10479        *     return n * 3;
10480        *   }); -> [3,6,9]
10481        *
10482        *   ['a','aa','aaa'].map('length') -> [1,2,3]
10483        *   ['A','B','C'].map('toLowerCase') -> ['a','b','c']
10484        *   users.map('name') -> array of user names
10485        *
10486        * @param {string|mapFn} map
10487        * @param {any} context
10488        * @callbackParam {ArrayElement} el
10489        * @callbackParam {number} i
10490        * @callbackParam {Array} arr
10491        * @callbackReturns {NewArrayElement} mapFn
10492        *
10493        ***/
10494        'map': fixArgumentLength(enhancedMap),
10495
10496        /***
10497        * @method some(search, [context])
10498        * @returns Boolean
10499        * @polyfill ES5
10500        * @short Returns true if `search` is true for any element in the array.
10501        * @extra [context] is the `this` object. Implements `enhanced matching`.
10502        *
10503        * @callback searchFn
10504        *
10505        *   el   The element of the current iteration.
10506        *   i    The index of the current iteration.
10507        *   arr  A reference to the array.
10508        *
10509        * @example
10510        *
10511        *   ['a','b','c'].some(function(n) {
10512        *     return n == 'a';
10513        *   });
10514        *   ['a','b','c'].some(function(n) {
10515        *     return n == 'd';
10516        *   });
10517        *   ['a','b','c'].some('a')    -> true
10518        *   [{a:2},{b:5}].some({a:2})  -> true
10519        *   users.some({ name: /^H/ }) -> true if any have a name starting with H
10520        *
10521        * @param {ArrayElement|searchFn} search
10522        * @param {any} context
10523        * @callbackParam {ArrayElement} el
10524        * @callbackParam {number} i
10525        * @callbackParam {Array} arr
10526        * @callbackReturns {boolean} searchFn
10527        *
10528        ***/
10529        'some': fixArgumentLength(enhancedSome),
10530
10531        /***
10532        * @method every(search, [context])
10533        * @returns Boolean
10534        * @polyfill ES5
10535        * @short Returns true if `search` is true for all elements of the array.
10536        * @extra [context] is the `this` object. Implements `enhanced matching`.
10537        *
10538        * @callback searchFn
10539        *
10540        *   el   The element of the current iteration.
10541        *   i    The index of the current iteration.
10542        *   arr  A reference to the array.
10543        *
10544        * @example
10545        *
10546        *   ['a','a','a'].every(function(n) {
10547        *     return n == 'a';
10548        *   });
10549        *   ['a','a','a'].every('a')   -> true
10550        *   [{a:2},{a:2}].every({a:2}) -> true
10551        *   users.every({ name: /^H/ }) -> true if all have a name starting with H
10552        *
10553        * @param {ArrayElement|searchFn} search
10554        * @param {any} context
10555        * @callbackParam {ArrayElement} el
10556        * @callbackParam {number} i
10557        * @callbackParam {Array} arr
10558        * @callbackReturns {boolean} searchFn
10559        *
10560        ***/
10561        'every': fixArgumentLength(enhancedEvery),
10562
10563        /***
10564        * @method filter(search, [context])
10565        * @returns Array
10566        * @polyfill ES5
10567        * @short Returns any elements in the array that match `search`.
10568        * @extra [context] is the `this` object. Implements `enhanced matching`.
10569        *
10570        * @callback searchFn
10571        *
10572        *   el   The element of the current iteration.
10573        *   i    The index of the current iteration.
10574        *   arr  A reference to the array.
10575        *
10576        * @example
10577        *
10578        *   [1,2,3].filter(function(n) {
10579        *     return n > 1;
10580        *   });
10581        *   [1,2,2,4].filter(2) -> 2
10582        *   users.filter({ name: /^H/ }) -> all users with a name starting with H
10583        *
10584        * @param {ArrayElement|searchFn} search
10585        * @param {any} context
10586        * @callbackParam {ArrayElement} el
10587        * @callbackParam {number} i
10588        * @callbackParam {Array} arr
10589        * @callbackReturns {boolean} searchFn
10590        *
10591        ***/
10592        'filter': fixArgumentLength(enhancedFilter),
10593
10594        /***
10595        * @method find(search, [context])
10596        * @returns Mixed
10597        * @polyfill ES6
10598        * @short Returns the first element in the array that matches `search`.
10599        * @extra Implements `enhanced matching`.
10600        *
10601        * @callback searchFn
10602        *
10603        *   el   The element of the current iteration.
10604        *   i    The index of the current iteration.
10605        *   arr  A reference to the array.
10606        *
10607        * @example
10608        *
10609        *   users.find(function(user) {
10610        *     return user.name = 'Harry';
10611        *   }); -> harry!
10612        *
10613        *   users.find({ name: 'Harry' }); -> harry!
10614        *   users.find({ name: /^[A-H]/ });  -> First user with name starting with A-H
10615        *   users.find({ titles: ['Ms', 'Dr'] }); -> not harry!
10616        *
10617        * @param {ArrayElement|searchFn} search
10618        * @param {any} context
10619        * @callbackParam {ArrayElement} el
10620        * @callbackParam {number} i
10621        * @callbackParam {Array} arr
10622        * @callbackReturns {boolean} searchFn
10623        *
10624        ***/
10625        'find': fixArgumentLength(enhancedFind),
10626
10627        /***
10628        * @method findIndex(search, [context])
10629        * @returns Number
10630        * @polyfill ES6
10631        * @short Returns the index of the first element in the array that matches
10632        *        `search`, or `-1` if none.
10633        * @extra [context] is the `this` object. Implements `enhanced matching`.
10634        *
10635        * @callback searchFn
10636        *
10637        *   el   The element of the current iteration.
10638        *   i    The index of the current iteration.
10639        *   arr  A reference to the array.
10640        *
10641        * @example
10642        *
10643        *   [1,2,3,4].findIndex(function(n) {
10644        *     return n % 2 == 0;
10645        *   }); -> 1
10646        *   ['a','b','c'].findIndex('c');        -> 2
10647        *   ['cuba','japan','canada'].find(/^c/) -> 0
10648        *
10649        * @param {ArrayElement|searchFn} search
10650        * @param {any} context
10651        * @callbackParam {ArrayElement} el
10652        * @callbackParam {number} i
10653        * @callbackParam {Array} arr
10654        * @callbackReturns {boolean} searchFn
10655        *
10656        ***/
10657        'findIndex': fixArgumentLength(enhancedFindIndex)
10658
10659    }, [ENHANCEMENTS_FLAG, ARRAY_ENHANCEMENTS_FLAG]);
10660
10661
10662    defineInstance(sugarArray, {
10663
10664        /***
10665        * @method none(search, [context])
10666        *
10667        * @returns Boolean
10668        * @short Returns true if none of the elements in the array match `search`.
10669        * @extra [context] is the `this` object. Implements `enhanced matching`.
10670        *
10671        * @callback searchFn
10672        *
10673        *   el   The element of the current iteration.
10674        *   i    The index of the current iteration.
10675        *   arr  A reference to the array.
10676        *
10677        * @example
10678        *
10679        *   [1,2,3].none(5)         -> true
10680        *   ['a','b','c'].none(/b/) -> false
10681        *   users.none(function(user) {
10682        *     return user.name == 'Wolverine';
10683        *   }); -> probably true
10684        *   users.none({ name: 'Wolverine' }); -> same as above
10685        *
10686        * @param {ArrayElement|searchFn} search
10687        * @param {any} context
10688        * @callbackParam {ArrayElement} el
10689        * @callbackParam {number} i
10690        * @callbackParam {Array} arr
10691        * @callbackReturns {boolean} searchFn
10692        *
10693        ***/
10694        'none': fixArgumentLength(arrayNone),
10695
10696        /***
10697        * @method count(search, [context])
10698        * @returns Number
10699        * @short Counts all elements in the array that match `search`.
10700        * @extra Implements `enhanced matching`.
10701        *
10702        * @callback searchFn
10703        *
10704        *   el   The element of the current iteration.
10705        *   i    The index of the current iteration.
10706        *   arr  A reference to the array.
10707        *
10708        * @example
10709        *
10710        *   ['a','b','a'].count('a') -> 2
10711        *   ['a','b','c'].count(/b/) -> 1
10712        *   users.count(function(user) {
10713        *     return user.age > 30;
10714        *   }); -> number of users older than 30
10715        *
10716        * @param {ArrayElement|searchFn} search
10717        * @param {any} context
10718        * @callbackParam {ArrayElement} el
10719        * @callbackParam {number} i
10720        * @callbackParam {Array} arr
10721        * @callbackReturns {boolean} searchFn
10722        *
10723        ***/
10724        'count': fixArgumentLength(arrayCount),
10725
10726        /***
10727        * @method min([all] = false, [map])
10728        * @returns Mixed
10729        * @short Returns the element in the array with the lowest value.
10730        * @extra [map] may be passed in place of [all], and is a function mapping the
10731        *        value to be checked or a string acting as a shortcut. If [all] is
10732        *        true, multiple elements will be returned. Supports `deep properties`.
10733        *
10734        * @callback mapFn
10735        *
10736        *   el   The element of the current iteration.
10737        *   i    The index of the current iteration.
10738        *   arr  A reference to the array.
10739        *
10740        * @example
10741        *
10742        *   [1,2,3].min()                          -> 1
10743        *   ['fee','fo','fum'].min('length')       -> 'fo'
10744        *   ['fee','fo','fum'].min(true, 'length') -> ['fo']
10745        *   users.min('age')                       -> youngest guy!
10746        *
10747        *   ['fee','fo','fum'].min(true, function(n) {
10748        *     return n.length;
10749        *   }); -> ['fo']
10750        *
10751        * @signature min([map])
10752        * @param {string|mapFn} map
10753        * @param {boolean} all
10754        * @callbackParam {ArrayElement} el
10755        * @callbackParam {number} i
10756        * @callbackParam {Array} arr
10757        * @callbackReturns {NewArrayElement} mapFn
10758        *
10759        ***/
10760        'min': function(arr, all, map) {
10761            return getMinOrMax(arr, all, map);
10762        },
10763
10764        /***
10765        * @method max([all] = false, [map])
10766        * @returns Mixed
10767        * @short Returns the element in the array with the greatest value.
10768        * @extra [map] may be passed in place of [all], and is a function mapping the
10769        *        value to be checked or a string acting as a shortcut. If [all] is
10770        *        true, multiple elements will be returned. Supports `deep properties`.
10771        *
10772        * @callback mapFn
10773        *
10774        *   el   The element of the current iteration.
10775        *   i    The index of the current iteration.
10776        *   arr  A reference to the array.
10777        *
10778        * @example
10779        *
10780        *   [1,2,3].max()                          -> 3
10781        *   ['fee','fo','fum'].max('length')       -> 'fee'
10782        *   ['fee','fo','fum'].max(true, 'length') -> ['fee','fum']
10783        *   users.max('age')                       -> oldest guy!
10784        *
10785        *   ['fee','fo','fum'].max(true, function(n) {
10786        *     return n.length;
10787        *   }); -> ['fee', 'fum']
10788        *
10789        * @signature max([map])
10790        * @param {string|mapFn} map
10791        * @param {boolean} all
10792        * @callbackParam {ArrayElement} el
10793        * @callbackParam {number} i
10794        * @callbackParam {Array} arr
10795        * @callbackReturns {NewArrayElement} mapFn
10796        *
10797        ***/
10798        'max': function(arr, all, map) {
10799            return getMinOrMax(arr, all, map, true);
10800        },
10801
10802        /***
10803        * @method least([all] = false, [map])
10804        * @returns Array
10805        * @short Returns the elements in the array with the least commonly occuring value.
10806        * @extra [map] may be passed in place of [all], and is a function mapping the
10807        *        value to be checked or a string acting as a shortcut. If [all] is
10808        *        true, will return multiple values in an array.
10809        *        Supports `deep properties`.
10810        *
10811        * @callback mapFn
10812        *
10813        *   el   The element of the current iteration.
10814        *   i    The index of the current iteration.
10815        *   arr  A reference to the array.
10816        *
10817        * @example
10818        *
10819        *   [3,2,2].least() -> 3
10820        *   ['fe','fo','fum'].least(true, 'length') -> ['fum']
10821        *   users.least('profile.type')             -> (user with least commonly occurring type)
10822        *   users.least(true, 'profile.type')       -> (users with least commonly occurring type)
10823        *
10824        * @signature least([map])
10825        * @param {string|mapFn} map
10826        * @param {boolean} all
10827        * @callbackParam {ArrayElement} el
10828        * @callbackParam {number} i
10829        * @callbackParam {Array} arr
10830        * @callbackReturns {NewArrayElement} mapFn
10831        *
10832        ***/
10833        'least': function(arr, all, map) {
10834            return getLeastOrMost(arr, all, map);
10835        },
10836
10837        /***
10838        * @method most([all] = false, [map])
10839        * @returns Array
10840        * @short Returns the elements in the array with the most commonly occuring value.
10841        * @extra [map] may be passed in place of [all], and is a function mapping the
10842        *        value to be checked or a string acting as a shortcut. If [all] is
10843        *        true, will return multiple values in an array.
10844        *        Supports `deep properties`.
10845        *
10846        * @callback mapFn
10847        *
10848        *   el   The element of the current iteration.
10849        *   i    The index of the current iteration.
10850        *   arr  A reference to the array.
10851        *
10852        * @example
10853        *
10854        *   [3,2,2].most(2) -> 2
10855        *   ['fe','fo','fum'].most(true, 'length') -> ['fe','fo']
10856        *   users.most('profile.type')             -> (user with most commonly occurring type)
10857        *   users.most(true, 'profile.type')       -> (users with most commonly occurring type)
10858        *
10859        * @signature most([map])
10860        * @param {string|mapFn} map
10861        * @param {boolean} all
10862        * @callbackParam {ArrayElement} el
10863        * @callbackParam {number} i
10864        * @callbackParam {Array} arr
10865        * @callbackReturns {NewArrayElement} mapFn
10866        *
10867        ***/
10868        'most': function(arr, all, map) {
10869            return getLeastOrMost(arr, all, map, true);
10870        },
10871
10872        /***
10873        * @method sum([map])
10874        * @returns Number
10875        * @short Sums all values in the array.
10876        * @extra [map] may be a function mapping the value to be summed or a string
10877        *        acting as a shortcut.
10878        *
10879        * @callback mapFn
10880        *
10881        *   el   The element of the current iteration.
10882        *   i    The index of the current iteration.
10883        *   arr  A reference to the array.
10884        *
10885        * @example
10886        *
10887        *   [1,2,2].sum() -> 5
10888        *   users.sum(function(user) {
10889        *     return user.votes;
10890        *   }); -> total votes!
10891        *   users.sum('votes') -> total votes!
10892        *
10893        * @param {string|mapFn} map
10894        * @callbackParam {ArrayElement} el
10895        * @callbackParam {number} i
10896        * @callbackParam {Array} arr
10897        * @callbackReturns {NewArrayElement} mapFn
10898        *
10899        ***/
10900        'sum': function(arr, map) {
10901            return sum(arr, map);
10902        },
10903
10904        /***
10905        * @method average([map])
10906        * @returns Number
10907        * @short Gets the mean average for all values in the array.
10908        * @extra [map] may be a function mapping the value to be averaged or a string
10909        *        acting as a shortcut. Supports `deep properties`.
10910        *
10911        * @callback mapFn
10912        *
10913        *   el   The element of the current iteration.
10914        *   i    The index of the current iteration.
10915        *   arr  A reference to the array.
10916        *
10917        * @example
10918        *
10919        *   [1,2,3,4].average() -> 2
10920        *   users.average(function(user) {
10921        *     return user.age;
10922        *   }); -> average user age
10923        *   users.average('age') -> average user age
10924        *   users.average('currencies.usd.balance') -> average USD balance
10925        *
10926        * @param {string|mapFn} map
10927        * @callbackParam {ArrayElement} el
10928        * @callbackParam {number} i
10929        * @callbackParam {Array} arr
10930        * @callbackReturns {NewArrayElement} mapFn
10931        *
10932        ***/
10933        'average': function(arr, map) {
10934            return average(arr, map);
10935        },
10936
10937        /***
10938        * @method median([map])
10939        * @returns Number
10940        * @short Gets the median average for all values in the array.
10941        * @extra [map] may be a function mapping the value to be averaged or a string
10942        *        acting as a shortcut.
10943        *
10944        * @callback mapFn
10945        *
10946        *   el   The element of the current iteration.
10947        *   i    The index of the current iteration.
10948        *   arr  A reference to the array.
10949        *
10950        * @example
10951        *
10952        *   [1,2,2].median() -> 2
10953        *   [{a:1},{a:2},{a:2}].median('a') -> 2
10954        *   users.median('age') -> median user age
10955        *   users.median('currencies.usd.balance') -> median USD balance
10956        *
10957        * @param {string|mapFn} map
10958        * @callbackParam {ArrayElement} el
10959        * @callbackParam {number} i
10960        * @callbackParam {Array} arr
10961        * @callbackReturns {NewArrayElement} mapFn
10962        *
10963        ***/
10964        'median': function(arr, map) {
10965            return median(arr, map);
10966        }
10967
10968    });
10969
10970
10971    /*** @namespace Object ***/
10972
10973    // Object matchers
10974    var objectSome = wrapObjectMatcher('some'),
10975      objectFind = wrapObjectMatcher('find'),
10976      objectEvery = wrapObjectMatcher('every');
10977
10978    function objectForEach(obj, fn) {
10979        assertCallable(fn);
10980        forEachProperty(obj, function(val, key) {
10981            fn(val, key, obj);
10982        });
10983        return obj;
10984    }
10985
10986    function objectMap(obj, map) {
10987        var result = {};
10988        forEachProperty(obj, function(val, key) {
10989            result[key] = mapWithShortcuts(val, map, obj, [val, key, obj]);
10990        });
10991        return result;
10992    }
10993
10994    function objectReduce(obj, fn, acc) {
10995        var init = isDefined(acc);
10996        forEachProperty(obj, function(val, key) {
10997            if (!init) {
10998                acc = val;
10999                init = true;
11000                return;
11001            }
11002            acc = fn(acc, val, key, obj);
11003        });
11004        return acc;
11005    }
11006
11007    function objectNone(obj, f) {
11008        return !objectSome(obj, f);
11009    }
11010
11011    function objectFilter(obj, f) {
11012        var matcher = getMatcher(f), result = {};
11013        forEachProperty(obj, function(val, key) {
11014            if (matcher(val, key, obj)) {
11015                result[key] = val;
11016            }
11017        });
11018        return result;
11019    }
11020
11021    function objectCount(obj, f) {
11022        var matcher = getMatcher(f), count = 0;
11023        forEachProperty(obj, function(val, key) {
11024            if (matcher(val, key, obj)) {
11025                count++;
11026            }
11027        });
11028        return count;
11029    }
11030
11031    // Support
11032
11033    function wrapObjectMatcher(name) {
11034        var nativeFn = Array.prototype[name];
11035        return function(obj, f) {
11036            var matcher = getMatcher(f);
11037            return nativeFn.call(getKeys(obj), function(key) {
11038                return matcher(obj[key], key, obj);
11039            });
11040        };
11041    }
11042
11043    defineInstanceAndStatic(sugarObject, {
11044
11045        /***
11046        * @method forEach(fn)
11047        * @returns Object
11048        * @short Runs `fn` against each property in the object.
11049        * @extra Does not iterate over inherited or non-enumerable properties.
11050        *
11051        * @callback eachFn
11052        *
11053        *   val  The value of the current iteration.
11054        *   key  The key of the current iteration.
11055        *   obj  A reference to the object.
11056        *
11057        * @example
11058        *
11059        *   Object.forEach({a:'b'}, function(val, key) {
11060        *     // val = 'b', key = a
11061        *   });
11062        *
11063        * @param {eachFn} fn
11064        * @callbackParam {Property} val
11065        * @callbackParam {string} key
11066        * @callbackParam {Object} obj
11067        *
11068        ***/
11069        'forEach': function(obj, fn) {
11070            return objectForEach(obj, fn);
11071        },
11072
11073        /***
11074        * @method map(map)
11075        * @returns Object
11076        * @short Maps the object to another object whose properties are the values
11077        *        returned by `map`.
11078        * @extra `map` can also be a string, which is a shortcut for a function that
11079        *        gets that property (or invokes a function) on each element.
11080        *        Supports `deep properties`.
11081        *
11082        * @callback mapFn
11083        *
11084        *   val  The value of the current property.
11085        *   key  The key of the current property.
11086        *   obj  A reference to the object.
11087        *
11088        * @example
11089        *
11090        *   data.map(function(val, key) {
11091        *     return key;
11092        *   }); -> {a:'b'}
11093        *   users.map('age');
11094        *
11095        * @param {string|mapFn} map
11096        * @callbackParam {Property} val
11097        * @callbackParam {string} key
11098        * @callbackParam {Object} obj
11099        * @callbackReturns {NewProperty} mapFn
11100        *
11101        ***/
11102        'map': function(obj, map) {
11103            return objectMap(obj, map);
11104        },
11105
11106        /***
11107        * @method some(search)
11108        * @returns Boolean
11109        * @short Returns true if `search` is true for any property in the object.
11110        * @extra Implements `enhanced matching`.
11111        *
11112        * @callback searchFn
11113        *
11114        *   val  The value of the current iteration.
11115        *   key  The key of the current iteration.
11116        *   obj  A reference to the object.
11117        *
11118        * @example
11119        *
11120        *   Object.some({a:1,b:2}, function(val) {
11121        *     return val == 1;
11122        *   }); -> true
11123        *   Object.some({a:1,b:2}, 1); -> true
11124        *
11125        * @param {Property|searchFn} search
11126        * @callbackParam {Property} val
11127        * @callbackParam {string} key
11128        * @callbackParam {Object} obj
11129        * @callbackReturns {boolean} searchFn
11130        *
11131        ***/
11132        'some': objectSome,
11133
11134        /***
11135        * @method every(search)
11136        * @returns Boolean
11137        * @short Returns true if `search` is true for all properties in the object.
11138        * @extra Implements `enhanced matching`.
11139        *
11140        * @callback searchFn
11141        *
11142        *   val  The value of the current iteration.
11143        *   key  The key of the current iteration.
11144        *   obj  A reference to the object.
11145        *
11146        * @example
11147        *
11148        *   Object.every({a:1,b:2}, function(val) {
11149        *     return val > 0;
11150        *   }); -> true
11151        *   Object.every({a:'a',b:'b'}, /[a-z]/); -> true
11152        *
11153        * @param {Property|searchFn} search
11154        * @callbackParam {Property} val
11155        * @callbackParam {string} key
11156        * @callbackParam {Object} obj
11157        * @callbackReturns {boolean} searchFn
11158        *
11159        ***/
11160        'every': objectEvery,
11161
11162        /***
11163        * @method filter(search)
11164        * @returns Array
11165        * @short Returns a new object with properties that match `search`.
11166        * @extra Implements `enhanced matching`.
11167        *
11168        * @callback searchFn
11169        *
11170        *   val  The value of the current iteration.
11171        *   key  The key of the current iteration.
11172        *   obj  A reference to the object.
11173        *
11174        * @example
11175        *
11176        *   Object.filter({a:1,b:2}, function(val) {
11177        *     return val == 1;
11178        *   }); -> {a:1}
11179        *   Object.filter({a:'a',z:'z'}, /[a-f]/); -> {a:'a'}
11180        *   Object.filter(usersByName, /^H/); -> all users with names starting with H
11181        *
11182        * @param {Property|searchFn} search
11183        * @callbackParam {Property} val
11184        * @callbackParam {string} key
11185        * @callbackParam {Object} obj
11186        * @callbackReturns {boolean} searchFn
11187        *
11188        ***/
11189        'filter': function(obj, f) {
11190            return objectFilter(obj, f);
11191        },
11192
11193        /***
11194        * @method reduce(reduceFn, [init])
11195        * @returns Mixed
11196        * @short Reduces the object to a single result.
11197        * @extra This operation is sometimes called "accumulation", as it takes the
11198        *        result of the last iteration of `fn` and passes it as the first
11199        *        argument to the next iteration, "accumulating" that value as it goes.
11200        *        The return value of this method will be the return value of the final
11201        *        iteration of `fn`. If [init] is passed, it will be the initial
11202        *        "accumulator" (the first argument). If [init] is not passed, then a
11203        *        property of the object will be used instead and `fn` will not be
11204        *        called for that property. Note that object properties have no order,
11205        *        and this may lead to bugs (for example if performing division or
11206        *        subtraction operations on a value). If order is important, use an
11207        *        array instead!
11208        *
11209        * @callback reduceFn
11210        *
11211        *   acc  The "accumulator", either [init], the result of the last iteration
11212        *        of `fn`, or a property of `obj`.
11213        *   val  The value of the current property called for `fn`.
11214        *   key  The key of the current property called for `fn`.
11215        *   obj  A reference to the object.
11216        *
11217        * @example
11218        *
11219        *   Object.reduce({a:2,b:4}, function(a, b) {
11220        *     return a * b;
11221        *   }); -> 8
11222        *
11223        *   Object.reduce({a:2,b:4}, function(a, b) {
11224        *     return a * b;
11225        *   }, 10); -> 80
11226        *
11227        *
11228        * @param {reduceFn} reduceFn
11229        * @param {any} [init]
11230        * @callbackParam {Property} acc
11231        * @callbackParam {Property} val
11232        * @callbackParam {string} key
11233        * @callbackParam {Object} obj
11234        *
11235        ***/
11236        'reduce': function(obj, fn, init) {
11237            return objectReduce(obj, fn, init);
11238        },
11239
11240        /***
11241        * @method find(search)
11242        * @returns Boolean
11243        * @short Returns the first key whose value matches `search`.
11244        * @extra Implements `enhanced matching`. Note that "first" is
11245        *        implementation-dependent. If order is important an array should be
11246        *        used instead.
11247        *
11248        * @callback searchFn
11249        *
11250        *   val  The value of the current iteration.
11251        *   key  The key of the current iteration.
11252        *   obj  A reference to the object.
11253        *
11254        * @example
11255        *
11256        *   Object.find({a:1,b:2}, function(val) {
11257        *     return val == 2;
11258        *   }); -> 'b'
11259        *   Object.find({a:'a',b:'b'}, /[a-z]/); -> 'a'
11260        *
11261        * @param {Property|searchFn} search
11262        * @callbackParam {Property} val
11263        * @callbackParam {string} key
11264        * @callbackParam {Object} obj
11265        * @callbackReturns {boolean} searchFn
11266        *
11267        ***/
11268        'find': objectFind,
11269
11270        /***
11271        * @method count(search)
11272        * @returns Number
11273        * @short Counts all properties in the object that match `search`.
11274        * @extra Implements `enhanced matching`.
11275        *
11276        * @callback searchFn
11277        *
11278        *   val  The value of the current iteration.
11279        *   key  The key of the current iteration.
11280        *   obj  A reference to the object.
11281        *
11282        * @example
11283        *
11284        *   Object.count({a:'a',b:'b',c:'a'}, 'a') -> 2
11285        *   Object.count(usersByName, function(user) {
11286        *     return user.age > 30;
11287        *   }); -> number of users older than 30
11288        *   Object.count(usersByName, { name: /^[H-Z]/ });
11289        *
11290        * @param {Property|searchFn} search
11291        * @callbackParam {Property} val
11292        * @callbackParam {string} key
11293        * @callbackParam {Object} obj
11294        * @callbackReturns {boolean} searchFn
11295        *
11296        ***/
11297        'count': function(obj, f) {
11298            return objectCount(obj, f);
11299        },
11300
11301        /***
11302        * @method none(search)
11303        * @returns Boolean
11304        * @short Returns true if none of the properties in the object match `search`.
11305        * @extra Implements `enhanced matching`.
11306        *
11307        * @callback searchFn
11308        *
11309        *   val  The value of the current iteration.
11310        *   key  The key of the current iteration.
11311        *   obj  A reference to the object.
11312        *
11313        * @example
11314        *
11315        *   Object.none({a:1,b:2}, 3); -> true
11316        *   Object.none(usersByName, function(user) {
11317        *     return user.name == 'Wolverine';
11318        *   }); -> probably true
11319        *
11320        * @param {Property|searchFn} search
11321        * @callbackParam {Property} val
11322        * @callbackParam {string} key
11323        * @callbackParam {Object} obj
11324        * @callbackReturns {boolean} searchFn
11325        *
11326        ***/
11327        'none': function(obj, f) {
11328            return objectNone(obj, f);
11329        },
11330
11331        /***
11332        * @method sum([map])
11333        * @returns Number
11334        * @short Sums all properties in the object.
11335        * @extra [map] may be a function mapping the value to be summed or a string
11336        *        acting as a shortcut.
11337        *
11338        * @callback mapFn
11339        *
11340        *   val  The value of the current iteration.
11341        *   key  The key of the current iteration.
11342        *   obj  A reference to the object.
11343        *
11344        * @example
11345        *
11346        *   Object.sum({a:35,b:13}); -> 48
11347        *   Object.sum(usersByName, function(user) {
11348        *     return user.votes;
11349        *   }); -> total user votes
11350        *
11351        * @param {string|mapFn} map
11352        * @callbackParam {Property} val
11353        * @callbackParam {string} key
11354        * @callbackParam {Object} obj
11355        * @callbackReturns {NewProperty} mapFn
11356        *
11357        ***/
11358        'sum': function(obj, map) {
11359            return sum(obj, map);
11360        },
11361
11362        /***
11363        * @method average([map])
11364        * @returns Number
11365        * @short Gets the mean average of all properties in the object.
11366        * @extra [map] may be a function mapping the value to be averaged or a string
11367        *        acting as a shortcut.
11368        *
11369        * @callback mapFn
11370        *
11371        *   val  The value of the current iteration.
11372        *   key  The key of the current iteration.
11373        *   obj  A reference to the object.
11374        *
11375        * @example
11376        *
11377        *   Object.average({a:35,b:11}); -> 23
11378        *   Object.average(usersByName, 'age'); -> average user age
11379        *   Object.average(usersByName, 'currencies.usd.balance'); -> USD mean balance
11380        *
11381        * @param {string|mapFn} map
11382        * @callbackParam {Property} val
11383        * @callbackParam {string} key
11384        * @callbackParam {Object} obj
11385        * @callbackReturns {NewProperty} mapFn
11386        *
11387        ***/
11388        'average': function(obj, map) {
11389            return average(obj, map);
11390        },
11391
11392        /***
11393        * @method median([map])
11394        * @returns Number
11395        * @short Gets the median average of all properties in the object.
11396        * @extra [map] may be a function mapping the value to be averaged or a string
11397        *        acting as a shortcut.
11398        *
11399        * @callback mapFn
11400        *
11401        *   val  The value of the current iteration.
11402        *   key  The key of the current iteration.
11403        *   obj  A reference to the object.
11404        *
11405        * @example
11406        *
11407        *   Object.median({a:1,b:2,c:2}) -> 2
11408        *   Object.median(usersByName, 'age'); -> median user age
11409        *   Object.median(usersByName, 'currencies.usd.balance'); -> USD median balance
11410        *
11411        * @param {string|mapFn} map
11412        * @callbackParam {Property} val
11413        * @callbackParam {string} key
11414        * @callbackParam {Object} obj
11415        * @callbackReturns {NewProperty} mapFn
11416        *
11417        ***/
11418        'median': function(obj, map) {
11419            return median(obj, map);
11420        },
11421
11422        /***
11423        * @method min([all] = false, [map])
11424        * @returns Mixed
11425        * @short Returns the key of the property in the object with the lowest value.
11426        * @extra If [all] is true, will return an object with all properties in the
11427        *        object with the lowest value. [map] may be passed in place of [all]
11428        *        and is a function mapping the value to be checked or a string acting
11429        *        as a shortcut.
11430        *
11431        * @callback mapFn
11432        *
11433        *   val  The value of the current iteration.
11434        *   key  The key of the current iteration.
11435        *   obj  A reference to the object.
11436        *
11437        * @example
11438        *
11439        *   Object.min({a:1,b:2,c:3})                    -> 'a'
11440        *   Object.min({a:'aaa',b:'bb',c:'c'}, 'length') -> 'c'
11441        *   Object.min({a:1,b:1,c:3}, true)              -> {a:1,b:1}
11442        *
11443        * @signature min([map])
11444        * @param {string|mapFn} map
11445        * @param {boolean} [all]
11446        * @callbackParam {Property} val
11447        * @callbackParam {string} key
11448        * @callbackParam {Object} obj
11449        * @callbackReturns {NewProperty} mapFn
11450        *
11451        ***/
11452        'min': function(obj, all, map) {
11453            return getMinOrMax(obj, all, map, false, true);
11454        },
11455
11456        /***
11457        * @method max([all] = false, [map])
11458        * @returns Mixed
11459        * @short Returns the key of the property in the object with the highest value.
11460        * @extra If [all] is true, will return an object with all properties in the
11461        *        object with the highest value. [map] may be passed in place of [all]
11462        *        and is a function mapping the value to be checked or a string acting
11463        *        as a shortcut.
11464        *
11465        * @callback mapFn
11466        *
11467        *   val  The value of the current iteration.
11468        *   key  The key of the current iteration.
11469        *   obj  A reference to the object.
11470        *
11471        * @example
11472        *
11473        *   Object.max({a:1,b:2,c:3})                    -> 'c'
11474        *   Object.max({a:'aaa',b:'bb',c:'c'}, 'length') -> 'a'
11475        *   Object.max({a:1,b:3,c:3}, true)              -> {b:3,c:3}
11476        *
11477        * @signature max([map])
11478        * @param {string|mapFn} map
11479        * @param {boolean} [all]
11480        * @callbackParam {Property} val
11481        * @callbackParam {string} key
11482        * @callbackParam {Object} obj
11483        * @callbackReturns {NewProperty} mapFn
11484        *
11485        ***/
11486        'max': function(obj, all, map) {
11487            return getMinOrMax(obj, all, map, true, true);
11488        },
11489
11490        /***
11491        * @method least([all] = false, [map])
11492        * @returns Mixed
11493        * @short Returns the key of the property in the object with the least commonly
11494        *        occuring value.
11495        * @extra If [all] is true, will return an object with all properties in the
11496        *        object with the least common value. [map] may be passed in place of
11497        *        [all] and is a function mapping the value to be checked or a string
11498        *        acting as a shortcut.
11499        *
11500        * @callback mapFn
11501        *
11502        *   val  The value of the current iteration.
11503        *   key  The key of the current iteration.
11504        *   obj  A reference to the object.
11505        *
11506        * @example
11507        *
11508        *   Object.least({a:1,b:3,c:3})                   -> 'a'
11509        *   Object.least({a:'aa',b:'bb',c:'c'}, 'length') -> 'c'
11510        *   Object.least({a:1,b:3,c:3}, true)             -> {a:1}
11511        *
11512        * @signature least([map])
11513        * @param {string|mapFn} map
11514        * @param {boolean} [all]
11515        * @callbackParam {Property} val
11516        * @callbackParam {string} key
11517        * @callbackParam {Object} obj
11518        * @callbackReturns {NewProperty} mapFn
11519        *
11520        ***/
11521        'least': function(obj, all, map) {
11522            return getLeastOrMost(obj, all, map, false, true);
11523        },
11524
11525        /***
11526        * @method most([all] = false, [map])
11527        * @returns Mixed
11528        * @short Returns the key of the property in the object with the most commonly
11529        *        occuring value.
11530        * @extra If [all] is true, will return an object with all properties in the
11531        *        object with the most common value. [map] may be passed in place of
11532        *        [all] and is a function mapping the value to be checked or a string
11533        *        acting as a shortcut.
11534        *
11535        * @callback mapFn
11536        *
11537        *   val  The value of the current iteration.
11538        *   key  The key of the current iteration.
11539        *   obj  A reference to the object.
11540        *
11541        * @example
11542        *
11543        *   Object.most({a:1,b:3,c:3})                   -> 'b'
11544        *   Object.most({a:'aa',b:'bb',c:'c'}, 'length') -> 'a'
11545        *   Object.most({a:1,b:3,c:3}, true)             -> {b:3,c:3}
11546        *
11547        * @signature most([map])
11548        * @param {string|mapFn} map
11549        * @param {boolean} [all]
11550        * @callbackParam {Property} val
11551        * @callbackParam {string} key
11552        * @callbackParam {Object} obj
11553        * @callbackReturns {NewProperty} mapFn
11554        *
11555        ***/
11556        'most': function(obj, all, map) {
11557            return getLeastOrMost(obj, all, map, true, true);
11558        }
11559
11560    });
11561
11562
11563    buildFromIndexMethods();
11564
11565    /***
11566    * @module Number
11567    * @description Number formatting, precision rounding, Math aliases, and more.
11568    *
11569    ***/
11570
11571
11572    var NUMBER_OPTIONS = {
11573        'decimal': HALF_WIDTH_PERIOD,
11574        'thousands': HALF_WIDTH_COMMA
11575    };
11576
11577    // Abbreviation Units
11578    var BASIC_UNITS = '|kmbt',
11579      MEMORY_UNITS = '|KMGTPE',
11580      MEMORY_BINARY_UNITS = '|,Ki,Mi,Gi,Ti,Pi,Ei',
11581      METRIC_UNITS_SHORT = 'nμm|k',
11582      METRIC_UNITS_FULL = 'yzafpnμm|KMGTPEZY';
11583
11584
11585    /***
11586    * @method getOption(name)
11587    * @returns Mixed
11588    * @accessor
11589    * @short Gets an option used interally by Number.
11590    * @example
11591    *
11592    *   Sugar.Number.getOption('thousands');
11593    *
11594    * @param {string} name
11595    *
11596    ***
11597    * @method setOption(name, value)
11598    * @accessor
11599    * @short Sets an option used interally by Number.
11600    * @extra If `value` is `null`, the default value will be restored.
11601    * @options
11602    *
11603    *   decimal     A string used as the decimal marker by `format`, `abbr`,
11604    *               `metric`, and `bytes`. Default is `.`.
11605    *
11606    *   thousands   A string used as the thousands marker by `format`, `abbr`,
11607    *               `metric`, and `bytes`. Default is `,`.
11608    *
11609    *
11610    * @example
11611    *
11612    *   Sugar.Number.setOption('decimal', ',');
11613    *   Sugar.Number.setOption('thousands', ' ');
11614    *
11615    * @signature setOption(options)
11616    * @param {NumberOptions} options
11617    * @param {string} name
11618    * @param {any} value
11619    * @option {string} decimal
11620    * @option {string} thousands
11621    *
11622    ***/
11623    var _numberOptions = defineOptionsAccessor(sugarNumber, NUMBER_OPTIONS);
11624
11625
11626    function abbreviateNumber(num, precision, ustr, bytes) {
11627        var fixed = num.toFixed(20),
11628        decimalPlace = fixed.search(/\./),
11629        numeralPlace = fixed.search(/[1-9]/),
11630        significant = decimalPlace - numeralPlace,
11631        units, unit, mid, i, divisor;
11632        if (significant > 0) {
11633            significant -= 1;
11634        }
11635        units = commaSplit(ustr);
11636        if (units.length === 1) {
11637            units = ustr.split('');
11638        }
11639        mid = units.indexOf('|');
11640        if (mid === -1) {
11641            // Skipping the placeholder means the units should start from zero,
11642            // otherwise assume they end at zero.
11643            mid = units[0] === '_' ? 0 : units.length;
11644        }
11645        i = max(min(floor(significant / 3), units.length - mid - 1), -mid);
11646        unit = units[i + mid];
11647        while (unit === '_') {
11648            i += i < 0 ? -1 : 1;
11649            unit = units[i + mid];
11650        }
11651        if (unit === '|') {
11652            unit = '';
11653        }
11654        if (significant < -9) {
11655            precision = abs(significant) - 9;
11656        }
11657        divisor = bytes ? pow(2, 10 * i) : pow(10, i * 3);
11658        return numberFormat(withPrecision(num / divisor, precision || 0)) + unit;
11659    }
11660
11661    function numberFormat(num, place) {
11662        var result = '', thousands, decimal, fraction, integer, split, str;
11663
11664        decimal = _numberOptions('decimal');
11665        thousands = _numberOptions('thousands');
11666
11667        if (isNumber(place)) {
11668            str = withPrecision(num, place || 0).toFixed(max(place, 0));
11669        } else {
11670            str = num.toString();
11671        }
11672
11673        str = str.replace(/^-/, '');
11674        split = periodSplit(str);
11675        integer = split[0];
11676        fraction = split[1];
11677        if (/e/.test(str)) {
11678            result = str;
11679        } else {
11680            for (var i = integer.length; i > 0; i -= 3) {
11681                if (i < integer.length) {
11682                    result = thousands + result;
11683                }
11684                result = integer.slice(max(0, i - 3), i) + result;
11685            }
11686        }
11687        if (fraction) {
11688            result += decimal + repeatString('0', (place || 0) - fraction.length) + fraction;
11689        }
11690        return (num < 0 ? '-' : '') + result;
11691    }
11692
11693    function isInteger(n) {
11694        return n % 1 === 0;
11695    }
11696
11697    function isMultipleOf(n1, n2) {
11698        return n1 % n2 === 0;
11699    }
11700
11701    function createRoundingFunction(fn) {
11702        return function(n, precision) {
11703            return precision ? withPrecision(n, precision, fn) : fn(n);
11704        };
11705    }
11706
11707    defineStatic(sugarNumber, {
11708
11709        /***
11710        * @method random([n1], [n2])
11711        * @returns Number
11712        * @static
11713        * @short Returns a random integer from [n1] to [n2] (both inclusive).
11714        * @extra If only 1 number is passed, the other will be 0. If none are passed,
11715        *        the number will be either 0 or 1.
11716        *
11717        * @example
11718        *
11719        *   Number.random(50, 100) -> ex. 85
11720        *   Number.random(50)      -> ex. 27
11721        *   Number.random()        -> ex. 0
11722        *
11723        * @param {number} [n1]
11724        * @param {number} [n2]
11725        *
11726        ***/
11727        'random': function(n1, n2) {
11728            var minNum, maxNum;
11729            if (arguments.length == 1) n2 = n1, n1 = 0;
11730            minNum = min(n1 || 0, isUndefined(n2) ? 1 : n2);
11731            maxNum = max(n1 || 0, isUndefined(n2) ? 1 : n2) + 1;
11732            return trunc((Math.random() * (maxNum - minNum)) + minNum);
11733        }
11734
11735    });
11736
11737    defineInstance(sugarNumber, {
11738
11739        /***
11740        * @method isInteger()
11741        * @returns Boolean
11742        * @short Returns true if the number has no trailing decimal.
11743        *
11744        * @example
11745        *
11746        *   (420).isInteger() -> true
11747        *   (4.5).isInteger() -> false
11748        *
11749        ***/
11750        'isInteger': function(n) {
11751            return isInteger(n);
11752        },
11753
11754        /***
11755        * @method isOdd()
11756        * @returns Boolean
11757        * @short Returns true if the number is odd.
11758        *
11759        * @example
11760        *
11761        *   (3).isOdd()  -> true
11762        *   (18).isOdd() -> false
11763        *
11764        ***/
11765        'isOdd': function(n) {
11766            return isInteger(n) && !isMultipleOf(n, 2);
11767        },
11768
11769        /***
11770        * @method isEven()
11771        * @returns Boolean
11772        * @short Returns true if the number is even.
11773        *
11774        * @example
11775        *
11776        *   (6).isEven()  -> true
11777        *   (17).isEven() -> false
11778        *
11779        ***/
11780        'isEven': function(n) {
11781            return isMultipleOf(n, 2);
11782        },
11783
11784        /***
11785        * @method isMultipleOf(num)
11786        * @returns Boolean
11787        * @short Returns true if the number is a multiple of `num`.
11788        *
11789        * @example
11790        *
11791        *   (6).isMultipleOf(2)  -> true
11792        *   (17).isMultipleOf(2) -> false
11793        *   (32).isMultipleOf(4) -> true
11794        *   (34).isMultipleOf(4) -> false
11795        *
11796        * @param {number} num
11797        *
11798        ***/
11799        'isMultipleOf': function(n, num) {
11800            return isMultipleOf(n, num);
11801        },
11802
11803        /***
11804        * @method log([base] = Math.E)
11805        * @returns Number
11806        * @short Returns the logarithm of the number with `base`, or the natural
11807        *        logarithm of the number if `base` is undefined.
11808        *
11809        * @example
11810        *
11811        *   (64).log(2) -> 6
11812        *   (9).log(3)  -> 2
11813        *   (5).log()   -> 1.6094379124341003
11814        *
11815        * @param {number} [base]
11816        *
11817        ***/
11818        'log': function(n, base) {
11819            return Math.log(n) / (base ? Math.log(base) : 1);
11820        },
11821
11822        /***
11823        * @method abbr([precision] = 0)
11824        * @returns String
11825        * @short Returns an abbreviated form of the number ("k" for thousand, "m"
11826        *        for million, etc).
11827        * @extra [precision] will round to the given precision. `thousands` and
11828        *        `decimal` allow custom separators to be used.
11829        *
11830        * @example
11831        *
11832        *   (1000).abbr()    -> "1k"
11833        *   (1000000).abbr() -> "1m"
11834        *   (1280).abbr(1)   -> "1.3k"
11835        *
11836        * @param {number} [precision]
11837        *
11838        ***/
11839        'abbr': function(n, precision) {
11840            return abbreviateNumber(n, precision, BASIC_UNITS);
11841        },
11842
11843        /***
11844        * @method metric([precision] = 0, [units] = "nμm|k")
11845        * @returns String
11846        * @short Returns the number as a string in metric notation.
11847        * @extra [precision] will round to the given precision (can be negative).
11848        *        [units] is a string that determines both the unit notation and the
11849        *        min/max unit allowed. The default is natural notation for common
11850        *        units (meters, grams, etc). "all" can be passed for [units] and is a
11851        *        shortcut to all standard SI units. The token `,` if present separates
11852        *        units, otherwise each character is a unit. The token `|` if present
11853        *        marks where fractional units end, otherwise no fractional units are
11854        *        used. Finally, the token `_` if present is a placeholder for no unit.
11855        *
11856        * @example
11857        *
11858        *   (1000).metric()        -> "1k"
11859        *   (1000000).metric()     -> "1,000k"
11860        *   (1249).metric(2) + 'g' -> "1.25kg"
11861        *   (0.025).metric() + 'm' -> "25mm"
11862        *   (1000000).metric(0, 'nμm|kM') -> "1M"
11863        *
11864        * @param {number} [precision]
11865        * @param {string} [units]
11866        *
11867        ***/
11868        'metric': function(n, precision, units) {
11869            if (units === 'all') {
11870                units = METRIC_UNITS_FULL;
11871            } else if (!units) {
11872                units = METRIC_UNITS_SHORT;
11873            }
11874            return abbreviateNumber(n, precision, units);
11875        },
11876
11877        /***
11878        * @method bytes([precision] = 0, [binary] = false, [units] = 'si')
11879        * @returns String
11880        * @short Returns an abbreviated form of the number, with 'B' on the end for "bytes".
11881        * @extra [precision] will round to the given precision. If [binary] is `true`,
11882        *        powers of 1024 will be used instead of 1000, and units will default
11883        *        to the binary units "KiB", "MiB", etc. Units can be overridden by
11884        *        passing "si" or "binary" for [units], or further customized by
11885        *        passing a unit string. See `metric` for more.
11886        *
11887        * @example
11888        *
11889        *   (1000).bytes()                 -> "1KB"
11890        *   (1289).bytes(2)                -> "1.29KB"
11891        *   (1000).bytes(2, true)          -> "0.98KiB"
11892        *   (1000).bytes(2, true, 'si')    -> "0.98KB"
11893        *
11894        * @param {number} [precision]
11895        * @param {boolean} [binary]
11896        * @param {string} [units]
11897        *
11898        ***/
11899        'bytes': function(n, precision, binary, units) {
11900            if (units === 'binary' || (!units && binary)) {
11901                units = MEMORY_BINARY_UNITS;
11902            } else if (units === 'si' || !units) {
11903                units = MEMORY_UNITS;
11904            }
11905            return abbreviateNumber(n, precision, units, binary) + 'B';
11906        },
11907
11908        /***
11909        * @method format([place] = 0)
11910        * @returns String
11911        * @short Formats the number to a readable string.
11912        * @extra If [place] is `undefined`, the place will automatically be determined.
11913        *        `thousands` and `decimal` allow custom markers to be used.
11914        *
11915        * @example
11916        *
11917        *   (56782).format()    -> '56,782'
11918        *   (56782).format(2)   -> '56,782.00'
11919        *   (4388.43).format(2) -> '4,388.43'
11920        *
11921        * @param {number} [place]
11922        *
11923        ***/
11924        'format': function(n, place) {
11925            return numberFormat(n, place);
11926        },
11927
11928        /***
11929        * @method hex([pad] = 1)
11930        * @returns String
11931        * @short Converts the number to hexidecimal.
11932        * @extra [pad] will pad the resulting string to that many places.
11933        *
11934        * @example
11935        *
11936        *   (255).hex()   -> 'ff';
11937        *   (255).hex(4)  -> '00ff';
11938        *   (23654).hex() -> '5c66';
11939        *
11940        * @param {number} [pad]
11941        *
11942        ***/
11943        'hex': function(n, pad) {
11944            return padNumber(n, pad || 1, false, 16);
11945        },
11946
11947        /***
11948        * @method times(fn)
11949        * @returns Mixed
11950        * @short Calls `fn` a number of times equivalent to the number.
11951        * @extra Any non-undefined return values of `fn` will be collected and
11952        *        returned in an array.
11953        *
11954        * @callback indexMapFn
11955        *
11956        *   i   The index of the current iteration.
11957        *
11958        * @example
11959        *
11960        *   (8).times(logHello) -> logs "hello" 8 times
11961        *   (7).times(function(n) {
11962        *     return Math.pow(2, n);
11963        *   });
11964        *
11965        * @callbackParam {number} i
11966        * @callbackReturns {any} indexMapFn
11967        * @param {indexMapFn} fn
11968        *
11969        ***/
11970        'times': function(n, fn) {
11971            var arr, result;
11972            for (var i = 0; i < n; i++) {
11973                result = fn.call(n, i);
11974                if (isDefined(result)) {
11975                    if (!arr) {
11976                        arr = [];
11977                    }
11978                    arr.push(result);
11979                }
11980            }
11981            return arr;
11982        },
11983
11984        /***
11985        * @method chr()
11986        * @returns String
11987        * @short Returns a string at the code point of the number.
11988        *
11989        * @example
11990        *
11991        *   (65).chr() -> "A"
11992        *   (75).chr() -> "K"
11993        *
11994        ***/
11995        'chr': function(n) {
11996            return chr(n);
11997        },
11998
11999        /***
12000        * @method pad([place] = 0, [sign] = false, [base] = 10)
12001        * @returns String
12002        * @short Pads a number with "0" to `place`.
12003        * @extra [sign] allows you to force the sign as well (+05, etc). [base] can
12004        *        change the base for numeral conversion.
12005        *
12006        * @example
12007        *
12008        *   (5).pad(2)        -> '05'
12009        *   (-5).pad(4)       -> '-0005'
12010        *   (82).pad(3, true) -> '+082'
12011        *
12012        * @param {number} place
12013        * @param {boolean} [sign]
12014        * @param {number} [base]
12015        *
12016        ***/
12017        'pad': function(n, place, sign, base) {
12018            return padNumber(n, place, sign, base);
12019        },
12020
12021        /***
12022        * @method ordinalize()
12023        * @returns String
12024        * @short Returns an ordinalized English string, i.e. "1st", "2nd", etc.
12025        *
12026        * @example
12027        *
12028        *   (1).ordinalize() -> '1st';
12029        *   (2).ordinalize() -> '2nd';
12030        *   (8).ordinalize() -> '8th';
12031        *
12032        ***/
12033        'ordinalize': function(n) {
12034            var num = abs(n), last = +num.toString().slice(-2);
12035            return n + getOrdinalSuffix(last);
12036        },
12037
12038        /***
12039        * @method toNumber()
12040        * @returns Number
12041        * @short Identity function for compatibilty.
12042        *
12043        * @example
12044        *
12045        *   (420).toNumber() -> 420
12046        *
12047        ***/
12048        'toNumber': function(n) {
12049            return n.valueOf();
12050        },
12051
12052        /***
12053        * @method round([precision] = 0)
12054        * @returns Number
12055        * @short Shortcut for `Math.round` that also allows a `precision`.
12056        *
12057        * @example
12058        *
12059        *   (3.241).round()  -> 3
12060        *   (-3.841).round() -> -4
12061        *   (3.241).round(2) -> 3.24
12062        *   (3748).round(-2) -> 3800
12063        *
12064        * @param {number} [precision]
12065        *
12066        ***/
12067        'round': createRoundingFunction(round),
12068
12069        /***
12070        * @method ceil([precision] = 0)
12071        * @returns Number
12072        * @short Shortcut for `Math.ceil` that also allows a `precision`.
12073        *
12074        * @example
12075        *
12076        *   (3.241).ceil()  -> 4
12077        *   (-3.241).ceil() -> -3
12078        *   (3.241).ceil(2) -> 3.25
12079        *   (3748).ceil(-2) -> 3800
12080        *
12081        * @param {number} [precision]
12082        *
12083        ***/
12084        'ceil': createRoundingFunction(ceil),
12085
12086        /***
12087        * @method floor([precision] = 0)
12088        * @returns Number
12089        * @short Shortcut for `Math.floor` that also allows a `precision`.
12090        *
12091        * @example
12092        *
12093        *   (3.241).floor()  -> 3
12094        *   (-3.841).floor() -> -4
12095        *   (3.241).floor(2) -> 3.24
12096        *   (3748).floor(-2) -> 3700
12097        *
12098        * @param {number} [precision]
12099        *
12100        ***/
12101        'floor': createRoundingFunction(floor)
12102
12103    });
12104
12105    /***
12106    * @method [math]()
12107    * @returns Number
12108    * @short Math related functions are mapped as shortcuts to numbers and are
12109    *        identical. Note that `log` provides some special defaults.
12110    *
12111    * @set
12112    *   abs
12113    *   sin
12114    *   asin
12115    *   cos
12116    *   acos
12117    *   tan
12118    *   atan
12119    *   sqrt
12120    *   exp
12121    *   pow
12122    *
12123    * @example
12124    *
12125    *   (3).pow(3) -> 27
12126    *   (-3).abs() -> 3
12127    *   (1024).sqrt() -> 32
12128    *
12129    ***/
12130    function buildMathAliases() {
12131        defineInstanceSimilar(sugarNumber, 'abs pow sin asin cos acos tan atan exp pow sqrt', function(methods, name) {
12132            methods[name] = function(n, arg) {
12133                // Note that .valueOf() here is only required due to a
12134                // very strange bug in iOS7 that only occurs occasionally
12135                // in which Math.abs() called on non-primitive numbers
12136                // returns a completely different number (Issue #400)
12137                return Math[name](n.valueOf(), arg);
12138            };
12139        });
12140    }
12141
12142    buildMathAliases();
12143
12144    /***
12145    * @module Function
12146    * @description Lazy, throttled, and memoized functions, delayed functions and
12147    *              handling of timers, argument currying.
12148    *
12149    ***/
12150
12151    var _lock = privatePropertyAccessor('lock');
12152    var _timers = privatePropertyAccessor('timers');
12153    var _partial = privatePropertyAccessor('partial');
12154    var _canceled = privatePropertyAccessor('canceled');
12155
12156    var createInstanceFromPrototype = Object.create || function(prototype) {
12157        var ctor = function() { };
12158        ctor.prototype = prototype;
12159        return new ctor;
12160    };
12161
12162    function setDelay(fn, ms, after, scope, args) {
12163        // Delay of infinity is never called of course...
12164        ms = coercePositiveInteger(ms || 0);
12165        if (!_timers(fn)) {
12166            _timers(fn, []);
12167        }
12168        // This is a workaround for <= IE8, which apparently has the
12169        // ability to call timeouts in the queue on the same tick (ms?)
12170        // even if functionally they have already been cleared.
12171        _canceled(fn, false);
12172        _timers(fn).push(setTimeout(function() {
12173            if (!_canceled(fn)) {
12174                after.apply(scope, args || []);
12175            }
12176        }, ms));
12177    }
12178
12179    function cancelFunction(fn) {
12180        var timers = _timers(fn), timer;
12181        if (isArray(timers)) {
12182            while (timer = timers.shift()) {
12183                clearTimeout(timer);
12184            }
12185        }
12186        _canceled(fn, true);
12187        return fn;
12188    }
12189
12190    function createLazyFunction(fn, ms, immediate, limit) {
12191        var queue = [], locked = false, execute, rounded, perExecution, result;
12192        ms = ms || 1;
12193        limit = limit || Infinity;
12194        rounded = ceil(ms);
12195        perExecution = round(rounded / ms) || 1;
12196        execute = function() {
12197            var queueLength = queue.length, maxPerRound;
12198            if (queueLength == 0) return;
12199            // Allow fractions of a millisecond by calling
12200            // multiple times per actual timeout execution
12201            maxPerRound = max(queueLength - perExecution, 0);
12202            while (queueLength > maxPerRound) {
12203                // Getting uber-meta here...
12204                result = Function.prototype.apply.apply(fn, queue.shift());
12205                queueLength--;
12206            }
12207            setDelay(lazy, rounded, function() {
12208                locked = false;
12209                execute();
12210            });
12211        };
12212        function lazy() {
12213            // If the execution has locked and it's immediate, then
12214            // allow 1 less in the queue as 1 call has already taken place.
12215            if (queue.length < limit - (locked && immediate ? 1 : 0)) {
12216                // Optimized: no leaking arguments
12217                var args = []; for (var $i = 0, $len = arguments.length; $i < $len; $i++) args.push(arguments[$i]);
12218                queue.push([this, args]);
12219            }
12220            if (!locked) {
12221                locked = true;
12222                if (immediate) {
12223                    execute();
12224                } else {
12225                    setDelay(lazy, rounded, execute);
12226                }
12227            }
12228            // Return the memoized result
12229            return result;
12230        }
12231        return lazy;
12232    }
12233
12234    // Collecting arguments in an array instead of
12235    // passing back the arguments object which will
12236    // deopt this function in V8.
12237    function collectArguments() {
12238        var args = arguments, i = args.length, arr = new Array(i);
12239        while (i--) {
12240            arr[i] = args[i];
12241        }
12242        return arr;
12243    }
12244
12245    function createHashedMemoizeFunction(fn, hashFn, limit) {
12246        var map = {}, refs = [], counter = 0;
12247        return function() {
12248            var hashObj = hashFn.apply(this, arguments);
12249            var key = serializeInternal(hashObj, refs);
12250            if (hasOwn(map, key)) {
12251                return getOwn(map, key);
12252            }
12253            if (counter === limit) {
12254                map = {};
12255                refs = [];
12256                counter = 0;
12257            }
12258            counter++;
12259            return map[key] = fn.apply(this, arguments);
12260        };
12261    }
12262
12263    defineInstance(sugarFunction, {
12264
12265        /***
12266        * @method lazy([ms] = 1, [immediate] = false, [limit] = Infinity)
12267        * @returns Function
12268        * @short Creates a lazy function that, when called repeatedly, will queue
12269        *        execution and wait [ms] milliseconds to execute.
12270        * @extra If [immediate] is `true`, first execution will happen immediately,
12271        *        then lock. If [limit] is a fininte number, calls past [limit] will
12272        *        be ignored while execution is locked. Compare this to `throttle`,
12273        *        which will execute only once per [ms] milliseconds. Note that [ms]
12274        *        can also be a fraction. Calling `cancel` on a lazy function will
12275        *        clear the entire queue.
12276        *
12277        * @example
12278        *
12279        *   var fn = logHello.lazy(250);
12280        *   runTenTimes(fn); -> Logs 10 times each time 250ms later
12281        *
12282        *   var fn = logHello.lazy(250, false, 5);
12283        *   runTenTimes(fn); -> Logs 5 times each time 250ms later
12284        *
12285        * @param {number} [ms]
12286        * @param {number} [limit]
12287        * @param {boolean} [immediate]
12288        *
12289        ***/
12290        'lazy': function(fn, ms, immediate, limit) {
12291            return createLazyFunction(fn, ms, immediate, limit);
12292        },
12293
12294        /***
12295        * @method throttle([ms] = 1)
12296        * @returns Function
12297        * @short Creates a "throttled" version of the function that will only be
12298        *        executed once per `ms` milliseconds.
12299        * @extra This is functionally equivalent to calling `lazy` with a [limit] of
12300        *        `1` and [immediate] as `true`. `throttle` is appropriate when you
12301        *        want to make sure a function is only executed at most once for a
12302        *        given duration.
12303        *
12304        * @example
12305        *
12306        *   var fn = logHello.throttle(50);
12307        *   runTenTimes(fn);
12308        *
12309        * @param {number} [ms]
12310        *
12311        ***/
12312        'throttle': function(fn, ms) {
12313            return createLazyFunction(fn, ms, true, 1);
12314        },
12315
12316        /***
12317        * @method debounce([ms] = 1)
12318        * @returns Function
12319        * @short Creates a "debounced" function that postpones its execution until
12320        *        after `ms` milliseconds have passed.
12321        * @extra This method is useful to execute a function after things have
12322        *        "settled down". A good example of this is when a user tabs quickly
12323        *        through form fields, execution of a heavy operation should happen
12324        *        after a few milliseconds when they have "settled" on a field.
12325        *
12326        * @example
12327        *
12328        *   var fn = logHello.debounce(250)
12329        *   runTenTimes(fn); -> called once 250ms later
12330        *
12331        * @param {number} [ms]
12332        *
12333        ***/
12334        'debounce': function(fn, ms) {
12335            function debounced() {
12336                // Optimized: no leaking arguments
12337                var args = []; for (var $i = 0, $len = arguments.length; $i < $len; $i++) args.push(arguments[$i]);
12338                cancelFunction(debounced);
12339                setDelay(debounced, ms, fn, this, args);
12340            }
12341            return debounced;
12342        },
12343
12344        /***
12345        * @method cancel()
12346        * @returns Function
12347        * @short Cancels a delayed function scheduled to be run.
12348        * @extra `delay`, `lazy`, `throttle`, and `debounce` can all set delays.
12349        *
12350        * @example
12351        *
12352        *   logHello.delay(500).cancel() -> never logs
12353        *
12354        ***/
12355        'cancel': function(fn) {
12356            return cancelFunction(fn);
12357        },
12358
12359        /***
12360        * @method after(n)
12361        * @returns Function
12362        * @short Creates a function that will execute after `n` calls.
12363        * @extra `after` is useful for running a final callback after a specific
12364        *        number of operations, often when the order in which the operations
12365        *        will complete is unknown. The created function will be passed an
12366        *        array of the arguments that it has collected from each after `n`.
12367        *        Note that the function will execute on every call after `n`.
12368        *        Use `once` in conjunction with this method to prevent being
12369        *        triggered by subsequent calls.
12370        *
12371        * @example
12372        *
12373        *   var fn = logHello.after(5);
12374        *   runTenTimes(fn); -> logs 6 times
12375        *
12376        *   var fn = logHello.once().after(5)
12377        *   runTenTimes(fn); -> logs once
12378        *
12379        * @param {number} [n]
12380        *
12381        ***/
12382        'after': function(fn, num) {
12383            var count = 0, collectedArgs = [];
12384            num = coercePositiveInteger(num);
12385            return function() {
12386                // Optimized: no leaking arguments
12387                var args = []; for (var $i = 0, $len = arguments.length; $i < $len; $i++) args.push(arguments[$i]);
12388                collectedArgs.push(args);
12389                count++;
12390                if (count >= num) {
12391                    return fn.call(this, collectedArgs);
12392                }
12393            };
12394        },
12395
12396        /***
12397        * @method once()
12398        * @returns Function
12399        * @short Creates a function that will execute only once and store the result.
12400        * @extra `once` is useful for creating functions that will cache the result
12401        *        of an expensive operation and use it on subsequent calls. Also it
12402        *        can be useful for creating initialization functions that only need
12403        *        to be run once.
12404        *
12405        * @example
12406        *
12407        *   var fn = logHello.once();
12408        *   runTenTimes(fn); -> logs once
12409        *
12410        ***/
12411        'once': function(fn) {
12412            var called = false, val;
12413            return function() {
12414                if (called) {
12415                    return val;
12416                }
12417                called = true;
12418                return val = fn.apply(this, arguments);
12419            };
12420        },
12421
12422        /***
12423        * @method memoize([hashFn], [limit])
12424        * @returns Function
12425        * @short Creates a function that will memoize results for unique calls.
12426        * @extra `memoize` can be thought of as a more powerful `once`. Where `once`
12427        *        will only call a function once ever, memoized functions will be
12428        *        called once per unique call. A "unique call" is determined by the
12429        *        return value of [hashFn], which is passed the arguments of each call.
12430        *        If [hashFn] is undefined, it will deeply serialize all arguments,
12431        *        such that any different argument signature will result in a unique
12432        *        call. [hashFn] may be a string (allows `deep properties`) that acts
12433        *        as a shortcut to return a property of the first argument passed.
12434        *        [limit] sets an upper limit on memoized results. The default is no
12435        *        limit, meaning that unique calls will continue to memoize results.
12436        *        For most use cases this is fine, however [limit] is useful for more
12437        *        persistent (often server-side) applications for whom memory leaks
12438        *        are a concern.
12439        *
12440        * @example
12441        *
12442        *   var fn = logHello.memoize();
12443        *   fn(1); fn(1); fn(2); -> logs twice, memoizing once
12444        *
12445        *   var fn = calculateUserBalance.memoize('id');
12446        *   fn(Harry); fn(Mark); fn(Mark); -> logs twice, memoizing once
12447        *
12448        * @param {string|Function} [hashFn]
12449        * @param {number} [limit]
12450        *
12451        ***/
12452        'memoize': function(fn, arg1, arg2) {
12453            var hashFn, limit, prop;
12454            if (isNumber(arg1)) {
12455                limit = arg1;
12456            } else {
12457                hashFn = arg1;
12458                limit = arg2;
12459            }
12460            if (isString(hashFn)) {
12461                prop = hashFn;
12462                hashFn = function(obj) {
12463                    return deepGetProperty(obj, prop);
12464                };
12465            } else if (!hashFn) {
12466                hashFn = collectArguments;
12467            }
12468            return createHashedMemoizeFunction(fn, hashFn, limit);
12469        },
12470
12471        /***
12472        * @method lock([n])
12473        * @returns Function
12474        * @short Locks the number of arguments accepted by the function.
12475        * @extra If not passed, [n] will be the length of the function. This method
12476        *        can be called on functions created by `partial`, in which case it
12477        *        will lock the total arguments during execution.
12478        *
12479        * @example
12480        *
12481        *   logArgs.lock(2)(1,2,3)      -> logs 1,2
12482        *
12483        * @param {number} [n]
12484        *
12485        ***/
12486        'lock': function(fn, n) {
12487            var lockedFn;
12488            if (_partial(fn)) {
12489                _lock(fn, isNumber(n) ? n : null);
12490                return fn;
12491            }
12492            lockedFn = function() {
12493                arguments.length = min(_lock(lockedFn), arguments.length);
12494                return fn.apply(this, arguments);
12495            };
12496            _lock(lockedFn, isNumber(n) ? n : fn.length);
12497            return lockedFn;
12498        }
12499
12500    });
12501
12502    defineInstanceWithArguments(sugarFunction, {
12503
12504        /***
12505        * @method partial([arg1], [arg2], ...)
12506        * @returns Function
12507        * @short Returns a new version of the function which has part of its arguments
12508        *        pre-emptively filled in, also known as "currying".
12509        * @extra `undefined` can be passed as any argument, and is a placeholder that
12510        *        will be replaced with arguments passed when the function is executed.
12511        *        This allows currying of arguments even when they occur toward the end
12512        *        of an argument list (the example demonstrates this more clearly).
12513        *
12514        * @example
12515        *
12516        *   logArgs.partial(undefined, 'b')('a') -> logs a, b
12517        *
12518        * @param {any} [arg1]
12519        * @param {any} [arg2]
12520        *
12521        ***/
12522        'partial': function(fn, curriedArgs) {
12523            var curriedLen = curriedArgs.length;
12524            var partialFn = function() {
12525                var argIndex = 0, applyArgs = [], self = this, lock = _lock(partialFn), result, i;
12526                for (i = 0; i < curriedLen; i++) {
12527                    var arg = curriedArgs[i];
12528                    if (isDefined(arg)) {
12529                        applyArgs[i] = arg;
12530                    } else {
12531                        applyArgs[i] = arguments[argIndex++];
12532                    }
12533                }
12534                for (i = argIndex; i < arguments.length; i++) {
12535                    applyArgs.push(arguments[i]);
12536                }
12537                if (lock === null) {
12538                    lock = curriedLen;
12539                }
12540                if (isNumber(lock)) {
12541                    applyArgs.length = min(applyArgs.length, lock);
12542                }
12543                // If the bound "this" object is an instance of the partialed
12544                // function, then "new" was used, so preserve the prototype
12545                // so that constructor functions can also be partialed.
12546                if (self instanceof partialFn) {
12547                    self = createInstanceFromPrototype(fn.prototype);
12548                    result = fn.apply(self, applyArgs);
12549                    // An explicit return value is allowed from constructors
12550                    // as long as they are of "object" type, so return the
12551                    // correct result here accordingly.
12552                    return isObjectType(result) ? result : self;
12553                }
12554                return fn.apply(self, applyArgs);
12555            };
12556            _partial(partialFn, true);
12557            return partialFn;
12558        },
12559
12560        /***
12561        * @method delay([ms] = 1, [arg1], [arg2], ...)
12562        * @returns Function
12563        * @short Executes the function after `ms` milliseconds.
12564        * @extra Returns a reference to itself. `delay` is also a way to execute non-
12565        *        blocking operations that will wait until the CPU is free. Delayed
12566        *        functions can be canceled using the `cancel` method. Can also curry
12567        *        arguments passed in after `ms`.
12568        *
12569        * @example
12570        *
12571        *   logHello.delay(500)     -> logs after 500ms
12572        *   logArgs.delay(500, 'a') -> logs "a" after 500ms
12573        *
12574        * @param {number} [ms]
12575        * @param {any} [arg1]
12576        * @param {any} [arg2]
12577        *
12578        ***/
12579        'delay': function(fn, ms, args) {
12580            setDelay(fn, ms, fn, fn, args);
12581            return fn;
12582        },
12583
12584        /***
12585        * @method every([ms] = 1, [arg1], [arg2], ...)
12586        * @returns Function
12587        * @short Executes the function every `ms` milliseconds.
12588        * @extra Returns a reference to itself. `every` uses `setTimeout`, which
12589        *        means that you are guaranteed a period of idle time equal to [ms]
12590        *        after execution has finished. Compare this to `setInterval` which
12591        *        will try to run a function every [ms], even when execution itself
12592        *        takes up a portion of that time. In most cases avoiding `setInterval`
12593        *        is better as calls won't "back up" when the CPU is under strain,
12594        *        however this also means that calls are less likely to happen at
12595        *        exact intervals of [ms], so the use case here should be considered.
12596        *        Additionally, `every` can curry arguments passed in after [ms], and
12597        *        also be canceled with `cancel`.
12598        *
12599        * @example
12600        *
12601        *   logHello.every(1000)        -> logs every second
12602        *   logArgs.every(1000, 'Hola') -> logs 'hola' every second
12603        *
12604        * @param {number} [ms]
12605        * @param {any} [arg1]
12606        * @param {any} [arg2]
12607        *
12608        ***/
12609        'every': function(fn, ms, args) {
12610            function execute() {
12611                // Set the delay first here, so that cancel
12612                // can be called within the executing function.
12613                setDelay(fn, ms, execute);
12614                fn.apply(fn, args);
12615            }
12616            setDelay(fn, ms, execute);
12617            return fn;
12618        }
12619
12620    });
12621
12622    /***
12623    * @module RegExp
12624    * @description RegExp escaping and flag manipulation.
12625    *
12626    * Note here that methods on the RegExp class like .exec and .test will fail in
12627    * the current version of SpiderMonkey being used by CouchDB when using
12628    * shorthand regex notation like /foo/. This is the reason for the intermixed
12629    * use of shorthand and compiled regexes here. If you're using JS in CouchDB, it
12630    * is safer to ALWAYS compile your regexes from a string.
12631    *
12632    ***/
12633
12634    defineStatic(sugarRegExp, {
12635
12636        /***
12637        * @method escape([str] = '')
12638        * @returns String
12639        * @static
12640        * @short Escapes all RegExp tokens in a string.
12641        *
12642        * @example
12643        *
12644        *   RegExp.escape('really?')      -> 'really\?'
12645        *   RegExp.escape('yes.')         -> 'yes\.'
12646        *   RegExp.escape('(not really)') -> '\(not really\)'
12647        *
12648        * @param {string} str
12649        *
12650        ***/
12651        'escape': function(str) {
12652            return escapeRegExp(str);
12653        }
12654
12655    });
12656
12657    defineInstance(sugarRegExp, {
12658
12659        /***
12660        * @method getFlags()
12661        * @returns String
12662        * @short Returns the flags of the regex as a string.
12663        *
12664        * @example
12665        *
12666        *   /texty/gim.getFlags() -> 'gim'
12667        *
12668        ***/
12669        'getFlags': function(r) {
12670            return getRegExpFlags(r);
12671        },
12672
12673        /***
12674        * @method setFlags(flags)
12675        * @returns RegExp
12676        * @short Creates a copy of the regex with `flags` set.
12677        *
12678        * @example
12679        *
12680        *   /texty/.setFlags('gim') -> now has global, ignoreCase, and multiline set
12681        *
12682        * @param {string} flags
12683        *
12684        ***/
12685        'setFlags': function(r, flags) {
12686            return RegExp(r.source, flags);
12687        },
12688
12689        /***
12690        * @method addFlags(flags)
12691        * @returns RegExp
12692        * @short Creates a copy of the regex with `flags` added.
12693        *
12694        * @example
12695        *
12696        *   /texty/.addFlags('g')  -> /texty/g
12697        *   /texty/.addFlags('im') -> /texty/im
12698        *
12699        * @param {string} flags
12700        *
12701        ***/
12702        'addFlags': function(r, flags) {
12703            return RegExp(r.source, getRegExpFlags(r, flags));
12704        },
12705
12706        /***
12707        * @method removeFlags(flags)
12708        * @returns RegExp
12709        * @short Creates a copy of the regex with `flags` removed.
12710        *
12711        * @example
12712        *
12713        *   /texty/gim.removeFlags('g')  -> /texty/im
12714        *   /texty/gim.removeFlags('im') -> /texty/g
12715        *
12716        * @param {string} flags
12717        *
12718        ***/
12719        'removeFlags': function(r, flags) {
12720            var reg = allCharsReg(flags);
12721            return RegExp(r.source, getRegExpFlags(r).replace(reg, ''));
12722        }
12723
12724    });
12725
12726    /***
12727    * @module Range
12728    * @description Date, Number, and String ranges that can be manipulated and compared,
12729    *              or enumerate over specific points within the range.
12730    *
12731    ***/
12732
12733    var DURATION_UNITS = 'year|month|week|day|hour|minute|second|millisecond';
12734    var DURATION_REG = RegExp('(\\d+)?\\s*(' + DURATION_UNITS + ')s?', 'i');
12735
12736    var MULTIPLIERS = {
12737        'Hours': 60 * 60 * 1000,
12738        'Minutes': 60 * 1000,
12739        'Seconds': 1000,
12740        'Milliseconds': 1
12741    };
12742
12743    var PrimitiveRangeConstructor = function(start, end) {
12744        return new Range(start, end);
12745    };
12746
12747    function Range(start, end) {
12748        this.start = cloneRangeMember(start);
12749        this.end = cloneRangeMember(end);
12750    }
12751
12752    function getRangeMemberNumericValue(m) {
12753        return isString(m) ? m.charCodeAt(0) : m;
12754    }
12755
12756    function getRangeMemberPrimitiveValue(m) {
12757        if (m == null) return m;
12758        return isDate(m) ? m.getTime() : m.valueOf();
12759    }
12760
12761    function getPrecision(n) {
12762        var split = periodSplit(n.toString());
12763        return split[1] ? split[1].length : 0;
12764    }
12765
12766    function getGreaterPrecision(n1, n2) {
12767        return max(getPrecision(n1), getPrecision(n2));
12768    }
12769
12770    function cloneRangeMember(m) {
12771        if (isDate(m)) {
12772            return new Date(m.getTime());
12773        } else {
12774            return getRangeMemberPrimitiveValue(m);
12775        }
12776    }
12777
12778    function isValidRangeMember(m) {
12779        var val = getRangeMemberPrimitiveValue(m);
12780        return (!!val || val === 0) && valueIsNotInfinite(m);
12781    }
12782
12783    function valueIsNotInfinite(m) {
12784        return m !== -Infinity && m !== Infinity;
12785    }
12786
12787    function rangeIsValid(range) {
12788        return isValidRangeMember(range.start) &&
12789           isValidRangeMember(range.end) &&
12790           typeof range.start === typeof range.end;
12791    }
12792
12793    function rangeEvery(range, step, countOnly, fn) {
12794        var increment,
12795        precision,
12796        dio,
12797        unit,
12798        start = range.start,
12799        end = range.end,
12800        inverse = end < start,
12801        current = start,
12802        index = 0,
12803        result = [];
12804
12805        if (!rangeIsValid(range)) {
12806            return [];
12807        }
12808        if (isFunction(step)) {
12809            fn = step;
12810            step = null;
12811        }
12812        step = step || 1;
12813        if (isNumber(start)) {
12814            precision = getGreaterPrecision(start, step);
12815            increment = function() {
12816                return incrementNumber(current, step, precision);
12817            };
12818        } else if (isString(start)) {
12819            increment = function() {
12820                return incrementString(current, step);
12821            };
12822        } else if (isDate(start)) {
12823            dio = getDateIncrementObject(step);
12824            step = dio[0];
12825            unit = dio[1];
12826            increment = function() {
12827                return incrementDate(current, step, unit);
12828            };
12829        }
12830        // Avoiding infinite loops
12831        if (inverse && step > 0) {
12832            step *= -1;
12833        }
12834        while (inverse ? current >= end : current <= end) {
12835            if (!countOnly) {
12836                result.push(current);
12837            }
12838            if (fn) {
12839                fn(current, index, range);
12840            }
12841            current = increment();
12842            index++;
12843        }
12844        return countOnly ? index - 1 : result;
12845    }
12846
12847    function getDateIncrementObject(amt) {
12848        var match, val, unit;
12849        if (isNumber(amt)) {
12850            return [amt, 'Milliseconds'];
12851        }
12852        match = amt.match(DURATION_REG);
12853        val = +match[1] || 1;
12854        unit = simpleCapitalize(match[2].toLowerCase());
12855        if (unit.match(/hour|minute|second/i)) {
12856            unit += 's';
12857        } else if (unit === 'Year') {
12858            unit = 'FullYear';
12859        } else if (unit === 'Week') {
12860            unit = 'Date';
12861            val *= 7;
12862        } else if (unit === 'Day') {
12863            unit = 'Date';
12864        }
12865        return [val, unit];
12866    }
12867
12868    function incrementDate(src, amount, unit) {
12869        var mult = MULTIPLIERS[unit], d;
12870        if (mult) {
12871            d = new Date(src.getTime() + (amount * mult));
12872        } else {
12873            d = new Date(src);
12874            callDateSet(d, unit, callDateGet(src, unit) + amount);
12875        }
12876        return d;
12877    }
12878
12879    function incrementString(current, amount) {
12880        return chr(current.charCodeAt(0) + amount);
12881    }
12882
12883    function incrementNumber(current, amount, precision) {
12884        return withPrecision(current + amount, precision);
12885    }
12886
12887    function rangeClamp(range, obj) {
12888        var clamped,
12889        start = range.start,
12890        end = range.end,
12891        min = end < start ? end : start,
12892        max = start > end ? start : end;
12893        if (obj < min) {
12894            clamped = min;
12895        } else if (obj > max) {
12896            clamped = max;
12897        } else {
12898            clamped = obj;
12899        }
12900        return cloneRangeMember(clamped);
12901    }
12902
12903    defineOnPrototype(Range, {
12904
12905        /***
12906        * @method toString()
12907        * @returns String
12908        * @short Returns a string representation of the range.
12909        *
12910        * @example
12911        *
12912        *   Number.range(1, 5).toString() -> 1..5
12913        *   janToMay.toString()           -> January 1, xxxx..May 1, xxxx
12914        *
12915        ***/
12916        'toString': function() {
12917            return rangeIsValid(this) ? this.start + '..' + this.end : 'Invalid Range';
12918        },
12919
12920        /***
12921        * @method isValid()
12922        * @returns Boolean
12923        * @short Returns true if the range is valid, false otherwise.
12924        *
12925        * @example
12926        *
12927        *   janToMay.isValid() -> true
12928        *   Number.range(NaN, NaN).isValid()                           -> false
12929        *
12930        ***/
12931        'isValid': function() {
12932            return rangeIsValid(this);
12933        },
12934
12935        /***
12936        * @method span()
12937        * @returns Number
12938        * @short Returns the span of the range. If the range is a date range, the
12939        *        value is in milliseconds.
12940        * @extra The span includes both the start and the end.
12941        *
12942        * @example
12943        *
12944        *   Number.range(5, 10).span()  -> 6
12945        *   Number.range(40, 25).span() -> 16
12946        *   janToMay.span()             -> 10368000001 (or more depending on leap year)
12947        *
12948        ***/
12949        'span': function() {
12950            var n = getRangeMemberNumericValue(this.end) - getRangeMemberNumericValue(this.start);
12951            return rangeIsValid(this) ? abs(n) + 1 : NaN;
12952        },
12953
12954        /***
12955        * @method contains(el)
12956        * @returns Boolean
12957        * @short Returns true if `el` is contained inside the range. `el` may be a
12958        *        value or another range.
12959        *
12960        * @example
12961        *
12962        *   Number.range(5, 10).contains(7)         -> true
12963        *   Number.range(5, 10).contains(2)         -> false
12964        *   janToMay.contains(mar)                  -> true
12965        *   janToMay.contains(marToAug)             -> false
12966        *   janToMay.contains(febToApr)             -> true
12967        *
12968        * @param {RangeElement} el
12969        *
12970        ***/
12971        'contains': function(el) {
12972            if (el == null) return false;
12973            if (el.start && el.end) {
12974                return el.start >= this.start && el.start <= this.end &&
12975               el.end >= this.start && el.end <= this.end;
12976            } else {
12977                return el >= this.start && el <= this.end;
12978            }
12979        },
12980
12981        /***
12982        * @method every(amount, [fn])
12983        * @returns Array
12984        * @short Iterates through the range by `amount`, calling [fn] for each step.
12985        * @extra Returns an array of each increment visited. For date ranges,
12986        *        `amount` can also be a string like `"2 days"`. This will step
12987        *        through the range by incrementing a date object by that specific
12988        *        unit, and so is generally preferable for vague units such as
12989        *        `"2 months"`.
12990        *
12991        * @callback rangeEveryFn
12992        *
12993        *   el   The element of the current iteration.
12994        *   i    The index of the current iteration.
12995        *   r    A reference to the range.
12996        *
12997        * @example
12998        *
12999        *   Number.range(2, 8).every(2) -> [2,4,6,8]
13000        *   janToMay.every('2 months')  -> [Jan 1, Mar 1, May 1]
13001        *
13002        *   sepToOct.every('week', function() {
13003        *     // Will be called every week from September to October
13004        *   })
13005        *
13006        * @param {string|number} amount
13007        * @param {rangeEveryFn} [fn]
13008        * @callbackParam {RangeElement} el
13009        * @callbackParam {number} i
13010        * @callbackParam {Range} r
13011        *
13012        ***/
13013        'every': function(amount, fn) {
13014            return rangeEvery(this, amount, false, fn);
13015        },
13016
13017        /***
13018        * @method toArray()
13019        * @returns Array
13020        * @short Creates an array from the range.
13021        * @extra If the range is a date range, every millisecond between the start
13022        *        and end dates will be returned. To control this use `every` instead.
13023        *
13024        * @example
13025        *
13026        *   Number.range(1, 5).toArray() -> [1,2,3,4,5]
13027        *   Date.range('1 millisecond ago', 'now').toArray() -> [1ms ago, now]
13028        *
13029        ***/
13030        'toArray': function() {
13031            return rangeEvery(this);
13032        },
13033
13034        /***
13035        * @method union(range)
13036        * @returns Range
13037        * @short Returns a new range with the earliest starting point as its start,
13038        *        and the latest ending point as its end. If the two ranges do not
13039        *        intersect this will effectively remove the "gap" between them.
13040        *
13041        * @example
13042        *
13043        *   oneToTen.union(fiveToTwenty) -> 1..20
13044        *   janToMay.union(marToAug)     -> Jan 1, xxxx..Aug 1, xxxx
13045        *
13046        * @param {Range} range
13047        *
13048        ***/
13049        'union': function(range) {
13050            return new Range(
13051        this.start < range.start ? this.start : range.start,
13052        this.end > range.end ? this.end : range.end
13053      );
13054        },
13055
13056        /***
13057        * @method intersect(range)
13058        * @returns Range
13059        * @short Returns a new range with the latest starting point as its start,
13060        *        and the earliest ending point as its end. If the two ranges do not
13061        *        intersect this will effectively produce an invali
13061d range.
13062        *
13063        * @example
13064        *
13065        *   oneToTen.intersect(fiveToTwenty) -> 5..10
13066        *   janToMay.intersect(marToAug)     -> Mar 1, xxxx..May 1, xxxx
13067        *
13068        * @param {Range} range
13069        *
13070        ***/
13071        'intersect': function(range) {
13072            if (range.start > this.end || range.end < this.start) {
13073                return new Range(NaN, NaN);
13074            }
13075            return new Range(
13076        this.start > range.start ? this.start : range.start,
13077        this.end < range.end ? this.end : range.end
13078      );
13079        },
13080
13081        /***
13082        * @method clone()
13083        * @returns Range
13084        * @short Clones the range.
13085        * @extra Members of the range will also be cloned.
13086        *
13087        * @example
13088        *
13089        *   Number.range(1, 5).clone() -> Returns a copy of the range.
13090        *
13091        ***/
13092        'clone': function() {
13093            return new Range(this.start, this.end);
13094        },
13095
13096        /***
13097        * @method clamp(el)
13098        * @returns Mixed
13099        * @short Clamps `el` to be within the range if it falls outside.
13100        *
13101        * @example
13102        *
13103        *   Number.range(1, 5).clamp(8)     -> 5
13104        *   janToMay.clamp(aug) -> May 1, xxxx
13105        *
13106        * @param {RangeElement} el
13107        *
13108        ***/
13109        'clamp': function(el) {
13110            return rangeClamp(this, el);
13111        }
13112
13113    });
13114
13115
13116    /*** @namespace Number ***/
13117
13118    defineStatic(sugarNumber, {
13119
13120        /***
13121        * @method range([start], [end])
13122        * @returns Range
13123        * @static
13124        * @short Creates a new number range between [start] and [end]. See `ranges`
13125        *        for more.
13126        *
13127        * @example
13128        *
13129        *   Number.range(5, 10)
13130        *   Number.range(20, 15)
13131        *
13132        * @param {number} [start]
13133        * @param {number} [end]
13134        *
13135        ***/
13136        'range': PrimitiveRangeConstructor
13137
13138    });
13139
13140    defineInstance(sugarNumber, {
13141
13142        /***
13143        * @method upto(num, [step] = 1, [fn])
13144        * @returns Array
13145        * @short Returns an array containing numbers from the number up to `num`.
13146        * @extra Optionally calls [fn] for each number in that array. [step] allows
13147        *        multiples other than 1. [fn] can be passed in place of [step].
13148        *
13149        * @callback rangeEveryFn
13150        *
13151        *   el   The element of the current iteration.
13152        *   i    The index of the current iteration.
13153        *   r    A reference to the range.
13154        *
13155        * @example
13156        *
13157        *   (2).upto(6) -> [2, 3, 4, 5, 6]
13158        *   (2).upto(6, function(n) {
13159        *     // This function is called 5 times receiving n as the value.
13160        *   });
13161        *   (2).upto(8, 2) -> [2, 4, 6, 8]
13162        *
13163        * @signature upto(num, [fn])
13164        * @param {number} num
13165        * @param {number} [step]
13166        * @param {rangeEveryFn} [fn]
13167        * @callbackParam {RangeElement} el
13168        * @callbackParam {number} i
13169        * @callbackParam {Range} r
13170        *
13171        ***/
13172        'upto': function(n, num, step, fn) {
13173            return rangeEvery(new Range(n, num), step, false, fn);
13174        },
13175
13176        /***
13177        * @method clamp([start] = Infinity, [end] = Infinity)
13178        * @returns Number
13179        * @short Constrains the number so that it falls on or between [start] and
13180        *        [end].
13181        * @extra This will build a range object that has an equivalent `clamp` method.
13182        *
13183        * @example
13184        *
13185        *   (3).clamp(50, 100)  -> 50
13186        *   (85).clamp(50, 100) -> 85
13187        *
13188        * @param {number} [start]
13189        * @param {number} [end]
13190        *
13191        ***/
13192        'clamp': function(n, start, end) {
13193            return rangeClamp(new Range(start, end), n);
13194        },
13195
13196        /***
13197        * @method cap([max] = Infinity)
13198        * @returns Number
13199        * @short Constrains the number so that it is no greater than [max].
13200        * @extra This will build a range object that has an equivalent `cap` method.
13201        *
13202        * @example
13203        *
13204        *   (100).cap(80) -> 80
13205        *
13206        * @param {number} [max]
13207        *
13208        ***/
13209        'cap': function(n, max) {
13210            return rangeClamp(new Range(undefined, max), n);
13211        }
13212
13213    });
13214
13215    /***
13216    * @method downto(num, [step] = 1, [fn])
13217    * @returns Array
13218    * @short Returns an array containing numbers from the number down to `num`.
13219    * @extra Optionally calls [fn] for each number in that array. [step] allows
13220    *        multiples other than 1. [fn] can be passed in place of [step].
13221    *
13222    * @callback rangeEveryFn
13223    *
13224    *   el   The element of the current iteration.
13225    *   i    The index of the current iteration.
13226    *   r    A reference to the range.
13227    *
13228    * @example
13229    *
13230    *   (8).downto(3) -> [8, 7, 6, 5, 4, 3]
13231    *   (8).downto(3, function(n) {
13232    *     // This function is called 6 times receiving n as the value.
13233    *   });
13234    *   (8).downto(2, 2) -> [8, 6, 4, 2]
13235    *
13236    * @signature upto(num, [fn])
13237    * @param {number} num
13238    * @param {number} [step]
13239    * @param {rangeEveryFn} [fn]
13240    * @callbackParam {RangeElement} el
13241    * @callbackParam {number} i
13242    * @callbackParam {Range} r
13243    *
13244    ***/
13245    alias(sugarNumber, 'downto', 'upto');
13246
13247
13248    /*** @namespace String ***/
13249
13250    defineStatic(sugarString, {
13251
13252        /***
13253        * @method range([start], [end])
13254        * @returns Range
13255        * @static
13256        * @short Creates a new string range between [start] and [end]. See `ranges`
13257        *        for more.
13258        *
13259        * @example
13260        *
13261        *   String.range('a', 'z')
13262        *   String.range('t', 'm')
13263        *
13264        * @param {string} [start]
13265        * @param {string} [end]
13266        *
13267        ***/
13268        'range': PrimitiveRangeConstructor
13269
13270    });
13271
13272
13273    /*** @namespace Date ***/
13274
13275
13276    var FULL_CAPTURED_DURATION = '((?:\\d+)?\\s*(?:' + DURATION_UNITS + '))s?';
13277
13278    // Duration text formats
13279    var RANGE_REG_FROM_TO = /(?:from)?\s*(.+)\s+(?:to|until)\s+(.+)$/i,
13280      RANGE_REG_REAR_DURATION = RegExp('(.+)\\s*for\\s*' + FULL_CAPTURED_DURATION, 'i'),
13281      RANGE_REG_FRONT_DURATION = RegExp('(?:for)?\\s*' + FULL_CAPTURED_DURATION + '\\s*(?:starting)?\\s*at\\s*(.+)', 'i');
13282
13283    var DateRangeConstructor = function(start, end) {
13284        if (arguments.length === 1 && isString(start)) {
13285            return createDateRangeFromString(start);
13286        }
13287        return new Range(getDateForRange(start), getDateForRange(end));
13288    };
13289
13290    function createDateRangeFromString(str) {
13291        var match, datetime, duration, dio, start, end;
13292        if (sugarDate.get && (match = str.match(RANGE_REG_FROM_TO))) {
13293            start = getDateForRange(match[1].replace('from', 'at'));
13294            end = sugarDate.get(start, match[2]);
13295            return new Range(start, end);
13296        }
13297        if (match = str.match(RANGE_REG_FRONT_DURATION)) {
13298            duration = match[1];
13299            datetime = match[2];
13300        }
13301        if (match = str.match(RANGE_REG_REAR_DURATION)) {
13302            datetime = match[1];
13303            duration = match[2];
13304        }
13305        if (datetime && duration) {
13306            start = getDateForRange(datetime);
13307            dio = getDateIncrementObject(duration);
13308            end = incrementDate(start, dio[0], dio[1]);
13309        } else {
13310            start = str;
13311        }
13312        return new Range(getDateForRange(start), getDateForRange(end));
13313    }
13314
13315    function getDateForRange(d) {
13316        if (isDate(d)) {
13317            return d;
13318        } else if (d == null) {
13319            return new Date();
13320        } else if (sugarDate.create) {
13321            return sugarDate.create(d);
13322        }
13323        return new Date(d);
13324    }
13325
13326    /***
13327    * @method [dateUnit]()
13328    * @returns Number
13329    * @namespace Range
13330    * @short Returns the span of a date range in the given unit.
13331    * @extra Higher order units ("days" and greater) walk the date to avoid
13332    *        discrepancies with ambiguity. Lower order units simply subtract the
13333    *        start from the end.
13334    *
13335    * @set
13336    *   milliseconds
13337    *   seconds
13338    *   minutes
13339    *   hours
13340    *   days
13341    *   weeks
13342    *   months
13343    *   years
13344    *
13345    * @example
13346    *
13347    *   janToMay.months()  -> 4
13348    *   janToMay.days()    -> 121
13349    *   janToMay.hours()   -> 2904
13350    *   janToMay.minutes() -> 220320
13351    *
13352    ***/
13353    function buildDateRangeUnits() {
13354        var methods = {};
13355        forEach(DURATION_UNITS.split('|'), function(unit, i) {
13356            var name = unit + 's', mult, fn;
13357            if (i < 4) {
13358                fn = function() {
13359                    return rangeEvery(this, unit, true);
13360                };
13361            } else {
13362                mult = MULTIPLIERS[simpleCapitalize(name)];
13363                fn = function() {
13364                    return trunc((this.end - this.start) / mult);
13365                };
13366            }
13367            methods[name] = fn;
13368        });
13369        defineOnPrototype(Range, methods);
13370    }
13371
13372    defineStatic(sugarDate, {
13373
13374        /***
13375        * @method range([start], [end])
13376        * @returns Range
13377        * @namespace Date
13378        * @static
13379        * @short Creates a new date range between [start] and [end].
13380        * @extra Arguments may be either dates or strings which will be forwarded to
13381        *        the date constructor (`create` will be used if present in the build).
13382        *        If either [start] or [end] are undefined, they will default to the
13383        *        current date. This method also accepts an alternate syntax of a
13384        *        single string describing the range in natural language. See `ranges`
13385        *        for more.
13386        *
13387        * @example
13388        *
13389        *   Date.range(jan, may)
13390        *   Date.range('today', 'tomorrow')
13391        *   Date.range('now', '5 days ago')
13392        *   Date.range('last Monday')
13393        *   Date.range('Monday to Friday')
13394        *   Date.range('tomorrow from 3pm to 5pm')
13395        *   Date.range('1 hour starting at 5pm Tuesday')
13396        *
13397        * @param {string|Date} [start]
13398        * @param {string|Date} [end]
13399        *
13400        ***/
13401        'range': DateRangeConstructor
13402
13403    });
13404
13405    buildDateRangeUnits();
13406
13407}).call(this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.