Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(267)

Side by Side Diff: bower_components/sugar/release/sugar-full.development.js

Issue 786953007: npm_modules: Fork bower_components into Polymer 0.4.0 and 0.5.0 versions (Closed) Base URL: https://chromium.googlesource.com/infra/third_party/npm_modules.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 = Str ing, 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 propert y on
31 // native objects. IE8 does not have defineProperies, however, so this check s aves 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|Arg uments\]$/;
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 === 'bo olean';
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 + HalfWi dthComma + 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 th e Space, Separator category.
387 function getTrimmableCharacters() {
388 return '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u 2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u30 00\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](valu e);
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 f rom
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 functi ons, DOM Elements, and instances of
538 // user-created classes. The latter can arguably be matched by value, but di stinguishing 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 p ackage can be excluded if you do not require legacy browser support (IE8 and bel ow).
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), resul t, 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 interna lly.
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 addit ion to providing this method for browsers that don't support it natively, this m ethod 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 ad dition to providing this method for browsers that don't support it natively, thi s 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 receive s three arguments: the current element, the current index, and a reference to th e 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 short cut for a function that gets that property (or invokes a function) on each eleme nt.
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 Ar ray(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> occu rs, or -1 if the element is not found.
816 * @extra [fromIndex] is the index from which to begin the search. This meth od 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% met hod 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> occur s, or -1 if the element is not found.
833 * @extra [fromIndex] is the index from which to begin the search. This meth od 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 suppor t 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 el ement in the array. From that point, the result of the callback will then be use d as the first argument of the next iteration. This is often refered to as "accu mulation", 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 th is case, on the first iteration only, the first argument will be the first eleme nt of the array, and the second argument will be the second. After that callback s work as normal, using the result of the previous callback as the first argumen t of the next. This method is only provided for those browsers that do not suppo rt 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 rever se order.
893 * @extra This method is only provided for those browsers that do not suppor t 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 st andard %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 cal led. Also allows currying an unlimited number of parameters.
972 * @extra "currying" means setting parameters ([arg1], [arg2], etc.) ahead o f time so that they are passed when the function is called later. If you pass ad ditional parameters when the function is actually called, they will be added wil l be added to the end of the curried parameters. This method is provided for bro wsers 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 o bject 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 o bject, 2 curied as the first parameter and 3 passed as the second when calling t he 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 n ot 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 t he 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:0 0 (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 ele ments, 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 r esult and
1238 // 2. It exists in the compared array and we're adding, or it doesn't exis t 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, isReferenc e);
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,fi nd,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ÚÙŮÛÜVW XYÝ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 singl e, 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 in to 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, numbe r, array, object, or alternately test against a function or regex. This method i mplements @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 n ot 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, numbe r, array, object, or alternately test against a function or regex. This method i mplements @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 tes t against a function or regex. Will continue from index = 0 if [loop] is true. T his 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 tes t against a function or regex. Will continue from index = 0 if [loop] is true. T his 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 tes t against a function or regex. Starts at [index], and will continue once from in dex = 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 tes t 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 ran ge between <start> and [end]. This method will change the array! If you don't in tend 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 t he 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 st ring acting as a shortcut. This is most commonly used when you have a key that e nsures 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 duplic ates 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 beg inning) 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 arra y.
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 strin g 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 strin g 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 strin g 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(fun ction(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 strin g 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(func tion(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 stri ng 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 th at 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','n one'] ]
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 th at 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 ord er. When the field being sorted on is a string, the resulting order will be dete rmined by an internal collation algorithm that is optimized for major Western la nguages, 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:7 2}]
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 h ave 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 firs t parameter is the current element, second parameter is the current index, and t hird 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 i t 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 ag ainst 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 a rray.
2188 * @extra If [all] is %true%, all "falsy" elements will be removed. This inc ludes 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 tes t 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 Ob ject 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 availa ble 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(EnumerableMappi ngMethods).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 a go", Number methods like "daysAgo", localization support with default English lo cale 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}(?::?('+Si xtyReg+'){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, TimeFor mat.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 format s 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 shoul d come before
2811 // {day}, as 2011 needs to be parsed as a year (2011) and not date (20) + ho urs (11)
2812
2813 // If the locale has time suffixes then add a time only format for that loca le
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 th is 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 mon th 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 begi nning 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 "pref er" argument which
3088 // could unintentionally send the year into the future, past, et c.
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 t he 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 separat e
3127 // them here, remove them from the params, and set up a callback t o
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 aft er 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 othe r 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, a fter 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().daysInMo nth());
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, hiBuf fer = 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 betwe en the relative date
3407 // and "now", which it is being compared to, so set an extra buffer to a ccount 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 specifi city,
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 exis ts.
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 Janua ry 1st
3500 // (non-DST) will have a shift of -1:00 which will traverse into the pre vious year.
3501 // Prevent this by proactively setting the day when we know it will be s et 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 i s 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 th e date is going
3510 // to be set anyway, while checkMonthTraversal resets the date to the la st 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('weekd ay'));
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 m ake 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 construc tor 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 spe cified, [d] is assumed to be now. [locale] can be passed to specify the locale t hat the date is in. %[unit]Ago% is provided as an alias to make this more readab le 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 spe cified, [d] is assumed to be now. [locale] can be passed to specify the locale t hat the date is in. %[unit]FromNow% is provided as an alias to make this more re adable 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 un its will be reset.
3696 * @extra Note that "months" is ambiguous as a unit of time. If the target dat e falls on a day that does not exist (ie. August 31 -> February 31), the date wi ll be shifted to the last day of the month. Don't use %addMonths% if you need pr ecision.
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 ti me)
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.multiplie r(), 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).getT ime());
3828 };
3829 until = function(f, localeCode) {
3830 return applyErrorMargin(date.create(f, localeCode).getTime() - this.getT ime());
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}))?', tru e, ['date','month','year'], true);
3876 English.addFormat('{full_month}[-.](\\d{4,4})', false, ['month','year']);
3877 English.addFormat('\\/Date\\((\\d+(?:[+-]\\d{4,4})?)\\)\\/', false, ['timest amp'])
3878 English.addFormat(prepareTime(RequiredTime, English), false, TimeFormat)
3879
3880 // When a new locale is initialized it will have the CoreDateFormats initial ized by default.
3881 // From there, adding new formats will push them in front of the previous on es, so the core
3882 // formats will be the last to be reached. However, the core formats themsel ves have English
3883 // months in them, which means that English needs to first be initialized an d creates a race
3884 // condition. I'm getting around this here by adding these generalized forma ts in the order
3885 // specific -> general, which will mean they will be added to the English lo calization 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 a nd a back format.
3888 CoreDateFormats = English.compiledFormats.slice(0,7).reverse();
3889 English.compiledFormats = English.compiledFormats.slice(7).concat(CoreDateFo rmats);
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 rathe r
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), fun ction(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 f ormats, 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 construc tor. [locale] can be passed to specify the locale that the date is in. When unsp ecified, 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 t he past.
4082 * @extra For example %"Sunday"% can be either "the Sunday coming up" or "th e 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 th e current month
4089 * Date.past('Wednesday') -> This wednesday or last depending on the c urrent 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 t he future.
4100 * @extra For example %"Sunday"% can be either "the Sunday coming up" or "th e Sunday last" depending on context. Note that dates explicitly in the past ("la st Sunday") will remain in the past. This method simply provides a hint when amb iguity 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 "D ate Locales" package. In addition you can define a new locale with %Date.addLoca le%. 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 requir ed:
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 con trol over date localizations. For more about locales, see @date_format.
4148 *
4149 ***/
4150 'getLocale': function(localeCode) {
4151 return !localeCode ? CurrentLocalization : getLocalization(localeCode, fal se);
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. <fo rmat> 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 constructo r). If [reset] is %true%, any units more specific than those passed will be rese t.
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 co ntrasts with ISO-8601 standard (used in %getISOWeek% and %setISOWeek%) which pla ces 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 pass ing %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 - callDat eGet(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 stan dard.
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 standa rd.
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 [i so] 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(off set % 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 method s 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 th e %utc% method set true). Note that even if the utc flag is set, %getTimezoneOff set% will always report the same thing as Javascript always reports that based o n 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 str ing in the format %3 days%, a single number as milliseconds, or enumerated param eters (as with the Date constructor). If [reset] is %true%, any units more speci fic 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 constructo r). If [reset] is %true%, any units more specific than those passed will be rese t. 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 accep t 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 accep t 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'), callDateGe t(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 spec ified 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 a go
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 r esulting string. [fn] is passed 4 arguments: the adjusted value, unit, offset in milliseconds, and a localization object. As an alternate syntax, [locale] can a lso 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% add itionally understands more generalized expressions like month/weekday names, 'to day', etc, and compares to the precision implied in <d>. [margin] allows an extr a 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 && callDateG et(comp, 'Day') < 6;
4536 case d === 'weekend': return callDateGet(comp, 'Day') === 0 || callDat eGet(comp, 'Day') === 6;
4537 case (tmp = English['weekdays'].indexOf(d) % 7) > -1: return callDateG et(comp, 'Day') === tmp;
4538 case (tmp = English['months'].indexOf(d) % 12) > -1: return callDateG et(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 mil liseconds.
4624 * @extra Method names can be singular or plural. Note that as "a month" is a mbiguous as a unit of time, %months% will be equivalent to 30.4375 days, the ave rage 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 l ast day of the month. Be careful using %monthsBefore% if you need exact precisio n. 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 dat e falls on a day that does not exist (ie. August 31 -> February 31), the date wi ll 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 l ast 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 dat e falls on a day that does not exist (ie. August 31 -> February 31), the date wi ll 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 local ized string.
4791 * @extra This method is the same as %Date#relative% without the localized e quivalent 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 in cluded.
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,O ctober,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,mon th:|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. The y can enumerate over specific points within that range, and be manipulated and c ompared.
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) && t ypeof 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 v alue 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.s tart)
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 whic h case it will increment a number of units. Note that %(2).months()% first reso lves to a number, which will be interpreted as milliseconds and is an approximat ion, so stepping through the actual months by passing %"2 months"% is usually pr eferable.
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 D ate(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, a nd 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(n ew 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 curren t 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. [ste p] 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% meth od.
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. [ste p] 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 e xecution 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 w hile execution is locked. Compare this to %throttle%, which will execute only on ce 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 ex ecuted once per <ms> milliseconds.
5369 * @extra This is functionally equivalent to calling %lazy% with a [limit] o f %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 se e @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 "set tled down". A good example of this is when a user tabs quickly through form fiel ds, execution of a heavy operation should happen after a few milliseconds when t hey 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 no n-blocking operations that will wait until the CPU is free. Delayed functions ca n be canceled using the %cancel% method. Can also curry arguments passed in afte r <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% ca n be canceled using the %cancel% method. Can also curry arguments passed in afte r <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 a synchronous 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 resul t.
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 al lows currying of arguments even when they occur toward the end of an argument li st (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. Alia ses 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 passe d, 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 l ogarithm 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 num bers and very small numbers are supported. [limit] is the upper limit for the un its. 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(limi t) ? 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 uppe r 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 id entical. 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,po w,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 in stanceof 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,watc h,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() : s anitizeURIComponent(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).rep lace(/%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 liter al, including instances of inherited classes. Note also that %isNaN% will ONLY r eturn 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 val ue, and the new value. The return value of [fn] will be set as the new value. Th is 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 %Objec t.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 [f n] for each key.
6113 * @extra This method is provided for browsers that don't support it nativel y, and additionally is enhanced to accept the callback [fn]. Returned keys are i n no particular order. %keys% is available as an instance method on extended obj ects.
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 a re "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 wi th 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 a lso be a function that resolves the conflict. In this case it will be passed 3 a rguments, %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, r esult;
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 avai lable 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 in to 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]), castBool ean);
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 s tandalone 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 O bject.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.prototy pe.
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-n ot-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 i f 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 i n <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 i f the key also exists in that object, effectively "subtracting" that object. Mul tiple selections may also be passed as an array or directly as enumerated argume nts. %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 i n 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 AL WAYS 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:conve rsion.
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], wh ich 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], whic h 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%22b ar%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 charact ers. [partial] is included here for completeness, but should very rarely be need ed.
6739 * @example
6740 *
6741 * 'http%3A%2F%2Ffoo.com%2Fthe%20bar'.unescapeURL() -> 'http://foo.co m/the bar'
6742 * 'http%3A%2F%2Ffoo.com%2Fthe%20bar'.unescapeURL(true) -> 'http%3A%2F%2F foo.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 u ses a custom implementation when not available. It can also handle Unicode strin g 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 u ses a custom implementation when not available. It can also handle Unicode strin g 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 re gex, 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 ma p.
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. Retu rns 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 a n 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 arr ay of words.
6913 * @extra A "word" here is defined as any sequence of non-whitespace charact ers.
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 arr ay of lines.
6930 * @example
6931 *
6932 * 'broken wear\nand\njumpy jump'.lines() -> ['broken wear','and','jumpy j ump']
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 a n array of paragraphs.
6946 * @extra A paragraph here is defined as a block of text bounded by two or m ore 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 whit espace.
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 w hen 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 be ginning) 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(ac ronym, 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 defin ed 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, wor d, 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 ot her 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 ye ars old today.'
7363 * '{n} and {r}'.assign({ n: 'Cheech' }, { r: 'Chong' }) -> 'Cheech and Ch ong'
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 w ords 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 i n 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 wh en titleized or humanized, and will
7476 * convert the acronym into a non-delimited single lowercase word when passe d 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 a nother 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 recognize d, 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 oth erwise needs to maintain a non-standard
7506 * capitalization. The only restriction is that the word must begin with a c apital 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 eith er 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 ei ther 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 singular ization at the same time. This can only be used
7546 * for strings, not regular expressions. You simply pass the irregular in si ngular 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, singularRe st), '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, singula rRest)), pluralFirstUpper + pluralRest);
7569 Inflector.plural(new regexp('{1}{2}$'.assign(singularFirstLower, singula rRest)), pluralFirstLower + pluralRest);
7570 Inflector.plural(new regexp('{1}{2}$'.assign(pluralFirstUpper, pluralRes t)), pluralFirstUpper + pluralRest);
7571 Inflector.plural(new regexp('{1}{2}$'.assign(pluralFirstLower, pluralRes t)), pluralFirstLower + pluralRest);
7572 Inflector.singular(new regexp('{1}{2}$'.assign(pluralFirstUpper, pluralR est)), singularFirstUpper + singularRest);
7573 Inflector.singular(new regexp('{1}{2}$'.assign(pluralFirstLower, pluralR est)), 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 fo rmatting is called after the replacement.
7593 * When a string is used, the human form should be specified as desired (exa mple: '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)s es$/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,sh eep,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 wo rd 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 s trips 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 stri ng 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, i sFirstOrLast;
7767 return this.spacify().humanize().words(function(word, index, words) {
7768 hasPunctuation = fullStopPunctuation.test(word);
7769 isFirstOrLast = index == 0 || index == words.length - 1 || hasPunctuatio n || 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({ 's ep': 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-wid th 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\u 0180-\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.tes t(this.trim()); });
7899 defineProperty(string.prototype, 'has' + name, function() { return has.t est(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-\u FFE6]/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?|u nctuation/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_TRAV ERSAL));
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|we ek)
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 th e 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 fo r
8089 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8096 *
8097 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8107 Date.addLocale('da', {
8108 'plural': true,
8109 'months': 'januar,februar,marts,april,maj,juni,juli,august,september,oktober,n ovember,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ån ed:|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|i overmorgen', '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|we ek)
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 th e 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 fo r
8171 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8178 *
8179 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8189 Date.addLocale('de', {
8190 'plural': true,
8191 'capitalizeUnit': true,
8192 'months': 'Januar,Februar,März|Marz,April,Mai,Juni,Juli,August,September,Oktob er,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,Mona t:|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+kom mende: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|we ek)
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 th e 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 fo r
8251 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8258 *
8259 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8269 Date.addLocale('es', {
8270 'plural': true,
8271 'months': 'enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubr e,noviembre,diciembre',
8272 'weekdays': 'domingo,lunes,martes,miércoles|miercoles,jueves,viernes,sábado|sa bado',
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 });
8306 Date.addLocale('fi', {
8307 'plural': true,
8308 'timeMarker': 'kello',
8309 'ampm': ',',
8310 'months': 'tammikuu,helmikuu,maaliskuu,huhtikuu,toukokuu,kesäkuu,heinäku u,elokuu,syyskuu,lokakuu,marraskuu,joulukuu',
8311 'weekdays': 'sunnuntai,maanantai,tiistai,keskiviikko,torstai,perjantai,lau antai',
8312 'units': 'millisekun:ti|tia|teja|tina|nin,sekun:ti|tia|teja|tina|nin,mi nuut:ti|tia|teja|tina|in,tun:ti|tia|teja|tina|nin,päiv:ä|ää|iä|änä|än,viik:ko|ko a|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,ku u: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ä|viim e|toissa', 'value': -1 },
8342 { 'name': 'shift', 'src': 'tänä|tämän', 'value': 0 },
8343 { 'name': 'shift', 'src': 'seuraava|seuraavana|tuleva|tulevana|ensi', 'v alue': 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|we ek)
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 th e 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 fo r
8387 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8394 *
8395 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8405 Date.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,moi s,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|we ek)
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 th e 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 fo r
8464 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8471 *
8472 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8482 Date.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,m es: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|we ek)
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 th e 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 fo r
8542 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8549 *
8550 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8560 Date.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|we ek)
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 th e 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 fo r
8618 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8625 *
8626 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8636 Date.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|we ek)
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 th e 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 fo r
8698 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8705 *
8706 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8716 Date.addLocale('nl', {
8717 'plural': true,
8718 'months': 'januari,februari,maart,april,mei,juni,juli,augustus,september,oktob er,november,december',
8719 'weekdays': 'zondag|zo,maandag|ma,dinsdag|di,woensdag|woe|wo,donderdag|do,vrij dag|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|we ek)
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 th e 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 fo r
8774 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8781 *
8782 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8792 Date.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ździern ik|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,tyd zień|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|pr zyszł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|we ek)
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 th e 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 fo r
8855 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8862 *
8863 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8873 Date.addLocale('pt', {
8874 'plural': true,
8875 'months': 'janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outu bro,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ês es|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|we ek)
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 th e 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 fo r
8934 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
8941 *
8942 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
8952 Date.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|we ek)
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 th e 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 fo r
9025 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
9032 *
9033 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
9043 Date.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|iforrg ar', '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', 'valu e': 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|we ek)
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 th e 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 fo r
9107 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
9114 *
9115 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
9125 Date.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|we ek)
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 th e 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 fo r
9187 * all entries in the modifiers array follow a special format indicated by a col on:
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 u nits array will result in:
9194 *
9195 * units: ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'sa turday', '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
9207 Date.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);
OLDNEW
« no previous file with comments | « bower_components/sugar/release/copyright.txt ('k') | bower_components/sugar/release/sugar-full.min.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698