master
Raw Download raw file
   1/*
   2 *  Sugar Library v1.4.1
   3 *
   4 *  Freely distributable and licensed under the MIT-style license.
   5 *  Copyright (c) 2013 Andrew Plummer
   6 *  http://sugarjs.com/
   7 *
   8 * ---------------------------- */
   9(function(){
  10  'use strict';
  11
  12  /***
  13   * @package Core
  14   * @description Internal utility and common methods.
  15   ***/
  16
  17
  18  // A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
  19  var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, math = Math, Undefined;
  20
  21  // The global context
  22  var globalContext = typeof global !== 'undefined' ? global : this;
  23
  24  // Internal toString
  25  var internalToString = object.prototype.toString;
  26
  27  // Internal hasOwnProperty
  28  var internalHasOwnProperty = object.prototype.hasOwnProperty;
  29
  30  // defineProperty exists in IE8 but will error when trying to define a property on
  31  // native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
  32  var definePropertySupport = object.defineProperty && object.defineProperties;
  33
  34  // Are regexes type function?
  35  var regexIsFunction = typeof regexp() === 'function';
  36
  37  // Do strings have no keys?
  38  var noKeysInStringObjects = !('0' in new string('a'));
  39
  40  // Type check methods need a way to be accessed dynamically.
  41  var typeChecks = {};
  42
  43  // Classes that can be matched by value
  44  var matchedByValueReg = /^\[object Date|Array|String|Number|RegExp|Boolean|Arguments\]$/;
  45
  46  // Class initializers and class helpers
  47  var ClassNames = 'Boolean,Number,String,Array,Date,RegExp,Function'.split(',');
  48
  49  var isBoolean  = buildPrimitiveClassCheck('boolean', ClassNames[0]);
  50  var isNumber   = buildPrimitiveClassCheck('number',  ClassNames[1]);
  51  var isString   = buildPrimitiveClassCheck('string',  ClassNames[2]);
  52
  53  var isArray    = buildClassCheck(ClassNames[3]);
  54  var isDate     = buildClassCheck(ClassNames[4]);
  55  var isRegExp   = buildClassCheck(ClassNames[5]);
  56
  57
  58  // Wanted to enhance performance here by using simply "typeof"
  59  // but Firefox has two major issues that make this impossible,
  60  // one fixed, the other not. Despite being typeof "function"
  61  // the objects below still report in as [object Function], so
  62  // we need to perform a full class check here.
  63  //
  64  // 1. Regexes can be typeof "function" in FF < 3
  65  //    https://bugzilla.mozilla.org/show_bug.cgi?id=61911 (fixed)
  66  //
  67  // 2. HTMLEmbedElement and HTMLObjectElement are be typeof "function"
  68  //    https://bugzilla.mozilla.org/show_bug.cgi?id=268945 (won't fix)
  69  //
  70  var isFunction = buildClassCheck(ClassNames[6]);
  71
  72  function isClass(obj, klass, cached) {
  73    var k = cached || className(obj);
  74    return k === '[object '+klass+']';
  75  }
  76
  77  function buildClassCheck(klass) {
  78    var fn = (klass === 'Array' && array.isArray) || function(obj, cached) {
  79      return isClass(obj, klass, cached);
  80    };
  81    typeChecks[klass] = fn;
  82    return fn;
  83  }
  84
  85  function buildPrimitiveClassCheck(type, klass) {
  86    var fn = function(obj) {
  87      if(isObjectType(obj)) {
  88        return isClass(obj, klass);
  89      }
  90      return typeof obj === type;
  91    }
  92    typeChecks[klass] = fn;
  93    return fn;
  94  }
  95
  96  function className(obj) {
  97    return internalToString.call(obj);
  98  }
  99
 100  function initializeClasses() {
 101    initializeClass(object);
 102    iterateOverObject(ClassNames, function(i,name) {
 103      initializeClass(globalContext[name]);
 104    });
 105  }
 106
 107  function initializeClass(klass) {
 108    if(klass['SugarMethods']) return;
 109    defineProperty(klass, 'SugarMethods', {});
 110    extend(klass, false, true, {
 111      'extend': function(methods, override, instance) {
 112        extend(klass, instance !== false, override, methods);
 113      },
 114      'sugarRestore': function() {
 115        return batchMethodExecute(this, klass, arguments, function(target, name, m) {
 116          defineProperty(target, name, m.method);
 117        });
 118      },
 119      'sugarRevert': function() {
 120        return batchMethodExecute(this, klass, arguments, function(target, name, m) {
 121          if(m['existed']) {
 122            defineProperty(target, name, m['original']);
 123          } else {
 124            delete target[name];
 125          }
 126        });
 127      }
 128    });
 129  }
 130
 131  // Class extending methods
 132
 133  function extend(klass, instance, override, methods) {
 134    var extendee = instance ? klass.prototype : klass;
 135    initializeClass(klass);
 136    iterateOverObject(methods, function(name, extendedFn) {
 137      var nativeFn = extendee[name],
 138          existed  = hasOwnProperty(extendee, name);
 139      if(isFunction(override) && nativeFn) {
 140        extendedFn = wrapNative(nativeFn, extendedFn, override);
 141      }
 142      if(override !== false || !nativeFn) {
 143        defineProperty(extendee, name, extendedFn);
 144      }
 145      // If the method is internal to Sugar, then
 146      // store a reference so it can be restored later.
 147      klass['SugarMethods'][name] = {
 148        'method':   extendedFn,
 149        'existed':  existed,
 150        'original': nativeFn,
 151        'instance': instance
 152      };
 153    });
 154  }
 155
 156  function extendSimilar(klass, instance, override, set, fn) {
 157    var methods = {};
 158    set = isString(set) ? set.split(',') : set;
 159    set.forEach(function(name, i) {
 160      fn(methods, name, i);
 161    });
 162    extend(klass, instance, override, methods);
 163  }
 164
 165  function batchMethodExecute(target, klass, args, fn) {
 166    var all = args.length === 0, methods = multiArgs(args), changed = false;
 167    iterateOverObject(klass['SugarMethods'], function(name, m) {
 168      if(all || methods.indexOf(name) !== -1) {
 169        changed = true;
 170        fn(m['instance'] ? target.prototype : target, name, m);
 171      }
 172    });
 173    return changed;
 174  }
 175
 176  function wrapNative(nativeFn, extendedFn, condition) {
 177    return function(a) {
 178      return condition.apply(this, arguments) ?
 179             extendedFn.apply(this, arguments) :
 180             nativeFn.apply(this, arguments);
 181    }
 182  }
 183
 184  function defineProperty(target, name, method) {
 185    if(definePropertySupport) {
 186      object.defineProperty(target, name, {
 187        'value': method,
 188        'configurable': true,
 189        'enumerable': false,
 190        'writable': true
 191      });
 192    } else {
 193      target[name] = method;
 194    }
 195  }
 196
 197
 198  // Argument helpers
 199
 200  function multiArgs(args, fn, from) {
 201    var result = [], i = from || 0, len;
 202    for(len = args.length; i < len; i++) {
 203      result.push(args[i]);
 204      if(fn) fn.call(args, args[i], i);
 205    }
 206    return result;
 207  }
 208
 209  function flattenedArgs(args, fn, from) {
 210    var arg = args[from || 0];
 211    if(isArray(arg)) {
 212      args = arg;
 213      from = 0;
 214    }
 215    return multiArgs(args, fn, from);
 216  }
 217
 218  function checkCallback(fn) {
 219    if(!fn || !fn.call) {
 220      throw new TypeError('Callback is not callable');
 221    }
 222  }
 223
 224
 225  // General helpers
 226
 227  function isDefined(o) {
 228    return o !== Undefined;
 229  }
 230
 231  function isUndefined(o) {
 232    return o === Undefined;
 233  }
 234
 235
 236  // Object helpers
 237
 238  function hasProperty(obj, prop) {
 239    return !isPrimitiveType(obj) && prop in obj;
 240  }
 241
 242  function hasOwnProperty(obj, prop) {
 243    return !!obj && internalHasOwnProperty.call(obj, prop);
 244  }
 245
 246  function isObjectType(obj) {
 247    // 1. Check for null
 248    // 2. Check for regexes in environments where they are "functions".
 249    return !!obj && (typeof obj === 'object' || (regexIsFunction && isRegExp(obj)));
 250  }
 251
 252  function isPrimitiveType(obj) {
 253    var type = typeof obj;
 254    return obj == null || type === 'string' || type === 'number' || type === 'boolean';
 255  }
 256
 257  function isPlainObject(obj, klass) {
 258    klass = klass || className(obj);
 259    try {
 260      // Not own constructor property must be Object
 261      // This code was borrowed from jQuery.isPlainObject
 262      if (obj && obj.constructor &&
 263            !hasOwnProperty(obj, 'constructor') &&
 264            !hasOwnProperty(obj.constructor.prototype, 'isPrototypeOf')) {
 265        return false;
 266      }
 267    } catch (e) {
 268      // IE8,9 Will throw exceptions on certain host objects.
 269      return false;
 270    }
 271    // === on the constructor is not safe across iframes
 272    // 'hasOwnProperty' ensures that the object also inherits
 273    // from Object, which is false for DOMElements in IE.
 274    return !!obj && klass === '[object Object]' && 'hasOwnProperty' in obj;
 275  }
 276
 277  function iterateOverObject(obj, fn) {
 278    var key;
 279    for(key in obj) {
 280      if(!hasOwnProperty(obj, key)) continue;
 281      if(fn.call(obj, key, obj[key], obj) === false) break;
 282    }
 283  }
 284
 285  function simpleRepeat(n, fn) {
 286    for(var i = 0; i < n; i++) {
 287      fn(i);
 288    }
 289  }
 290
 291  function simpleMerge(target, source) {
 292    iterateOverObject(source, function(key) {
 293      target[key] = source[key];
 294    });
 295    return target;
 296  }
 297
 298   // Make primtives types like strings into objects.
 299   function coercePrimitiveToObject(obj) {
 300     if(isPrimitiveType(obj)) {
 301       obj = object(obj);
 302     }
 303     if(noKeysInStringObjects && isString(obj)) {
 304       forceStringCoercion(obj);
 305     }
 306     return obj;
 307   }
 308
 309   // Force strings to have their indexes set in
 310   // environments that don't do this automatically.
 311   function forceStringCoercion(obj) {
 312     var i = 0, chr;
 313     while(chr = obj.charAt(i)) {
 314       obj[i++] = chr;
 315     }
 316   }
 317
 318  // Hash definition
 319
 320  function Hash(obj) {
 321    simpleMerge(this, coercePrimitiveToObject(obj));
 322  };
 323
 324  Hash.prototype.constructor = object;
 325
 326  // Math helpers
 327
 328  var abs   = math.abs;
 329  var pow   = math.pow;
 330  var ceil  = math.ceil;
 331  var floor = math.floor;
 332  var round = math.round;
 333  var min   = math.min;
 334  var max   = math.max;
 335
 336  function withPrecision(val, precision, fn) {
 337    var multiplier = pow(10, abs(precision || 0));
 338    fn = fn || round;
 339    if(precision < 0) multiplier = 1 / multiplier;
 340    return fn(val * multiplier) / multiplier;
 341  }
 342
 343  // Full width number helpers
 344
 345  var HalfWidthZeroCode = 0x30;
 346  var HalfWidthNineCode = 0x39;
 347  var FullWidthZeroCode = 0xff10;
 348  var FullWidthNineCode = 0xff19;
 349
 350  var HalfWidthPeriod = '.';
 351  var FullWidthPeriod = '.';
 352  var HalfWidthComma  = ',';
 353
 354  // Used here and later in the Date package.
 355  var FullWidthDigits   = '';
 356
 357  var NumberNormalizeMap = {};
 358  var NumberNormalizeReg;
 359
 360  function codeIsNumeral(code) {
 361    return (code >= HalfWidthZeroCode && code <= HalfWidthNineCode) ||
 362           (code >= FullWidthZeroCode && code <= FullWidthNineCode);
 363  }
 364
 365  function buildNumberHelpers() {
 366    var digit, i;
 367    for(i = 0; i <= 9; i++) {
 368      digit = chr(i + FullWidthZeroCode);
 369      FullWidthDigits += digit;
 370      NumberNormalizeMap[digit] = chr(i + HalfWidthZeroCode);
 371    }
 372    NumberNormalizeMap[HalfWidthComma] = '';
 373    NumberNormalizeMap[FullWidthPeriod] = HalfWidthPeriod;
 374    // Mapping this to itself to easily be able to easily
 375    // capture it in stringToNumber to detect decimals later.
 376    NumberNormalizeMap[HalfWidthPeriod] = HalfWidthPeriod;
 377    NumberNormalizeReg = regexp('[' + FullWidthDigits + FullWidthPeriod + HalfWidthComma + HalfWidthPeriod + ']', 'g');
 378  }
 379
 380  // String helpers
 381
 382  function chr(num) {
 383    return string.fromCharCode(num);
 384  }
 385
 386  // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
 387  function getTrimmableCharacters() {
 388    return '\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';
 389  }
 390
 391  function repeatString(str, num) {
 392    var result = '', str = str.toString();
 393    while (num > 0) {
 394      if (num & 1) {
 395        result += str;
 396      }
 397      if (num >>= 1) {
 398        str += str;
 399      }
 400    }
 401    return result;
 402  }
 403
 404  // Returns taking into account full-width characters, commas, and decimals.
 405  function stringToNumber(str, base) {
 406    var sanitized, isDecimal;
 407    sanitized = str.replace(NumberNormalizeReg, function(chr) {
 408      var replacement = NumberNormalizeMap[chr];
 409      if(replacement === HalfWidthPeriod) {
 410        isDecimal = true;
 411      }
 412      return replacement;
 413    });
 414    return isDecimal ? parseFloat(sanitized) : parseInt(sanitized, base || 10);
 415  }
 416
 417
 418  // Used by Number and Date
 419
 420  function padNumber(num, place, sign, base) {
 421    var str = abs(num).toString(base || 10);
 422    str = repeatString('0', place - str.replace(/\.\d+/, '').length) + str;
 423    if(sign || num < 0) {
 424      str = (num < 0 ? '-' : '+') + str;
 425    }
 426    return str;
 427  }
 428
 429  function getOrdinalizedSuffix(num) {
 430    if(num >= 11 && num <= 13) {
 431      return 'th';
 432    } else {
 433      switch(num % 10) {
 434        case 1:  return 'st';
 435        case 2:  return 'nd';
 436        case 3:  return 'rd';
 437        default: return 'th';
 438      }
 439    }
 440  }
 441
 442
 443  // RegExp helpers
 444
 445  function getRegExpFlags(reg, add) {
 446    var flags = '';
 447    add = add || '';
 448    function checkFlag(prop, flag) {
 449      if(prop || add.indexOf(flag) > -1) {
 450        flags += flag;
 451      }
 452    }
 453    checkFlag(reg.multiline, 'm');
 454    checkFlag(reg.ignoreCase, 'i');
 455    checkFlag(reg.global, 'g');
 456    checkFlag(reg.sticky, 'y');
 457    return flags;
 458  }
 459
 460  function escapeRegExp(str) {
 461    if(!isString(str)) str = string(str);
 462    return str.replace(/([\\/\'*+?|()\[\]{}.^$])/g,'\\$1');
 463  }
 464
 465
 466  // Date helpers
 467
 468  function callDateGet(d, method) {
 469    return d['get' + (d._utc ? 'UTC' : '') + method]();
 470  }
 471
 472  function callDateSet(d, method, value) {
 473    return d['set' + (d._utc && method != 'ISOWeek' ? 'UTC' : '') + method](value);
 474  }
 475
 476  // Used by Array#unique and Object.equal
 477
 478  function stringify(thing, stack) {
 479    var type = typeof thing,
 480        thingIsObject,
 481        thingIsArray,
 482        klass, value,
 483        arr, key, i, len;
 484
 485    // Return quickly if string to save cycles
 486    if(type === 'string') return thing;
 487
 488    klass         = internalToString.call(thing)
 489    thingIsObject = isPlainObject(thing, klass);
 490    thingIsArray  = isArray(thing, klass);
 491
 492    if(thing != null && thingIsObject || thingIsArray) {
 493      // This method for checking for cyclic structures was egregiously stolen from
 494      // the ingenious method by @kitcambridge from the Underscore script:
 495      // https://github.com/documentcloud/underscore/issues/240
 496      if(!stack) stack = [];
 497      // Allowing a step into the structure before triggering this
 498      // script to save cycles on standard JSON structures and also to
 499      // try as hard as possible to catch basic properties that may have
 500      // been modified.
 501      if(stack.length > 1) {
 502        i = stack.length;
 503        while (i--) {
 504          if (stack[i] === thing) {
 505            return 'CYC';
 506          }
 507        }
 508      }
 509      stack.push(thing);
 510      value = thing.valueOf() + string(thing.constructor);
 511      arr = thingIsArray ? thing : object.keys(thing).sort();
 512      for(i = 0, len = arr.length; i < len; i++) {
 513        key = thingIsArray ? i : arr[i];
 514        value += key + stringify(thing[key], stack);
 515      }
 516      stack.pop();
 517    } else if(1 / thing === -Infinity) {
 518      value = '-0';
 519    } else {
 520      value = string(thing && thing.valueOf ? thing.valueOf() : thing);
 521    }
 522    return type + klass + value;
 523  }
 524
 525  function isEqual(a, b) {
 526    if(a === b) {
 527      // Return quickly up front when matching by reference,
 528      // but be careful about 0 !== -0.
 529      return a !== 0 || 1 / a === 1 / b;
 530    } else if(objectIsMatchedByValue(a) && objectIsMatchedByValue(b)) {
 531      return stringify(a) === stringify(b);
 532    }
 533    return false;
 534  }
 535
 536  function objectIsMatchedByValue(obj) {
 537    // Only known objects are matched by value. This is notably excluding functions, DOM Elements, and instances of
 538    // user-created classes. The latter can arguably be matched by value, but distinguishing between these and
 539    // host objects -- which should never be compared by value -- is very tricky so not dealing with it here.
 540    var klass = className(obj);
 541    return matchedByValueReg.test(klass) || isPlainObject(obj, klass);
 542  }
 543
 544
 545  // Used by Array#at and String#at
 546
 547  function getEntriesForIndexes(obj, args, isString) {
 548    var result,
 549        length    = obj.length,
 550        argsLen   = args.length,
 551        overshoot = args[argsLen - 1] !== false,
 552        multiple  = argsLen > (overshoot ? 1 : 2);
 553    if(!multiple) {
 554      return entryAtIndex(obj, length, args[0], overshoot, isString);
 555    }
 556    result = [];
 557    multiArgs(args, function(index) {
 558      if(isBoolean(index)) return false;
 559      result.push(entryAtIndex(obj, length, index, overshoot, isString));
 560    });
 561    return result;
 562  }
 563
 564  function entryAtIndex(obj, length, index, overshoot, isString) {
 565    if(overshoot) {
 566      index = index % length;
 567      if(index < 0) index = length + index;
 568    }
 569    return isString ? obj.charAt(index) : obj[index];
 570  }
 571
 572
 573  // Object class methods implemented as instance methods
 574
 575  function buildObjectInstanceMethods(set, target) {
 576    extendSimilar(target, true, false, set, function(methods, name) {
 577      methods[name + (name === 'equal' ? 's' : '')] = function() {
 578        return object[name].apply(null, [this].concat(multiArgs(arguments)));
 579      }
 580    });
 581  }
 582
 583  initializeClasses();
 584  buildNumberHelpers();
 585
 586
 587  /***
 588   * @package ES5
 589   * @description Shim methods that provide ES5 compatible functionality. This package can be excluded if you do not require legacy browser support (IE8 and below).
 590   *
 591   ***/
 592
 593
 594  /***
 595   * Object module
 596   *
 597   ***/
 598
 599  extend(object, false, false, {
 600
 601    'keys': function(obj) {
 602      var keys = [];
 603      if(!isObjectType(obj) && !isRegExp(obj) && !isFunction(obj)) {
 604        throw new TypeError('Object required');
 605      }
 606      iterateOverObject(obj, function(key, value) {
 607        keys.push(key);
 608      });
 609      return keys;
 610    }
 611
 612  });
 613
 614
 615  /***
 616   * Array module
 617   *
 618   ***/
 619
 620  // ECMA5 methods
 621
 622  function arrayIndexOf(arr, search, fromIndex, increment) {
 623    var length = arr.length,
 624        fromRight = increment == -1,
 625        start = fromRight ? length - 1 : 0,
 626        index = toIntegerWithDefault(fromIndex, start);
 627    if(index < 0) {
 628      index = length + index;
 629    }
 630    if((!fromRight && index < 0) || (fromRight && index >= length)) {
 631      index = start;
 632    }
 633    while((fromRight && index >= 0) || (!fromRight && index < length)) {
 634      if(arr[index] === search) {
 635        return index;
 636      }
 637      index += increment;
 638    }
 639    return -1;
 640  }
 641
 642  function arrayReduce(arr, fn, initialValue, fromRight) {
 643    var length = arr.length, count = 0, defined = isDefined(initialValue), result, index;
 644    checkCallback(fn);
 645    if(length == 0 && !defined) {
 646      throw new TypeError('Reduce called on empty array with no initial value');
 647    } else if(defined) {
 648      result = initialValue;
 649    } else {
 650      result = arr[fromRight ? length - 1 : count];
 651      count++;
 652    }
 653    while(count < length) {
 654      index = fromRight ? length - count - 1 : count;
 655      if(index in arr) {
 656        result = fn(result, arr[index], index, arr);
 657      }
 658      count++;
 659    }
 660    return result;
 661  }
 662
 663  function toIntegerWithDefault(i, d) {
 664    if(isNaN(i)) {
 665      return d;
 666    } else {
 667      return parseInt(i >> 0);
 668    }
 669  }
 670
 671  function checkFirstArgumentExists(args) {
 672    if(args.length === 0) {
 673      throw new TypeError('First argument must be defined');
 674    }
 675  }
 676
 677
 678
 679
 680  extend(array, false, false, {
 681
 682    /***
 683     *
 684     * @method Array.isArray(<obj>)
 685     * @returns Boolean
 686     * @short Returns true if <obj> is an Array.
 687     * @extra This method is provided for browsers that don't support it internally.
 688     * @example
 689     *
 690     *   Array.isArray(3)        -> false
 691     *   Array.isArray(true)     -> false
 692     *   Array.isArray('wasabi') -> false
 693     *   Array.isArray([1,2,3])  -> true
 694     *
 695     ***/
 696    'isArray': function(obj) {
 697      return isArray(obj);
 698    }
 699
 700  });
 701
 702
 703  extend(array, true, false, {
 704
 705    /***
 706     * @method every(<f>, [scope])
 707     * @returns Boolean
 708     * @short Returns true if all elements in the array match <f>.
 709     * @extra [scope] is the %this% object. %all% is provided an alias. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
 710     * @example
 711     *
 712     +   ['a','a','a'].every(function(n) {
 713     *     return n == 'a';
 714     *   });
 715     *   ['a','a','a'].every('a')   -> true
 716     *   [{a:2},{a:2}].every({a:2}) -> true
 717     ***/
 718    'every': function(fn, scope) {
 719      var length = this.length, index = 0;
 720      checkFirstArgumentExists(arguments);
 721      while(index < length) {
 722        if(index in this && !fn.call(scope, this[index], index, this)) {
 723          return false;
 724        }
 725        index++;
 726      }
 727      return true;
 728    },
 729
 730    /***
 731     * @method some(<f>, [scope])
 732     * @returns Boolean
 733     * @short Returns true if any element in the array matches <f>.
 734     * @extra [scope] is the %this% object. %any% is provided as an alias. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
 735     * @example
 736     *
 737     +   ['a','b','c'].some(function(n) {
 738     *     return n == 'a';
 739     *   });
 740     +   ['a','b','c'].some(function(n) {
 741     *     return n == 'd';
 742     *   });
 743     *   ['a','b','c'].some('a')   -> true
 744     *   [{a:2},{b:5}].some({a:2}) -> true
 745     ***/
 746    'some': function(fn, scope) {
 747      var length = this.length, index = 0;
 748      checkFirstArgumentExists(arguments);
 749      while(index < length) {
 750        if(index in this && fn.call(scope, this[index], index, this)) {
 751          return true;
 752        }
 753        index++;
 754      }
 755      return false;
 756    },
 757
 758    /***
 759     * @method map(<map>, [scope])
 760     * @returns Array
 761     * @short Maps the array to another array containing the values that are the result of calling <map> on each element.
 762     * @extra [scope] is the %this% object. When <map> is a function, it receives three arguments: the current element, the current index, and a reference to the array. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts a string, which is a shortcut for a function that gets that property (or invokes a function) on each element.
 763     * @example
 764     *
 765     *   [1,2,3].map(function(n) {
 766     *     return n * 3;
 767     *   });                                  -> [3,6,9]
 768     *   ['one','two','three'].map(function(n) {
 769     *     return n.length;
 770     *   });                                  -> [3,3,5]
 771     *   ['one','two','three'].map('length')  -> [3,3,5]
 772     *
 773     ***/
 774    'map': function(fn, scope) {
 775      var scope = arguments[1], length = this.length, index = 0, result = new Array(length);
 776      checkFirstArgumentExists(arguments);
 777      while(index < length) {
 778        if(index in this) {
 779          result[index] = fn.call(scope, this[index], index, this);
 780        }
 781        index++;
 782      }
 783      return result;
 784    },
 785
 786    /***
 787     * @method filter(<f>, [scope])
 788     * @returns Array
 789     * @short Returns any elements in the array that match <f>.
 790     * @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
 791     * @example
 792     *
 793     +   [1,2,3].filter(function(n) {
 794     *     return n > 1;
 795     *   });
 796     *   [1,2,2,4].filter(2) -> 2
 797     *
 798     ***/
 799    'filter': function(fn) {
 800      var scope = arguments[1];
 801      var length = this.length, index = 0, result = [];
 802      checkFirstArgumentExists(arguments);
 803      while(index < length) {
 804        if(index in this && fn.call(scope, this[index], index, this)) {
 805          result.push(this[index]);
 806        }
 807        index++;
 808      }
 809      return result;
 810    },
 811
 812    /***
 813     * @method indexOf(<search>, [fromIndex])
 814     * @returns Number
 815     * @short Searches the array and returns the first index where <search> occurs, or -1 if the element is not found.
 816     * @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>. It does not support enhanced functionality such as searching the contents against a regex, callback, or deep comparison of objects. For such functionality, use the %findIndex% method instead.
 817     * @example
 818     *
 819     *   [1,2,3].indexOf(3)           -> 1
 820     *   [1,2,3].indexOf(7)           -> -1
 821     *
 822     ***/
 823    'indexOf': function(search) {
 824      var fromIndex = arguments[1];
 825      if(isString(this)) return this.indexOf(search, fromIndex);
 826      return arrayIndexOf(this, search, fromIndex, 1);
 827    },
 828
 829    /***
 830     * @method lastIndexOf(<search>, [fromIndex])
 831     * @returns Number
 832     * @short Searches the array and returns the last index where <search> occurs, or -1 if the element is not found.
 833     * @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>.
 834     * @example
 835     *
 836     *   [1,2,1].lastIndexOf(1)                 -> 2
 837     *   [1,2,1].lastIndexOf(7)                 -> -1
 838     *
 839     ***/
 840    'lastIndexOf': function(search) {
 841      var fromIndex = arguments[1];
 842      if(isString(this)) return this.lastIndexOf(search, fromIndex);
 843      return arrayIndexOf(this, search, fromIndex, -1);
 844    },
 845
 846    /***
 847     * @method forEach([fn], [scope])
 848     * @returns Nothing
 849     * @short Iterates over the array, calling [fn] on each loop.
 850     * @extra This method is only provided for those browsers that do not support it natively. [scope] becomes the %this% object.
 851     * @example
 852     *
 853     *   ['a','b','c'].forEach(function(a) {
 854     *     // Called 3 times: 'a','b','c'
 855     *   });
 856     *
 857     ***/
 858    'forEach': function(fn) {
 859      var length = this.length, index = 0, scope = arguments[1];
 860      checkCallback(fn);
 861      while(index < length) {
 862        if(index in this) {
 863          fn.call(scope, this[index], index, this);
 864        }
 865        index++;
 866      }
 867    },
 868
 869    /***
 870     * @method reduce(<fn>, [init])
 871     * @returns Mixed
 872     * @short Reduces the array to a single result.
 873     * @extra If [init] is passed as a starting value, that value will be passed as the first argument to the callback. The second argument will be the first element in the array. From that point, the result of the callback will then be used as the first argument of the next iteration. This is often refered to as "accumulation", and [init] is often called an "accumulator". If [init] is not passed, then <fn> will be called n - 1 times, where n is the length of the array. In this case, on the first iteration only, the first argument will be the first element of the array, and the second argument will be the second. After that callbacks work as normal, using the result of the previous callback as the first argument of the next. This method is only provided for those browsers that do not support it natively.
 874     *
 875     * @example
 876     *
 877     +   [1,2,3,4].reduce(function(a, b) {
 878     *     return a - b;
 879     *   });
 880     +   [1,2,3,4].reduce(function(a, b) {
 881     *     return a - b;
 882     *   }, 100);
 883     *
 884     ***/
 885    'reduce': function(fn) {
 886      return arrayReduce(this, fn, arguments[1]);
 887    },
 888
 889    /***
 890     * @method reduceRight([fn], [init])
 891     * @returns Mixed
 892     * @short Identical to %Array#reduce%, but operates on the elements in reverse order.
 893     * @extra This method is only provided for those browsers that do not support it natively.
 894     *
 895     *
 896     *
 897     *
 898     * @example
 899     *
 900     +   [1,2,3,4].reduceRight(function(a, b) {
 901     *     return a - b;
 902     *   });
 903     *
 904     ***/
 905    'reduceRight': function(fn) {
 906      return arrayReduce(this, fn, arguments[1], true);
 907    }
 908
 909
 910  });
 911
 912
 913
 914
 915  /***
 916   * String module
 917   *
 918   ***/
 919
 920
 921  function buildTrim() {
 922    var support = getTrimmableCharacters().match(/^\s+$/);
 923    try { string.prototype.trim.call([1]); } catch(e) { support = false; }
 924    extend(string, true, !support, {
 925
 926      /***
 927       * @method trim[Side]()
 928       * @returns String
 929       * @short Removes leading and/or trailing whitespace from the string.
 930       * @extra Whitespace is defined as line breaks, tabs, and any character in the "Space, Separator" Unicode category, conforming to the the ES5 spec. The standard %trim% method is only added when not fully supported natively.
 931       *
 932       * @set
 933       *   trim
 934       *   trimLeft
 935       *   trimRight
 936       *
 937       * @example
 938       *
 939       *   '   wasabi   '.trim()      -> 'wasabi'
 940       *   '   wasabi   '.trimLeft()  -> 'wasabi   '
 941       *   '   wasabi   '.trimRight() -> '   wasabi'
 942       *
 943       ***/
 944      'trim': function() {
 945        return this.toString().trimLeft().trimRight();
 946      },
 947
 948      'trimLeft': function() {
 949        return this.replace(regexp('^['+getTrimmableCharacters()+']+'), '');
 950      },
 951
 952      'trimRight': function() {
 953        return this.replace(regexp('['+getTrimmableCharacters()+']+$'), '');
 954      }
 955    });
 956  }
 957
 958
 959
 960  /***
 961   * Function module
 962   *
 963   ***/
 964
 965
 966  extend(Function, true, false, {
 967
 968     /***
 969     * @method bind(<scope>, [arg1], ...)
 970     * @returns Function
 971     * @short Binds <scope> as the %this% object for the function when it is called. Also allows currying an unlimited number of parameters.
 972     * @extra "currying" means setting parameters ([arg1], [arg2], etc.) ahead of time so that they are passed when the function is called later. If you pass additional parameters when the function is actually called, they will be added will be added to the end of the curried parameters. This method is provided for browsers that don't support it internally.
 973     * @example
 974     *
 975     +   (function() {
 976     *     return this;
 977     *   }).bind('woof')(); -> returns 'woof'; function is bound with 'woof' as the this object.
 978     *   (function(a) {
 979     *     return a;
 980     *   }).bind(1, 2)();   -> returns 2; function is bound with 1 as the this object and 2 curried as the first parameter
 981     *   (function(a, b) {
 982     *     return a + b;
 983     *   }).bind(1, 2)(3);  -> returns 5; function is bound with 1 as the this object, 2 curied as the first parameter and 3 passed as the second when calling the function
 984     *
 985     ***/
 986    'bind': function(scope) {
 987      var fn = this, args = multiArgs(arguments, null, 1), bound;
 988      if(!isFunction(this)) {
 989        throw new TypeError('Function.prototype.bind called on a non-function');
 990      }
 991      bound = function() {
 992        return fn.apply(fn.prototype && this instanceof fn ? this : scope, args.concat(multiArgs(arguments)));
 993      }
 994      bound.prototype = this.prototype;
 995      return bound;
 996    }
 997
 998  });
 999
1000  /***
1001   * Date module
1002   *
1003   ***/
1004
1005   /***
1006   * @method toISOString()
1007   * @returns String
1008   * @short Formats the string to ISO8601 format.
1009   * @extra This will always format as UTC time. Provided for browsers that do not support this method.
1010   * @example
1011   *
1012   *   Date.create().toISOString() -> ex. 2011-07-05 12:24:55.528Z
1013   *
1014   ***
1015   * @method toJSON()
1016   * @returns String
1017   * @short Returns a JSON representation of the date.
1018   * @extra This is effectively an alias for %toISOString%. Will always return the date in UTC time. Provided for browsers that do not support this method.
1019   * @example
1020   *
1021   *   Date.create().toJSON() -> ex. 2011-07-05 12:24:55.528Z
1022   *
1023   ***/
1024
1025  extend(date, false, false, {
1026
1027     /***
1028     * @method Date.now()
1029     * @returns String
1030     * @short Returns the number of milliseconds since January 1st, 1970 00:00:00 (UTC time).
1031     * @extra Provided for browsers that do not support this method.
1032     * @example
1033     *
1034     *   Date.now() -> ex. 1311938296231
1035     *
1036     ***/
1037    'now': function() {
1038      return new date().getTime();
1039    }
1040
1041  });
1042
1043   function buildISOString() {
1044    var d = new date(date.UTC(1999, 11, 31)), target = '1999-12-31T00:00:00.000Z';
1045    var support = d.toISOString && d.toISOString() === target;
1046    extendSimilar(date, true, !support, 'toISOString,toJSON', function(methods, name) {
1047      methods[name] = function() {
1048        return padNumber(this.getUTCFullYear(), 4) + '-' +
1049               padNumber(this.getUTCMonth() + 1, 2) + '-' +
1050               padNumber(this.getUTCDate(), 2) + 'T' +
1051               padNumber(this.getUTCHours(), 2) + ':' +
1052               padNumber(this.getUTCMinutes(), 2) + ':' +
1053               padNumber(this.getUTCSeconds(), 2) + '.' +
1054               padNumber(this.getUTCMilliseconds(), 3) + 'Z';
1055      }
1056    });
1057   }
1058
1059  // Initialize
1060  buildTrim();
1061  buildISOString();
1062
1063
1064  /***
1065   * @package Array
1066   * @dependency core
1067   * @description Array manipulation and traversal, "fuzzy matching" against elements, alphanumeric sorting and collation, enumerable methods on Object.
1068   *
1069   ***/
1070
1071
1072  function regexMatcher(reg) {
1073    reg = regexp(reg);
1074    return function (el) {
1075      return reg.test(el);
1076    }
1077  }
1078
1079  function dateMatcher(d) {
1080    var ms = d.getTime();
1081    return function (el) {
1082      return !!(el && el.getTime) && el.getTime() === ms;
1083    }
1084  }
1085
1086  function functionMatcher(fn) {
1087    return function (el, i, arr) {
1088      // Return true up front if match by reference
1089      return el === fn || fn.call(this, el, i, arr);
1090    }
1091  }
1092
1093  function invertedArgsFunctionMatcher(fn) {
1094    return function (value, key, obj) {
1095      // Return true up front if match by reference
1096      return value === fn || fn.call(obj, key, value, obj);
1097    }
1098  }
1099
1100  function fuzzyMatcher(obj, isObject) {
1101    var matchers = {};
1102    return function (el, i, arr) {
1103      var key;
1104      if(!isObjectType(el)) {
1105        return false;
1106      }
1107      for(key in obj) {
1108        matchers[key] = matchers[key] || getMatcher(obj[key], isObject);
1109        if(matchers[key].call(arr, el[key], i, arr) === false) {
1110          return false;
1111        }
1112      }
1113      return true;
1114    }
1115  }
1116
1117  function defaultMatcher(f) {
1118    return function (el) {
1119      return el === f || isEqual(el, f);
1120    }
1121  }
1122
1123  function getMatcher(f, isObject) {
1124    if(isPrimitiveType(f)) {
1125      // Do nothing and fall through to the
1126      // default matcher below.
1127    } else if(isRegExp(f)) {
1128      // Match against a regexp
1129      return regexMatcher(f);
1130    } else if(isDate(f)) {
1131      // Match against a date. isEqual below should also
1132      // catch this but matching directly up front for speed.
1133      return dateMatcher(f);
1134    } else if(isFunction(f)) {
1135      // Match against a filtering function
1136      if(isObject) {
1137        return invertedArgsFunctionMatcher(f);
1138      } else {
1139        return functionMatcher(f);
1140      }
1141    } else if(isPlainObject(f)) {
1142      // Match against a fuzzy hash or array.
1143      return fuzzyMatcher(f, isObject);
1144    }
1145    // Default is standard isEqual
1146    return defaultMatcher(f);
1147  }
1148
1149  function transformArgument(el, map, context, mapArgs) {
1150    if(!map) {
1151      return el;
1152    } else if(map.apply) {
1153      return map.apply(context, mapArgs || []);
1154    } else if(isFunction(el[map])) {
1155      return el[map].call(el);
1156    } else {
1157      return el[map];
1158    }
1159  }
1160
1161  // Basic array internal methods
1162
1163  function arrayEach(arr, fn, startIndex, loop) {
1164    var index, i, length = +arr.length;
1165    if(startIndex < 0) startIndex = arr.length + startIndex;
1166    i = isNaN(startIndex) ? 0 : startIndex;
1167    if(loop === true) {
1168      length += i;
1169    }
1170    while(i < length) {
1171      index = i % arr.length;
1172      if(!(index in arr)) {
1173        return iterateOverSparseArray(arr, fn, i, loop);
1174      } else if(fn.call(arr, arr[index], index, arr) === false) {
1175        break;
1176      }
1177      i++;
1178    }
1179  }
1180
1181  function iterateOverSparseArray(arr, fn, fromIndex, loop) {
1182    var indexes = [], i;
1183    for(i in arr) {
1184      if(isArrayIndex(arr, i) && i >= fromIndex) {
1185        indexes.push(parseInt(i));
1186      }
1187    }
1188    indexes.sort().each(function(index) {
1189      return fn.call(arr, arr[index], index, arr);
1190    });
1191    return arr;
1192  }
1193
1194  function isArrayIndex(arr, i) {
1195    return i in arr && toUInt32(i) == i && i != 0xffffffff;
1196  }
1197
1198  function toUInt32(i) {
1199    return i >>> 0;
1200  }
1201
1202  function arrayFind(arr, f, startIndex, loop, returnIndex, context) {
1203    var result, index, matcher;
1204    if(arr.length > 0) {
1205      matcher = getMatcher(f);
1206      arrayEach(arr, function(el, i) {
1207        if(matcher.call(context, el, i, arr)) {
1208          result = el;
1209          index = i;
1210          return false;
1211        }
1212      }, startIndex, loop);
1213    }
1214    return returnIndex ? index : result;
1215  }
1216
1217  function arrayUnique(arr, map) {
1218    var result = [], o = {}, transformed;
1219    arrayEach(arr, function(el, i) {
1220      transformed = map ? transformArgument(el, map, arr, [el, i, arr]) : el;
1221      if(!checkForElementInHashAndSet(o, transformed)) {
1222        result.push(el);
1223      }
1224    })
1225    return result;
1226  }
1227
1228  function arrayIntersect(arr1, arr2, subtract) {
1229    var result = [], o = {};
1230    arr2.each(function(el) {
1231      checkForElementInHashAndSet(o, el);
1232    });
1233    arr1.each(function(el) {
1234      var stringified = stringify(el),
1235          isReference = !objectIsMatchedByValue(el);
1236      // Add the result to the array if:
1237      // 1. We're subtracting intersections or it doesn't already exist in the result and
1238      // 2. It exists in the compared array and we're adding, or it doesn't exist and we're removing.
1239      if(elementExistsInHash(o, stringified, el, isReference) !== subtract) {
1240        discardElementFromHash(o, stringified, el, isReference);
1241        result.push(el);
1242      }
1243    });
1244    return result;
1245  }
1246
1247  function arrayFlatten(arr, level, current) {
1248    level = level || Infinity;
1249    current = current || 0;
1250    var result = [];
1251    arrayEach(arr, function(el) {
1252      if(isArray(el) && current < level) {
1253        result = result.concat(arrayFlatten(el, level, current + 1));
1254      } else {
1255        result.push(el);
1256      }
1257    });
1258    return result;
1259  }
1260
1261  function isArrayLike(obj) {
1262    return hasProperty(obj, 'length') && !isString(obj) && !isPlainObject(obj);
1263  }
1264
1265  function isArgumentsObject(obj) {
1266    // .callee exists on Arguments objects in < IE8
1267    return hasProperty(obj, 'length') && (className(obj) === '[object Arguments]' || !!obj.callee);
1268  }
1269
1270  function flatArguments(args) {
1271    var result = [];
1272    multiArgs(args, function(arg) {
1273      result = result.concat(arg);
1274    });
1275    return result;
1276  }
1277
1278  function elementExistsInHash(hash, key, element, isReference) {
1279    var exists = key in hash;
1280    if(isReference) {
1281      if(!hash[key]) {
1282        hash[key] = [];
1283      }
1284      exists = hash[key].indexOf(element) !== -1;
1285    }
1286    return exists;
1287  }
1288
1289  function checkForElementInHashAndSet(hash, element) {
1290    var stringified = stringify(element),
1291        isReference = !objectIsMatchedByValue(element),
1292        exists      = elementExistsInHash(hash, stringified, element, isReference);
1293    if(isReference) {
1294      hash[stringified].push(element);
1295    } else {
1296      hash[stringified] = element;
1297    }
1298    return exists;
1299  }
1300
1301  function discardElementFromHash(hash, key, element, isReference) {
1302    var arr, i = 0;
1303    if(isReference) {
1304      arr = hash[key];
1305      while(i < arr.length) {
1306        if(arr[i] === element) {
1307          arr.splice(i, 1);
1308        } else {
1309          i += 1;
1310        }
1311      }
1312    } else {
1313      delete hash[key];
1314    }
1315  }
1316
1317  // Support methods
1318
1319  function getMinOrMax(obj, map, which, all) {
1320    var el,
1321        key,
1322        edge,
1323        test,
1324        result = [],
1325        max = which === 'max',
1326        min = which === 'min',
1327        isArray = array.isArray(obj);
1328    for(key in obj) {
1329      if(!obj.hasOwnProperty(key)) continue;
1330      el   = obj[key];
1331      test = transformArgument(el, map, obj, isArray ? [el, parseInt(key), obj] : []);
1332      if(isUndefined(test)) {
1333        throw new TypeError('Cannot compare with undefined');
1334      }
1335      if(test === edge) {
1336        result.push(el);
1337      } else if(isUndefined(edge) || (max && test > edge) || (min && test < edge)) {
1338        result = [el];
1339        edge = test;
1340      }
1341    }
1342    if(!isArray) result = arrayFlatten(result, 1);
1343    return all ? result : result[0];
1344  }
1345
1346
1347  // Alphanumeric collation helpers
1348
1349  function collateStrings(a, b) {
1350    var aValue, bValue, aChar, bChar, aEquiv, bEquiv, index = 0, tiebreaker = 0;
1351
1352    var sortIgnore      = array[AlphanumericSortIgnore];
1353    var sortIgnoreCase  = array[AlphanumericSortIgnoreCase];
1354    var sortEquivalents = array[AlphanumericSortEquivalents];
1355    var sortOrder       = array[AlphanumericSortOrder];
1356    var naturalSort     = array[AlphanumericSortNatural];
1357
1358    a = getCollationReadyString(a, sortIgnore, sortIgnoreCase);
1359    b = getCollationReadyString(b, sortIgnore, sortIgnoreCase);
1360
1361    do {
1362
1363      aChar  = getCollationCharacter(a, index, sortEquivalents);
1364      bChar  = getCollationCharacter(b, index, sortEquivalents);
1365      aValue = getSortOrderIndex(aChar, sortOrder);
1366      bValue = getSortOrderIndex(bChar, sortOrder);
1367
1368      if(aValue === -1 || bValue === -1) {
1369        aValue = a.charCodeAt(index) || null;
1370        bValue = b.charCodeAt(index) || null;
1371        if(naturalSort && codeIsNumeral(aValue) && codeIsNumeral(bValue)) {
1372          aValue = stringToNumber(a.slice(index));
1373          bValue = stringToNumber(b.slice(index));
1374        }
1375      } else {
1376        aEquiv = aChar !== a.charAt(index);
1377        bEquiv = bChar !== b.charAt(index);
1378        if(aEquiv !== bEquiv && tiebreaker === 0) {
1379          tiebreaker = aEquiv - bEquiv;
1380        }
1381      }
1382      index += 1;
1383    } while(aValue != null && bValue != null && aValue === bValue);
1384    if(aValue === bValue) return tiebreaker;
1385    return aValue - bValue;
1386  }
1387
1388  function getCollationReadyString(str, sortIgnore, sortIgnoreCase) {
1389    if(!isString(str)) str = string(str);
1390    if(sortIgnoreCase) {
1391      str = str.toLowerCase();
1392    }
1393    if(sortIgnore) {
1394      str = str.replace(sortIgnore, '');
1395    }
1396    return str;
1397  }
1398
1399  function getCollationCharacter(str, index, sortEquivalents) {
1400    var chr = str.charAt(index);
1401    return sortEquivalents[chr] || chr;
1402  }
1403
1404  function getSortOrderIndex(chr, sortOrder) {
1405    if(!chr) {
1406      return null;
1407    } else {
1408      return sortOrder.indexOf(chr);
1409    }
1410  }
1411
1412  var AlphanumericSort            = 'AlphanumericSort';
1413  var AlphanumericSortOrder       = 'AlphanumericSortOrder';
1414  var AlphanumericSortIgnore      = 'AlphanumericSortIgnore';
1415  var AlphanumericSortIgnoreCase  = 'AlphanumericSortIgnoreCase';
1416  var AlphanumericSortEquivalents = 'AlphanumericSortEquivalents';
1417  var AlphanumericSortNatural     = 'AlphanumericSortNatural';
1418
1419
1420
1421  function buildEnhancements() {
1422    var nativeMap = array.prototype.map;
1423    var callbackCheck = function() {
1424      var args = arguments;
1425      return args.length > 0 && !isFunction(args[0]);
1426    };
1427    extendSimilar(array, true, callbackCheck, 'every,all,some,filter,any,none,find,findIndex', function(methods, name) {
1428      var nativeFn = array.prototype[name]
1429      methods[name] = function(f) {
1430        var matcher = getMatcher(f);
1431        return nativeFn.call(this, function(el, index) {
1432          return matcher(el, index, this);
1433        });
1434      }
1435    });
1436    extend(array, true, callbackCheck, {
1437      'map': function(f) {
1438        return nativeMap.call(this, function(el, index) {
1439          return transformArgument(el, f, this, [el, index, this]);
1440        });
1441      }
1442    });
1443  }
1444
1445  function buildAlphanumericSort() {
1446    var order = 'AÁÀÂÃĄBCĆČÇDĎÐEÉÈĚÊËĘFGĞHıIÍÌİÎÏJKLŁMNŃŇÑOÓÒÔPQRŘSŚŠŞTŤUÚÙŮÛÜVWXYÝZŹŻŽÞÆŒØÕÅÄÖ';
1447    var equiv = 'AÁÀÂÃÄ,CÇ,EÉÈÊË,IÍÌİÎÏ,OÓÒÔÕÖ,Sß,UÚÙÛÜ';
1448    array[AlphanumericSortOrder] = order.split('').map(function(str) {
1449      return str + str.toLowerCase();
1450    }).join('');
1451    var equivalents = {};
1452    arrayEach(equiv.split(','), function(set) {
1453      var equivalent = set.charAt(0);
1454      arrayEach(set.slice(1).split(''), function(chr) {
1455        equivalents[chr] = equivalent;
1456        equivalents[chr.toLowerCase()] = equivalent.toLowerCase();
1457      });
1458    });
1459    array[AlphanumericSortNatural] = true;
1460    array[AlphanumericSortIgnoreCase] = true;
1461    array[AlphanumericSortEquivalents] = equivalents;
1462  }
1463
1464  extend(array, false, true, {
1465
1466    /***
1467     *
1468     * @method Array.create(<obj1>, <obj2>, ...)
1469     * @returns Array
1470     * @short Alternate array constructor.
1471     * @extra This method will create a single array by calling %concat% on all arguments passed. In addition to ensuring that an unknown variable is in a single, flat array (the standard constructor will create nested arrays, this one will not), it is also a useful shorthand to convert a function's arguments object into a standard array.
1472     * @example
1473     *
1474     *   Array.create('one', true, 3)   -> ['one', true, 3]
1475     *   Array.create(['one', true, 3]) -> ['one', true, 3]
1476     +   Array.create(function(n) {
1477     *     return arguments;
1478     *   }('howdy', 'doody'));
1479     *
1480     ***/
1481    'create': function() {
1482      var result = [];
1483      multiArgs(arguments, function(a) {
1484        if(isArgumentsObject(a) || isArrayLike(a)) {
1485          a = array.prototype.slice.call(a, 0);
1486        }
1487        result = result.concat(a);
1488      });
1489      return result;
1490    }
1491
1492  });
1493
1494  extend(array, true, false, {
1495
1496    /***
1497     * @method find(<f>, [context] = undefined)
1498     * @returns Mixed
1499     * @short Returns the first element that matches <f>.
1500     * @extra [context] is the %this% object if passed. When <f> is a function, will use native implementation if it exists. <f> will also match a string, number, array, object, or alternately test against a function or regex. This method implements @array_matching.
1501     * @example
1502     *
1503     +   [{a:1,b:2},{a:1,b:3},{a:1,b:4}].find(function(n) {
1504     *     return n['a'] == 1;
1505     *   });                                  -> {a:1,b:3}
1506     *   ['cuba','japan','canada'].find(/^c/) -> 'cuba'
1507     *
1508     ***/
1509    'find': function(f, context) {
1510      checkCallback(f);
1511      return arrayFind(this, f, 0, false, false, context);
1512    },
1513
1514    /***
1515     * @method findIndex(<f>, [context] = undefined)
1516     * @returns Number
1517     * @short Returns the index of the first element that matches <f> or -1 if not found.
1518     * @extra [context] is the %this% object if passed. When <f> is a function, will use native implementation if it exists. <f> will also match a string, number, array, object, or alternately test against a function or regex. This method implements @array_matching.
1519     *
1520     * @example
1521     *
1522     +   [1,2,3,4].findIndex(function(n) {
1523     *     return n % 2 == 0;
1524     *   }); -> 1
1525     +   [1,2,3,4].findIndex(3);               -> 2
1526     +   ['one','two','three'].findIndex(/t/); -> 1
1527     *
1528     ***/
1529    'findIndex': function(f, context) {
1530      var index;
1531      checkCallback(f);
1532      index = arrayFind(this, f, 0, false, true, context);
1533      return isUndefined(index) ? -1 : index;
1534    }
1535
1536  });
1537
1538  extend(array, true, true, {
1539
1540    /***
1541     * @method findFrom(<f>, [index] = 0, [loop] = false)
1542     * @returns Array
1543     * @short Returns any element that matches <f>, beginning from [index].
1544     * @extra <f> will match a string, number, array, object, or alternately test against a function or regex. Will continue from index = 0 if [loop] is true. This method implements @array_matching.
1545     * @example
1546     *
1547     *   ['cuba','japan','canada'].findFrom(/^c/, 2) -> 'canada'
1548     *
1549     ***/
1550    'findFrom': function(f, index, loop) {
1551      return arrayFind(this, f, index, loop);
1552    },
1553
1554    /***
1555     * @method findIndexFrom(<f>, [index] = 0, [loop] = false)
1556     * @returns Array
1557     * @short Returns the index of any element that matches <f>, beginning from [index].
1558     * @extra <f> will match a string, number, array, object, or alternately test against a function or regex. Will continue from index = 0 if [loop] is true. This method implements @array_matching.
1559     * @example
1560     *
1561     *   ['cuba','japan','canada'].findIndexFrom(/^c/, 2) -> 2
1562     *
1563     ***/
1564    'findIndexFrom': function(f, index, loop) {
1565      var index = arrayFind(this, f, index, loop, true);
1566      return isUndefined(index) ? -1 : index;
1567    },
1568
1569    /***
1570     * @method findAll(<f>, [index] = 0, [loop] = false)
1571     * @returns Array
1572     * @short Returns all elements that match <f>.
1573     * @extra <f> will match a string, number, array, object, or alternately test against a function or regex. Starts at [index], and will continue once from index = 0 if [loop] is true. This method implements @array_matching.
1574     * @example
1575     *
1576     +   [{a:1,b:2},{a:1,b:3},{a:2,b:4}].findAll(function(n) {
1577     *     return n['a'] == 1;
1578     *   });                                        -> [{a:1,b:3},{a:1,b:4}]
1579     *   ['cuba','japan','canada'].findAll(/^c/)    -> 'cuba','canada'
1580     *   ['cuba','japan','canada'].findAll(/^c/, 2) -> 'canada'
1581     *
1582     ***/
1583    'findAll': function(f, index, loop) {
1584      var result = [], matcher;
1585      if(this.length > 0) {
1586        matcher = getMatcher(f);
1587        arrayEach(this, function(el, i, arr) {
1588          if(matcher(el, i, arr)) {
1589            result.push(el);
1590          }
1591        }, index, loop);
1592      }
1593      return result;
1594    },
1595
1596    /***
1597     * @method count(<f>)
1598     * @returns Number
1599     * @short Counts all elements in the array that match <f>.
1600     * @extra <f> will match a string, number, array, object, or alternately test against a function or regex. This method implements @array_matching.
1601     * @example
1602     *
1603     *   [1,2,3,1].count(1)       -> 2
1604     *   ['a','b','c'].count(/b/) -> 1
1605     +   [{a:1},{b:2}].count(function(n) {
1606     *     return n['a'] > 1;
1607     *   });                      -> 0
1608     *
1609     ***/
1610    'count': function(f) {
1611      if(isUndefined(f)) return this.length;
1612      return this.findAll(f).length;
1613    },
1614
1615    /***
1616     * @method removeAt(<start>, [end])
1617     * @returns Array
1618     * @short Removes element at <start>. If [end] is specified, removes the range between <start> and [end]. This method will change the array! If you don't intend the array to be changed use %clone% first.
1619     * @example
1620     *
1621     *   ['a','b','c'].removeAt(0) -> ['b','c']
1622     *   [1,2,3,4].removeAt(1, 3)  -> [1]
1623     *
1624     ***/
1625    'removeAt': function(start, end) {
1626      if(isUndefined(start)) return this;
1627      if(isUndefined(end))   end = start;
1628      this.splice(start, end - start + 1);
1629      return this;
1630    },
1631
1632    /***
1633     * @method include(<el>, [index])
1634     * @returns Array
1635     * @short Adds <el> to the array.
1636     * @extra This is a non-destructive alias for %add%. It will not change the original array.
1637     * @example
1638     *
1639     *   [1,2,3,4].include(5)       -> [1,2,3,4,5]
1640     *   [1,2,3,4].include(8, 1)    -> [1,8,2,3,4]
1641     *   [1,2,3,4].include([5,6,7]) -> [1,2,3,4,5,6,7]
1642     *
1643     ***/
1644    'include': function(el, index) {
1645      return this.clone().add(el, index);
1646    },
1647
1648    /***
1649     * @method exclude([f1], [f2], ...)
1650     * @returns Array
1651     * @short Removes any element in the array that matches [f1], [f2], etc.
1652     * @extra This is a non-destructive alias for %remove%. It will not change the original array. This method implements @array_matching.
1653     * @example
1654     *
1655     *   [1,2,3].exclude(3)         -> [1,2]
1656     *   ['a','b','c'].exclude(/b/) -> ['a','c']
1657     +   [{a:1},{b:2}].exclude(function(n) {
1658     *     return n['a'] == 1;
1659     *   });                       -> [{b:2}]
1660     *
1661     ***/
1662    'exclude': function() {
1663      return array.prototype.remove.apply(this.clone(), arguments);
1664    },
1665
1666    /***
1667     * @method clone()
1668     * @returns Array
1669     * @short Makes a shallow clone of the array.
1670     * @example
1671     *
1672     *   [1,2,3].clone() -> [1,2,3]
1673     *
1674     ***/
1675    'clone': function() {
1676      return simpleMerge([], this);
1677    },
1678
1679    /***
1680     * @method unique([map] = null)
1681     * @returns Array
1682     * @short Removes all duplicate elements in the array.
1683     * @extra [map] may be a function mapping the value to be uniqued on or a string acting as a shortcut. This is most commonly used when you have a key that ensures the object's uniqueness, and don't need to check all fields. This method will also correctly operate on arrays of objects.
1684     * @example
1685     *
1686     *   [1,2,2,3].unique()                 -> [1,2,3]
1687     *   [{foo:'bar'},{foo:'bar'}].unique() -> [{foo:'bar'}]
1688     +   [{foo:'bar'},{foo:'bar'}].unique(function(obj){
1689     *     return obj.foo;
1690     *   }); -> [{foo:'bar'}]
1691     *   [{foo:'bar'},{foo:'bar'}].unique('foo') -> [{foo:'bar'}]
1692     *
1693     ***/
1694    'unique': function(map) {
1695      return arrayUnique(this, map);
1696    },
1697
1698    /***
1699     * @method flatten([limit] = Infinity)
1700     * @returns Array
1701     * @short Returns a flattened, one-dimensional copy of the array.
1702     * @extra You can optionally specify a [limit], which will only flatten that depth.
1703     * @example
1704     *
1705     *   [[1], 2, [3]].flatten()      -> [1,2,3]
1706     *   [['a'],[],'b','c'].flatten() -> ['a','b','c']
1707     *
1708     ***/
1709    'flatten': function(limit) {
1710      return arrayFlatten(this, limit);
1711    },
1712
1713    /***
1714     * @method union([a1], [a2], ...)
1715     * @returns Array
1716     * @short Returns an array containing all elements in all arrays with duplicates removed.
1717     * @extra This method will also correctly operate on arrays of objects.
1718     * @example
1719     *
1720     *   [1,3,5].union([5,7,9])     -> [1,3,5,7,9]
1721     *   ['a','b'].union(['b','c']) -> ['a','b','c']
1722     *
1723     ***/
1724    'union': function() {
1725      return arrayUnique(this.concat(flatArguments(arguments)));
1726    },
1727
1728    /***
1729     * @method intersect([a1], [a2], ...)
1730     * @returns Array
1731     * @short Returns an array containing the elements all arrays have in common.
1732     * @extra This method will also correctly operate on arrays of objects.
1733     * @example
1734     *
1735     *   [1,3,5].intersect([5,7,9])   -> [5]
1736     *   ['a','b'].intersect('b','c') -> ['b']
1737     *
1738     ***/
1739    'intersect': function() {
1740      return arrayIntersect(this, flatArguments(arguments), false);
1741    },
1742
1743    /***
1744     * @method subtract([a1], [a2], ...)
1745     * @returns Array
1746     * @short Subtracts from the array all elements in [a1], [a2], etc.
1747     * @extra This method will also correctly operate on arrays of objects.
1748     * @example
1749     *
1750     *   [1,3,5].subtract([5,7,9])   -> [1,3]
1751     *   [1,3,5].subtract([3],[5])   -> [1]
1752     *   ['a','b'].subtract('b','c') -> ['a']
1753     *
1754     ***/
1755    'subtract': function(a) {
1756      return arrayIntersect(this, flatArguments(arguments), true);
1757    },
1758
1759    /***
1760     * @method at(<index>, [loop] = true)
1761     * @returns Mixed
1762     * @short Gets the element(s) at a given index.
1763     * @extra When [loop] is true, overshooting the end of the array (or the beginning) will begin counting from the other end. As an alternate syntax, passing multiple indexes will get the elements at those indexes.
1764     * @example
1765     *
1766     *   [1,2,3].at(0)        -> 1
1767     *   [1,2,3].at(2)        -> 3
1768     *   [1,2,3].at(4)        -> 2
1769     *   [1,2,3].at(4, false) -> null
1770     *   [1,2,3].at(-1)       -> 3
1771     *   [1,2,3].at(0,1)      -> [1,2]
1772     *
1773     ***/
1774    'at': function() {
1775      return getEntriesForIndexes(this, arguments);
1776    },
1777
1778    /***
1779     * @method first([num] = 1)
1780     * @returns Mixed
1781     * @short Returns the first element(s) in the array.
1782     * @extra When <num> is passed, returns the first <num> elements in the array.
1783     * @example
1784     *
1785     *   [1,2,3].first()        -> 1
1786     *   [1,2,3].first(2)       -> [1,2]
1787     *
1788     ***/
1789    'first': function(num) {
1790      if(isUndefined(num)) return this[0];
1791      if(num < 0) num = 0;
1792      return this.slice(0, num);
1793    },
1794
1795    /***
1796     * @method last([num] = 1)
1797     * @returns Mixed
1798     * @short Returns the last element(s) in the array.
1799     * @extra When <num> is passed, returns the last <num> elements in the array.
1800     * @example
1801     *
1802     *   [1,2,3].last()        -> 3
1803     *   [1,2,3].last(2)       -> [2,3]
1804     *
1805     ***/
1806    'last': function(num) {
1807      if(isUndefined(num)) return this[this.length - 1];
1808      var start = this.length - num < 0 ? 0 : this.length - num;
1809      return this.slice(start);
1810    },
1811
1812    /***
1813     * @method from(<index>)
1814     * @returns Array
1815     * @short Returns a slice of the array from <index>.
1816     * @example
1817     *
1818     *   [1,2,3].from(1)  -> [2,3]
1819     *   [1,2,3].from(2)  -> [3]
1820     *
1821     ***/
1822    'from': function(num) {
1823      return this.slice(num);
1824    },
1825
1826    /***
1827     * @method to(<index>)
1828     * @returns Array
1829     * @short Returns a slice of the array up to <index>.
1830     * @example
1831     *
1832     *   [1,2,3].to(1)  -> [1]
1833     *   [1,2,3].to(2)  -> [1,2]
1834     *
1835     ***/
1836    'to': function(num) {
1837      if(isUndefined(num)) num = this.length;
1838      return this.slice(0, num);
1839    },
1840
1841    /***
1842     * @method min([map], [all] = false)
1843     * @returns Mixed
1844     * @short Returns the element in the array with the lowest value.
1845     * @extra [map] may be a function mapping the value to be checked or a string acting as a shortcut. If [all] is true, will return all min values in an array.
1846     * @example
1847     *
1848     *   [1,2,3].min()                          -> 1
1849     *   ['fee','fo','fum'].min('length')       -> 'fo'
1850     *   ['fee','fo','fum'].min('length', true) -> ['fo']
1851     +   ['fee','fo','fum'].min(function(n) {
1852     *     return n.length;
1853     *   });                              -> ['fo']
1854     +   [{a:3,a:2}].min(function(n) {
1855     *     return n['a'];
1856     *   });                              -> [{a:2}]
1857     *
1858     ***/
1859    'min': function(map, all) {
1860      return getMinOrMax(this, map, 'min', all);
1861    },
1862
1863    /***
1864     * @method max([map], [all] = false)
1865     * @returns Mixed
1866     * @short Returns the element in the array with the greatest value.
1867     * @extra [map] may be a function mapping the value to be checked or a string acting as a shortcut. If [all] is true, will return all max values in an array.
1868     * @example
1869     *
1870     *   [1,2,3].max()                          -> 3
1871     *   ['fee','fo','fum'].max('length')       -> 'fee'
1872     *   ['fee','fo','fum'].max('length', true) -> ['fee']
1873     +   [{a:3,a:2}].max(function(n) {
1874     *     return n['a'];
1875     *   });                              -> {a:3}
1876     *
1877     ***/
1878    'max': function(map, all) {
1879      return getMinOrMax(this, map, 'max', all);
1880    },
1881
1882    /***
1883     * @method least([map])
1884     * @returns Array
1885     * @short Returns the elements in the array with the least commonly occuring value.
1886     * @extra [map] may be a function mapping the value to be checked or a string acting as a shortcut.
1887     * @example
1888     *
1889     *   [3,2,2].least()                   -> [3]
1890     *   ['fe','fo','fum'].least('length') -> ['fum']
1891     +   [{age:35,name:'ken'},{age:12,name:'bob'},{age:12,name:'ted'}].least(function(n) {
1892     *     return n.age;
1893     *   });                               -> [{age:35,name:'ken'}]
1894     *
1895     ***/
1896    'least': function(map, all) {
1897      return getMinOrMax(this.groupBy.apply(this, [map]), 'length', 'min', all);
1898    },
1899
1900    /***
1901     * @method most([map])
1902     * @returns Array
1903     * @short Returns the elements in the array with the most commonly occuring value.
1904     * @extra [map] may be a function mapping the value to be checked or a string acting as a shortcut.
1905     * @example
1906     *
1907     *   [3,2,2].most()                   -> [2]
1908     *   ['fe','fo','fum'].most('length') -> ['fe','fo']
1909     +   [{age:35,name:'ken'},{age:12,name:'bob'},{age:12,name:'ted'}].most(function(n) {
1910     *     return n.age;
1911     *   });                              -> [{age:12,name:'bob'},{age:12,name:'ted'}]
1912     *
1913     ***/
1914    'most': function(map, all) {
1915      return getMinOrMax(this.groupBy.apply(this, [map]), 'length', 'max', all);
1916    },
1917
1918    /***
1919     * @method sum([map])
1920     * @returns Number
1921     * @short Sums all values in the array.
1922     * @extra [map] may be a function mapping the value to be summed or a string acting as a shortcut.
1923     * @example
1924     *
1925     *   [1,2,2].sum()                           -> 5
1926     +   [{age:35},{age:12},{age:12}].sum(function(n) {
1927     *     return n.age;
1928     *   });                                     -> 59
1929     *   [{age:35},{age:12},{age:12}].sum('age') -> 59
1930     *
1931     ***/
1932    'sum': function(map) {
1933      var arr = map ? this.map(map) : this;
1934      return arr.length > 0 ? arr.reduce(function(a,b) { return a + b; }) : 0;
1935    },
1936
1937    /***
1938     * @method average([map])
1939     * @returns Number
1940     * @short Gets the mean average for all values in the array.
1941     * @extra [map] may be a function mapping the value to be averaged or a string acting as a shortcut.
1942     * @example
1943     *
1944     *   [1,2,3].average()                           -> 2
1945     +   [{age:35},{age:11},{age:11}].average(function(n) {
1946     *     return n.age;
1947     *   });                                         -> 19
1948     *   [{age:35},{age:11},{age:11}].average('age') -> 19
1949     *
1950     ***/
1951    'average': function(map) {
1952      var arr = map ? this.map(map) : this;
1953      return arr.length > 0 ? arr.sum() / arr.length : 0;
1954    },
1955
1956    /***
1957     * @method inGroups(<num>, [padding])
1958     * @returns Array
1959     * @short Groups the array into <num> arrays.
1960     * @extra [padding] specifies a value with which to pad the last array so that they are all equal length.
1961     * @example
1962     *
1963     *   [1,2,3,4,5,6,7].inGroups(3)         -> [ [1,2,3], [4,5,6], [7] ]
1964     *   [1,2,3,4,5,6,7].inGroups(3, 'none') -> [ [1,2,3], [4,5,6], [7,'none','none'] ]
1965     *
1966     ***/
1967    'inGroups': function(num, padding) {
1968      var pad = arguments.length > 1;
1969      var arr = this;
1970      var result = [];
1971      var divisor = ceil(this.length / num);
1972      simpleRepeat(num, function(i) {
1973        var index = i * divisor;
1974        var group = arr.slice(index, index + divisor);
1975        if(pad && group.length < divisor) {
1976          simpleRepeat(divisor - group.length, function() {
1977            group = group.add(padding);
1978          });
1979        }
1980        result.push(group);
1981      });
1982      return result;
1983    },
1984
1985    /***
1986     * @method inGroupsOf(<num>, [padding] = null)
1987     * @returns Array
1988     * @short Groups the array into arrays of <num> elements each.
1989     * @extra [padding] specifies a value with which to pad the last array so that they are all equal length.
1990     * @example
1991     *
1992     *   [1,2,3,4,5,6,7].inGroupsOf(4)         -> [ [1,2,3,4], [5,6,7] ]
1993     *   [1,2,3,4,5,6,7].inGroupsOf(4, 'none') -> [ [1,2,3,4], [5,6,7,'none'] ]
1994     *
1995     ***/
1996    'inGroupsOf': function(num, padding) {
1997      var result = [], len = this.length, arr = this, group;
1998      if(len === 0 || num === 0) return arr;
1999      if(isUndefined(num)) num = 1;
2000      if(isUndefined(padding)) padding = null;
2001      simpleRepeat(ceil(len / num), function(i) {
2002        group = arr.slice(num * i, num * i + num);
2003        while(group.length < num) {
2004          group.push(padding);
2005        }
2006        result.push(group);
2007      });
2008      return result;
2009    },
2010
2011    /***
2012     * @method isEmpty()
2013     * @returns Boolean
2014     * @short Returns true if the array is empty.
2015     * @extra This is true if the array has a length of zero, or contains only %undefined%, %null%, or %NaN%.
2016     * @example
2017     *
2018     *   [].isEmpty()               -> true
2019     *   [null,undefined].isEmpty() -> true
2020     *
2021     ***/
2022    'isEmpty': function() {
2023      return this.compact().length == 0;
2024    },
2025
2026    /***
2027     * @method sortBy(<map>, [desc] = false)
2028     * @returns Array
2029     * @short Sorts the array by <map>.
2030     * @extra <map> may be a function, a string acting as a shortcut, or blank (direct comparison of array values). [desc] will sort the array in descending order. When the field being sorted on is a string, the resulting order will be determined by an internal collation algorithm that is optimized for major Western languages, but can be customized. For more information see @array_sorting.
2031     * @example
2032     *
2033     *   ['world','a','new'].sortBy('length')       -> ['a','new','world']
2034     *   ['world','a','new'].sortBy('length', true) -> ['world','new','a']
2035     +   [{age:72},{age:13},{age:18}].sortBy(function(n) {
2036     *     return n.age;
2037     *   });                                        -> [{age:13},{age:18},{age:72}]
2038     *
2039     ***/
2040    'sortBy': function(map, desc) {
2041      var arr = this.clone();
2042      arr.sort(function(a, b) {
2043        var aProperty, bProperty, comp;
2044        aProperty = transformArgument(a, map, arr, [a]);
2045        bProperty = transformArgument(b, map, arr, [b]);
2046        if(isString(aProperty) && isString(bProperty)) {
2047          comp = collateStrings(aProperty, bProperty);
2048        } else if(aProperty < bProperty) {
2049          comp = -1;
2050        } else if(aProperty > bProperty) {
2051          comp = 1;
2052        } else {
2053          comp = 0;
2054        }
2055        return comp * (desc ? -1 : 1);
2056      });
2057      return arr;
2058    },
2059
2060    /***
2061     * @method randomize()
2062     * @returns Array
2063     * @short Returns a copy of the array with the elements randomized.
2064     * @extra Uses Fisher-Yates algorithm.
2065     * @example
2066     *
2067     *   [1,2,3,4].randomize()  -> [?,?,?,?]
2068     *
2069     ***/
2070    'randomize': function() {
2071      var arr = this.concat(), i = arr.length, j, x;
2072      while(i) {
2073        j = (math.random() * i) | 0;
2074        x = arr[--i];
2075        arr[i] = arr[j];
2076        arr[j] = x;
2077      }
2078      return arr;
2079    },
2080
2081    /***
2082     * @method zip([arr1], [arr2], ...)
2083     * @returns Array
2084     * @short Merges multiple arrays together.
2085     * @extra This method "zips up" smaller arrays into one large whose elements are "all elements at index 0", "all elements at index 1", etc. Useful when you have associated data that is split over separated arrays. If the arrays passed have more elements than the original array, they will be discarded. If they have fewer elements, the missing elements will filled with %null%.
2086     * @example
2087     *
2088     *   [1,2,3].zip([4,5,6])                                       -> [[1,2], [3,4], [5,6]]
2089     *   ['Martin','John'].zip(['Luther','F.'], ['King','Kennedy']) -> [['Martin','Luther','King'], ['John','F.','Kennedy']]
2090     *
2091     ***/
2092    'zip': function() {
2093      var args = multiArgs(arguments);
2094      return this.map(function(el, i) {
2095        return [el].concat(args.map(function(k) {
2096          return (i in k) ? k[i] : null;
2097        }));
2098      });
2099    },
2100
2101    /***
2102     * @method sample([num])
2103     * @returns Mixed
2104     * @short Returns a random element from the array.
2105     * @extra If [num] is passed, will return [num] samples from the array.
2106     * @example
2107     *
2108     *   [1,2,3,4,5].sample()  -> // Random element
2109     *   [1,2,3,4,5].sample(3) -> // Array of 3 random elements
2110     *
2111     ***/
2112    'sample': function(num) {
2113      var arr = this.randomize();
2114      return arguments.length > 0 ? arr.slice(0, num) : arr[0];
2115    },
2116
2117    /***
2118     * @method each(<fn>, [index] = 0, [loop] = false)
2119     * @returns Array
2120     * @short Runs <fn> against each element in the array. Enhanced version of %Array#forEach%.
2121     * @extra Parameters passed to <fn> are identical to %forEach%, ie. the first parameter is the current element, second parameter is the current index, and third parameter is the array itself. If <fn> returns %false% at any time it will break out of the loop. Once %each% finishes, it will return the array. If [index] is passed, <fn> will begin at that index and work its way to the end. If [loop] is true, it will then start over from the beginning of the array and continue until it reaches [index] - 1.
2122     * @example
2123     *
2124     *   [1,2,3,4].each(function(n) {
2125     *     // Called 4 times: 1, 2, 3, 4
2126     *   });
2127     *   [1,2,3,4].each(function(n) {
2128     *     // Called 4 times: 3, 4, 1, 2
2129     *   }, 2, true);
2130     *
2131     ***/
2132    'each': function(fn, index, loop) {
2133      arrayEach(this, fn, index, loop);
2134      return this;
2135    },
2136
2137    /***
2138     * @method add(<el>, [index])
2139     * @returns Array
2140     * @short Adds <el> to the array.
2141     * @extra If [index] is specified, it will add at [index], otherwise adds to the end of the array. %add% behaves like %concat% in that if <el> is an array it will be joined, not inserted. This method will change the array! Use %include% for a non-destructive alias. Also, %insert% is provided as an alias that reads better when using an index.
2142     * @example
2143     *
2144     *   [1,2,3,4].add(5)       -> [1,2,3,4,5]
2145     *   [1,2,3,4].add([5,6,7]) -> [1,2,3,4,5,6,7]
2146     *   [1,2,3,4].insert(8, 1) -> [1,8,2,3,4]
2147     *
2148     ***/
2149    'add': function(el, index) {
2150      if(!isNumber(number(index)) || isNaN(index)) index = this.length;
2151      array.prototype.splice.apply(this, [index, 0].concat(el));
2152      return this;
2153    },
2154
2155    /***
2156     * @method remove([f1], [f2], ...)
2157     * @returns Array
2158     * @short Removes any element in the array that matches [f1], [f2], etc.
2159     * @extra Will match a string, number, array, object, or alternately test against a function or regex. This method will change the array! Use %exclude% for a non-destructive alias. This method implements @array_matching.
2160     * @example
2161     *
2162     *   [1,2,3].remove(3)         -> [1,2]
2163     *   ['a','b','c'].remove(/b/) -> ['a','c']
2164     +   [{a:1},{b:2}].remove(function(n) {
2165     *     return n['a'] == 1;
2166     *   });                       -> [{b:2}]
2167     *
2168     ***/
2169    'remove': function() {
2170      var arr = this;
2171      multiArgs(arguments, function(f) {
2172        var i = 0, matcher = getMatcher(f);
2173        while(i < arr.length) {
2174          if(matcher(arr[i], i, arr)) {
2175            arr.splice(i, 1);
2176          } else {
2177            i++;
2178          }
2179        }
2180      });
2181      return arr;
2182    },
2183
2184    /***
2185     * @method compact([all] = false)
2186     * @returns Array
2187     * @short Removes all instances of %undefined%, %null%, and %NaN% from the array.
2188     * @extra If [all] is %true%, all "falsy" elements will be removed. This includes empty strings, 0, and false.
2189     * @example
2190     *
2191     *   [1,null,2,undefined,3].compact() -> [1,2,3]
2192     *   [1,'',2,false,3].compact()       -> [1,'',2,false,3]
2193     *   [1,'',2,false,3].compact(true)   -> [1,2,3]
2194     *
2195     ***/
2196    'compact': function(all) {
2197      var result = [];
2198      arrayEach(this, function(el, i) {
2199        if(isArray(el)) {
2200          result.push(el.compact());
2201        } else if(all && el) {
2202          result.push(el);
2203        } else if(!all && el != null && el.valueOf() === el.valueOf()) {
2204          result.push(el);
2205        }
2206      });
2207      return result;
2208    },
2209
2210    /***
2211     * @method groupBy(<map>, [fn])
2212     * @returns Object
2213     * @short Groups the array by <map>.
2214     * @extra Will return an object with keys equal to the grouped values. <map> may be a mapping function, or a string acting as a shortcut. Optionally calls [fn] for each group.
2215     * @example
2216     *
2217     *   ['fee','fi','fum'].groupBy('length') -> { 2: ['fi'], 3: ['fee','fum'] }
2218     +   [{age:35,name:'ken'},{age:15,name:'bob'}].groupBy(function(n) {
2219     *     return n.age;
2220     *   });                                  -> { 35: [{age:35,name:'ken'}], 15: [{age:15,name:'bob'}] }
2221     *
2222     ***/
2223    'groupBy': function(map, fn) {
2224      var arr = this, result = {}, key;
2225      arrayEach(arr, function(el, index) {
2226        key = transformArgument(el, map, arr, [el, index, arr]);
2227        if(!result[key]) result[key] = [];
2228        result[key].push(el);
2229      });
2230      if(fn) {
2231        iterateOverObject(result, fn);
2232      }
2233      return result;
2234    },
2235
2236    /***
2237     * @method none(<f>)
2238     * @returns Boolean
2239     * @short Returns true if none of the elements in the array match <f>.
2240     * @extra <f> will match a string, number, array, object, or alternately test against a function or regex. This method implements @array_matching.
2241     * @example
2242     *
2243     *   [1,2,3].none(5)         -> true
2244     *   ['a','b','c'].none(/b/) -> false
2245     +   [{a:1},{b:2}].none(function(n) {
2246     *     return n['a'] > 1;
2247     *   });                     -> true
2248     *
2249     ***/
2250    'none': function() {
2251      return !this.any.apply(this, arguments);
2252    }
2253
2254
2255  });
2256
2257
2258  // Aliases
2259
2260  extend(array, true, true, {
2261
2262    /***
2263     * @method all()
2264     * @alias every
2265     *
2266     ***/
2267    'all': array.prototype.every,
2268
2269    /*** @method any()
2270     * @alias some
2271     *
2272     ***/
2273    'any': array.prototype.some,
2274
2275    /***
2276     * @method insert()
2277     * @alias add
2278     *
2279     ***/
2280    'insert': array.prototype.add
2281
2282  });
2283
2284
2285  /***
2286   * Object module
2287   * Enumerable methods on objects
2288   *
2289   ***/
2290
2291   function keysWithObjectCoercion(obj) {
2292     return object.keys(coercePrimitiveToObject(obj));
2293   }
2294
2295  /***
2296   * @method [enumerable](<obj>)
2297   * @returns Boolean
2298   * @short Enumerable methods in the Array package are also available to the Object class. They will perform their normal operations for every property in <obj>.
2299   * @extra In cases where a callback is used, instead of %element, index%, the callback will instead be passed %key, value%. Enumerable methods are also available to extended objects as instance methods.
2300   *
2301   * @set
2302   *   each
2303   *   map
2304   *   any
2305   *   all
2306   *   none
2307   *   count
2308   *   find
2309   *   findAll
2310   *   reduce
2311   *   isEmpty
2312   *   sum
2313   *   average
2314   *   min
2315   *   max
2316   *   least
2317   *   most
2318   *
2319   * @example
2320   *
2321   *   Object.any({foo:'bar'}, 'bar')            -> true
2322   *   Object.extended({foo:'bar'}).any('bar')   -> true
2323   *   Object.isEmpty({})                        -> true
2324   +   Object.map({ fred: { age: 52 } }, 'age'); -> { fred: 52 }
2325   *
2326   ***/
2327
2328  function buildEnumerableMethods(names, mapping) {
2329    extendSimilar(object, false, true, names, function(methods, name) {
2330      methods[name] = function(obj, arg1, arg2) {
2331        var result, coerced = keysWithObjectCoercion(obj), matcher;
2332        if(!mapping) {
2333          matcher = getMatcher(arg1, true);
2334        }
2335        result = array.prototype[name].call(coerced, function(key) {
2336          var value = obj[key];
2337          if(mapping) {
2338            return transformArgument(value, arg1, obj, [key, value, obj]);
2339          } else {
2340            return matcher(value, key, obj);
2341          }
2342        }, arg2);
2343        if(isArray(result)) {
2344          // The method has returned an array of keys so use this array
2345          // to build up the resulting object in the form we want it in.
2346          result = result.reduce(function(o, key, i) {
2347            o[key] = obj[key];
2348            return o;
2349          }, {});
2350        }
2351        return result;
2352      };
2353    });
2354    buildObjectInstanceMethods(names, Hash);
2355  }
2356
2357  function exportSortAlgorithm() {
2358    array[AlphanumericSort] = collateStrings;
2359  }
2360
2361  extend(object, false, true, {
2362
2363    'map': function(obj, map) {
2364      var result = {}, key, value;
2365      for(key in obj) {
2366        if(!hasOwnProperty(obj, key)) continue;
2367        value = obj[key];
2368        result[key] = transformArgument(value, map, obj, [key, value, obj]);
2369      }
2370      return result;
2371    },
2372
2373    'reduce': function(obj) {
2374      var values = keysWithObjectCoercion(obj).map(function(key) {
2375        return obj[key];
2376      });
2377      return values.reduce.apply(values, multiArgs(arguments, null, 1));
2378    },
2379
2380    'each': function(obj, fn) {
2381      checkCallback(fn);
2382      iterateOverObject(obj, fn);
2383      return obj;
2384    },
2385
2386    /***
2387     * @method size(<obj>)
2388     * @returns Number
2389     * @short Returns the number of properties in <obj>.
2390     * @extra %size% is available as an instance method on extended objects.
2391     * @example
2392     *
2393     *   Object.size({ foo: 'bar' }) -> 1
2394     *
2395     ***/
2396    'size': function (obj) {
2397      return keysWithObjectCoercion(obj).length;
2398    }
2399
2400  });
2401
2402  var EnumerableFindingMethods = 'any,all,none,count,find,findAll,isEmpty'.split(',');
2403  var EnumerableMappingMethods = 'sum,average,min,max,least,most'.split(',');
2404  var EnumerableOtherMethods   = 'map,reduce,size'.split(',');
2405  var EnumerableMethods        = EnumerableFindingMethods.concat(EnumerableMappingMethods).concat(EnumerableOtherMethods);
2406
2407  buildEnhancements();
2408  buildAlphanumericSort();
2409  buildEnumerableMethods(EnumerableFindingMethods);
2410  buildEnumerableMethods(EnumerableMappingMethods, true);
2411  buildObjectInstanceMethods(EnumerableOtherMethods, Hash);
2412  exportSortAlgorithm();
2413
2414
2415  /***
2416   * @package Date
2417   * @dependency core
2418   * @description Date parsing and formatting, relative formats like "1 minute ago", Number methods like "daysAgo", localization support with default English locale definition.
2419   *
2420   ***/
2421
2422  var English;
2423  var CurrentLocalization;
2424
2425  var TimeFormat = ['ampm','hour','minute','second','ampm','utc','offset_sign','offset_hours','offset_minutes','ampm']
2426  var DecimalReg = '(?:[,.]\\d+)?';
2427  var HoursReg   = '\\d{1,2}' + DecimalReg;
2428  var SixtyReg   = '[0-5]\\d' + DecimalReg;
2429  var RequiredTime = '({t})?\\s*('+HoursReg+')(?:{h}('+SixtyReg+')?{m}(?::?('+SixtyReg+'){s})?\\s*(?:({t})|(Z)|(?:([+-])(\\d{2,2})(?::?(\\d{2,2}))?)?)?|\\s*({t}))';
2430
2431  var KanjiDigits = '〇一二三四五六七八九十百千万';
2432  var AsianDigitMap = {};
2433  var AsianDigitReg;
2434
2435  var DateArgumentUnits;
2436  var DateUnitsReversed;
2437  var CoreDateFormats = [];
2438  var CompiledOutputFormats = {};
2439
2440  var DateFormatTokens = {
2441
2442    'yyyy': function(d) {
2443      return callDateGet(d, 'FullYear');
2444    },
2445
2446    'yy': function(d) {
2447      return callDateGet(d, 'FullYear') % 100;
2448    },
2449
2450    'ord': function(d) {
2451      var date = callDateGet(d, 'Date');
2452      return date + getOrdinalizedSuffix(date);
2453    },
2454
2455    'tz': function(d) {
2456      return d.getUTCOffset();
2457    },
2458
2459    'isotz': function(d) {
2460      return d.getUTCOffset(true);
2461    },
2462
2463    'Z': function(d) {
2464      return d.getUTCOffset();
2465    },
2466
2467    'ZZ': function(d) {
2468      return d.getUTCOffset().replace(/(\d{2})$/, ':$1');
2469    }
2470
2471  };
2472
2473  var DateUnits = [
2474    {
2475      name: 'year',
2476      method: 'FullYear',
2477      ambiguous: true,
2478      multiplier: function(d) {
2479        var adjust = d ? (d.isLeapYear() ? 1 : 0) : 0.25;
2480        return (365 + adjust) * 24 * 60 * 60 * 1000;
2481      }
2482    },
2483    {
2484      name: 'month',
2485      error: 0.919, // Feb 1-28 over 1 month
2486      method: 'Month',
2487      ambiguous: true,
2488      multiplier: function(d, ms) {
2489        var days = 30.4375, inMonth;
2490        if(d) {
2491          inMonth = d.daysInMonth();
2492          if(ms <= inMonth.days()) {
2493            days = inMonth;
2494          }
2495        }
2496        return days * 24 * 60 * 60 * 1000;
2497      }
2498    },
2499    {
2500      name: 'week',
2501      method: 'ISOWeek',
2502      multiplier: function() {
2503        return 7 * 24 * 60 * 60 * 1000;
2504      }
2505    },
2506    {
2507      name: 'day',
2508      error: 0.958, // DST traversal over 1 day
2509      method: 'Date',
2510      ambiguous: true,
2511      multiplier: function() {
2512        return 24 * 60 * 60 * 1000;
2513      }
2514    },
2515    {
2516      name: 'hour',
2517      method: 'Hours',
2518      multiplier: function() {
2519        return 60 * 60 * 1000;
2520      }
2521    },
2522    {
2523      name: 'minute',
2524      method: 'Minutes',
2525      multiplier: function() {
2526        return 60 * 1000;
2527      }
2528    },
2529    {
2530      name: 'second',
2531      method: 'Seconds',
2532      multiplier: function() {
2533        return 1000;
2534      }
2535    },
2536    {
2537      name: 'millisecond',
2538      method: 'Milliseconds',
2539      multiplier: function() {
2540        return 1;
2541      }
2542    }
2543  ];
2544
2545
2546
2547
2548  // Date Localization
2549
2550  var Localizations = {};
2551
2552  // Localization object
2553
2554  function Localization(l) {
2555    simpleMerge(this, l);
2556    this.compiledFormats = CoreDateFormats.concat();
2557  }
2558
2559  Localization.prototype = {
2560
2561    getMonth: function(n) {
2562      if(isNumber(n)) {
2563        return n - 1;
2564      } else {
2565        return this['months'].indexOf(n) % 12;
2566      }
2567    },
2568
2569    getWeekday: function(n) {
2570      return this['weekdays'].indexOf(n) % 7;
2571    },
2572
2573    getNumber: function(n) {
2574      var i;
2575      if(isNumber(n)) {
2576        return n;
2577      } else if(n && (i = this['numbers'].indexOf(n)) !== -1) {
2578        return (i + 1) % 10;
2579      } else {
2580        return 1;
2581      }
2582    },
2583
2584    getNumericDate: function(n) {
2585      var self = this;
2586      return n.replace(regexp(this['num'], 'g'), function(d) {
2587        var num = self.getNumber(d);
2588        return num || '';
2589      });
2590    },
2591
2592    getUnitIndex: function(n) {
2593      return this['units'].indexOf(n) % 8;
2594    },
2595
2596    getRelativeFormat: function(adu) {
2597      return this.convertAdjustedToFormat(adu, adu[2] > 0 ? 'future' : 'past');
2598    },
2599
2600    getDuration: function(ms) {
2601      return this.convertAdjustedToFormat(getAdjustedUnit(ms), 'duration');
2602    },
2603
2604    hasVariant: function(code) {
2605      code = code || this.code;
2606      return code === 'en' || code === 'en-US' ? true : this['variant'];
2607    },
2608
2609    matchAM: function(str) {
2610      return str === this['ampm'][0];
2611    },
2612
2613    matchPM: function(str) {
2614      return str && str === this['ampm'][1];
2615    },
2616
2617    convertAdjustedToFormat: function(adu, mode) {
2618      var sign, unit, mult,
2619          num    = adu[0],
2620          u      = adu[1],
2621          ms     = adu[2],
2622          format = this[mode] || this['relative'];
2623      if(isFunction(format)) {
2624        return format.call(this, num, u, ms, mode);
2625      }
2626      mult = this['plural'] && num > 1 ? 1 : 0;
2627      unit = this['units'][mult * 8 + u] || this['units'][u];
2628      if(this['capitalizeUnit']) unit = simpleCapitalize(unit);
2629      sign = this['modifiers'].filter(function(m) { return m.name == 'sign' && m.value == (ms > 0 ? 1 : -1); })[0];
2630      return format.replace(/\{(.*?)\}/g, function(full, match) {
2631        switch(match) {
2632          case 'num': return num;
2633          case 'unit': return unit;
2634          case 'sign': return sign.src;
2635        }
2636      });
2637    },
2638
2639    getFormats: function() {
2640      return this.cachedFormat ? [this.cachedFormat].concat(this.compiledFormats) : this.compiledFormats;
2641    },
2642
2643    addFormat: function(src, allowsTime, match, variant, iso) {
2644      var to = match || [], loc = this, time, timeMarkers, lastIsNumeral;
2645
2646      src = src.replace(/\s+/g, '[,. ]*');
2647      src = src.replace(/\{([^,]+?)\}/g, function(all, k) {
2648        var value, arr, result,
2649            opt   = k.match(/\?$/),
2650            nc    = k.match(/^(\d+)\??$/),
2651            slice = k.match(/(\d)(?:-(\d))?/),
2652            key   = k.replace(/[^a-z]+$/, '');
2653        if(nc) {
2654          value = loc['tokens'][nc[1]];
2655        } else if(loc[key]) {
2656          value = loc[key];
2657        } else if(loc[key + 's']) {
2658          value = loc[key + 's'];
2659          if(slice) {
2660            // Can't use filter here as Prototype hijacks the method and doesn't
2661            // pass an index, so use a simple loop instead!
2662            arr = [];
2663            value.forEach(function(m, i) {
2664              var mod = i % (loc['units'] ? 8 : value.length);
2665              if(mod >= slice[1] && mod <= (slice[2] || slice[1])) {
2666                arr.push(m);
2667              }
2668            });
2669            value = arr;
2670          }
2671          value = arrayToAlternates(value);
2672        }
2673        if(nc) {
2674          result = '(?:' + value + ')';
2675        } else {
2676          if(!match) {
2677            to.push(key);
2678          }
2679          result = '(' + value + ')';
2680        }
2681        if(opt) {
2682          result += '?';
2683        }
2684        return result;
2685      });
2686      if(allowsTime) {
2687        time = prepareTime(RequiredTime, loc, iso);
2688        timeMarkers = ['t','[\\s\\u3000]'].concat(loc['timeMarker']);
2689        lastIsNumeral = src.match(/\\d\{\d,\d\}\)+\??$/);
2690        addDateInputFormat(loc, '(?:' + time + ')[,\\s\\u3000]+?' + src, TimeFormat.concat(to), variant);
2691        addDateInputFormat(loc, src + '(?:[,\\s]*(?:' + timeMarkers.join('|') + (lastIsNumeral ? '+' : '*') +')' + time + ')?', to.concat(TimeFormat), variant);
2692      } else {
2693        addDateInputFormat(loc, src, to, variant);
2694      }
2695    }
2696
2697  };
2698
2699
2700  // Localization helpers
2701
2702  function getLocalization(localeCode, fallback) {
2703    var loc;
2704    if(!isString(localeCode)) localeCode = '';
2705    loc = Localizations[localeCode] || Localizations[localeCode.slice(0,2)];
2706    if(fallback === false && !loc) {
2707      throw new TypeError('Invalid locale.');
2708    }
2709    return loc || CurrentLocalization;
2710  }
2711
2712  function setLocalization(localeCode, set) {
2713    var loc, canAbbreviate;
2714
2715    function initializeField(name) {
2716      var val = loc[name];
2717      if(isString(val)) {
2718        loc[name] = val.split(',');
2719      } else if(!val) {
2720        loc[name] = [];
2721      }
2722    }
2723
2724    function eachAlternate(str, fn) {
2725      str = str.split('+').map(function(split) {
2726        return split.replace(/(.+):(.+)$/, function(full, base, suffixes) {
2727          return suffixes.split('|').map(function(suffix) {
2728            return base + suffix;
2729          }).join('|');
2730        });
2731      }).join('|');
2732      return str.split('|').forEach(fn);
2733    }
2734
2735    function setArray(name, abbreviate, multiple) {
2736      var arr = [];
2737      loc[name].forEach(function(full, i) {
2738        if(abbreviate) {
2739          full += '+' + full.slice(0,3);
2740        }
2741        eachAlternate(full, function(day, j) {
2742          arr[j * multiple + i] = day.toLowerCase();
2743        });
2744      });
2745      loc[name] = arr;
2746    }
2747
2748    function getDigit(start, stop, allowNumbers) {
2749      var str = '\\d{' + start + ',' + stop + '}';
2750      if(allowNumbers) str += '|(?:' + arrayToAlternates(loc['numbers']) + ')+';
2751      return str;
2752    }
2753
2754    function getNum() {
2755      var arr = ['-?\\d+'].concat(loc['articles']);
2756      if(loc['numbers']) arr = arr.concat(loc['numbers']);
2757      return arrayToAlternates(arr);
2758    }
2759
2760    function setDefault(name, value) {
2761      loc[name] = loc[name] || value;
2762    }
2763
2764    function setModifiers() {
2765      var arr = [];
2766      loc.modifiersByName = {};
2767      loc['modifiers'].push({ 'name': 'day', 'src': 'yesterday', 'value': -1 });
2768      loc['modifiers'].push({ 'name': 'day', 'src': 'today', 'value': 0 });
2769      loc['modifiers'].push({ 'name': 'day', 'src': 'tomorrow', 'value': 1 });
2770      loc['modifiers'].forEach(function(modifier) {
2771        var name = modifier.name;
2772        eachAlternate(modifier.src, function(t) {
2773          var locEntry = loc[name];
2774          loc.modifiersByName[t] = modifier;
2775          arr.push({ name: name, src: t, value: modifier.value });
2776          loc[name] = locEntry ? locEntry + '|' + t : t;
2777        });
2778      });
2779      loc['day'] += '|' + arrayToAlternates(loc['weekdays']);
2780      loc['modifiers'] = arr;
2781    }
2782
2783    // Initialize the locale
2784    loc = new Localization(set);
2785    initializeField('modifiers');
2786    'months,weekdays,units,numbers,articles,tokens,timeMarker,ampm,timeSuffixes,dateParse,timeParse'.split(',').forEach(initializeField);
2787
2788    canAbbreviate = !loc['monthSuffix'];
2789
2790    setArray('months',   canAbbreviate, 12);
2791    setArray('weekdays', canAbbreviate, 7);
2792    setArray('units', false, 8);
2793    setArray('numbers', false, 10);
2794
2795    setDefault('code', localeCode);
2796    setDefault('date', getDigit(1,2, loc['digitDate']));
2797    setDefault('year', "'\\d{2}|" + getDigit(4,4));
2798    setDefault('num', getNum());
2799
2800    setModifiers();
2801
2802    if(loc['monthSuffix']) {
2803      loc['month'] = getDigit(1,2);
2804      loc['months'] = '1,2,3,4,5,6,7,8,9,10,11,12'.split(',').map(function(n) { return n + loc['monthSuffix']; });
2805    }
2806    loc['full_month'] = getDigit(1,2) + '|' + arrayToAlternates(loc['months']);
2807
2808    // The order of these formats is very important. Order is reversed so formats that come
2809    // later will take precedence over formats that come before. This generally means that
2810    // more specific formats should come later, however, the {year} format should come before
2811    // {day}, as 2011 needs to be parsed as a year (2011) and not date (20) + hours (11)
2812
2813    // If the locale has time suffixes then add a time only format for that locale
2814    // that is separate from the core English-based one.
2815    if(loc['timeSuffixes'].length > 0) {
2816      loc.addFormat(prepareTime(RequiredTime, loc), false, TimeFormat)
2817    }
2818
2819    loc.addFormat('{day}', true);
2820    loc.addFormat('{month}' + (loc['monthSuffix'] || ''));
2821    loc.addFormat('{year}' + (loc['yearSuffix'] || ''));
2822
2823    loc['timeParse'].forEach(function(src) {
2824      loc.addFormat(src, true);
2825    });
2826
2827    loc['dateParse'].forEach(function(src) {
2828      loc.addFormat(src);
2829    });
2830
2831    return Localizations[localeCode] = loc;
2832  }
2833
2834
2835  // General helpers
2836
2837  function addDateInputFormat(locale, format, match, variant) {
2838    locale.compiledFormats.unshift({
2839      variant: variant,
2840      locale: locale,
2841      reg: regexp('^' + format + '$', 'i'),
2842      to: match
2843    });
2844  }
2845
2846  function simpleCapitalize(str) {
2847    return str.slice(0,1).toUpperCase() + str.slice(1);
2848  }
2849
2850  function arrayToAlternates(arr) {
2851    return arr.filter(function(el) {
2852      return !!el;
2853    }).join('|');
2854  }
2855
2856  function getNewDate() {
2857    var fn = date.SugarNewDate;
2858    return fn ? fn() : new date;
2859  }
2860
2861  // Date argument helpers
2862
2863  function collectDateArguments(args, allowDuration) {
2864    var obj;
2865    if(isObjectType(args[0])) {
2866      return args;
2867    } else if (isNumber(args[0]) && !isNumber(args[1])) {
2868      return [args[0]];
2869    } else if (isString(args[0]) && allowDuration) {
2870      return [getDateParamsFromString(args[0]), args[1]];
2871    }
2872    obj = {};
2873    DateArgumentUnits.forEach(function(u,i) {
2874      obj[u.name] = args[i];
2875    });
2876    return [obj];
2877  }
2878
2879  function getDateParamsFromString(str, num) {
2880    var match, params = {};
2881    match = str.match(/^(\d+)?\s?(\w+?)s?$/i);
2882    if(match) {
2883      if(isUndefined(num)) {
2884        num = parseInt(match[1]) || 1;
2885      }
2886      params[match[2].toLowerCase()] = num;
2887    }
2888    return params;
2889  }
2890
2891  // Date iteration helpers
2892
2893  function iterateOverDateUnits(fn, from, to) {
2894    var i, unit;
2895    if(isUndefined(to)) to = DateUnitsReversed.length;
2896    for(i = from || 0; i < to; i++) {
2897      unit = DateUnitsReversed[i];
2898      if(fn(unit.name, unit, i) === false) {
2899        break;
2900      }
2901    }
2902  }
2903
2904  // Date parsing helpers
2905
2906  function getFormatMatch(match, arr) {
2907    var obj = {}, value, num;
2908    arr.forEach(function(key, i) {
2909      value = match[i + 1];
2910      if(isUndefined(value) || value === '') return;
2911      if(key === 'year') {
2912        obj.yearAsString = value.replace(/'/, '');
2913      }
2914      num = parseFloat(value.replace(/'/, '').replace(/,/, '.'));
2915      obj[key] = !isNaN(num) ? num : value.toLowerCase();
2916    });
2917    return obj;
2918  }
2919
2920  function cleanDateInput(str) {
2921    str = str.trim().replace(/^just (?=now)|\.+$/i, '');
2922    return convertAsianDigits(str);
2923  }
2924
2925  function convertAsianDigits(str) {
2926    return str.replace(AsianDigitReg, function(full, disallowed, match) {
2927      var sum = 0, place = 1, lastWasHolder, lastHolder;
2928      if(disallowed) return full;
2929      match.split('').reverse().forEach(function(letter) {
2930        var value = AsianDigitMap[letter], holder = value > 9;
2931        if(holder) {
2932          if(lastWasHolder) sum += place;
2933          place *= value / (lastHolder || 1);
2934          lastHolder = value;
2935        } else {
2936          if(lastWasHolder === false) {
2937            place *= 10;
2938          }
2939          sum += place * value;
2940        }
2941        lastWasHolder = holder;
2942      });
2943      if(lastWasHolder) sum += place;
2944      return sum;
2945    });
2946  }
2947
2948  function getExtendedDate(f, localeCode, prefer, forceUTC) {
2949    var d, relative, baseLocalization, afterCallbacks, loc, set, unit, unitIndex, weekday, num, tmp;
2950
2951    d = getNewDate();
2952    afterCallbacks = [];
2953
2954    function afterDateSet(fn) {
2955      afterCallbacks.push(fn);
2956    }
2957
2958    function fireCallbacks() {
2959      afterCallbacks.forEach(function(fn) {
2960        fn.call();
2961      });
2962    }
2963
2964    function setWeekdayOfMonth() {
2965      var w = d.getWeekday();
2966      d.setWeekday((7 * (set['num'] - 1)) + (w > weekday ? weekday + 7 : weekday));
2967    }
2968
2969    function setUnitEdge() {
2970      var modifier = loc.modifiersByName[set['edge']];
2971      iterateOverDateUnits(function(name) {
2972        if(isDefined(set[name])) {
2973          unit = name;
2974          return false;
2975        }
2976      }, 4);
2977      if(unit === 'year') set.specificity = 'month';
2978      else if(unit === 'month' || unit === 'week') set.specificity = 'day';
2979      d[(modifier.value < 0 ? 'endOf' : 'beginningOf') + simpleCapitalize(unit)]();
2980      // This value of -2 is arbitrary but it's a nice clean way to hook into this system.
2981      if(modifier.value === -2) d.reset();
2982    }
2983
2984    function separateAbsoluteUnits() {
2985      var params;
2986      iterateOverDateUnits(function(name, u, i) {
2987        if(name === 'day') name = 'date';
2988        if(isDefined(set[name])) {
2989          // If there is a time unit set that is more specific than
2990          // the matched unit we have a string like "5:30am in 2 minutes",
2991          // which is meaningless, so invalidate the date...
2992          if(i >= unitIndex) {
2993            invalidateDate(d);
2994            return false;
2995          }
2996          // ...otherwise set the params to set the absolute date
2997          // as a callback after the relative date has been set.
2998          params = params || {};
2999          params[name] = set[name];
3000          delete set[name];
3001        }
3002      });
3003      if(params) {
3004        afterDateSet(function() {
3005          d.set(params, true);
3006        });
3007      }
3008    }
3009
3010    d.utc(forceUTC);
3011
3012    if(isDate(f)) {
3013      // If the source here is already a date object, then the operation
3014      // is the same as cloning the date, which preserves the UTC flag.
3015      d.utc(f.isUTC()).setTime(f.getTime());
3016    } else if(isNumber(f)) {
3017      d.setTime(f);
3018    } else if(isObjectType(f)) {
3019      d.set(f, true);
3020      set = f;
3021    } else if(isString(f)) {
3022
3023      // The act of getting the localization will pre-initialize
3024      // if it is missing and add the required formats.
3025      baseLocalization = getLocalization(localeCode);
3026
3027      // Clean the input and convert Kanji based numerals if they exist.
3028      f = cleanDateInput(f);
3029
3030      if(baseLocalization) {
3031        iterateOverObject(baseLocalization.getFormats(), function(i, dif) {
3032          var match = f.match(dif.reg);
3033          if(match) {
3034
3035            loc = dif.locale;
3036            set = getFormatMatch(match, dif.to, loc);
3037            loc.cachedFormat = dif;
3038
3039
3040            if(set['utc']) {
3041              d.utc();
3042            }
3043
3044            if(set.timestamp) {
3045              set = set.timestamp;
3046              return false;
3047            }
3048
3049            // If there's a variant (crazy Endian American format), swap the month and day.
3050            if(dif.variant && !isString(set['month']) && (isString(set['date']) || baseLocalization.hasVariant(localeCode))) {
3051              tmp = set['month'];
3052              set['month'] = set['date'];
3053              set['date']  = tmp;
3054            }
3055
3056            // If the year is 2 digits then get the implied century.
3057            if(set['year'] && set.yearAsString.length === 2) {
3058              set['year'] = getYearFromAbbreviation(set['year']);
3059            }
3060
3061            // Set the month which may be localized.
3062            if(set['month']) {
3063              set['month'] = loc.getMonth(set['month']);
3064              if(set['shift'] && !set['unit']) set['unit'] = loc['units'][7];
3065            }
3066
3067            // If there is both a weekday and a date, the date takes precedence.
3068            if(set['weekday'] && set['date']) {
3069              delete set['weekday'];
3070            // Otherwise set a localized weekday.
3071            } else if(set['weekday']) {
3072              set['weekday'] = loc.getWeekday(set['weekday']);
3073              if(set['shift'] && !set['unit']) set['unit'] = loc['units'][5];
3074            }
3075
3076            // Relative day localizations such as "today" and "tomorrow".
3077            if(set['day'] && (tmp = loc.modifiersByName[set['day']])) {
3078              set['day'] = tmp.value;
3079              d.reset();
3080              relative = true;
3081            // If the day is a weekday, then set that instead.
3082            } else if(set['day'] && (weekday = loc.getWeekday(set['day'])) > -1) {
3083              delete set['day'];
3084              if(set['num'] && set['month']) {
3085                // If we have "the 2nd tuesday of June", set the day to the beginning of the month, then
3086                // set the weekday after all other properties have been set. The weekday needs to be set
3087                // after the actual set because it requires overriding the "prefer" argument which
3088                // could unintentionally send the year into the future, past, etc.
3089                afterDateSet(setWeekdayOfMonth);
3090                set['day'] = 1;
3091              } else {
3092                set['weekday'] = weekday;
3093              }
3094            }
3095
3096            if(set['date'] && !isNumber(set['date'])) {
3097              set['date'] = loc.getNumericDate(set['date']);
3098            }
3099
3100            // If the time is 1pm-11pm advance the time by 12 hours.
3101            if(loc.matchPM(set['ampm']) && set['hour'] < 12) {
3102              set['hour'] += 12;
3103            } else if(loc.matchAM(set['ampm']) && set['hour'] === 12) {
3104              set['hour'] = 0;
3105            }
3106
3107            // Adjust for timezone offset
3108            if('offset_hours' in set || 'offset_minutes' in set) {
3109              d.utc();
3110              set['offset_minutes'] = set['offset_minutes'] || 0;
3111              set['offset_minutes'] += set['offset_hours'] * 60;
3112              if(set['offset_sign'] === '-') {
3113                set['offset_minutes'] *= -1;
3114              }
3115              set['minute'] -= set['offset_minutes'];
3116            }
3117
3118            // Date has a unit like "days", "months", etc. are all relative to the current date.
3119            if(set['unit']) {
3120              relative  = true;
3121              num       = loc.getNumber(set['num']);
3122              unitIndex = loc.getUnitIndex(set['unit']);
3123              unit      = English['units'][unitIndex];
3124
3125              // Formats like "the 15th of last month" or "6:30pm of next week"
3126              // contain absolute units in addition to relative ones, so separate
3127              // them here, remove them from the params, and set up a callback to
3128              // set them after the relative ones have been set.
3129              separateAbsoluteUnits();
3130
3131              // Shift and unit, ie "next month", "last week", etc.
3132              if(set['shift']) {
3133                num *= (tmp = loc.modifiersByName[set['shift']]) ? tmp.value : 0;
3134              }
3135
3136              // Unit and sign, ie "months ago", "weeks from now", etc.
3137              if(set['sign'] && (tmp = loc.modifiersByName[set['sign']])) {
3138                num *= tmp.value;
3139              }
3140
3141              // Units can be with non-relative dates, set here. ie "the day after monday"
3142              if(isDefined(set['weekday'])) {
3143                d.set({'weekday': set['weekday'] }, true);
3144                delete set['weekday'];
3145              }
3146
3147              // Finally shift the unit.
3148              set[unit] = (set[unit] || 0) + num;
3149            }
3150
3151            // If there is an "edge" it needs to be set after the
3152            // other fields are set. ie "the end of February"
3153            if(set['edge']) {
3154              afterDateSet(setUnitEdge);
3155            }
3156
3157            if(set['year_sign'] === '-') {
3158              set['year'] *= -1;
3159            }
3160
3161            iterateOverDateUnits(function(name, unit, i) {
3162              var value = set[name], fraction = value % 1;
3163              if(fraction) {
3164                set[DateUnitsReversed[i - 1].name] = round(fraction * (name === 'second' ? 1000 : 60));
3165                set[name] = floor(value);
3166              }
3167            }, 1, 4);
3168            return false;
3169          }
3170        });
3171      }
3172      if(!set) {
3173        // The Date constructor does something tricky like checking the number
3174        // of arguments so simply passing in undefined won't work.
3175        if(f !== 'now') {
3176          d = new date(f);
3177        }
3178        if(forceUTC) {
3179          // Falling back to system date here which cannot be parsed as UTC,
3180          // so if we're forcing UTC then simply add the offset.
3181          d.addMinutes(-d.getTimezoneOffset());
3182        }
3183      } else if(relative) {
3184        d.advance(set);
3185      } else {
3186        if(d._utc) {
3187          // UTC times can traverse into other days or even months,
3188          // so preemtively reset the time here to prevent this.
3189          d.reset();
3190        }
3191        updateDate(d, set, true, false, prefer);
3192      }
3193      fireCallbacks();
3194      // A date created by parsing a string presumes that the format *itself* is UTC, but
3195      // not that the date, once created, should be manipulated as such. In other words,
3196      // if you are creating a date object from a server time "2012-11-15T12:00:00Z",
3197      // in the majority of cases you are using it to create a date that will, after creation,
3198      // be manipulated as local, so reset the utc flag here.
3199      d.utc(false);
3200    }
3201    return {
3202      date: d,
3203      set: set
3204    }
3205  }
3206
3207  // If the year is two digits, add the most appropriate century prefix.
3208  function getYearFromAbbreviation(year) {
3209    return round(callDateGet(getNewDate(), 'FullYear') / 100) * 100 - round(year / 100) * 100 + year;
3210  }
3211
3212  function getShortHour(d) {
3213    var hours = callDateGet(d, 'Hours');
3214    return hours === 0 ? 12 : hours - (floor(hours / 13) * 12);
3215  }
3216
3217  // weeksSince won't work here as the result needs to be floored, not rounded.
3218  function getWeekNumber(date) {
3219    date = date.clone();
3220    var dow = callDateGet(date, 'Day') || 7;
3221    date.addDays(4 - dow).reset();
3222    return 1 + floor(date.daysSince(date.clone().beginningOfYear()) / 7);
3223  }
3224
3225  function getAdjustedUnit(ms) {
3226    var next, ams = abs(ms), value = ams, unitIndex = 0;
3227    iterateOverDateUnits(function(name, unit, i) {
3228      next = floor(withPrecision(ams / unit.multiplier(), 1));
3229      if(next >= 1) {
3230        value = next;
3231        unitIndex = i;
3232      }
3233    }, 1);
3234    return [value, unitIndex, ms];
3235  }
3236
3237  function getRelativeWithMonthFallback(date) {
3238    var adu = getAdjustedUnit(date.millisecondsFromNow());
3239    if(allowMonthFallback(date, adu)) {
3240      // If the adjusted unit is in months, then better to use
3241      // the "monthsfromNow" which applies a special error margin
3242      // for edge cases such as Jan-09 - Mar-09 being less than
3243      // 2 months apart (when using a strict numeric definition).
3244      // The third "ms" element in the array will handle the sign
3245      // (past or future), so simply take the absolute value here.
3246      adu[0] = abs(date.monthsFromNow());
3247      adu[1] = 6;
3248    }
3249    return adu;
3250  }
3251
3252  function allowMonthFallback(date, adu) {
3253    // Allow falling back to monthsFromNow if the unit is in months...
3254    return adu[1] === 6 ||
3255    // ...or if it's === 4 weeks and there are more days than in the given month
3256    (adu[1] === 5 && adu[0] === 4 && date.daysFromNow() >= getNewDate().daysInMonth());
3257  }
3258
3259
3260  // Date format token helpers
3261
3262  function createMeridianTokens(slice, caps) {
3263    var fn = function(d, localeCode) {
3264      var hours = callDateGet(d, 'Hours');
3265      return getLocalization(localeCode)['ampm'][floor(hours / 12)] || '';
3266    }
3267    createFormatToken('t', fn, 1);
3268    createFormatToken('tt', fn);
3269    createFormatToken('T', fn, 1, 1);
3270    createFormatToken('TT', fn, null, 2);
3271  }
3272
3273  function createWeekdayTokens(slice, caps) {
3274    var fn = function(d, localeCode) {
3275      var dow = callDateGet(d, 'Day');
3276      return getLocalization(localeCode)['weekdays'][dow];
3277    }
3278    createFormatToken('dow', fn, 3);
3279    createFormatToken('Dow', fn, 3, 1);
3280    createFormatToken('weekday', fn);
3281    createFormatToken('Weekday', fn, null, 1);
3282  }
3283
3284  function createMonthTokens(slice, caps) {
3285    createMonthToken('mon', 0, 3);
3286    createMonthToken('month', 0);
3287
3288    // For inflected month forms, namely Russian.
3289    createMonthToken('month2', 1);
3290    createMonthToken('month3', 2);
3291  }
3292
3293  function createMonthToken(token, multiplier, slice) {
3294    var fn = function(d, localeCode) {
3295      var month = callDateGet(d, 'Month');
3296      return getLocalization(localeCode)['months'][month + (multiplier * 12)];
3297    };
3298    createFormatToken(token, fn, slice);
3299    createFormatToken(simpleCapitalize(token), fn, slice, 1);
3300  }
3301
3302  function createFormatToken(t, fn, slice, caps) {
3303    DateFormatTokens[t] = function(d, localeCode) {
3304      var str = fn(d, localeCode);
3305      if(slice) str = str.slice(0, slice);
3306      if(caps)  str = str.slice(0, caps).toUpperCase() + str.slice(caps);
3307      return str;
3308    }
3309  }
3310
3311  function createPaddedToken(t, fn, ms) {
3312    DateFormatTokens[t] = fn;
3313    DateFormatTokens[t + t] = function (d, localeCode) {
3314      return padNumber(fn(d, localeCode), 2);
3315    };
3316    if(ms) {
3317      DateFormatTokens[t + t + t] = function (d, localeCode) {
3318        return padNumber(fn(d, localeCode), 3);
3319      };
3320      DateFormatTokens[t + t + t + t] = function (d, localeCode) {
3321        return padNumber(fn(d, localeCode), 4);
3322      };
3323    }
3324  }
3325
3326
3327  // Date formatting helpers
3328
3329  function buildCompiledOutputFormat(format) {
3330    var match = format.match(/(\{\w+\})|[^{}]+/g);
3331    CompiledOutputFormats[format] = match.map(function(p) {
3332      p.replace(/\{(\w+)\}/, function(full, token) {
3333        p = DateFormatTokens[token] || token;
3334        return token;
3335      });
3336      return p;
3337    });
3338  }
3339
3340  function executeCompiledOutputFormat(date, format, localeCode) {
3341    var compiledFormat, length, i, t, result = '';
3342    compiledFormat = CompiledOutputFormats[format];
3343    for(i = 0, length = compiledFormat.length; i < length; i++) {
3344      t = compiledFormat[i];
3345      result += isFunction(t) ? t(date, localeCode) : t;
3346    }
3347    return result;
3348  }
3349
3350  function formatDate(date, format, relative, localeCode) {
3351    var adu;
3352    if(!date.isValid()) {
3353      return 'Invalid Date';
3354    } else if(Date[format]) {
3355      format = Date[format];
3356    } else if(isFunction(format)) {
3357      adu = getRelativeWithMonthFallback(date);
3358      format = format.apply(date, adu.concat(getLocalization(localeCode)));
3359    }
3360    if(!format && relative) {
3361      adu = adu || getRelativeWithMonthFallback(date);
3362      // Adjust up if time is in ms, as this doesn't
3363      // look very good for a standard relative date.
3364      if(adu[1] === 0) {
3365        adu[1] = 1;
3366        adu[0] = 1;
3367      }
3368      return getLocalization(localeCode).getRelativeFormat(adu);
3369    }
3370    format = format || 'long';
3371    if(format === 'short' || format === 'long' || format === 'full') {
3372      format = getLocalization(localeCode)[format];
3373    }
3374
3375    if(!CompiledOutputFormats[format]) {
3376      buildCompiledOutputFormat(format);
3377    }
3378
3379    return executeCompiledOutputFormat(date, format, localeCode);
3380  }
3381
3382  // Date comparison helpers
3383
3384  function compareDate(d, find, localeCode, buffer, forceUTC) {
3385    var p, t, min, max, override, capitalized, accuracy = 0, loBuffer = 0, hiBuffer = 0;
3386    p = getExtendedDate(find, localeCode, null, forceUTC);
3387    if(buffer > 0) {
3388      loBuffer = hiBuffer = buffer;
3389      override = true;
3390    }
3391    if(!p.date.isValid()) return false;
3392    if(p.set && p.set.specificity) {
3393      DateUnits.forEach(function(u, i) {
3394        if(u.name === p.set.specificity) {
3395          accuracy = u.multiplier(p.date, d - p.date) - 1;
3396        }
3397      });
3398      capitalized = simpleCapitalize(p.set.specificity);
3399      if(p.set['edge'] || p.set['shift']) {
3400        p.date['beginningOf' + capitalized]();
3401      }
3402      if(p.set.specificity === 'month') {
3403        max = p.date.clone()['endOf' + capitalized]().getTime();
3404      }
3405      if(!override && p.set['sign'] && p.set.specificity != 'millisecond') {
3406        // If the time is relative, there can occasionally be an disparity between the relative date
3407        // and "now", which it is being compared to, so set an extra buffer to account for this.
3408        loBuffer = 50;
3409        hiBuffer = -50;
3410      }
3411    }
3412    t   = d.getTime();
3413    min = p.date.getTime();
3414    max = max || (min + accuracy);
3415    max = compensateForTimezoneTraversal(d, min, max);
3416    return t >= (min - loBuffer) && t <= (max + hiBuffer);
3417  }
3418
3419  function compensateForTimezoneTraversal(d, min, max) {
3420    var dMin, dMax, minOffset, maxOffset;
3421    dMin = new date(min);
3422    dMax = new date(max).utc(d.isUTC());
3423    if(callDateGet(dMax, 'Hours') !== 23) {
3424      minOffset = dMin.getTimezoneOffset();
3425      maxOffset = dMax.getTimezoneOffset();
3426      if(minOffset !== maxOffset) {
3427        max += (maxOffset - minOffset).minutes();
3428      }
3429    }
3430    return max;
3431  }
3432
3433  function updateDate(d, params, reset, advance, prefer) {
3434    var weekday, specificityIndex;
3435
3436    function getParam(key) {
3437      return isDefined(params[key]) ? params[key] : params[key + 's'];
3438    }
3439
3440    function paramExists(key) {
3441      return isDefined(getParam(key));
3442    }
3443
3444    function uniqueParamExists(key, isDay) {
3445      return paramExists(key) || (isDay && paramExists('weekday'));
3446    }
3447
3448    function canDisambiguate() {
3449      switch(prefer) {
3450        case -1: return d > getNewDate();
3451        case  1: return d < getNewDate();
3452      }
3453    }
3454
3455    if(isNumber(params) && advance) {
3456      // If param is a number and we're advancing, the number is presumed to be milliseconds.
3457      params = { 'milliseconds': params };
3458    } else if(isNumber(params)) {
3459      // Otherwise just set the timestamp and return.
3460      d.setTime(params);
3461      return d;
3462    }
3463
3464    // "date" can also be passed for the day
3465    if(isDefined(params['date'])) {
3466      params['day'] = params['date'];
3467    }
3468
3469    // Reset any unit lower than the least specific unit set. Do not do this for weeks
3470    // or for years. This needs to be performed before the acutal setting of the date
3471    // because the order needs to be reversed in order to get the lowest specificity,
3472    // also because higher order units can be overwritten by lower order units, such
3473    // as setting hour: 3, minute: 345, etc.
3474    iterateOverDateUnits(function(name, unit, i) {
3475      var isDay = name === 'day';
3476      if(uniqueParamExists(name, isDay)) {
3477        params.specificity = name;
3478        specificityIndex = +i;
3479        return false;
3480      } else if(reset && name !== 'week' && (!isDay || !paramExists('week'))) {
3481        // Days are relative to months, not weeks, so don't reset if a week exists.
3482        callDateSet(d, unit.method, (isDay ? 1 : 0));
3483      }
3484    });
3485
3486    // Now actually set or advance the date in order, higher units first.
3487    DateUnits.forEach(function(u, i) {
3488      var name = u.name, method = u.method, higherUnit = DateUnits[i - 1], value;
3489      value = getParam(name)
3490      if(isUndefined(value)) return;
3491      if(advance) {
3492        if(name === 'week') {
3493          value  = (params['day'] || 0) + (value * 7);
3494          method = 'Date';
3495        }
3496        value = (value * advance) + callDateGet(d, method);
3497      } else if(name === 'month' && paramExists('day')) {
3498        // When setting the month, there is a chance that we will traverse into a new month.
3499        // This happens in DST shifts, for example June 1st DST jumping to January 1st
3500        // (non-DST) will have a shift of -1:00 which will traverse into the previous year.
3501        // Prevent this by proactively setting the day when we know it will be set again anyway.
3502        // It can also happen when there are not enough days in the target month. This second
3503        // situation is identical to checkMonthTraversal below, however when we are advancing
3504        // we want to reset the date to "the last date in the target month". In the case of
3505        // DST shifts, however, we want to avoid the "edges" of months as that is where this
3506        // unintended traversal can happen. This is the reason for the different handling of
3507        // two similar but slightly different situations.
3508        //
3509        // TL;DR This method avoids the edges of a month IF not advancing and the date is going
3510        // to be set anyway, while checkMonthTraversal resets the date to the last day if advancing.
3511        //
3512        callDateSet(d, 'Date', 15);
3513      }
3514      callDateSet(d, method, value);
3515      if(advance && name === 'month') {
3516        checkMonthTraversal(d, value);
3517      }
3518    });
3519
3520
3521    // If a weekday is included in the params, set it ahead of time and set the params
3522    // to reflect the updated date so that resetting works properly.
3523    if(!advance && !paramExists('day') && paramExists('weekday')) {
3524      var weekday = getParam('weekday'), isAhead, futurePreferred;
3525      d.setWeekday(weekday);
3526    }
3527
3528    // If past or future is preferred, then the process of "disambiguation" will ensure that an
3529    // ambiguous time/date ("4pm", "thursday", "June", etc.) will be in the past or future.
3530    if(canDisambiguate()) {
3531      iterateOverDateUnits(function(name, unit) {
3532        var ambiguous = unit.ambiguous || (name === 'week' && paramExists('weekday'));
3533        if(ambiguous && !uniqueParamExists(name, name === 'day')) {
3534          d[unit.addMethod](prefer);
3535          return false;
3536        }
3537      }, specificityIndex + 1);
3538    }
3539    return d;
3540  }
3541
3542  // The ISO format allows times strung together without a demarcating ":", so make sure
3543  // that these markers are now optional.
3544  function prepareTime(format, loc, iso) {
3545    var timeSuffixMapping = {'h':0,'m':1,'s':2}, add;
3546    loc = loc || English;
3547    return format.replace(/{([a-z])}/g, function(full, token) {
3548      var separators = [],
3549          isHours = token === 'h',
3550          tokenIsRequired = isHours && !iso;
3551      if(token === 't') {
3552        return loc['ampm'].join('|');
3553      } else {
3554        if(isHours) {
3555          separators.push(':');
3556        }
3557        if(add = loc['timeSuffixes'][timeSuffixMapping[token]]) {
3558          separators.push(add + '\\s*');
3559        }
3560        return separators.length === 0 ? '' : '(?:' + separators.join('|') + ')' + (tokenIsRequired ? '' : '?');
3561      }
3562    });
3563  }
3564
3565
3566  // If the month is being set, then we don't want to accidentally
3567  // traverse into a new month just because the target month doesn't have enough
3568  // days. In other words, "5 months ago" from July 30th is still February, even
3569  // though there is no February 30th, so it will of necessity be February 28th
3570  // (or 29th in the case of a leap year).
3571
3572  function checkMonthTraversal(date, targetMonth) {
3573    if(targetMonth < 0) {
3574      targetMonth = targetMonth % 12 + 12;
3575    }
3576    if(targetMonth % 12 != callDateGet(date, 'Month')) {
3577      callDateSet(date, 'Date', 0);
3578    }
3579  }
3580
3581  function createDate(args, prefer, forceUTC) {
3582    var f, localeCode;
3583    if(isNumber(args[1])) {
3584      // If the second argument is a number, then we have an enumerated constructor type as in "new Date(2003, 2, 12);"
3585      f = collectDateArguments(args)[0];
3586    } else {
3587      f          = args[0];
3588      localeCode = args[1];
3589    }
3590    return getExtendedDate(f, localeCode, prefer, forceUTC).date;
3591  }
3592
3593  function invalidateDate(d) {
3594    d.setTime(NaN);
3595  }
3596
3597  function buildDateUnits() {
3598    DateUnitsReversed = DateUnits.concat().reverse();
3599    DateArgumentUnits = DateUnits.concat();
3600    DateArgumentUnits.splice(2,1);
3601  }
3602
3603
3604  /***
3605   * @method [units]Since([d], [locale] = currentLocale)
3606   * @returns Number
3607   * @short Returns the time since [d] in the appropriate unit.
3608   * @extra [d] will accept a date object, timestamp, or text format. If not specified, [d] is assumed to be now. [locale] can be passed to specify the locale that the date is in. %[unit]Ago% is provided as an alias to make this more readable when [d] is assumed to be the current date. For more see @date_format.
3609   *
3610   * @set
3611   *   millisecondsSince
3612   *   secondsSince
3613   *   minutesSince
3614   *   hoursSince
3615   *   daysSince
3616   *   weeksSince
3617   *   monthsSince
3618   *   yearsSince
3619   *
3620   * @example
3621   *
3622   *   Date.create().millisecondsSince('1 hour ago') -> 3,600,000
3623   *   Date.create().daysSince('1 week ago')         -> 7
3624   *   Date.create().yearsSince('15 years ago')      -> 15
3625   *   Date.create('15 years ago').yearsAgo()        -> 15
3626   *
3627   ***
3628   * @method [units]Ago()
3629   * @returns Number
3630   * @short Returns the time ago in the appropriate unit.
3631   *
3632   * @set
3633   *   millisecondsAgo
3634   *   secondsAgo
3635   *   minutesAgo
3636   *   hoursAgo
3637   *   daysAgo
3638   *   weeksAgo
3639   *   monthsAgo
3640   *   yearsAgo
3641   *
3642   * @example
3643   *
3644   *   Date.create('last year').millisecondsAgo() -> 3,600,000
3645   *   Date.create('last year').daysAgo()         -> 7
3646   *   Date.create('last year').yearsAgo()        -> 15
3647   *
3648   ***
3649   * @method [units]Until([d], [locale] = currentLocale)
3650   * @returns Number
3651   * @short Returns the time until [d] in the appropriate unit.
3652   * @extra [d] will accept a date object, timestamp, or text format. If not specified, [d] is assumed to be now. [locale] can be passed to specify the locale that the date is in. %[unit]FromNow% is provided as an alias to make this more readable when [d] is assumed to be the current date. For more see @date_format.
3653   *
3654   * @set
3655   *   millisecondsUntil
3656   *   secondsUntil
3657   *   minutesUntil
3658   *   hoursUntil
3659   *   daysUntil
3660   *   weeksUntil
3661   *   monthsUntil
3662   *   yearsUntil
3663   *
3664   * @example
3665   *
3666   *   Date.create().millisecondsUntil('1 hour from now') -> 3,600,000
3667   *   Date.create().daysUntil('1 week from now')         -> 7
3668   *   Date.create().yearsUntil('15 years from now')      -> 15
3669   *   Date.create('15 years from now').yearsFromNow()    -> 15
3670   *
3671   ***
3672   * @method [units]FromNow()
3673   * @returns Number
3674   * @short Returns the time from now in the appropriate unit.
3675   *
3676   * @set
3677   *   millisecondsFromNow
3678   *   secondsFromNow
3679   *   minutesFromNow
3680   *   hoursFromNow
3681   *   daysFromNow
3682   *   weeksFromNow
3683   *   monthsFromNow
3684   *   yearsFromNow
3685   *
3686   * @example
3687   *
3688   *   Date.create('next year').millisecondsFromNow() -> 3,600,000
3689   *   Date.create('next year').daysFromNow()         -> 7
3690   *   Date.create('next year').yearsFromNow()        -> 15
3691   *
3692   ***
3693   * @method add[Units](<num>, [reset] = false)
3694   * @returns Date
3695   * @short Adds <num> of the unit to the date. If [reset] is true, all lower units will be reset.
3696   * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Don't use %addMonths% if you need precision.
3697   *
3698   * @set
3699   *   addMilliseconds
3700   *   addSeconds
3701   *   addMinutes
3702   *   addHours
3703   *   addDays
3704   *   addWeeks
3705   *   addMonths
3706   *   addYears
3707   *
3708   * @example
3709   *
3710   *   Date.create().addMilliseconds(5) -> current time + 5 milliseconds
3711   *   Date.create().addDays(5)         -> current time + 5 days
3712   *   Date.create().addYears(5)        -> current time + 5 years
3713   *
3714   ***
3715   * @method isLast[Unit]()
3716   * @returns Boolean
3717   * @short Returns true if the date is last week/month/year.
3718   *
3719   * @set
3720   *   isLastWeek
3721   *   isLastMonth
3722   *   isLastYear
3723   *
3724   * @example
3725   *
3726   *   Date.create('yesterday').isLastWeek()  -> true or false?
3727   *   Date.create('yesterday').isLastMonth() -> probably not...
3728   *   Date.create('yesterday').isLastYear()  -> even less likely...
3729   *
3730   ***
3731   * @method isThis[Unit]()
3732   * @returns Boolean
3733   * @short Returns true if the date is this week/month/year.
3734   *
3735   * @set
3736   *   isThisWeek
3737   *   isThisMonth
3738   *   isThisYear
3739   *
3740   * @example
3741   *
3742   *   Date.create('tomorrow').isThisWeek()  -> true or false?
3743   *   Date.create('tomorrow').isThisMonth() -> probably...
3744   *   Date.create('tomorrow').isThisYear()  -> signs point to yes...
3745   *
3746   ***
3747   * @method isNext[Unit]()
3748   * @returns Boolean
3749   * @short Returns true if the date is next week/month/year.
3750   *
3751   * @set
3752   *   isNextWeek
3753   *   isNextMonth
3754   *   isNextYear
3755   *
3756   * @example
3757   *
3758   *   Date.create('tomorrow').isNextWeek()  -> true or false?
3759   *   Date.create('tomorrow').isNextMonth() -> probably not...
3760   *   Date.create('tomorrow').isNextYear()  -> even less likely...
3761   *
3762   ***
3763   * @method beginningOf[Unit]()
3764   * @returns Date
3765   * @short Sets the date to the beginning of the appropriate unit.
3766   *
3767   * @set
3768   *   beginningOfDay
3769   *   beginningOfWeek
3770   *   beginningOfMonth
3771   *   beginningOfYear
3772   *
3773   * @example
3774   *
3775   *   Date.create().beginningOfDay()   -> the beginning of today (resets the time)
3776   *   Date.create().beginningOfWeek()  -> the beginning of the week
3777   *   Date.create().beginningOfMonth() -> the beginning of the month
3778   *   Date.create().beginningOfYear()  -> the beginning of the year
3779   *
3780   ***
3781   * @method endOf[Unit]()
3782   * @returns Date
3783   * @short Sets the date to the end of the appropriate unit.
3784   *
3785   * @set
3786   *   endOfDay
3787   *   endOfWeek
3788   *   endOfMonth
3789   *   endOfYear
3790   *
3791   * @example
3792   *
3793   *   Date.create().endOfDay()   -> the end of today (sets the time to 23:59:59.999)
3794   *   Date.create().endOfWeek()  -> the end of the week
3795   *   Date.create().endOfMonth() -> the end of the month
3796   *   Date.create().endOfYear()  -> the end of the year
3797   *
3798   ***/
3799
3800  function buildDateMethods() {
3801    extendSimilar(date, true, true, DateUnits, function(methods, u, i) {
3802      var name = u.name, caps = simpleCapitalize(name), multiplier = u.multiplier(), since, until;
3803      u.addMethod = 'add' + caps + 's';
3804      // "since/until now" only count "past" an integer, i.e. "2 days ago" is
3805      // anything between 2 - 2.999 days. The default margin of error is 0.999,
3806      // but "months" have an inherently larger margin, as the number of days
3807      // in a given month may be significantly less than the number of days in
3808      // the average month, so for example "30 days" before March 15 may in fact
3809      // be 1 month ago. Years also have a margin of error due to leap years,
3810      // but this is roughly 0.999 anyway (365 / 365.25). Other units do not
3811      // technically need the error margin applied to them but this accounts
3812      // for discrepancies like (15).hoursAgo() which technically creates the
3813      // current date first, then creates a date 15 hours before and compares
3814      // them, the discrepancy between the creation of the 2 dates means that
3815      // they may actually be 15.0001 hours apart. Milliseconds don't have
3816      // fractions, so they won't be subject to this error margin.
3817      function applyErrorMargin(ms) {
3818        var num      = ms / multiplier,
3819            fraction = num % 1,
3820            error    = u.error || 0.999;
3821        if(fraction && abs(fraction % 1) > error) {
3822          num = round(num);
3823        }
3824        return num < 0 ? ceil(num) : floor(num);
3825      }
3826      since = function(f, localeCode) {
3827        return applyErrorMargin(this.getTime() - date.create(f, localeCode).getTime());
3828      };
3829      until = function(f, localeCode) {
3830        return applyErrorMargin(date.create(f, localeCode).getTime() - this.getTime());
3831      };
3832      methods[name+'sAgo']     = until;
3833      methods[name+'sUntil']   = until;
3834      methods[name+'sSince']   = since;
3835      methods[name+'sFromNow'] = since;
3836      methods[u.addMethod] = function(num, reset) {
3837        var set = {};
3838        set[name] = num;
3839        return this.advance(set, reset);
3840      };
3841      buildNumberToDateAlias(u, multiplier);
3842      if(i < 3) {
3843        ['Last','This','Next'].forEach(function(shift) {
3844          methods['is' + shift + caps] = function() {
3845            return compareDate(this, shift + ' ' + name, 'en');
3846          };
3847        });
3848      }
3849      if(i < 4) {
3850        methods['beginningOf' + caps] = function() {
3851          var set = {};
3852          switch(name) {
3853            case 'year':  set['year']    = callDateGet(this, 'FullYear'); break;
3854            case 'month': set['month']   = callDateGet(this, 'Month');    break;
3855            case 'day':   set['day']     = callDateGet(this, 'Date');     break;
3856            case 'week':  set['weekday'] = 0; break;
3857          }
3858          return this.set(set, true);
3859        };
3860        methods['endOf' + caps] = function() {
3861          var set = { 'hours': 23, 'minutes': 59, 'seconds': 59, 'milliseconds': 999 };
3862          switch(name) {
3863            case 'year':  set['month']   = 11; set['day'] = 31; break;
3864            case 'month': set['day']     = this.daysInMonth();  break;
3865            case 'week':  set['weekday'] = 6;                   break;
3866          }
3867          return this.set(set, true);
3868        };
3869      }
3870    });
3871  }
3872
3873  function buildCoreInputFormats() {
3874    English.addFormat('([+-])?(\\d{4,4})[-.]?{full_month}[-.]?(\\d{1,2})?', true, ['year_sign','year','month','date'], false, true);
3875    English.addFormat('(\\d{1,2})[-.\\/]{full_month}(?:[-.\\/](\\d{2,4}))?', true, ['date','month','year'], true);
3876    English.addFormat('{full_month}[-.](\\d{4,4})', false, ['month','year']);
3877    English.addFormat('\\/Date\\((\\d+(?:[+-]\\d{4,4})?)\\)\\/', false, ['timestamp'])
3878    English.addFormat(prepareTime(RequiredTime, English), false, TimeFormat)
3879
3880    // When a new locale is initialized it will have the CoreDateFormats initialized by default.
3881    // From there, adding new formats will push them in front of the previous ones, so the core
3882    // formats will be the last to be reached. However, the core formats themselves have English
3883    // months in them, which means that English needs to first be initialized and creates a race
3884    // condition. I'm getting around this here by adding these generalized formats in the order
3885    // specific -> general, which will mean they will be added to the English localization in
3886    // general -> specific order, then chopping them off the front and reversing to get the correct
3887    // order. Note that there are 7 formats as 2 have times which adds a front and a back format.
3888    CoreDateFormats = English.compiledFormats.slice(0,7).reverse();
3889    English.compiledFormats = English.compiledFormats.slice(7).concat(CoreDateFormats);
3890  }
3891
3892  function buildFormatTokens() {
3893
3894    createPaddedToken('f', function(d) {
3895      return callDateGet(d, 'Milliseconds');
3896    }, true);
3897
3898    createPaddedToken('s', function(d) {
3899      return callDateGet(d, 'Seconds');
3900    });
3901
3902    createPaddedToken('m', function(d) {
3903      return callDateGet(d, 'Minutes');
3904    });
3905
3906    createPaddedToken('h', function(d) {
3907      return callDateGet(d, 'Hours') % 12 || 12;
3908    });
3909
3910    createPaddedToken('H', function(d) {
3911      return callDateGet(d, 'Hours');
3912    });
3913
3914    createPaddedToken('d', function(d) {
3915      return callDateGet(d, 'Date');
3916    });
3917
3918    createPaddedToken('M', function(d) {
3919      return callDateGet(d, 'Month') + 1;
3920    });
3921
3922    createMeridianTokens();
3923    createWeekdayTokens();
3924    createMonthTokens();
3925
3926    // Aliases
3927    DateFormatTokens['ms']           = DateFormatTokens['f'];
3928    DateFormatTokens['milliseconds'] = DateFormatTokens['f'];
3929    DateFormatTokens['seconds']      = DateFormatTokens['s'];
3930    DateFormatTokens['minutes']      = DateFormatTokens['m'];
3931    DateFormatTokens['hours']        = DateFormatTokens['h'];
3932    DateFormatTokens['24hr']         = DateFormatTokens['H'];
3933    DateFormatTokens['12hr']         = DateFormatTokens['h'];
3934    DateFormatTokens['date']         = DateFormatTokens['d'];
3935    DateFormatTokens['day']          = DateFormatTokens['d'];
3936    DateFormatTokens['year']         = DateFormatTokens['yyyy'];
3937
3938  }
3939
3940  function buildFormatShortcuts() {
3941    extendSimilar(date, true, true, 'short,long,full', function(methods, name) {
3942      methods[name] = function(localeCode) {
3943        return formatDate(this, name, false, localeCode);
3944      }
3945    });
3946  }
3947
3948  function buildAsianDigits() {
3949    KanjiDigits.split('').forEach(function(digit, value) {
3950      var holder;
3951      if(value > 9) {
3952        value = pow(10, value - 9);
3953      }
3954      AsianDigitMap[digit] = value;
3955    });
3956    simpleMerge(AsianDigitMap, NumberNormalizeMap);
3957    // Kanji numerals may also be included in phrases which are text-based rather
3958    // than actual numbers such as Chinese weekdays (上周三), and "the day before
3959    // yesterday" (一昨日) in Japanese, so don't match these.
3960    AsianDigitReg = regexp('([期週周])?([' + KanjiDigits + FullWidthDigits + ']+)(?!昨)', 'g');
3961  }
3962
3963   /***
3964   * @method is[Day]()
3965   * @returns Boolean
3966   * @short Returns true if the date falls on that day.
3967   * @extra Also available: %isYesterday%, %isToday%, %isTomorrow%, %isWeekday%, and %isWeekend%.
3968   *
3969   * @set
3970   *   isToday
3971   *   isYesterday
3972   *   isTomorrow
3973   *   isWeekday
3974   *   isWeekend
3975   *   isSunday
3976   *   isMonday
3977   *   isTuesday
3978   *   isWednesday
3979   *   isThursday
3980   *   isFriday
3981   *   isSaturday
3982   *
3983   * @example
3984   *
3985   *   Date.create('tomorrow').isToday() -> false
3986   *   Date.create('thursday').isTomorrow() -> ?
3987   *   Date.create('yesterday').isWednesday() -> ?
3988   *   Date.create('today').isWeekend() -> ?
3989   *
3990   ***
3991   * @method isFuture()
3992   * @returns Boolean
3993   * @short Returns true if the date is in the future.
3994   * @example
3995   *
3996   *   Date.create('next week').isFuture() -> true
3997   *   Date.create('last week').isFuture() -> false
3998   *
3999   ***
4000   * @method isPast()
4001   * @returns Boolean
4002   * @short Returns true if the date is in the past.
4003   * @example
4004   *
4005   *   Date.create('last week').isPast() -> true
4006   *   Date.create('next week').isPast() -> false
4007   *
4008   ***/
4009  function buildRelativeAliases() {
4010    var special  = 'today,yesterday,tomorrow,weekday,weekend,future,past'.split(',');
4011    var weekdays = English['weekdays'].slice(0,7);
4012    var months   = English['months'].slice(0,12);
4013    extendSimilar(date, true, true, special.concat(weekdays).concat(months), function(methods, name) {
4014      methods['is'+ simpleCapitalize(name)] = function(utc) {
4015       return this.is(name, 0, utc);
4016      };
4017    });
4018  }
4019
4020  function buildUTCAliases() {
4021    // Don't want to use extend here as it will override
4022    // the actual "utc" method on the prototype.
4023    if(date['utc']) return;
4024    date['utc'] = {
4025
4026        'create': function() {
4027          return createDate(arguments, 0, true);
4028        },
4029
4030        'past': function() {
4031          return createDate(arguments, -1, true);
4032        },
4033
4034        'future': function() {
4035          return createDate(arguments, 1, true);
4036        }
4037    };
4038  }
4039
4040  function setDateProperties() {
4041    extend(date, false , true, {
4042      'RFC1123': '{Dow}, {dd} {Mon} {yyyy} {HH}:{mm}:{ss} {tz}',
4043      'RFC1036': '{Weekday}, {dd}-{Mon}-{yy} {HH}:{mm}:{ss} {tz}',
4044      'ISO8601_DATE': '{yyyy}-{MM}-{dd}',
4045      'ISO8601_DATETIME': '{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{fff}{isotz}'
4046    });
4047  }
4048
4049
4050  extend(date, false, true, {
4051
4052     /***
4053     * @method Date.create(<d>, [locale] = currentLocale)
4054     * @returns Date
4055     * @short Alternate Date constructor which understands many different text formats, a timestamp, or another date.
4056     * @extra If no argument is given, date is assumed to be now. %Date.create% additionally can accept enumerated parameters as with the standard date constructor. [locale] can be passed to specify the locale that the date is in. When unspecified, the current locale (default is English) is assumed. UTC-based dates can be created through the %utc% object. For more see @date_format.
4057     * @set
4058     *   Date.utc.create
4059     *
4060     * @example
4061     *
4062     *   Date.create('July')          -> July of this year
4063     *   Date.create('1776')          -> 1776
4064     *   Date.create('today')         -> today
4065     *   Date.create('wednesday')     -> This wednesday
4066     *   Date.create('next friday')   -> Next friday
4067     *   Date.create('July 4, 1776')  -> July 4, 1776
4068     *   Date.create(-446806800000)   -> November 5, 1955
4069     *   Date.create(1776, 6, 4)      -> July 4, 1776
4070     *   Date.create('1776年07月04日', 'ja') -> July 4, 1776
4071     *   Date.utc.create('July 4, 1776', 'en')  -> July 4, 1776
4072     *
4073     ***/
4074    'create': function() {
4075      return createDate(arguments);
4076    },
4077
4078     /***
4079     * @method Date.past(<d>, [locale] = currentLocale)
4080     * @returns Date
4081     * @short Alternate form of %Date.create% with any ambiguity assumed to be the past.
4082     * @extra For example %"Sunday"% can be either "the Sunday coming up" or "the Sunday last" depending on context. Note that dates explicitly in the future ("next Sunday") will remain in the future. This method simply provides a hint when ambiguity exists. UTC-based dates can be created through the %utc% object. For more, see @date_format.
4083     * @set
4084     *   Date.utc.past
4085     *
4086     * @example
4087     *
4088     *   Date.past('July')          -> July of this year or last depending on the current month
4089     *   Date.past('Wednesday')     -> This wednesday or last depending on the current weekday
4090     *
4091     ***/
4092    'past': function() {
4093      return createDate(arguments, -1);
4094    },
4095
4096     /***
4097     * @method Date.future(<d>, [locale] = currentLocale)
4098     * @returns Date
4099     * @short Alternate form of %Date.create% with any ambiguity assumed to be the future.
4100     * @extra For example %"Sunday"% can be either "the Sunday coming up" or "the Sunday last" depending on context. Note that dates explicitly in the past ("last Sunday") will remain in the past. This method simply provides a hint when ambiguity exists. UTC-based dates can be created through the %utc% object. For more, see @date_format.
4101     * @set
4102     *   Date.utc.future
4103     *
4104     * @example
4105     *
4106     *   Date.future('July')          -> July of this year or next depending on the current month
4107     *   Date.future('Wednesday')     -> This wednesday or next depending on the current weekday
4108     *
4109     ***/
4110    'future': function() {
4111      return createDate(arguments, 1);
4112    },
4113
4114     /***
4115     * @method Date.addLocale(<code>, <set>)
4116     * @returns Locale
4117     * @short Adds a locale <set> to the locales understood by Sugar.
4118     * @extra For more see @date_format.
4119     *
4120     ***/
4121    'addLocale': function(localeCode, set) {
4122      return setLocalization(localeCode, set);
4123    },
4124
4125     /***
4126     * @method Date.setLocale(<code>)
4127     * @returns Locale
4128     * @short Sets the current locale to be used with dates.
4129     * @extra Sugar has support for 13 locales that are available through the "Date Locales" package. In addition you can define a new locale with %Date.addLocale%. For more see @date_format.
4130     *
4131     ***/
4132    'setLocale': function(localeCode, set) {
4133      var loc = getLocalization(localeCode, false);
4134      CurrentLocalization = loc;
4135      // The code is allowed to be more specific than the codes which are required:
4136      // i.e. zh-CN or en-US. Currently this only affects US date variants such as 8/10/2000.
4137      if(localeCode && localeCode != loc['code']) {
4138        loc['code'] = localeCode;
4139      }
4140      return loc;
4141    },
4142
4143     /***
4144     * @method Date.getLocale([code] = current)
4145     * @returns Locale
4146     * @short Gets the locale for the given code, or the current locale.
4147     * @extra The resulting locale object can be manipulated to provide more control over date localizations. For more about locales, see @date_format.
4148     *
4149     ***/
4150    'getLocale': function(localeCode) {
4151      return !localeCode ? CurrentLocalization : getLocalization(localeCode, false);
4152    },
4153
4154     /**
4155     * @method Date.addFormat(<format>, <match>, [code] = null)
4156     * @returns Nothing
4157     * @short Manually adds a new date input format.
4158     * @extra This method allows fine grained control for alternate formats. <format> is a string that can have regex tokens inside. <match> is an array of the tokens that each regex capturing group will map to, for example %year%, %date%, etc. For more, see @date_format.
4159     *
4160     **/
4161    'addFormat': function(format, match, localeCode) {
4162      addDateInputFormat(getLocalization(localeCode), format, match);
4163    }
4164
4165  });
4166
4167  extend(date, true, true, {
4168
4169     /***
4170     * @method set(<set>, [reset] = false)
4171     * @returns Date
4172     * @short Sets the date object.
4173     * @extra This method can accept multiple formats including a single number as a timestamp, an object, or enumerated parameters (as with the Date constructor). If [reset] is %true%, any units more specific than those passed will be reset.
4174     *
4175     * @example
4176     *
4177     *   new Date().set({ year: 2011, month: 11, day: 31 }) -> December 31, 2011
4178     *   new Date().set(2011, 11, 31)                       -> December 31, 2011
4179     *   new Date().set(86400000)                           -> 1 day after Jan 1, 1970
4180     *   new Date().set({ year: 2004, month: 6 }, true)     -> June 1, 2004, 00:00:00.000
4181     *
4182     ***/
4183    'set': function() {
4184      var args = collectDateArguments(arguments);
4185      return updateDate(this, args[0], args[1])
4186    },
4187
4188     /***
4189     * @method setWeekday()
4190     * @returns Nothing
4191     * @short Sets the weekday of the date.
4192     * @extra In order to maintain a parallel with %getWeekday% (which itself is an alias for Javascript native %getDay%), Sunday is considered day %0%. This contrasts with ISO-8601 standard (used in %getISOWeek% and %setISOWeek%) which places Sunday at the end of the week (day 7). This effectively means that passing %0% to this method while in the middle of a week will rewind the date, where passing %7% will advance it.
4193     *
4194     * @example
4195     *
4196     *   d = new Date(); d.setWeekday(1); d; -> Monday of this week
4197     *   d = new Date(); d.setWeekday(6); d; -> Saturday of this week
4198     *
4199     ***/
4200    'setWeekday': function(dow) {
4201      if(isUndefined(dow)) return;
4202      return callDateSet(this, 'Date', callDateGet(this, 'Date') + dow - callDateGet(this, 'Day'));
4203    },
4204
4205     /***
4206     * @method setISOWeek()
4207     * @returns Nothing
4208     * @short Sets the week (of the year) as defined by the ISO-8601 standard.
4209     * @extra Note that this standard places Sunday at the end of the week (day 7).
4210     *
4211     * @example
4212     *
4213     *   d = new Date(); d.setISOWeek(15); d; -> 15th week of the year
4214     *
4215     ***/
4216    'setISOWeek': function(week) {
4217      var weekday = callDateGet(this, 'Day') || 7;
4218      if(isUndefined(week)) return;
4219      this.set({ 'month': 0, 'date': 4 });
4220      this.set({ 'weekday': 1 });
4221      if(week > 1) {
4222        this.addWeeks(week - 1);
4223      }
4224      if(weekday !== 1) {
4225        this.advance({ 'days': weekday - 1 });
4226      }
4227      return this.getTime();
4228    },
4229
4230     /***
4231     * @method getISOWeek()
4232     * @returns Number
4233     * @short Gets the date's week (of the year) as defined by the ISO-8601 standard.
4234     * @extra Note that this standard places Sunday at the end of the week (day 7). If %utc% is set on the date, the week will be according to UTC time.
4235     *
4236     * @example
4237     *
4238     *   new Date().getISOWeek()    -> today's week of the year
4239     *
4240     ***/
4241    'getISOWeek': function() {
4242      return getWeekNumber(this);
4243    },
4244
4245     /***
4246     * @method beginningOfISOWeek()
4247     * @returns Date
4248     * @short Set the date to the beginning of week as defined by this ISO-8601 standard.
4249     * @extra Note that this standard places Monday at the start of the week.
4250     * @example
4251     *
4252     *   Date.create().beginningOfISOWeek() -> Monday
4253     *
4254     ***/
4255    'beginningOfISOWeek': function() {
4256      var day = this.getDay();
4257      if(day === 0) {
4258        day = -6;
4259      } else if(day !== 1) {
4260        day = 1;
4261      }
4262      this.setWeekday(day);
4263      return this.reset();
4264    },
4265
4266     /***
4267     * @method endOfISOWeek()
4268     * @returns Date
4269     * @short Set the date to the end of week as defined by this ISO-8601 standard.
4270     * @extra Note that this standard places Sunday at the end of the week.
4271     * @example
4272     *
4273     *   Date.create().endOfISOWeek() -> Sunday
4274     *
4275     ***/
4276    'endOfISOWeek': function() {
4277      if(this.getDay() !== 0) {
4278        this.setWeekday(7);
4279      }
4280      return this.endOfDay()
4281    },
4282
4283     /***
4284     * @method getUTCOffset([iso])
4285     * @returns String
4286     * @short Returns a string representation of the offset from UTC time. If [iso] is true the offset will be in ISO8601 format.
4287     * @example
4288     *
4289     *   new Date().getUTCOffset()     -> "+0900"
4290     *   new Date().getUTCOffset(true) -> "+09:00"
4291     *
4292     ***/
4293    'getUTCOffset': function(iso) {
4294      var offset = this._utc ? 0 : this.getTimezoneOffset();
4295      var colon  = iso === true ? ':' : '';
4296      if(!offset && iso) return 'Z';
4297      return padNumber(floor(-offset / 60), 2, true) + colon + padNumber(abs(offset % 60), 2);
4298    },
4299
4300     /***
4301     * @method utc([on] = true)
4302     * @returns Date
4303     * @short Sets the internal utc flag for the date. When on, UTC-based methods will be called internally.
4304     * @extra For more see @date_format.
4305     * @example
4306     *
4307     *   new Date().utc(true)
4308     *   new Date().utc(false)
4309     *
4310     ***/
4311    'utc': function(set) {
4312      defineProperty(this, '_utc', set === true || arguments.length === 0);
4313      return this;
4314    },
4315
4316     /***
4317     * @method isUTC()
4318     * @returns Boolean
4319     * @short Returns true if the date has no timezone offset.
4320     * @extra This will also return true for utc-based dates (dates that have the %utc% method set true). Note that even if the utc flag is set, %getTimezoneOffset% will always report the same thing as Javascript always reports that based on the environment's locale.
4321     * @example
4322     *
4323     *   new Date().isUTC()           -> true or false?
4324     *   new Date().utc(true).isUTC() -> true
4325     *
4326     ***/
4327    'isUTC': function() {
4328      return !!this._utc || this.getTimezoneOffset() === 0;
4329    },
4330
4331     /***
4332     * @method advance(<set>, [reset] = false)
4333     * @returns Date
4334     * @short Sets the date forward.
4335     * @extra This method can accept multiple formats including an object, a string in the format %3 days%, a single number as milliseconds, or enumerated parameters (as with the Date constructor). If [reset] is %true%, any units more specific than those passed will be reset. For more see @date_format.
4336     * @example
4337     *
4338     *   new Date().advance({ year: 2 }) -> 2 years in the future
4339     *   new Date().advance('2 days')    -> 2 days in the future
4340     *   new Date().advance(0, 2, 3)     -> 2 months 3 days in the future
4341     *   new Date().advance(86400000)    -> 1 day in the future
4342     *
4343     ***/
4344    'advance': function() {
4345      var args = collectDateArguments(arguments, true);
4346      return updateDate(this, args[0], args[1], 1);
4347    },
4348
4349     /***
4350     * @method rewind(<set>, [reset] = false)
4351     * @returns Date
4352     * @short Sets the date back.
4353     * @extra This method can accept multiple formats including a single number as a timestamp, an object, or enumerated parameters (as with the Date constructor). If [reset] is %true%, any units more specific than those passed will be reset. For more see @date_format.
4354     * @example
4355     *
4356     *   new Date().rewind({ year: 2 }) -> 2 years in the past
4357     *   new Date().rewind(0, 2, 3)     -> 2 months 3 days in the past
4358     *   new Date().rewind(86400000)    -> 1 day in the past
4359     *
4360     ***/
4361    'rewind': function() {
4362      var args = collectDateArguments(arguments, true);
4363      return updateDate(this, args[0], args[1], -1);
4364    },
4365
4366     /***
4367     * @method isValid()
4368     * @returns Boolean
4369     * @short Returns true if the date is valid.
4370     * @example
4371     *
4372     *   new Date().isValid()         -> true
4373     *   new Date('flexor').isValid() -> false
4374     *
4375     ***/
4376    'isValid': function() {
4377      return !isNaN(this.getTime());
4378    },
4379
4380     /***
4381     * @method isAfter(<d>, [margin] = 0)
4382     * @returns Boolean
4383     * @short Returns true if the date is after the <d>.
4384     * @extra [margin] is to allow extra margin of error (in ms). <d> will accept a date object, timestamp, or text format. If not specified, <d> is assumed to be now. See @date_format for more.
4385     * @example
4386     *
4387     *   new Date().isAfter('tomorrow')  -> false
4388     *   new Date().isAfter('yesterday') -> true
4389     *
4390     ***/
4391    'isAfter': function(d, margin, utc) {
4392      return this.getTime() > date.create(d).getTime() - (margin || 0);
4393    },
4394
4395     /***
4396     * @method isBefore(<d>, [margin] = 0)
4397     * @returns Boolean
4398     * @short Returns true if the date is before <d>.
4399     * @extra [margin] is to allow extra margin of error (in ms). <d> will accept a date object, timestamp, or text format. If not specified, <d> is assumed to be now. See @date_format for more.
4400     * @example
4401     *
4402     *   new Date().isBefore('tomorrow')  -> true
4403     *   new Date().isBefore('yesterday') -> false
4404     *
4405     ***/
4406    'isBefore': function(d, margin) {
4407      return this.getTime() < date.create(d).getTime() + (margin || 0);
4408    },
4409
4410     /***
4411     * @method isBetween(<d1>, <d2>, [margin] = 0)
4412     * @returns Boolean
4413     * @short Returns true if the date falls between <d1> and <d2>.
4414     * @extra [margin] is to allow extra margin of error (in ms). <d1> and <d2> will accept a date object, timestamp, or text format. If not specified, they are assumed to be now. See @date_format for more.
4415     * @example
4416     *
4417     *   new Date().isBetween('yesterday', 'tomorrow')    -> true
4418     *   new Date().isBetween('last year', '2 years ago') -> false
4419     *
4420     ***/
4421    'isBetween': function(d1, d2, margin) {
4422      var t  = this.getTime();
4423      var t1 = date.create(d1).getTime();
4424      var t2 = date.create(d2).getTime();
4425      var lo = min(t1, t2);
4426      var hi = max(t1, t2);
4427      margin = margin || 0;
4428      return (lo - margin < t) && (hi + margin > t);
4429    },
4430
4431     /***
4432     * @method isLeapYear()
4433     * @returns Boolean
4434     * @short Returns true if the date is a leap year.
4435     * @example
4436     *
4437     *   Date.create('2000').isLeapYear() -> true
4438     *
4439     ***/
4440    'isLeapYear': function() {
4441      var year = callDateGet(this, 'FullYear');
4442      return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
4443    },
4444
4445     /***
4446     * @method daysInMonth()
4447     * @returns Number
4448     * @short Returns the number of days in the date's month.
4449     * @example
4450     *
4451     *   Date.create('May').daysInMonth()            -> 31
4452     *   Date.create('February, 2000').daysInMonth() -> 29
4453     *
4454     ***/
4455    'daysInMonth': function() {
4456      return 32 - callDateGet(new date(callDateGet(this, 'FullYear'), callDateGet(this, 'Month'), 32), 'Date');
4457    },
4458
4459     /***
4460     * @method format(<format>, [locale] = currentLocale)
4461     * @returns String
4462     * @short Formats and outputs the date.
4463     * @extra <format> can be a number of pre-determined formats or a string of tokens. Locale-specific formats are %short%, %long%, and %full% which have their own aliases and can be called with %date.short()%, etc. If <format> is not specified the %long% format is assumed. [locale] specifies a locale code to use (if not specified the current locale is used). See @date_format for more details.
4464     *
4465     * @set
4466     *   short
4467     *   long
4468     *   full
4469     *
4470     * @example
4471     *
4472     *   Date.create().format()                                   -> ex. July 4, 2003
4473     *   Date.create().format('{Weekday} {d} {Month}, {yyyy}')    -> ex. Monday July 4, 2003
4474     *   Date.create().format('{hh}:{mm}')                        -> ex. 15:57
4475     *   Date.create().format('{12hr}:{mm}{tt}')                  -> ex. 3:57pm
4476     *   Date.create().format(Date.ISO8601_DATETIME)              -> ex. 2011-07-05 12:24:55.528Z
4477     *   Date.create('last week').format('short', 'ja')                -> ex. 先週
4478     *   Date.create('yesterday').format(function(value,unit,ms,loc) {
4479     *     // value = 1, unit = 3, ms = -86400000, loc = [current locale object]
4480     *   });                                                      -> ex. 1 day ago
4481     *
4482     ***/
4483    'format': function(f, localeCode) {
4484      return formatDate(this, f, false, localeCode);
4485    },
4486
4487     /***
4488     * @method relative([fn], [locale] = currentLocale)
4489     * @returns String
4490     * @short Returns a relative date string offset to the current time.
4491     * @extra [fn] can be passed to provide for more granular control over the resulting string. [fn] is passed 4 arguments: the adjusted value, unit, offset in milliseconds, and a localization object. As an alternate syntax, [locale] can also be passed as the first (and only) parameter. For more, see @date_format.
4492     * @example
4493     *
4494     *   Date.create('90 seconds ago').relative() -> 1 minute ago
4495     *   Date.create('January').relative()        -> ex. 5 months ago
4496     *   Date.create('January').relative('ja')    -> 3ヶ月前
4497     *   Date.create('120 minutes ago').relative(function(val,unit,ms,loc) {
4498     *     // value = 2, unit = 3, ms = -7200, loc = [current locale object]
4499     *   });                                      -> ex. 5 months ago
4500     *
4501     ***/
4502    'relative': function(fn, localeCode) {
4503      if(isString(fn)) {
4504        localeCode = fn;
4505        fn = null;
4506      }
4507      return formatDate(this, fn, true, localeCode);
4508    },
4509
4510     /***
4511     * @method is(<d>, [margin] = 0)
4512     * @returns Boolean
4513     * @short Returns true if the date is <d>.
4514     * @extra <d> will accept a date object, timestamp, or text format. %is% additionally understands more generalized expressions like month/weekday names, 'today', etc, and compares to the precision implied in <d>. [margin] allows an extra margin of error in milliseconds.  For more, see @date_format.
4515     * @example
4516     *
4517     *   Date.create().is('July')               -> true or false?
4518     *   Date.create().is('1776')               -> false
4519     *   Date.create().is('today')              -> true
4520     *   Date.create().is('weekday')            -> true or false?
4521     *   Date.create().is('July 4, 1776')       -> false
4522     *   Date.create().is(-6106093200000)       -> false
4523     *   Date.create().is(new Date(1776, 6, 4)) -> false
4524     *
4525     ***/
4526    'is': function(d, margin, utc) {
4527      var tmp, comp;
4528      if(!this.isValid()) return;
4529      if(isString(d)) {
4530        d = d.trim().toLowerCase();
4531        comp = this.clone().utc(utc);
4532        switch(true) {
4533          case d === 'future':  return this.getTime() > getNewDate().getTime();
4534          case d === 'past':    return this.getTime() < getNewDate().getTime();
4535          case d === 'weekday': return callDateGet(comp, 'Day') > 0 && callDateGet(comp, 'Day') < 6;
4536          case d === 'weekend': return callDateGet(comp, 'Day') === 0 || callDateGet(comp, 'Day') === 6;
4537          case (tmp = English['weekdays'].indexOf(d) % 7) > -1: return callDateGet(comp, 'Day') === tmp;
4538          case (tmp = English['months'].indexOf(d) % 12) > -1:  return callDateGet(comp, 'Month') === tmp;
4539        }
4540      }
4541      return compareDate(this, d, null, margin, utc);
4542    },
4543
4544     /***
4545     * @method reset([unit] = 'hours')
4546     * @returns Date
4547     * @short Resets the unit passed and all smaller units. Default is "hours", effectively resetting the time.
4548     * @example
4549     *
4550     *   Date.create().reset('day')   -> Beginning of today
4551     *   Date.create().reset('month') -> 1st of the month
4552     *
4553     ***/
4554    'reset': function(unit) {
4555      var params = {}, recognized;
4556      unit = unit || 'hours';
4557      if(unit === 'date') unit = 'days';
4558      recognized = DateUnits.some(function(u) {
4559        return unit === u.name || unit === u.name + 's';
4560      });
4561      params[unit] = unit.match(/^days?/) ? 1 : 0;
4562      return recognized ? this.set(params, true) : this;
4563    },
4564
4565     /***
4566     * @method clone()
4567     * @returns Date
4568     * @short Clones the date.
4569     * @example
4570     *
4571     *   Date.create().clone() -> Copy of now
4572     *
4573     ***/
4574    'clone': function() {
4575      var d = new date(this.getTime());
4576      d.utc(!!this._utc);
4577      return d;
4578    }
4579
4580  });
4581
4582
4583  // Instance aliases
4584  extend(date, true, true, {
4585
4586     /***
4587     * @method iso()
4588     * @alias toISOString
4589     *
4590     ***/
4591    'iso': function() {
4592      return this.toISOString();
4593    },
4594
4595     /***
4596     * @method getWeekday()
4597     * @returns Number
4598     * @short Alias for %getDay%.
4599     * @set
4600     *   getUTCWeekday
4601     *
4602     * @example
4603     *
4604     +   Date.create().getWeekday();    -> (ex.) 3
4605     +   Date.create().getUTCWeekday();    -> (ex.) 3
4606     *
4607     ***/
4608    'getWeekday':    date.prototype.getDay,
4609    'getUTCWeekday':    date.prototype.getUTCDay
4610
4611  });
4612
4613
4614
4615  /***
4616   * Number module
4617   *
4618   ***/
4619
4620  /***
4621   * @method [unit]()
4622   * @returns Number
4623   * @short Takes the number as a corresponding unit of time and converts to milliseconds.
4624   * @extra Method names can be singular or plural.  Note that as "a month" is ambiguous as a unit of time, %months% will be equivalent to 30.4375 days, the average number in a month. Be careful using %months% if you need exact precision.
4625   *
4626   * @set
4627   *   millisecond
4628   *   milliseconds
4629   *   second
4630   *   seconds
4631   *   minute
4632   *   minutes
4633   *   hour
4634   *   hours
4635   *   day
4636   *   days
4637   *   week
4638   *   weeks
4639   *   month
4640   *   months
4641   *   year
4642   *   years
4643   *
4644   * @example
4645   *
4646   *   (5).milliseconds() -> 5
4647   *   (10).hours()       -> 36000000
4648   *   (1).day()          -> 86400000
4649   *
4650   ***
4651   * @method [unit]Before([d], [locale] = currentLocale)
4652   * @returns Date
4653   * @short Returns a date that is <n> units before [d], where <n> is the number.
4654   * @extra [d] will accept a date object, timestamp, or text format. Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsBefore% if you need exact precision. See @date_format for more.
4655   *
4656   * @set
4657   *   millisecondBefore
4658   *   millisecondsBefore
4659   *   secondBefore
4660   *   secondsBefore
4661   *   minuteBefore
4662   *   minutesBefore
4663   *   hourBefore
4664   *   hoursBefore
4665   *   dayBefore
4666   *   daysBefore
4667   *   weekBefore
4668   *   weeksBefore
4669   *   monthBefore
4670   *   monthsBefore
4671   *   yearBefore
4672   *   yearsBefore
4673   *
4674   * @example
4675   *
4676   *   (5).daysBefore('tuesday')          -> 5 days before tuesday of this week
4677   *   (1).yearBefore('January 23, 1997') -> January 23, 1996
4678   *
4679   ***
4680   * @method [unit]Ago()
4681   * @returns Date
4682   * @short Returns a date that is <n> units ago.
4683   * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsAgo% if you need exact precision.
4684   *
4685   * @set
4686   *   millisecondAgo
4687   *   millisecondsAgo
4688   *   secondAgo
4689   *   secondsAgo
4690   *   minuteAgo
4691   *   minutesAgo
4692   *   hourAgo
4693   *   hoursAgo
4694   *   dayAgo
4695   *   daysAgo
4696   *   weekAgo
4697   *   weeksAgo
4698   *   monthAgo
4699   *   monthsAgo
4700   *   yearAgo
4701   *   yearsAgo
4702   *
4703   * @example
4704   *
4705   *   (5).weeksAgo() -> 5 weeks ago
4706   *   (1).yearAgo()  -> January 23, 1996
4707   *
4708   ***
4709   * @method [unit]After([d], [locale] = currentLocale)
4710   * @returns Date
4711   * @short Returns a date <n> units after [d], where <n> is the number.
4712   * @extra [d] will accept a date object, timestamp, or text format. Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsAfter% if you need exact precision. See @date_format for more.
4713   *
4714   * @set
4715   *   millisecondAfter
4716   *   millisecondsAfter
4717   *   secondAfter
4718   *   secondsAfter
4719   *   minuteAfter
4720   *   minutesAfter
4721   *   hourAfter
4722   *   hoursAfter
4723   *   dayAfter
4724   *   daysAfter
4725   *   weekAfter
4726   *   weeksAfter
4727   *   monthAfter
4728   *   monthsAfter
4729   *   yearAfter
4730   *   yearsAfter
4731   *
4732   * @example
4733   *
4734   *   (5).daysAfter('tuesday')          -> 5 days after tuesday of this week
4735   *   (1).yearAfter('January 23, 1997') -> January 23, 1998
4736   *
4737   ***
4738   * @method [unit]FromNow()
4739   * @returns Date
4740   * @short Returns a date <n> units from now.
4741   * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsFromNow% if you need exact precision.
4742   *
4743   * @set
4744   *   millisecondFromNow
4745   *   millisecondsFromNow
4746   *   secondFromNow
4747   *   secondsFromNow
4748   *   minuteFromNow
4749   *   minutesFromNow
4750   *   hourFromNow
4751   *   hoursFromNow
4752   *   dayFromNow
4753   *   daysFromNow
4754   *   weekFromNow
4755   *   weeksFromNow
4756   *   monthFromNow
4757   *   monthsFromNow
4758   *   yearFromNow
4759   *   yearsFromNow
4760   *
4761   * @example
4762   *
4763   *   (5).weeksFromNow() -> 5 weeks ago
4764   *   (1).yearFromNow()  -> January 23, 1998
4765   *
4766   ***/
4767  function buildNumberToDateAlias(u, multiplier) {
4768    var name = u.name, methods = {};
4769    function base() { return round(this * multiplier); }
4770    function after() { return createDate(arguments)[u.addMethod](this);  }
4771    function before() { return createDate(arguments)[u.addMethod](-this); }
4772    methods[name] = base;
4773    methods[name + 's'] = base;
4774    methods[name + 'Before'] = before;
4775    methods[name + 'sBefore'] = before;
4776    methods[name + 'Ago'] = before;
4777    methods[name + 'sAgo'] = before;
4778    methods[name + 'After'] = after;
4779    methods[name + 'sAfter'] = after;
4780    methods[name + 'FromNow'] = after;
4781    methods[name + 'sFromNow'] = after;
4782    number.extend(methods);
4783  }
4784
4785  extend(number, true, true, {
4786
4787     /***
4788     * @method duration([locale] = currentLocale)
4789     * @returns String
4790     * @short Takes the number as milliseconds and returns a unit-adjusted localized string.
4791     * @extra This method is the same as %Date#relative% without the localized equivalent of "from now" or "ago". [locale] can be passed as the first (and only) parameter. Note that this method is only available when the dates package is included.
4792     * @example
4793     *
4794     *   (500).duration() -> '500 milliseconds'
4795     *   (1200).duration() -> '1 second'
4796     *   (75).minutes().duration() -> '1 hour'
4797     *   (75).minutes().duration('es') -> '1 hora'
4798     *
4799     ***/
4800    'duration': function(localeCode) {
4801      return getLocalization(localeCode).getDuration(this);
4802    }
4803
4804  });
4805
4806
4807  English = CurrentLocalization = date.addLocale('en', {
4808    'plural':     true,
4809    'timeMarker': 'at',
4810    'ampm':       'am,pm',
4811    'months':     'January,February,March,April,May,June,July,August,September,October,November,December',
4812    'weekdays':   'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday',
4813    'units':      'millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s',
4814    'numbers':    'one,two,three,four,five,six,seven,eight,nine,ten',
4815    'articles':   'a,an,the',
4816    'tokens':     'the,st|nd|rd|th,of',
4817    'short':      '{Month} {d}, {yyyy}',
4818    'long':       '{Month} {d}, {yyyy} {h}:{mm}{tt}',
4819    'full':       '{Weekday} {Month} {d}, {yyyy} {h}:{mm}:{ss}{tt}',
4820    'past':       '{num} {unit} {sign}',
4821    'future':     '{num} {unit} {sign}',
4822    'duration':   '{num} {unit}',
4823    'modifiers': [
4824      { 'name': 'sign',  'src': 'ago|before', 'value': -1 },
4825      { 'name': 'sign',  'src': 'from now|after|from|in|later', 'value': 1 },
4826      { 'name': 'edge',  'src': 'last day', 'value': -2 },
4827      { 'name': 'edge',  'src': 'end', 'value': -1 },
4828      { 'name': 'edge',  'src': 'first day|beginning', 'value': 1 },
4829      { 'name': 'shift', 'src': 'last', 'value': -1 },
4830      { 'name': 'shift', 'src': 'the|this', 'value': 0 },
4831      { 'name': 'shift', 'src': 'next', 'value': 1 }
4832    ],
4833    'dateParse': [
4834      '{month} {year}',
4835      '{shift} {unit=5-7}',
4836      '{0?} {date}{1}',
4837      '{0?} {edge} of {shift?} {unit=4-7?}{month?}{year?}'
4838    ],
4839    'timeParse': [
4840      '{num} {unit} {sign}',
4841      '{sign} {num} {unit}',
4842      '{0} {num}{1} {day} of {month} {year?}',
4843      '{weekday?} {month} {date}{1?} {year?}',
4844      '{date} {month} {year}',
4845      '{date} {month}',
4846      '{shift} {weekday}',
4847      '{shift} week {weekday}',
4848      '{weekday} {2?} {shift} week',
4849      '{num} {unit=4-5} {sign} {day}',
4850      '{0?} {date}{1} of {month}',
4851      '{0?}{month?} {date?}{1?} of {shift} {unit=6-7}'
4852    ]
4853  });
4854
4855  buildDateUnits();
4856  buildDateMethods();
4857  buildCoreInputFormats();
4858  buildFormatTokens();
4859  buildFormatShortcuts();
4860  buildAsianDigits();
4861  buildRelativeAliases();
4862  buildUTCAliases();
4863  setDateProperties();
4864
4865
4866  /***
4867   * @package Range
4868   * @dependency core
4869   * @description Ranges allow creating spans of numbers, strings, or dates. They can enumerate over specific points within that range, and be manipulated and compared.
4870   *
4871   ***/
4872
4873  function Range(start, end) {
4874    this.start = cloneRangeMember(start);
4875    this.end   = cloneRangeMember(end);
4876  };
4877
4878  function getRangeMemberNumericValue(m) {
4879    return isString(m) ? m.charCodeAt(0) : m;
4880  }
4881
4882  function getRangeMemberPrimitiveValue(m) {
4883    if(m == null) return m;
4884    return isDate(m) ? m.getTime() : m.valueOf();
4885  }
4886
4887  function cloneRangeMember(m) {
4888    if(isDate(m)) {
4889      return new date(m.getTime());
4890    } else {
4891      return getRangeMemberPrimitiveValue(m);
4892    }
4893  }
4894
4895  function isValidRangeMember(m) {
4896    var val = getRangeMemberPrimitiveValue(m);
4897    return !!val || val === 0;
4898  }
4899
4900  function getDuration(amt) {
4901    var match, val, unit;
4902    if(isNumber(amt)) {
4903      return amt;
4904    }
4905    match = amt.toLowerCase().match(/^(\d+)?\s?(\w+?)s?$/i);
4906    val = parseInt(match[1]) || 1;
4907    unit = match[2].slice(0,1).toUpperCase() + match[2].slice(1);
4908    if(unit.match(/hour|minute|second/i)) {
4909      unit += 's';
4910    } else if(unit === 'Year') {
4911      unit = 'FullYear';
4912    } else if(unit === 'Day') {
4913      unit = 'Date';
4914    }
4915    return [val, unit];
4916  }
4917
4918  function incrementDate(current, amount) {
4919    var num, unit, val, d;
4920    if(isNumber(amount)) {
4921      return new date(current.getTime() + amount);
4922    }
4923    num  = amount[0];
4924    unit = amount[1];
4925    val  = callDateGet(current, unit);
4926    d    = new date(current.getTime());
4927    callDateSet(d, unit, val + num);
4928    return d;
4929  }
4930
4931  function incrementString(current, amount) {
4932    return string.fromCharCode(current.charCodeAt(0) + amount);
4933  }
4934
4935  function incrementNumber(current, amount) {
4936    return current + amount;
4937  }
4938
4939  /***
4940   * @method toString()
4941   * @returns String
4942   * @short Returns a string representation of the range.
4943   * @example
4944   *
4945   *   Number.range(1, 5).toString()                               -> 1..5
4946   *   Date.range(new Date(2003, 0), new Date(2005, 0)).toString() -> January 1, 2003..January 1, 2005
4947   *
4948   ***/
4949
4950  // Note: 'toString' doesn't appear in a for..in loop in IE even though
4951  // hasOwnProperty reports true, so extend() can't be used here.
4952  // Also tried simply setting the prototype = {} up front for all
4953  // methods but GCC very oddly started dropping properties in the
4954  // object randomly (maybe because of the global scope?) hence
4955  // the need for the split logic here.
4956  Range.prototype.toString = function() {
4957    return this.isValid() ? this.start + ".." + this.end : 'Invalid Range';
4958  };
4959
4960  extend(Range, true, true, {
4961
4962    /***
4963     * @method isValid()
4964     * @returns Boolean
4965     * @short Returns true if the range is valid, false otherwise.
4966     * @example
4967     *
4968     *   Date.range(new Date(2003, 0), new Date(2005, 0)).isValid() -> true
4969     *   Number.range(NaN, NaN).isValid()                           -> false
4970     *
4971     ***/
4972    'isValid': function() {
4973      return isValidRangeMember(this.start) && isValidRangeMember(this.end) && typeof this.start === typeof this.end;
4974    },
4975
4976    /***
4977     * @method span()
4978     * @returns Number
4979     * @short Returns the span of the range. If the range is a date range, the value is in milliseconds.
4980     * @extra The span includes both the start and the end.
4981     * @example
4982     *
4983     *   Number.range(5, 10).span()                              -> 6
4984     *   Date.range(new Date(2003, 0), new Date(2005, 0)).span() -> 94694400000
4985     *
4986     ***/
4987    'span': function() {
4988      return this.isValid() ? abs(
4989        getRangeMemberNumericValue(this.end) - getRangeMemberNumericValue(this.start)
4990      ) + 1 : NaN;
4991    },
4992
4993    /***
4994     * @method contains(<obj>)
4995     * @returns Boolean
4996     * @short Returns true if <obj> is contained inside the range. <obj> may be a value or another range.
4997     * @example
4998     *
4999     *   Number.range(5, 10).contains(7)                                              -> true
5000     *   Date.range(new Date(2003, 0), new Date(2005, 0)).contains(new Date(2004, 0)) -> true
5001     *
5002     ***/
5003    'contains': function(obj) {
5004      var self = this, arr;
5005      if(obj == null) return false;
5006      if(obj.start && obj.end) {
5007        return obj.start >= this.start && obj.start <= this.end &&
5008               obj.end   >= this.start && obj.end   <= this.end;
5009      } else {
5010        return obj >= this.start && obj <= this.end;
5011      }
5012    },
5013
5014    /***
5015     * @method every(<amount>, [fn])
5016     * @returns Array
5017     * @short Iterates through the range for every <amount>, calling [fn] if it is passed. Returns an array of each increment visited.
5018     * @extra In the case of date ranges, <amount> can also be a string, in which case it will increment a number of  units. Note that %(2).months()% first resolves to a number, which will be interpreted as milliseconds and is an approximation, so stepping through the actual months by passing %"2 months"% is usually preferable.
5019     * @example
5020     *
5021     *   Number.range(2, 8).every(2)                                       -> [2,4,6,8]
5022     *   Date.range(new Date(2003, 1), new Date(2003,3)).every("2 months") -> [...]
5023     *
5024     ***/
5025    'every': function(amount, fn) {
5026      var increment,
5027          start   = this.start,
5028          end     = this.end,
5029          inverse = end < start,
5030          current = start,
5031          index   = 0,
5032          result  = [];
5033
5034      if(isFunction(amount)) {
5035        fn = amount;
5036        amount = null;
5037      }
5038      amount = amount || 1;
5039      if(isNumber(start)) {
5040        increment = incrementNumber;
5041      } else if(isString(start)) {
5042        increment = incrementString;
5043      } else if(isDate(start)) {
5044        amount    = getDuration(amount);
5045        increment = incrementDate;
5046      }
5047      // Avoiding infinite loops
5048      if(inverse && amount > 0) {
5049        amount *= -1;
5050      }
5051      while(inverse ? current >= end : current <= end) {
5052        result.push(current);
5053        if(fn) {
5054          fn(current, index);
5055        }
5056        current = increment(current, amount);
5057        index++;
5058      }
5059      return result;
5060    },
5061
5062    /***
5063     * @method union(<range>)
5064     * @returns Range
5065     * @short Returns a new range with the earliest starting point as its start, and the latest ending point as its end. If the two ranges do not intersect this will effectively remove the "gap" between them.
5066     * @example
5067     *
5068     *   Number.range(1, 3).union(Number.range(2, 5)) -> 1..5
5069     *   Date.range(new Date(2003, 1), new Date(2005, 1)).union(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2003..Jan 1, 2006
5070     *
5071     ***/
5072    'union': function(range) {
5073      return new Range(
5074        this.start < range.start ? this.start : range.start,
5075        this.end   > range.end   ? this.end   : range.end
5076      );
5077    },
5078
5079    /***
5080     * @method intersect(<range>)
5081     * @returns Range
5082     * @short Returns a new range with the latest starting point as its start, and the earliest ending point as its end. If the two ranges do not intersect this will effectively produce an invalid range.
5083     * @example
5084     *
5085     *   Number.range(1, 5).intersect(Number.range(4, 8)) -> 4..5
5086     *   Date.range(new Date(2003, 1), new Date(2005, 1)).intersect(Date.range(new Date(2004, 1), new Date(2006, 1))) -> Jan 1, 2004..Jan 1, 2005
5087     *
5088     ***/
5089    'intersect': function(range) {
5090      if(range.start > this.end || range.end < this.start) {
5091        return new Range(NaN, NaN);
5092      }
5093      return new Range(
5094        this.start > range.start ? this.start : range.start,
5095        this.end   < range.end   ? this.end   : range.end
5096      );
5097    },
5098
5099    /***
5100     * @method clone()
5101     * @returns Range
5102     * @short Clones the range.
5103     * @extra Members of the range will also be cloned.
5104     * @example
5105     *
5106     *   Number.range(1, 5).clone() -> Returns a copy of the range.
5107     *
5108     ***/
5109    'clone': function(range) {
5110      return new Range(this.start, this.end);
5111    },
5112
5113    /***
5114     * @method clamp(<obj>)
5115     * @returns Mixed
5116     * @short Clamps <obj> to be within the range if it falls outside.
5117     * @example
5118     *
5119     *   Number.range(1, 5).clamp(8) -> 5
5120     *   Date.range(new Date(2010, 0), new Date(2012, 0)).clamp(new Date(2013, 0)) -> 2012-01
5121     *
5122     ***/
5123    'clamp': function(obj) {
5124      var clamped,
5125          start = this.start,
5126          end = this.end,
5127          min = end < start ? end : start,
5128          max = start > end ? start : end;
5129      if(obj < min) {
5130        clamped = min;
5131      } else if(obj > max) {
5132        clamped = max;
5133      } else {
5134        clamped = obj;
5135      }
5136      return cloneRangeMember(clamped);
5137    }
5138
5139  });
5140
5141
5142  /***
5143   * Number module
5144   ***
5145   * @method Number.range([start], [end])
5146   * @returns Range
5147   * @short Creates a new range between [start] and [end]. See @ranges for more.
5148   * @example
5149   *
5150   *   Number.range(5, 10)
5151   *
5152   ***
5153   * String module
5154   ***
5155   * @method String.range([start], [end])
5156   * @returns Range
5157   * @short Creates a new range between [start] and [end]. See @ranges for more.
5158   * @example
5159   *
5160   *   String.range('a', 'z')
5161   *
5162   ***
5163   * Date module
5164   ***
5165   * @method Date.range([start], [end])
5166   * @returns Range
5167   * @short Creates a new range between [start] and [end].
5168   * @extra If either [start] or [end] are null, they will default to the current date. See @ranges for more.
5169   * @example
5170   *
5171   *   Date.range('today', 'tomorrow')
5172   *
5173   ***/
5174  [number, string, date].forEach(function(klass) {
5175     extend(klass, false, true, {
5176
5177      'range': function(start, end) {
5178        if(klass.create) {
5179          start = klass.create(start);
5180          end   = klass.create(end);
5181        }
5182        return new Range(start, end);
5183      }
5184
5185    });
5186
5187  });
5188
5189  /***
5190   * Number module
5191   *
5192   ***/
5193
5194  extend(number, true, true, {
5195
5196    /***
5197     * @method upto(<num>, [fn], [step] = 1)
5198     * @returns Array
5199     * @short Returns an array containing numbers from the number up to <num>.
5200     * @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
5201     * @example
5202     *
5203     *   (2).upto(6) -> [2, 3, 4, 5, 6]
5204     *   (2).upto(6, function(n) {
5205     *     // This function is called 5 times receiving n as the value.
5206     *   });
5207     *   (2).upto(8, null, 2) -> [2, 4, 6, 8]
5208     *
5209     ***/
5210    'upto': function(num, fn, step) {
5211      return number.range(this, num).every(step, fn);
5212    },
5213
5214     /***
5215     * @method clamp([start] = Infinity, [end] = Infinity)
5216     * @returns Number
5217     * @short Constrains the number so that it is between [start] and [end].
5218     * @extra This will build a range object that has an equivalent %clamp% method.
5219     * @example
5220     *
5221     *   (3).clamp(50, 100)  -> 50
5222     *   (85).clamp(50, 100) -> 85
5223     *
5224     ***/
5225    'clamp': function(start, end) {
5226      return new Range(start, end).clamp(this);
5227    },
5228
5229     /***
5230     * @method cap([max] = Infinity)
5231     * @returns Number
5232     * @short Constrains the number so that it is no greater than [max].
5233     * @extra This will build a range object that has an equivalent %cap% method.
5234     * @example
5235     *
5236     *   (100).cap(80) -> 80
5237     *
5238     ***/
5239    'cap': function(max) {
5240      return this.clamp(Undefined, max);
5241    }
5242
5243  });
5244
5245  extend(number, true, true, {
5246
5247    /***
5248     * @method downto(<num>, [fn], [step] = 1)
5249     * @returns Array
5250     * @short Returns an array containing numbers from the number down to <num>.
5251     * @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
5252     * @example
5253     *
5254     *   (8).downto(3) -> [8, 7, 6, 5, 4, 3]
5255     *   (8).downto(3, function(n) {
5256     *     // This function is called 6 times receiving n as the value.
5257     *   });
5258     *   (8).downto(2, null, 2) -> [8, 6, 4, 2]
5259     *
5260     ***/
5261    'downto': number.prototype.upto
5262
5263  });
5264
5265
5266  /***
5267   * Array module
5268   *
5269   ***/
5270
5271  extend(array, false, function(a) { return a instanceof Range; }, {
5272
5273    'create': function(range) {
5274      return range.every();
5275    }
5276
5277  });
5278
5279
5280  /***
5281   * @package Function
5282   * @dependency core
5283   * @description Lazy, throttled, and memoized functions, delayed functions and handling of timers, argument currying.
5284   *
5285   ***/
5286
5287  function setDelay(fn, ms, after, scope, args) {
5288    // Delay of infinity is never called of course...
5289    if(ms === Infinity) return;
5290    if(!fn.timers) fn.timers = [];
5291    if(!isNumber(ms)) ms = 1;
5292    // This is a workaround for <= IE8, which apparently has the
5293    // ability to call timeouts in the queue on the same tick (ms?)
5294    // even if functionally they have already been cleared.
5295    fn._canceled = false;
5296    fn.timers.push(setTimeout(function(){
5297      if(!fn._canceled) {
5298        after.apply(scope, args || []);
5299      }
5300    }, ms));
5301  }
5302
5303  extend(Function, true, true, {
5304
5305     /***
5306     * @method lazy([ms] = 1, [immediate] = false, [limit] = Infinity)
5307     * @returns Function
5308     * @short Creates a lazy function that, when called repeatedly, will queue execution and wait [ms] milliseconds to execute.
5309     * @extra If [immediate] is %true%, first execution will happen immediately, then lock. If [limit] is a fininte number, calls past [limit] will be ignored while execution is locked. Compare this to %throttle%, which will execute only once per [ms] milliseconds. Note that [ms] can also be a fraction. Calling %cancel% on a lazy function will clear the entire queue. For more see @functions.
5310     * @example
5311     *
5312     *   (function() {
5313     *     // Executes immediately.
5314     *   }).lazy()();
5315     *   (3).times(function() {
5316     *     // Executes 3 times, with each execution 20ms later than the last.
5317     *   }.lazy(20));
5318     *   (100).times(function() {
5319     *     // Executes 50 times, with each execution 20ms later than the last.
5320     *   }.lazy(20, false, 50));
5321     *
5322     ***/
5323    'lazy': function(ms, immediate, limit) {
5324      var fn = this, queue = [], locked = false, execute, rounded, perExecution, result;
5325      ms = ms || 1;
5326      limit = limit || Infinity;
5327      rounded = ceil(ms);
5328      perExecution = round(rounded / ms) || 1;
5329      execute = function() {
5330        var queueLength = queue.length, maxPerRound;
5331        if(queueLength == 0) return;
5332        // Allow fractions of a millisecond by calling
5333        // multiple times per actual timeout execution
5334        maxPerRound = max(queueLength - perExecution, 0);
5335        while(queueLength > maxPerRound) {
5336          // Getting uber-meta here...
5337          result = Function.prototype.apply.apply(fn, queue.shift());
5338          queueLength--;
5339        }
5340        setDelay(lazy, rounded, function() {
5341          locked = false;
5342          execute();
5343        });
5344      }
5345      function lazy() {
5346        // If the execution has locked and it's immediate, then
5347        // allow 1 less in the queue as 1 call has already taken place.
5348        if(queue.length < limit - (locked && immediate ? 1 : 0)) {
5349          queue.push([this, arguments]);
5350        }
5351        if(!locked) {
5352          locked = true;
5353          if(immediate) {
5354            execute();
5355          } else {
5356            setDelay(lazy, rounded, execute);
5357          }
5358        }
5359        // Return the memoized result
5360        return result;
5361      }
5362      return lazy;
5363    },
5364
5365     /***
5366     * @method throttle([ms] = 1)
5367     * @returns Function
5368     * @short Creates a "throttled" version of the function that will only be executed once per <ms> milliseconds.
5369     * @extra This is functionally equivalent to calling %lazy% with a [limit] of %1% and [immediate] as %true%. %throttle% is appropriate when you want to make sure a function is only executed at most once for a given duration. For more see @functions.
5370     * @example
5371     *
5372     *   (3).times(function() {
5373     *     // called only once. will wait 50ms until it responds again
5374     *   }.throttle(50));
5375     *
5376     ***/
5377    'throttle': function(ms) {
5378      return this.lazy(ms, true, 1);
5379    },
5380
5381     /***
5382     * @method debounce([ms] = 1)
5383     * @returns Function
5384     * @short Creates a "debounced" function that postpones its execution until after <ms> milliseconds have passed.
5385     * @extra This method is useful to execute a function after things have "settled down". A good example of this is when a user tabs quickly through form fields, execution of a heavy operation should happen after a few milliseconds when they have "settled" on a field. For more see @functions.
5386     * @example
5387     *
5388     *   var fn = (function(arg1) {
5389     *     // called once 50ms later
5390     *   }).debounce(50); fn() fn() fn();
5391     *
5392     ***/
5393    'debounce': function(ms) {
5394      var fn = this;
5395      function debounced() {
5396        debounced.cancel();
5397        setDelay(debounced, ms, fn, this, arguments);
5398      };
5399      return debounced;
5400    },
5401
5402     /***
5403     * @method delay([ms] = 1, [arg1], ...)
5404     * @returns Function
5405     * @short Executes the function after <ms> milliseconds.
5406     * @extra Returns a reference to itself. %delay% is also a way to execute non-blocking operations that will wait until the CPU is free. Delayed functions can be canceled using the %cancel% method. Can also curry arguments passed in after <ms>.
5407     * @example
5408     *
5409     *   (function(arg1) {
5410     *     // called 1s later
5411     *   }).delay(1000, 'arg1');
5412     *
5413     ***/
5414    'delay': function(ms) {
5415      var fn = this;
5416      var args = multiArgs(arguments, null, 1);
5417      setDelay(fn, ms, fn, fn, args);
5418      return fn;
5419    },
5420
5421     /***
5422     * @method every([ms] = 1, [arg1], ...)
5423     * @returns Function
5424     * @short Executes the function every <ms> milliseconds.
5425     * @extra Returns a reference to itself. Repeating functions with %every% can be canceled using the %cancel% method. Can also curry arguments passed in after <ms>.
5426     * @example
5427     *
5428     *   (function(arg1) {
5429     *     // called every 1s
5430     *   }).every(1000, 'arg1');
5431     *
5432     ***/
5433    'every': function(ms) {
5434      var fn = this, args = arguments;
5435      args = args.length > 1 ? multiArgs(args, null, 1) : [];
5436      function execute () {
5437        fn.apply(fn, args);
5438        setDelay(fn, ms, execute);
5439      }
5440      setDelay(fn, ms, execute);
5441      return fn;
5442    },
5443
5444     /***
5445     * @method cancel()
5446     * @returns Function
5447     * @short Cancels a delayed function scheduled to be run.
5448     * @extra %delay%, %lazy%, %throttle%, and %debounce% can all set delays.
5449     * @example
5450     *
5451     *   (function() {
5452     *     alert('hay'); // Never called
5453     *   }).delay(500).cancel();
5454     *
5455     ***/
5456    'cancel': function() {
5457      var timers = this.timers, timer;
5458      if(isArray(timers)) {
5459        while(timer = timers.shift()) {
5460          clearTimeout(timer);
5461        }
5462      }
5463      this._canceled = true;
5464      return this;
5465    },
5466
5467     /***
5468     * @method after([num] = 1)
5469     * @returns Function
5470     * @short Creates a function that will execute after [num] calls.
5471     * @extra %after% is useful for running a final callback after a series of asynchronous operations, when the order in which the operations will complete is unknown.
5472     * @example
5473     *
5474     *   var fn = (function() {
5475     *     // Will be executed once only
5476     *   }).after(3); fn(); fn(); fn();
5477     *
5478     ***/
5479    'after': function(num) {
5480      var fn = this, counter = 0, storedArguments = [];
5481      if(!isNumber(num)) {
5482        num = 1;
5483      } else if(num === 0) {
5484        fn.call();
5485        return fn;
5486      }
5487      return function() {
5488        var ret;
5489        storedArguments.push(multiArgs(arguments));
5490        counter++;
5491        if(counter == num) {
5492          ret = fn.call(this, storedArguments);
5493          counter = 0;
5494          storedArguments = [];
5495          return ret;
5496        }
5497      }
5498    },
5499
5500     /***
5501     * @method once()
5502     * @returns Function
5503     * @short Creates a function that will execute only once and store the result.
5504     * @extra %once% is useful for creating functions that will cache the result of an expensive operation and use it on subsequent calls. Also it can be useful for creating initialization functions that only need to be run once.
5505     * @example
5506     *
5507     *   var fn = (function() {
5508     *     // Will be executed once only
5509     *   }).once(); fn(); fn(); fn();
5510     *
5511     ***/
5512    'once': function() {
5513      return this.throttle(Infinity, true);
5514    },
5515
5516     /***
5517     * @method fill(<arg1>, <arg2>, ...)
5518     * @returns Function
5519     * @short Returns a new version of the function which when called will have some of its arguments pre-emptively filled in, also known as "currying".
5520     * @extra Arguments passed to a "filled" function are generally appended to the curried arguments. However, if %undefined% is passed as any of the arguments to %fill%, it will be replaced, when the "filled" function is executed. This allows currying of arguments even when they occur toward the end of an argument list (the example demonstrates this much more clearly).
5521     * @example
5522     *
5523     *   var delayOneSecond = setTimeout.fill(undefined, 1000);
5524     *   delayOneSecond(function() {
5525     *     // Will be executed 1s later
5526     *   });
5527     *
5528     ***/
5529    'fill': function() {
5530      var fn = this, curried = multiArgs(arguments);
5531      return function() {
5532        var args = multiArgs(arguments);
5533        curried.forEach(function(arg, index) {
5534          if(arg != null || index >= args.length) args.splice(index, 0, arg);
5535        });
5536        return fn.apply(this, args);
5537      }
5538    }
5539
5540
5541  });
5542
5543
5544  /***
5545   * @package Number
5546   * @dependency core
5547   * @description Number formatting, rounding (with precision), and ranges. Aliases to Math methods.
5548   *
5549   ***/
5550
5551
5552  function abbreviateNumber(num, roundTo, str, mid, limit, bytes) {
5553    var fixed        = num.toFixed(20),
5554        decimalPlace = fixed.search(/\./),
5555        numeralPlace = fixed.search(/[1-9]/),
5556        significant  = decimalPlace - numeralPlace,
5557        unit, i, divisor;
5558    if(significant > 0) {
5559      significant -= 1;
5560    }
5561    i = max(min(floor(significant / 3), limit === false ? str.length : limit), -mid);
5562    unit = str.charAt(i + mid - 1);
5563    if(significant < -9) {
5564      i = -3;
5565      roundTo = abs(significant) - 9;
5566      unit = str.slice(0,1);
5567    }
5568    divisor = bytes ? pow(2, 10 * i) : pow(10, i * 3);
5569    return withPrecision(num / divisor, roundTo || 0).format() + unit.trim();
5570  }
5571
5572
5573  extend(number, false, true, {
5574
5575    /***
5576     * @method Number.random([n1], [n2])
5577     * @returns Number
5578     * @short Returns a random integer between [n1] and [n2].
5579     * @extra If only 1 number is passed, the other will be 0. If none are passed, the number will be either 0 or 1.
5580     * @example
5581     *
5582     *   Number.random(50, 100) -> ex. 85
5583     *   Number.random(50)      -> ex. 27
5584     *   Number.random()        -> ex. 0
5585     *
5586     ***/
5587    'random': function(n1, n2) {
5588      var minNum, maxNum;
5589      if(arguments.length == 1) n2 = n1, n1 = 0;
5590      minNum = min(n1 || 0, isUndefined(n2) ? 1 : n2);
5591      maxNum = max(n1 || 0, isUndefined(n2) ? 1 : n2) + 1;
5592      return floor((math.random() * (maxNum - minNum)) + minNum);
5593    }
5594
5595  });
5596
5597  extend(number, true, true, {
5598
5599    /***
5600     * @method log(<base> = Math.E)
5601     * @returns Number
5602     * @short Returns the logarithm of the number with base <base>, or natural logarithm of the number if <base> is undefined.
5603     * @example
5604     *
5605     *   (64).log(2) -> 6
5606     *   (9).log(3)  -> 2
5607     *   (5).log()   -> 1.6094379124341003
5608     *
5609     ***/
5610
5611    'log': function(base) {
5612       return math.log(this) / (base ? math.log(base) : 1);
5613     },
5614
5615    /***
5616     * @method abbr([precision] = 0)
5617     * @returns String
5618     * @short Returns an abbreviated form of the number.
5619     * @extra [precision] will round to the given precision.
5620     * @example
5621     *
5622     *   (1000).abbr()    -> "1k"
5623     *   (1000000).abbr() -> "1m"
5624     *   (1280).abbr(1)   -> "1.3k"
5625     *
5626     ***/
5627    'abbr': function(precision) {
5628      return abbreviateNumber(this, precision, 'kmbt', 0, 4);
5629    },
5630
5631    /***
5632     * @method metric([precision] = 0, [limit] = 1)
5633     * @returns String
5634     * @short Returns the number as a string in metric notation.
5635     * @extra [precision] will round to the given precision. Both very large numbers and very small numbers are supported. [limit] is the upper limit for the units. The default is %1%, which is "kilo". If [limit] is %false%, the upper limit will be "exa". The lower limit is "nano", and cannot be changed.
5636     * @example
5637     *
5638     *   (1000).metric()            -> "1k"
5639     *   (1000000).metric()         -> "1,000k"
5640     *   (1000000).metric(0, false) -> "1M"
5641     *   (1249).metric(2) + 'g'     -> "1.25kg"
5642     *   (0.025).metric() + 'm'     -> "25mm"
5643     *
5644     ***/
5645    'metric': function(precision, limit) {
5646      return abbreviateNumber(this, precision, 'nμm kMGTPE', 4, isUndefined(limit) ? 1 : limit);
5647    },
5648
5649    /***
5650     * @method bytes([precision] = 0, [limit] = 4)
5651     * @returns String
5652     * @short Returns an abbreviated form of the number, considered to be "Bytes".
5653     * @extra [precision] will round to the given precision. [limit] is the upper limit for the units. The default is %4%, which is "terabytes" (TB). If [limit] is %false%, the upper limit will be "exa".
5654     * @example
5655     *
5656     *   (1000).bytes()                 -> "1kB"
5657     *   (1000).bytes(2)                -> "0.98kB"
5658     *   ((10).pow(20)).bytes()         -> "90,949,470TB"
5659     *   ((10).pow(20)).bytes(0, false) -> "87EB"
5660     *
5661     ***/
5662    'bytes': function(precision, limit) {
5663      return abbreviateNumber(this, precision, 'kMGTPE', 0, isUndefined(limit) ? 4 : limit, true) + 'B';
5664    },
5665
5666    /***
5667     * @method isInteger()
5668     * @returns Boolean
5669     * @short Returns true if the number has no trailing decimal.
5670     * @example
5671     *
5672     *   (420).isInteger() -> true
5673     *   (4.5).isInteger() -> false
5674     *
5675     ***/
5676    'isInteger': function() {
5677      return this % 1 == 0;
5678    },
5679
5680    /***
5681     * @method isOdd()
5682     * @returns Boolean
5683     * @short Returns true if the number is odd.
5684     * @example
5685     *
5686     *   (3).isOdd()  -> true
5687     *   (18).isOdd() -> false
5688     *
5689     ***/
5690    'isOdd': function() {
5691      return !isNaN(this) && !this.isMultipleOf(2);
5692    },
5693
5694    /***
5695     * @method isEven()
5696     * @returns Boolean
5697     * @short Returns true if the number is even.
5698     * @example
5699     *
5700     *   (6).isEven()  -> true
5701     *   (17).isEven() -> false
5702     *
5703     ***/
5704    'isEven': function() {
5705      return this.isMultipleOf(2);
5706    },
5707
5708    /***
5709     * @method isMultipleOf(<num>)
5710     * @returns Boolean
5711     * @short Returns true if the number is a multiple of <num>.
5712     * @example
5713     *
5714     *   (6).isMultipleOf(2)  -> true
5715     *   (17).isMultipleOf(2) -> false
5716     *   (32).isMultipleOf(4) -> true
5717     *   (34).isMultipleOf(4) -> false
5718     *
5719     ***/
5720    'isMultipleOf': function(num) {
5721      return this % num === 0;
5722    },
5723
5724
5725    /***
5726     * @method format([place] = 0, [thousands] = ',', [decimal] = '.')
5727     * @returns String
5728     * @short Formats the number to a readable string.
5729     * @extra If [place] is %undefined%, will automatically determine the place. [thousands] is the character used for the thousands separator. [decimal] is the character used for the decimal point.
5730     * @example
5731     *
5732     *   (56782).format()           -> '56,782'
5733     *   (56782).format(2)          -> '56,782.00'
5734     *   (4388.43).format(2, ' ')      -> '4 388.43'
5735     *   (4388.43).format(2, '.', ',') -> '4.388,43'
5736     *
5737     ***/
5738    'format': function(place, thousands, decimal) {
5739      var i, str, split, integer, fraction, result = '';
5740      if(isUndefined(thousands)) {
5741        thousands = ',';
5742      }
5743      if(isUndefined(decimal)) {
5744        decimal = '.';
5745      }
5746      str      = (isNumber(place) ? withPrecision(this, place || 0).toFixed(max(place, 0)) : this.toString()).replace(/^-/, '');
5747      split    = str.split('.');
5748      integer  = split[0];
5749      fraction = split[1];
5750      for(i = integer.length; i > 0; i -= 3) {
5751        if(i < integer.length) {
5752          result = thousands + result;
5753        }
5754        result = integer.slice(max(0, i - 3), i) + result;
5755      }
5756      if(fraction) {
5757        result += decimal + repeatString('0', (place || 0) - fraction.length) + fraction;
5758      }
5759      return (this < 0 ? '-' : '') + result;
5760    },
5761
5762    /***
5763     * @method hex([pad] = 1)
5764     * @returns String
5765     * @short Converts the number to hexidecimal.
5766     * @extra [pad] will pad the resulting string to that many places.
5767     * @example
5768     *
5769     *   (255).hex()   -> 'ff';
5770     *   (255).hex(4)  -> '00ff';
5771     *   (23654).hex() -> '5c66';
5772     *
5773     ***/
5774    'hex': function(pad) {
5775      return this.pad(pad || 1, false, 16);
5776    },
5777
5778    /***
5779     * @method times(<fn>)
5780     * @returns Number
5781     * @short Calls <fn> a number of times equivalent to the number.
5782     * @example
5783     *
5784     *   (8).times(function(i) {
5785     *     // This function is called 8 times.
5786     *   });
5787     *
5788     ***/
5789    'times': function(fn) {
5790      if(fn) {
5791        for(var i = 0; i < this; i++) {
5792          fn.call(this, i);
5793        }
5794      }
5795      return this.toNumber();
5796    },
5797
5798    /***
5799     * @method chr()
5800     * @returns String
5801     * @short Returns a string at the code point of the number.
5802     * @example
5803     *
5804     *   (65).chr() -> "A"
5805     *   (75).chr() -> "K"
5806     *
5807     ***/
5808    'chr': function() {
5809      return string.fromCharCode(this);
5810    },
5811
5812    /***
5813     * @method pad(<place> = 0, [sign] = false, [base] = 10)
5814     * @returns String
5815     * @short Pads a number with "0" to <place>.
5816     * @extra [sign] allows you to force the sign as well (+05, etc). [base] can change the base for numeral conversion.
5817     * @example
5818     *
5819     *   (5).pad(2)        -> '05'
5820     *   (-5).pad(4)       -> '-0005'
5821     *   (82).pad(3, true) -> '+082'
5822     *
5823     ***/
5824    'pad': function(place, sign, base) {
5825      return padNumber(this, place, sign, base);
5826    },
5827
5828    /***
5829     * @method ordinalize()
5830     * @returns String
5831     * @short Returns an ordinalized (English) string, i.e. "1st", "2nd", etc.
5832     * @example
5833     *
5834     *   (1).ordinalize() -> '1st';
5835     *   (2).ordinalize() -> '2nd';
5836     *   (8).ordinalize() -> '8th';
5837     *
5838     ***/
5839    'ordinalize': function() {
5840      var suffix, num = abs(this), last = parseInt(num.toString().slice(-2));
5841      return this + getOrdinalizedSuffix(last);
5842    },
5843
5844    /***
5845     * @method toNumber()
5846     * @returns Number
5847     * @short Returns a number. This is mostly for compatibility reasons.
5848     * @example
5849     *
5850     *   (420).toNumber() -> 420
5851     *
5852     ***/
5853    'toNumber': function() {
5854      return parseFloat(this, 10);
5855    }
5856
5857  });
5858
5859  /***
5860   * @method round(<precision> = 0)
5861   * @returns Number
5862   * @short Shortcut for %Math.round% that also allows a <precision>.
5863   *
5864   * @example
5865   *
5866   *   (3.241).round()  -> 3
5867   *   (-3.841).round() -> -4
5868   *   (3.241).round(2) -> 3.24
5869   *   (3748).round(-2) -> 3800
5870   *
5871   ***
5872   * @method ceil(<precision> = 0)
5873   * @returns Number
5874   * @short Shortcut for %Math.ceil% that also allows a <precision>.
5875   *
5876   * @example
5877   *
5878   *   (3.241).ceil()  -> 4
5879   *   (-3.241).ceil() -> -3
5880   *   (3.241).ceil(2) -> 3.25
5881   *   (3748).ceil(-2) -> 3800
5882   *
5883   ***
5884   * @method floor(<precision> = 0)
5885   * @returns Number
5886   * @short Shortcut for %Math.floor% that also allows a <precision>.
5887   *
5888   * @example
5889   *
5890   *   (3.241).floor()  -> 3
5891   *   (-3.841).floor() -> -4
5892   *   (3.241).floor(2) -> 3.24
5893   *   (3748).floor(-2) -> 3700
5894   *
5895   ***
5896   * @method [math]()
5897   * @returns Number
5898   * @short Math related functions are mapped as shortcuts to numbers and are identical. Note that %Number#log% provides some special defaults.
5899   *
5900   * @set
5901   *   abs
5902   *   sin
5903   *   asin
5904   *   cos
5905   *   acos
5906   *   tan
5907   *   atan
5908   *   sqrt
5909   *   exp
5910   *   pow
5911   *
5912   * @example
5913   *
5914   *   (3).pow(3) -> 27
5915   *   (-3).abs() -> 3
5916   *   (1024).sqrt() -> 32
5917   *
5918   ***/
5919
5920  function buildNumber() {
5921    function createRoundingFunction(fn) {
5922      return function (precision) {
5923        return precision ? withPrecision(this, precision, fn) : fn(this);
5924      }
5925    }
5926    extend(number, true, true, {
5927      'ceil':   createRoundingFunction(ceil),
5928      'round':  createRoundingFunction(round),
5929      'floor':  createRoundingFunction(floor)
5930    });
5931    extendSimilar(number, true, true, 'abs,pow,sin,asin,cos,acos,tan,atan,exp,pow,sqrt', function(methods, name) {
5932      methods[name] = function(a, b) {
5933        return math[name](this, a, b);
5934      }
5935    });
5936  }
5937
5938  buildNumber();
5939
5940
5941  /***
5942   * @package Object
5943   * @dependency core
5944   * @description Object manipulation, type checking (isNumber, isString, ...), extended objects with hash-like methods available as instance methods.
5945   *
5946   * Much thanks to kangax for his informative aricle about how problems with instanceof and constructor
5947   * http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
5948   *
5949   ***/
5950
5951  var ObjectTypeMethods = 'isObject,isNaN'.split(',');
5952  var ObjectHashMethods = 'keys,values,select,reject,each,merge,clone,equal,watch,tap,has,toQueryString'.split(',');
5953
5954  function setParamsObject(obj, param, value, castBoolean) {
5955    var reg = /^(.+?)(\[.*\])$/, paramIsArray, match, allKeys, key;
5956    if(match = param.match(reg)) {
5957      key = match[1];
5958      allKeys = match[2].replace(/^\[|\]$/g, '').split('][');
5959      allKeys.forEach(function(k) {
5960        paramIsArray = !k || k.match(/^\d+$/);
5961        if(!key && isArray(obj)) key = obj.length;
5962        if(!hasOwnProperty(obj, key)) {
5963          obj[key] = paramIsArray ? [] : {};
5964        }
5965        obj = obj[key];
5966        key = k;
5967      });
5968      if(!key && paramIsArray) key = obj.length.toString();
5969      setParamsObject(obj, key, value, castBoolean);
5970    } else if(castBoolean && value === 'true') {
5971      obj[param] = true;
5972    } else if(castBoolean && value === 'false') {
5973      obj[param] = false;
5974    } else {
5975      obj[param] = value;
5976    }
5977  }
5978
5979  function objectToQueryString(base, obj) {
5980    var tmp;
5981    // If a custom toString exists bail here and use that instead
5982    if(isArray(obj) || (isObjectType(obj) && obj.toString === internalToString)) {
5983      tmp = [];
5984      iterateOverObject(obj, function(key, value) {
5985        if(base) {
5986          key = base + '[' + key + ']';
5987        }
5988        tmp.push(objectToQueryString(key, value));
5989      });
5990      return tmp.join('&');
5991    } else {
5992      if(!base) return '';
5993      return sanitizeURIComponent(base) + '=' + (isDate(obj) ? obj.getTime() : sanitizeURIComponent(obj));
5994    }
5995  }
5996
5997  function sanitizeURIComponent(obj) {
5998    // undefined, null, and NaN are represented as a blank string,
5999    // while false and 0 are stringified. "+" is allowed in query string
6000    return !obj && obj !== false && obj !== 0 ? '' : encodeURIComponent(obj).replace(/%20/g, '+');
6001  }
6002
6003  function matchInObject(match, key, value) {
6004    if(isRegExp(match)) {
6005      return match.test(key);
6006    } else if(isObjectType(match)) {
6007      return match[key] === value;
6008    } else {
6009      return key === string(match);
6010    }
6011  }
6012
6013  function selectFromObject(obj, args, select) {
6014    var match, result = obj instanceof Hash ? new Hash : {};
6015    iterateOverObject(obj, function(key, value) {
6016      match = false;
6017      flattenedArgs(args, function(arg) {
6018        if(matchInObject(arg, key, value)) {
6019          match = true;
6020        }
6021      }, 1);
6022      if(match === select) {
6023        result[key] = value;
6024      }
6025    });
6026    return result;
6027  }
6028
6029
6030  /***
6031   * @method Object.is[Type](<obj>)
6032   * @returns Boolean
6033   * @short Returns true if <obj> is an object of that type.
6034   * @extra %isObject% will return false on anything that is not an object literal, including instances of inherited classes. Note also that %isNaN% will ONLY return true if the object IS %NaN%. It does not mean the same as browser native %isNaN%, which returns true for anything that is "not a number".
6035   *
6036   * @set
6037   *   isArray
6038   *   isObject
6039   *   isBoolean
6040   *   isDate
6041   *   isFunction
6042   *   isNaN
6043   *   isNumber
6044   *   isString
6045   *   isRegExp
6046   *
6047   * @example
6048   *
6049   *   Object.isArray([1,2,3])            -> true
6050   *   Object.isDate(3)                   -> false
6051   *   Object.isRegExp(/wasabi/)          -> true
6052   *   Object.isObject({ broken:'wear' }) -> true
6053   *
6054   ***/
6055  function buildTypeMethods() {
6056    extendSimilar(object, false, true, ClassNames, function(methods, name) {
6057      var method = 'is' + name;
6058      ObjectTypeMethods.push(method);
6059      methods[method] = typeChecks[name];
6060    });
6061  }
6062
6063  function buildObjectExtend() {
6064    extend(object, false, function(){ return arguments.length === 0; }, {
6065      'extend': function() {
6066        var methods = ObjectTypeMethods.concat(ObjectHashMethods)
6067        if(typeof EnumerableMethods !== 'undefined') {
6068          methods = methods.concat(EnumerableMethods);
6069        }
6070        buildObjectInstanceMethods(methods, object);
6071      }
6072    });
6073  }
6074
6075  extend(object, false, true, {
6076      /***
6077       * @method watch(<obj>, <prop>, <fn>)
6078       * @returns Nothing
6079       * @short Watches a property of <obj> and runs <fn> when it changes.
6080       * @extra <fn> is passed three arguments: the property <prop>, the old value, and the new value. The return value of [fn] will be set as the new value. This method is useful for things such as validating or cleaning the value when it is set. Warning: this method WILL NOT work in browsers that don't support %Object.defineProperty% (IE 8 and below). This is the only method in Sugar that is not fully compatible with all browsers. %watch% is available as an instance method on extended objects.
6081       * @example
6082       *
6083       *   Object.watch({ foo: 'bar' }, 'foo', function(prop, oldVal, newVal) {
6084       *     // Will be run when the property 'foo' is set on the object.
6085       *   });
6086       *   Object.extended().watch({ foo: 'bar' }, 'foo', function(prop, oldVal, newVal) {
6087       *     // Will be run when the property 'foo' is set on the object.
6088       *   });
6089       *
6090       ***/
6091    'watch': function(obj, prop, fn) {
6092      if(!definePropertySupport) return;
6093      var value = obj[prop];
6094      object.defineProperty(obj, prop, {
6095        'enumerable'  : true,
6096        'configurable': true,
6097        'get': function() {
6098          return value;
6099        },
6100        'set': function(to) {
6101          value = fn.call(obj, prop, value, to);
6102        }
6103      });
6104    }
6105  });
6106
6107  extend(object, false, function() { return arguments.length > 1; }, {
6108
6109    /***
6110     * @method keys(<obj>, [fn])
6111     * @returns Array
6112     * @short Returns an array containing the keys in <obj>. Optionally calls [fn] for each key.
6113     * @extra This method is provided for browsers that don't support it natively, and additionally is enhanced to accept the callback [fn]. Returned keys are in no particular order. %keys% is available as an instance method on extended objects.
6114     * @example
6115     *
6116     *   Object.keys({ broken: 'wear' }) -> ['broken']
6117     *   Object.keys({ broken: 'wear' }, function(key, value) {
6118     *     // Called once for each key.
6119     *   });
6120     *   Object.extended({ broken: 'wear' }).keys() -> ['broken']
6121     *
6122     ***/
6123    'keys': function(obj, fn) {
6124      var keys = object.keys(obj);
6125      keys.forEach(function(key) {
6126        fn.call(obj, key, obj[key]);
6127      });
6128      return keys;
6129    }
6130
6131  });
6132
6133  extend(object, false, true, {
6134
6135    'isObject': function(obj) {
6136      return isPlainObject(obj);
6137    },
6138
6139    'isNaN': function(obj) {
6140      // This is only true of NaN
6141      return isNumber(obj) && obj.valueOf() !== obj.valueOf();
6142    },
6143
6144    /***
6145     * @method equal(<a>, <b>)
6146     * @returns Boolean
6147     * @short Returns true if <a> and <b> are equal.
6148     * @extra %equal% in Sugar is "egal", meaning the values are equal if they are "not observably distinguishable". Note that on extended objects the name is %equals% for readability.
6149     * @example
6150     *
6151     *   Object.equal({a:2}, {a:2}) -> true
6152     *   Object.equal({a:2}, {a:3}) -> false
6153     *   Object.extended({a:2}).equals({a:3}) -> false
6154     *
6155     ***/
6156    'equal': function(a, b) {
6157      return isEqual(a, b);
6158    },
6159
6160    /***
6161     * @method Object.extended(<obj> = {})
6162     * @returns Extended object
6163     * @short Creates a new object, equivalent to %new Object()% or %{}%, but with extended methods.
6164     * @extra See extended objects for more.
6165     * @example
6166     *
6167     *   Object.extended()
6168     *   Object.extended({ happy:true, pappy:false }).keys() -> ['happy','pappy']
6169     *   Object.extended({ happy:true, pappy:false }).values() -> [true, false]
6170     *
6171     ***/
6172    'extended': function(obj) {
6173      return new Hash(obj);
6174    },
6175
6176    /***
6177     * @method merge(<target>, <source>, [deep] = false, [resolve] = true)
6178     * @returns Merged object
6179     * @short Merges all the properties of <source> into <target>.
6180     * @extra Merges are shallow unless [deep] is %true%. Properties of <source> will win in the case of conflicts, unless [resolve] is %false%. [resolve] can also be a function that resolves the conflict. In this case it will be passed 3 arguments, %key%, %targetVal%, and %sourceVal%, with the context set to <source>. This will allow you to solve conflict any way you want, ie. adding two numbers together, etc. %merge% is available as an instance method on extended objects.
6181     * @example
6182     *
6183     *   Object.merge({a:1},{b:2}) -> { a:1, b:2 }
6184     *   Object.merge({a:1},{a:2}, false, false) -> { a:1 }
6185     +   Object.merge({a:1},{a:2}, false, function(key, a, b) {
6186     *     return a + b;
6187     *   }); -> { a:3 }
6188     *   Object.extended({a:1}).merge({b:2}) -> { a:1, b:2 }
6189     *
6190     ***/
6191    'merge': function(target, source, deep, resolve) {
6192      var key, sourceIsObject, targetIsObject, sourceVal, targetVal, conflict, result;
6193      // Strings cannot be reliably merged thanks to
6194      // their properties not being enumerable in < IE8.
6195      if(target && typeof source !== 'string') {
6196        for(key in source) {
6197          if(!hasOwnProperty(source, key) || !target) continue;
6198          sourceVal      = source[key];
6199          targetVal      = target[key];
6200          conflict       = isDefined(targetVal);
6201          sourceIsObject = isObjectType(sourceVal);
6202          targetIsObject = isObjectType(targetVal);
6203          result         = conflict && resolve === false ? targetVal : sourceVal;
6204
6205          if(conflict) {
6206            if(isFunction(resolve)) {
6207              // Use the result of the callback as the result.
6208              result = resolve.call(source, key, targetVal, sourceVal)
6209            }
6210          }
6211
6212          // Going deep
6213          if(deep && (sourceIsObject || targetIsObject)) {
6214            if(isDate(sourceVal)) {
6215              result = new date(sourceVal.getTime());
6216            } else if(isRegExp(sourceVal)) {
6217              result = new regexp(sourceVal.source, getRegExpFlags(sourceVal));
6218            } else {
6219              if(!targetIsObject) target[key] = array.isArray(sourceVal) ? [] : {};
6220              object.merge(target[key], sourceVal, deep, resolve);
6221              continue;
6222            }
6223          }
6224          target[key] = result;
6225        }
6226      }
6227      return target;
6228    },
6229
6230    /***
6231     * @method values(<obj>, [fn])
6232     * @returns Array
6233     * @short Returns an array containing the values in <obj>. Optionally calls [fn] for each value.
6234     * @extra Returned values are in no particular order. %values% is available as an instance method on extended objects.
6235     * @example
6236     *
6237     *   Object.values({ broken: 'wear' }) -> ['wear']
6238     *   Object.values({ broken: 'wear' }, function(value) {
6239     *     // Called once for each value.
6240     *   });
6241     *   Object.extended({ broken: 'wear' }).values() -> ['wear']
6242     *
6243     ***/
6244    'values': function(obj, fn) {
6245      var values = [];
6246      iterateOverObject(obj, function(k,v) {
6247        values.push(v);
6248        if(fn) fn.call(obj,v);
6249      });
6250      return values;
6251    },
6252
6253    /***
6254     * @method clone(<obj> = {}, [deep] = false)
6255     * @returns Cloned object
6256     * @short Creates a clone (copy) of <obj>.
6257     * @extra Default is a shallow clone, unless [deep] is true. %clone% is available as an instance method on extended objects.
6258     * @example
6259     *
6260     *   Object.clone({foo:'bar'})            -> { foo: 'bar' }
6261     *   Object.clone()                       -> {}
6262     *   Object.extended({foo:'bar'}).clone() -> { foo: 'bar' }
6263     *
6264     ***/
6265    'clone': function(obj, deep) {
6266      var target, klass;
6267      if(!isObjectType(obj)) {
6268        return obj;
6269      }
6270      klass = className(obj);
6271      if(isDate(obj, klass) && obj.clone) {
6272        // Preserve internal UTC flag when applicable.
6273        return obj.clone();
6274      } else if(isDate(obj, klass) || isRegExp(obj, klass)) {
6275        return new obj.constructor(obj);
6276      } else if(obj instanceof Hash) {
6277        target = new Hash;
6278      } else if(isArray(obj, klass)) {
6279        target = [];
6280      } else if(isPlainObject(obj, klass)) {
6281        target = {};
6282      } else {
6283        throw new TypeError('Clone must be a basic data type.');
6284      }
6285      return object.merge(target, obj, deep);
6286    },
6287
6288    /***
6289     * @method Object.fromQueryString(<str>, [booleans] = false)
6290     * @returns Object
6291     * @short Converts the query string of a URL into an object.
6292     * @extra If [booleans] is true, then %"true"% and %"false"% will be cast into booleans. All other values, including numbers will remain their string values.
6293     * @example
6294     *
6295     *   Object.fromQueryString('foo=bar&broken=wear') -> { foo: 'bar', broken: 'wear' }
6296     *   Object.fromQueryString('foo[]=1&foo[]=2')     -> { foo: ['1','2'] }
6297     *   Object.fromQueryString('foo=true', true)      -> { foo: true }
6298     *
6299     ***/
6300    'fromQueryString': function(str, castBoolean) {
6301      var result = object.extended(), split;
6302      str = str && str.toString ? str.toString() : '';
6303      str.replace(/^.*?\?/, '').split('&').forEach(function(p) {
6304        var split = p.split('=');
6305        if(split.length !== 2) return;
6306        setParamsObject(result, split[0], decodeURIComponent(split[1]), castBoolean);
6307      });
6308      return result;
6309    },
6310
6311    /***
6312     * @method Object.toQueryString(<obj>, [namespace] = null)
6313     * @returns Object
6314     * @short Converts the object into a query string.
6315     * @extra Accepts deep nested objects and arrays. If [namespace] is passed, it will be prefixed to all param names.
6316     * @example
6317     *
6318     *   Object.toQueryString({foo:'bar'})          -> 'foo=bar'
6319     *   Object.toQueryString({foo:['a','b','c']})  -> 'foo[0]=a&foo[1]=b&foo[2]=c'
6320     *   Object.toQueryString({name:'Bob'}, 'user') -> 'user[name]=Bob'
6321     *
6322     ***/
6323    'toQueryString': function(obj, namespace) {
6324      return objectToQueryString(namespace, obj);
6325    },
6326
6327    /***
6328     * @method tap(<obj>, <fn>)
6329     * @returns Object
6330     * @short Runs <fn> and returns <obj>.
6331     * @extra  A string can also be used as a shortcut to a method. This method is used to run an intermediary function in the middle of method chaining. As a standalone method on the Object class it doesn't have too much use. The power of %tap% comes when using extended objects or modifying the Object prototype with Object.extend().
6332     * @example
6333     *
6334     *   Object.extend();
6335     *   [2,4,6].map(Math.exp).tap(function(arr) {
6336     *     arr.pop()
6337     *   });
6338     *   [2,4,6].map(Math.exp).tap('pop').map(Math.round); ->  [7,55]
6339     *
6340     ***/
6341    'tap': function(obj, arg) {
6342      var fn = arg;
6343      if(!isFunction(arg)) {
6344        fn = function() {
6345          if(arg) obj[arg]();
6346        }
6347      }
6348      fn.call(obj, obj);
6349      return obj;
6350    },
6351
6352    /***
6353     * @method has(<obj>, <key>)
6354     * @returns Boolean
6355     * @short Checks if <obj> has <key> using hasOwnProperty from Object.prototype.
6356     * @extra This method is considered safer than %Object#hasOwnProperty% when using objects as hashes. See http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/ for more.
6357     * @example
6358     *
6359     *   Object.has({ foo: 'bar' }, 'foo') -> true
6360     *   Object.has({ foo: 'bar' }, 'baz') -> false
6361     *   Object.has({ hasOwnProperty: true }, 'foo') -> false
6362     *
6363     ***/
6364    'has': function (obj, key) {
6365      return hasOwnProperty(obj, key);
6366    },
6367
6368    /***
6369     * @method select(<obj>, <find>, ...)
6370     * @returns Object
6371     * @short Builds a new object containing the values specified in <find>.
6372     * @extra When <find> is a string, that single key will be selected. It can also be a regex, selecting any key that matches, or an object which will match if the key also exists in that object, effectively doing an "intersect" operation on that object. Multiple selections may also be passed as an array or directly as enumerated arguments. %select% is available as an instance method on extended objects.
6373     * @example
6374     *
6375     *   Object.select({a:1,b:2}, 'a')        -> {a:1}
6376     *   Object.select({a:1,b:2}, /[a-z]/)    -> {a:1,ba:2}
6377     *   Object.select({a:1,b:2}, {a:1})      -> {a:1}
6378     *   Object.select({a:1,b:2}, 'a', 'b')   -> {a:1,b:2}
6379     *   Object.select({a:1,b:2}, ['a', 'b']) -> {a:1,b:2}
6380     *
6381     ***/
6382    'select': function (obj) {
6383      return selectFromObject(obj, arguments, true);
6384    },
6385
6386    /***
6387     * @method reject(<obj>, <find>, ...)
6388     * @returns Object
6389     * @short Builds a new object containing all values except those specified in <find>.
6390     * @extra When <find> is a string, that single key will be rejected. It can also be a regex, rejecting any key that matches, or an object which will match if the key also exists in that object, effectively "subtracting" that object. Multiple selections may also be passed as an array or directly as enumerated arguments. %reject% is available as an instance method on extended objects.
6391     * @example
6392     *
6393     *   Object.reject({a:1,b:2}, 'a')        -> {b:2}
6394     *   Object.reject({a:1,b:2}, /[a-z]/)    -> {}
6395     *   Object.reject({a:1,b:2}, {a:1})      -> {b:2}
6396     *   Object.reject({a:1,b:2}, 'a', 'b')   -> {}
6397     *   Object.reject({a:1,b:2}, ['a', 'b']) -> {}
6398     *
6399     ***/
6400    'reject': function (obj) {
6401      return selectFromObject(obj, arguments, false);
6402    }
6403
6404  });
6405
6406
6407  buildTypeMethods();
6408  buildObjectExtend();
6409  buildObjectInstanceMethods(ObjectHashMethods, Hash);
6410
6411  /***
6412   * @package RegExp
6413   * @dependency core
6414   * @description Escaping regexes and manipulating their flags.
6415   *
6416   * Note here that methods on the RegExp class like .exec and .test will fail in the current version of SpiderMonkey being
6417   * used by CouchDB when using shorthand regex notation like /foo/. This is the reason for the intermixed use of shorthand
6418   * and compiled regexes here. If you're using JS in CouchDB, it is safer to ALWAYS compile your regexes from a string.
6419   *
6420   ***/
6421
6422  extend(regexp, false, true, {
6423
6424   /***
6425    * @method RegExp.escape(<str> = '')
6426    * @returns String
6427    * @short Escapes all RegExp tokens in a string.
6428    * @example
6429    *
6430    *   RegExp.escape('really?')      -> 'really\?'
6431    *   RegExp.escape('yes.')         -> 'yes\.'
6432    *   RegExp.escape('(not really)') -> '\(not really\)'
6433    *
6434    ***/
6435    'escape': function(str) {
6436      return escapeRegExp(str);
6437    }
6438
6439  });
6440
6441  extend(regexp, true, true, {
6442
6443   /***
6444    * @method getFlags()
6445    * @returns String
6446    * @short Returns the flags of the regex as a string.
6447    * @example
6448    *
6449    *   /texty/gim.getFlags('testy') -> 'gim'
6450    *
6451    ***/
6452    'getFlags': function() {
6453      return getRegExpFlags(this);
6454    },
6455
6456   /***
6457    * @method setFlags(<flags>)
6458    * @returns RegExp
6459    * @short Sets the flags on a regex and retuns a copy.
6460    * @example
6461    *
6462    *   /texty/.setFlags('gim') -> now has global, ignoreCase, and multiline set
6463    *
6464    ***/
6465    'setFlags': function(flags) {
6466      return regexp(this.source, flags);
6467    },
6468
6469   /***
6470    * @method addFlag(<flag>)
6471    * @returns RegExp
6472    * @short Adds <flag> to the regex.
6473    * @example
6474    *
6475    *   /texty/.addFlag('g') -> now has global flag set
6476    *
6477    ***/
6478    'addFlag': function(flag) {
6479      return this.setFlags(getRegExpFlags(this, flag));
6480    },
6481
6482   /***
6483    * @method removeFlag(<flag>)
6484    * @returns RegExp
6485    * @short Removes <flag> from the regex.
6486    * @example
6487    *
6488    *   /texty/g.removeFlag('g') -> now has global flag removed
6489    *
6490    ***/
6491    'removeFlag': function(flag) {
6492      return this.setFlags(getRegExpFlags(this).replace(flag, ''));
6493    }
6494
6495  });
6496
6497
6498
6499  /***
6500   * @package String
6501   * @dependency core
6502   * @description String manupulation, escaping, encoding, truncation, and:conversion.
6503   *
6504   ***/
6505
6506  function getAcronym(word) {
6507    var inflector = string.Inflector;
6508    var word = inflector && inflector.acronyms[word];
6509    if(isString(word)) {
6510      return word;
6511    }
6512  }
6513
6514  function checkRepeatRange(num) {
6515    num = +num;
6516    if(num < 0 || num === Infinity) {
6517      throw new RangeError('Invalid number');
6518    }
6519    return num;
6520  }
6521
6522  function padString(num, padding) {
6523    return repeatString(isDefined(padding) ? padding : ' ', num);
6524  }
6525
6526  function truncateString(str, length, from, ellipsis, split) {
6527    var str1, str2, len1, len2;
6528    if(str.length <= length) {
6529      return str.toString();
6530    }
6531    ellipsis = isUndefined(ellipsis) ? '...' : ellipsis;
6532    switch(from) {
6533      case 'left':
6534        str2 = split ? truncateOnWord(str, length, true) : str.slice(str.length - length);
6535        return ellipsis + str2;
6536      case 'middle':
6537        len1 = ceil(length / 2);
6538        len2 = floor(length / 2);
6539        str1 = split ? truncateOnWord(str, len1) : str.slice(0, len1);
6540        str2 = split ? truncateOnWord(str, len2, true) : str.slice(str.length - len2);
6541        return str1 + ellipsis + str2;
6542      default:
6543        str1 = split ? truncateOnWord(str, length) : str.slice(0, length);
6544        return str1 + ellipsis;
6545    }
6546  }
6547
6548  function truncateOnWord(str, limit, fromLeft) {
6549    if(fromLeft) {
6550      return truncateOnWord(str.reverse(), limit).reverse();
6551    }
6552    var reg = regexp('(?=[' + getTrimmableCharacters() + '])');
6553    var words = str.split(reg);
6554    var count = 0;
6555    return words.filter(function(word) {
6556      count += word.length;
6557      return count <= limit;
6558    }).join('');
6559  }
6560
6561  function numberOrIndex(str, n, from) {
6562    if(isString(n)) {
6563      n = str.indexOf(n);
6564      if(n === -1) {
6565        n = from ? str.length : 0;
6566      }
6567    }
6568    return n;
6569  }
6570
6571  var btoa, atob;
6572
6573  function buildBase64(key) {
6574    if(globalContext.btoa) {
6575      btoa = globalContext.btoa;
6576      atob = globalContext.atob;
6577      return;
6578    }
6579    var base64reg = /[^A-Za-z0-9\+\/\=]/g;
6580    btoa = function(str) {
6581      var output = '';
6582      var chr1, chr2, chr3;
6583      var enc1, enc2, enc3, enc4;
6584      var i = 0;
6585      do {
6586        chr1 = str.charCodeAt(i++);
6587        chr2 = str.charCodeAt(i++);
6588        chr3 = str.charCodeAt(i++);
6589        enc1 = chr1 >> 2;
6590        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
6591        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
6592        enc4 = chr3 & 63;
6593        if (isNaN(chr2)) {
6594          enc3 = enc4 = 64;
6595        } else if (isNaN(chr3)) {
6596          enc4 = 64;
6597        }
6598        output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
6599        chr1 = chr2 = chr3 = '';
6600        enc1 = enc2 = enc3 = enc4 = '';
6601      } while (i < str.length);
6602      return output;
6603    }
6604    atob = function(input) {
6605      var output = '';
6606      var chr1, chr2, chr3;
6607      var enc1, enc2, enc3, enc4;
6608      var i = 0;
6609      if(input.match(base64reg)) {
6610        throw new Error('String contains invalid base64 characters');
6611      }
6612      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
6613      do {
6614        enc1 = key.indexOf(input.charAt(i++));
6615        enc2 = key.indexOf(input.charAt(i++));
6616        enc3 = key.indexOf(input.charAt(i++));
6617        enc4 = key.indexOf(input.charAt(i++));
6618        chr1 = (enc1 << 2) | (enc2 >> 4);
6619        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
6620        chr3 = ((enc3 & 3) << 6) | enc4;
6621        output = output + chr(chr1);
6622        if (enc3 != 64) {
6623          output = output + chr(chr2);
6624        }
6625        if (enc4 != 64) {
6626          output = output + chr(chr3);
6627        }
6628        chr1 = chr2 = chr3 = '';
6629        enc1 = enc2 = enc3 = enc4 = '';
6630      } while (i < input.length);
6631      return output;
6632    }
6633  }
6634
6635  extend(string, true, false, {
6636    /***
6637     * @method repeat([num] = 0)
6638     * @returns String
6639     * @short Returns the string repeated [num] times.
6640     * @example
6641     *
6642     *   'jumpy'.repeat(2) -> 'jumpyjumpy'
6643     *   'a'.repeat(5)     -> 'aaaaa'
6644     *   'a'.repeat(0)     -> ''
6645     *
6646     ***/
6647    'repeat': function(num) {
6648      num = checkRepeatRange(num);
6649      return repeatString(this, num);
6650    }
6651
6652  });
6653
6654  extend(string, true, function(reg) { return isRegExp(reg) || arguments.length > 2; }, {
6655
6656    /***
6657     * @method startsWith(<find>, [pos] = 0, [case] = true)
6658     * @returns Boolean
6659     * @short Returns true if the string starts with <find>.
6660     * @extra <find> may be either a string or regex. Search begins at [pos], which defaults to the entire string. Case sensitive if [case] is true.
6661     * @example
6662     *
6663     *   'hello'.startsWith('hell')           -> true
6664     *   'hello'.startsWith(/[a-h]/)          -> true
6665     *   'hello'.startsWith('HELL')           -> false
6666     *   'hello'.startsWith('ell', 1)         -> true
6667     *   'hello'.startsWith('HELL', 0, false) -> true
6668     *
6669     ***/
6670    'startsWith': function(reg) {
6671      var args = arguments, pos = args[1], c = args[2], str = this, source;
6672      if(pos) str = str.slice(pos);
6673      if(isUndefined(c)) c = true;
6674      source = isRegExp(reg) ? reg.source.replace('^', '') : escapeRegExp(reg);
6675      return regexp('^' + source, c ? '' : 'i').test(str);
6676    },
6677
6678    /***
6679     * @method endsWith(<find>, [pos] = length, [case] = true)
6680     * @returns Boolean
6681     * @short Returns true if the string ends with <find>.
6682     * @extra <find> may be either a string or regex. Search ends at [pos], which defaults to the entire string. Case sensitive if [case] is true.
6683     * @example
6684     *
6685     *   'jumpy'.endsWith('py')            -> true
6686     *   'jumpy'.endsWith(/[q-z]/)         -> true
6687     *   'jumpy'.endsWith('MPY')           -> false
6688     *   'jumpy'.endsWith('mp', 4)         -> false
6689     *   'jumpy'.endsWith('MPY', 5, false) -> true
6690     *
6691     ***/
6692    'endsWith': function(reg) {
6693      var args = arguments, pos = args[1], c = args[2], str = this, source;
6694      if(isDefined(pos)) str = str.slice(0, pos);
6695      if(isUndefined(c)) c = true;
6696      source = isRegExp(reg) ? reg.source.replace('$', '') : escapeRegExp(reg);
6697      return regexp(source + '$', c ? '' : 'i').test(str);
6698    }
6699
6700  });
6701
6702  extend(string, true, true, {
6703
6704     /***
6705      * @method escapeRegExp()
6706      * @returns String
6707      * @short Escapes all RegExp tokens in the string.
6708      * @example
6709      *
6710      *   'really?'.escapeRegExp()       -> 'really\?'
6711      *   'yes.'.escapeRegExp()         -> 'yes\.'
6712      *   '(not really)'.escapeRegExp() -> '\(not really\)'
6713      *
6714      ***/
6715    'escapeRegExp': function() {
6716      return escapeRegExp(this);
6717    },
6718
6719     /***
6720      * @method escapeURL([param] = false)
6721      * @returns String
6722      * @short Escapes characters in a string to make a valid URL.
6723      * @extra If [param] is true, it will also escape valid URL characters for use as a URL parameter.
6724      * @example
6725      *
6726      *   'http://foo.com/"bar"'.escapeURL()     -> 'http://foo.com/%22bar%22'
6727      *   'http://foo.com/"bar"'.escapeURL(true) -> 'http%3A%2F%2Ffoo.com%2F%22bar%22'
6728      *
6729      ***/
6730    'escapeURL': function(param) {
6731      return param ? encodeURIComponent(this) : encodeURI(this);
6732    },
6733
6734     /***
6735      * @method unescapeURL([partial] = false)
6736      * @returns String
6737      * @short Restores escaped characters in a URL escaped string.
6738      * @extra If [partial] is true, it will only unescape non-valid URL characters. [partial] is included here for completeness, but should very rarely be needed.
6739      * @example
6740      *
6741      *   'http%3A%2F%2Ffoo.com%2Fthe%20bar'.unescapeURL()     -> 'http://foo.com/the bar'
6742      *   'http%3A%2F%2Ffoo.com%2Fthe%20bar'.unescapeURL(true) -> 'http%3A%2F%2Ffoo.com%2Fthe bar'
6743      *
6744      ***/
6745    'unescapeURL': function(param) {
6746      return param ? decodeURI(this) : decodeURIComponent(this);
6747    },
6748
6749     /***
6750      * @method escapeHTML()
6751      * @returns String
6752      * @short Converts HTML characters to their entity equivalents.
6753      * @example
6754      *
6755      *   '<p>some text</p>'.escapeHTML() -> '&lt;p&gt;some text&lt;/p&gt;'
6756      *   'one & two'.escapeHTML()        -> 'one &amp; two'
6757      *
6758      ***/
6759    'escapeHTML': function() {
6760      return this.replace(/&/g,  '&amp;' )
6761                 .replace(/</g,  '&lt;'  )
6762                 .replace(/>/g,  '&gt;'  )
6763                 .replace(/"/g,  '&quot;')
6764                 .replace(/'/g,  '&apos;')
6765                 .replace(/\//g, '&#x2f;');
6766    },
6767
6768     /***
6769      * @method unescapeHTML([partial] = false)
6770      * @returns String
6771      * @short Restores escaped HTML characters.
6772      * @example
6773      *
6774      *   '&lt;p&gt;some text&lt;/p&gt;'.unescapeHTML() -> '<p>some text</p>'
6775      *   'one &amp; two'.unescapeHTML()                -> 'one & two'
6776      *
6777      ***/
6778    'unescapeHTML': function() {
6779      return this.replace(/&lt;/g,   '<')
6780                 .replace(/&gt;/g,   '>')
6781                 .replace(/&quot;/g, '"')
6782                 .replace(/&apos;/g, "'")
6783                 .replace(/&#x2f;/g, '/')
6784                 .replace(/&amp;/g,  '&');
6785    },
6786
6787     /***
6788      * @method encodeBase64()
6789      * @returns String
6790      * @short Encodes the string into base64 encoding.
6791      * @extra This method wraps the browser native %btoa% when available, and uses a custom implementation when not available. It can also handle Unicode string encodings.
6792      * @example
6793      *
6794      *   'gonna get encoded!'.encodeBase64()  -> 'Z29ubmEgZ2V0IGVuY29kZWQh'
6795      *   'http://twitter.com/'.encodeBase64() -> 'aHR0cDovL3R3aXR0ZXIuY29tLw=='
6796      *
6797      ***/
6798    'encodeBase64': function() {
6799      return btoa(unescape(encodeURIComponent(this)));
6800    },
6801
6802     /***
6803      * @method decodeBase64()
6804      * @returns String
6805      * @short Decodes the string from base64 encoding.
6806      * @extra This method wraps the browser native %atob% when available, and uses a custom implementation when not available. It can also handle Unicode string encodings.
6807      * @example
6808      *
6809      *   'aHR0cDovL3R3aXR0ZXIuY29tLw=='.decodeBase64() -> 'http://twitter.com/'
6810      *   'anVzdCBnb3QgZGVjb2RlZA=='.decodeBase64()     -> 'just got decoded!'
6811      *
6812      ***/
6813    'decodeBase64': function() {
6814      return decodeURIComponent(escape(atob(this)));
6815    },
6816
6817    /***
6818     * @method each([search] = single character, [fn])
6819     * @returns Array
6820     * @short Runs callback [fn] against each occurence of [search].
6821     * @extra Returns an array of matches. [search] may be either a string or regex, and defaults to every character in the string.
6822     * @example
6823     *
6824     *   'jumpy'.each() -> ['j','u','m','p','y']
6825     *   'jumpy'.each(/[r-z]/) -> ['u','y']
6826     *   'jumpy'.each(/[r-z]/, function(m) {
6827     *     // Called twice: "u", "y"
6828     *   });
6829     *
6830     ***/
6831    'each': function(search, fn) {
6832      var match, i, len;
6833      if(isFunction(search)) {
6834        fn = search;
6835        search = /[\s\S]/g;
6836      } else if(!search) {
6837        search = /[\s\S]/g
6838      } else if(isString(search)) {
6839        search = regexp(escapeRegExp(search), 'gi');
6840      } else if(isRegExp(search)) {
6841        search = regexp(search.source, getRegExpFlags(search, 'g'));
6842      }
6843      match = this.match(search) || [];
6844      if(fn) {
6845        for(i = 0, len = match.length; i < len; i++) {
6846          match[i] = fn.call(this, match[i], i, match) || match[i];
6847        }
6848      }
6849      return match;
6850    },
6851
6852    /***
6853     * @method shift(<n>)
6854     * @returns Array
6855     * @short Shifts each character in the string <n> places in the character map.
6856     * @example
6857     *
6858     *   'a'.shift(1)  -> 'b'
6859     *   'ク'.shift(1) -> 'グ'
6860     *
6861     ***/
6862    'shift': function(n) {
6863      var result = '';
6864      n = n || 0;
6865      this.codes(function(c) {
6866        result += chr(c + n);
6867      });
6868      return result;
6869    },
6870
6871    /***
6872     * @method codes([fn])
6873     * @returns Array
6874     * @short Runs callback [fn] against each character code in the string. Returns an array of character codes.
6875     * @example
6876     *
6877     *   'jumpy'.codes() -> [106,117,109,112,121]
6878     *   'jumpy'.codes(function(c) {
6879     *     // Called 5 times: 106, 117, 109, 112, 121
6880     *   });
6881     *
6882     ***/
6883    'codes': function(fn) {
6884      var codes = [], i, len;
6885      for(i = 0, len = this.length; i < len; i++) {
6886        var code = this.charCodeAt(i);
6887        codes.push(code);
6888        if(fn) fn.call(this, code, i);
6889      }
6890      return codes;
6891    },
6892
6893    /***
6894     * @method chars([fn])
6895     * @returns Array
6896     * @short Runs callback [fn] against each character in the string. Returns an array of characters.
6897     * @example
6898     *
6899     *   'jumpy'.chars() -> ['j','u','m','p','y']
6900     *   'jumpy'.chars(function(c) {
6901     *     // Called 5 times: "j","u","m","p","y"
6902     *   });
6903     *
6904     ***/
6905    'chars': function(fn) {
6906      return this.each(fn);
6907    },
6908
6909    /***
6910     * @method words([fn])
6911     * @returns Array
6912     * @short Runs callback [fn] against each word in the string. Returns an array of words.
6913     * @extra A "word" here is defined as any sequence of non-whitespace characters.
6914     * @example
6915     *
6916     *   'broken wear'.words() -> ['broken','wear']
6917     *   'broken wear'.words(function(w) {
6918     *     // Called twice: "broken", "wear"
6919     *   });
6920     *
6921     ***/
6922    'words': function(fn) {
6923      return this.trim().each(/\S+/g, fn);
6924    },
6925
6926    /***
6927     * @method lines([fn])
6928     * @returns Array
6929     * @short Runs callback [fn] against each line in the string. Returns an array of lines.
6930     * @example
6931     *
6932     *   'broken wear\nand\njumpy jump'.lines() -> ['broken wear','and','jumpy jump']
6933     *   'broken wear\nand\njumpy jump'.lines(function(l) {
6934     *     // Called three times: "broken wear", "and", "jumpy jump"
6935     *   });
6936     *
6937     ***/
6938    'lines': function(fn) {
6939      return this.trim().each(/^.*$/gm, fn);
6940    },
6941
6942    /***
6943     * @method paragraphs([fn])
6944     * @returns Array
6945     * @short Runs callback [fn] against each paragraph in the string. Returns an array of paragraphs.
6946     * @extra A paragraph here is defined as a block of text bounded by two or more line breaks.
6947     * @example
6948     *
6949     *   'Once upon a time.\n\nIn the land of oz...'.paragraphs() -> ['Once upon a time.','In the land of oz...']
6950     *   'Once upon a time.\n\nIn the land of oz...'.paragraphs(function(p) {
6951     *     // Called twice: "Once upon a time.", "In teh land of oz..."
6952     *   });
6953     *
6954     ***/
6955    'paragraphs': function(fn) {
6956      var paragraphs = this.trim().split(/[\r\n]{2,}/);
6957      paragraphs = paragraphs.map(function(p) {
6958        if(fn) var s = fn.call(p);
6959        return s ? s : p;
6960      });
6961      return paragraphs;
6962    },
6963
6964    /***
6965     * @method isBlank()
6966     * @returns Boolean
6967     * @short Returns true if the string has a length of 0 or contains only whitespace.
6968     * @example
6969     *
6970     *   ''.isBlank()      -> true
6971     *   '   '.isBlank()   -> true
6972     *   'noway'.isBlank() -> false
6973     *
6974     ***/
6975    'isBlank': function() {
6976      return this.trim().length === 0;
6977    },
6978
6979    /***
6980     * @method has(<find>)
6981     * @returns Boolean
6982     * @short Returns true if the string matches <find>.
6983     * @extra <find> may be a string or regex.
6984     * @example
6985     *
6986     *   'jumpy'.has('py')     -> true
6987     *   'broken'.has(/[a-n]/) -> true
6988     *   'broken'.has(/[s-z]/) -> false
6989     *
6990     ***/
6991    'has': function(find) {
6992      return this.search(isRegExp(find) ? find : escapeRegExp(find)) !== -1;
6993    },
6994
6995
6996    /***
6997     * @method add(<str>, [index] = length)
6998     * @returns String
6999     * @short Adds <str> at [index]. Negative values are also allowed.
7000     * @extra %insert% is provided as an alias, and is generally more readable when using an index.
7001     * @example
7002     *
7003     *   'schfifty'.add(' five')      -> schfifty five
7004     *   'dopamine'.insert('e', 3)       -> dopeamine
7005     *   'spelling eror'.insert('r', -3) -> spelling error
7006     *
7007     ***/
7008    'add': function(str, index) {
7009      index = isUndefined(index) ? this.length : index;
7010      return this.slice(0, index) + str + this.slice(index);
7011    },
7012
7013    /***
7014     * @method remove(<f>)
7015     * @returns String
7016     * @short Removes any part of the string that matches <f>.
7017     * @extra <f> can be a string or a regex.
7018     * @example
7019     *
7020     *   'schfifty five'.remove('f')     -> 'schity ive'
7021     *   'schfifty five'.remove(/[a-f]/g) -> 'shity iv'
7022     *
7023     ***/
7024    'remove': function(f) {
7025      return this.replace(f, '');
7026    },
7027
7028    /***
7029     * @method reverse()
7030     * @returns String
7031     * @short Reverses the string.
7032     * @example
7033     *
7034     *   'jumpy'.reverse()        -> 'ypmuj'
7035     *   'lucky charms'.reverse() -> 'smrahc ykcul'
7036     *
7037     ***/
7038    'reverse': function() {
7039      return this.split('').reverse().join('');
7040    },
7041
7042    /***
7043     * @method compact()
7044     * @returns String
7045     * @short Compacts all white space in the string to a single space and trims the ends.
7046     * @example
7047     *
7048     *   'too \n much \n space'.compact() -> 'too much space'
7049     *   'enough \n '.compact()           -> 'enought'
7050     *
7051     ***/
7052    'compact': function() {
7053      return this.trim().replace(/([\r\n\s ])+/g, function(match, whitespace){
7054        return whitespace === ' ' ? whitespace : ' ';
7055      });
7056    },
7057
7058    /***
7059     * @method at(<index>, [loop] = true)
7060     * @returns String or Array
7061     * @short Gets the character(s) at a given index.
7062     * @extra When [loop] is true, overshooting the end of the string (or the beginning) will begin counting from the other end. As an alternate syntax, passing multiple indexes will get the characters at those indexes.
7063     * @example
7064     *
7065     *   'jumpy'.at(0)               -> 'j'
7066     *   'jumpy'.at(2)               -> 'm'
7067     *   'jumpy'.at(5)               -> 'j'
7068     *   'jumpy'.at(5, false)        -> ''
7069     *   'jumpy'.at(-1)              -> 'y'
7070     *   'lucky charms'.at(2,4,6,8) -> ['u','k','y',c']
7071     *
7072     ***/
7073    'at': function() {
7074      return getEntriesForIndexes(this, arguments, true);
7075    },
7076
7077    /***
7078     * @method from([index] = 0)
7079     * @returns String
7080     * @short Returns a section of the string starting from [index].
7081     * @example
7082     *
7083     *   'lucky charms'.from()   -> 'lucky charms'
7084     *   'lucky charms'.from(7)  -> 'harms'
7085     *
7086     ***/
7087    'from': function(from) {
7088      return this.slice(numberOrIndex(this, from, true));
7089    },
7090
7091    /***
7092     * @method to([index] = end)
7093     * @returns String
7094     * @short Returns a section of the string ending at [index].
7095     * @example
7096     *
7097     *   'lucky charms'.to()   -> 'lucky charms'
7098     *   'lucky charms'.to(7)  -> 'lucky ch'
7099     *
7100     ***/
7101    'to': function(to) {
7102      if(isUndefined(to)) to = this.length;
7103      return this.slice(0, numberOrIndex(this, to));
7104    },
7105
7106    /***
7107     * @method dasherize()
7108     * @returns String
7109     * @short Converts underscores and camel casing to hypens.
7110     * @example
7111     *
7112     *   'a_farewell_to_arms'.dasherize() -> 'a-farewell-to-arms'
7113     *   'capsLock'.dasherize()           -> 'caps-lock'
7114     *
7115     ***/
7116    'dasherize': function() {
7117      return this.underscore().replace(/_/g, '-');
7118    },
7119
7120    /***
7121     * @method underscore()
7122     * @returns String
7123     * @short Converts hyphens and camel casing to underscores.
7124     * @example
7125     *
7126     *   'a-farewell-to-arms'.underscore() -> 'a_farewell_to_arms'
7127     *   'capsLock'.underscore()           -> 'caps_lock'
7128     *
7129     ***/
7130    'underscore': function() {
7131      return this
7132        .replace(/[-\s]+/g, '_')
7133        .replace(string.Inflector && string.Inflector.acronymRegExp, function(acronym, index) {
7134          return (index > 0 ? '_' : '') + acronym.toLowerCase();
7135        })
7136        .replace(/([A-Z\d]+)([A-Z][a-z])/g,'$1_$2')
7137        .replace(/([a-z\d])([A-Z])/g,'$1_$2')
7138        .toLowerCase();
7139    },
7140
7141    /***
7142     * @method camelize([first] = true)
7143     * @returns String
7144     * @short Converts underscores and hyphens to camel case. If [first] is true the first letter will also be capitalized.
7145     * @extra If the Inflections package is included acryonyms can also be defined that will be used when camelizing.
7146     * @example
7147     *
7148     *   'caps_lock'.camelize()              -> 'CapsLock'
7149     *   'moz-border-radius'.camelize()      -> 'MozBorderRadius'
7150     *   'moz-border-radius'.camelize(false) -> 'mozBorderRadius'
7151     *
7152     ***/
7153    'camelize': function(first) {
7154      return this.underscore().replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
7155        var acronym = getAcronym(word), capitalize = first !== false || index > 0;
7156        if(acronym) return capitalize ? acronym : acronym.toLowerCase();
7157        return capitalize ? word.capitalize() : word;
7158      });
7159    },
7160
7161    /***
7162     * @method spacify()
7163     * @returns String
7164     * @short Converts camel case, underscores, and hyphens to a properly spaced string.
7165     * @example
7166     *
7167     *   'camelCase'.spacify()                         -> 'camel case'
7168     *   'an-ugly-string'.spacify()                    -> 'an ugly string'
7169     *   'oh-no_youDid-not'.spacify().capitalize(true) -> 'something else'
7170     *
7171     ***/
7172    'spacify': function() {
7173      return this.underscore().replace(/_/g, ' ');
7174    },
7175
7176    /***
7177     * @method stripTags([tag1], [tag2], ...)
7178     * @returns String
7179     * @short Strips all HTML tags from the string.
7180     * @extra Tags to strip may be enumerated in the parameters, otherwise will strip all.
7181     * @example
7182     *
7183     *   '<p>just <b>some</b> text</p>'.stripTags()    -> 'just some text'
7184     *   '<p>just <b>some</b> text</p>'.stripTags('p') -> 'just <b>some</b> text'
7185     *
7186     ***/
7187    'stripTags': function() {
7188      var str = this, args = arguments.length > 0 ? arguments : [''];
7189      flattenedArgs(args, function(tag) {
7190        str = str.replace(regexp('<\/?' + escapeRegExp(tag) + '[^<>]*>', 'gi'), '');
7191      });
7192      return str;
7193    },
7194
7195    /***
7196     * @method removeTags([tag1], [tag2], ...)
7197     * @returns String
7198     * @short Removes all HTML tags and their contents from the string.
7199     * @extra Tags to remove may be enumerated in the parameters, otherwise will remove all.
7200     * @example
7201     *
7202     *   '<p>just <b>some</b> text</p>'.removeTags()    -> ''
7203     *   '<p>just <b>some</b> text</p>'.removeTags('b') -> '<p>just text</p>'
7204     *
7205     ***/
7206    'removeTags': function() {
7207      var str = this, args = arguments.length > 0 ? arguments : ['\\S+'];
7208      flattenedArgs(args, function(t) {
7209        var reg = regexp('<(' + t + ')[^<>]*(?:\\/>|>.*?<\\/\\1>)', 'gi');
7210        str = str.replace(reg, '');
7211      });
7212      return str;
7213    },
7214
7215    /***
7216     * @method truncate(<length>, [from] = 'right', [ellipsis] = '...')
7217     * @returns String
7218     * @short Truncates a string.
7219     * @extra [from] can be %'right'%, %'left'%, or %'middle'%. If the string is shorter than <length>, [ellipsis] will not be added.
7220     * @example
7221     *
7222     *   'sittin on the dock of the bay'.truncate(18)           -> 'just sittin on the do...'
7223     *   'sittin on the dock of the bay'.truncate(18, 'left')   -> '...the dock of the bay'
7224     *   'sittin on the dock of the bay'.truncate(18, 'middle') -> 'just sitt...of the bay'
7225     *
7226     ***/
7227    'truncate': function(length, from, ellipsis) {
7228      return truncateString(this, length, from, ellipsis);
7229    },
7230
7231    /***
7232     * @method truncateOnWord(<length>, [from] = 'right', [ellipsis] = '...')
7233     * @returns String
7234     * @short Truncates a string without splitting up words.
7235     * @extra [from] can be %'right'%, %'left'%, or %'middle'%. If the string is shorter than <length>, [ellipsis] will not be added.
7236     * @example
7237     *
7238     *   'here we go'.truncateOnWord(5)               -> 'here...'
7239     *   'here we go'.truncateOnWord(5, 'left')       -> '...we go'
7240     *
7241     ***/
7242    'truncateOnWord': function(length, from, ellipsis) {
7243      return truncateString(this, length, from, ellipsis, true);
7244    },
7245
7246    /***
7247     * @method pad[Side](<num> = null, [padding] = ' ')
7248     * @returns String
7249     * @short Pads the string out with [padding] to be exactly <num> characters.
7250     *
7251     * @set
7252     *   pad
7253     *   padLeft
7254     *   padRight
7255     *
7256     * @example
7257     *
7258     *   'wasabi'.pad(8)           -> ' wasabi '
7259     *   'wasabi'.padLeft(8)       -> '  wasabi'
7260     *   'wasabi'.padRight(8)      -> 'wasabi  '
7261     *   'wasabi'.padRight(8, '-') -> 'wasabi--'
7262     *
7263     ***/
7264    'pad': function(num, padding) {
7265      var half, front, back;
7266      num   = checkRepeatRange(num);
7267      half  = max(0, num - this.length) / 2;
7268      front = floor(half);
7269      back  = ceil(half);
7270      return padString(front, padding) + this + padString(back, padding);
7271    },
7272
7273    'padLeft': function(num, padding) {
7274      num = checkRepeatRange(num);
7275      return padString(max(0, num - this.length), padding) + this;
7276    },
7277
7278    'padRight': function(num, padding) {
7279      num = checkRepeatRange(num);
7280      return this + padString(max(0, num - this.length), padding);
7281    },
7282
7283    /***
7284     * @method first([n] = 1)
7285     * @returns String
7286     * @short Returns the first [n] characters of the string.
7287     * @example
7288     *
7289     *   'lucky charms'.first()   -> 'l'
7290     *   'lucky charms'.first(3)  -> 'luc'
7291     *
7292     ***/
7293    'first': function(num) {
7294      if(isUndefined(num)) num = 1;
7295      return this.substr(0, num);
7296    },
7297
7298    /***
7299     * @method last([n] = 1)
7300     * @returns String
7301     * @short Returns the last [n] characters of the string.
7302     * @example
7303     *
7304     *   'lucky charms'.last()   -> 's'
7305     *   'lucky charms'.last(3)  -> 'rms'
7306     *
7307     ***/
7308    'last': function(num) {
7309      if(isUndefined(num)) num = 1;
7310      var start = this.length - num < 0 ? 0 : this.length - num;
7311      return this.substr(start);
7312    },
7313
7314    /***
7315     * @method toNumber([base] = 10)
7316     * @returns Number
7317     * @short Converts the string into a number.
7318     * @extra Any value with a "." fill be converted to a floating point value, otherwise an integer.
7319     * @example
7320     *
7321     *   '153'.toNumber()    -> 153
7322     *   '12,000'.toNumber() -> 12000
7323     *   '10px'.toNumber()   -> 10
7324     *   'ff'.toNumber(16)   -> 255
7325     *
7326     ***/
7327    'toNumber': function(base) {
7328      return stringToNumber(this, base);
7329    },
7330
7331    /***
7332     * @method capitalize([all] = false)
7333     * @returns String
7334     * @short Capitalizes the first character in the string and downcases all other letters.
7335     * @extra If [all] is true, all words in the string will be capitalized.
7336     * @example
7337     *
7338     *   'hello'.capitalize()           -> 'Hello'
7339     *   'hello kitty'.capitalize()     -> 'Hello kitty'
7340     *   'hello kitty'.capitalize(true) -> 'Hello Kitty'
7341     *
7342     *
7343     ***/
7344    'capitalize': function(all) {
7345      var lastResponded;
7346      return this.toLowerCase().replace(all ? /[^']/g : /^\S/, function(lower) {
7347        var upper = lower.toUpperCase(), result;
7348        result = lastResponded ? lower : upper;
7349        lastResponded = upper !== lower;
7350        return result;
7351      });
7352    },
7353
7354    /***
7355     * @method assign(<obj1>, <obj2>, ...)
7356     * @returns String
7357     * @short Assigns variables to tokens in a string, demarcated with `{}`.
7358     * @extra If an object is passed, it's properties can be assigned using the object's keys (i.e. {name}). If a non-object (string, number, etc.) is passed it can be accessed by the argument number beginning with {1} (as with regex tokens). Multiple objects can be passed and will be merged together (original objects are unaffected).
7359     * @example
7360     *
7361     *   'Welcome, Mr. {name}.'.assign({ name: 'Franklin' })   -> 'Welcome, Mr. Franklin.'
7362     *   'You are {1} years old today.'.assign(14)             -> 'You are 14 years old today.'
7363     *   '{n} and {r}'.assign({ n: 'Cheech' }, { r: 'Chong' }) -> 'Cheech and Chong'
7364     *
7365     ***/
7366    'assign': function() {
7367      var assign = {};
7368      flattenedArgs(arguments, function(a, i) {
7369        if(isObjectType(a)) {
7370          simpleMerge(assign, a);
7371        } else {
7372          assign[i + 1] = a;
7373        }
7374      });
7375      return this.replace(/\{([^{]+?)\}/g, function(m, key) {
7376        return hasOwnProperty(assign, key) ? assign[key] : m;
7377      });
7378    }
7379
7380  });
7381
7382
7383  // Aliases
7384
7385  extend(string, true, true, {
7386
7387    /***
7388     * @method insert()
7389     * @alias add
7390     *
7391     ***/
7392    'insert': string.prototype.add
7393  });
7394
7395  buildBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
7396
7397
7398  /***
7399   *
7400   * @package Inflections
7401   * @dependency string
7402   * @description Pluralization similar to ActiveSupport including uncountable words and acronyms. Humanized and URL-friendly strings.
7403   *
7404   ***/
7405
7406  /***
7407   * String module
7408   *
7409   ***/
7410
7411
7412  var plurals      = [],
7413      singulars    = [],
7414      uncountables = [],
7415      humans       = [],
7416      acronyms     = {},
7417      Downcased,
7418      Inflector;
7419
7420  function removeFromArray(arr, find) {
7421    var index = arr.indexOf(find);
7422    if(index > -1) {
7423      arr.splice(index, 1);
7424    }
7425  }
7426
7427  function removeFromUncountablesAndAddTo(arr, rule, replacement) {
7428    if(isString(rule)) {
7429      removeFromArray(uncountables, rule);
7430    }
7431    removeFromArray(uncountables, replacement);
7432    arr.unshift({ rule: rule, replacement: replacement })
7433  }
7434
7435  function paramMatchesType(param, type) {
7436    return param == type || param == 'all' || !param;
7437  }
7438
7439  function isUncountable(word) {
7440    return uncountables.some(function(uncountable) {
7441      return new regexp('\\b' + uncountable + '$', 'i').test(word);
7442    });
7443  }
7444
7445  function inflect(word, pluralize) {
7446    word = isString(word) ? word.toString() : '';
7447    if(word.isBlank() || isUncountable(word)) {
7448      return word;
7449    } else {
7450      return runReplacements(word, pluralize ? plurals : singulars);
7451    }
7452  }
7453
7454  function runReplacements(word, table) {
7455    iterateOverObject(table, function(i, inflection) {
7456      if(word.match(inflection.rule)) {
7457        word = word.replace(inflection.rule, inflection.replacement);
7458        return false;
7459      }
7460    });
7461    return word;
7462  }
7463
7464  function capitalize(word) {
7465    return word.replace(/^\W*[a-z]/, function(w){
7466      return w.toUpperCase();
7467    });
7468  }
7469
7470  Inflector = {
7471
7472    /*
7473     * Specifies a new acronym. An acronym must be specified as it will appear in a camelized string.  An underscore
7474     * string that contains the acronym will retain the acronym when passed to %camelize%, %humanize%, or %titleize%.
7475     * A camelized string that contains the acronym will maintain the acronym when titleized or humanized, and will
7476     * convert the acronym into a non-delimited single lowercase word when passed to String#underscore.
7477     *
7478     * Examples:
7479     *   String.Inflector.acronym('HTML')
7480     *   'html'.titleize()     -> 'HTML'
7481     *   'html'.camelize()     -> 'HTML'
7482     *   'MyHTML'.underscore() -> 'my_html'
7483     *
7484     * The acronym, however, must occur as a delimited unit and not be part of another word for conversions to recognize it:
7485     *
7486     *   String.Inflector.acronym('HTTP')
7487     *   'my_http_delimited'.camelize() -> 'MyHTTPDelimited'
7488     *   'https'.camelize()             -> 'Https', not 'HTTPs'
7489     *   'HTTPS'.underscore()           -> 'http_s', not 'https'
7490     *
7491     *   String.Inflector.acronym('HTTPS')
7492     *   'https'.camelize()   -> 'HTTPS'
7493     *   'HTTPS'.underscore() -> 'https'
7494     *
7495     * Note: Acronyms that are passed to %pluralize% will no longer be recognized, since the acronym will not occur as
7496     * a delimited unit in the pluralized result. To work around this, you must specify the pluralized form as an
7497     * acronym as well:
7498     *
7499     *    String.Inflector.acronym('API')
7500     *    'api'.pluralize().camelize() -> 'Apis'
7501     *
7502     *    String.Inflector.acronym('APIs')
7503     *    'api'.pluralize().camelize() -> 'APIs'
7504     *
7505     * %acronym% may be used to specify any word that contains an acronym or otherwise needs to maintain a non-standard
7506     * capitalization. The only restriction is that the word must begin with a capital letter.
7507     *
7508     * Examples:
7509     *   String.Inflector.acronym('RESTful')
7510     *   'RESTful'.underscore()           -> 'restful'
7511     *   'RESTfulController'.underscore() -> 'restful_controller'
7512     *   'RESTfulController'.titleize()   -> 'RESTful Controller'
7513     *   'restful'.camelize()             -> 'RESTful'
7514     *   'restful_controller'.camelize()  -> 'RESTfulController'
7515     *
7516     *   String.Inflector.acronym('McDonald')
7517     *   'McDonald'.underscore() -> 'mcdonald'
7518     *   'mcdonald'.camelize()   -> 'McDonald'
7519     */
7520    'acronym': function(word) {
7521      acronyms[word.toLowerCase()] = word;
7522      var all = object.keys(acronyms).map(function(key) {
7523        return acronyms[key];
7524      });
7525      Inflector.acronymRegExp = regexp(all.join('|'), 'g');
7526    },
7527
7528    /*
7529     * Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
7530     * The replacement should always be a string that may include references to the matched data from the rule.
7531     */
7532    'plural': function(rule, replacement) {
7533      removeFromUncountablesAndAddTo(plurals, rule, replacement);
7534    },
7535
7536    /*
7537     * Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
7538     * The replacement should always be a string that may include references to the matched data from the rule.
7539     */
7540    'singular': function(rule, replacement) {
7541      removeFromUncountablesAndAddTo(singulars, rule, replacement);
7542    },
7543
7544    /*
7545     * Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
7546     * for strings, not regular expressions. You simply pass the irregular in singular and plural form.
7547     *
7548     * Examples:
7549     *   String.Inflector.irregular('octopus', 'octopi')
7550     *   String.Inflector.irregular('person', 'people')
7551     */
7552    'irregular': function(singular, plural) {
7553      var singularFirst      = singular.first(),
7554          singularRest       = singular.from(1),
7555          pluralFirst        = plural.first(),
7556          pluralRest         = plural.from(1),
7557          pluralFirstUpper   = pluralFirst.toUpperCase(),
7558          pluralFirstLower   = pluralFirst.toLowerCase(),
7559          singularFirstUpper = singularFirst.toUpperCase(),
7560          singularFirstLower = singularFirst.toLowerCase();
7561      removeFromArray(uncountables, singular);
7562      removeFromArray(uncountables, plural);
7563      if(singularFirstUpper == pluralFirstUpper) {
7564        Inflector.plural(new regexp('({1}){2}$'.assign(singularFirst, singularRest), 'i'), '$1' + pluralRest);
7565        Inflector.plural(new regexp('({1}){2}$'.assign(pluralFirst, pluralRest), 'i'), '$1' + pluralRest);
7566        Inflector.singular(new regexp('({1}){2}$'.assign(pluralFirst, pluralRest), 'i'), '$1' + singularRest);
7567      } else {
7568        Inflector.plural(new regexp('{1}{2}$'.assign(singularFirstUpper, singularRest)), pluralFirstUpper + pluralRest);
7569        Inflector.plural(new regexp('{1}{2}$'.assign(singularFirstLower, singularRest)), pluralFirstLower + pluralRest);
7570        Inflector.plural(new regexp('{1}{2}$'.assign(pluralFirstUpper, pluralRest)), pluralFirstUpper + pluralRest);
7571        Inflector.plural(new regexp('{1}{2}$'.assign(pluralFirstLower, pluralRest)), pluralFirstLower + pluralRest);
7572        Inflector.singular(new regexp('{1}{2}$'.assign(pluralFirstUpper, pluralRest)), singularFirstUpper + singularRest);
7573        Inflector.singular(new regexp('{1}{2}$'.assign(pluralFirstLower, pluralRest)), singularFirstLower + singularRest);
7574      }
7575    },
7576
7577    /*
7578     * Add uncountable words that shouldn't be attempted inflected.
7579     *
7580     * Examples:
7581     *   String.Inflector.uncountable('money')
7582     *   String.Inflector.uncountable('money', 'information')
7583     *   String.Inflector.uncountable(['money', 'information', 'rice'])
7584     */
7585    'uncountable': function(first) {
7586      var add = array.isArray(first) ? first : multiArgs(arguments);
7587      uncountables = uncountables.concat(add);
7588    },
7589
7590    /*
7591     * Specifies a humanized form of a string by a regular expression rule or by a string mapping.
7592     * When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
7593     * When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
7594     *
7595     * Examples:
7596     *   String.Inflector.human(/_cnt$/i, '_count')
7597     *   String.Inflector.human('legacy_col_person_name', 'Name')
7598     */
7599    'human': function(rule, replacement) {
7600      humans.unshift({ rule: rule, replacement: replacement })
7601    },
7602
7603
7604    /*
7605     * Clears the loaded inflections within a given scope (default is 'all').
7606     * Options are: 'all', 'plurals', 'singulars', 'uncountables', 'humans'.
7607     *
7608     * Examples:
7609     *   String.Inflector.clear('all')
7610     *   String.Inflector.clear('plurals')
7611     */
7612    'clear': function(type) {
7613      if(paramMatchesType(type, 'singulars'))    singulars    = [];
7614      if(paramMatchesType(type, 'plurals'))      plurals      = [];
7615      if(paramMatchesType(type, 'uncountables')) uncountables = [];
7616      if(paramMatchesType(type, 'humans'))       humans       = [];
7617      if(paramMatchesType(type, 'acronyms'))     acronyms     = {};
7618    }
7619
7620  };
7621
7622  Downcased = [
7623    'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at',
7624    'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over',
7625    'with', 'for'
7626  ];
7627
7628  Inflector.plural(/$/, 's');
7629  Inflector.plural(/s$/gi, 's');
7630  Inflector.plural(/(ax|test)is$/gi, '$1es');
7631  Inflector.plural(/(octop|vir|fung|foc|radi|alumn)(i|us)$/gi, '$1i');
7632  Inflector.plural(/(census|alias|status)$/gi, '$1es');
7633  Inflector.plural(/(bu)s$/gi, '$1ses');
7634  Inflector.plural(/(buffal|tomat)o$/gi, '$1oes');
7635  Inflector.plural(/([ti])um$/gi, '$1a');
7636  Inflector.plural(/([ti])a$/gi, '$1a');
7637  Inflector.plural(/sis$/gi, 'ses');
7638  Inflector.plural(/f+e?$/gi, 'ves');
7639  Inflector.plural(/(cuff|roof)$/gi, '$1s');
7640  Inflector.plural(/([ht]ive)$/gi, '$1s');
7641  Inflector.plural(/([^aeiouy]o)$/gi, '$1es');
7642  Inflector.plural(/([^aeiouy]|qu)y$/gi, '$1ies');
7643  Inflector.plural(/(x|ch|ss|sh)$/gi, '$1es');
7644  Inflector.plural(/(matr|vert|ind)(?:ix|ex)$/gi, '$1ices');
7645  Inflector.plural(/([ml])ouse$/gi, '$1ice');
7646  Inflector.plural(/([ml])ice$/gi, '$1ice');
7647  Inflector.plural(/^(ox)$/gi, '$1en');
7648  Inflector.plural(/^(oxen)$/gi, '$1');
7649  Inflector.plural(/(quiz)$/gi, '$1zes');
7650  Inflector.plural(/(phot|cant|hom|zer|pian|portic|pr|quart|kimon)o$/gi, '$1os');
7651  Inflector.plural(/(craft)$/gi, '$1');
7652  Inflector.plural(/([ft])[eo]{2}(th?)$/gi, '$1ee$2');
7653
7654  Inflector.singular(/s$/gi, '');
7655  Inflector.singular(/([pst][aiu]s)$/gi, '$1');
7656  Inflector.singular(/([aeiouy])ss$/gi, '$1ss');
7657  Inflector.singular(/(n)ews$/gi, '$1ews');
7658  Inflector.singular(/([ti])a$/gi, '$1um');
7659  Inflector.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/gi, '$1$2sis');
7660  Inflector.singular(/(^analy)ses$/gi, '$1sis');
7661  Inflector.singular(/(i)(f|ves)$/i, '$1fe');
7662  Inflector.singular(/([aeolr]f?)(f|ves)$/i, '$1f');
7663  Inflector.singular(/([ht]ive)s$/gi, '$1');
7664  Inflector.singular(/([^aeiouy]|qu)ies$/gi, '$1y');
7665  Inflector.singular(/(s)eries$/gi, '$1eries');
7666  Inflector.singular(/(m)ovies$/gi, '$1ovie');
7667  Inflector.singular(/(x|ch|ss|sh)es$/gi, '$1');
7668  Inflector.singular(/([ml])(ous|ic)e$/gi, '$1ouse');
7669  Inflector.singular(/(bus)(es)?$/gi, '$1');
7670  Inflector.singular(/(o)es$/gi, '$1');
7671  Inflector.singular(/(shoe)s?$/gi, '$1');
7672  Inflector.singular(/(cris|ax|test)[ie]s$/gi, '$1is');
7673  Inflector.singular(/(octop|vir|fung|foc|radi|alumn)(i|us)$/gi, '$1us');
7674  Inflector.singular(/(census|alias|status)(es)?$/gi, '$1');
7675  Inflector.singular(/^(ox)(en)?/gi, '$1');
7676  Inflector.singular(/(vert|ind)(ex|ices)$/gi, '$1ex');
7677  Inflector.singular(/(matr)(ix|ices)$/gi, '$1ix');
7678  Inflector.singular(/(quiz)(zes)?$/gi, '$1');
7679  Inflector.singular(/(database)s?$/gi, '$1');
7680  Inflector.singular(/ee(th?)$/gi, 'oo$1');
7681
7682  Inflector.irregular('person', 'people');
7683  Inflector.irregular('man', 'men');
7684  Inflector.irregular('child', 'children');
7685  Inflector.irregular('sex', 'sexes');
7686  Inflector.irregular('move', 'moves');
7687  Inflector.irregular('save', 'saves');
7688  Inflector.irregular('cow', 'kine');
7689  Inflector.irregular('goose', 'geese');
7690  Inflector.irregular('zombie', 'zombies');
7691
7692  Inflector.uncountable('equipment,information,rice,money,species,series,fish,sheep,jeans'.split(','));
7693
7694
7695  extend(string, true, true, {
7696
7697    /***
7698     * @method pluralize()
7699     * @returns String
7700     * @short Returns the plural form of the word in the string.
7701     * @example
7702     *
7703     *   'post'.pluralize()         -> 'posts'
7704     *   'octopus'.pluralize()      -> 'octopi'
7705     *   'sheep'.pluralize()        -> 'sheep'
7706     *   'words'.pluralize()        -> 'words'
7707     *   'CamelOctopus'.pluralize() -> 'CamelOctopi'
7708     *
7709     ***/
7710    'pluralize': function() {
7711      return inflect(this, true);
7712    },
7713
7714    /***
7715     * @method singularize()
7716     * @returns String
7717     * @short The reverse of String#pluralize. Returns the singular form of a word in a string.
7718     * @example
7719     *
7720     *   'posts'.singularize()       -> 'post'
7721     *   'octopi'.singularize()      -> 'octopus'
7722     *   'sheep'.singularize()       -> 'sheep'
7723     *   'word'.singularize()        -> 'word'
7724     *   'CamelOctopi'.singularize() -> 'CamelOctopus'
7725     *
7726     ***/
7727    'singularize': function() {
7728      return inflect(this, false);
7729    },
7730
7731    /***
7732     * @method humanize()
7733     * @returns String
7734     * @short Creates a human readable string.
7735     * @extra Capitalizes the first word and turns underscores into spaces and strips a trailing '_id', if any. Like String#titleize, this is meant for creating pretty output.
7736     * @example
7737     *
7738     *   'employee_salary'.humanize() -> 'Employee salary'
7739     *   'author_id'.humanize()       -> 'Author'
7740     *
7741     ***/
7742    'humanize': function() {
7743      var str = runReplacements(this, humans), acronym;
7744      str = str.replace(/_id$/g, '');
7745      str = str.replace(/(_)?([a-z\d]*)/gi, function(match, _, word){
7746        acronym = hasOwnProperty(acronyms, word) ? acronyms[word] : null;
7747        return (_ ? ' ' : '') + (acronym || word.toLowerCase());
7748      });
7749      return capitalize(str);
7750    },
7751
7752    /***
7753     * @method titleize()
7754     * @returns String
7755     * @short Creates a title version of the string.
7756     * @extra Capitalizes all the words and replaces some characters in the string to create a nicer looking title. String#titleize is meant for creating pretty output.
7757     * @example
7758     *
7759     *   'man from the boondocks'.titleize() -> 'Man from the Boondocks'
7760     *   'x-men: the last stand'.titleize() -> 'X Men: The Last Stand'
7761     *   'TheManWithoutAPast'.titleize() -> 'The Man Without a Past'
7762     *   'raiders_of_the_lost_ark'.titleize() -> 'Raiders of the Lost Ark'
7763     *
7764     ***/
7765    'titleize': function() {
7766      var fullStopPunctuation = /[.:;!]$/, hasPunctuation, lastHadPunctuation, isFirstOrLast;
7767      return this.spacify().humanize().words(function(word, index, words) {
7768        hasPunctuation = fullStopPunctuation.test(word);
7769        isFirstOrLast = index == 0 || index == words.length - 1 || hasPunctuation || lastHadPunctuation;
7770        lastHadPunctuation = hasPunctuation;
7771        if(isFirstOrLast || Downcased.indexOf(word) === -1) {
7772          return capitalize(word);
7773        } else {
7774          return word;
7775        }
7776      }).join(' ');
7777    },
7778
7779    /***
7780     * @method parameterize()
7781     * @returns String
7782     * @short Replaces special characters in a string so that it may be used as part of a pretty URL.
7783     * @example
7784     *
7785     *   'hell, no!'.parameterize() -> 'hell-no'
7786     *
7787     ***/
7788    'parameterize': function(separator) {
7789      var str = this;
7790      if(separator === undefined) separator = '-';
7791      if(str.normalize) {
7792        str = str.normalize();
7793      }
7794      str = str.replace(/[^a-z0-9\-_]+/gi, separator)
7795      if(separator) {
7796        str = str.replace(new regexp('^{sep}+|{sep}+$|({sep}){sep}+'.assign({ 'sep': escapeRegExp(separator) }), 'g'), '$1');
7797      }
7798      return encodeURI(str.toLowerCase());
7799    }
7800
7801  });
7802
7803  string.Inflector = Inflector;
7804  string.Inflector.acronyms = acronyms;
7805
7806
7807  /***
7808   *
7809   * @package Language
7810   * @dependency string
7811   * @description Detecting language by character block. Full-width <-> half-width character conversion. Hiragana and Katakana conversions.
7812   *
7813   ***/
7814
7815  /***
7816   * String module
7817   *
7818   ***/
7819
7820
7821  /***
7822   * @method has[Script]()
7823   * @returns Boolean
7824   * @short Returns true if the string contains any characters in that script.
7825   *
7826   * @set
7827   *   hasArabic
7828   *   hasCyrillic
7829   *   hasGreek
7830   *   hasHangul
7831   *   hasHan
7832   *   hasKanji
7833   *   hasHebrew
7834   *   hasHiragana
7835   *   hasKana
7836   *   hasKatakana
7837   *   hasLatin
7838   *   hasThai
7839   *   hasDevanagari
7840   *
7841   * @example
7842   *
7843   *   'أتكلم'.hasArabic()          -> true
7844   *   'визит'.hasCyrillic()        -> true
7845   *   '잘 먹겠습니다!'.hasHangul() -> true
7846   *   'ミックスです'.hasKatakana() -> true
7847   *   "l'année".hasLatin()         -> true
7848   *
7849   ***
7850   * @method is[Script]()
7851   * @returns Boolean
7852   * @short Returns true if the string contains only characters in that script. Whitespace is ignored.
7853   *
7854   * @set
7855   *   isArabic
7856   *   isCyrillic
7857   *   isGreek
7858   *   isHangul
7859   *   isHan
7860   *   isKanji
7861   *   isHebrew
7862   *   isHiragana
7863   *   isKana
7864   *   isKatakana
7865   *   isKatakana
7866   *   isThai
7867   *   isDevanagari
7868   *
7869   * @example
7870   *
7871   *   'أتكلم'.isArabic()          -> true
7872   *   'визит'.isCyrillic()        -> true
7873   *   '잘 먹겠습니다!'.isHangul() -> true
7874   *   'ミックスです'.isKatakana() -> false
7875   *   "l'année".isLatin()         -> true
7876   *
7877   ***/
7878  var unicodeScripts = [
7879    { names: ['Arabic'],      source: '\u0600-\u06FF' },
7880    { names: ['Cyrillic'],    source: '\u0400-\u04FF' },
7881    { names: ['Devanagari'],  source: '\u0900-\u097F' },
7882    { names: ['Greek'],       source: '\u0370-\u03FF' },
7883    { names: ['Hangul'],      source: '\uAC00-\uD7AF\u1100-\u11FF' },
7884    { names: ['Han','Kanji'], source: '\u4E00-\u9FFF\uF900-\uFAFF' },
7885    { names: ['Hebrew'],      source: '\u0590-\u05FF' },
7886    { names: ['Hiragana'],    source: '\u3040-\u309F\u30FB-\u30FC' },
7887    { names: ['Kana'],        source: '\u3040-\u30FF\uFF61-\uFF9F' },
7888    { names: ['Katakana'],    source: '\u30A0-\u30FF\uFF61-\uFF9F' },
7889    { names: ['Latin'],       source: '\u0001-\u007F\u0080-\u00FF\u0100-\u017F\u0180-\u024F' },
7890    { names: ['Thai'],        source: '\u0E00-\u0E7F' }
7891  ];
7892
7893  function buildUnicodeScripts() {
7894    unicodeScripts.forEach(function(s) {
7895      var is = regexp('^['+s.source+'\\s]+$');
7896      var has = regexp('['+s.source+']');
7897      s.names.forEach(function(name) {
7898        defineProperty(string.prototype, 'is' + name, function() { return is.test(this.trim()); });
7899        defineProperty(string.prototype, 'has' + name, function() { return has.test(this); });
7900      });
7901    });
7902  }
7903
7904  // Support for converting character widths and katakana to hiragana.
7905
7906  var HALF_WIDTH_TO_FULL_WIDTH_TRAVERSAL = 65248;
7907
7908  var widthConversionRanges = [
7909    { type: 'a', start: 65,  end: 90  },
7910    { type: 'a', start: 97,  end: 122 },
7911    { type: 'n', start: 48,  end: 57  },
7912    { type: 'p', start: 33,  end: 47  },
7913    { type: 'p', start: 58,  end: 64  },
7914    { type: 'p', start: 91,  end: 96  },
7915    { type: 'p', start: 123, end: 126 }
7916  ];
7917
7918  var WidthConversionTable;
7919  var allHankaku   = /[\u0020-\u00A5]|[\uFF61-\uFF9F][゙゚]?/g;
7920  var allZenkaku   = /[\u3000-\u301C]|[\u301A-\u30FC]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g;
7921  var hankakuPunctuation  = '。、「」¥¢£';
7922  var zenkakuPunctuation  = '。、「」¥¢£';
7923  var voicedKatakana      = /[カキクケコサシスセソタチツテトハヒフヘホ]/;
7924  var semiVoicedKatakana  = /[ハヒフヘホヲ]/;
7925  var hankakuKatakana     = 'アイウエオァィゥェォカキクケコサシスセソタチツッテトナニヌネノハヒフヘホマミムメモヤャユュヨョラリルレロワヲンー・';
7926  var zenkakuKatakana     = 'アイウエオァィゥェォカキクケコサシスセソタチツッテトナニヌネノハヒフヘホマミムメモヤャユュヨョラリルレロワヲンー・';
7927
7928  function convertCharacterWidth(str, args, reg, type) {
7929    if(!WidthConversionTable) {
7930      buildWidthConversionTables();
7931    }
7932    var mode = multiArgs(args).join(''), table = WidthConversionTable[type];
7933    mode = mode.replace(/all/, '').replace(/(\w)lphabet|umbers?|atakana|paces?|unctuation/g, '$1');
7934    return str.replace(reg, function(c) {
7935      if(table[c] && (!mode || mode.has(table[c].type))) {
7936        return table[c].to;
7937      } else {
7938        return c;
7939      }
7940    });
7941  }
7942
7943  function buildWidthConversionTables() {
7944    var hankaku;
7945    WidthConversionTable = {
7946      'zenkaku': {},
7947      'hankaku': {}
7948    };
7949    widthConversionRanges.forEach(function(r) {
7950      simpleRepeat(r.end - r.start + 1, function(n) {
7951        n += r.start;
7952        setWidthConversion(r.type, chr(n), chr(n + HALF_WIDTH_TO_FULL_WIDTH_TRAVERSAL));
7953      });
7954    });
7955    zenkakuKatakana.each(function(c, i) {
7956      hankaku = hankakuKatakana.charAt(i);
7957      setWidthConversion('k', hankaku, c);
7958      if(c.match(voicedKatakana)) {
7959        setWidthConversion('k', hankaku + '゙', c.shift(1));
7960      }
7961      if(c.match(semiVoicedKatakana)) {
7962        setWidthConversion('k', hankaku + '゚', c.shift(2));
7963      }
7964    });
7965    zenkakuPunctuation.each(function(c, i) {
7966      setWidthConversion('p', hankakuPunctuation.charAt(i), c);
7967    });
7968    setWidthConversion('k', 'ヴ', 'ヴ');
7969    setWidthConversion('k', 'ヺ', 'ヺ');
7970    setWidthConversion('s', ' ', ' ');
7971  }
7972
7973  function setWidthConversion(type, half, full) {
7974    WidthConversionTable['zenkaku'][half] = { type: type, to: full };
7975    WidthConversionTable['hankaku'][full] = { type: type, to: half };
7976  }
7977
7978
7979  extend(string, true, true, {
7980
7981    /***
7982     * @method hankaku([mode] = 'all')
7983     * @returns String
7984     * @short Converts full-width characters (zenkaku) to half-width (hankaku).
7985     * @extra [mode] accepts any combination of "a" (alphabet), "n" (numbers), "k" (katakana), "s" (spaces), "p" (punctuation), or "all".
7986     * @example
7987     *
7988     *   'タロウ YAMADAです!'.hankaku()                      -> 'タロウ YAMADAです!'
7989     *   'タロウ YAMADAです!'.hankaku('a')                   -> 'タロウ YAMADAです!'
7990     *   'タロウ YAMADAです!'.hankaku('alphabet')            -> 'タロウ YAMADAです!'
7991     *   'タロウです! 25歳です!'.hankaku('katakana', 'numbers') -> 'タロウです! 25歳です!'
7992     *   'タロウです! 25歳です!'.hankaku('k', 'n')              -> 'タロウです! 25歳です!'
7993     *   'タロウです! 25歳です!'.hankaku('kn')                  -> 'タロウです! 25歳です!'
7994     *   'タロウです! 25歳です!'.hankaku('sp')                  -> 'タロウです! 25歳です!'
7995     *
7996     ***/
7997    'hankaku': function() {
7998      return convertCharacterWidth(this, arguments, allZenkaku, 'hankaku');
7999    },
8000
8001    /***
8002     * @method zenkaku([mode] = 'all')
8003     * @returns String
8004     * @short Converts half-width characters (hankaku) to full-width (zenkaku).
8005     * @extra [mode] accepts any combination of "a" (alphabet), "n" (numbers), "k" (katakana), "s" (spaces), "p" (punctuation), or "all".
8006     * @example
8007     *
8008     *   'タロウ YAMADAです!'.zenkaku()                         -> 'タロウ YAMADAです!'
8009     *   'タロウ YAMADAです!'.zenkaku('a')                      -> 'タロウ YAMADAです!'
8010     *   'タロウ YAMADAです!'.zenkaku('alphabet')               -> 'タロウ YAMADAです!'
8011     *   'タロウです! 25歳です!'.zenkaku('katakana', 'numbers') -> 'タロウです! 25歳です!'
8012     *   'タロウです! 25歳です!'.zenkaku('k', 'n')              -> 'タロウです! 25歳です!'
8013     *   'タロウです! 25歳です!'.zenkaku('kn')                  -> 'タロウです! 25歳です!'
8014     *   'タロウです! 25歳です!'.zenkaku('sp')                  -> 'タロウです! 25歳です!'
8015     *
8016     ***/
8017    'zenkaku': function() {
8018      return convertCharacterWidth(this, arguments, allHankaku, 'zenkaku');
8019    },
8020
8021    /***
8022     * @method hiragana([all] = true)
8023     * @returns String
8024     * @short Converts katakana into hiragana.
8025     * @extra If [all] is false, only full-width katakana will be converted.
8026     * @example
8027     *
8028     *   'カタカナ'.hiragana()   -> 'かたかな'
8029     *   'コンニチハ'.hiragana() -> 'こんにちは'
8030     *   'カタカナ'.hiragana()       -> 'かたかな'
8031     *   'カタカナ'.hiragana(false)  -> 'カタカナ'
8032     *
8033     ***/
8034    'hiragana': function(all) {
8035      var str = this;
8036      if(all !== false) {
8037        str = str.zenkaku('k');
8038      }
8039      return str.replace(/[\u30A1-\u30F6]/g, function(c) {
8040        return c.shift(-96);
8041      });
8042    },
8043
8044    /***
8045     * @method katakana()
8046     * @returns String
8047     * @short Converts hiragana into katakana.
8048     * @example
8049     *
8050     *   'かたかな'.katakana()   -> 'カタカナ'
8051     *   'こんにちは'.katakana() -> 'コンニチハ'
8052     *
8053     ***/
8054    'katakana': function() {
8055      return this.replace(/[\u3041-\u3096]/g, function(c) {
8056        return c.shift(96);
8057      });
8058    }
8059
8060
8061  });
8062
8063  buildUnicodeScripts();
8064
8065/*
8066 *
8067 * Date.addLocale(<code>) adds this locale to Sugar.
8068 * To set the locale globally, simply call:
8069 *
8070 * Date.setLocale('da');
8071 *
8072 * var locale = Date.getLocale(<code>) will return this object, which
8073 * can be tweaked to change the behavior of parsing/formatting in the locales.
8074 *
8075 * locale.addFormat adds a date format (see this file for examples).
8076 * Special tokens in the date format will be parsed out into regex tokens:
8077 *
8078 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8079 * {unit} is a reference to all units. Output: (day|week|month|...)
8080 * {unit3} is a reference to a specific unit. Output: (hour)
8081 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8082 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8083 *
8084 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8085 *
8086 * All spaces are optional and will be converted to "\s*"
8087 *
8088 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8089 * all entries in the modifiers array follow a special format indicated by a colon:
8090 *
8091 * minute:|s  = minute|minutes
8092 * thicke:n|r = thicken|thicker
8093 *
8094 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8095 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8096 *
8097 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8098 *
8099 * When matched, the index will be found using:
8100 *
8101 * units.indexOf(match) % 7;
8102 *
8103 * Resulting in the correct index with any number of alternates for that entry.
8104 *
8105 */
8106
8107Date.addLocale('da', {
8108  'plural': true,
8109  'months': 'januar,februar,marts,april,maj,juni,juli,august,september,oktober,november,december',
8110  'weekdays': 'søndag|sondag,mandag,tirsdag,onsdag,torsdag,fredag,lørdag|lordag',
8111  'units': 'millisekund:|er,sekund:|er,minut:|ter,tim:e|er,dag:|e,ug:e|er|en,måned:|er|en+maaned:|er|en,år:||et+aar:||et',
8112  'numbers': 'en|et,to,tre,fire,fem,seks,syv,otte,ni,ti',
8113  'tokens': 'den,for',
8114  'articles': 'den',
8115  'short':'d. {d}. {month} {yyyy}',
8116  'long': 'den {d}. {month} {yyyy} {H}:{mm}',
8117  'full': '{Weekday} den {d}. {month} {yyyy} {H}:{mm}:{ss}',
8118  'past': '{num} {unit} {sign}',
8119  'future': '{sign} {num} {unit}',
8120  'duration': '{num} {unit}',
8121  'ampm': 'am,pm',
8122  'modifiers': [
8123    { 'name': 'day', 'src': 'forgårs|i forgårs|forgaars|i forgaars', 'value': -2 },
8124    { 'name': 'day', 'src': 'i går|igår|i gaar|igaar', 'value': -1 },
8125    { 'name': 'day', 'src': 'i dag|idag', 'value': 0 },
8126    { 'name': 'day', 'src': 'i morgen|imorgen', 'value': 1 },
8127    { 'name': 'day', 'src': 'over morgon|overmorgen|i over morgen|i overmorgen|iovermorgen', 'value': 2 },
8128    { 'name': 'sign', 'src': 'siden', 'value': -1 },
8129    { 'name': 'sign', 'src': 'om', 'value':  1 },
8130    { 'name': 'shift', 'src': 'i sidste|sidste', 'value': -1 },
8131    { 'name': 'shift', 'src': 'denne', 'value': 0 },
8132    { 'name': 'shift', 'src': 'næste|naeste', 'value': 1 }
8133  ],
8134  'dateParse': [
8135    '{num} {unit} {sign}',
8136    '{sign} {num} {unit}',
8137    '{1?} {num} {unit} {sign}',
8138    '{shift} {unit=5-7}'
8139  ],
8140  'timeParse': [
8141    '{0?} {weekday?} {date?} {month} {year}',
8142    '{date} {month}',
8143    '{shift} {weekday}'
8144  ]
8145});
8146
8147/*
8148 *
8149 * Date.addLocale(<code>) adds this locale to Sugar.
8150 * To set the locale globally, simply call:
8151 *
8152 * Date.setLocale('de');
8153 *
8154 * var locale = Date.getLocale(<code>) will return this object, which
8155 * can be tweaked to change the behavior of parsing/formatting in the locales.
8156 *
8157 * locale.addFormat adds a date format (see this file for examples).
8158 * Special tokens in the date format will be parsed out into regex tokens:
8159 *
8160 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8161 * {unit} is a reference to all units. Output: (day|week|month|...)
8162 * {unit3} is a reference to a specific unit. Output: (hour)
8163 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8164 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8165 *
8166 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8167 *
8168 * All spaces are optional and will be converted to "\s*"
8169 *
8170 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8171 * all entries in the modifiers array follow a special format indicated by a colon:
8172 *
8173 * minute:|s  = minute|minutes
8174 * thicke:n|r = thicken|thicker
8175 *
8176 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8177 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8178 *
8179 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8180 *
8181 * When matched, the index will be found using:
8182 *
8183 * units.indexOf(match) % 7;
8184 *
8185 * Resulting in the correct index with any number of alternates for that entry.
8186 *
8187 */
8188
8189Date.addLocale('de', {
8190  'plural': true,
8191   'capitalizeUnit': true,
8192  'months': 'Januar,Februar,März|Marz,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember',
8193  'weekdays': 'Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag',
8194  'units': 'Millisekunde:|n,Sekunde:|n,Minute:|n,Stunde:|n,Tag:|en,Woche:|n,Monat:|en,Jahr:|en',
8195  'numbers': 'ein:|e|er|en|em,zwei,drei,vier,fuenf,sechs,sieben,acht,neun,zehn',
8196  'tokens': 'der',
8197  'short':'{d}. {Month} {yyyy}',
8198  'long': '{d}. {Month} {yyyy} {H}:{mm}',
8199  'full': '{Weekday} {d}. {Month} {yyyy} {H}:{mm}:{ss}',
8200  'past': '{sign} {num} {unit}',
8201  'future': '{sign} {num} {unit}',
8202  'duration': '{num} {unit}',
8203  'timeMarker': 'um',
8204  'ampm': 'am,pm',
8205  'modifiers': [
8206    { 'name': 'day', 'src': 'vorgestern', 'value': -2 },
8207    { 'name': 'day', 'src': 'gestern', 'value': -1 },
8208    { 'name': 'day', 'src': 'heute', 'value': 0 },
8209    { 'name': 'day', 'src': 'morgen', 'value': 1 },
8210    { 'name': 'day', 'src': 'übermorgen|ubermorgen|uebermorgen', 'value': 2 },
8211    { 'name': 'sign', 'src': 'vor:|her', 'value': -1 },
8212    { 'name': 'sign', 'src': 'in', 'value': 1 },
8213    { 'name': 'shift', 'src': 'letzte:|r|n|s', 'value': -1 },
8214    { 'name': 'shift', 'src': 'nächste:|r|n|s+nachste:|r|n|s+naechste:|r|n|s+kommende:n|r', 'value': 1 }
8215  ],
8216  'dateParse': [
8217    '{sign} {num} {unit}',
8218    '{num} {unit} {sign}',
8219    '{shift} {unit=5-7}'
8220  ],
8221  'timeParse': [
8222    '{weekday?} {date?} {month} {year?}',
8223    '{shift} {weekday}'
8224  ]
8225});
8226
8227/*
8228 *
8229 * Date.addLocale(<code>) adds this locale to Sugar.
8230 * To set the locale globally, simply call:
8231 *
8232 * Date.setLocale('es');
8233 *
8234 * var locale = Date.getLocale(<code>) will return this object, which
8235 * can be tweaked to change the behavior of parsing/formatting in the locales.
8236 *
8237 * locale.addFormat adds a date format (see this file for examples).
8238 * Special tokens in the date format will be parsed out into regex tokens:
8239 *
8240 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8241 * {unit} is a reference to all units. Output: (day|week|month|...)
8242 * {unit3} is a reference to a specific unit. Output: (hour)
8243 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8244 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8245 *
8246 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8247 *
8248 * All spaces are optional and will be converted to "\s*"
8249 *
8250 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8251 * all entries in the modifiers array follow a special format indicated by a colon:
8252 *
8253 * minute:|s  = minute|minutes
8254 * thicke:n|r = thicken|thicker
8255 *
8256 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8257 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8258 *
8259 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8260 *
8261 * When matched, the index will be found using:
8262 *
8263 * units.indexOf(match) % 7;
8264 *
8265 * Resulting in the correct index with any number of alternates for that entry.
8266 *
8267 */
8268
8269Date.addLocale('es', {
8270  'plural': true,
8271  'months': 'enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre',
8272  'weekdays': 'domingo,lunes,martes,miércoles|miercoles,jueves,viernes,sábado|sabado',
8273  'units': 'milisegundo:|s,segundo:|s,minuto:|s,hora:|s,día|días|dia|dias,semana:|s,mes:|es,año|años|ano|anos',
8274  'numbers': 'uno,dos,tres,cuatro,cinco,seis,siete,ocho,nueve,diez',
8275  'tokens': 'el,la,de',
8276  'short':'{d} {month} {yyyy}',
8277  'long': '{d} {month} {yyyy} {H}:{mm}',
8278  'full': '{Weekday} {d} {month} {yyyy} {H}:{mm}:{ss}',
8279  'past': '{sign} {num} {unit}',
8280  'future': '{sign} {num} {unit}',
8281  'duration': '{num} {unit}',
8282  'timeMarker': 'a las',
8283  'ampm': 'am,pm',
8284  'modifiers': [
8285    { 'name': 'day', 'src': 'anteayer', 'value': -2 },
8286    { 'name': 'day', 'src': 'ayer', 'value': -1 },
8287    { 'name': 'day', 'src': 'hoy', 'value': 0 },
8288    { 'name': 'day', 'src': 'mañana|manana', 'value': 1 },
8289    { 'name': 'sign', 'src': 'hace', 'value': -1 },
8290    { 'name': 'sign', 'src': 'dentro de', 'value': 1 },
8291    { 'name': 'shift', 'src': 'pasad:o|a', 'value': -1 },
8292    { 'name': 'shift', 'src': 'próximo|próxima|proximo|proxima', 'value': 1 }
8293  ],
8294  'dateParse': [
8295    '{sign} {num} {unit}',
8296    '{num} {unit} {sign}',
8297    '{0?}{1?} {unit=5-7} {shift}',
8298    '{0?}{1?} {shift} {unit=5-7}'
8299  ],
8300  'timeParse': [
8301    '{shift} {weekday}',
8302    '{weekday} {shift}',
8303    '{date?} {2?} {month} {2?} {year?}'
8304  ]
8305});
8306Date.addLocale('fi', {
8307    'plural':     true,
8308    'timeMarker': 'kello',
8309    'ampm':       ',',
8310    'months':     'tammikuu,helmikuu,maaliskuu,huhtikuu,toukokuu,kesäkuu,heinäkuu,elokuu,syyskuu,lokakuu,marraskuu,joulukuu',
8311    'weekdays':   'sunnuntai,maanantai,tiistai,keskiviikko,torstai,perjantai,lauantai',
8312    'units':      'millisekun:ti|tia|teja|tina|nin,sekun:ti|tia|teja|tina|nin,minuut:ti|tia|teja|tina|in,tun:ti|tia|teja|tina|nin,päiv:ä|ää|iä|änä|än,viik:ko|koa|koja|on|kona,kuukau:si|sia|tta|den|tena,vuo:si|sia|tta|den|tena',
8313    'numbers':    'yksi|ensimmäinen,kaksi|toinen,kolm:e|as,neljä:s,vii:si|des,kuu:si|des,seitsemä:n|s,kahdeksa:n|s,yhdeksä:n|s,kymmene:n|s',
8314    'articles':   '',
8315    'optionals':  '',
8316    'short':      '{d}. {month}ta {yyyy}',
8317    'long':       '{d}. {month}ta {yyyy} kello {H}.{mm}',
8318    'full':       '{Weekday}na {d}. {month}ta {yyyy} kello {H}.{mm}',
8319    'relative':       function(num, unit, ms, format) {
8320      var units = this['units'];
8321      function numberWithUnit(mult) {
8322        return (num === 1 ? '' : num + ' ') + units[(8 * mult) + unit];
8323      }
8324      switch(format) {
8325        case 'duration':  return numberWithUnit(0);
8326        case 'past':      return numberWithUnit(num > 1 ? 1 : 0) + ' sitten';
8327        case 'future':    return numberWithUnit(4) + ' päästä';
8328      }
8329    },
8330    'modifiers': [
8331        { 'name': 'day',   'src': 'toissa päivänä|toissa päiväistä', 'value': -2 },
8332        { 'name': 'day',   'src': 'eilen|eilistä', 'value': -1 },
8333        { 'name': 'day',   'src': 'tänään', 'value': 0 },
8334        { 'name': 'day',   'src': 'huomenna|huomista', 'value': 1 },
8335        { 'name': 'day',   'src': 'ylihuomenna|ylihuomista', 'value': 2 },
8336        { 'name': 'sign',  'src': 'sitten|aiemmin', 'value': -1 },
8337        { 'name': 'sign',  'src': 'päästä|kuluttua|myöhemmin', 'value': 1 },
8338        { 'name': 'edge',  'src': 'viimeinen|viimeisenä', 'value': -2 },
8339        { 'name': 'edge',  'src': 'lopussa', 'value': -1 },
8340        { 'name': 'edge',  'src': 'ensimmäinen|ensimmäisenä', 'value': 1 },
8341        { 'name': 'shift', 'src': 'edellinen|edellisenä|edeltävä|edeltävänä|viime|toissa', 'value': -1 },
8342        { 'name': 'shift', 'src': 'tänä|tämän', 'value': 0 },
8343        { 'name': 'shift', 'src': 'seuraava|seuraavana|tuleva|tulevana|ensi', 'value': 1 }
8344    ],
8345    'dateParse': [
8346        '{num} {unit} {sign}',
8347        '{sign} {num} {unit}',
8348        '{num} {unit=4-5} {sign} {day}',
8349        '{month} {year}',
8350        '{shift} {unit=5-7}'
8351    ],
8352    'timeParse': [
8353        '{0} {num}{1} {day} of {month} {year?}',
8354        '{weekday?} {month} {date}{1} {year?}',
8355        '{date} {month} {year}',
8356        '{shift} {weekday}',
8357        '{shift} week {weekday}',
8358        '{weekday} {2} {shift} week',
8359        '{0} {date}{1} of {month}',
8360        '{0}{month?} {date?}{1} of {shift} {unit=6-7}'
8361    ]
8362});
8363/*
8364 *
8365 * Date.addLocale(<code>) adds this locale to Sugar.
8366 * To set the locale globally, simply call:
8367 *
8368 * Date.setLocale('fr');
8369 *
8370 * var locale = Date.getLocale(<code>) will return this object, which
8371 * can be tweaked to change the behavior of parsing/formatting in the locales.
8372 *
8373 * locale.addFormat adds a date format (see this file for examples).
8374 * Special tokens in the date format will be parsed out into regex tokens:
8375 *
8376 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8377 * {unit} is a reference to all units. Output: (day|week|month|...)
8378 * {unit3} is a reference to a specific unit. Output: (hour)
8379 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8380 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8381 *
8382 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8383 *
8384 * All spaces are optional and will be converted to "\s*"
8385 *
8386 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8387 * all entries in the modifiers array follow a special format indicated by a colon:
8388 *
8389 * minute:|s  = minute|minutes
8390 * thicke:n|r = thicken|thicker
8391 *
8392 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8393 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8394 *
8395 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8396 *
8397 * When matched, the index will be found using:
8398 *
8399 * units.indexOf(match) % 7;
8400 *
8401 * Resulting in the correct index with any number of alternates for that entry.
8402 *
8403 */
8404
8405Date.addLocale('fr', {
8406  'plural': true,
8407  'months': 'janvier,février|fevrier,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre|decembre',
8408  'weekdays': 'dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi',
8409  'units': 'milliseconde:|s,seconde:|s,minute:|s,heure:|s,jour:|s,semaine:|s,mois,an:|s|née|nee',
8410  'numbers': 'un:|e,deux,trois,quatre,cinq,six,sept,huit,neuf,dix',
8411  'tokens': "l'|la|le",
8412  'short':'{d} {month} {yyyy}',
8413  'long': '{d} {month} {yyyy} {H}:{mm}',
8414  'full': '{Weekday} {d} {month} {yyyy} {H}:{mm}:{ss}',
8415  'past': '{sign} {num} {unit}',
8416  'future': '{sign} {num} {unit}',
8417  'duration': '{num} {unit}',
8418  'timeMarker': 'à',
8419  'ampm': 'am,pm',
8420  'modifiers': [
8421    { 'name': 'day', 'src': 'hier', 'value': -1 },
8422    { 'name': 'day', 'src': "aujourd'hui", 'value': 0 },
8423    { 'name': 'day', 'src': 'demain', 'value': 1 },
8424    { 'name': 'sign', 'src': 'il y a', 'value': -1 },
8425    { 'name': 'sign', 'src': "dans|d'ici", 'value': 1 },
8426    { 'name': 'shift', 'src': 'derni:èr|er|ère|ere', 'value': -1 },
8427    { 'name': 'shift', 'src': 'prochain:|e', 'value': 1 }
8428  ],
8429  'dateParse': [
8430    '{sign} {num} {unit}',
8431    '{sign} {num} {unit}',
8432    '{0?} {unit=5-7} {shift}'
8433  ],
8434  'timeParse': [
8435    '{weekday?} {0?} {date?} {month} {year?}',
8436    '{0?} {weekday} {shift}'
8437  ]
8438});
8439
8440/*
8441 *
8442 * Date.addLocale(<code>) adds this locale to Sugar.
8443 * To set the locale globally, simply call:
8444 *
8445 * Date.setLocale('it');
8446 *
8447 * var locale = Date.getLocale(<code>) will return this object, which
8448 * can be tweaked to change the behavior of parsing/formatting in the locales.
8449 *
8450 * locale.addFormat adds a date format (see this file for examples).
8451 * Special tokens in the date format will be parsed out into regex tokens:
8452 *
8453 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8454 * {unit} is a reference to all units. Output: (day|week|month|...)
8455 * {unit3} is a reference to a specific unit. Output: (hour)
8456 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8457 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8458 *
8459 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8460 *
8461 * All spaces are optional and will be converted to "\s*"
8462 *
8463 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8464 * all entries in the modifiers array follow a special format indicated by a colon:
8465 *
8466 * minute:|s  = minute|minutes
8467 * thicke:n|r = thicken|thicker
8468 *
8469 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8470 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8471 *
8472 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8473 *
8474 * When matched, the index will be found using:
8475 *
8476 * units.indexOf(match) % 7;
8477 *
8478 * Resulting in the correct index with any number of alternates for that entry.
8479 *
8480 */
8481
8482Date.addLocale('it', {
8483  'plural': true,
8484  'months': 'Gennaio,Febbraio,Marzo,Aprile,Maggio,Giugno,Luglio,Agosto,Settembre,Ottobre,Novembre,Dicembre',
8485  'weekdays': 'Domenica,Luned:ì|i,Marted:ì|i,Mercoled:ì|i,Gioved:ì|i,Venerd:ì|i,Sabato',
8486  'units': 'millisecond:o|i,second:o|i,minut:o|i,or:a|e,giorn:o|i,settiman:a|e,mes:e|i,ann:o|i',
8487  'numbers': "un:|a|o|',due,tre,quattro,cinque,sei,sette,otto,nove,dieci",
8488  'tokens': "l'|la|il",
8489  'short':'{d} {Month} {yyyy}',
8490  'long': '{d} {Month} {yyyy} {H}:{mm}',
8491  'full': '{Weekday} {d} {Month} {yyyy} {H}:{mm}:{ss}',
8492  'past': '{num} {unit} {sign}',
8493  'future': '{num} {unit} {sign}',
8494  'duration': '{num} {unit}',
8495  'timeMarker': 'alle',
8496  'ampm': 'am,pm',
8497  'modifiers': [
8498    { 'name': 'day', 'src': 'ieri', 'value': -1 },
8499    { 'name': 'day', 'src': 'oggi', 'value': 0 },
8500    { 'name': 'day', 'src': 'domani', 'value': 1 },
8501    { 'name': 'day', 'src': 'dopodomani', 'value': 2 },
8502    { 'name': 'sign', 'src': 'fa', 'value': -1 },
8503    { 'name': 'sign', 'src': 'da adesso', 'value': 1 },
8504    { 'name': 'shift', 'src': 'scors:o|a', 'value': -1 },
8505    { 'name': 'shift', 'src': 'prossim:o|a', 'value': 1 }
8506  ],
8507  'dateParse': [
8508    '{num} {unit} {sign}',
8509    '{0?} {unit=5-7} {shift}',
8510    '{0?} {shift} {unit=5-7}'
8511  ],
8512  'timeParse': [
8513    '{weekday?} {date?} {month} {year?}',
8514    '{shift} {weekday}'
8515  ]
8516});
8517
8518/*
8519 *
8520 * Date.addLocale(<code>) adds this locale to Sugar.
8521 * To set the locale globally, simply call:
8522 *
8523 * Date.setLocale('ja');
8524 *
8525 * var locale = Date.getLocale(<code>) will return this object, which
8526 * can be tweaked to change the behavior of parsing/formatting in the locales.
8527 *
8528 * locale.addFormat adds a date format (see this file for examples).
8529 * Special tokens in the date format will be parsed out into regex tokens:
8530 *
8531 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8532 * {unit} is a reference to all units. Output: (day|week|month|...)
8533 * {unit3} is a reference to a specific unit. Output: (hour)
8534 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8535 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8536 *
8537 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8538 *
8539 * All spaces are optional and will be converted to "\s*"
8540 *
8541 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8542 * all entries in the modifiers array follow a special format indicated by a colon:
8543 *
8544 * minute:|s  = minute|minutes
8545 * thicke:n|r = thicken|thicker
8546 *
8547 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8548 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8549 *
8550 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8551 *
8552 * When matched, the index will be found using:
8553 *
8554 * units.indexOf(match) % 7;
8555 *
8556 * Resulting in the correct index with any number of alternates for that entry.
8557 *
8558 */
8559
8560Date.addLocale('ja', {
8561  'monthSuffix': '月',
8562  'weekdays': '日曜日,月曜日,火曜日,水曜日,木曜日,金曜日,土曜日',
8563  'units': 'ミリ秒,秒,分,時間,日,週間|週,ヶ月|ヵ月|月,年',
8564  'short': '{yyyy}年{M}月{d}日',
8565  'long': '{yyyy}年{M}月{d}日 {H}時{mm}分',
8566  'full': '{yyyy}年{M}月{d}日 {Weekday} {H}時{mm}分{ss}秒',
8567  'past': '{num}{unit}{sign}',
8568  'future': '{num}{unit}{sign}',
8569  'duration': '{num}{unit}',
8570  'timeSuffixes': '時,分,秒',
8571  'ampm': '午前,午後',
8572  'modifiers': [
8573    { 'name': 'day', 'src': '一昨日', 'value': -2 },
8574    { 'name': 'day', 'src': '昨日', 'value': -1 },
8575    { 'name': 'day', 'src': '今日', 'value': 0 },
8576    { 'name': 'day', 'src': '明日', 'value': 1 },
8577    { 'name': 'day', 'src': '明後日', 'value': 2 },
8578    { 'name': 'sign', 'src': '前', 'value': -1 },
8579    { 'name': 'sign', 'src': '後', 'value':  1 },
8580    { 'name': 'shift', 'src': '去|先', 'value': -1 },
8581    { 'name': 'shift', 'src': '来', 'value':  1 }
8582  ],
8583  'dateParse': [
8584    '{num}{unit}{sign}'
8585  ],
8586  'timeParse': [
8587    '{shift}{unit=5-7}{weekday?}',
8588    '{year}年{month?}月?{date?}日?',
8589    '{month}月{date?}日?',
8590    '{date}日'
8591  ]
8592});
8593
8594/*
8595 *
8596 * Date.addLocale(<code>) adds this locale to Sugar.
8597 * To set the locale globally, simply call:
8598 *
8599 * Date.setLocale('ko');
8600 *
8601 * var locale = Date.getLocale(<code>) will return this object, which
8602 * can be tweaked to change the behavior of parsing/formatting in the locales.
8603 *
8604 * locale.addFormat adds a date format (see this file for examples).
8605 * Special tokens in the date format will be parsed out into regex tokens:
8606 *
8607 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8608 * {unit} is a reference to all units. Output: (day|week|month|...)
8609 * {unit3} is a reference to a specific unit. Output: (hour)
8610 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8611 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8612 *
8613 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8614 *
8615 * All spaces are optional and will be converted to "\s*"
8616 *
8617 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8618 * all entries in the modifiers array follow a special format indicated by a colon:
8619 *
8620 * minute:|s  = minute|minutes
8621 * thicke:n|r = thicken|thicker
8622 *
8623 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8624 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8625 *
8626 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8627 *
8628 * When matched, the index will be found using:
8629 *
8630 * units.indexOf(match) % 7;
8631 *
8632 * Resulting in the correct index with any number of alternates for that entry.
8633 *
8634 */
8635
8636Date.addLocale('ko', {
8637  'digitDate': true,
8638  'monthSuffix': '월',
8639  'weekdays': '일요일,월요일,화요일,수요일,목요일,금요일,토요일',
8640  'units': '밀리초,초,분,시간,일,주,개월|달,년',
8641  'numbers': '일|한,이,삼,사,오,육,칠,팔,구,십',
8642  'short': '{yyyy}년{M}월{d}일',
8643  'long': '{yyyy}년{M}월{d}일 {H}시{mm}분',
8644  'full': '{yyyy}년{M}월{d}일 {Weekday} {H}시{mm}분{ss}초',
8645  'past': '{num}{unit} {sign}',
8646  'future': '{num}{unit} {sign}',
8647  'duration': '{num}{unit}',
8648  'timeSuffixes': '시,분,초',
8649  'ampm': '오전,오후',
8650  'modifiers': [
8651    { 'name': 'day', 'src': '그저께', 'value': -2 },
8652    { 'name': 'day', 'src': '어제', 'value': -1 },
8653    { 'name': 'day', 'src': '오늘', 'value': 0 },
8654    { 'name': 'day', 'src': '내일', 'value': 1 },
8655    { 'name': 'day', 'src': '모레', 'value': 2 },
8656    { 'name': 'sign', 'src': '전', 'value': -1 },
8657    { 'name': 'sign', 'src': '후', 'value':  1 },
8658    { 'name': 'shift', 'src': '지난|작', 'value': -1 },
8659    { 'name': 'shift', 'src': '이번', 'value': 0 },
8660    { 'name': 'shift', 'src': '다음|내', 'value': 1 }
8661  ],
8662  'dateParse': [
8663    '{num}{unit} {sign}',
8664    '{shift?} {unit=5-7}'
8665  ],
8666  'timeParse': [
8667    '{shift} {unit=5?} {weekday}',
8668    '{year}년{month?}월?{date?}일?',
8669    '{month}월{date?}일?',
8670    '{date}일'
8671  ]
8672});
8673
8674/*
8675 *
8676 * Date.addLocale(<code>) adds this locale to Sugar.
8677 * To set the locale globally, simply call:
8678 *
8679 * Date.setLocale('nl');
8680 *
8681 * var locale = Date.getLocale(<code>) will return this object, which
8682 * can be tweaked to change the behavior of parsing/formatting in the locales.
8683 *
8684 * locale.addFormat adds a date format (see this file for examples).
8685 * Special tokens in the date format will be parsed out into regex tokens:
8686 *
8687 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8688 * {unit} is a reference to all units. Output: (day|week|month|...)
8689 * {unit3} is a reference to a specific unit. Output: (hour)
8690 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8691 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8692 *
8693 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8694 *
8695 * All spaces are optional and will be converted to "\s*"
8696 *
8697 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8698 * all entries in the modifiers array follow a special format indicated by a colon:
8699 *
8700 * minute:|s  = minute|minutes
8701 * thicke:n|r = thicken|thicker
8702 *
8703 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8704 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8705 *
8706 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8707 *
8708 * When matched, the index will be found using:
8709 *
8710 * units.indexOf(match) % 7;
8711 *
8712 * Resulting in the correct index with any number of alternates for that entry.
8713 *
8714 */
8715
8716Date.addLocale('nl', {
8717  'plural': true,
8718  'months': 'januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december',
8719  'weekdays': 'zondag|zo,maandag|ma,dinsdag|di,woensdag|woe|wo,donderdag|do,vrijdag|vrij|vr,zaterdag|za',
8720  'units': 'milliseconde:|n,seconde:|n,minu:ut|ten,uur,dag:|en,we:ek|ken,maand:|en,jaar',
8721  'numbers': 'een,twee,drie,vier,vijf,zes,zeven,acht,negen',
8722  'tokens': '',
8723  'short':'{d} {Month} {yyyy}',
8724  'long': '{d} {Month} {yyyy} {H}:{mm}',
8725  'full': '{Weekday} {d} {Month} {yyyy} {H}:{mm}:{ss}',
8726  'past': '{num} {unit} {sign}',
8727  'future': '{num} {unit} {sign}',
8728  'duration': '{num} {unit}',
8729  'timeMarker': "'s|om",
8730  'modifiers': [
8731    { 'name': 'day', 'src': 'gisteren', 'value': -1 },
8732    { 'name': 'day', 'src': 'vandaag', 'value': 0 },
8733    { 'name': 'day', 'src': 'morgen', 'value': 1 },
8734    { 'name': 'day', 'src': 'overmorgen', 'value': 2 },
8735    { 'name': 'sign', 'src': 'geleden', 'value': -1 },
8736    { 'name': 'sign', 'src': 'vanaf nu', 'value': 1 },
8737    { 'name': 'shift', 'src': 'laatste|vorige|afgelopen', 'value': -1 },
8738    { 'name': 'shift', 'src': 'volgend:|e', 'value': 1 }
8739  ],
8740  'dateParse': [
8741    '{num} {unit} {sign}',
8742    '{0?} {unit=5-7} {shift}',
8743    '{0?} {shift} {unit=5-7}'
8744  ],
8745  'timeParse': [
8746    '{weekday?} {date?} {month} {year?}',
8747    '{shift} {weekday}'
8748  ]
8749});
8750/*
8751 *
8752 * Date.addLocale(<code>) adds this locale to Sugar.
8753 * To set the locale globally, simply call:
8754 *
8755 * Date.setLocale('pl');
8756 *
8757 * var locale = Date.getLocale(<code>) will return this object, which
8758 * can be tweaked to change the behavior of parsing/formatting in the locales.
8759 *
8760 * locale.addFormat adds a date format (see this file for examples).
8761 * Special tokens in the date format will be parsed out into regex tokens:
8762 *
8763 * {0} is a reference to an entry in locale.optionals. Output: (?:the)?
8764 * {unit} is a reference to all units. Output: (day|week|month|...)
8765 * {unit3} is a reference to a specific unit. Output: (hour)
8766 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8767 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8768 *
8769 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8770 *
8771 * All spaces are optional and will be converted to "\s*"
8772 *
8773 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8774 * all entries in the modifiers array follow a special format indicated by a colon:
8775 *
8776 * minute:|s  = minute|minutes
8777 * thicke:n|r = thicken|thicker
8778 *
8779 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8780 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8781 *
8782 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8783 *
8784 * When matched, the index will be found using:
8785 *
8786 * units.indexOf(match) % 7;
8787 *
8788 * Resulting in the correct index with any number of alternates for that entry.
8789 *
8790 */
8791
8792Date.addLocale('pl', {
8793  'plural':    true,
8794  'months':    'Styczeń|Stycznia,Luty|Lutego,Marzec|Marca,Kwiecień|Kwietnia,Maj|Maja,Czerwiec|Czerwca,Lipiec|Lipca,Sierpień|Sierpnia,Wrzesień|Września,Październik|Października,Listopad|Listopada,Grudzień|Grudnia',
8795  'weekdays':  'Niedziela|Niedzielę,Poniedziałek,Wtorek,Środ:a|ę,Czwartek,Piątek,Sobota|Sobotę',
8796  'units':     'milisekund:a|y|,sekund:a|y|,minut:a|y|,godzin:a|y|,dzień|dni,tydzień|tygodnie|tygodni,miesiące|miesiące|miesięcy,rok|lata|lat',
8797  'numbers':   'jeden|jedną,dwa|dwie,trzy,cztery,pięć,sześć,siedem,osiem,dziewięć,dziesięć',
8798  'optionals': 'w|we,roku',
8799  'short':     '{d} {Month} {yyyy}',
8800  'long':      '{d} {Month} {yyyy} {H}:{mm}',
8801  'full' :     '{Weekday}, {d} {Month} {yyyy} {H}:{mm}:{ss}',
8802  'past':      '{num} {unit} {sign}',
8803  'future':    '{sign} {num} {unit}',
8804  'duration':  '{num} {unit}',
8805  'timeMarker':'o',
8806  'ampm':      'am,pm',
8807  'modifiers': [
8808    { 'name': 'day', 'src': 'przedwczoraj', 'value': -2 },
8809    { 'name': 'day', 'src': 'wczoraj', 'value': -1 },
8810    { 'name': 'day', 'src': 'dzisiaj|dziś', 'value': 0 },
8811    { 'name': 'day', 'src': 'jutro', 'value': 1 },
8812    { 'name': 'day', 'src': 'pojutrze', 'value': 2 },
8813    { 'name': 'sign', 'src': 'temu|przed', 'value': -1 },
8814    { 'name': 'sign', 'src': 'za', 'value': 1 },
8815    { 'name': 'shift', 'src': 'zeszły|zeszła|ostatni|ostatnia', 'value': -1 },
8816    { 'name': 'shift', 'src': 'następny|następna|następnego|przyszły|przyszła|przyszłego', 'value': 1 }
8817  ],
8818  'dateParse': [
8819    '{num} {unit} {sign}',
8820    '{sign} {num} {unit}',
8821    '{month} {year}',
8822    '{shift} {unit=5-7}',
8823    '{0} {shift?} {weekday}'
8824  ],
8825  'timeParse': [
8826    '{date} {month} {year?} {1}',
8827    '{0} {shift?} {weekday}'
8828  ]
8829});
8830
8831/*
8832 *
8833 * Date.addLocale(<code>) adds this locale to Sugar.
8834 * To set the locale globally, simply call:
8835 *
8836 * Date.setLocale('pt');
8837 *
8838 * var locale = Date.getLocale(<code>) will return this object, which
8839 * can be tweaked to change the behavior of parsing/formatting in the locales.
8840 *
8841 * locale.addFormat adds a date format (see this file for examples).
8842 * Special tokens in the date format will be parsed out into regex tokens:
8843 *
8844 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8845 * {unit} is a reference to all units. Output: (day|week|month|...)
8846 * {unit3} is a reference to a specific unit. Output: (hour)
8847 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8848 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8849 *
8850 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8851 *
8852 * All spaces are optional and will be converted to "\s*"
8853 *
8854 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8855 * all entries in the modifiers array follow a special format indicated by a colon:
8856 *
8857 * minute:|s  = minute|minutes
8858 * thicke:n|r = thicken|thicker
8859 *
8860 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8861 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8862 *
8863 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8864 *
8865 * When matched, the index will be found using:
8866 *
8867 * units.indexOf(match) % 7;
8868 *
8869 * Resulting in the correct index with any number of alternates for that entry.
8870 *
8871 */
8872
8873Date.addLocale('pt', {
8874  'plural': true,
8875  'months': 'janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outubro,novembro,dezembro',
8876  'weekdays': 'domingo,segunda-feira,terça-feira,quarta-feira,quinta-feira,sexta-feira,sábado|sabado',
8877  'units': 'milisegundo:|s,segundo:|s,minuto:|s,hora:|s,dia:|s,semana:|s,mês|mêses|mes|meses,ano:|s',
8878  'numbers': 'um,dois,três|tres,quatro,cinco,seis,sete,oito,nove,dez,uma,duas',
8879  'tokens': 'a,de',
8880  'short':'{d} de {month} de {yyyy}',
8881  'long': '{d} de {month} de {yyyy} {H}:{mm}',
8882  'full': '{Weekday}, {d} de {month} de {yyyy} {H}:{mm}:{ss}',
8883  'past': '{num} {unit} {sign}',
8884  'future': '{sign} {num} {unit}',
8885  'duration': '{num} {unit}',
8886  'timeMarker': 'às',
8887  'ampm': 'am,pm',
8888  'modifiers': [
8889    { 'name': 'day', 'src': 'anteontem', 'value': -2 },
8890    { 'name': 'day', 'src': 'ontem', 'value': -1 },
8891    { 'name': 'day', 'src': 'hoje', 'value': 0 },
8892    { 'name': 'day', 'src': 'amanh:ã|a', 'value': 1 },
8893    { 'name': 'sign', 'src': 'atrás|atras|há|ha', 'value': -1 },
8894    { 'name': 'sign', 'src': 'daqui a', 'value': 1 },
8895    { 'name': 'shift', 'src': 'passad:o|a', 'value': -1 },
8896    { 'name': 'shift', 'src': 'próximo|próxima|proximo|proxima', 'value': 1 }
8897  ],
8898  'dateParse': [
8899    '{num} {unit} {sign}',
8900    '{sign} {num} {unit}',
8901    '{0?} {unit=5-7} {shift}',
8902    '{0?} {shift} {unit=5-7}'
8903  ],
8904  'timeParse': [
8905    '{date?} {1?} {month} {1?} {year?}',
8906    '{0?} {shift} {weekday}'
8907  ]
8908});
8909
8910/*
8911 *
8912 * Date.addLocale(<code>) adds this locale to Sugar.
8913 * To set the locale globally, simply call:
8914 *
8915 * Date.setLocale('ru');
8916 *
8917 * var locale = Date.getLocale(<code>) will return this object, which
8918 * can be tweaked to change the behavior of parsing/formatting in the locales.
8919 *
8920 * locale.addFormat adds a date format (see this file for examples).
8921 * Special tokens in the date format will be parsed out into regex tokens:
8922 *
8923 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
8924 * {unit} is a reference to all units. Output: (day|week|month|...)
8925 * {unit3} is a reference to a specific unit. Output: (hour)
8926 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
8927 * {unit?} "?" makes that token optional. Output: (day|week|month)?
8928 *
8929 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
8930 *
8931 * All spaces are optional and will be converted to "\s*"
8932 *
8933 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
8934 * all entries in the modifiers array follow a special format indicated by a colon:
8935 *
8936 * minute:|s  = minute|minutes
8937 * thicke:n|r = thicken|thicker
8938 *
8939 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
8940 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
8941 *
8942 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
8943 *
8944 * When matched, the index will be found using:
8945 *
8946 * units.indexOf(match) % 7;
8947 *
8948 * Resulting in the correct index with any number of alternates for that entry.
8949 *
8950 */
8951
8952Date.addLocale('ru', {
8953  'months': 'Январ:я|ь,Феврал:я|ь,Март:а|,Апрел:я|ь,Ма:я|й,Июн:я|ь,Июл:я|ь,Август:а|,Сентябр:я|ь,Октябр:я|ь,Ноябр:я|ь,Декабр:я|ь',
8954  'weekdays': 'Воскресенье,Понедельник,Вторник,Среда,Четверг,Пятница,Суббота',
8955  'units': 'миллисекунд:а|у|ы|,секунд:а|у|ы|,минут:а|у|ы|,час:||а|ов,день|день|дня|дней,недел:я|ю|и|ь|е,месяц:||а|ев|е,год|год|года|лет|году',
8956  'numbers': 'од:ин|ну,дв:а|е,три,четыре,пять,шесть,семь,восемь,девять,десять',
8957  'tokens': 'в|на,года',
8958  'short':'{d} {month} {yyyy} года',
8959  'long': '{d} {month} {yyyy} года {H}:{mm}',
8960  'full': '{Weekday} {d} {month} {yyyy} года {H}:{mm}:{ss}',
8961  'relative': function(num, unit, ms, format) {
8962    var numberWithUnit, last = num.toString().slice(-1), mult;
8963    switch(true) {
8964      case num >= 11 && num <= 15: mult = 3; break;
8965      case last == 1: mult = 1; break;
8966      case last >= 2 && last <= 4: mult = 2; break;
8967      default: mult = 3;
8968    }
8969    numberWithUnit = num + ' ' + this['units'][(mult * 8) + unit];
8970    switch(format) {
8971      case 'duration':  return numberWithUnit;
8972      case 'past':      return numberWithUnit + ' назад';
8973      case 'future':    return 'через ' + numberWithUnit;
8974    }
8975  },
8976  'timeMarker': 'в',
8977  'ampm': ' утра, вечера',
8978  'modifiers': [
8979    { 'name': 'day', 'src': 'позавчера', 'value': -2 },
8980    { 'name': 'day', 'src': 'вчера', 'value': -1 },
8981    { 'name': 'day', 'src': 'сегодня', 'value': 0 },
8982    { 'name': 'day', 'src': 'завтра', 'value': 1 },
8983    { 'name': 'day', 'src': 'послезавтра', 'value': 2 },
8984    { 'name': 'sign', 'src': 'назад', 'value': -1 },
8985    { 'name': 'sign', 'src': 'через', 'value': 1 },
8986    { 'name': 'shift', 'src': 'прошл:ый|ой|ом', 'value': -1 },
8987    { 'name': 'shift', 'src': 'следующ:ий|ей|ем', 'value': 1 }
8988  ],
8989  'dateParse': [
8990    '{num} {unit} {sign}',
8991    '{sign} {num} {unit}',
8992    '{month} {year}',
8993    '{0?} {shift} {unit=5-7}'
8994  ],
8995  'timeParse': [
8996    '{date} {month} {year?} {1?}',
8997    '{0?} {shift} {weekday}'
8998  ]
8999});
9000
9001/*
9002 *
9003 * Date.addLocale(<code>) adds this locale to Sugar.
9004 * To set the locale globally, simply call:
9005 *
9006 * Date.setLocale('sv');
9007 *
9008 * var locale = Date.getLocale(<code>) will return this object, which
9009 * can be tweaked to change the behavior of parsing/formatting in the locales.
9010 *
9011 * locale.addFormat adds a date format (see this file for examples).
9012 * Special tokens in the date format will be parsed out into regex tokens:
9013 *
9014 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
9015 * {unit} is a reference to all units. Output: (day|week|month|...)
9016 * {unit3} is a reference to a specific unit. Output: (hour)
9017 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
9018 * {unit?} "?" makes that token optional. Output: (day|week|month)?
9019 *
9020 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
9021 *
9022 * All spaces are optional and will be converted to "\s*"
9023 *
9024 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
9025 * all entries in the modifiers array follow a special format indicated by a colon:
9026 *
9027 * minute:|s  = minute|minutes
9028 * thicke:n|r = thicken|thicker
9029 *
9030 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
9031 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
9032 *
9033 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
9034 *
9035 * When matched, the index will be found using:
9036 *
9037 * units.indexOf(match) % 7;
9038 *
9039 * Resulting in the correct index with any number of alternates for that entry.
9040 *
9041 */
9042
9043Date.addLocale('sv', {
9044  'plural': true,
9045  'months': 'januari,februari,mars,april,maj,juni,juli,augusti,september,oktober,november,december',
9046  'weekdays': 'söndag|sondag,måndag:|en+mandag:|en,tisdag,onsdag,torsdag,fredag,lördag|lordag',
9047  'units': 'millisekund:|er,sekund:|er,minut:|er,timm:e|ar,dag:|ar,veck:a|or|an,månad:|er|en+manad:|er|en,år:||et+ar:||et',
9048  'numbers': 'en|ett,två|tva,tre,fyra,fem,sex,sju,åtta|atta,nio,tio',
9049  'tokens': 'den,för|for',
9050  'articles': 'den',
9051  'short':'den {d} {month} {yyyy}',
9052  'long': 'den {d} {month} {yyyy} {H}:{mm}',
9053  'full': '{Weekday} den {d} {month} {yyyy} {H}:{mm}:{ss}',
9054  'past': '{num} {unit} {sign}',
9055  'future': '{sign} {num} {unit}',
9056  'duration': '{num} {unit}',
9057  'ampm': 'am,pm',
9058  'modifiers': [
9059    { 'name': 'day', 'src': 'förrgår|i förrgår|iförrgår|forrgar|i forrgar|iforrgar', 'value': -2 },
9060    { 'name': 'day', 'src': 'går|i går|igår|gar|i gar|igar', 'value': -1 },
9061    { 'name': 'day', 'src': 'dag|i dag|idag', 'value': 0 },
9062    { 'name': 'day', 'src': 'morgon|i morgon|imorgon', 'value': 1 },
9063    { 'name': 'day', 'src': 'över morgon|övermorgon|i över morgon|i övermorgon|iövermorgon|over morgon|overmorgon|i over morgon|i overmorgon|iovermorgon', 'value': 2 },
9064    { 'name': 'sign', 'src': 'sedan|sen', 'value': -1 },
9065    { 'name': 'sign', 'src': 'om', 'value':  1 },
9066    { 'name': 'shift', 'src': 'i förra|förra|i forra|forra', 'value': -1 },
9067    { 'name': 'shift', 'src': 'denna', 'value': 0 },
9068    { 'name': 'shift', 'src': 'nästa|nasta', 'value': 1 }
9069  ],
9070  'dateParse': [
9071    '{num} {unit} {sign}',
9072    '{sign} {num} {unit}',
9073    '{1?} {num} {unit} {sign}',
9074    '{shift} {unit=5-7}'
9075  ],
9076  'timeParse': [
9077    '{0?} {weekday?} {date?} {month} {year}',
9078    '{date} {month}',
9079    '{shift} {weekday}'
9080  ]
9081});
9082
9083/*
9084 *
9085 * Date.addLocale(<code>) adds this locale to Sugar.
9086 * To set the locale globally, simply call:
9087 *
9088 * Date.setLocale('zh-CN');
9089 *
9090 * var locale = Date.getLocale(<code>) will return this object, which
9091 * can be tweaked to change the behavior of parsing/formatting in the locales.
9092 *
9093 * locale.addFormat adds a date format (see this file for examples).
9094 * Special tokens in the date format will be parsed out into regex tokens:
9095 *
9096 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
9097 * {unit} is a reference to all units. Output: (day|week|month|...)
9098 * {unit3} is a reference to a specific unit. Output: (hour)
9099 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
9100 * {unit?} "?" makes that token optional. Output: (day|week|month)?
9101 *
9102 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
9103 *
9104 * All spaces are optional and will be converted to "\s*"
9105 *
9106 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
9107 * all entries in the modifiers array follow a special format indicated by a colon:
9108 *
9109 * minute:|s  = minute|minutes
9110 * thicke:n|r = thicken|thicker
9111 *
9112 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
9113 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
9114 *
9115 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
9116 *
9117 * When matched, the index will be found using:
9118 *
9119 * units.indexOf(match) % 7;
9120 *
9121 * Resulting in the correct index with any number of alternates for that entry.
9122 *
9123 */
9124
9125Date.addLocale('zh-CN', {
9126  'variant': true,
9127  'monthSuffix': '月',
9128  'weekdays': '星期日|周日,星期一|周一,星期二|周二,星期三|周三,星期四|周四,星期五|周五,星期六|周六',
9129  'units': '毫秒,秒钟,分钟,小时,天,个星期|周,个月,年',
9130  'tokens': '日|号',
9131  'short':'{yyyy}年{M}月{d}日',
9132  'long': '{yyyy}年{M}月{d}日 {tt}{h}:{mm}',
9133  'full': '{yyyy}年{M}月{d}日 {weekday} {tt}{h}:{mm}:{ss}',
9134  'past': '{num}{unit}{sign}',
9135  'future': '{num}{unit}{sign}',
9136  'duration': '{num}{unit}',
9137  'timeSuffixes': '点|时,分钟?,秒',
9138  'ampm': '上午,下午',
9139  'modifiers': [
9140    { 'name': 'day', 'src': '前天', 'value': -2 },
9141    { 'name': 'day', 'src': '昨天', 'value': -1 },
9142    { 'name': 'day', 'src': '今天', 'value': 0 },
9143    { 'name': 'day', 'src': '明天', 'value': 1 },
9144    { 'name': 'day', 'src': '后天', 'value': 2 },
9145    { 'name': 'sign', 'src': '前', 'value': -1 },
9146    { 'name': 'sign', 'src': '后', 'value':  1 },
9147    { 'name': 'shift', 'src': '上|去', 'value': -1 },
9148    { 'name': 'shift', 'src': '这', 'value':  0 },
9149    { 'name': 'shift', 'src': '下|明', 'value':  1 }
9150  ],
9151  'dateParse': [
9152    '{num}{unit}{sign}',
9153    '{shift}{unit=5-7}'
9154  ],
9155  'timeParse': [
9156    '{shift}{weekday}',
9157    '{year}年{month?}月?{date?}{0?}',
9158    '{month}月{date?}{0?}',
9159    '{date}[日号]'
9160  ]
9161});
9162
9163/*
9164 *
9165 * Date.addLocale(<code>) adds this locale to Sugar.
9166 * To set the locale globally, simply call:
9167 *
9168 * Date.setLocale('zh-TW');
9169 *
9170 * var locale = Date.getLocale(<code>) will return this object, which
9171 * can be tweaked to change the behavior of parsing/formatting in the locales.
9172 *
9173 * locale.addFormat adds a date format (see this file for examples).
9174 * Special tokens in the date format will be parsed out into regex tokens:
9175 *
9176 * {0} is a reference to an entry in locale.tokens. Output: (?:the)?
9177 * {unit} is a reference to all units. Output: (day|week|month|...)
9178 * {unit3} is a reference to a specific unit. Output: (hour)
9179 * {unit3-5} is a reference to a subset of the units array. Output: (hour|day|week)
9180 * {unit?} "?" makes that token optional. Output: (day|week|month)?
9181 *
9182 * {day} Any reference to tokens in the modifiers array will include all with the same name. Output: (yesterday|today|tomorrow)
9183 *
9184 * All spaces are optional and will be converted to "\s*"
9185 *
9186 * Locale arrays months, weekdays, units, numbers, as well as the "src" field for
9187 * all entries in the modifiers array follow a special format indicated by a colon:
9188 *
9189 * minute:|s  = minute|minutes
9190 * thicke:n|r = thicken|thicker
9191 *
9192 * Additionally in the months, weekdays, units, and numbers array these will be added at indexes that are multiples
9193 * of the relevant number for retrieval. For example having "sunday:|s" in the units array will result in:
9194 *
9195 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sundays']
9196 *
9197 * When matched, the index will be found using:
9198 *
9199 * units.indexOf(match) % 7;
9200 *
9201 * Resulting in the correct index with any number of alternates for that entry.
9202 *
9203 */
9204
9205  //'zh-TW': '1;月;年;;星期日|週日,星期一|週一,星期二|週二,星期三|週三,星期四|週四,星期五|週五,星期六|週六;毫秒,秒鐘,分鐘,小時,天,個星期|週,個月,年;;;日|號;;上午,下午;點|時,分鐘?,秒;{num}{unit}{sign},{shift}{unit=5-7};{shift}{weekday},{year}年{month?}月?{date?}{0},{month}月{date?}{0},{date}{0};{yyyy}年{M}月{d}日 {Weekday};{tt}{h}:{mm}:{ss};前天,昨天,今天,明天,後天;,前,,後;,上|去,這,下|明',
9206
9207Date.addLocale('zh-TW', {
9208  'monthSuffix': '月',
9209  'weekdays': '星期日|週日,星期一|週一,星期二|週二,星期三|週三,星期四|週四,星期五|週五,星期六|週六',
9210  'units': '毫秒,秒鐘,分鐘,小時,天,個星期|週,個月,年',
9211  'tokens': '日|號',
9212  'short':'{yyyy}年{M}月{d}日',
9213  'long': '{yyyy}年{M}月{d}日 {tt}{h}:{mm}',
9214  'full': '{yyyy}年{M}月{d}日 {Weekday} {tt}{h}:{mm}:{ss}',
9215  'past': '{num}{unit}{sign}',
9216  'future': '{num}{unit}{sign}',
9217  'duration': '{num}{unit}',
9218  'timeSuffixes': '點|時,分鐘?,秒',
9219  'ampm': '上午,下午',
9220  'modifiers': [
9221    { 'name': 'day', 'src': '前天', 'value': -2 },
9222    { 'name': 'day', 'src': '昨天', 'value': -1 },
9223    { 'name': 'day', 'src': '今天', 'value': 0 },
9224    { 'name': 'day', 'src': '明天', 'value': 1 },
9225    { 'name': 'day', 'src': '後天', 'value': 2 },
9226    { 'name': 'sign', 'src': '前', 'value': -1 },
9227    { 'name': 'sign', 'src': '後', 'value': 1 },
9228    { 'name': 'shift', 'src': '上|去', 'value': -1 },
9229    { 'name': 'shift', 'src': '這', 'value':  0 },
9230    { 'name': 'shift', 'src': '下|明', 'value':  1 }
9231  ],
9232  'dateParse': [
9233    '{num}{unit}{sign}',
9234    '{shift}{unit=5-7}'
9235  ],
9236  'timeParse': [
9237    '{shift}{weekday}',
9238    '{year}年{month?}月?{date?}{0?}',
9239    '{month}月{date?}{0?}',
9240    '{date}[日號]'
9241  ]
9242});
9243
9244
9245}).call(this);