sugar-rails 1.2.5.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README.md +110 -28
  2. data/lib/generators/sugar/build/build_generator.rb +107 -0
  3. data/lib/generators/sugar/install/install_generator.rb +2 -2
  4. data/lib/sugar/rails/version.rb +2 -2
  5. data/sugar-rails.gemspec +1 -1
  6. data/vendor/assets/javascripts/precompiled/development/array.js +1212 -0
  7. data/vendor/assets/javascripts/precompiled/development/core.js +329 -0
  8. data/vendor/assets/javascripts/precompiled/development/date.js +2179 -0
  9. data/vendor/assets/javascripts/precompiled/development/date_locales.js +952 -0
  10. data/vendor/assets/javascripts/precompiled/development/date_ranges.js +183 -0
  11. data/vendor/assets/javascripts/precompiled/development/es5.js +443 -0
  12. data/vendor/assets/javascripts/precompiled/development/function.js +222 -0
  13. data/vendor/assets/javascripts/{sugar-inflections.js → precompiled/development/inflections.js} +51 -162
  14. data/vendor/assets/javascripts/precompiled/development/language.js +383 -0
  15. data/vendor/assets/javascripts/precompiled/development/number.js +422 -0
  16. data/vendor/assets/javascripts/precompiled/development/object.js +348 -0
  17. data/vendor/assets/javascripts/precompiled/development/regexp.js +92 -0
  18. data/vendor/assets/javascripts/precompiled/development/string.js +871 -0
  19. data/vendor/assets/javascripts/precompiled/minified/array.js +16 -0
  20. data/vendor/assets/javascripts/precompiled/minified/core.js +8 -0
  21. data/vendor/assets/javascripts/precompiled/minified/date.js +40 -0
  22. data/vendor/assets/javascripts/precompiled/minified/date_locales.js +38 -0
  23. data/vendor/assets/javascripts/precompiled/minified/date_ranges.js +3 -0
  24. data/vendor/assets/javascripts/precompiled/minified/es5.js +7 -0
  25. data/vendor/assets/javascripts/precompiled/minified/function.js +4 -0
  26. data/vendor/assets/javascripts/precompiled/minified/inflections.js +11 -0
  27. data/vendor/assets/javascripts/precompiled/minified/language.js +19 -0
  28. data/vendor/assets/javascripts/precompiled/minified/number.js +5 -0
  29. data/vendor/assets/javascripts/precompiled/minified/object.js +6 -0
  30. data/vendor/assets/javascripts/precompiled/minified/regexp.js +2 -0
  31. data/vendor/assets/javascripts/precompiled/minified/string.js +12 -0
  32. data/vendor/assets/javascripts/precompiled/readme.txt +3 -0
  33. data/vendor/assets/javascripts/sugar-development.js +8054 -0
  34. data/vendor/assets/javascripts/sugar-full.js +179 -0
  35. data/vendor/assets/javascripts/sugar.js +111 -6211
  36. metadata +35 -9
  37. data/vendor/assets/javascripts/sugar-core.js +0 -4001
  38. data/vendor/assets/javascripts/sugar-dates-only.js +0 -3121
  39. data/vendor/assets/javascripts/sugar-dates.js +0 -2210
@@ -0,0 +1,329 @@
1
+
2
+
3
+ /***
4
+ * @package Core
5
+ * @description Internal utility and common methods.
6
+ ***/
7
+
8
+
9
+ // A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
10
+ var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, math = Math, Undefined;
11
+
12
+ // The global context
13
+ var globalContext = typeof global !== 'undefined' ? global : this;
14
+
15
+ // defineProperty exists in IE8 but will error when trying to define a property on
16
+ // native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
17
+ var definePropertySupport = object.defineProperty && object.defineProperties;
18
+
19
+ // Class initializers and isClass type helpers
20
+
21
+ var ClassNames = 'Array,Boolean,Date,Function,Number,String,RegExp'.split(',');
22
+
23
+ var isArray = buildClassCheck(ClassNames[0]);
24
+ var isBoolean = buildClassCheck(ClassNames[1]);
25
+ var isDate = buildClassCheck(ClassNames[2]);
26
+ var isFunction = buildClassCheck(ClassNames[3]);
27
+ var isNumber = buildClassCheck(ClassNames[4]);
28
+ var isString = buildClassCheck(ClassNames[5]);
29
+ var isRegExp = buildClassCheck(ClassNames[6]);
30
+
31
+ function buildClassCheck(type) {
32
+ return function(obj) {
33
+ return isClass(obj, type);
34
+ }
35
+ }
36
+
37
+ function isClass(obj, str) {
38
+ return object.prototype.toString.call(obj) === '[object '+str+']';
39
+ }
40
+
41
+ function initializeClasses() {
42
+ initializeClass(object);
43
+ iterateOverObject(ClassNames, function(i,name) {
44
+ initializeClass(globalContext[name]);
45
+ });
46
+ }
47
+
48
+ function initializeClass(klass) {
49
+ if(klass['SugarMethods']) return;
50
+ defineProperty(klass, 'SugarMethods', {});
51
+ extend(klass, false, false, {
52
+ 'restore': function() {
53
+ var all = arguments.length === 0, methods = multiArgs(arguments);
54
+ iterateOverObject(klass['SugarMethods'], function(name, m) {
55
+ if(all || methods.indexOf(name) > -1) {
56
+ defineProperty(m.instance ? klass.prototype : klass, name, m.method);
57
+ }
58
+ });
59
+ },
60
+ 'extend': function(methods, override, instance) {
61
+ extend(klass, instance !== false, override, methods);
62
+ }
63
+ });
64
+ }
65
+
66
+ // Class extending methods
67
+
68
+ function extend(klass, instance, override, methods) {
69
+ var extendee = instance ? klass.prototype : klass, original;
70
+ initializeClass(klass);
71
+ iterateOverObject(methods, function(name, method) {
72
+ original = extendee[name];
73
+ if(typeof override === 'function') {
74
+ method = wrapNative(extendee[name], method, override);
75
+ }
76
+ if(override !== false || !extendee[name]) {
77
+ defineProperty(extendee, name, method);
78
+ }
79
+ // If the method is internal to Sugar, then store a reference so it can be restored later.
80
+ klass['SugarMethods'][name] = { instance: instance, method: method, original: original };
81
+ });
82
+ }
83
+
84
+ function extendSimilar(klass, instance, override, set, fn) {
85
+ var methods = {};
86
+ set = isString(set) ? set.split(',') : set;
87
+ set.forEach(function(name, i) {
88
+ fn(methods, name, i);
89
+ });
90
+ extend(klass, instance, override, methods);
91
+ }
92
+
93
+ function wrapNative(nativeFn, extendedFn, condition) {
94
+ return function() {
95
+ if(nativeFn && (condition === true || !condition.apply(this, arguments))) {
96
+ return nativeFn.apply(this, arguments);
97
+ } else {
98
+ return extendedFn.apply(this, arguments);
99
+ }
100
+ }
101
+ }
102
+
103
+ function defineProperty(target, name, method) {
104
+ if(definePropertySupport) {
105
+ object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true });
106
+ } else {
107
+ target[name] = method;
108
+ }
109
+ }
110
+
111
+
112
+ // Argument helpers
113
+
114
+ function multiArgs(args, fn) {
115
+ var result = [], i = 0;
116
+ for(i = 0; i < args.length; i++) {
117
+ result.push(args[i]);
118
+ if(fn) fn.call(args, args[i], i);
119
+ }
120
+ return result;
121
+ }
122
+
123
+
124
+ // General helpers
125
+
126
+ function isDefined(o) {
127
+ return o !== Undefined;
128
+ }
129
+
130
+ function isUndefined(o) {
131
+ return o === Undefined;
132
+ }
133
+
134
+
135
+ // Object helpers
136
+
137
+ function isObjectPrimitive(obj) {
138
+ // Check for null
139
+ return obj && typeof obj === 'object';
140
+ }
141
+
142
+ function isObject(obj) {
143
+ // === on the constructor is not safe across iframes
144
+ return !!obj && isClass(obj, 'Object') && string(obj.constructor) === string(object);
145
+ }
146
+
147
+ function hasOwnProperty(obj, key) {
148
+ return object['hasOwnProperty'].call(obj, key);
149
+ }
150
+
151
+ function iterateOverObject(obj, fn) {
152
+ var key;
153
+ for(key in obj) {
154
+ if(!hasOwnProperty(obj, key)) continue;
155
+ if(fn.call(obj, key, obj[key]) === false) break;
156
+ }
157
+ }
158
+
159
+ function simpleMerge(target, source) {
160
+ iterateOverObject(source, function(key) {
161
+ target[key] = source[key];
162
+ });
163
+ return target;
164
+ }
165
+
166
+ // Hash definition
167
+
168
+ function Hash(obj) {
169
+ simpleMerge(this, obj);
170
+ };
171
+
172
+ Hash.prototype.constructor = object;
173
+
174
+ // Number helpers
175
+
176
+ function getRange(start, stop, fn, step) {
177
+ var arr = [], i = parseInt(start), down = step < 0;
178
+ while((!down && i <= stop) || (down && i >= stop)) {
179
+ arr.push(i);
180
+ if(fn) fn.call(this, i);
181
+ i += step || 1;
182
+ }
183
+ return arr;
184
+ }
185
+
186
+ function round(val, precision, method) {
187
+ var fn = math[method || 'round'];
188
+ var multiplier = math.pow(10, math.abs(precision || 0));
189
+ if(precision < 0) multiplier = 1 / multiplier;
190
+ return fn(val * multiplier) / multiplier;
191
+ }
192
+
193
+ function ceil(val, precision) {
194
+ return round(val, precision, 'ceil');
195
+ }
196
+
197
+ function floor(val, precision) {
198
+ return round(val, precision, 'floor');
199
+ }
200
+
201
+ function padNumber(num, place, sign, base) {
202
+ var str = math.abs(num).toString(base || 10);
203
+ str = repeatString(place - str.replace(/\.\d+/, '').length, '0') + str;
204
+ if(sign || num < 0) {
205
+ str = (num < 0 ? '-' : '+') + str;
206
+ }
207
+ return str;
208
+ }
209
+
210
+ function getOrdinalizedSuffix(num) {
211
+ if(num >= 11 && num <= 13) {
212
+ return 'th';
213
+ } else {
214
+ switch(num % 10) {
215
+ case 1: return 'st';
216
+ case 2: return 'nd';
217
+ case 3: return 'rd';
218
+ default: return 'th';
219
+ }
220
+ }
221
+ }
222
+
223
+
224
+ // String helpers
225
+
226
+ // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
227
+ function getTrimmableCharacters() {
228
+ 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';
229
+ }
230
+
231
+ function repeatString(times, str) {
232
+ return array(math.max(0, isDefined(times) ? times : 1) + 1).join(str || '');
233
+ }
234
+
235
+
236
+ // RegExp helpers
237
+
238
+ function getRegExpFlags(reg, add) {
239
+ var flags = reg.toString().match(/[^/]*$/)[0];
240
+ if(add) {
241
+ flags = (flags + add).split('').sort().join('').replace(/([gimy])\1+/g, '$1');
242
+ }
243
+ return flags;
244
+ }
245
+
246
+ function escapeRegExp(str) {
247
+ if(!isString(str)) str = string(str);
248
+ return str.replace(/([\\/'*+?|()\[\]{}.^$])/g,'\\$1');
249
+ }
250
+
251
+
252
+ // Specialized helpers
253
+
254
+
255
+ // Used by Array#unique and Object.equal
256
+
257
+ function stringify(thing, stack) {
258
+ var value, klass, isObject, isArray, arr, i, key, type = typeof thing;
259
+
260
+ // Return quickly if string to save cycles
261
+ if(type === 'string') return thing;
262
+
263
+ klass = object.prototype.toString.call(thing)
264
+ isObject = klass === '[object Object]';
265
+ isArray = klass === '[object Array]';
266
+
267
+ if(thing != null && isObject || isArray) {
268
+ // This method for checking for cyclic structures was egregiously stolen from
269
+ // the ingenious method by @kitcambridge from the Underscore script:
270
+ // https://github.com/documentcloud/underscore/issues/240
271
+ if(!stack) stack = [];
272
+ // Allowing a step into the structure before triggering this
273
+ // script to save cycles on standard JSON structures and also to
274
+ // try as hard as possible to catch basic properties that may have
275
+ // been modified.
276
+ if(stack.length > 1) {
277
+ i = stack.length;
278
+ while (i--) {
279
+ if (stack[i] === thing) {
280
+ return 'CYC';
281
+ }
282
+ }
283
+ }
284
+ stack.push(thing);
285
+ value = string(thing.constructor);
286
+ arr = isArray ? thing : object.keys(thing).sort();
287
+ for(i = 0; i < arr.length; i++) {
288
+ key = isArray ? i : arr[i];
289
+ value += key + stringify(thing[key], stack);
290
+ }
291
+ stack.pop();
292
+ } else if(1 / thing === -Infinity) {
293
+ value = '-0';
294
+ } else {
295
+ value = string(thing && thing.valueOf());
296
+ }
297
+ return type + klass + value;
298
+ }
299
+
300
+
301
+ // Used by Array#at and String#at
302
+
303
+ function entryAtIndex(arr, args, str) {
304
+ var result = [], length = arr.length, loop = args[args.length - 1] !== false, r;
305
+ multiArgs(args, function(index) {
306
+ if(isBoolean(index)) return false;
307
+ if(loop) {
308
+ index = index % length;
309
+ if(index < 0) index = length + index;
310
+ }
311
+ r = str ? arr.charAt(index) || '' : arr[index];
312
+ result.push(r);
313
+ });
314
+ return result.length < 2 ? result[0] : result;
315
+ }
316
+
317
+
318
+ // Object class methods implemented as instance methods
319
+
320
+ function buildObjectInstanceMethods(set, target) {
321
+ extendSimilar(target, true, false, set, function(methods, name) {
322
+ methods[name + (name === 'equal' ? 's' : '')] = function() {
323
+ return object[name].apply(null, [this].concat(multiArgs(arguments)));
324
+ }
325
+ });
326
+ }
327
+
328
+ initializeClasses();
329
+
@@ -0,0 +1,2179 @@
1
+
2
+ /***
3
+ * @package Date
4
+ * @dependency core
5
+ * @description Date parsing and formatting, relative formats like "1 minute ago", Number methods like "daysAgo", localization support with default English locale definition.
6
+ *
7
+ ***/
8
+
9
+ var English;
10
+ var CurrentLocalization;
11
+
12
+ var TimeFormat = ['ampm','hour','minute','second','ampm','utc','offset_sign','offset_hours','offset_minutes','ampm']
13
+ var FloatReg = '\\d{1,2}(?:[,.]\\d+)?';
14
+ var RequiredTime = '({t})?\\s*('+FloatReg+')(?:{h}('+FloatReg+')?{m}(?::?('+FloatReg+'){s})?\\s*(?:({t})|(Z)|(?:([+-])(\\d{2,2})(?::?(\\d{2,2}))?)?)?|\\s*({t}))';
15
+
16
+ var KanjiDigits = '〇一二三四五六七八九十百千万';
17
+ var FullWidthDigits = '0123456789';
18
+ var AsianDigitMap = {};
19
+ var AsianDigitReg;
20
+
21
+ var DateArgumentUnits;
22
+ var DateUnitsReversed;
23
+ var CoreDateFormats = [];
24
+
25
+ var DateOutputFormats = [
26
+ {
27
+ token: 'f{1,4}|ms|milliseconds',
28
+ format: function(d) {
29
+ return d.getMilliseconds();
30
+ }
31
+ },
32
+ {
33
+ token: 'ss?|seconds',
34
+ format: function(d, len) {
35
+ return d.getSeconds();
36
+ }
37
+ },
38
+ {
39
+ token: 'mm?|minutes',
40
+ format: function(d, len) {
41
+ return d.getMinutes();
42
+ }
43
+ },
44
+ {
45
+ token: 'hh?|hours|12hr',
46
+ format: function(d) {
47
+ return getShortHour(d);
48
+ }
49
+ },
50
+ {
51
+ token: 'HH?|24hr',
52
+ format: function(d) {
53
+ return d.getHours();
54
+ }
55
+ },
56
+ {
57
+ token: 'dd?|date|day',
58
+ format: function(d) {
59
+ return d.getDate();
60
+ }
61
+ },
62
+ {
63
+ token: 'dow|weekday',
64
+ word: true,
65
+ format: function(d, loc, n, t) {
66
+ return loc['weekdays'][d.getDay() + (n - 1) * 7];
67
+ }
68
+ },
69
+ {
70
+ token: 'MM?',
71
+ format: function(d) {
72
+ return d.getMonth() + 1;
73
+ }
74
+ },
75
+ {
76
+ token: 'mon|month',
77
+ word: true,
78
+ format: function(d, loc, n, len) {
79
+ return loc['months'][d.getMonth() + (n - 1) * 12];
80
+ }
81
+ },
82
+ {
83
+ token: 'y{2,4}|year',
84
+ format: function(d) {
85
+ return d.getFullYear();
86
+ }
87
+ },
88
+ {
89
+ token: '[Tt]{1,2}',
90
+ format: function(d, loc, n, format) {
91
+ var str = loc['ampm'][floor(d.getHours() / 12)];
92
+ if(format.length === 1) str = str.slice(0,1);
93
+ if(format.slice(0,1) === 'T') str = str.toUpperCase();
94
+ return str;
95
+ }
96
+ },
97
+ {
98
+ token: 'z{1,4}|tz|timezone',
99
+ text: true,
100
+ format: function(d, loc, n, format) {
101
+ var tz = d.getUTCOffset();
102
+ if(format == 'z' || format == 'zz') {
103
+ tz = tz.replace(/(\d{2})(\d{2})/, function(f,h,m) {
104
+ return padNumber(h, format.length);
105
+ });
106
+ }
107
+ return tz;
108
+ }
109
+ },
110
+ {
111
+ token: 'iso(tz|timezone)',
112
+ format: function(d) {
113
+ return d.getUTCOffset(true);
114
+ }
115
+ },
116
+ {
117
+ token: 'ord',
118
+ format: function(d) {
119
+ var d = d.getDate();
120
+ return d + getOrdinalizedSuffix(d);
121
+ }
122
+ }
123
+ ];
124
+
125
+ var DateUnits = [
126
+ {
127
+ unit: 'year',
128
+ method: 'FullYear',
129
+ multiplier: function(d) {
130
+ var adjust = d ? (d.isLeapYear() ? 1 : 0) : 0.25;
131
+ return (365 + adjust) * 24 * 60 * 60 * 1000;
132
+ }
133
+ },
134
+ {
135
+ unit: 'month',
136
+ method: 'Month',
137
+ ambiguous: true,
138
+ multiplier: function(d, ms) {
139
+ var days = 30.4375, inMonth;
140
+ if(d) {
141
+ inMonth = d.daysInMonth();
142
+ if(ms <= inMonth.days()) {
143
+ days = inMonth;
144
+ }
145
+ }
146
+ return days * 24 * 60 * 60 * 1000;
147
+ }
148
+ },
149
+ {
150
+ unit: 'week',
151
+ method: 'Week',
152
+ multiplier: function() {
153
+ return 7 * 24 * 60 * 60 * 1000;
154
+ }
155
+ },
156
+ {
157
+ unit: 'day',
158
+ method: 'Date',
159
+ ambiguous: true,
160
+ multiplier: function() {
161
+ return 24 * 60 * 60 * 1000;
162
+ }
163
+ },
164
+ {
165
+ unit: 'hour',
166
+ method: 'Hours',
167
+ multiplier: function() {
168
+ return 60 * 60 * 1000;
169
+ }
170
+ },
171
+ {
172
+ unit: 'minute',
173
+ method: 'Minutes',
174
+ multiplier: function() {
175
+ return 60 * 1000;
176
+ }
177
+ },
178
+ {
179
+ unit: 'second',
180
+ method: 'Seconds',
181
+ multiplier: function() {
182
+ return 1000;
183
+ }
184
+ },
185
+ {
186
+ unit: 'millisecond',
187
+ method: 'Milliseconds',
188
+ multiplier: function() {
189
+ return 1;
190
+ }
191
+ }
192
+ ];
193
+
194
+
195
+
196
+
197
+ // Date Localization
198
+
199
+ var Localizations = {};
200
+
201
+ // Localization object
202
+
203
+ function Localization(l) {
204
+ simpleMerge(this, l);
205
+ this.compiledFormats = CoreDateFormats.concat();
206
+ }
207
+
208
+ Localization.prototype = {
209
+
210
+ getMonth: function(n) {
211
+ if(isNumber(n)) {
212
+ return n - 1;
213
+ } else {
214
+ return this['months'].indexOf(n) % 12;
215
+ }
216
+ },
217
+
218
+ getWeekday: function(n) {
219
+ return this['weekdays'].indexOf(n) % 7;
220
+ },
221
+
222
+ getNumber: function(n) {
223
+ var i;
224
+ if(isNumber(n)) {
225
+ return n;
226
+ } else if(n && (i = this['numbers'].indexOf(n)) !== -1) {
227
+ return (i + 1) % 10;
228
+ } else {
229
+ return 1;
230
+ }
231
+ },
232
+
233
+ getNumericDate: function(n) {
234
+ var self = this;
235
+ return n.replace(regexp(this['num'], 'g'), function(d) {
236
+ var num = self.getNumber(d);
237
+ return num || '';
238
+ });
239
+ },
240
+
241
+ getEnglishUnit: function(n) {
242
+ return English['units'][this['units'].indexOf(n) % 8];
243
+ },
244
+
245
+ relative: function(adu) {
246
+ return this.convertAdjustedToFormat(adu, adu[2] > 0 ? 'future' : 'past');
247
+ },
248
+
249
+ getDuration: function(ms) {
250
+ return this.convertAdjustedToFormat(getAdjustedUnit(ms), 'duration');
251
+ },
252
+
253
+ hasVariant: function(code) {
254
+ code = code || this.code;
255
+ return code === 'en' || code === 'en-US' ? true : this['variant'];
256
+ },
257
+
258
+ matchPM: function(str) {
259
+ return str === this['ampm'][1];
260
+ },
261
+
262
+ convertAdjustedToFormat: function(adu, format) {
263
+ var num = adu[0], u = adu[1], ms = adu[2], sign, unit, last, mult;
264
+ if(this['code'] == 'ru') {
265
+ last = num.toString().slice(-1);
266
+ switch(true) {
267
+ case last == 1: mult = 1; break;
268
+ case last >= 2 && last <= 4: mult = 2; break;
269
+ default: mult = 3;
270
+ }
271
+ } else {
272
+ mult = this['plural'] && num > 1 ? 1 : 0;
273
+ }
274
+ unit = this['units'][mult * 8 + u] || this['units'][u];
275
+ if(this['capitalizeUnit']) unit = simpleCapitalize(unit);
276
+ sign = this['modifiers'].filter(function(m) { return m.name == 'sign' && m.value == (ms > 0 ? 1 : -1); })[0];
277
+ return this[format].replace(/\{(.*?)\}/g, function(full, match) {
278
+ switch(match) {
279
+ case 'num': return num;
280
+ case 'unit': return unit;
281
+ case 'sign': return sign.src;
282
+ }
283
+ });
284
+ },
285
+
286
+ getFormats: function() {
287
+ return this.cachedFormat ? [this.cachedFormat].concat(this.compiledFormats) : this.compiledFormats;
288
+ },
289
+
290
+ addFormat: function(src, allowsTime, match, variant, iso) {
291
+ var to = match || [], loc = this, time, timeMarkers, lastIsNumeral;
292
+
293
+ src = src.replace(/\s+/g, '[-,. ]*');
294
+ src = src.replace(/\{([^,]+?)\}/g, function(all, k) {
295
+ var opt = k.match(/\?$/), slice = k.match(/(\d)(?:-(\d))?/), nc = k.match(/^\d+$/), key = k.replace(/[^a-z]+$/, ''), value, arr;
296
+ if(nc) {
297
+ value = loc['optionals'][nc[0]];
298
+ } else if(loc[key]) {
299
+ value = loc[key];
300
+ } else if(loc[key + 's']) {
301
+ value = loc[key + 's'];
302
+ if(slice) {
303
+ // Can't use filter here as Prototype hijacks the method and doesn't
304
+ // pass an index, so use a simple loop instead!
305
+ arr = [];
306
+ value.forEach(function(m, i) {
307
+ var mod = i % (loc['units'] ? 8 : value.length);
308
+ if(mod >= slice[1] && mod <= (slice[2] || slice[1])) {
309
+ arr.push(m);
310
+ }
311
+ });
312
+ value = arr;
313
+ }
314
+ value = arrayToAlternates(value);
315
+ }
316
+ if(nc) {
317
+ return '(?:' + value + ')?';
318
+ } else {
319
+ if(!match) {
320
+ to.push(key);
321
+ }
322
+ return '(' + value + ')' + (opt ? '?' : '');
323
+ }
324
+ });
325
+ if(allowsTime) {
326
+ time = prepareTime(RequiredTime, loc, iso);
327
+ timeMarkers = ['t','[\\s\\u3000]'].concat(loc['timeMarker']);
328
+ lastIsNumeral = src.match(/\\d\{\d,\d\}\)+\??$/);
329
+ addDateInputFormat(loc, '(?:' + time + ')[,\\s\\u3000]+?' + src, TimeFormat.concat(to), variant);
330
+ addDateInputFormat(loc, src + '(?:[,\\s]*(?:' + timeMarkers.join('|') + (lastIsNumeral ? '+' : '*') +')' + time + ')?', to.concat(TimeFormat), variant);
331
+ } else {
332
+ addDateInputFormat(loc, src, to, variant);
333
+ }
334
+ }
335
+
336
+ };
337
+
338
+
339
+ // Localization helpers
340
+
341
+ function getLocalization(localeCode, fallback) {
342
+ var loc;
343
+ if(!isString(localeCode)) localeCode = '';
344
+ loc = Localizations[localeCode] || Localizations[localeCode.slice(0,2)];
345
+ if(fallback === false && !loc) {
346
+ throw new Error('Invalid locale.');
347
+ }
348
+ return loc || CurrentLocalization;
349
+ }
350
+
351
+ function setLocalization(localeCode, set) {
352
+ var loc;
353
+
354
+ function initializeField(name) {
355
+ var val = loc[name];
356
+ if(isString(val)) {
357
+ loc[name] = val.split(',');
358
+ } else if(!val) {
359
+ loc[name] = [];
360
+ }
361
+ }
362
+
363
+ function eachAlternate(str, fn) {
364
+ str = str.split('+').map(function(split) {
365
+ return split.replace(/(.+):(.+)$/, function(full, base, suffixes) {
366
+ return suffixes.split('|').map(function(suffix) {
367
+ return base + suffix;
368
+ }).join('|');
369
+ });
370
+ }).join('|');
371
+ return str.split('|').forEach(fn);
372
+ }
373
+
374
+ function setArray(name, abbreviate, multiple) {
375
+ var arr = [];
376
+ if(!loc[name]) return;
377
+ loc[name].forEach(function(el, i) {
378
+ eachAlternate(el, function(str, j) {
379
+ arr[j * multiple + i] = str.toLowerCase();
380
+ });
381
+ });
382
+ if(abbreviate) arr = arr.concat(loc[name].map(function(str) {
383
+ return str.slice(0,3).toLowerCase();
384
+ }));
385
+ return loc[name] = arr;
386
+ }
387
+
388
+ function getDigit(start, stop, allowNumbers) {
389
+ var str = '\\d{' + start + ',' + stop + '}';
390
+ if(allowNumbers) str += '|(?:' + arrayToAlternates(loc['numbers']) + ')+';
391
+ return str;
392
+ }
393
+
394
+ function getNum() {
395
+ var arr = ['\\d+'].concat(loc['articles']);
396
+ if(loc['numbers']) arr = arr.concat(loc['numbers']);
397
+ return arrayToAlternates(arr);
398
+ }
399
+
400
+ function setModifiers() {
401
+ var arr = [];
402
+ loc.modifiersByName = {};
403
+ loc['modifiers'].forEach(function(modifier) {
404
+ var name = modifier.name;
405
+ eachAlternate(modifier.src, function(t) {
406
+ var locEntry = loc[name];
407
+ loc.modifiersByName[t] = modifier;
408
+ arr.push({ name: name, src: t, value: modifier.value });
409
+ loc[name] = locEntry ? locEntry + '|' + t : t;
410
+ });
411
+ });
412
+ loc['day'] += '|' + arrayToAlternates(loc['weekdays']);
413
+ loc['modifiers'] = arr;
414
+ }
415
+
416
+ // Initialize the locale
417
+ loc = new Localization(set);
418
+ initializeField('modifiers');
419
+ 'months,weekdays,units,numbers,articles,optionals,timeMarker,ampm,timeSuffixes,dateParse,timeParse'.split(',').forEach(initializeField);
420
+
421
+ setArray('months', true, 12);
422
+ setArray('weekdays', true, 7);
423
+ setArray('units', false, 8);
424
+ setArray('numbers', false, 10);
425
+
426
+ loc['code'] = localeCode;
427
+ loc['date'] = getDigit(1,2, loc['digitDate']);
428
+ loc['year'] = getDigit(4,4);
429
+ loc['num'] = getNum();
430
+
431
+ setModifiers();
432
+
433
+ if(loc['monthSuffix']) {
434
+ loc['month'] = getDigit(1,2);
435
+ loc['months'] = getRange(1, 12).map(function(n) { return n + loc['monthSuffix']; });
436
+ }
437
+ loc['full_month'] = getDigit(1,2) + '|' + arrayToAlternates(loc['months']);
438
+
439
+ // The order of these formats is very important. Order is reversed so formats that come
440
+ // later will take precedence over formats that come before. This generally means that
441
+ // more specific formats should come later, however, the {year} format should come before
442
+ // {day}, as 2011 needs to be parsed as a year (2011) and not date (20) + hours (11)
443
+
444
+ // If the locale has time suffixes then add a time only format for that locale
445
+ // that is separate from the core English-based one.
446
+ if(loc['timeSuffixes'].length > 0) {
447
+ loc.addFormat(prepareTime(RequiredTime, loc), false, TimeFormat)
448
+ }
449
+
450
+ loc.addFormat('{day}', true);
451
+ loc.addFormat('{month}' + (loc['monthSuffix'] || ''));
452
+ loc.addFormat('{year}' + (loc['yearSuffix'] || ''));
453
+
454
+ loc['timeParse'].forEach(function(src) {
455
+ loc.addFormat(src, true);
456
+ });
457
+
458
+ loc['dateParse'].forEach(function(src) {
459
+ loc.addFormat(src);
460
+ });
461
+
462
+ return Localizations[localeCode] = loc;
463
+ }
464
+
465
+
466
+ // General helpers
467
+
468
+ function addDateInputFormat(locale, format, match, variant) {
469
+ locale.compiledFormats.unshift({
470
+ variant: variant,
471
+ locale: locale,
472
+ reg: regexp('^' + format + '$', 'i'),
473
+ to: match
474
+ });
475
+ }
476
+
477
+ function simpleCapitalize(str) {
478
+ return str.slice(0,1).toUpperCase() + str.slice(1);
479
+ }
480
+
481
+ function arrayToAlternates(arr) {
482
+ return arr.filter(function(el) {
483
+ return !!el;
484
+ }).join('|');
485
+ }
486
+
487
+ // Date argument helpers
488
+
489
+ function collectDateArguments(args, allowDuration) {
490
+ var obj, arr;
491
+ if(isObject(args[0])) {
492
+ return args;
493
+ } else if (isNumber(args[0]) && !isNumber(args[1])) {
494
+ return [args[0]];
495
+ } else if (isString(args[0]) && allowDuration) {
496
+ return [getDateParamsFromString(args[0]), args[1]];
497
+ }
498
+ obj = {};
499
+ DateArgumentUnits.forEach(function(u,i) {
500
+ obj[u.unit] = args[i];
501
+ });
502
+ return [obj];
503
+ }
504
+
505
+ function getDateParamsFromString(str, num) {
506
+ var params = {};
507
+ match = str.match(/^(\d+)?\s?(\w+?)s?$/i);
508
+ if(isUndefined(num)) {
509
+ num = parseInt(match[1]) || 1;
510
+ }
511
+ params[match[2].toLowerCase()] = num;
512
+ return params;
513
+ }
514
+
515
+ // Date parsing helpers
516
+
517
+ function getFormatMatch(match, arr) {
518
+ var obj = {}, value, num;
519
+ arr.forEach(function(key, i) {
520
+ value = match[i + 1];
521
+ if(isUndefined(value) || value === '') return;
522
+ if(key === 'year') obj.yearAsString = value;
523
+ num = parseFloat(value.replace(/,/, '.'));
524
+ obj[key] = !isNaN(num) ? num : value.toLowerCase();
525
+ });
526
+ return obj;
527
+ }
528
+
529
+ function cleanDateInput(str) {
530
+ str = str.trim().replace(/^(just )?now|\.+$/i, '');
531
+ return convertAsianDigits(str);
532
+ }
533
+
534
+ function convertAsianDigits(str) {
535
+ return str.replace(AsianDigitReg, function(full, disallowed, match) {
536
+ var sum = 0, place = 1, lastWasHolder, lastHolder;
537
+ if(disallowed) return full;
538
+ match.split('').reverse().forEach(function(letter) {
539
+ var value = AsianDigitMap[letter], holder = value > 9;
540
+ if(holder) {
541
+ if(lastWasHolder) sum += place;
542
+ place *= value / (lastHolder || 1);
543
+ lastHolder = value;
544
+ } else {
545
+ if(lastWasHolder === false) {
546
+ place *= 10;
547
+ }
548
+ sum += place * value;
549
+ }
550
+ lastWasHolder = holder;
551
+ });
552
+ if(lastWasHolder) sum += place;
553
+ return sum;
554
+ });
555
+ }
556
+
557
+ function getExtendedDate(f, localeCode, prefer) {
558
+ var d = new date(), relative = false, baseLocalization, loc, format, set, unit, weekday, num, tmp, after;
559
+ if(isDate(f)) {
560
+ d = f.clone();
561
+ } else if(isNumber(f)) {
562
+ d = new date(f);
563
+ } else if(isObject(f)) {
564
+ d = new date().set(f, true);
565
+ set = f;
566
+ } else if(isString(f)) {
567
+
568
+ // The act of getting the localization will pre-initialize
569
+ // if it is missing and add the required formats.
570
+ baseLocalization = getLocalization(localeCode);
571
+
572
+ // Clean the input and convert Kanji based numerals if they exist.
573
+ f = cleanDateInput(f);
574
+
575
+ if(baseLocalization) {
576
+ iterateOverObject(baseLocalization.getFormats(), function(i, dif) {
577
+ var match = f.match(dif.reg);
578
+ if(match) {
579
+ format = dif;
580
+ loc = format.locale;
581
+ set = getFormatMatch(match, format.to, loc);
582
+ loc.cachedFormat = format;
583
+
584
+ if(set.timestamp) {
585
+ set = set.timestamp;
586
+ return false;
587
+ }
588
+
589
+ // If there's a variant (crazy Endian American format), swap the month and day.
590
+ if(format.variant && !isString(set['month']) && (isString(set['date']) || baseLocalization.hasVariant(localeCode))) {
591
+ tmp = set['month'];
592
+ set['month'] = set['date'];
593
+ set['date'] = tmp;
594
+ }
595
+
596
+ // If the year is 2 digits then get the implied century.
597
+ if(set['year'] && set.yearAsString.length === 2) {
598
+ set['year'] = getYearFromAbbreviation(set['year']);
599
+ }
600
+
601
+ // Set the month which may be localized.
602
+ if(set['month']) {
603
+ set['month'] = loc.getMonth(set['month']);
604
+ if(set['shift'] && !set['unit']) set['unit'] = loc['units'][7];
605
+ }
606
+
607
+ // If there is both a weekday and a date, the date takes precedence.
608
+ if(set['weekday'] && set['date']) {
609
+ delete set['weekday'];
610
+ // Otherwise set a localized weekday.
611
+ } else if(set['weekday']) {
612
+ set['weekday'] = loc.getWeekday(set['weekday']);
613
+ if(set['shift'] && !set['unit']) set['unit'] = loc['units'][5];
614
+ }
615
+
616
+ // Relative day localizations such as "today" and "tomorrow".
617
+ if(set['day'] && (tmp = loc.modifiersByName[set['day']])) {
618
+ set['day'] = tmp.value;
619
+ d.reset();
620
+ relative = true;
621
+ // If the day is a weekday, then set that instead.
622
+ } else if(set['day'] && (weekday = loc.getWeekday(set['day'])) > -1) {
623
+ delete set['day'];
624
+ if(set['num'] && set['month']) {
625
+ // If we have "the 2nd tuesday of June", set the day to the beginning of the month, then
626
+ // look ahead to set the weekday after all other properties have been set. The weekday needs
627
+ // to be set after the actual set because it requires overriding the "prefer" argument which
628
+ // could unintentionally send the year into the future, past, etc.
629
+ after = function() {
630
+ updateDate(d, { 'weekday': weekday + (7 * (set['num'] - 1)) }, false, false, false, 1);
631
+ }
632
+ set['day'] = 1;
633
+ } else {
634
+ set['weekday'] = weekday;
635
+ }
636
+ }
637
+
638
+ if(set['date'] && !isNumber(set['date'])) {
639
+ set['date'] = loc.getNumericDate(set['date']);
640
+ }
641
+
642
+ // If the time is 1pm-11pm advance the time by 12 hours.
643
+ if(loc.matchPM(set['ampm']) && set['hour'] < 12) {
644
+ set['hour'] += 12;
645
+ }
646
+
647
+ // Adjust for timezone offset
648
+ if('offset_hours' in set || 'offset_minutes' in set) {
649
+ set['utc'] = true;
650
+ set['offset_minutes'] = set['offset_minutes'] || 0;
651
+ set['offset_minutes'] += set['offset_hours'] * 60;
652
+ if(set['offset_sign'] === '-') {
653
+ set['offset_minutes'] *= -1;
654
+ }
655
+ set['minute'] -= set['offset_minutes'];
656
+ }
657
+
658
+ // Date has a unit like "days", "months", etc. are all relative to the current date.
659
+ if(set['unit']) {
660
+ relative = true;
661
+ num = loc.getNumber(set['num']);
662
+ unit = loc.getEnglishUnit(set['unit']);
663
+
664
+ // Shift and unit, ie "next month", "last week", etc.
665
+ if(set['shift'] || set['edge']) {
666
+ num *= (tmp = loc.modifiersByName[set['shift']]) ? tmp.value : 0;
667
+
668
+ // Relative month and static date: "the 15th of last month"
669
+ if(unit === 'month' && isDefined(set['date'])) {
670
+ d.set({ 'day': set['date'] }, true);
671
+ delete set['date'];
672
+ }
673
+
674
+ // Relative year and static month/date: "June 15th of last year"
675
+ if(unit === 'year' && isDefined(set['month'])) {
676
+ d.set({ 'month': set['month'], 'day': set['date'] }, true);
677
+ delete set['month'];
678
+ delete set['date'];
679
+ }
680
+ }
681
+ // Unit and sign, ie "months ago", "weeks from now", etc.
682
+ if(set['sign'] && (tmp = loc.modifiersByName[set['sign']])) {
683
+ num *= tmp.value;
684
+ }
685
+
686
+ // Units can be with non-relative dates, set here. ie "the day after monday"
687
+ if(isDefined(set['weekday'])) {
688
+ d.set({'weekday': set['weekday'] }, true);
689
+ delete set['weekday'];
690
+ }
691
+
692
+ // Finally shift the unit.
693
+ set[unit] = (set[unit] || 0) + num;
694
+ }
695
+
696
+ if(set['year_sign'] === '-') {
697
+ set['year'] *= -1;
698
+ }
699
+
700
+ DateUnitsReversed.slice(1,4).forEach(function(u, i) {
701
+ var value = set[u.unit], fraction = value % 1;
702
+ if(fraction) {
703
+ set[DateUnitsReversed[i].unit] = round(fraction * (u.unit === 'second' ? 1000 : 60));
704
+ set[u.unit] = floor(value);
705
+ }
706
+ });
707
+ return false;
708
+ }
709
+ });
710
+ }
711
+ if(!format) {
712
+ // The Date constructor does something tricky like checking the number
713
+ // of arguments so simply passing in undefined won't work.
714
+ d = f ? new date(f) : new date();
715
+ } else if(relative) {
716
+ d.advance(set);
717
+ } else {
718
+ if(set['utc']) {
719
+ // UTC times can traverse into other days or even months,
720
+ // so preemtively reset the time here to prevent this.
721
+ d.reset();
722
+ }
723
+ updateDate(d, set, true, set['utc'], false, prefer);
724
+ }
725
+
726
+ // If there is an "edge" it needs to be set after the
727
+ // other fields are set. ie "the end of February"
728
+ if(set && set['edge']) {
729
+ tmp = loc.modifiersByName[set['edge']];
730
+ iterateOverObject(DateUnitsReversed.slice(4), function(i, u) {
731
+ if(isDefined(set[u.unit])) {
732
+ unit = u.unit;
733
+ return false;
734
+ }
735
+ });
736
+ if(unit === 'year') set.specificity = 'month';
737
+ else if(unit === 'month' || unit === 'week') set.specificity = 'day';
738
+ d[(tmp.value < 0 ? 'endOf' : 'beginningOf') + simpleCapitalize(unit)]();
739
+ // This value of -2 is arbitrary but it's a nice clean way to hook into this system.
740
+ if(tmp.value === -2) d.reset();
741
+ }
742
+ if(after) {
743
+ after();
744
+ }
745
+
746
+ }
747
+ return {
748
+ date: d,
749
+ set: set
750
+ }
751
+ }
752
+
753
+ // If the year is two digits, add the most appropriate century prefix.
754
+ function getYearFromAbbreviation(year) {
755
+ return round(new date().getFullYear() / 100) * 100 - round(year / 100) * 100 + year;
756
+ }
757
+
758
+ function getShortHour(d, utc) {
759
+ var hours = callDateMethod(d, 'get', utc, 'Hours');
760
+ return hours === 0 ? 12 : hours - (floor(hours / 13) * 12);
761
+ }
762
+
763
+ // weeksSince won't work here as the result needs to be floored, not rounded.
764
+ function getWeekNumber(date) {
765
+ var dow = date.getDay() || 7;
766
+ date.addDays(4 - dow).reset();
767
+ return 1 + floor(date.daysSince(date.clone().beginningOfYear()) / 7);
768
+ }
769
+
770
+ function getAdjustedUnit(ms) {
771
+ var next, ams = math.abs(ms), value = ams, unit = 0;
772
+ DateUnitsReversed.slice(1).forEach(function(u, i) {
773
+ next = floor(round(ams / u.multiplier() * 10) / 10);
774
+ if(next >= 1) {
775
+ value = next;
776
+ unit = i + 1;
777
+ }
778
+ });
779
+ return [value, unit, ms];
780
+ }
781
+
782
+
783
+ // Date formatting helpers
784
+
785
+ function formatDate(date, format, relative, localeCode) {
786
+ var adu, loc = getLocalization(localeCode), caps = regexp(/^[A-Z]/), value, shortcut;
787
+ if(!date.isValid()) {
788
+ return 'Invalid Date';
789
+ } else if(Date[format]) {
790
+ format = Date[format];
791
+ } else if(isFunction(format)) {
792
+ adu = getAdjustedUnit(date.millisecondsFromNow());
793
+ format = format.apply(date, adu.concat(loc));
794
+ }
795
+ if(!format && relative) {
796
+ adu = adu || getAdjustedUnit(date.millisecondsFromNow());
797
+ // Adjust up if time is in ms, as this doesn't
798
+ // look very good for a standard relative date.
799
+ if(adu[1] === 0) {
800
+ adu[1] = 1;
801
+ adu[0] = 1;
802
+ }
803
+ return loc.relative(adu);
804
+ }
805
+
806
+ format = format || 'long';
807
+ format = loc[format] || format;
808
+
809
+ DateOutputFormats.forEach(function(dof) {
810
+ format = format.replace(regexp('\\{('+dof.token+')(\\d)?\\}', dof.word ? 'i' : ''), function(m,t,d) {
811
+ var val = dof.format(date, loc, d || 1, t), l = t.length, one = t.match(/^(.)\1+$/);
812
+ if(dof.word) {
813
+ if(l === 3) val = val.slice(0,3);
814
+ if(one || t.match(caps)) val = simpleCapitalize(val);
815
+ } else if(one && !dof.text) {
816
+ val = (isNumber(val) ? padNumber(val, l) : val.toString()).slice(-l);
817
+ }
818
+ return val;
819
+ });
820
+ });
821
+ return format;
822
+ }
823
+
824
+ // Date comparison helpers
825
+
826
+ function compareDate(d, find, buffer) {
827
+ var p = getExtendedDate(find), accuracy = 0, loBuffer = 0, hiBuffer = 0, override, capitalized;
828
+ if(buffer > 0) {
829
+ loBuffer = hiBuffer = buffer;
830
+ override = true;
831
+ }
832
+ if(!p.date.isValid()) return false;
833
+ if(p.set && p.set.specificity) {
834
+ DateUnits.forEach(function(u, i) {
835
+ if(u.unit === p.set.specificity) {
836
+ accuracy = u.multiplier(p.date, d - p.date) - 1;
837
+ }
838
+ });
839
+ capitalized = simpleCapitalize(p.set.specificity);
840
+ if(p.set['edge'] || p.set['shift']) {
841
+ p.date['beginningOf' + capitalized]();
842
+ }
843
+ if(p.set.specificity === 'month') {
844
+ max = p.date.clone()['endOf' + capitalized]().getTime();
845
+ }
846
+ if(!override && p.set['sign'] && p.set.specificity != 'millisecond') {
847
+ // If the time is relative, there can occasionally be an disparity between the relative date
848
+ // and "now", which it is being compared to, so set an extra buffer to account for this.
849
+ loBuffer = 50;
850
+ hiBuffer = -50;
851
+ }
852
+ }
853
+ var t = d.getTime();
854
+ var min = p.date.getTime();
855
+ var max = max || (min + accuracy);
856
+ // Offset any shift that may occur as a result of DST traversal.
857
+ return t >= (min - loBuffer) && t <= (max + hiBuffer);
858
+ }
859
+
860
+ function updateDate(d, params, reset, utc, advance, prefer) {
861
+ var weekday;
862
+
863
+ function getParam(key) {
864
+ return isDefined(params[key]) ? params[key] : params[key + 's'];
865
+ }
866
+
867
+ function paramExists(key) {
868
+ return isDefined(getParam(key));
869
+ }
870
+
871
+ function canDisambiguate(u, higherUnit) {
872
+ return prefer && u.ambiguous && !paramExists(higherUnit.unit);
873
+ }
874
+
875
+ if(isNumber(params) && advance) {
876
+ // If param is a number and we're advancing, the number is presumed to be milliseconds.
877
+ params = { 'milliseconds': params };
878
+ } else if(isNumber(params)) {
879
+ // Otherwise just set the timestamp and return.
880
+ d.setTime(params);
881
+ return d;
882
+ }
883
+
884
+ // "date" can also be passed for the day
885
+ if(params['date']) params['day'] = params['date'];
886
+
887
+ // Reset any unit lower than the least specific unit set. Do not do this for weeks
888
+ // or for years. This needs to be performed before the acutal setting of the date
889
+ // because the order needs to be reversed in order to get the lowest specificity,
890
+ // also because higher order units can be overwritten by lower order units, such
891
+ // as setting hour: 3, minute: 345, etc.
892
+ iterateOverObject(DateUnitsReversed, function(i,u) {
893
+ var isDay = u.unit === 'day';
894
+ if(paramExists(u.unit) || (isDay && paramExists('weekday'))) {
895
+ params.specificity = u.unit;
896
+ return false;
897
+ } else if(reset && u.unit !== 'week' && (!isDay || !paramExists('week'))) {
898
+ // Days are relative to months, not weeks, so don't reset if a week exists.
899
+ callDateMethod(d, 'set', utc, u.method, (isDay ? 1 : 0));
900
+ }
901
+ });
902
+
903
+ // Now actually set or advance the date in order, higher units first.
904
+ DateUnits.forEach(function(u,i) {
905
+ var unit = u.unit, method = u.method, higherUnit = DateUnits[i - 1], value;
906
+ value = getParam(unit)
907
+ if(isUndefined(value)) return;
908
+ if(canDisambiguate(u, higherUnit)) {
909
+ // Formats like "June" have an ambiguous year. If no preference is stated, this
910
+ // is fine as "June of this year", however in a future context, this would mean
911
+ // "the next June", which may be either this year or next year. If we have an
912
+ // ambiguity *and* a preference for resolving it, then advance or rewind the
913
+ // higher order as necessary. Note that weekdays are handled differently below.
914
+ var current = callDateMethod(new date, 'get', utc, u.method);
915
+ if(current >= value === (prefer === 1)) {
916
+ d[higherUnit.addMethod](prefer);
917
+ }
918
+ }
919
+ if(advance) {
920
+ if(unit === 'week') {
921
+ value = (params['day'] || 0) + (value * 7);
922
+ method = 'Date';
923
+ }
924
+ value = (value * advance) + callDateMethod(d, 'get', false, method);
925
+ } else if(unit === 'month' && paramExists('day')) {
926
+ // When setting the month, there is a chance that we will traverse into a new month.
927
+ // This happens in DST shifts, for example June 1st DST jumping to January 1st
928
+ // (non-DST) will have a shift of -1:00 which will traverse into the previous year.
929
+ // Prevent this by proactively setting the day when we know it will be set again anyway.
930
+ // It can also happen when there are not enough days in the target month. This second
931
+ // situation is identical to checkMonthTraversal below, however when we are advancing
932
+ // we want to reset the date to "the last date in the target month". In the case of
933
+ // DST shifts, however, we want to avoid the "edges" of months as that is where this
934
+ // unintended traversal can happen. This is the reason for the different handling of
935
+ // two similar but slightly different situations.
936
+ //
937
+ // TL;DR This method avoids the edges of a month IF not advancing and the date is going
938
+ // to be set anyway, while checkMonthTraversal resets the date to the last day if advancing.
939
+ //
940
+ d.setDate(15);
941
+ }
942
+ callDateMethod(d, 'set', utc, method, value);
943
+ if(advance && unit === 'month') {
944
+ checkMonthTraversal(d, value);
945
+ }
946
+ });
947
+
948
+ // If a weekday is included in the params, set it ahead of time and set the params
949
+ // to reflect the updated date so that resetting works properly.
950
+ if(!advance && !paramExists('day') && paramExists('weekday')) {
951
+ var weekday = getParam('weekday'), isAhead, futurePreferred;
952
+ if(isDefined(prefer)) {
953
+ // If there is a preference as to whether this weekday is in the future,
954
+ // then add an offset as needed. NOTE: Was previously doing something much
955
+ // more one-liner-hipster here, but it made Opera choke (order of operations
956
+ // bug??) ... better to be more explicit here anyway.
957
+ isAhead = callDateMethod(d, 'get', utc, 'Day') - (weekday % 7) >= 0;
958
+ futurePreferred = prefer === 1;
959
+ if(isAhead === futurePreferred) {
960
+ weekday += prefer * 7;
961
+ }
962
+ }
963
+ callDateMethod(d, 'set', utc, 'Weekday', weekday)
964
+ }
965
+ return d;
966
+ }
967
+
968
+ function callDateMethod(d, prefix, utc, method, value) {
969
+ return d[prefix + (utc ? 'UTC' : '') + method](value);
970
+ }
971
+
972
+ // The ISO format allows times strung together without a demarcating ":", so make sure
973
+ // that these markers are now optional.
974
+ function prepareTime(format, loc, iso) {
975
+ var timeSuffixMapping = {'h':0,'m':1,'s':2}, add;
976
+ loc = loc || English;
977
+ return format.replace(/{([a-z])}/g, function(full, token) {
978
+ var separators = [],
979
+ isHours = token === 'h',
980
+ tokenIsRequired = isHours && !iso;
981
+ if(token === 't') {
982
+ return loc['ampm'].join('|');
983
+ } else {
984
+ if(isHours) {
985
+ separators.push(':');
986
+ }
987
+ if(add = loc['timeSuffixes'][timeSuffixMapping[token]]) {
988
+ separators.push(add + '\\s*');
989
+ }
990
+ return separators.length === 0 ? '' : '(?:' + separators.join('|') + ')' + (tokenIsRequired ? '' : '?');
991
+ }
992
+ });
993
+ }
994
+
995
+
996
+ // If the month is being set, then we don't want to accidentally
997
+ // traverse into a new month just because the target month doesn't have enough
998
+ // days. In other words, "5 months ago" from July 30th is still February, even
999
+ // though there is no February 30th, so it will of necessity be February 28th
1000
+ // (or 29th in the case of a leap year).
1001
+
1002
+ function checkMonthTraversal(date, targetMonth) {
1003
+ if(targetMonth < 0) targetMonth += 12;
1004
+ if(targetMonth % 12 != date.getMonth()) {
1005
+ date.setDate(0);
1006
+ }
1007
+ }
1008
+
1009
+ function createDate(args, prefer) {
1010
+ var f;
1011
+ if(isNumber(args[1])) {
1012
+ // If the second argument is a number, then we have an enumerated constructor type as in "new Date(2003, 2, 12);"
1013
+ f = collectDateArguments(args)[0];
1014
+ } else {
1015
+ f = args[0];
1016
+ }
1017
+ return getExtendedDate(f, args[1], prefer).date;
1018
+ }
1019
+
1020
+ function buildDateUnits() {
1021
+ DateUnitsReversed = DateUnits.concat().reverse();
1022
+ DateArgumentUnits = DateUnits.concat();
1023
+ DateArgumentUnits.splice(2,1);
1024
+ }
1025
+
1026
+
1027
+ /***
1028
+ * @method [units]Since([d], [locale] = currentLocale)
1029
+ * @returns Number
1030
+ * @short Returns the time since [d] in the appropriate unit.
1031
+ * @extra [d] will accept a date object, timestamp, or text format. If not specified, [d] is assumed to be now. [locale] can be passed to specify the locale that the date is in. %[unit]Ago% is provided as an alias to make this more readable when [d] is assumed to be the current date. For more see @date_format.
1032
+ *
1033
+ * @set
1034
+ * millisecondsSince
1035
+ * secondsSince
1036
+ * minutesSince
1037
+ * hoursSince
1038
+ * daysSince
1039
+ * weeksSince
1040
+ * monthsSince
1041
+ * yearsSince
1042
+ *
1043
+ * @example
1044
+ *
1045
+ * Date.create().millisecondsSince('1 hour ago') -> 3,600,000
1046
+ * Date.create().daysSince('1 week ago') -> 7
1047
+ * Date.create().yearsSince('15 years ago') -> 15
1048
+ * Date.create('15 years ago').yearsAgo() -> 15
1049
+ *
1050
+ ***
1051
+ * @method [units]Ago()
1052
+ * @returns Number
1053
+ * @short Returns the time ago in the appropriate unit.
1054
+ *
1055
+ * @set
1056
+ * millisecondsAgo
1057
+ * secondsAgo
1058
+ * minutesAgo
1059
+ * hoursAgo
1060
+ * daysAgo
1061
+ * weeksAgo
1062
+ * monthsAgo
1063
+ * yearsAgo
1064
+ *
1065
+ * @example
1066
+ *
1067
+ * Date.create('last year').millisecondsAgo() -> 3,600,000
1068
+ * Date.create('last year').daysAgo() -> 7
1069
+ * Date.create('last year').yearsAgo() -> 15
1070
+ *
1071
+ ***
1072
+ * @method [units]Until([d], [locale] = currentLocale)
1073
+ * @returns Number
1074
+ * @short Returns the time until [d] in the appropriate unit.
1075
+ * @extra [d] will accept a date object, timestamp, or text format. If not specified, [d] is assumed to be now. [locale] can be passed to specify the locale that the date is in. %[unit]FromNow% is provided as an alias to make this more readable when [d] is assumed to be the current date. For more see @date_format.
1076
+ *
1077
+ * @set
1078
+ * millisecondsUntil
1079
+ * secondsUntil
1080
+ * minutesUntil
1081
+ * hoursUntil
1082
+ * daysUntil
1083
+ * weeksUntil
1084
+ * monthsUntil
1085
+ * yearsUntil
1086
+ *
1087
+ * @example
1088
+ *
1089
+ * Date.create().millisecondsUntil('1 hour from now') -> 3,600,000
1090
+ * Date.create().daysUntil('1 week from now') -> 7
1091
+ * Date.create().yearsUntil('15 years from now') -> 15
1092
+ * Date.create('15 years from now').yearsFromNow() -> 15
1093
+ *
1094
+ ***
1095
+ * @method [units]FromNow()
1096
+ * @returns Number
1097
+ * @short Returns the time from now in the appropriate unit.
1098
+ *
1099
+ * @set
1100
+ * millisecondsFromNow
1101
+ * secondsFromNow
1102
+ * minutesFromNow
1103
+ * hoursFromNow
1104
+ * daysFromNow
1105
+ * weeksFromNow
1106
+ * monthsFromNow
1107
+ * yearsFromNow
1108
+ *
1109
+ * @example
1110
+ *
1111
+ * Date.create('next year').millisecondsFromNow() -> 3,600,000
1112
+ * Date.create('next year').daysFromNow() -> 7
1113
+ * Date.create('next year').yearsFromNow() -> 15
1114
+ *
1115
+ ***
1116
+ * @method add[Units](<num>, [reset] = false)
1117
+ * @returns Date
1118
+ * @short Adds <num> of the unit to the date. If [reset] is true, all lower units will be reset.
1119
+ * @extra Note that "months" is ambiguous as a unit of time. If the target date falls on a day that does not exist (ie. August 31 -> February 31), the date will be shifted to the last day of the month. Don't use %addMonths% if you need precision.
1120
+ *
1121
+ * @set
1122
+ * addMilliseconds
1123
+ * addSeconds
1124
+ * addMinutes
1125
+ * addHours
1126
+ * addDays
1127
+ * addWeeks
1128
+ * addMonths
1129
+ * addYears
1130
+ *
1131
+ * @example
1132
+ *
1133
+ * Date.create().addMilliseconds(5) -> current time + 5 milliseconds
1134
+ * Date.create().addDays(5) -> current time + 5 days
1135
+ * Date.create().addYears(5) -> current time + 5 years
1136
+ *
1137
+ ***
1138
+ * @method isLast[Unit]()
1139
+ * @returns Boolean
1140
+ * @short Returns true if the date is last week/month/year.
1141
+ *
1142
+ * @set
1143
+ * isLastWeek
1144
+ * isLastMonth
1145
+ * isLastYear
1146
+ *
1147
+ * @example
1148
+ *
1149
+ * Date.create('yesterday').isLastWeek() -> true or false?
1150
+ * Date.create('yesterday').isLastMonth() -> probably not...
1151
+ * Date.create('yesterday').isLastYear() -> even less likely...
1152
+ *
1153
+ ***
1154
+ * @method isThis[Unit]()
1155
+ * @returns Boolean
1156
+ * @short Returns true if the date is this week/month/year.
1157
+ *
1158
+ * @set
1159
+ * isThisWeek
1160
+ * isThisMonth
1161
+ * isThisYear
1162
+ *
1163
+ * @example
1164
+ *
1165
+ * Date.create('tomorrow').isThisWeek() -> true or false?
1166
+ * Date.create('tomorrow').isThisMonth() -> probably...
1167
+ * Date.create('tomorrow').isThisYear() -> signs point to yes...
1168
+ *
1169
+ ***
1170
+ * @method isNext[Unit]()
1171
+ * @returns Boolean
1172
+ * @short Returns true if the date is next week/month/year.
1173
+ *
1174
+ * @set
1175
+ * isNextWeek
1176
+ * isNextMonth
1177
+ * isNextYear
1178
+ *
1179
+ * @example
1180
+ *
1181
+ * Date.create('tomorrow').isNextWeek() -> true or false?
1182
+ * Date.create('tomorrow').isNextMonth() -> probably not...
1183
+ * Date.create('tomorrow').isNextYear() -> even less likely...
1184
+ *
1185
+ ***
1186
+ * @method beginningOf[Unit]()
1187
+ * @returns Date
1188
+ * @short Sets the date to the beginning of the appropriate unit.
1189
+ *
1190
+ * @set
1191
+ * beginningOfDay
1192
+ * beginningOfWeek
1193
+ * beginningOfMonth
1194
+ * beginningOfYear
1195
+ *
1196
+ * @example
1197
+ *
1198
+ * Date.create().beginningOfDay() -> the beginning of today (resets the time)
1199
+ * Date.create().beginningOfWeek() -> the beginning of the week
1200
+ * Date.create().beginningOfMonth() -> the beginning of the month
1201
+ * Date.create().beginningOfYear() -> the beginning of the year
1202
+ *
1203
+ ***
1204
+ * @method endOf[Unit]()
1205
+ * @returns Date
1206
+ * @short Sets the date to the end of the appropriate unit.
1207
+ *
1208
+ * @set
1209
+ * endOfDay
1210
+ * endOfWeek
1211
+ * endOfMonth
1212
+ * endOfYear
1213
+ *
1214
+ * @example
1215
+ *
1216
+ * Date.create().endOfDay() -> the end of today (sets the time to 23:59:59.999)
1217
+ * Date.create().endOfWeek() -> the end of the week
1218
+ * Date.create().endOfMonth() -> the end of the month
1219
+ * Date.create().endOfYear() -> the end of the year
1220
+ *
1221
+ ***/
1222
+
1223
+ function buildDateMethods() {
1224
+ extendSimilar(date, true, false, DateUnits, function(methods, u, i) {
1225
+ var unit = u.unit, caps = simpleCapitalize(unit), multiplier = u.multiplier(), since, until;
1226
+ u.addMethod = 'add' + caps + 's';
1227
+ since = function(f, localeCode) {
1228
+ return round((this.getTime() - date.create(f, localeCode).getTime()) / multiplier);
1229
+ };
1230
+ until = function(f, localeCode) {
1231
+ return round((date.create(f, localeCode).getTime() - this.getTime()) / multiplier);
1232
+ };
1233
+ methods[unit+'sAgo'] = until;
1234
+ methods[unit+'sUntil'] = until;
1235
+ methods[unit+'sSince'] = since;
1236
+ methods[unit+'sFromNow'] = since;
1237
+ methods[u.addMethod] = function(num, reset) {
1238
+ var set = {};
1239
+ set[unit] = num;
1240
+ return this.advance(set, reset);
1241
+ };
1242
+ buildNumberToDateAlias(u, multiplier);
1243
+ if(i < 3) {
1244
+ ['Last','This','Next'].forEach(function(shift) {
1245
+ methods['is' + shift + caps] = function() {
1246
+ return this.is(shift + ' ' + unit);
1247
+ };
1248
+ });
1249
+ }
1250
+ if(i < 4) {
1251
+ methods['beginningOf' + caps] = function() {
1252
+ var set = {};
1253
+ switch(unit) {
1254
+ case 'year': set['year'] = this.getFullYear(); break;
1255
+ case 'month': set['month'] = this.getMonth(); break;
1256
+ case 'day': set['day'] = this.getDate(); break;
1257
+ case 'week': set['weekday'] = 0; break;
1258
+ }
1259
+ return this.set(set, true);
1260
+ };
1261
+ methods['endOf' + caps] = function() {
1262
+ var set = { 'hours': 23, 'minutes': 59, 'seconds': 59, 'milliseconds': 999 };
1263
+ switch(unit) {
1264
+ case 'year': set['month'] = 11; set['day'] = 31; break;
1265
+ case 'month': set['day'] = this.daysInMonth(); break;
1266
+ case 'week': set['weekday'] = 6; break;
1267
+ }
1268
+ return this.set(set, true);
1269
+ };
1270
+ }
1271
+ });
1272
+ }
1273
+
1274
+ function buildCoreInputFormats() {
1275
+ English.addFormat('([+-])?(\\d{4,4})[-.]?{full_month}[-.]?(\\d{1,2})?', true, ['year_sign','year','month','date'], false, true);
1276
+ English.addFormat('(\\d{1,2})[-.\\/]{full_month}(?:[-.\\/](\\d{2,4}))?', true, ['date','month','year'], true);
1277
+ English.addFormat('{full_month}[-.](\\d{4,4})', false, ['month','year']);
1278
+ English.addFormat('\\/Date\\((\\d+(?:\\+\\d{4,4})?)\\)\\/', false, ['timestamp'])
1279
+ English.addFormat(prepareTime(RequiredTime, English), false, TimeFormat)
1280
+
1281
+ // When a new locale is initialized it will have the CoreDateFormats initialized by default.
1282
+ // From there, adding new formats will push them in front of the previous ones, so the core
1283
+ // formats will be the last to be reached. However, the core formats themselves have English
1284
+ // months in them, which means that English needs to first be initialized and creates a race
1285
+ // condition. I'm getting around this here by adding these generalized formats in the order
1286
+ // specific -> general, which will mean they will be added to the English localization in
1287
+ // general -> specific order, then chopping them off the front and reversing to get the correct
1288
+ // order. Note that there are 7 formats as 2 have times which adds a front and a back format.
1289
+ CoreDateFormats = English.compiledFormats.slice(0,7).reverse();
1290
+ English.compiledFormats = English.compiledFormats.slice(7).concat(CoreDateFormats);
1291
+ }
1292
+
1293
+ function buildDateOutputShortcuts() {
1294
+ extendSimilar(date, true, false, 'short,long,full', function(methods, name) {
1295
+ methods[name] = function(localeCode) {
1296
+ return formatDate(this, name, false, localeCode);
1297
+ }
1298
+ });
1299
+ }
1300
+
1301
+ function buildAsianDigits() {
1302
+ KanjiDigits.split('').forEach(function(digit, value) {
1303
+ var holder;
1304
+ if(value > 9) {
1305
+ value = math.pow(10, value - 9);
1306
+ }
1307
+ AsianDigitMap[digit] = value;
1308
+ });
1309
+ FullWidthDigits.split('').forEach(function(digit, value) {
1310
+ AsianDigitMap[digit] = value;
1311
+ });
1312
+ // Kanji numerals may also be included in phrases which are text-based rather
1313
+ // than actual numbers such as Chinese weekdays (上周三), and "the day before
1314
+ // yesterday" (一昨日) in Japanese, so don't match these.
1315
+ AsianDigitReg = regexp('([期週周])?([' + KanjiDigits + FullWidthDigits + ']+)(?!昨)', 'g');
1316
+ }
1317
+
1318
+ /***
1319
+ * @method is[Day]()
1320
+ * @returns Boolean
1321
+ * @short Returns true if the date falls on that day.
1322
+ * @extra Also available: %isYesterday%, %isToday%, %isTomorrow%, %isWeekday%, and %isWeekend%.
1323
+ *
1324
+ * @set
1325
+ * isToday
1326
+ * isYesterday
1327
+ * isTomorrow
1328
+ * isWeekday
1329
+ * isWeekend
1330
+ * isSunday
1331
+ * isMonday
1332
+ * isTuesday
1333
+ * isWednesday
1334
+ * isThursday
1335
+ * isFriday
1336
+ * isSaturday
1337
+ *
1338
+ * @example
1339
+ *
1340
+ * Date.create('tomorrow').isToday() -> false
1341
+ * Date.create('thursday').isTomorrow() -> ?
1342
+ * Date.create('yesterday').isWednesday() -> ?
1343
+ * Date.create('today').isWeekend() -> ?
1344
+ *
1345
+ ***
1346
+ * @method isFuture()
1347
+ * @returns Boolean
1348
+ * @short Returns true if the date is in the future.
1349
+ * @example
1350
+ *
1351
+ * Date.create('next week').isFuture() -> true
1352
+ * Date.create('last week').isFuture() -> false
1353
+ *
1354
+ ***
1355
+ * @method isPast()
1356
+ * @returns Boolean
1357
+ * @short Returns true if the date is in the past.
1358
+ * @example
1359
+ *
1360
+ * Date.create('last week').isPast() -> true
1361
+ * Date.create('next week').isPast() -> false
1362
+ *
1363
+ ***/
1364
+ function buildRelativeAliases() {
1365
+ var special = 'today,yesterday,tomorrow,weekday,weekend,future,past'.split(',');
1366
+ var weekdays = English['weekdays'].slice(0,7);
1367
+ var months = English['months'].slice(0,12);
1368
+ extendSimilar(date, true, false, special.concat(weekdays).concat(months), function(methods, name) {
1369
+ methods['is'+ simpleCapitalize(name)] = function() {
1370
+ return this.is(name);
1371
+ };
1372
+ });
1373
+ }
1374
+
1375
+ function setDateProperties() {
1376
+ date.extend({
1377
+ 'RFC1123': '{Dow}, {dd} {Mon} {yyyy} {HH}:{mm}:{ss} {tz}',
1378
+ 'RFC1036': '{Weekday}, {dd}-{Mon}-{yy} {HH}:{mm}:{ss} {tz}',
1379
+ 'ISO8601_DATE': '{yyyy}-{MM}-{dd}',
1380
+ 'ISO8601_DATETIME': '{yyyy}-{MM}-{dd}T{HH}:{mm}:{ss}.{fff}{isotz}'
1381
+ }, false, false);
1382
+ }
1383
+
1384
+ function buildDate() {
1385
+ buildDateUnits();
1386
+ buildDateMethods();
1387
+ buildCoreInputFormats();
1388
+ buildDateOutputShortcuts();
1389
+ buildAsianDigits();
1390
+ buildRelativeAliases();
1391
+ setDateProperties();
1392
+ }
1393
+
1394
+
1395
+ date.extend({
1396
+
1397
+ /***
1398
+ * @method Date.create(<d>, [locale] = currentLocale)
1399
+ * @returns Date
1400
+ * @short Alternate Date constructor which understands many different text formats, a timestamp, or another date.
1401
+ * @extra If no argument is given, date is assumed to be now. %Date.create% additionally can accept enumerated parameters as with the standard date constructor. [locale] can be passed to specify the locale that the date is in. When unspecified, the current locale (default is English) is assumed. For more information, see @date_format.
1402
+ * @example
1403
+ *
1404
+ * Date.create('July') -> July of this year
1405
+ * Date.create('1776') -> 1776
1406
+ * Date.create('today') -> today
1407
+ * Date.create('wednesday') -> This wednesday
1408
+ * Date.create('next friday') -> Next friday
1409
+ * Date.create('July 4, 1776') -> July 4, 1776
1410
+ * Date.create(-446806800000) -> November 5, 1955
1411
+ * Date.create(1776, 6, 4) -> July 4, 1776
1412
+ * Date.create('1776年07月04日', 'ja') -> July 4, 1776
1413
+ *
1414
+ ***/
1415
+ 'create': function() {
1416
+ return createDate(arguments);
1417
+ },
1418
+
1419
+ /***
1420
+ * @method Date.past(<d>, [locale] = currentLocale)
1421
+ * @returns Date
1422
+ * @short Alternate form of %Date.create% with any ambiguity assumed to be the past.
1423
+ * @extra For example %"Sunday"% can be either "the Sunday coming up" or "the Sunday last" depending on context. Note that dates explicitly in the future ("next Sunday") will remain in the future. This method simply provides a hint when ambiguity exists.
1424
+ * @example
1425
+ *
1426
+ * Date.past('July') -> July of this year or last depending on the current month
1427
+ * Date.past('Wednesday') -> This wednesday or last depending on the current weekday
1428
+ *
1429
+ ***/
1430
+ 'past': function() {
1431
+ return createDate(arguments, -1);
1432
+ },
1433
+
1434
+ /***
1435
+ * @method Date.future(<d>, [locale] = currentLocale)
1436
+ * @returns Date
1437
+ * @short Alternate form of %Date.create% with any ambiguity assumed to be the future.
1438
+ * @extra For example %"Sunday"% can be either "the Sunday coming up" or "the Sunday last" depending on context. Note that dates explicitly in the past ("last Sunday") will remain in the past. This method simply provides a hint when ambiguity exists.
1439
+ * @example
1440
+ *
1441
+ * Date.future('July') -> July of this year or next depending on the current month
1442
+ * Date.future('Wednesday') -> This wednesday or next depending on the current weekday
1443
+ *
1444
+ ***/
1445
+ 'future': function() {
1446
+ return createDate(arguments, 1);
1447
+ },
1448
+
1449
+ /***
1450
+ * @method Date.addLocale(<code>, <set>)
1451
+ * @returns Locale
1452
+ * @short Adds a locale <set> to the locales understood by Sugar.
1453
+ * @extra For more see @date_format.
1454
+ *
1455
+ ***/
1456
+ 'addLocale': function(localeCode, set) {
1457
+ return setLocalization(localeCode, set);
1458
+ },
1459
+
1460
+ /***
1461
+ * @method Date.setLocale(<code>)
1462
+ * @returns Locale
1463
+ * @short Sets the current locale to be used with dates.
1464
+ * @extra Sugar has support for 13 locales that are available through the "Date Locales" package. In addition you can define a new locale with %Date.addLocale%. For more see @date_format.
1465
+ *
1466
+ ***/
1467
+ 'setLocale': function(localeCode, set) {
1468
+ var loc = getLocalization(localeCode, false);
1469
+ CurrentLocalization = loc;
1470
+ // The code is allowed to be more specific than the codes which are required:
1471
+ // i.e. zh-CN or en-US. Currently this only affects US date variants such as 8/10/2000.
1472
+ if(localeCode && localeCode != loc['code']) {
1473
+ loc['code'] = localeCode;
1474
+ }
1475
+ return loc;
1476
+ },
1477
+
1478
+ /***
1479
+ * @method Date.getLocale([code] = current)
1480
+ * @returns Locale
1481
+ * @short Gets the locale for the given code, or the current locale.
1482
+ * @extra The resulting locale object can be manipulated to provide more control over date localizations. For more about locales, see @date_format.
1483
+ *
1484
+ ***/
1485
+ 'getLocale': function(localeCode) {
1486
+ return !localeCode ? CurrentLocalization : getLocalization(localeCode, false);
1487
+ },
1488
+
1489
+ /**
1490
+ * @method Date.addFormat(<format>, <match>, [code] = null)
1491
+ * @returns Nothing
1492
+ * @short Manually adds a new date input format.
1493
+ * @extra This method allows fine grained control for alternate formats. <format> is a string that can have regex tokens inside. <match> is an array of the tokens that each regex capturing group will map to, for example %year%, %date%, etc. For more, see @date_format.
1494
+ *
1495
+ **/
1496
+ 'addFormat': function(format, match, localeCode) {
1497
+ addDateInputFormat(getLocalization(localeCode), format, match);
1498
+ }
1499
+
1500
+ }, false, false);
1501
+
1502
+ date.extend({
1503
+
1504
+ /***
1505
+ * @method set(<set>, [reset] = false)
1506
+ * @returns Date
1507
+ * @short Sets the date object.
1508
+ * @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.
1509
+ *
1510
+ * @set
1511
+ * setUTC
1512
+ *
1513
+ * @example
1514
+ *
1515
+ * new Date().set({ year: 2011, month: 11, day: 31 }) -> December 31, 2011
1516
+ * new Date().set(2011, 11, 31) -> December 31, 2011
1517
+ * new Date().set(86400000) -> 1 day after Jan 1, 1970
1518
+ * new Date().set({ year: 2004, month: 6 }, true) -> June 1, 2004, 00:00:00.000
1519
+ *
1520
+ ***/
1521
+ 'set': function() {
1522
+ var args = collectDateArguments(arguments);
1523
+ return updateDate(this, args[0], args[1])
1524
+ },
1525
+
1526
+ 'setUTC': function() {
1527
+ var args = collectDateArguments(arguments);
1528
+ return updateDate(this, args[0], args[1], true)
1529
+ },
1530
+
1531
+ /***
1532
+ * @method setWeekday()
1533
+ * @returns Nothing
1534
+ * @short Sets the weekday of the date.
1535
+ * @extra %setUTCWeekday% sets according to universal time.
1536
+ *
1537
+ * @set
1538
+ * setUTCWeekday
1539
+ *
1540
+ * @example
1541
+ *
1542
+ * d = new Date(); d.setWeekday(1); d; -> Monday of this week
1543
+ * d = new Date(); d.setWeekday(6); d; -> Saturday of this week
1544
+ *
1545
+ ***/
1546
+ 'setWeekday': function(dow) {
1547
+ if(isUndefined(dow)) return;
1548
+ this.setDate(this.getDate() + dow - this.getDay());
1549
+ },
1550
+
1551
+ 'setUTCWeekday': function(dow) {
1552
+ if(isUndefined(dow)) return;
1553
+ this.setDate(this.getUTCDate() + dow - this.getDay());
1554
+ },
1555
+
1556
+ /***
1557
+ * @method setWeek()
1558
+ * @returns Nothing
1559
+ * @short Sets the week (of the year).
1560
+ * @extra %setUTCWeek% sets according to universal time.
1561
+ *
1562
+ * @set
1563
+ * setUTCWeek
1564
+ *
1565
+ * @example
1566
+ *
1567
+ * d = new Date(); d.setWeek(15); d; -> 15th week of the year
1568
+ *
1569
+ ***/
1570
+ 'setWeek': function(week) {
1571
+ if(isUndefined(week)) return;
1572
+ var date = this.getDate();
1573
+ this.setMonth(0);
1574
+ this.setDate((week * 7) + 1);
1575
+ },
1576
+
1577
+ 'setUTCWeek': function(week) {
1578
+ if(isUndefined(week)) return;
1579
+ var date = this.getUTCDate();
1580
+ this.setMonth(0);
1581
+ this.setUTCDate((week * 7) + 1);
1582
+ },
1583
+
1584
+ /***
1585
+ * @method getWeek()
1586
+ * @returns Number
1587
+ * @short Gets the date's week (of the year).
1588
+ * @extra %getUTCWeek% gets the week according to universal time.
1589
+ *
1590
+ * @set
1591
+ * getUTCWeek
1592
+ *
1593
+ * @example
1594
+ *
1595
+ * new Date().getWeek() -> today's week of the year
1596
+ * new Date().getUTCWeek() -> today's week of the year
1597
+ *
1598
+ ***/
1599
+ 'getWeek': function() {
1600
+ return getWeekNumber(this);
1601
+ },
1602
+
1603
+ 'getUTCWeek': function() {
1604
+ return getWeekNumber(this.toUTC());
1605
+ },
1606
+
1607
+ /***
1608
+ * @method getUTCOffset([iso])
1609
+ * @returns String
1610
+ * @short Returns a string representation of the offset from UTC time. If [iso] is true the offset will be in ISO8601 format.
1611
+ * @example
1612
+ *
1613
+ * new Date().getUTCOffset() -> "+0900"
1614
+ * new Date().getUTCOffset(true) -> "+09:00"
1615
+ *
1616
+ ***/
1617
+ 'getUTCOffset': function(iso) {
1618
+ var offset = this.utc ? 0 : this.getTimezoneOffset();
1619
+ var colon = iso === true ? ':' : '';
1620
+ if(!offset && iso) return 'Z';
1621
+ return padNumber(round(-offset / 60), 2, true) + colon + padNumber(offset % 60, 2);
1622
+ },
1623
+
1624
+ /***
1625
+ * @method toUTC()
1626
+ * @returns Date
1627
+ * @short Converts the date to UTC time, effectively subtracting the timezone offset.
1628
+ * @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, and %isUTC% will return %true%. Once a date is set to UTC the only way to unset is the %clone% method.
1629
+ * @example
1630
+ *
1631
+ * new Date().toUTC() -> current time in UTC
1632
+ *
1633
+ ***/
1634
+ 'toUTC': function() {
1635
+ if(this.utc) return this;
1636
+ var d = this.clone().addMinutes(this.getTimezoneOffset());
1637
+ d.utc = true;
1638
+ return d;
1639
+ },
1640
+
1641
+ /***
1642
+ * @method isUTC()
1643
+ * @returns Boolean
1644
+ * @short Returns true if the date has no timezone offset.
1645
+ * @extra This will also return true for a date that has had %toUTC% called on it. This is intended to help approximate shifting timezones which is not possible in client-side Javascript. Note that the native method %getTimezoneOffset% will always report the same thing, even if %isUTC% becomes true.
1646
+ * @example
1647
+ *
1648
+ * new Date().isUTC() -> true or false?
1649
+ * new Date().toUTC().isUTC() -> true
1650
+ *
1651
+ ***/
1652
+ 'isUTC': function() {
1653
+ return this.utc || this.getTimezoneOffset() === 0;
1654
+ },
1655
+
1656
+ /***
1657
+ * @method advance(<set>, [reset] = false)
1658
+ * @returns Date
1659
+ * @short Sets the date forward.
1660
+ * @extra This method can accept multiple formats including an object, a string in the format %3 days%, a single number as milliseconds, or enumerated parameters (as with the Date constructor). If [reset] is %true%, any units more specific than those passed will be reset. For more see @date_format.
1661
+ * @example
1662
+ *
1663
+ * new Date().advance({ year: 2 }) -> 2 years in the future
1664
+ * new Date().advance('2 days') -> 2 days in the future
1665
+ * new Date().advance(0, 2, 3) -> 2 months 3 days in the future
1666
+ * new Date().advance(86400000) -> 1 day in the future
1667
+ *
1668
+ ***/
1669
+ 'advance': function() {
1670
+ var args = collectDateArguments(arguments, true);
1671
+ return updateDate(this, args[0], args[1], false, 1);
1672
+ },
1673
+
1674
+ /***
1675
+ * @method rewind(<set>, [reset] = false)
1676
+ * @returns Date
1677
+ * @short Sets the date back.
1678
+ * @extra This method can accept multiple formats including a single number as a timestamp, an object, or enumerated parameters (as with the Date constructor). If [reset] is %true%, any units more specific than those passed will be reset. For more see @date_format.
1679
+ * @example
1680
+ *
1681
+ * new Date().rewind({ year: 2 }) -> 2 years in the past
1682
+ * new Date().rewind(0, 2, 3) -> 2 months 3 days in the past
1683
+ * new Date().rewind(86400000) -> 1 day in the past
1684
+ *
1685
+ ***/
1686
+ 'rewind': function() {
1687
+ var args = collectDateArguments(arguments, true);
1688
+ return updateDate(this, args[0], args[1], false, -1);
1689
+ },
1690
+
1691
+ /***
1692
+ * @method isValid()
1693
+ * @returns Boolean
1694
+ * @short Returns true if the date is valid.
1695
+ * @example
1696
+ *
1697
+ * new Date().isValid() -> true
1698
+ * new Date('flexor').isValid() -> false
1699
+ *
1700
+ ***/
1701
+ 'isValid': function() {
1702
+ return !isNaN(this.getTime());
1703
+ },
1704
+
1705
+ /***
1706
+ * @method isAfter(<d>, [margin] = 0)
1707
+ * @returns Boolean
1708
+ * @short Returns true if the date is after the <d>.
1709
+ * @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.
1710
+ * @example
1711
+ *
1712
+ * new Date().isAfter('tomorrow') -> false
1713
+ * new Date().isAfter('yesterday') -> true
1714
+ *
1715
+ ***/
1716
+ 'isAfter': function(d, margin) {
1717
+ return this.getTime() > date.create(d).getTime() - (margin || 0);
1718
+ },
1719
+
1720
+ /***
1721
+ * @method isBefore(<d>, [margin] = 0)
1722
+ * @returns Boolean
1723
+ * @short Returns true if the date is before <d>.
1724
+ * @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.
1725
+ * @example
1726
+ *
1727
+ * new Date().isBefore('tomorrow') -> true
1728
+ * new Date().isBefore('yesterday') -> false
1729
+ *
1730
+ ***/
1731
+ 'isBefore': function(d, margin) {
1732
+ return this.getTime() < date.create(d).getTime() + (margin || 0);
1733
+ },
1734
+
1735
+ /***
1736
+ * @method isBetween(<d1>, <d2>, [margin] = 0)
1737
+ * @returns Boolean
1738
+ * @short Returns true if the date falls between <d1> and <d2>.
1739
+ * @extra [margin] is to allow extra margin of error (in ms). <d1> and <d2> will accept a date object, timestamp, or text format. If not specified, they are assumed to be now. See @date_format for more information.
1740
+ * @example
1741
+ *
1742
+ * new Date().isBetween('yesterday', 'tomorrow') -> true
1743
+ * new Date().isBetween('last year', '2 years ago') -> false
1744
+ *
1745
+ ***/
1746
+ 'isBetween': function(d1, d2, margin) {
1747
+ var t = this.getTime();
1748
+ var t1 = date.create(d1).getTime();
1749
+ var t2 = date.create(d2).getTime();
1750
+ var lo = math.min(t1, t2);
1751
+ var hi = math.max(t1, t2);
1752
+ margin = margin || 0;
1753
+ return (lo - margin < t) && (hi + margin > t);
1754
+ },
1755
+
1756
+ /***
1757
+ * @method isLeapYear()
1758
+ * @returns Boolean
1759
+ * @short Returns true if the date is a leap year.
1760
+ * @example
1761
+ *
1762
+ * Date.create('2000').isLeapYear() -> true
1763
+ *
1764
+ ***/
1765
+ 'isLeapYear': function() {
1766
+ var year = this.getFullYear();
1767
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
1768
+ },
1769
+
1770
+ /***
1771
+ * @method daysInMonth()
1772
+ * @returns Number
1773
+ * @short Returns the number of days in the date's month.
1774
+ * @example
1775
+ *
1776
+ * Date.create('May').daysInMonth() -> 31
1777
+ * Date.create('February, 2000').daysInMonth() -> 29
1778
+ *
1779
+ ***/
1780
+ 'daysInMonth': function() {
1781
+ return 32 - new date(this.getFullYear(), this.getMonth(), 32).getDate();
1782
+ },
1783
+
1784
+ /***
1785
+ * @method format(<format>, [locale] = currentLocale)
1786
+ * @returns String
1787
+ * @short Formats and outputs the date.
1788
+ * @extra <format> can be a number of pre-determined formats or a string of tokens. Locale-specific formats are %short%, %long%, and %full% which have their own aliases and can be called with %date.short()%, etc. If <format> is not specified the %long% format is assumed. [locale] specifies a locale code to use (if not specified the current locale is used). See @date_format for more details.
1789
+ *
1790
+ * @set
1791
+ * short
1792
+ * long
1793
+ * full
1794
+ *
1795
+ * @example
1796
+ *
1797
+ * Date.create().format() -> ex. July 4, 2003
1798
+ * Date.create().format('{Weekday} {d} {Month}, {yyyy}') -> ex. Monday July 4, 2003
1799
+ * Date.create().format('{hh}:{mm}') -> ex. 15:57
1800
+ * Date.create().format('{12hr}:{mm}{tt}') -> ex. 3:57pm
1801
+ * Date.create().format(Date.ISO8601_DATETIME) -> ex. 2011-07-05 12:24:55.528Z
1802
+ * Date.create('last week').format('short', 'ja') -> ex. 先週
1803
+ * Date.create('yesterday').format(function(value,unit,ms,loc) {
1804
+ * // value = 1, unit = 3, ms = -86400000, loc = [current locale object]
1805
+ * }); -> ex. 1 day ago
1806
+ *
1807
+ ***/
1808
+ 'format': function(f, localeCode) {
1809
+ return formatDate(this, f, false, localeCode);
1810
+ },
1811
+
1812
+ /***
1813
+ * @method relative([fn], [locale] = currentLocale)
1814
+ * @returns String
1815
+ * @short Returns a relative date string offset to the current time.
1816
+ * @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.
1817
+ * @example
1818
+ *
1819
+ * Date.create('90 seconds ago').relative() -> 1 minute ago
1820
+ * Date.create('January').relative() -> ex. 5 months ago
1821
+ * Date.create('January').relative('ja') -> 3ヶ月前
1822
+ * Date.create('120 minutes ago').relative(function(val,unit,ms,loc) {
1823
+ * // value = 2, unit = 3, ms = -7200, loc = [current locale object]
1824
+ * }); -> ex. 5 months ago
1825
+ *
1826
+ ***/
1827
+ 'relative': function(f, localeCode) {
1828
+ if(isString(f)) {
1829
+ localeCode = f;
1830
+ f = null;
1831
+ }
1832
+ return formatDate(this, f, true, localeCode);
1833
+ },
1834
+
1835
+ /***
1836
+ * @method is(<d>, [margin] = 0)
1837
+ * @returns Boolean
1838
+ * @short Returns true if the date is <d>.
1839
+ * @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.
1840
+ * @example
1841
+ *
1842
+ * Date.create().is('July') -> true or false?
1843
+ * Date.create().is('1776') -> false
1844
+ * Date.create().is('today') -> true
1845
+ * Date.create().is('weekday') -> true or false?
1846
+ * Date.create().is('July 4, 1776') -> false
1847
+ * Date.create().is(-6106093200000) -> false
1848
+ * Date.create().is(new Date(1776, 6, 4)) -> false
1849
+ *
1850
+ ***/
1851
+ 'is': function(d, margin) {
1852
+ var tmp;
1853
+ if(!this.isValid()) return;
1854
+ if(isString(d)) {
1855
+ d = d.trim().toLowerCase();
1856
+ switch(true) {
1857
+ case d === 'future': return this.getTime() > new date().getTime();
1858
+ case d === 'past': return this.getTime() < new date().getTime();
1859
+ case d === 'weekday': return this.getDay() > 0 && this.getDay() < 6;
1860
+ case d === 'weekend': return this.getDay() === 0 || this.getDay() === 6;
1861
+ case (tmp = English['weekdays'].indexOf(d) % 7) > -1: return this.getDay() === tmp;
1862
+ case (tmp = English['months'].indexOf(d) % 12) > -1: return this.getMonth() === tmp;
1863
+ }
1864
+ }
1865
+ return compareDate(this, d, margin);
1866
+ },
1867
+
1868
+ /***
1869
+ * @method reset([unit] = 'hours')
1870
+ * @returns Date
1871
+ * @short Resets the unit passed and all smaller units. Default is "hours", effectively resetting the time.
1872
+ * @example
1873
+ *
1874
+ * Date.create().reset('day') -> Beginning of today
1875
+ * Date.create().reset('month') -> 1st of the month
1876
+ *
1877
+ ***/
1878
+ 'reset': function(unit) {
1879
+ var params = {}, recognized;
1880
+ unit = unit || 'hours';
1881
+ if(unit === 'date') unit = 'days';
1882
+ recognized = DateUnits.some(function(u) {
1883
+ return unit === u.unit || unit === u.unit + 's';
1884
+ });
1885
+ params[unit] = unit.match(/^days?/) ? 1 : 0;
1886
+ return recognized ? this.set(params, true) : this;
1887
+ },
1888
+
1889
+ /***
1890
+ * @method clone()
1891
+ * @returns Date
1892
+ * @short Clones the date.
1893
+ * @example
1894
+ *
1895
+ * Date.create().clone() -> Copy of now
1896
+ *
1897
+ ***/
1898
+ 'clone': function() {
1899
+ return new date(this.getTime());
1900
+ }
1901
+
1902
+ });
1903
+
1904
+
1905
+ // Instance aliases
1906
+ date.extend({
1907
+
1908
+ /***
1909
+ * @method iso()
1910
+ * @alias toISOString
1911
+ *
1912
+ ***/
1913
+ 'iso': function() {
1914
+ return this.toISOString();
1915
+ },
1916
+
1917
+ /***
1918
+ * @method getWeekday()
1919
+ * @returns Number
1920
+ * @short Alias for %getDay%.
1921
+ * @set
1922
+ * getUTCWeekday
1923
+ *
1924
+ * @example
1925
+ *
1926
+ + Date.create().getWeekday(); -> (ex.) 3
1927
+ + Date.create().getUTCWeekday(); -> (ex.) 3
1928
+ *
1929
+ ***/
1930
+ 'getWeekday': date.prototype.getDay,
1931
+ 'getUTCWeekday': date.prototype.getUTCDay
1932
+
1933
+ });
1934
+
1935
+
1936
+
1937
+ /***
1938
+ * Number module
1939
+ *
1940
+ ***/
1941
+
1942
+ /***
1943
+ * @method [unit]()
1944
+ * @returns Number
1945
+ * @short Takes the number as a corresponding unit of time and converts to milliseconds.
1946
+ * @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.
1947
+ *
1948
+ * @set
1949
+ * millisecond
1950
+ * milliseconds
1951
+ * second
1952
+ * seconds
1953
+ * minute
1954
+ * minutes
1955
+ * hour
1956
+ * hours
1957
+ * day
1958
+ * days
1959
+ * week
1960
+ * weeks
1961
+ * month
1962
+ * months
1963
+ * year
1964
+ * years
1965
+ *
1966
+ * @example
1967
+ *
1968
+ * (5).milliseconds() -> 5
1969
+ * (10).hours() -> 36000000
1970
+ * (1).day() -> 86400000
1971
+ *
1972
+ ***
1973
+ * @method [unit]Before([d], [locale] = currentLocale)
1974
+ * @returns Date
1975
+ * @short Returns a date that is <n> units before [d], where <n> is the number.
1976
+ * @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.
1977
+ *
1978
+ * @set
1979
+ * millisecondBefore
1980
+ * millisecondsBefore
1981
+ * secondBefore
1982
+ * secondsBefore
1983
+ * minuteBefore
1984
+ * minutesBefore
1985
+ * hourBefore
1986
+ * hoursBefore
1987
+ * dayBefore
1988
+ * daysBefore
1989
+ * weekBefore
1990
+ * weeksBefore
1991
+ * monthBefore
1992
+ * monthsBefore
1993
+ * yearBefore
1994
+ * yearsBefore
1995
+ *
1996
+ * @example
1997
+ *
1998
+ * (5).daysBefore('tuesday') -> 5 days before tuesday of this week
1999
+ * (1).yearBefore('January 23, 1997') -> January 23, 1996
2000
+ *
2001
+ ***
2002
+ * @method [unit]Ago()
2003
+ * @returns Date
2004
+ * @short Returns a date that is <n> units ago.
2005
+ * @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.
2006
+ *
2007
+ * @set
2008
+ * millisecondAgo
2009
+ * millisecondsAgo
2010
+ * secondAgo
2011
+ * secondsAgo
2012
+ * minuteAgo
2013
+ * minutesAgo
2014
+ * hourAgo
2015
+ * hoursAgo
2016
+ * dayAgo
2017
+ * daysAgo
2018
+ * weekAgo
2019
+ * weeksAgo
2020
+ * monthAgo
2021
+ * monthsAgo
2022
+ * yearAgo
2023
+ * yearsAgo
2024
+ *
2025
+ * @example
2026
+ *
2027
+ * (5).weeksAgo() -> 5 weeks ago
2028
+ * (1).yearAgo() -> January 23, 1996
2029
+ *
2030
+ ***
2031
+ * @method [unit]After([d], [locale] = currentLocale)
2032
+ * @returns Date
2033
+ * @short Returns a date <n> units after [d], where <n> is the number.
2034
+ * @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.
2035
+ *
2036
+ * @set
2037
+ * millisecondAfter
2038
+ * millisecondsAfter
2039
+ * secondAfter
2040
+ * secondsAfter
2041
+ * minuteAfter
2042
+ * minutesAfter
2043
+ * hourAfter
2044
+ * hoursAfter
2045
+ * dayAfter
2046
+ * daysAfter
2047
+ * weekAfter
2048
+ * weeksAfter
2049
+ * monthAfter
2050
+ * monthsAfter
2051
+ * yearAfter
2052
+ * yearsAfter
2053
+ *
2054
+ * @example
2055
+ *
2056
+ * (5).daysAfter('tuesday') -> 5 days after tuesday of this week
2057
+ * (1).yearAfter('January 23, 1997') -> January 23, 1998
2058
+ *
2059
+ ***
2060
+ * @method [unit]FromNow()
2061
+ * @returns Date
2062
+ * @short Returns a date <n> units from now.
2063
+ * @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.
2064
+ *
2065
+ * @set
2066
+ * millisecondFromNow
2067
+ * millisecondsFromNow
2068
+ * secondFromNow
2069
+ * secondsFromNow
2070
+ * minuteFromNow
2071
+ * minutesFromNow
2072
+ * hourFromNow
2073
+ * hoursFromNow
2074
+ * dayFromNow
2075
+ * daysFromNow
2076
+ * weekFromNow
2077
+ * weeksFromNow
2078
+ * monthFromNow
2079
+ * monthsFromNow
2080
+ * yearFromNow
2081
+ * yearsFromNow
2082
+ *
2083
+ * @example
2084
+ *
2085
+ * (5).weeksFromNow() -> 5 weeks ago
2086
+ * (1).yearFromNow() -> January 23, 1998
2087
+ *
2088
+ ***/
2089
+ function buildNumberToDateAlias(u, multiplier) {
2090
+ var unit = u.unit, methods = {};
2091
+ function base() { return round(this * multiplier); }
2092
+ function after() { return createDate(arguments)[u.addMethod](this); }
2093
+ function before() { return createDate(arguments)[u.addMethod](-this); }
2094
+ methods[unit] = base;
2095
+ methods[unit + 's'] = base;
2096
+ methods[unit + 'Before'] = before;
2097
+ methods[unit + 'sBefore'] = before;
2098
+ methods[unit + 'Ago'] = before;
2099
+ methods[unit + 'sAgo'] = before;
2100
+ methods[unit + 'After'] = after;
2101
+ methods[unit + 'sAfter'] = after;
2102
+ methods[unit + 'FromNow'] = after;
2103
+ methods[unit + 'sFromNow'] = after;
2104
+ number.extend(methods);
2105
+ }
2106
+
2107
+ number.extend({
2108
+
2109
+ /***
2110
+ * @method duration([locale] = currentLocale)
2111
+ * @returns String
2112
+ * @short Takes the number as milliseconds and returns a unit-adjusted localized string.
2113
+ * @extra This method is the same as %Date#relative% without the localized equivalent of "from now" or "ago". [locale] can be passed as the first (and only) parameter. Note that this method is only available when the dates package is included.
2114
+ * @example
2115
+ *
2116
+ * (500).duration() -> '500 milliseconds'
2117
+ * (1200).duration() -> '1 second'
2118
+ * (75).minutes().duration() -> '1 hour'
2119
+ * (75).minutes().duration('es') -> '1 hora'
2120
+ *
2121
+ ***/
2122
+ 'duration': function(localeCode) {
2123
+ return getLocalization(localeCode).getDuration(this);
2124
+ }
2125
+
2126
+ });
2127
+
2128
+
2129
+ English = CurrentLocalization = date.addLocale('en', {
2130
+ 'plural': true,
2131
+ 'timeMarker': 'at',
2132
+ 'ampm': 'am,pm',
2133
+ 'months': 'January,February,March,April,May,June,July,August,September,October,November,December',
2134
+ 'weekdays': 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday',
2135
+ 'units': 'millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s',
2136
+ 'numbers': 'one,two,three,four,five,six,seven,eight,nine,ten',
2137
+ 'articles': 'a,an,the',
2138
+ 'optionals': 'the,st|nd|rd|th,of',
2139
+ 'short': '{Month} {d}, {yyyy}',
2140
+ 'long': '{Month} {d}, {yyyy} {h}:{mm}{tt}',
2141
+ 'full': '{Weekday} {Month} {d}, {yyyy} {h}:{mm}:{ss}{tt}',
2142
+ 'past': '{num} {unit} {sign}',
2143
+ 'future': '{num} {unit} {sign}',
2144
+ 'duration': '{num} {unit}',
2145
+ 'modifiers': [
2146
+ { 'name': 'day', 'src': 'yesterday', 'value': -1 },
2147
+ { 'name': 'day', 'src': 'today', 'value': 0 },
2148
+ { 'name': 'day', 'src': 'tomorrow', 'value': 1 },
2149
+ { 'name': 'sign', 'src': 'ago|before', 'value': -1 },
2150
+ { 'name': 'sign', 'src': 'from now|after|from|in', 'value': 1 },
2151
+ { 'name': 'edge', 'src': 'last day', 'value': -2 },
2152
+ { 'name': 'edge', 'src': 'end', 'value': -1 },
2153
+ { 'name': 'edge', 'src': 'first day|beginning', 'value': 1 },
2154
+ { 'name': 'shift', 'src': 'last', 'value': -1 },
2155
+ { 'name': 'shift', 'src': 'the|this', 'value': 0 },
2156
+ { 'name': 'shift', 'src': 'next', 'value': 1 }
2157
+ ],
2158
+ 'dateParse': [
2159
+ '{num} {unit} {sign}',
2160
+ '{sign} {num} {unit}',
2161
+ '{num} {unit=4-5} {sign} {day}',
2162
+ '{month} {year}',
2163
+ '{shift} {unit=5-7}',
2164
+ '{0} {edge} of {shift?} {unit=4-7?}{month?}{year?}'
2165
+ ],
2166
+ 'timeParse': [
2167
+ '{0} {num}{1} {day} of {month} {year?}',
2168
+ '{weekday?} {month} {date}{1} {year?}',
2169
+ '{date} {month} {year}',
2170
+ '{shift} {weekday}',
2171
+ '{shift} week {weekday}',
2172
+ '{weekday} {2} {shift} week',
2173
+ '{0} {date}{1} of {month}',
2174
+ '{0}{month?} {date?}{1} of {shift} {unit=6-7}'
2175
+ ]
2176
+ });
2177
+
2178
+ buildDate();
2179
+