sugar-rails 1.2.5 → 1.2.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3121 @@
1
+ // Google Closure Compiler will output a wrapping function here.
2
+ (function() {
3
+
4
+ // A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
5
+ var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, Undefined, English;
6
+
7
+ // defineProperty exists in IE8 but will error when trying to define a property on
8
+ // native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
9
+ var definePropertySupport = object.defineProperty && object.defineProperties;
10
+
11
+ function extend(klass, instance, override, methods) {
12
+ var extendee = instance ? klass.prototype : klass;
13
+ iterateOverObject(methods, function(name, method) {
14
+ if(typeof override === 'function') {
15
+ defineProperty(extendee, name, wrapNative(extendee[name], method, override));
16
+ } else if(override === true || !extendee[name]) {
17
+ defineProperty(extendee, name, method);
18
+ }
19
+ });
20
+ }
21
+
22
+ function wrapNative(nativeFn, extendedFn, condition) {
23
+ return function() {
24
+ if(nativeFn && (condition === true || condition.apply(this, arguments))) {
25
+ return nativeFn.apply(this, arguments);
26
+ } else {
27
+ return extendedFn.apply(this, arguments);
28
+ }
29
+ }
30
+ }
31
+
32
+ function defineProperty(target, name, method) {
33
+ if(definePropertySupport) {
34
+ object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true });
35
+ } else {
36
+ target[name] = method;
37
+ }
38
+ }
39
+
40
+ function iterateOverObject(obj, fn) {
41
+ var count = 0;
42
+ for(var key in obj) {
43
+ if(!obj.hasOwnProperty(key)) continue;
44
+ fn.call(obj, key, obj[key], count);
45
+ count++;
46
+ }
47
+ }
48
+
49
+ function multiMatch(el, match, scope, params) {
50
+ if(el === match) {
51
+ // Match strictly equal values up front.
52
+ return true;
53
+ } else if(object.isRegExp(match)) {
54
+ // Match against a regexp
55
+ return regexp(match).test(el);
56
+ } else if(object.isFunction(match)) {
57
+ // Match against a filtering function
58
+ return match.apply(scope, [el].concat(params));
59
+ } else {
60
+ // Match against a hash or array.
61
+ return object.equal(match, el);
62
+ }
63
+ }
64
+
65
+ function transformArgument(el, map, context, mapArgs) {
66
+ if(isUndefined(map)) {
67
+ return el;
68
+ } else if(object.isFunction(map)) {
69
+ return map.apply(context, mapArgs || []);
70
+ } else if(object.isFunction(el[map])) {
71
+ return el[map].call(el);
72
+ } else {
73
+ return el[map];
74
+ }
75
+ }
76
+
77
+ function getArgs(args, index) {
78
+ return Array.prototype.slice.call(args, index);
79
+ }
80
+
81
+ function multiArgs(args, fn, flatten, index) {
82
+ args = getArgs(args);
83
+ if(flatten !== false) args = arrayFlatten(args);
84
+ arrayEach(args, fn, index);
85
+ }
86
+
87
+ /***
88
+ * Object module
89
+ *
90
+ * Much thanks to kangax for his informative aricle about how problems with instanceof and constructor
91
+ * http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
92
+ *
93
+ ***/
94
+
95
+ function instanceOf(obj, str) {
96
+ return object.prototype.toString.call(obj) === '[object '+str+']';
97
+ }
98
+
99
+ function isObjectPrimitive(o) {
100
+ return typeof o == 'object';
101
+ }
102
+
103
+ function isNull(o) {
104
+ return o === null;
105
+ }
106
+
107
+ function isUndefined(o) {
108
+ return o === Undefined;
109
+ }
110
+
111
+ function isDefined(o) {
112
+ return o !== Undefined;
113
+ }
114
+
115
+ function mergeObject(target, source, deep, resolve) {
116
+ if(isObjectPrimitive(source)) {
117
+ iterateOverObject(source, function(key, val) {
118
+ var prop = target[key], conflict = isDefined(prop), isArray = object.isArray(val);
119
+ if(deep === true && (isArray || object.isObject(val))) {
120
+ if(!prop) prop = isArray ? [] : {};
121
+ mergeObject(prop, val, deep);
122
+ } else if(conflict && object.isFunction(resolve)) {
123
+ prop = resolve.call(source, key, target[key], source[key])
124
+ } else if(!conflict || (conflict && resolve !== false)) {
125
+ prop = source[key];
126
+ }
127
+ if(isDefined(prop)) target[key] = prop;
128
+ });
129
+ }
130
+ return target;
131
+ }
132
+
133
+ function Hash(obj) {
134
+ var self = this;
135
+ iterateOverObject(obj, function(key, value) {
136
+ self[key] = value;
137
+ });
138
+ }
139
+
140
+ Hash.prototype.constructor = object;
141
+
142
+ /***
143
+ * @method is[Type](<obj>)
144
+ * @returns Boolean
145
+ * @short Returns true if <obj> is an object of that type.
146
+ * @extra %isObject% will return false on anything that is not an object literal, including instances of inherited classes. Note also that %isNaN% will ONLY return true if the object IS %NaN%. It does not mean the same as browser native %isNaN%, which returns true for anything that is "not a number". Type methods are available as instance methods on extended objects and when using Object.extend().
147
+ * @example
148
+ *
149
+ * Object.isArray([1,2,3]) -> true
150
+ * Object.isDate(3) -> false
151
+ * Object.isRegExp(/wasabi/) -> true
152
+ * Object.isObject({ broken:'wear' }) -> true
153
+ *
154
+ ***
155
+ * @method isArray()
156
+ * @set isType
157
+ ***
158
+ * @method isBoolean()
159
+ * @set isType
160
+ ***
161
+ * @method isDate()
162
+ * @set isType
163
+ ***
164
+ * @method isFunction()
165
+ * @set isType
166
+ ***
167
+ * @method isNumber()
168
+ * @set isType
169
+ ***
170
+ * @method isString()
171
+ * @set isType
172
+ ***
173
+ * @method isRegExp()
174
+ * @set isType
175
+ ***/
176
+
177
+
178
+ function buildTypeMethods() {
179
+ var methods = {};
180
+ arrayEach(['Array','Boolean','Date','Function','Number','String','RegExp'], function(type) {
181
+ methods['is' + type] = function(obj) {
182
+ return instanceOf(obj, type);
183
+ }
184
+ });
185
+ extend(Object, false, false, methods);
186
+ }
187
+
188
+ function buildObject() {
189
+ buildTypeMethods();
190
+ }
191
+
192
+ extend(object, false, false, {
193
+
194
+ /***
195
+ * @method isObject()
196
+ * @set isType
197
+ ***/
198
+ 'isObject': function(obj) {
199
+ if(isNull(obj) || isUndefined(obj)) {
200
+ return false;
201
+ } else {
202
+ return instanceOf(obj, 'Object') && obj.constructor === object;
203
+ }
204
+ },
205
+
206
+ /***
207
+ * @method each(<obj>, [fn])
208
+ * @returns Object
209
+ * @short Iterates over each property in <obj> calling [fn] on each iteration.
210
+ * @extra %each% is available as an instance method on extended objects and when using Object.extend().
211
+ * @example
212
+ *
213
+ * Object.each({ broken:'wear' }, function(key, value) {
214
+ * // Iterates over each key/value pair.
215
+ * });
216
+ * Object.extended({ broken:'wear' }).each(function(key, value) {
217
+ * // Iterates over each key/value pair.
218
+ * });
219
+ *
220
+ ***/
221
+ 'each': function(obj, fn) {
222
+ if(fn) {
223
+ iterateOverObject(obj, function(k,v) {
224
+ fn.call(obj, k, v, obj);
225
+ });
226
+ }
227
+ return obj;
228
+ },
229
+
230
+ /***
231
+ * @method merge(<target>, <source>, [resolve] = true)
232
+ * @returns Merged object
233
+ * @short Merges all the properties of <source> into <target>.
234
+ * @extra Properties of <source> will win in the case of conflicts, unless [resolve] is %false%. [resolve] can also be a function that resolves the conflict. In this case it will be passed 3 arguments, %key%, %targetVal%, and %sourceVal%, with the context set to <source>. This will allow you to solve conflict any way you want, ie. adding two numbers together, etc. %merge% is available as an instance method on extended objects and when using Object.extend().
235
+ * @example
236
+ *
237
+ * Object.merge({a:1},{b:2}) -> { a:1, b:2 }
238
+ * Object.merge({a:1},{a:2}, false) -> { a:1 }
239
+ + Object.merge({a:1},{a:2}, function(key, a, b) {
240
+ * return a + b;
241
+ * }); -> { a:3 }
242
+ * Object.extended({a:1}).merge({b:2}) -> { a:1, b:2 }
243
+ *
244
+ ***/
245
+ 'merge': function(target, merge, resolve) {
246
+ return mergeObject(target, merge, true, resolve);
247
+ }
248
+
249
+ });
250
+
251
+
252
+
253
+
254
+
255
+
256
+ /***
257
+ * Array module
258
+ *
259
+ ***/
260
+
261
+
262
+
263
+ // Basic array internal methods
264
+
265
+ function arrayEach(arr, fn, startIndex, loop) {
266
+ var length, index, i;
267
+ checkCallback(fn);
268
+ if(startIndex < 0) startIndex = arr.length + startIndex;
269
+ i = toIntegerWithDefault(startIndex, 0);
270
+ length = loop === true ? arr.length + i : arr.length;
271
+ while(i < length) {
272
+ index = i % arr.length;
273
+ if(fn.call(arr, arr[index], index, arr) === false) {
274
+ break;
275
+ }
276
+ i++;
277
+ }
278
+ }
279
+
280
+ function arrayFind(arr, f, startIndex, loop, returnIndex) {
281
+ var result, index;
282
+ arrayEach(arr, function(el, i, arr) {
283
+ if(multiMatch(el, f, arr, [i, arr])) {
284
+ result = el;
285
+ index = i;
286
+ return false;
287
+ }
288
+ }, startIndex, loop);
289
+ return returnIndex ? index : result;
290
+ }
291
+
292
+ function arrayFlatten(arr, level, current) {
293
+ level = level || Infinity;
294
+ current = current || 0;
295
+ var result = [];
296
+ arrayEach(arr, function(el) {
297
+ if(object.isArray(el) && current < level) {
298
+ result = result.concat(arrayFlatten(el, level, current + 1));
299
+ } else {
300
+ result.push(el);
301
+ }
302
+ });
303
+ return result;
304
+ }
305
+
306
+ // ECMA5 methods
307
+
308
+ function arrayIndexOf(arr, search, fromIndex, increment) {
309
+ var length = arr.length,
310
+ fromRight = increment == -1,
311
+ start = fromRight ? length - 1 : 0,
312
+ index = toIntegerWithDefault(fromIndex, start);
313
+ if(index < 0) {
314
+ index = length + index;
315
+ }
316
+ if((!fromRight && index < 0) || (fromRight && index >= length)) {
317
+ index = start;
318
+ }
319
+ while((fromRight && index >= 0) || (!fromRight && index < length)) {
320
+ if(arr[index] === search) {
321
+ return index;
322
+ }
323
+ index += increment;
324
+ }
325
+ return -1;
326
+ }
327
+
328
+ function toIntegerWithDefault(i, d) {
329
+ if(isNaN(i)) {
330
+ return d;
331
+ } else {
332
+ return parseInt(i >> 0);
333
+ }
334
+ }
335
+
336
+ function isArrayIndex(arr, i) {
337
+ return i in arr && toUInt32(i) == i && i != 0xffffffff;
338
+ }
339
+
340
+ function toUInt32(i) {
341
+ return i >>> 0;
342
+ }
343
+
344
+ function checkCallback(fn) {
345
+ if(!fn || !fn.call) {
346
+ throw new TypeError('Callback is not callable');
347
+ }
348
+ }
349
+
350
+ function checkFirstArgumentExists(args) {
351
+ if(args.length === 0) {
352
+ throw new TypeError('First argument must be defined');
353
+ }
354
+ }
355
+
356
+ extend(array, true, function() { var a = arguments; return a.length === 0 || object.isFunction(a[0]); }, {
357
+
358
+ /***
359
+ * @method every(<f>, [scope])
360
+ * @returns Boolean
361
+ * @short Returns true if all elements in the array match <f>.
362
+ * @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts strings, numbers, deep objects, and arrays for <f>. %all% is provided an alias.
363
+ * @example
364
+ *
365
+ + ['a','a','a'].every(function(n) {
366
+ * return n == 'a';
367
+ * });
368
+ * ['a','a','a'].every('a') -> true
369
+ * [{a:2},{a:2}].every({a:2}) -> true
370
+ *
371
+ ***/
372
+ 'every': function(f, scope) {
373
+ var length = this.length, index = 0;
374
+ checkFirstArgumentExists(arguments);
375
+ while(index < length) {
376
+ if(index in this && !multiMatch(this[index], f, scope, [index, this])) {
377
+ return false;
378
+ }
379
+ index++;
380
+ }
381
+ return true;
382
+ },
383
+
384
+ /***
385
+ * @method some(<f>, [scope])
386
+ * @returns Boolean
387
+ * @short Returns true if any element in the array matches <f>.
388
+ * @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts strings, numbers, deep objects, and arrays for <f>. %any% and %has% are provided as aliases.
389
+ * @example
390
+ *
391
+ + ['a','b','c'].some(function(n) {
392
+ * return n == 'a';
393
+ * });
394
+ + ['a','b','c'].some(function(n) {
395
+ * return n == 'd';
396
+ * });
397
+ * ['a','b','c'].some('a') -> true
398
+ * [{a:2},{b:5}].some({a:2}) -> true
399
+ *
400
+ ***/
401
+ 'some': function(f, scope) {
402
+ var length = this.length, index = 0;
403
+ checkFirstArgumentExists(arguments);
404
+ while(index < length) {
405
+ if(index in this && multiMatch(this[index], f, scope, [index, this])) {
406
+ return true;
407
+ }
408
+ index++;
409
+ }
410
+ return false;
411
+ },
412
+
413
+ /***
414
+ * @method map(<map>, [scope])
415
+ * @returns Array
416
+ * @short Maps the array to another array containing the values that are the result of calling <map> on each element.
417
+ * @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts a string, which is a shortcut for a function that gets that property (or invokes a function) on each element. %collect% is provided as an alias.
418
+ * @example
419
+ *
420
+ + [1,2,3].map(function(n) {
421
+ * return n * 3;
422
+ * }); -> [3,6,9]
423
+ * ['one','two','three'].map(function(n) {
424
+ * return n.length;
425
+ * }); -> [3,3,5]
426
+ * ['one','two','three'].map('length') -> [3,3,5]
427
+ *
428
+ ***/
429
+ 'map': function(map, scope) {
430
+ var length = this.length, index = 0, el, result = new Array(length);
431
+ checkFirstArgumentExists(arguments);
432
+ while(index < length) {
433
+ if(index in this) {
434
+ el = this[index];
435
+ result[index] = transformArgument(el, map, scope, [el, index, this]);
436
+ }
437
+ index++;
438
+ }
439
+ return result;
440
+ },
441
+
442
+ /***
443
+ * @method filter(<f>, [scope])
444
+ * @returns Array
445
+ * @short Returns any elements in the array that match <f>.
446
+ * @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts strings, numbers, deep objects, and arrays for <f>.
447
+ * @example
448
+ *
449
+ + [1,2,3].filter(function(n) {
450
+ * return n > 1;
451
+ * });
452
+ * [1,2,2,4].filter(2) -> 2
453
+ *
454
+ ***/
455
+ 'filter': function(f, scope) {
456
+ var length = this.length, index = 0, result = [];
457
+ checkFirstArgumentExists(arguments);
458
+ while(index < length) {
459
+ if(index in this && multiMatch(this[index], f, scope, [index, this])) {
460
+ result.push(this[index]);
461
+ }
462
+ index++;
463
+ }
464
+ return result;
465
+ }
466
+
467
+ });
468
+
469
+
470
+
471
+
472
+ extend(array, true, false, {
473
+
474
+ /***
475
+ * @method indexOf(<search>, [fromIndex])
476
+ * @returns Number
477
+ * @short Searches the array and returns the first index where <search> occurs, or -1 if the element is not found.
478
+ * @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>. It does not support enhanced functionality such as searching the contents against a regex, callback, or deep comparison of objects. For such functionality, use the %find% method instead.
479
+ * @example
480
+ *
481
+ * [1,2,3].indexOf(3) -> 1
482
+ * [1,2,3].indexOf(7) -> -1
483
+ *
484
+ ***/
485
+ 'indexOf': function(search, fromIndex) {
486
+ if(object.isString(this)) return this.indexOf(search, fromIndex);
487
+ return arrayIndexOf(this, search, fromIndex, 1);
488
+ },
489
+
490
+ /***
491
+ * @method lastIndexOf(<search>, [fromIndex])
492
+ * @returns Number
493
+ * @short Searches the array and returns the last index where <search> occurs, or -1 if the element is not found.
494
+ * @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>.
495
+ * @example
496
+ *
497
+ * [1,2,1].lastIndexOf(1) -> 2
498
+ * [1,2,1].lastIndexOf(7) -> -1
499
+ *
500
+ ***/
501
+ 'lastIndexOf': function(search, fromIndex) {
502
+ if(object.isString(this)) return this.lastIndexOf(search, fromIndex);
503
+ return arrayIndexOf(this, search, fromIndex, -1);
504
+ },
505
+
506
+ /***
507
+ * @method forEach([fn], [scope])
508
+ * @returns Nothing
509
+ * @short Iterates over the array, calling [fn] on each loop.
510
+ * @extra This method is only provided for those browsers that do not support it natively. [scope] becomes the %this% object.
511
+ * @example
512
+ *
513
+ * ['a','b','c'].forEach(function(a) {
514
+ * // Called 3 times: 'a','b','c'
515
+ * });
516
+ *
517
+ ***/
518
+ 'forEach': function(fn, scope) {
519
+ var length = this.length, index = 0;
520
+ checkCallback(fn);
521
+ while(index < length) {
522
+ if(index in this) {
523
+ fn.call(scope, this[index], index, this);
524
+ }
525
+ index++;
526
+ }
527
+ },
528
+
529
+ /***
530
+ * @method reduce([fn], [init])
531
+ * @returns Mixed
532
+ * @short Reduces the array to a single result.
533
+ * @extra By default this method calls [fn] n - 1 times, where n is the length of the array. On the first call it is passed the first and second elements in the array. The result of that callback will then be passed into the next iteration until it reaches the end, where the accumulated value will be returned as the final result. If [init] is passed, it will call [fn] one extra time in the beginning passing in [init] along with the first element. This method is only provided for those browsers that do not support it natively.
534
+ * @example
535
+ *
536
+ + [1,2,3,4].reduce(function(a, b) {
537
+ * return a + b;
538
+ * });
539
+ + [1,2,3,4].reduce(function(a, b) {
540
+ * return a + b;
541
+ * }, 100);
542
+ *
543
+ ***/
544
+ 'reduce': function(fn, init) {
545
+ return arrayReduce(this, fn, init);
546
+ },
547
+
548
+ /***
549
+ * @method reduceRight([fn], [init])
550
+ * @returns Mixed
551
+ * @short Reduces the array to a single result by stepping through it from the right.
552
+ * @extra By default this method calls [fn] n - 1 times, where n is the length of the array. On the first call it is passed the last and second to last elements in the array. The result of that callback will then be passed into the next iteration until it reaches the beginning, where the accumulated value will be returned as the final result. If [init] is passed, it will call [fn] one extra time in the beginning passing in [init] along with the last element. This method is only provided for those browsers that do not support it natively.
553
+ * @example
554
+ *
555
+ + [1,2,3,4].reduceRight(function(a, b) {
556
+ * return a - b;
557
+ * });
558
+ *
559
+ ***/
560
+ 'reduceRight': function(fn, init) {
561
+ return arrayReduce(this, fn, init, true);
562
+ },
563
+
564
+ /***
565
+ * @method groupBy(<map>, [fn])
566
+ * @returns Object
567
+ * @short Groups the array by <map>.
568
+ * @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.
569
+ * @example
570
+ *
571
+ * ['fee','fi','fum'].groupBy('length') -> { 2: ['fi'], 3: ['fee','fum'] }
572
+ + [{age:35,name:'ken'},{age:15,name:'bob'}].groupBy(function(n) {
573
+ * return n.age;
574
+ * }); -> { 35: [{age:35,name:'ken'}], 15: [{age:15,name:'bob'}] }
575
+ *
576
+ ***/
577
+ 'groupBy': function(map, fn) {
578
+ var arr = this, result = {}, key;
579
+ arrayEach(arr, function(el, index) {
580
+ key = transformArgument(el, map, arr, [el, index, arr]);
581
+ if(!result[key]) result[key] = [];
582
+ result[key].push(el);
583
+ });
584
+ return object.each(result, fn);
585
+ },
586
+
587
+ /***
588
+ * @method compact([all] = false)
589
+ * @returns Array
590
+ * @short Removes all instances of %undefined%, %null%, and %NaN% from the array.
591
+ * @extra If [all] is %true%, all "falsy" elements will be removed. This includes empty strings, 0, and false.
592
+ * @example
593
+ *
594
+ * [1,null,2,undefined,3].compact() -> [1,2,3]
595
+ * [1,'',2,false,3].compact() -> [1,'',2,false,3]
596
+ * [1,'',2,false,3].compact(true) -> [1,2,3]
597
+ *
598
+ ***/
599
+ 'compact': function(all) {
600
+ var result = [];
601
+ arrayEach(this, function(el, i) {
602
+ if(object.isArray(el)) {
603
+ result.push(el.compact());
604
+ } else if(all && el) {
605
+ result.push(el);
606
+ } else if(!all && isDefined(el) && !isNull(el) && (!object.isNumber(el) || !isNaN(el))) {
607
+ result.push(el);
608
+ }
609
+ });
610
+ return result;
611
+ }
612
+
613
+
614
+ });
615
+
616
+
617
+
618
+
619
+
620
+ /***
621
+ * Number module
622
+ *
623
+ ***/
624
+
625
+
626
+ function round(val, precision, method) {
627
+ var fn = Math[method || 'round'];
628
+ var multiplier = Math.abs(Math.pow(10, (precision || 0)));
629
+ if(precision < 0) multiplier = 1 / multiplier;
630
+ return fn(val * multiplier) / multiplier;
631
+ }
632
+
633
+ function getRange(start, stop, fn, step) {
634
+ var arr = [], i = parseInt(start), up = step > 0;
635
+ while((up && i <= stop) || (!up && i >= stop)) {
636
+ arr.push(i);
637
+ if(fn) fn.call(this, i);
638
+ i += step;
639
+ }
640
+ return arr;
641
+ }
642
+
643
+
644
+ extend(number, true, false, {
645
+
646
+ /***
647
+ * @method toNumber()
648
+ * @returns Number
649
+ * @short Returns a number. This is mostly for compatibility reasons.
650
+ * @example
651
+ *
652
+ * (420).toNumber() -> 420
653
+ *
654
+ ***/
655
+ 'toNumber': function() {
656
+ return parseFloat(this, 10);
657
+ },
658
+
659
+ /***
660
+ * @method ordinalize()
661
+ * @returns String
662
+ * @short Returns an ordinalized (English) string, i.e. "1st", "2nd", etc.
663
+ * @example
664
+ *
665
+ * (1).ordinalize() -> '1st';
666
+ * (2).ordinalize() -> '2nd';
667
+ * (8).ordinalize() -> '8th';
668
+ *
669
+ ***/
670
+ 'ordinalize': function() {
671
+ var suffix;
672
+ if(this >= 11 && this <= 13) {
673
+ suffix = 'th';
674
+ } else {
675
+ switch(this % 10) {
676
+ case 1: suffix = 'st'; break;
677
+ case 2: suffix = 'nd'; break;
678
+ case 3: suffix = 'rd'; break;
679
+ default: suffix = 'th';
680
+ }
681
+ }
682
+ return this.toString() + suffix;
683
+ },
684
+
685
+ /***
686
+ * @method pad(<place> = 0, [sign] = false, [base] = 10)
687
+ * @returns String
688
+ * @short Pads a number with "0" to <place>.
689
+ * @extra [sign] allows you to force the sign as well (+05, etc). [base] can change the base for numeral conversion.
690
+ * @example
691
+ *
692
+ * (5).pad(2) -> '05'
693
+ * (-5).pad(4) -> '-0005'
694
+ * (82).pad(3, true) -> '+082'
695
+ *
696
+ ***/
697
+ 'pad': function(place, sign, base) {
698
+ base = base || 10;
699
+ var str = this.toNumber() === 0 ? '' : this.toString(base).replace(/^-/, '');
700
+ str = padString(str, '0', place - str.replace(/\.\d+$/, '').length, 0);
701
+ if(sign || this < 0) {
702
+ str = (this < 0 ? '-' : '+') + str;
703
+ }
704
+ return str;
705
+ }
706
+
707
+ });
708
+
709
+
710
+ /***
711
+ * @method [unit]()
712
+ * @returns Number
713
+ * @short Takes the number as a corresponding unit of time and converts to milliseconds.
714
+ * @extra Method names can be both singular and plural. Note that as "a month" is ambiguous as a unit of time, %months% will be equivalent to 30.4375 days, the average number in a month. Be careful using %months% if you need exact precision.
715
+ * @example
716
+ *
717
+ * (5).milliseconds() -> 5
718
+ * (10).hours() -> 36000000
719
+ * (1).day() -> 86400000
720
+ *
721
+ ***
722
+ * @method millisecond()
723
+ * @set unit
724
+ ***
725
+ * @method milliseconds()
726
+ * @set unit
727
+ ***
728
+ * @method second()
729
+ * @set unit
730
+ ***
731
+ * @method seconds()
732
+ * @set unit
733
+ ***
734
+ * @method minute()
735
+ * @set unit
736
+ ***
737
+ * @method minutes()
738
+ * @set unit
739
+ ***
740
+ * @method hour()
741
+ * @set unit
742
+ ***
743
+ * @method hours()
744
+ * @set unit
745
+ ***
746
+ * @method day()
747
+ * @set unit
748
+ ***
749
+ * @method days()
750
+ * @set unit
751
+ ***
752
+ * @method week()
753
+ * @set unit
754
+ ***
755
+ * @method weeks()
756
+ * @set unit
757
+ ***
758
+ * @method month()
759
+ * @set unit
760
+ ***
761
+ * @method months()
762
+ * @set unit
763
+ ***
764
+ * @method year()
765
+ * @set unit
766
+ ***
767
+ * @method years()
768
+ * @set unit
769
+ ***
770
+ * @method [unit]Before([d], [locale] = currentLocale)
771
+ * @returns Date
772
+ * @short Returns a date that is <n> units before [d], where <n> is the number.
773
+ * @extra [d] will accept a date object, timestamp, or text format. Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsBefore% if you need exact precision. See @date_format for more information.
774
+ * @example
775
+ *
776
+ * (5).daysBefore('tuesday') -> 5 days before tuesday of this week
777
+ * (1).yearBefore('January 23, 1997') -> January 23, 1996
778
+ *
779
+ ***
780
+ * @method millisecondBefore()
781
+ * @set unitBefore
782
+ ***
783
+ * @method millisecondsBefore()
784
+ * @set unitBefore
785
+ ***
786
+ * @method secondBefore()
787
+ * @set unitBefore
788
+ ***
789
+ * @method secondsBefore()
790
+ * @set unitBefore
791
+ ***
792
+ * @method minuteBefore()
793
+ * @set unitBefore
794
+ ***
795
+ * @method minutesBefore()
796
+ * @set unitBefore
797
+ ***
798
+ * @method hourBefore()
799
+ * @set unitBefore
800
+ ***
801
+ * @method hoursBefore()
802
+ * @set unitBefore
803
+ ***
804
+ * @method dayBefore()
805
+ * @set unitBefore
806
+ ***
807
+ * @method daysBefore()
808
+ * @set unitBefore
809
+ ***
810
+ * @method weekBefore()
811
+ * @set unitBefore
812
+ ***
813
+ * @method weeksBefore()
814
+ * @set unitBefore
815
+ ***
816
+ * @method monthBefore()
817
+ * @set unitBefore
818
+ ***
819
+ * @method monthsBefore()
820
+ * @set unitBefore
821
+ ***
822
+ * @method yearBefore()
823
+ * @set unitBefore
824
+ ***
825
+ * @method yearsBefore()
826
+ * @set unitBefore
827
+ ***
828
+ * @method [unit]Ago()
829
+ * @returns Date
830
+ * @short Returns a date that is <n> units ago.
831
+ * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsAgo% if you need exact precision.
832
+ * @example
833
+ *
834
+ * (5).weeksAgo() -> 5 weeks ago
835
+ * (1).yearAgo() -> January 23, 1996
836
+ *
837
+ ***
838
+ * @method millisecondAgo()
839
+ * @set unitAgo
840
+ ***
841
+ * @method millisecondsAgo()
842
+ * @set unitAgo
843
+ ***
844
+ * @method secondAgo()
845
+ * @set unitAgo
846
+ ***
847
+ * @method secondsAgo()
848
+ * @set unitAgo
849
+ ***
850
+ * @method minuteAgo()
851
+ * @set unitAgo
852
+ ***
853
+ * @method minutesAgo()
854
+ * @set unitAgo
855
+ ***
856
+ * @method hourAgo()
857
+ * @set unitAgo
858
+ ***
859
+ * @method hoursAgo()
860
+ * @set unitAgo
861
+ ***
862
+ * @method dayAgo()
863
+ * @set unitAgo
864
+ ***
865
+ * @method daysAgo()
866
+ * @set unitAgo
867
+ ***
868
+ * @method weekAgo()
869
+ * @set unitAgo
870
+ ***
871
+ * @method weeksAgo()
872
+ * @set unitAgo
873
+ ***
874
+ * @method monthAgo()
875
+ * @set unitAgo
876
+ ***
877
+ * @method monthsAgo()
878
+ * @set unitAgo
879
+ ***
880
+ * @method yearAgo()
881
+ * @set unitAgo
882
+ ***
883
+ * @method yearsAgo()
884
+ * @set unitAgo
885
+ ***
886
+ * @method [unit]After([d], [locale] = currentLocale)
887
+ * @returns Date
888
+ * @short Returns a date <n> units after [d], where <n> is the number.
889
+ * @extra [d] will accept a date object, timestamp, or text format. Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsAfter% if you need exact precision. See @date_format for more information.
890
+ * @example
891
+ *
892
+ * (5).daysAfter('tuesday') -> 5 days after tuesday of this week
893
+ * (1).yearAfter('January 23, 1997') -> January 23, 1998
894
+ *
895
+ ***
896
+ * @method millisecondAfter()
897
+ * @set unitAfter
898
+ ***
899
+ * @method millisecondsAfter()
900
+ * @set unitAfter
901
+ ***
902
+ * @method secondAfter()
903
+ * @set unitAfter
904
+ ***
905
+ * @method secondsAfter()
906
+ * @set unitAfter
907
+ ***
908
+ * @method minuteAfter()
909
+ * @set unitAfter
910
+ ***
911
+ * @method minutesAfter()
912
+ * @set unitAfter
913
+ ***
914
+ * @method hourAfter()
915
+ * @set unitAfter
916
+ ***
917
+ * @method hoursAfter()
918
+ * @set unitAfter
919
+ ***
920
+ * @method dayAfter()
921
+ * @set unitAfter
922
+ ***
923
+ * @method daysAfter()
924
+ * @set unitAfter
925
+ ***
926
+ * @method weekAfter()
927
+ * @set unitAfter
928
+ ***
929
+ * @method weeksAfter()
930
+ * @set unitAfter
931
+ ***
932
+ * @method monthAfter()
933
+ * @set unitAfter
934
+ ***
935
+ * @method monthsAfter()
936
+ * @set unitAfter
937
+ ***
938
+ * @method yearAfter()
939
+ * @set unitAfter
940
+ ***
941
+ * @method yearsAfter()
942
+ * @set unitAfter
943
+ ***
944
+ * @method [unit]FromNow()
945
+ * @returns Date
946
+ * @short Returns a date <n> units from now.
947
+ * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Be careful using %monthsFromNow% if you need exact precision.
948
+ * @example
949
+ *
950
+ * (5).weeksFromNow() -> 5 weeks ago
951
+ * (1).yearFromNow() -> January 23, 1998
952
+ *
953
+ ***
954
+ * @method millisecondFromNow()
955
+ * @set unitFromNow
956
+ ***
957
+ * @method millisecondsFromNow()
958
+ * @set unitFromNow
959
+ ***
960
+ * @method secondFromNow()
961
+ * @set unitFromNow
962
+ ***
963
+ * @method secondsFromNow()
964
+ * @set unitFromNow
965
+ ***
966
+ * @method minuteFromNow()
967
+ * @set unitFromNow
968
+ ***
969
+ * @method minutesFromNow()
970
+ * @set unitFromNow
971
+ ***
972
+ * @method hourFromNow()
973
+ * @set unitFromNow
974
+ ***
975
+ * @method hoursFromNow()
976
+ * @set unitFromNow
977
+ ***
978
+ * @method dayFromNow()
979
+ * @set unitFromNow
980
+ ***
981
+ * @method daysFromNow()
982
+ * @set unitFromNow
983
+ ***
984
+ * @method weekFromNow()
985
+ * @set unitFromNow
986
+ ***
987
+ * @method weeksFromNow()
988
+ * @set unitFromNow
989
+ ***
990
+ * @method monthFromNow()
991
+ * @set unitFromNow
992
+ ***
993
+ * @method monthsFromNow()
994
+ * @set unitFromNow
995
+ ***
996
+ * @method yearFromNow()
997
+ * @set unitFromNow
998
+ ***
999
+ * @method yearsFromNow()
1000
+ * @set unitFromNow
1001
+ ***/
1002
+ function buildNumberToDateAlias(unit, multiplier) {
1003
+ var add = 'add' + unit.capitalize() + 's';
1004
+ function base() { return round(this * multiplier); }
1005
+ function after() { return createDate(arguments)[add](this); }
1006
+ function before() { return createDate(arguments)[add](-this); }
1007
+ defineProperty(number.prototype, unit, base);
1008
+ defineProperty(number.prototype, unit + 's', base);
1009
+ defineProperty(number.prototype, unit + 'Before', before);
1010
+ defineProperty(number.prototype, unit + 'sBefore', before);
1011
+ defineProperty(number.prototype, unit + 'Ago', before);
1012
+ defineProperty(number.prototype, unit + 'sAgo', before);
1013
+ defineProperty(number.prototype, unit + 'After', after);
1014
+ defineProperty(number.prototype, unit + 'sAfter', after);
1015
+ defineProperty(number.prototype, unit + 'FromNow', after);
1016
+ defineProperty(number.prototype, unit + 'sFromNow', after);
1017
+ }
1018
+
1019
+
1020
+
1021
+
1022
+ /***
1023
+ * String module
1024
+ *
1025
+ ***/
1026
+
1027
+
1028
+ // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
1029
+ var getTrimmableCharacters = function() {
1030
+ return '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF';
1031
+ }
1032
+
1033
+ function padString(str, p, left, right) {
1034
+ var padding = String(p);
1035
+ if(padding != p) {
1036
+ padding = '';
1037
+ }
1038
+ if(!object.isNumber(left)) left = 1;
1039
+ if(!object.isNumber(right)) right = 1;
1040
+ return padding.repeat(left) + str + padding.repeat(right);
1041
+ }
1042
+
1043
+ function buildTrim() {
1044
+ var support = getTrimmableCharacters().match(/^\s+$/);
1045
+ try { string.prototype.trim.call([1]); } catch(e) { support = false; }
1046
+ var trimL = regexp('^['+getTrimmableCharacters()+']+');
1047
+ var trimR = regexp('['+getTrimmableCharacters()+']+$');
1048
+ extend(string, true, !support, {
1049
+
1050
+ /***
1051
+ * @method trim[Side]()
1052
+ * @returns String
1053
+ * @short Removes leading and/or trailing whitespace from the string.
1054
+ * @extra Whitespace is defined as line breaks, tabs, and any character in the "Space, Separator" Unicode category, conforming to the the ES5 spec. The standard %trim% method is only added when not fully supported natively.
1055
+ * @example
1056
+ *
1057
+ * ' wasabi '.trim() -> 'wasabi'
1058
+ * ' wasabi '.trimLeft() -> 'wasabi '
1059
+ * ' wasabi '.trimRight() -> ' wasabi'
1060
+ *
1061
+ ***
1062
+ * @method trim()
1063
+ * @set trimSide
1064
+ ***/
1065
+ 'trim': function() {
1066
+ return this.toString().trimLeft().trimRight();
1067
+ },
1068
+
1069
+ /***
1070
+ * @method trimLeft()
1071
+ * @set trimSide
1072
+ ***/
1073
+ 'trimLeft': function() {
1074
+ return this.replace(trimL, '');
1075
+ },
1076
+
1077
+ /***
1078
+ * @method trimRight()
1079
+ * @set trimSide
1080
+ ***/
1081
+ 'trimRight': function() {
1082
+ return this.replace(trimR, '');
1083
+ }
1084
+ });
1085
+ }
1086
+
1087
+ function buildString() {
1088
+ buildTrim();
1089
+ }
1090
+
1091
+ extend(string, true, false, {
1092
+
1093
+ /***
1094
+ * @method capitalize([all] = false)
1095
+ * @returns String
1096
+ * @short Capitalizes the first character in the string.
1097
+ * @extra If [all] is true, all words in the string will be capitalized.
1098
+ * @example
1099
+ *
1100
+ * 'hello'.capitalize() -> 'hello'
1101
+ * 'hello kitty'.capitalize() -> 'hello kitty'
1102
+ * 'hello kitty'.capitalize(true) -> 'hello kitty'
1103
+ *
1104
+ *
1105
+ ***/
1106
+ 'capitalize': function(all) {
1107
+ var reg = all ? /\b[a-z]/g : /^[a-z]/;
1108
+ return this.toLowerCase().replace(reg, function(letter) {
1109
+ return letter.toUpperCase();
1110
+ });
1111
+ },
1112
+
1113
+ /***
1114
+ * @method repeat([num] = 0)
1115
+ * @returns String
1116
+ * @short Returns the string repeated [num] times.
1117
+ * @example
1118
+ *
1119
+ * 'jumpy'.repeat(2) -> 'jumpyjumpy'
1120
+ * 'a'.repeat(5) -> 'aaaaa'
1121
+ *
1122
+ ***/
1123
+ 'repeat': function(num) {
1124
+ var str = '', i = 0;
1125
+ if(object.isNumber(num) && num > 0) {
1126
+ while(i < num) {
1127
+ str += this;
1128
+ i++;
1129
+ }
1130
+ }
1131
+ return str;
1132
+ },
1133
+
1134
+ /***
1135
+ * @method toNumber([base] = 10)
1136
+ * @returns Number
1137
+ * @short Converts the string into a number.
1138
+ * @extra Any value with a "." fill be converted to a floating point value, otherwise an integer.
1139
+ * @example
1140
+ *
1141
+ * '153'.toNumber() -> 153
1142
+ * '12,000'.toNumber() -> 12000
1143
+ * '10px'.toNumber() -> 10
1144
+ * 'ff'.toNumber(16) -> 255
1145
+ *
1146
+ ***/
1147
+ 'toNumber': function(base) {
1148
+ var str = this.replace(/,/g, '');
1149
+ return str.match(/\./) ? parseFloat(str) : parseInt(str, base || 10);
1150
+ },
1151
+
1152
+ /***
1153
+ * @method first([n] = 1)
1154
+ * @returns String
1155
+ * @short Returns the first [n] characters of the string.
1156
+ * @example
1157
+ *
1158
+ * 'lucky charms'.first() -> 'l'
1159
+ * 'lucky charms'.first(3) -> 'luc'
1160
+ *
1161
+ ***/
1162
+ 'first': function(num) {
1163
+ num = isUndefined(num) ? 1 : num;
1164
+ return this.substr(0, num);
1165
+ },
1166
+
1167
+ /***
1168
+ * @method last([n] = 1)
1169
+ * @returns String
1170
+ * @short Returns the last [n] characters of the string.
1171
+ * @example
1172
+ *
1173
+ * 'lucky charms'.last() -> 's'
1174
+ * 'lucky charms'.last(3) -> 'rms'
1175
+ *
1176
+ ***/
1177
+ 'last': function(num) {
1178
+ num = isUndefined(num) ? 1 : num;
1179
+ var start = this.length - num < 0 ? 0 : this.length - num;
1180
+ return this.substr(start);
1181
+ },
1182
+
1183
+ /***
1184
+ * @method to([index] = end)
1185
+ * @returns String
1186
+ * @short Returns a section of the string ending at [index].
1187
+ * @example
1188
+ *
1189
+ * 'lucky charms'.to() -> 'lucky charms'
1190
+ * 'lucky charms'.to(7) -> 'lucky ch'
1191
+ *
1192
+ ***/
1193
+ 'to': function(num) {
1194
+ if(isUndefined(num)) num = this.length;
1195
+ return this.slice(0, num);
1196
+ },
1197
+
1198
+ /***
1199
+ * @method toDate([locale])
1200
+ * @returns Date
1201
+ * @short Creates a date from the string.
1202
+ * @extra Accepts a wide range of input. [locale] allows you to specify a locale code. See @date_format for more information.
1203
+ * @example
1204
+ *
1205
+ * 'January 25, 2015'.toDate() -> same as Date.create('January 25, 2015')
1206
+ * 'yesterday'.toDate() -> same as Date.create('yesterday')
1207
+ * 'next Monday'.toDate() -> same as Date.create('next Monday')
1208
+ *
1209
+ ***/
1210
+ 'toDate': function(locale) {
1211
+ return createDate([this.toString(), locale]);
1212
+ },
1213
+
1214
+ /***
1215
+ * @method assign(<obj1>, <obj2>, ...)
1216
+ * @returns String
1217
+ * @short Assigns variables to tokens in a string.
1218
+ * @extra If an object is passed, it's properties can be assigned using the object's keys. 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.
1219
+ * @example
1220
+ *
1221
+ * 'Welcome, Mr. {name}.'.assign({ name: 'Franklin' }) -> 'Welcome, Mr. Franklin.'
1222
+ * 'You are {1} years old today.'.assign(14) -> 'You are 14 years old today.'
1223
+ * '{n} and {r}'.assign({ n: 'Cheech' }, { r: 'Chong' }) -> 'Cheech and Chong'
1224
+ *
1225
+ ***/
1226
+ 'assign': function() {
1227
+ var assign = {};
1228
+ multiArgs(arguments, function(a, i) {
1229
+ if(object.isObject(a)) {
1230
+ object.merge(assign, a);
1231
+ } else {
1232
+ assign[i + 1] = a;
1233
+ }
1234
+ });
1235
+ return this.replace(/\{(.+?)\}/g, function(m, key) {
1236
+ return assign.hasOwnProperty(key) ? assign[key] : m;
1237
+ });
1238
+ }
1239
+
1240
+ });
1241
+
1242
+
1243
+
1244
+
1245
+ /***
1246
+ * Date module
1247
+ *
1248
+ ***/
1249
+
1250
+ var TimeFormat = ['hour','minute','second','meridian','utc','offset_sign','offset_hours','offset_minutes']
1251
+ var FloatReg = '\\d{1,2}(?:[,.]\\d+)?';
1252
+ var RequiredTime = '('+FloatReg+'):?('+FloatReg+')?:?('+FloatReg+')?(am|pm)?(?:(Z)|(?:([+-])(\\d{2})(?::?(\\d{2}))?)?)?';
1253
+ var OptionalTime = '\\s*(?:(?:t|at |\\s+)' + RequiredTime + ')?';
1254
+
1255
+ var LowerAsianDigits = '一二三四五六七八九';
1256
+ var UpperAsianDigits = '十百千万';
1257
+ var AsianDigitReg = regexp('[' + LowerAsianDigits + UpperAsianDigits + ']', 'g');
1258
+ var DateInputFormats = [];
1259
+ var DateArgumentUnits;
1260
+ var DateUnitsReversed;
1261
+
1262
+ var StaticInputFormats = [
1263
+ // @date_format 2010
1264
+ { src: '(\\d{4})', to: ['year'] },
1265
+ // @date_format 2010-05
1266
+ // @date_format 2010.05
1267
+ // @date_format 2010/05
1268
+ // @date_format 2010-05-25 (ISO8601)
1269
+ // @date_format 2010-05-25T12:30:40.299Z (ISO8601)
1270
+ // @date_format 2010-05-25T12:30:40.299+01:00 (ISO8601)
1271
+ // @date_format 2010.05.25
1272
+ // @date_format 2010/05/25
1273
+ { src: '([+-])?(\\d{4})[-.]?({month})[-.]?(\\d{1,2})?', to: ['year_sign','year','month','date'] },
1274
+ // @date_format 05-25
1275
+ // @date_format 05/25
1276
+ // @date_format 05.25
1277
+ // @date_format 05-25-2010
1278
+ // @date_format 05/25/2010
1279
+ // @date_format 05.25.2010
1280
+ { src: '(\\d{1,2})[-.\\/]({month})[-.\\/]?(\\d{2,4})?', to: ['month','date','year'], variant: true },
1281
+ // @date_format Date(628318530718)
1282
+ { src: '\\/Date\\((\\d+(?:\\+\\d{4})?)\\)\\/', to: ['timestamp'], time: false }
1283
+
1284
+ ];
1285
+
1286
+ var DateOutputFormats = [
1287
+ {
1288
+ token: 'f{1,4}|ms|milliseconds',
1289
+ format: function(d) {
1290
+ return d.getMilliseconds();
1291
+ }
1292
+ },
1293
+ {
1294
+ token: 'ss?|seconds',
1295
+ format: function(d, len) {
1296
+ return d.getSeconds();
1297
+ }
1298
+ },
1299
+ {
1300
+ token: 'mm?|minutes',
1301
+ format: function(d, len) {
1302
+ return d.getMinutes();
1303
+ }
1304
+ },
1305
+ {
1306
+ token: 'hh?|hours|12hr',
1307
+ format: function(d) {
1308
+ return getShortHour(d);
1309
+ }
1310
+ },
1311
+ {
1312
+ token: 'HH?|24hr',
1313
+ format: function(d) {
1314
+ return d.getHours();
1315
+ }
1316
+ },
1317
+ {
1318
+ token: 'dd?|date|day',
1319
+ format: function(d) {
1320
+ return d.getDate();
1321
+ }
1322
+ },
1323
+ {
1324
+ token: 'dow|weekday',
1325
+ word: true,
1326
+ format: function(d, loc, n, t) {
1327
+ return loc['weekdays'][d.getDay() + (n - 1) * 7];
1328
+ }
1329
+ },
1330
+ {
1331
+ token: 'MM?',
1332
+ format: function(d) {
1333
+ return d.getMonth() + 1;
1334
+ }
1335
+ },
1336
+ {
1337
+ token: 'mon|month',
1338
+ word: true,
1339
+ format: function(d, loc, n, len) {
1340
+ return loc['months'][d.getMonth() + (n - 1) * 12];
1341
+ }
1342
+ },
1343
+ {
1344
+ token: 'y{2,4}|year',
1345
+ format: function(d) {
1346
+ return d.getFullYear();
1347
+ }
1348
+ },
1349
+ {
1350
+ token: '[Tt]{1,2}',
1351
+ format: function(d, loc, n, format) {
1352
+ var m = getMeridian(d);
1353
+ if(format.length === 1) m = m.first();
1354
+ if(format.first() === 'T') m = m.toUpperCase();
1355
+ return m;
1356
+ }
1357
+ },
1358
+ {
1359
+ token: 'z{1,4}|tz|timezone',
1360
+ text: true,
1361
+ format: function(d, loc, n, format) {
1362
+ var tz = d.getUTCOffset();
1363
+ if(format == 'z' || format == 'zz') {
1364
+ tz = tz.replace(/(\d{2})(\d{2})/, function(f,h,m) {
1365
+ return h.toNumber().pad(format.length);
1366
+ });
1367
+ }
1368
+ return tz;
1369
+ }
1370
+ },
1371
+ {
1372
+ token: 'iso(tz|timezone)',
1373
+ format: function(d) {
1374
+ return d.getUTCOffset(true);
1375
+ }
1376
+ },
1377
+ {
1378
+ token: 'ord',
1379
+ format: function(d) {
1380
+ return d.getDate().ordinalize();
1381
+ }
1382
+ }
1383
+ ];
1384
+
1385
+ var DateUnits = [
1386
+ {
1387
+ unit: 'year',
1388
+ method: 'FullYear',
1389
+ multiplier: function(d) {
1390
+ var adjust = d ? (d.isLeapYear() ? 1 : 0) : 0.25;
1391
+ return (365 + adjust) * 24 * 60 * 60 * 1000;
1392
+ }
1393
+ },
1394
+ {
1395
+ unit: 'month',
1396
+ method: 'Month',
1397
+ multiplier: function(d, ms) {
1398
+ var days = 30.4375, inMonth;
1399
+ if(d) {
1400
+ inMonth = d.daysInMonth();
1401
+ if(ms <= inMonth.days()) {
1402
+ days = inMonth;
1403
+ }
1404
+ }
1405
+ return days * 24 * 60 * 60 * 1000;
1406
+ }
1407
+ },
1408
+ {
1409
+ unit: 'week',
1410
+ method: 'Week',
1411
+ multiplier: function() {
1412
+ return 7 * 24 * 60 * 60 * 1000;
1413
+ }
1414
+ },
1415
+ {
1416
+ unit: 'day',
1417
+ method: 'Date',
1418
+ multiplier: function() {
1419
+ return 24 * 60 * 60 * 1000;
1420
+ }
1421
+ },
1422
+ {
1423
+ unit: 'hour',
1424
+ method: 'Hours',
1425
+ multiplier: function() {
1426
+ return 60 * 60 * 1000;
1427
+ }
1428
+ },
1429
+ {
1430
+ unit: 'minute',
1431
+ method: 'Minutes',
1432
+ multiplier: function() {
1433
+ return 60 * 1000;
1434
+ }
1435
+ },
1436
+ {
1437
+ unit: 'second',
1438
+ method: 'Seconds',
1439
+ multiplier: function() {
1440
+ return 1000;
1441
+ }
1442
+ },
1443
+ {
1444
+ unit: 'millisecond',
1445
+ method: 'Milliseconds',
1446
+ multiplier: function() {
1447
+ return 1;
1448
+ }
1449
+ }
1450
+ ];
1451
+
1452
+
1453
+
1454
+
1455
+ // Date Localization
1456
+
1457
+ var Localizations = {};
1458
+
1459
+ var CommonLocales = {
1460
+
1461
+ 'en': '2;;January,February,March,April,May,June,July,August,September,October,November,December;Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday;millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s;one,two,three,four,five,six,seven,eight,nine,ten;a,an,the;the,st|nd|rd|th,of;{num} {unit} {sign},{num} {unit=4-5} {sign} {day},{weekday?} {month} {date}{1} {year?} {time?},{date} {month} {year},{month} {year},{shift?} {weekday} {time?},{shift} week {weekday} {time?},{shift} {unit=5-7},{0} {edge} of {shift?} {unit=4-7?}{month?}{year?},{weekday} {2} {shift} week,{0} {date}{1} of {month},{0}{month?} {date?}{1} of {shift} {unit=6-7},{day} at {time?},{time} {day};{Month} {d}, {yyyy};,yesterday,today,tomorrow;,ago|before,,from now|after|from;,last,the|this,next;last day,end,,first day|beginning',
1462
+
1463
+ 'ja': '1;月;;日曜日,月曜日,火曜日,水曜日,木曜日,金曜日,土曜日;ミリ秒,秒,分,時間,日,週間|週,ヶ月|ヵ月|月,年;;;;{num}{unit}{sign},{shift}{unit=5-7}{weekday?},{year}年{month?}月?{date?}日?,{month}月{date?}日?,{date}日;{yyyy}年{M}月{d}日;一昨日,昨日,今日,明日,明後日;,前,,後;,去|先,,来',
1464
+
1465
+ 'ko': '1;월;;일요일,월요일,화요일,수요일,목요일,금요일,토요일;밀리초,초,분,시간,일,주,개월|달,년;일|한,이,삼,사,오,육,칠,팔,구,십;;;{num}{unit} {sign},{shift} {unit=5-7},{shift} {unit=5?} {weekday},{year}년{month?}월?{date?}일?,{month}월{date?}일?,{date}일;{yyyy}년{M}월{d}일;그저께,어제,오늘,내일,모레;,전,,후;,지난|작,이번,다음|내',
1466
+
1467
+ 'ru': '4;;Январ:я|ь,Феврал:я|ь,Март:а|,Апрел:я|ь,Ма:я|й,Июн:я|ь,Июл:я|ь,Август:а|,Сентябр:я|ь,Октябр:я|ь,Ноябр:я|ь,Декабр:я|ь;Воскресенье,Понедельник,Вторник,Среда,Четверг,Пятница,Суббота;миллисекунд:а|у|ы|,секунд:а|у|ы|,минут:а|у|ы|,час:||а|ов,день|день|дня|дней,недел:я|ю|и|ь|е,месяц:||а|ев|е,год|год|года|лет|году;од:ин|ну,дв:а|е,три,четыре,пять,шесть,семь,восемь,девять,десять;;в|на,года;{num} {unit} {sign},{sign} {num} {unit},{date} {month} {year?} {1},{month} {year},{0} {shift} {unit=5-7};{d} {month} {yyyy} года;позавчера,вчера,сегодня,завтра,послезавтра;,назад,,через;,прошло:й|м,,следующе:й|м',
1468
+
1469
+ 'es': '6;;enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre;domingo,lunes,martes,miércoles|miercoles,jueves,viernes,sábado|sabado;milisegundo:|s,segundo:|s,minuto:|s,hora:|s,día|días|dia|dias,semana:|s,mes:|es,año|años|ano|anos;uno,dos,tres,cuatro,cinco,seis,siete,ocho,nueve,diez;;el,de;{sign} {num} {unit},{num} {unit} {sign},{date?} {1} {month} {1} {year?},{0} {unit=5-7} {shift},{0} {shift} {unit=5-7};{d} de {month} de {yyyy};anteayer,ayer,hoy,mañana|manana;,hace,,de ahora;,pasad:o|a,,próximo|próxima|proximo|proxima',
1470
+
1471
+ 'pt': '6;;janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outubro,novembro,dezembro;domingo,segunda-feira,terça-feira,quarta-feira,quinta-feira,sexta-feira,sábado|sabado;milisegundo:|s,segundo:|s,minuto:|s,hora:|s,dia:|s,semana:|s,mês|mêses|mes|meses,ano:|s;um,dois,três|tres,quatro,cinco,seis,sete,oito,nove,dez,uma,duas;;a,de;{num} {unit} {sign},{sign} {num} {unit},{date?} {1} {month} {1} {year?},{0} {unit=5-7} {shift},{0} {shift} {unit=5-7};{d} de {month} de {yyyy};anteontem,ontem,hoje,amanh:ã|a;,atrás|atras|há|ha,,daqui a;,passad:o|a,,próximo|próxima|proximo|proxima',
1472
+
1473
+ 'fr': '2;;janvier,février|fevrier,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre|decembre;dimanche,lundi,mardi,mercredi,jeudi,vendredi,samedi;milliseconde:|s,seconde:|s,minute:|s,heure:|s,jour:|s,semaine:|s,mois,an:|s|née|nee;un:|e,deux,trois,quatre,cinq,six,sept,huit,neuf,dix;;l\'|la|le;{sign} {num} {unit},{sign} {num} {unit},{0} {date?} {month} {year?},{0} {unit=5-7} {shift};{d} {month} {yyyy};,hier,aujourd\'hui,demain;,il y a,,dans|d\'ici;,derni:er|ère|ere,,prochain:|e',
1474
+
1475
+ 'it': '2;;Gennaio,Febbraio,Marzo,Aprile,Maggio,Giugno,Luglio,Agosto,Settembre,Ottobre,Novembre,Dicembre;Domenica,Luned:ì|i,Marted:ì|i,Mercoled:ì|i,Gioved:ì|i,Venerd:ì|i,Sabato;millisecond:o|i,second:o|i,minut:o|i,or:a|e,giorn:o|i,settiman:a|e,mes:e|i,ann:o|i;un:|\'|a|o,due,tre,quattro,cinque,sei,sette,otto,nove,dieci;;l\'|la|il;{num} {unit} {sign},{weekday?} {date?} {month} {year?},{0} {unit=5-7} {shift},{0} {shift} {unit=5-7};{d} {month} {yyyy};,ieri,oggi,domani,dopodomani;,fa,,da adesso;,scors:o|a,,prossim:o|a',
1476
+
1477
+ 'de': '2;;Januar,Februar,März|Marz,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember;Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag;Millisekunde:|n,Sekunde:|n,Minute:|n,Stunde:|n,Tag:|en,Woche:|n,Monat:|en,Jahr:|en;ein:|e|er|em|en,zwei,drei,vier,fuenf,sechs,sieben,acht,neun,zehn;;der;{sign} {num} {unit},{num} {unit} {sign},{num} {unit} {sign},{sign} {num} {unit},{weekday?} {date?} {month} {year?},{shift} {unit=5-7};{d}. {Month} {yyyy};vorgestern,gestern,heute,morgen,übermorgen|ubermorgen|uebermorgen;,vor:|her,,in;,letzte:|r|n|s,,nächste:|r|n|s+naechste:|r|n|s',
1478
+
1479
+ 'zh-TW': '1;月;;日,一,二,三,四,五,六;毫秒,秒鐘,分鐘,小時,天,個星期|週,個月,年;;;日|號;{num}{unit}{sign},星期{weekday},{shift}{unit=5-7},{shift}{unit=5}{weekday},{year}年{month?}月?{date?}{0},{month}月{date?}{0},{date}{0};{yyyy}年{M}月{d}日;前天,昨天,今天,明天,後天;,前,,後;,上|去,這,下|明',
1480
+
1481
+ 'zh-CN': '1;月;;日,一,二,三,四,五,六;毫秒,秒钟,分钟,小时,天,个星期|周,个月,年;;;日|号;{num}{unit}{sign},星期{weekday},{shift}{unit=5-7},{shift}{unit=5}{weekday},{year}年{month?}月?{date?}{0},{month}月{date?}{0},{date}{0};{yyyy}年{M}月{d}日;前天,昨天,今天,明天,后天;,前,,后;,上|去,这,下|明'
1482
+
1483
+ }
1484
+
1485
+ function checkLocaleFormatsAdded(loc) {
1486
+ var addFormat = date.addFormat, code = loc['code'];
1487
+ if(loc.formatsAdded) return;
1488
+ addDateInputFormat('(' + loc['months'].compact().join('|') + ')', ['month'], code);
1489
+ addDateInputFormat('(' + loc['weekdays'].compact().join('|') + ')', ['weekday'], code);
1490
+ addDateInputFormat('(' + loc['modifiers'].filter(function(m){ return m.name === 'day'; }).map('src').join('|') + ')', ['day'], code);
1491
+ arrayEach(loc['formats'], function(src) {
1492
+ loc.addFormat(src, code, false);
1493
+ });
1494
+ loc.formatsAdded = true;
1495
+ }
1496
+
1497
+ function addDateInputFormat(format, match, locale, variant, method) {
1498
+ method = method || 'push';
1499
+ DateInputFormats[method]({
1500
+ variant: variant,
1501
+ locale: locale,
1502
+ reg: regexp('^' + format + '$', 'i'),
1503
+ to: match
1504
+ });
1505
+ }
1506
+
1507
+ function getLocalization(code, fallback, set) {
1508
+ if(fallback && (!object.isString(code) || !code)) code = Date['currentLocale'];
1509
+ if(code && !Localizations[code]) initializeLocalization(code, set);
1510
+ return Localizations[code];
1511
+ }
1512
+
1513
+ function initializeLocalization(code, set) {
1514
+ set = set || getCommonLocalization(code);
1515
+ if(!set) {
1516
+ throw new Error('Invalid locale.');
1517
+ }
1518
+
1519
+ function eachAlternate(str, fn) {
1520
+ str = str.split('+').map(function(split) {
1521
+ return split.replace(/(.+):(.+)$/, function(full, base, suffixes) {
1522
+ return suffixes.split('|').map(function(suffix) {
1523
+ return base + suffix;
1524
+ }).join('|');
1525
+ });
1526
+ }).join('|');
1527
+ return arrayEach(str.split('|'), fn);
1528
+ }
1529
+
1530
+ function setArray(name, abbreviate, multiple) {
1531
+ var arr = [];
1532
+ if(!set[name]) return;
1533
+ arrayEach(set[name], function(el, i) {
1534
+ eachAlternate(el, function(str, j) {
1535
+ arr[j * multiple + i] = str.toLowerCase();
1536
+ });
1537
+ });
1538
+ if(abbreviate) arr = arr.concat(set[name].map(function(str) {
1539
+ return str.slice(0,3).toLowerCase();
1540
+ }));
1541
+ return set[name] = arr;
1542
+ }
1543
+
1544
+ function getDigit(start, stop) {
1545
+ var str = '[0-90-9]' + (start ? '{' + start + ',' + stop + '}' : '+');
1546
+ if(set['digits']) str += '|[' + set['digits'] + ']+';
1547
+ return str;
1548
+ }
1549
+
1550
+ function getNum() {
1551
+ var arr = [getDigit()].concat(set['articles']);
1552
+ if(!set['digits']) arr = arr.concat(set['numbers']);
1553
+ return arr.compact().join('|');
1554
+ }
1555
+
1556
+ function setModifiers() {
1557
+ var arr = [];
1558
+ set.modifiersByName = {};
1559
+ arrayEach(set['modifiers'], function(modifier) {
1560
+ eachAlternate(modifier.src, function(t) {
1561
+ set.modifiersByName[t] = modifier;
1562
+ arr.push({ name: modifier.name, src: t, value: modifier.value });
1563
+ });
1564
+ });
1565
+ arr.groupBy('name', function(name, group) {
1566
+ group = group.map('src');
1567
+ if(name === 'day') group = group.concat(set['weekdays']);
1568
+ set[name] = group.join('|');
1569
+ });
1570
+ set['modifiers'] = arr;
1571
+ }
1572
+
1573
+ setArray('months', true, 12);
1574
+ setArray('weekdays', true, 7);
1575
+ setArray('units', false, 8);
1576
+ setArray('numbers', false, 10);
1577
+
1578
+ set['code'] = code;
1579
+ set['date'] = getDigit(1,2);
1580
+ set['year'] = getDigit(4,4);
1581
+ set['num'] = getNum();
1582
+
1583
+ setModifiers();
1584
+
1585
+ if(set['monthSuffix']) {
1586
+ set['month'] = getDigit(1,2);
1587
+ set['months'] = getRange(1, 12, null, 1).map(function(n) { return n + set['monthSuffix']; });
1588
+ }
1589
+ Localizations[code] = new Localization(set);
1590
+ }
1591
+
1592
+ function getCommonLocalization(code) {
1593
+ if(code.slice(0,3) == 'en-') code = 'en';
1594
+ if(!CommonLocales[code]) return null;
1595
+ var set = { 'modifiers': [] }, pre = CommonLocales[code].split(';');
1596
+ function bool(n) {
1597
+ return !!(pre[0] & Math.pow(2,n-1));
1598
+ }
1599
+ arrayEach(['months','weekdays','units','numbers','articles','optionals','formats'], function(name, i) {
1600
+ set[name] = pre[i + 2] ? pre[i + 2].split(',') : [];
1601
+ });
1602
+ set['outputFormat'] = pre[9];
1603
+ arrayEach(['day','sign','shift','edge'], function(name, i) {
1604
+ if(!pre[i + 10]) return;
1605
+ arrayEach(pre[i + 10].split(','), function(t, j) {
1606
+ if(t) set['modifiers'].push({ name: name, src: t, value: j - 2 });
1607
+ });
1608
+ });
1609
+ if(bool(1)) {
1610
+ set['digits'] = LowerAsianDigits + UpperAsianDigits;
1611
+ if(set['numbers'].length > 0) {
1612
+ set['digits'] += set['numbers'].join('');
1613
+ } else {
1614
+ set['numbers'] = LowerAsianDigits.split('');
1615
+ }
1616
+ set['monthSuffix'] = pre[1];
1617
+ }
1618
+ set['capitalizeUnit'] = (code == 'de');
1619
+ set['hasPlural'] = bool(2);
1620
+ set['pastRelativeFormat'] = set['formats'][0];
1621
+ set['futureRelativeFormat'] = set['formats'][bool(3) ? 1 : 0];
1622
+ set['durationFormat'] = set['formats'][0].replace(/\s*\{sign\}\s*/, '');
1623
+ return set;
1624
+ }
1625
+
1626
+ function getVariant(locale) {
1627
+ if(!locale) locale = Date['currentLocale'];
1628
+ return locale != 'en' && locale != 'en-US';
1629
+ }
1630
+
1631
+ function Localization(l) {
1632
+ object.merge(this, l);
1633
+ }
1634
+
1635
+ extend(Localization, true, false, {
1636
+
1637
+ getMonth: function(n) {
1638
+ if(object.isNumber(n)) {
1639
+ return n - 1;
1640
+ } else {
1641
+ return arrayFind(this['months'], regexp(n, 'i'), 0, false, true) % 12;
1642
+ }
1643
+ },
1644
+
1645
+ getWeekday: function(n) {
1646
+ return arrayFind(this['weekdays'], regexp(n, 'i'), 0, false, true) % 7;
1647
+ },
1648
+
1649
+ getNumber: function(n) {
1650
+ var i;
1651
+ if(object.isNumber(n)) {
1652
+ return n;
1653
+ } else if(n && (i = this['numbers'].indexOf(n)) !== -1) {
1654
+ return (i + 1) % 10;
1655
+ } else {
1656
+ return 1;
1657
+ }
1658
+ },
1659
+
1660
+ getNumericDate: function(n) {
1661
+ var self = this;
1662
+ return n.replace(this['numbers'][9], '').replace(/./g, function(d) {
1663
+ return self.getNumber(d);
1664
+ });
1665
+ },
1666
+
1667
+ getEnglishUnit: function(n) {
1668
+ return English['units'][this['units'].indexOf(n) % 8];
1669
+ },
1670
+
1671
+ relative: function(adu) {
1672
+ return this.convertAdjustedToFormat(adu, adu[2] > 0 ? 'futureRelativeFormat' : 'pastRelativeFormat');
1673
+ },
1674
+
1675
+ duration: function(ms) {
1676
+ return this.convertAdjustedToFormat(getAdjustedUnit(ms), 'durationFormat');
1677
+ },
1678
+
1679
+ convertAdjustedToFormat: function(adu, format) {
1680
+ var num = adu[0], u = adu[1], ms = adu[2], sign, unit, last, mult;
1681
+ if(this['code'] == 'ru') {
1682
+ last = num.toString().slice(-1);
1683
+ switch(true) {
1684
+ case last == 1: mult = 1; break;
1685
+ case last >= 2 && last <= 4: mult = 2; break;
1686
+ default: mult = 3;
1687
+ }
1688
+ } else {
1689
+ mult = this['hasPlural'] && num > 1 ? 1 : 0;
1690
+ }
1691
+ unit = this['units'][mult * 8 + u] || this['units'][u];
1692
+ if(this['capitalizeUnit']) unit = unit.capitalize();
1693
+ sign = arrayFind(this['modifiers'], function(m) { return m.name == 'sign' && m.value == (ms > 0 ? 1 : -1); });
1694
+ return this[format].assign({ 'num': num, 'unit': unit, 'sign': sign.src });
1695
+ },
1696
+
1697
+ addFormat: function(src, code, add) {
1698
+ var to = [], loc = this;
1699
+ if(add !== false) loc['formats'].push(src);
1700
+ src = src.replace(/\s+/g, '[-,. ]*');
1701
+ src = src.replace(/\{(.+?)\}/g, function(all, k) {
1702
+ var opt = k.match(/\?$/), slice = k.match(/(\d)(?:-(\d))?/), nc = k.match(/^\d+$/), key = k.replace(/[^a-z]+$/, ''), value, arr;
1703
+ if(key === 'time') {
1704
+ to = to.concat(TimeFormat);
1705
+ return opt ? OptionalTime : RequiredTime;
1706
+ }
1707
+ if(nc) {
1708
+ value = loc['optionals'][nc[0]];
1709
+ } else if(loc[key]) {
1710
+ value = loc[key];
1711
+ } else if(loc[key + 's']) {
1712
+ value = loc[key + 's'];
1713
+ if(slice) {
1714
+ // Can't use filter here as Prototype hijacks the method and doesn't
1715
+ // pass an index, so use a simple loop instead!
1716
+ arr = [];
1717
+ value.forEach(function(m, i) {
1718
+ var mod = i % (loc['units'] ? 8 : value.length);
1719
+ if(mod >= slice[1] && mod <= (slice[2] || slice[1])) {
1720
+ arr.push(m);
1721
+ }
1722
+ });
1723
+ value = arr;
1724
+ }
1725
+ value = value.compact().join('|');
1726
+ }
1727
+ if(nc) {
1728
+ return '(?:' + value + ')?';
1729
+ } else {
1730
+ to.push(key);
1731
+ return '(' + value + ')' + (opt ? '?' : '');
1732
+ }
1733
+ });
1734
+ addDateInputFormat(src, to, code);
1735
+ }
1736
+
1737
+ });
1738
+
1739
+ function collectDateArguments(args) {
1740
+ var obj, arr;
1741
+ if(isObjectPrimitive(args[0])) {
1742
+ return args;
1743
+ } else if (args.length == 1 && object.isNumber(args[0])) {
1744
+ return [args[0]];
1745
+ }
1746
+ obj = {};
1747
+ arrayEach(DateArgumentUnits, function(u,i) {
1748
+ obj[u.unit] = args[i];
1749
+ });
1750
+ return [obj];
1751
+ }
1752
+
1753
+ function convertAsianDigits(str, key) {
1754
+ str = str.replace(/[1234567890]/g, function(d) {
1755
+ return String.fromCharCode(d.charCodeAt(0) - 65248);
1756
+ });
1757
+ if(key != 'date' && key != 'month' && key != 'year') return str;
1758
+ return str.replace(AsianDigitReg, function(d) {
1759
+ var index = LowerAsianDigits.indexOf(d);
1760
+ return (index + 1) || '';
1761
+ });
1762
+ }
1763
+
1764
+ function getFormatMatch(match, arr) {
1765
+ var obj = {}, value, num;
1766
+ arrayEach(arr, function(key, i) {
1767
+ value = match[i + 1];
1768
+ if(isUndefined(value) || value === '') return;
1769
+ value = convertAsianDigits(value, key);
1770
+ if(key === 'year') obj.yearAsString = value;
1771
+ num = parseFloat(value.replace(/,/, '.'));
1772
+ obj[key] = !isNaN(num) ? num : value.toLowerCase();
1773
+ });
1774
+ return obj;
1775
+ }
1776
+
1777
+ function getExtendedDate(f, locale) {
1778
+ var d = new date(), relative = false, loc, variant, format, set, unit, num, tmp;
1779
+ if(object.isDate(f)) {
1780
+ d = f;
1781
+ } else if(object.isNumber(f)) {
1782
+ d = new date(f);
1783
+ } else if(object.isObject(f)) {
1784
+ d = new date().set(f, true);
1785
+ set = f;
1786
+ } else if(object.isString(f)) {
1787
+ // Pre-initialize the localization formats.
1788
+
1789
+ checkLocaleFormatsAdded(getLocalization(locale, true));
1790
+ variant = getVariant(locale);
1791
+ f = f.trim().replace(/\.+$/,'').replace(/^now$/, '');
1792
+ arrayEach(DateInputFormats, function(dif) {
1793
+ var match = f.match(dif.reg);
1794
+ if(match) {
1795
+ format = dif;
1796
+ set = getFormatMatch(match, format.to);
1797
+ loc = getLocalization(format.locale, true);
1798
+
1799
+ if(set.timestamp) {
1800
+ d.setTime(0);
1801
+ set = { 'milliseconds': set.timestamp };
1802
+ return false;
1803
+ }
1804
+
1805
+ // If there's a European variant, swap the month and day.
1806
+ if(format.variant && !object.isString(set['month']) && (object.isString(set['date']) || variant)) {
1807
+ tmp = set['month'];
1808
+ set['month'] = set['date'];
1809
+ set['date'] = tmp;
1810
+ }
1811
+
1812
+ // If the year is 2 digits then get the implied century.
1813
+ if(set['year'] && set.yearAsString.length === 2) {
1814
+ set['year'] = getYearFromAbbreviation(set['year']);
1815
+ }
1816
+
1817
+ // Set the month which may be localized.
1818
+ if(set['month']) {
1819
+ set['month'] = loc.getMonth(set['month']);
1820
+ if(set['shift'] && !set['unit']) set['unit'] = 'year';
1821
+ }
1822
+
1823
+ // If there is both a weekday and a date, the date takes precedence.
1824
+ if(set['weekday'] && set['date']) {
1825
+ delete set['weekday'];
1826
+ // Otherwise set a localized weekday.
1827
+ } else if(set['weekday']) {
1828
+ set['weekday'] = loc.getWeekday(set['weekday']);
1829
+ if(set['shift'] && !set['unit']) set['unit'] = 'week';
1830
+ }
1831
+
1832
+ // Relative day localizations such as "today" and "tomorrow".
1833
+ if(set['day'] && (tmp = loc.modifiersByName[set['day']])) {
1834
+ set['day'] = tmp.value;
1835
+ d.resetTime();
1836
+ relative = true;
1837
+ // If the day is a weekday, then set that instead.
1838
+ } else if(set['day'] && (tmp = loc.getWeekday(set['day'])) > -1) {
1839
+ delete set['day'];
1840
+ set['weekday'] = tmp;
1841
+ }
1842
+
1843
+ if(set['date'] && !object.isNumber(set['date'])) {
1844
+ set['date'] = loc.getNumericDate(set['date']);
1845
+ }
1846
+
1847
+ // If the time is 1pm-11pm advance the time by 12 hours.
1848
+ if(set['meridian']) {
1849
+ if(set['meridian'] === 'pm' && set['hour'] < 12) set['hour'] += 12;
1850
+ }
1851
+
1852
+ // Adjust for timezone offset
1853
+ if('offset_hours' in set || 'offset_minutes' in set) {
1854
+ set['utc'] = true;
1855
+ set['offset_minutes'] = set['offset_minutes'] || 0;
1856
+ set['offset_minutes'] += set['offset_hours'] * 60;
1857
+ if(set['offset_sign'] === '-') {
1858
+ set['offset_minutes'] *= -1;
1859
+ }
1860
+ set['minute'] -= set['offset_minutes'];
1861
+ }
1862
+
1863
+ // Date has a unit like "days", "months", etc. are all relative to the current date.
1864
+ if(set['unit']) {
1865
+ relative = true;
1866
+ num = loc.getNumber(set['num']);
1867
+ unit = loc.getEnglishUnit(set['unit']);
1868
+
1869
+ // Shift and unit, ie "next month", "last week", etc.
1870
+ if(set['shift'] || set['edge']) {
1871
+ num *= (tmp = loc.modifiersByName[set['shift']]) ? tmp.value : 0;
1872
+
1873
+ // Relative month and static date: "the 15th of last month"
1874
+ if(unit === 'month' && isDefined(set['date'])) {
1875
+ d.set({ 'day': set['date'] }, true);
1876
+ delete set['date'];
1877
+ }
1878
+
1879
+ // Relative year and static month/date: "June 15th of last year"
1880
+ if(unit === 'year' && isDefined(set['month'])) {
1881
+ d.set({ 'month': set['month'], 'day': set['date'] }, true);
1882
+ delete set['month'];
1883
+ delete set['date'];
1884
+ }
1885
+ }
1886
+
1887
+ // Unit and sign, ie "months ago", "weeks from now", etc.
1888
+ if(set['sign'] && (tmp = loc.modifiersByName[set['sign']])) {
1889
+ num *= tmp.value;
1890
+ }
1891
+
1892
+ // Units can be with non-relative dates, set here. ie "the day after monday"
1893
+ if(isDefined(set['weekday'])) {
1894
+ d.set({'weekday': set['weekday'] }, true);
1895
+ delete set['weekday'];
1896
+ }
1897
+
1898
+ // Finally shift the unit.
1899
+ set[unit] = (set[unit] || 0) + num;
1900
+ }
1901
+
1902
+ if(set['year_sign'] === '-') {
1903
+ set['year'] *= -1;
1904
+ }
1905
+
1906
+ arrayEach(DateUnitsReversed.slice(1,4), function(u, i) {
1907
+ var value = set[u.unit], fraction = value % 1;
1908
+ if(fraction) {
1909
+ set[DateUnitsReversed[i].unit] = round(fraction * (u.unit === 'second' ? 1000 : 60));
1910
+ set[u.unit] = value | 0;
1911
+ }
1912
+ });
1913
+ return false;
1914
+ }
1915
+ });
1916
+ if(!format) {
1917
+ // The Date constructor does something tricky like checking the number
1918
+ // of arguments so simply passing in undefined won't work.
1919
+ d = f ? new date(f) : new date();
1920
+ } else if(relative) {
1921
+ d.advance(set);
1922
+ } else if(set['utc']) {
1923
+ // UTC times can traverse into other days or even months,
1924
+ // so preemtively reset the time here to prevent this.
1925
+ d.resetTime();
1926
+ d.setUTC(set, true);
1927
+ } else {
1928
+ d.set(set, true);
1929
+ }
1930
+
1931
+ // If there is an "edge" it needs to be set after the
1932
+ // other fields are set. ie "the end of February"
1933
+ if(set && set['edge']) {
1934
+ tmp = loc.modifiersByName[set['edge']];
1935
+ arrayEach(DateUnitsReversed.slice(4), function(u) {
1936
+ if(isDefined(set[u.unit])) {
1937
+ unit = u.unit;
1938
+ return false;
1939
+ }
1940
+ });
1941
+ if(unit === 'year') set.specificity = 'month';
1942
+ else if(unit === 'month' || unit === 'week') set.specificity = 'day';
1943
+ d[(tmp.value < 0 ? 'endOf' : 'beginningOf') + unit.capitalize()]();
1944
+ // This value of -2 is arbitrary but it's a nice clean way to hook into this system.
1945
+ if(tmp.value === -2) d.resetTime();
1946
+ }
1947
+ }
1948
+ return {
1949
+ date: d,
1950
+ set: set
1951
+ }
1952
+ }
1953
+
1954
+ function formatDate(date, f, relative, locale) {
1955
+ var adu, loc = getLocalization(locale, true), caps = regexp(/^[A-Z]/), value, l;
1956
+ if(!date.isValid()) {
1957
+ return 'Invalid Date';
1958
+ } else if(Date[f]) {
1959
+ f = Date[f];
1960
+ } else if(object.isFunction(f)) {
1961
+ adu = getAdjustedUnit(date.millisecondsFromNow());
1962
+ f = f.apply(date, adu.concat(loc));
1963
+ }
1964
+ if(!f && !relative) {
1965
+ f = loc['outputFormat'];
1966
+ } else if(!f && relative) {
1967
+ adu = adu || getAdjustedUnit(date.millisecondsFromNow());
1968
+ // Adjust up if time is in ms, as this doesn't
1969
+ // look very good for a standard relative date.
1970
+ if(adu[1] === 0) {
1971
+ adu[1] = 1;
1972
+ adu[0] = 1;
1973
+ }
1974
+ return loc.relative(adu);
1975
+ }
1976
+ arrayEach(DateOutputFormats, function(dof) {
1977
+ f = f.replace(regexp('\\{('+dof.token+')(\\d)?\\}', dof.word ? 'i' : ''), function(m,t,d) {
1978
+ var val = dof.format(date, loc, d || 1, t), l = t.length, one = t.match(/^(.)\1+$/);
1979
+ if(dof.word) {
1980
+ if(l === 3) val = val.slice(0, 3);
1981
+ if(one || t.match(caps)) val = val.capitalize();
1982
+ } else if(one && !dof.text) {
1983
+ val = (object.isNumber(val) ? val.pad(l) : val.toString()).last(l);
1984
+ }
1985
+ return val;
1986
+ });
1987
+ });
1988
+ return f;
1989
+ }
1990
+
1991
+ function compareDate(d, find, buffer) {
1992
+ var p = getExtendedDate(find), accuracy = 0, loBuffer = 0, hiBuffer = 0, override;
1993
+ if(buffer > 0) {
1994
+ loBuffer = hiBuffer = buffer;
1995
+ override = true;
1996
+ }
1997
+ if(!p.date.isValid()) return false;
1998
+ if(p.set && p.set.specificity) {
1999
+ arrayEach(DateUnits, function(u, i) {
2000
+ if(u.unit === p.set.specificity) {
2001
+ accuracy = u.multiplier(p.date, d - p.date) - 1;
2002
+ }
2003
+ });
2004
+ if(p.set['edge'] || p.set['shift']) {
2005
+ p.date['beginningOf' + p.set.specificity.capitalize()]();
2006
+ }
2007
+ if(!override && p.set['sign'] && p.set.specificity != 'millisecond') {
2008
+ // If the time is relative, there can occasionally be an disparity between the relative date
2009
+ // and "now", which it is being compared to, so set an extra buffer to account for this.
2010
+ loBuffer = 50;
2011
+ hiBuffer = -50;
2012
+ }
2013
+ }
2014
+ var t = d.getTime();
2015
+ var min = p.date.getTime();
2016
+ var max = min + accuracy;
2017
+ if(p.set && p.set.specificity == 'week' && new Date(max + 1).getHours() != 0) {
2018
+ max -= date['DSTOffset'];
2019
+ }
2020
+ return t >= (min - loBuffer) && t <= (max + hiBuffer);
2021
+ }
2022
+
2023
+ function updateDate(date, params, reset, utc, advance) {
2024
+ if(object.isNumber(params) && advance) {
2025
+ // If param is a number and we're advancing, the number is presumed to be milliseconds.
2026
+ params = { 'milliseconds': params };
2027
+ } else if(object.isNumber(params)) {
2028
+ // Otherwise just set the timestamp and return.
2029
+ date.setTime(params);
2030
+ return date;
2031
+ }
2032
+
2033
+ // "date" can also be passed for the day
2034
+ if(params['date']) params['day'] = params['date'];
2035
+ // If a weekday is included in the params, set it ahead of time and set the params
2036
+ // to reflect the updated date so that resetting works properly.
2037
+ if(!advance && isUndefined(params['day']) && isDefined(params['weekday'])) {
2038
+ callDateMethod(date, 'set', utc, 'Weekday', params['weekday'])
2039
+ params['day'] = callDateMethod(date, 'get', utc, 'Date');
2040
+ delete params['weekday'];
2041
+ }
2042
+ // Reset any unit lower than the least specific unit set. Do not do this for weeks
2043
+ // or for years. This needs to be performed before the acutal setting of the date
2044
+ // because the order needs to be reversed in order to get the lowest specificity.
2045
+ // The order of the date setting is also fixed because higher order units can be
2046
+ // overwritten by lower order units, such as setting hour: 3, minute: 345, etc.
2047
+ arrayEach(DateUnitsReversed, function(u) {
2048
+ if(isDefined(params[u.unit]) || isDefined(params[u.unit + 's'])) {
2049
+ params.specificity = u.unit;
2050
+ return false;
2051
+ } else if(reset && u.unit !== 'week' && u.unit !== 'year') {
2052
+ callDateMethod(date, 'set', utc, u.method, (u.unit === 'day') ? 1 : 0);
2053
+ }
2054
+ });
2055
+ // Now actually set or advance the date in order, higher units first.
2056
+ arrayEach(DateUnits, function(u,i) {
2057
+ var unit = u.unit;
2058
+ var method = u.method;
2059
+ var value = isDefined(params[unit]) ? params[unit] : params[unit + 's'];
2060
+ if(isUndefined(value)) return;
2061
+ if(advance) {
2062
+ if(unit === 'week') {
2063
+ value = (params['day'] || 0) + (value * 7);
2064
+ method = 'Date';
2065
+ }
2066
+ value = (value * advance) + callDateMethod(date, 'get', '', method);
2067
+ }
2068
+ callDateMethod(date, 'set', utc, method, value);
2069
+ if(unit === 'month') {
2070
+ checkMonthTraversal(date, value);
2071
+ }
2072
+ });
2073
+ return date;
2074
+ }
2075
+
2076
+ function callDateMethod(d, prefix, utc, method, value) {
2077
+ return d[prefix + (utc ? 'UTC' : '') + method](value);
2078
+ }
2079
+
2080
+ // If the year is two digits, add the most appropriate century prefix.
2081
+ function getYearFromAbbreviation(year) {
2082
+ return round(new date().getFullYear() / 100) * 100 - round(year / 100) * 100 + year;
2083
+ }
2084
+
2085
+ function getShortHour(d, utc) {
2086
+ var hours = callDateMethod(d, 'get', utc, 'Hours');
2087
+ return hours === 0 ? 12 : hours - ((hours / 13 | 0) * 12);
2088
+ }
2089
+
2090
+ function getMeridian(d, utc) {
2091
+ var hours = callDateMethod(d, 'get', utc, 'Hours');
2092
+ return hours < 12 ? 'am' : 'pm';
2093
+ }
2094
+
2095
+ // weeksSince won't work here as the result needs to be floored, not rounded.
2096
+ function getWeekNumber(date) {
2097
+ var dow = date.getDay() || 7;
2098
+ date.addDays(4 - dow).resetTime();
2099
+ return 1 + (date.daysSince(date.clone().beginningOfYear()) / 7 | 0);
2100
+ }
2101
+
2102
+ function getAdjustedUnit(ms) {
2103
+ var next, ams = Math.abs(ms), value = ams, unit = 0;
2104
+ arrayEach(DateUnitsReversed.slice(1), function(u, i) {
2105
+ next = round(ams / u.multiplier(), 1) | 0;
2106
+ if(next >= 1) {
2107
+ value = next;
2108
+ unit = i + 1;
2109
+ }
2110
+ });
2111
+ return [value, unit, ms];
2112
+ }
2113
+
2114
+
2115
+ // If the month is being set, then we don't want to accidentally
2116
+ // traverse into a new month just because the target month doesn't have enough
2117
+ // days. In other words, "5 months ago" from July 30th is still February, even
2118
+ // though there is no February 30th, so it will of necessity be February 28th
2119
+ // (or 29th in the case of a leap year).
2120
+
2121
+ function checkMonthTraversal(date, targetMonth) {
2122
+ if(targetMonth < 0) targetMonth += 12;
2123
+ if(targetMonth % 12 != date.getMonth()) {
2124
+ date.setDate(0);
2125
+ }
2126
+ }
2127
+
2128
+ function createDate(args) {
2129
+ var f;
2130
+ if(object.isNumber(args[1])) {
2131
+ // If the second argument is a number, then we have an enumerated constructor type as in "new Date(2003, 2, 12);"
2132
+ f = collectDateArguments(args)[0];
2133
+ } else {
2134
+ f = args[0];
2135
+ }
2136
+ return getExtendedDate(f, args[1]).date;
2137
+ }
2138
+
2139
+
2140
+
2141
+ /***
2142
+ * @method [units]Since([d], [locale] = currentLocale)
2143
+ * @returns Number
2144
+ * @short Returns the time since [d] in the appropriate unit.
2145
+ * @extra [d] will accept a date object, timestamp, or text format. If not specified, [d] is assumed to be now. [locale] can be passed to specify the locale that the date is in. For more see @date_format.
2146
+ * @example
2147
+ *
2148
+ * Date.create().millisecondsSince('1 hour ago') -> 3,600,000
2149
+ * Date.create().daysSince('1 week ago') -> 7
2150
+ * Date.create().yearsSince('15 years ago') -> 15
2151
+ * Date.create('15 years ago').yearsAgo() -> 15
2152
+ *
2153
+ ***
2154
+ * @method millisecondsSince()
2155
+ * @set unitsSince
2156
+ ***
2157
+ * @method secondsSince()
2158
+ * @set unitsSince
2159
+ ***
2160
+ * @method minutesSince()
2161
+ * @set unitsSince
2162
+ ***
2163
+ * @method hoursSince()
2164
+ * @set unitsSince
2165
+ ***
2166
+ * @method daysSince()
2167
+ * @set unitsSince
2168
+ ***
2169
+ * @method weeksSince()
2170
+ * @set unitsSince
2171
+ ***
2172
+ * @method monthsSince()
2173
+ * @set unitsSince
2174
+ ***
2175
+ * @method yearsSince()
2176
+ * @set unitsSince
2177
+ ***
2178
+ * @method [units]Ago()
2179
+ * @returns Number
2180
+ * @short Returns the time ago in the appropriate unit.
2181
+ * @example
2182
+ *
2183
+ * Date.create('last year').millisecondsAgo() -> 3,600,000
2184
+ * Date.create('last year').daysAgo() -> 7
2185
+ * Date.create('last year').yearsAgo() -> 15
2186
+ *
2187
+ ***
2188
+ * @method millisecondsAgo()
2189
+ * @set unitsAgo
2190
+ ***
2191
+ * @method secondsAgo()
2192
+ * @set unitsAgo
2193
+ ***
2194
+ * @method minutesAgo()
2195
+ * @set unitsAgo
2196
+ ***
2197
+ * @method hoursAgo()
2198
+ * @set unitsAgo
2199
+ ***
2200
+ * @method daysAgo()
2201
+ * @set unitsAgo
2202
+ ***
2203
+ * @method weeksAgo()
2204
+ * @set unitsAgo
2205
+ ***
2206
+ * @method monthsAgo()
2207
+ * @set unitsAgo
2208
+ ***
2209
+ * @method yearsAgo()
2210
+ * @set unitsAgo
2211
+ ***
2212
+ * @method [units]Until([d], [locale] = currentLocale)
2213
+ * @returns Number
2214
+ * @short Returns the time until [d] in the appropriate unit.
2215
+ * @extra [d] will accept a date object, timestamp, or text format. If not specified, [d] is assumed to be now. [locale] can be passed to specify the locale that the date is in. %[unit]FromNow% is provided as an alias to make this more readable. For more see @date_format.
2216
+ * @example
2217
+ *
2218
+ * Date.create().millisecondsUntil('1 hour from now') -> 3,600,000
2219
+ * Date.create().daysUntil('1 week from now') -> 7
2220
+ * Date.create().yearsUntil('15 years from now') -> 15
2221
+ * Date.create('15 years from now').yearsFromNow() -> 15
2222
+ *
2223
+ ***
2224
+ * @method millisecondsUntil()
2225
+ * @set unitsUntil
2226
+ ***
2227
+ * @method secondsUntil()
2228
+ * @set unitsUntil
2229
+ ***
2230
+ * @method minutesUntil()
2231
+ * @set unitsUntil
2232
+ ***
2233
+ * @method hoursUntil()
2234
+ * @set unitsUntil
2235
+ ***
2236
+ * @method daysUntil()
2237
+ * @set unitsUntil
2238
+ ***
2239
+ * @method weeksUntil()
2240
+ * @set unitsUntil
2241
+ ***
2242
+ * @method monthsUntil()
2243
+ * @set unitsUntil
2244
+ ***
2245
+ * @method yearsUntil()
2246
+ * @set unitsUntil
2247
+ ***
2248
+ * @method [units]FromNow()
2249
+ * @returns Number
2250
+ * @short Returns the time from now in the appropriate unit.
2251
+ * @example
2252
+ *
2253
+ * Date.create('next year').millisecondsFromNow() -> 3,600,000
2254
+ * Date.create('next year').daysFromNow() -> 7
2255
+ * Date.create('next year').yearsFromNow() -> 15
2256
+ *
2257
+ ***
2258
+ * @method millisecondsFromNow()
2259
+ * @set unitsFromNow
2260
+ ***
2261
+ * @method secondsFromNow()
2262
+ * @set unitsFromNow
2263
+ ***
2264
+ * @method minutesFromNow()
2265
+ * @set unitsFromNow
2266
+ ***
2267
+ * @method hoursFromNow()
2268
+ * @set unitsFromNow
2269
+ ***
2270
+ * @method daysFromNow()
2271
+ * @set unitsFromNow
2272
+ ***
2273
+ * @method weeksFromNow()
2274
+ * @set unitsFromNow
2275
+ ***
2276
+ * @method monthsFromNow()
2277
+ * @set unitsFromNow
2278
+ ***
2279
+ * @method yearsFromNow()
2280
+ * @set unitsFromNow
2281
+ ***
2282
+ * @method add[Units](<num>)
2283
+ * @returns Date
2284
+ * @short Adds <num> of the unit to the date.
2285
+ * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Don't use this method if you need precision.
2286
+ * @example
2287
+ *
2288
+ * Date.create().addMilliseconds(5) -> current time + 5 milliseconds
2289
+ * Date.create().addDays(5) -> current time + 5 days
2290
+ * Date.create().addYears(5) -> current time + 5 years
2291
+ *
2292
+ ***
2293
+ * @method addMilliseconds()
2294
+ * @set addUnits
2295
+ ***
2296
+ * @method addSeconds()
2297
+ * @set addUnits
2298
+ ***
2299
+ * @method addMinutes()
2300
+ * @set addUnits
2301
+ ***
2302
+ * @method addHours()
2303
+ * @set addUnits
2304
+ ***
2305
+ * @method addDays()
2306
+ * @set addUnits
2307
+ ***
2308
+ * @method addWeeks()
2309
+ * @set addUnits
2310
+ ***
2311
+ * @method addMonths()
2312
+ * @set addUnits
2313
+ ***
2314
+ * @method addYears()
2315
+ * @set addUnits
2316
+ ***
2317
+ * @method isLast[Unit]()
2318
+ * @returns Boolean
2319
+ * @short Returns true if the date is last week/month/year.
2320
+ * @example
2321
+ *
2322
+ * Date.create('yesterday').isLastWeek() -> true or false?
2323
+ * Date.create('yesterday').isLastMonth() -> probably not...
2324
+ * Date.create('yesterday').isLastYear() -> even less likely...
2325
+ *
2326
+ ***
2327
+ * @method isThis[Unit]()
2328
+ * @returns Boolean
2329
+ * @short Returns true if the date is this week/month/year.
2330
+ * @example
2331
+ *
2332
+ * Date.create('tomorrow').isThisWeek() -> true or false?
2333
+ * Date.create('tomorrow').isThisMonth() -> probably...
2334
+ * Date.create('tomorrow').isThisYear() -> signs point to yes...
2335
+ *
2336
+ ***
2337
+ * @method isNext[Unit]()
2338
+ * @returns Boolean
2339
+ * @short Returns true if the date is next week/month/year.
2340
+ * @example
2341
+ *
2342
+ * Date.create('tomorrow').isNextWeek() -> true or false?
2343
+ * Date.create('tomorrow').isNextMonth() -> probably not...
2344
+ * Date.create('tomorrow').isNextYear() -> even less likely...
2345
+ *
2346
+ ***
2347
+ * @method isLastWeek()
2348
+ * @set isLastUnit
2349
+ ***
2350
+ * @method isLastMonth()
2351
+ * @set isLastUnit
2352
+ ***
2353
+ * @method isLastYear()
2354
+ * @set isLastUnit
2355
+ ***
2356
+ * @method isThisWeek()
2357
+ * @set isThisUnit
2358
+ ***
2359
+ * @method isThisMonth()
2360
+ * @set isThisUnit
2361
+ ***
2362
+ * @method isThisYear()
2363
+ * @set isThisUnit
2364
+ ***
2365
+ * @method isNextWeek()
2366
+ * @set isNextUnit
2367
+ ***
2368
+ * @method isNextMonth()
2369
+ * @set isNextUnit
2370
+ ***
2371
+ * @method isNextYear()
2372
+ * @set isNextUnit
2373
+ ***
2374
+ * @method beginningOf[Unit]()
2375
+ * @returns Date
2376
+ * @short Sets the date to the beginning of the appropriate unit.
2377
+ * @example
2378
+ *
2379
+ * Date.create().beginningOfDay() -> the beginning of today (resets the time)
2380
+ * Date.create().beginningOfWeek() -> the beginning of the week
2381
+ * Date.create().beginningOfMonth() -> the beginning of the month
2382
+ * Date.create().beginningOfYear() -> the beginning of the year
2383
+ *
2384
+ ***
2385
+ * @method endOf[Unit]()
2386
+ * @returns Date
2387
+ * @short Sets the date to the end of the appropriate unit.
2388
+ * @example
2389
+ *
2390
+ * Date.create().endOfDay() -> the end of today (sets the time to 23:59:59.999)
2391
+ * Date.create().endOfWeek() -> the end of the week
2392
+ * Date.create().endOfMonth() -> the end of the month
2393
+ * Date.create().endOfYear() -> the end of the year
2394
+ *
2395
+ ***
2396
+ * @method beginningOfDay()
2397
+ * @set beginningOfUnit
2398
+ ***
2399
+ * @method beginningOfWeek()
2400
+ * @set beginningOfUnit
2401
+ ***
2402
+ * @method beginningOfMonth()
2403
+ * @set beginningOfUnit
2404
+ ***
2405
+ * @method beginningOfYear()
2406
+ * @set beginningOfUnit
2407
+ ***
2408
+ * @method endOfDay()
2409
+ * @set endOfUnit
2410
+ ***
2411
+ * @method endOfWeek()
2412
+ * @set endOfUnit
2413
+ ***
2414
+ * @method endOfMonth()
2415
+ * @set endOfUnit
2416
+ ***
2417
+ * @method endOfYear()
2418
+ * @set endOfUnit
2419
+ ***/
2420
+ function buildDateMethods() {
2421
+ arrayEach(DateUnits, function(u, i) {
2422
+ var unit = u.unit;
2423
+ var caps = unit.capitalize();
2424
+ var multiplier = u.multiplier();
2425
+ defineProperty(date.prototype, unit+'sSince', function(f, code) {
2426
+ return round((this.getTime() - date.create(f, code).getTime()) / multiplier);
2427
+ });
2428
+ defineProperty(date.prototype, unit+'sUntil', function(f, code) {
2429
+ return round((date.create(f, code).getTime() - this.getTime()) / multiplier);
2430
+ });
2431
+ defineProperty(date.prototype, unit+'sAgo', date.prototype[unit+'sUntil']);
2432
+ defineProperty(date.prototype, unit+'sFromNow', date.prototype[unit+'sSince']);
2433
+ defineProperty(date.prototype, 'add'+caps+'s', function(num) {
2434
+ var set = {};
2435
+ set[unit] = num;
2436
+ return this.advance(set);
2437
+ });
2438
+ buildNumberToDateAlias(unit, multiplier);
2439
+ if(i < 3) {
2440
+ arrayEach(['Last','This','Next'], function(shift) {
2441
+ defineProperty(date.prototype, 'is' + shift + caps, function() {
2442
+ return this.is(shift + ' ' + unit);
2443
+ });
2444
+ });
2445
+ }
2446
+ if(i < 4) {
2447
+ defineProperty(date.prototype, 'beginningOf' + caps, function() {
2448
+ var set = {};
2449
+ switch(unit) {
2450
+ case 'year': set['year'] = this.getFullYear(); break;
2451
+ case 'month': set['month'] = this.getMonth(); break;
2452
+ case 'day': set['day'] = this.getDate(); break;
2453
+ case 'week': set['weekday'] = 0; break;
2454
+ }
2455
+ return this.set(set, true);
2456
+ });
2457
+ defineProperty(date.prototype, 'endOf' + caps, function() {
2458
+ var set = { 'hours': 23, 'minutes': 59, 'seconds': 59, 'milliseconds': 999 };
2459
+ switch(unit) {
2460
+ case 'year': set['month'] = 11; set['day'] = 31; break;
2461
+ case 'month': set['day'] = this.daysInMonth(); break;
2462
+ case 'week': set['weekday'] = 6; break;
2463
+ }
2464
+ return this.set(set, true);
2465
+ });
2466
+ }
2467
+ });
2468
+ }
2469
+
2470
+ function buildDateInputFormats() {
2471
+ DateArgumentUnits = DateUnits.concat();
2472
+ DateArgumentUnits.splice(2, 1);
2473
+ DateUnitsReversed = DateUnits.concat().reverse();
2474
+ var monthReg = '\\d{1,2}|' + English['months'].join('|');
2475
+ arrayEach(StaticInputFormats, function(f) {
2476
+ date.addFormat(f.src.replace(/\{month\}/, monthReg) + (f.time === false ? '' : OptionalTime), f.to.concat(TimeFormat), 'en', f.variant);
2477
+ });
2478
+ addDateInputFormat(RequiredTime, TimeFormat);
2479
+ }
2480
+
2481
+ /***
2482
+ * @method is[Day]()
2483
+ * @returns Boolean
2484
+ * @short Returns true if the date falls on that day.
2485
+ * @extra Also available: %isYesterday%, %isToday%, %isTomorrow%, %isWeekday%, and %isWeekend%.
2486
+ * @example
2487
+ *
2488
+ * Date.create('tomorrow').isToday() -> false
2489
+ * Date.create('thursday').isTomorrow() -> ?
2490
+ * Date.create('yesterday').isWednesday() -> ?
2491
+ * Date.create('today').isWeekend() -> ?
2492
+ *
2493
+ ***
2494
+ * @method isToday()
2495
+ * @set isDay
2496
+ ***
2497
+ * @method isYesterday()
2498
+ * @set isDay
2499
+ ***
2500
+ * @method isTomorrow()
2501
+ * @set isDay
2502
+ ***
2503
+ * @method isWeekday()
2504
+ * @set isDay
2505
+ ***
2506
+ * @method isWeekend()
2507
+ * @set isDay
2508
+ ***
2509
+ * @method isSunday()
2510
+ * @set isDay
2511
+ ***
2512
+ * @method isMonday()
2513
+ * @set isDay
2514
+ ***
2515
+ * @method isTuesday()
2516
+ * @set isDay
2517
+ ***
2518
+ * @method isWednesday()
2519
+ * @set isDay
2520
+ ***
2521
+ * @method isThursday()
2522
+ * @set isDay
2523
+ ***
2524
+ * @method isFriday()
2525
+ * @set isDay
2526
+ ***
2527
+ * @method isSaturday()
2528
+ * @set isDay
2529
+ ***
2530
+ * @method isFuture()
2531
+ * @returns Boolean
2532
+ * @short Returns true if the date is in the future.
2533
+ * @example
2534
+ *
2535
+ * Date.create('next week').isFuture() -> true
2536
+ * Date.create('last week').isFuture() -> false
2537
+ *
2538
+ ***
2539
+ * @method isPast()
2540
+ * @returns Boolean
2541
+ * @short Returns true if the date is in the past.
2542
+ * @example
2543
+ *
2544
+ * Date.create('last week').isPast() -> true
2545
+ * Date.create('next week').isPast() -> false
2546
+ *
2547
+ ***/
2548
+ function buildRelativeAliases() {
2549
+ var weekdays = English['weekdays'].slice(0,7);
2550
+ var months = English['months'].slice(0,12);
2551
+ arrayEach(['today','yesterday','tomorrow','weekday','weekend','future','past'].concat(weekdays).concat(months), function(s) {
2552
+ defineProperty(date.prototype, 'is'+ s.capitalize(), function() {
2553
+ return this.is(s);
2554
+ });
2555
+ });
2556
+ }
2557
+
2558
+ function setDateProperties() {
2559
+ extend(date, false, true, {
2560
+ 'DSTOffset': (new date(2000, 6, 1).getTimezoneOffset() - new date(2000, 0, 1).getTimezoneOffset()) * 60 * 1000,
2561
+ 'INTERNATIONAL_TIME': '{h}:{mm}:{ss}',
2562
+ 'RFC1123': '{Dow}, {dd} {Mon} {yyyy} {HH}:{mm}:{ss} {tz}',
2563
+ 'RFC1036': '{Weekday}, {dd}-{Mon}-{yy} {HH}:{mm}:{ss} {tz}',
2564
+ 'ISO8601_DATE': '{yyyy}-{MM}-{dd}',
2565
+ 'ISO8601_DATETIME': '{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{fff}{isotz}'
2566
+ });
2567
+ }
2568
+
2569
+
2570
+ /***
2571
+ * @method toISOString()
2572
+ * @returns String
2573
+ * @short Formats the string to ISO8601 format.
2574
+ * @extra This will always format as UTC time. Provided for browsers that do not support this method.
2575
+ * @example
2576
+ *
2577
+ * Date.create().toISOString() -> ex. 2011-07-05 12:24:55.528Z
2578
+ *
2579
+ ***
2580
+ * @method toJSON()
2581
+ * @returns String
2582
+ * @short Returns a JSON representation of the date.
2583
+ * @extra This is effectively an alias for %toISOString%. Will always return the date in UTC time. Implemented for browsers that do not support it.
2584
+ * @example
2585
+ *
2586
+ * Date.create().toJSON() -> ex. 2011-07-05 12:24:55.528Z
2587
+ *
2588
+ ***/
2589
+
2590
+ function buildISOString(name) {
2591
+ var d = new date(date.UTC(1999, 11, 31)), target = '1999-12-31T00:00:00.000Z', methods = {};
2592
+ if(!d[name] || d[name]() !== target) {
2593
+ methods[name] = function() { return formatDate(this.toUTC(), date['ISO8601_DATETIME']); }
2594
+ extend(date, true, true, methods);
2595
+ }
2596
+ }
2597
+
2598
+
2599
+ function buildDate() {
2600
+ English = date.setLocale('en');
2601
+ buildDateMethods();
2602
+ buildDateInputFormats();
2603
+ buildRelativeAliases();
2604
+ buildISOString('toISOString');
2605
+ buildISOString('toJSON');
2606
+ setDateProperties();
2607
+ }
2608
+
2609
+ extend(date, false, false, {
2610
+
2611
+ /***
2612
+ * @method Date.create(<d>, [locale] = currentLocale)
2613
+ * @returns Date
2614
+ * @short Alternate Date constructor which understands various formats.
2615
+ * @extra Accepts a multitude of text formats, a timestamp, or another date. If no argument is given, date is assumed to be now. %Date.create% additionally can accept enumerated parameters as with the standard date constructor. [locale] can be passed to specify the locale that the date is in. For more information, see @date_format.
2616
+ * @example
2617
+ *
2618
+ * Date.create('July') -> July of this year
2619
+ * Date.create('1776') -> 1776
2620
+ * Date.create('today') -> today
2621
+ * Date.create('wednesday') -> This wednesday
2622
+ * Date.create('next friday') -> Next friday
2623
+ * Date.create('July 4, 1776') -> July 4, 1776
2624
+ * Date.create(-446806800000) -> November 5, 1955
2625
+ * Date.create(1776, 6, 4) -> July 4, 1776
2626
+ * Date.create('1776年07月04日', 'ja') -> July 4, 1776
2627
+ *
2628
+ ***/
2629
+ 'create': function() {
2630
+ return createDate(arguments);
2631
+ },
2632
+
2633
+ /***
2634
+ * @method Date.now()
2635
+ * @returns String
2636
+ * @short Returns the number of milliseconds since January 1st, 1970 00:00:00 (UTC time).
2637
+ * @example
2638
+ *
2639
+ * Date.now() -> ex. 1311938296231
2640
+ *
2641
+ ***/
2642
+ 'now': function() {
2643
+ return new date().getTime();
2644
+ },
2645
+
2646
+ /***
2647
+ * @method Date.setLocale(<code>, [set])
2648
+ * @returns String
2649
+ * @short Sets the current locale to be used with dates.
2650
+ * @extra Predefined locales are: English (en), French (fr), Italian (it), Spanish (es), Portuguese (pt), German (de), Russian (ru), Japanese (ja), Korean (ko), Simplified Chinese (zh-CN), and Traditional Chinese (zh-TW). In addition to available major locales, you can define a new local here by passing an object for [set]. For more see @date_format.
2651
+ *
2652
+ ***/
2653
+ 'setLocale': function(code, set) {
2654
+ var loc = getLocalization(code, false, set);
2655
+ if(loc) {
2656
+ Date['currentLocale'] = code;
2657
+ checkLocaleFormatsAdded(loc);
2658
+ return loc;
2659
+ }
2660
+ },
2661
+
2662
+ /*
2663
+ * Hiding this from the API docs for now...
2664
+ * @method Date.getLocale([code] = currentLocale)
2665
+ * @returns String
2666
+ * @short Gets a localization object for [code], or the current locale.
2667
+ * @extra Manipulating the localization can give you more control over date localizations. For more see @date_format.
2668
+ *
2669
+ */
2670
+ 'getLocale': function(code) {
2671
+ return getLocalization(code, true);
2672
+ },
2673
+
2674
+ /* Let's not document this one for now... */
2675
+ 'addFormat': function(format, match, locale, variant) {
2676
+ addDateInputFormat(format, match, locale, variant, 'unshift');
2677
+ }
2678
+
2679
+ });
2680
+
2681
+ extend(date, true, false, {
2682
+
2683
+ /***
2684
+ * @method set(<set>, [reset] = false)
2685
+ * @returns Date
2686
+ * @short Sets the date object.
2687
+ * @extra This method can accept multiple formats including a single number as a timestamp, an object, or enumerated parameters (as with the Date constructor). If [reset] is %true%, any units more specific than those passed will be reset. %setUTC% will set the date according to universal time.
2688
+ * @example
2689
+ *
2690
+ * new Date().set({ year: 2011, month: 11, day: 31 }) -> December 31, 2011
2691
+ * new Date().set(2011, 11, 31) -> December 31, 2011
2692
+ * new Date().set(86400000) -> 1 day after Jan 1, 1970
2693
+ * new Date().set({ year: 2004, month: 6 }, true) -> June 1, 2004, 00:00:00.000
2694
+ *
2695
+ ***/
2696
+ 'set': function() {
2697
+ var args = collectDateArguments(arguments);
2698
+ return updateDate(this, args[0], args[1])
2699
+ },
2700
+
2701
+ /***
2702
+ * @method setUTC()
2703
+ * @set set
2704
+ ***/
2705
+ 'setUTC': function() {
2706
+ var args = collectDateArguments(arguments);
2707
+ return updateDate(this, args[0], args[1], true)
2708
+ },
2709
+
2710
+ /***
2711
+ * @method setWeekday()
2712
+ * @returns Nothing
2713
+ * @short Sets the weekday of the date.
2714
+ * @extra %setUTCWeekday% sets according to universal time.
2715
+ * @example
2716
+ *
2717
+ * d = new Date(); d.setWeekday(1); d; -> Monday of this week
2718
+ * d = new Date(); d.setWeekday(6); d; -> Saturday of this week
2719
+ *
2720
+ ***/
2721
+ 'setWeekday': function(dow) {
2722
+ if(isUndefined(dow)) return;
2723
+ this.setDate(this.getDate() + dow - this.getDay());
2724
+ },
2725
+
2726
+ /***
2727
+ * @method setUTCWeekday()
2728
+ * @set setWeekday
2729
+ ***/
2730
+ 'setUTCWeekday': function(dow) {
2731
+ if(isUndefined(dow)) return;
2732
+ this.setDate(this.getUTCDate() + dow - this.getDay());
2733
+ },
2734
+
2735
+ /***
2736
+ * @method setWeek()
2737
+ * @returns Nothing
2738
+ * @short Sets the week (of the year).
2739
+ * @extra %setUTCWeek% sets according to universal time.
2740
+ * @example
2741
+ *
2742
+ * d = new Date(); d.setWeek(15); d; -> 15th week of the year
2743
+ *
2744
+ ***/
2745
+ 'setWeek': function(week) {
2746
+ if(isUndefined(week)) return;
2747
+ var date = this.getDate();
2748
+ this.setMonth(0);
2749
+ this.setDate((week * 7) + 1);
2750
+ },
2751
+
2752
+ /***
2753
+ * @method setUTCWeek()
2754
+ * @set setWeek
2755
+ ***/
2756
+ 'setUTCWeek': function(week) {
2757
+ if(isUndefined(week)) return;
2758
+ var date = this.getUTCDate();
2759
+ this.setMonth(0);
2760
+ this.setUTCDate((week * 7) + 1);
2761
+ },
2762
+
2763
+ /***
2764
+ * @method getWeek()
2765
+ * @returns Number
2766
+ * @short Gets the date's week (of the year).
2767
+ * @extra %getUTCWeek% gets the time according to universal time.
2768
+ * @example
2769
+ *
2770
+ * new Date().getWeek() -> today's week of the year
2771
+ *
2772
+ ***/
2773
+ 'getWeek': function() {
2774
+ return getWeekNumber(this);
2775
+ },
2776
+
2777
+ /***
2778
+ * @method getUTCWeek()
2779
+ * @set getWeek
2780
+ ***/
2781
+ 'getUTCWeek': function() {
2782
+ return getWeekNumber(this.toUTC());
2783
+ },
2784
+
2785
+ /***
2786
+ * @method getUTCOffset([iso])
2787
+ * @returns String
2788
+ * @short Returns a string representation of the offset from UTC time. If [iso] is true the offset will be in ISO8601 format.
2789
+ * @example
2790
+ *
2791
+ * new Date().getUTCOffset() -> "+0900"
2792
+ * new Date().getUTCOffset(true) -> "+09:00"
2793
+ *
2794
+ ***/
2795
+ 'getUTCOffset': function(iso) {
2796
+ var offset = this.utc ? 0 : this.getTimezoneOffset();
2797
+ var colon = iso === true ? ':' : '';
2798
+ if(!offset && iso) return 'Z';
2799
+ return round(-offset / 60).pad(2, true) + colon + (offset % 60).pad(2);
2800
+ },
2801
+
2802
+ /***
2803
+ * @method toUTC()
2804
+ * @returns Date
2805
+ * @short Converts the date to UTC time, effectively subtracting the timezone offset.
2806
+ * @extra Note here that the method %getTimezoneOffset% will still show an offset even after this method is called, as this method effectively just rewinds the date. %format% however, will correctly set the %{tz}% (timezone) token as UTC once this method has been called on the date. Once a date is set to UTC the only way to unset is the %clone% method.
2807
+ * @example
2808
+ *
2809
+ * new Date().toUTC() -> current time in UTC
2810
+ *
2811
+ ***/
2812
+ 'toUTC': function() {
2813
+ if(this.utc) return this;
2814
+ var d = this.clone().addMinutes(this.getTimezoneOffset());
2815
+ d.utc = true;
2816
+ return d;
2817
+ },
2818
+
2819
+ /***
2820
+ * @method isUTC()
2821
+ * @returns Boolean
2822
+ * @short Returns true if the date has no timezone offset.
2823
+ * @example
2824
+ *
2825
+ * new Date().isUTC() -> true or false?
2826
+ *
2827
+ ***/
2828
+ 'isUTC': function() {
2829
+ return this.utc || this.getTimezoneOffset() === 0;
2830
+ },
2831
+
2832
+ /***
2833
+ * @method advance()
2834
+ * @returns Date
2835
+ * @short Sets the date forward.
2836
+ * @extra This method can accept multiple formats including a single number as a timestamp, an object, or enumerated parameters (as with the Date constructor). For more see @date_format.
2837
+ * @example
2838
+ *
2839
+ * new Date().advance({ year: 2 }) -> 2 years in the future
2840
+ * new Date().advance(0, 2, 3) -> 2 months 3 days in the future
2841
+ * new Date().advance(86400000) -> 1 day in the future
2842
+ *
2843
+ ***/
2844
+ 'advance': function(params) {
2845
+ var args = collectDateArguments(arguments);
2846
+ return updateDate(this, args[0], false, false, 1, true);
2847
+ },
2848
+
2849
+ /***
2850
+ * @method rewind()
2851
+ * @returns Date
2852
+ * @short Sets the date back.
2853
+ * @extra This method can accept multiple formats including a single number as a timestamp, an object, or enumerated parameters (as with the Date constructor). For more see @date_format.
2854
+ * @example
2855
+ *
2856
+ * new Date().rewind({ year: 2 }) -> 2 years in the past
2857
+ * new Date().rewind(0, 2, 3) -> 2 months 3 days in the past
2858
+ * new Date().rewind(86400000) -> 1 day in the past
2859
+ *
2860
+ ***/
2861
+ 'rewind': function(params) {
2862
+ var args = collectDateArguments(arguments);
2863
+ return updateDate(this, args[0], false, false, -1);
2864
+ },
2865
+
2866
+ /***
2867
+ * @method isValid()
2868
+ * @returns Boolean
2869
+ * @short Returns true if the date is valid.
2870
+ * @example
2871
+ *
2872
+ * new Date().isValid() -> true
2873
+ * new Date('flexor').isValid() -> false
2874
+ *
2875
+ ***/
2876
+ 'isValid': function() {
2877
+ return !isNaN(this.getTime());
2878
+ },
2879
+
2880
+ /***
2881
+ * @method isAfter(<d>, [margin])
2882
+ * @returns Boolean
2883
+ * @short Returns true if the date is after the <d>.
2884
+ * @extra [margin] is to allow extra margin of error (in ms). <d> will accept a date object, timestamp, or text format. If not specified, <d> is assumed to be now. See @date_format for more information.
2885
+ * @example
2886
+ *
2887
+ * new Date().isAfter('tomorrow') -> false
2888
+ * new Date().isAfter('yesterday') -> true
2889
+ *
2890
+ ***/
2891
+ 'isAfter': function(d, margin) {
2892
+ return this.getTime() > date.create(d).getTime() - (margin || 0);
2893
+ },
2894
+
2895
+ /***
2896
+ * @method isBefore(<d>, [margin])
2897
+ * @returns Boolean
2898
+ * @short Returns true if the date is before <d>.
2899
+ * @extra [margin] is to allow extra margin of error (in ms). <d> will accept a date object, timestamp, or text format. If not specified, <d> is assumed to be now. See @date_format for more information.
2900
+ * @example
2901
+ *
2902
+ * new Date().isBefore('tomorrow') -> true
2903
+ * new Date().isBefore('yesterday') -> false
2904
+ *
2905
+ ***/
2906
+ 'isBefore': function(d, margin) {
2907
+ return this.getTime() < date.create(d).getTime() + (margin || 0);
2908
+ },
2909
+
2910
+ /***
2911
+ * @method isBetween(<d1>, <d2>, [buffer] = 0)
2912
+ * @returns Boolean
2913
+ * @short Returns true if the date falls between <d1> and <d2>.
2914
+ * @extra [buffer] is to allow extra buffer 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 information.
2915
+ * @example
2916
+ *
2917
+ * new Date().isBetween('yesterday', 'tomorrow') -> true
2918
+ * new Date().isBetween('last year', '2 years ago') -> false
2919
+ *
2920
+ ***/
2921
+ 'isBetween': function(d1, d2, buffer) {
2922
+ var t = this.getTime();
2923
+ var t1 = date.create(d1).getTime();
2924
+ var t2 = date.create(d2).getTime();
2925
+ var lo = Math.min(t1, t2);
2926
+ var hi = Math.max(t1, t2);
2927
+ buffer = buffer || 0;
2928
+ return (lo - buffer < t) && (hi + buffer > t);
2929
+ },
2930
+
2931
+ /***
2932
+ * @method isLeapYear()
2933
+ * @returns Boolean
2934
+ * @short Returns true if the date is a leap year.
2935
+ * @example
2936
+ *
2937
+ * Date.create('2000').isLeapYear() -> true
2938
+ *
2939
+ ***/
2940
+ 'isLeapYear': function() {
2941
+ var year = this.getFullYear();
2942
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
2943
+ },
2944
+
2945
+ /***
2946
+ * @method daysInMonth()
2947
+ * @returns Number
2948
+ * @short Returns the number of days in the date's month.
2949
+ * @example
2950
+ *
2951
+ * Date.create('May').daysInMonth() -> 31
2952
+ * Date.create('February, 2000').daysInMonth() -> 29
2953
+ *
2954
+ ***/
2955
+ 'daysInMonth': function() {
2956
+ return 32 - new date(this.getFullYear(), this.getMonth(), 32).getDate();
2957
+ },
2958
+
2959
+ /***
2960
+ * @method format(<format>, [locale] = currentLocale)
2961
+ * @returns String
2962
+ * @short Formats the date.
2963
+ * @extra <format> will accept a number of tokens as well as pre-determined formats. [locale] specifies a locale code to use (if not specified the current locale is used). If <format> is falsy, a default format for the locale is used. A function may also be passed here to allow more granular control. See @date_format for more details.
2964
+ * @example
2965
+ *
2966
+ * Date.create().format() -> ex. July 4, 2003
2967
+ * Date.create().format('{Weekday} {d} {Month}, {yyyy}') -> ex. Monday July 4, 2003
2968
+ * Date.create().format('{hh}:{mm}') -> ex. 15:57
2969
+ * Date.create().format('{12hr}:{mm}{tt}') -> ex. 3:57pm
2970
+ * Date.create().format(Date.ISO8601_DATETIME) -> ex. 2011-07-05 12:24:55.528Z
2971
+ * Date.create('last week').format('', 'ja') -> ex. 先週
2972
+ * Date.create('yesterday').format(function(value,unit,ms,loc) {
2973
+ * // value = 1, unit = 3, ms = -86400000, loc = [current locale object]
2974
+ * }); -> ex. 1 day ago
2975
+ *
2976
+ ***/
2977
+ 'format': function(f, locale) {
2978
+ return formatDate(this, f, false, locale);
2979
+ },
2980
+
2981
+ /***
2982
+ * @method relative([fn], [locale] = currentLocale)
2983
+ * @returns String
2984
+ * @short Returns a relative date string offset to the current time.
2985
+ * @extra [fn] can be passed to provide for more granular control over the resulting string. [fn] is passed 4 arguments: the adjusted value, unit, offset in milliseconds, and a localization object. As an alternate syntax, [locale] can also be passed as the first (and only) parameter. For more information, see @date_format.
2986
+ * @example
2987
+ *
2988
+ * Date.create('90 seconds ago').relative() -> 1 minute ago
2989
+ * Date.create('January').relative() -> ex. 5 months ago
2990
+ * Date.create('January').relative('ja') -> 3ヶ月前
2991
+ * Date.create('120 minutes ago').relative(function(val,unit,ms,loc) {
2992
+ * // value = 2, unit = 3, ms = -7200, loc = [current locale object]
2993
+ * }); -> ex. 5 months ago
2994
+ *
2995
+ ***/
2996
+ 'relative': function(f, locale) {
2997
+ if(object.isString(f)) {
2998
+ locale = f;
2999
+ f = null;
3000
+ }
3001
+ return formatDate(this, f, true, locale);
3002
+ },
3003
+
3004
+ /***
3005
+ * @method is(<d>, [margin])
3006
+ * @returns Boolean
3007
+ * @short Returns true if the date is <d>.
3008
+ * @extra <d> will accept a date object, timestamp, or text format. %is% additionally understands more generalized expressions like month/weekday names, 'today', etc, and compares to the precision implied in <d>. [margin] allows an extra margin of error in milliseconds. For more information, see @date_format.
3009
+ * @example
3010
+ *
3011
+ * Date.create().is('July') -> true or false?
3012
+ * Date.create().is('1776') -> false
3013
+ * Date.create().is('today') -> true
3014
+ * Date.create().is('weekday') -> true or false?
3015
+ * Date.create().is('July 4, 1776') -> false
3016
+ * Date.create().is(-6106093200000) -> false
3017
+ * Date.create().is(new Date(1776, 6, 4)) -> false
3018
+ *
3019
+ ***/
3020
+ 'is': function(d, margin) {
3021
+ var tmp;
3022
+ if(object.isString(d)) {
3023
+ d = d.trim().toLowerCase();
3024
+ switch(true) {
3025
+ case d === 'future': return this.getTime() > new date().getTime();
3026
+ case d === 'past': return this.getTime() < new date().getTime();
3027
+ case d === 'weekday': return this.getDay() > 0 && this.getDay() < 6;
3028
+ case d === 'weekend': return this.getDay() === 0 || this.getDay() === 6;
3029
+ case (tmp = English['weekdays'].indexOf(d) % 7) > -1: return this.getDay() === tmp;
3030
+ case (tmp = English['months'].indexOf(d) % 12) > -1: return this.getMonth() === tmp;
3031
+ }
3032
+ }
3033
+ return compareDate(this, d, margin);
3034
+ },
3035
+
3036
+ /***
3037
+ * @method resetTime()
3038
+ * @returns Date
3039
+ * @short Resets the time in the date to 00:00:00.000.
3040
+ * @example
3041
+ *
3042
+ * Date.create().resetTime() -> Beginning of today
3043
+ *
3044
+ ***/
3045
+ 'resetTime': function() {
3046
+ return this.set({ 'hour': 0, 'minute': 0, 'second': 0, 'millisecond': 0 });
3047
+ },
3048
+
3049
+ /***
3050
+ * @method clone()
3051
+ * @returns Date
3052
+ * @short Clones the date.
3053
+ * @example
3054
+ *
3055
+ * Date.create().clone() -> Copy of now
3056
+ *
3057
+ ***/
3058
+ 'clone': function() {
3059
+ return new date(this.getTime());
3060
+ }
3061
+
3062
+ });
3063
+
3064
+
3065
+ // Instance aliases
3066
+ extend(date, true, false, {
3067
+
3068
+ /***
3069
+ * @method iso()
3070
+ * @alias toISOString
3071
+ *
3072
+ ***/
3073
+ 'iso': function() {
3074
+ return this.toISOString();
3075
+ },
3076
+
3077
+ /***
3078
+ * @method getWeekday()
3079
+ * @alias getDay
3080
+ *
3081
+ ***/
3082
+ 'getWeekday': date.prototype.getDay,
3083
+
3084
+ /***
3085
+ * @method getUTCWeekday()
3086
+ * @alias getUTCDay
3087
+ *
3088
+ ***/
3089
+ 'getUTCWeekday': date.prototype.getUTCDay
3090
+
3091
+ });
3092
+
3093
+
3094
+ extend(number, true, false, {
3095
+
3096
+ /***
3097
+ * @method duration([locale] = currentLocale)
3098
+ * @returns String
3099
+ * @short Takes the number as milliseconds and returns a unit-adjusted localized string.
3100
+ * @extra This method is the same as %Date#relative% without the localized equivalent of "from now" or "ago". [locale] c
3101
+ * @example
3102
+ *
3103
+ * (500).duration() -> '500 milliseconds'
3104
+ * (1200).duration() -> '1 second'
3105
+ * (75).minutes().duration() -> '1 hour'
3106
+ * (75).minutes().duration('es') -> '1 hora'
3107
+ *
3108
+ ***/
3109
+ 'duration': function(code) {
3110
+ return date.getLocale(code).duration(this);
3111
+ }
3112
+
3113
+ });
3114
+
3115
+
3116
+ // Initialize
3117
+ buildObject();
3118
+ buildString();
3119
+ buildDate();
3120
+
3121
+ })();