make[1]: Entering directory `/home/bill/dev/later' Coverage

Coverage

95%
799
762
37

array/array.js

100%
1
1
0
LineHitsSource
11later.array = {};

array/next.js

100%
11
11
0
LineHitsSource
1/**
2* Next
3* (c) 2013 Bill, BunKat LLC.
4*
5* Returns the next valid value in a range of values, wrapping as needed. Assumes
6* the array has already been sorted.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.array.next = function (val, values, extent) {
14
159815 var cur,
16 zeroIsLargest = extent[0] !== 0,
17 nextIdx = 0;
18
199815 for(var i = values.length-1; i > -1; --i) {
2029761 cur = values[i];
21
2229761 if(cur === val) {
236579 return cur;
24 }
25
2623182 if(cur > val || (cur === 0 && zeroIsLargest && extent[1] > val)) {
2721233 nextIdx = i;
2821233 continue;
29 }
30
311949 break;
32 }
33
343236 return values[nextIdx];
35};

array/nextinvalid.js

100%
12
12
0
LineHitsSource
1/**
2* Next Invalid
3* (c) 2013 Bill, BunKat LLC.
4*
5* Returns the next invalid value in a range of values, wrapping as needed. Assumes
6* the array has already been sorted.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.array.nextInvalid = function (val, values, extent) {
14
151255 var min = extent[0], max = extent[1], len = values.length,
16 zeroVal = values[len-1] === 0 && min !== 0 ? max : 0,
17 next = val,
18 i = values.indexOf(val),
19 start = next;
20
211255 while(next === (values[i] || zeroVal)) {
22
231448 next++;
241448 if(next > max) {
2517 next = min;
26 }
27
281448 i++;
291448 if(i === len) {
3066 i = 0;
31 }
32
331448 if(next === start) {
343 return undefined;
35 }
36 }
37
381252 return next;
39};

array/prev.js

100%
11
11
0
LineHitsSource
1/**
2* Previous
3* (c) 2013 Bill, BunKat LLC.
4*
5* Returns the previous valid value in a range of values, wrapping as needed. Assumes
6* the array has already been sorted.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.array.prev = function (val, values, extent) {
14
153574 var cur, len = values.length,
16 zeroIsLargest = extent[0] !== 0,
17 prevIdx = len-1;
18
193574 for(var i = 0; i < len; i++) {
205660 cur = values[i];
21
225660 if(cur === val) {
231983 return cur;
24 }
25
263677 if(cur < val || (cur === 0 && zeroIsLargest && extent[1] < val)) {
272897 prevIdx = i;
282897 continue;
29 }
30
31780 break;
32 }
33
341591 return values[prevIdx];
35};

array/previnvalid.js

100%
12
12
0
LineHitsSource
1/**
2* Previous Invalid
3* (c) 2013 Bill, BunKat LLC.
4*
5* Returns the previous invalid value in a range of values, wrapping as needed. Assumes
6* the array has already been sorted.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.array.prevInvalid = function (val, values, extent) {
14
15187 var min = extent[0], max = extent[1], len = values.length,
16 zeroVal = values[len-1] === 0 && min !== 0 ? max : 0,
17 next = val,
18 i = values.indexOf(val),
19 start = next;
20
21187 while(next === (values[i] || zeroVal)) {
22366 next--;
23
24366 if(next < min) {
2522 next = max;
26 }
27
28366 i--;
29366 if(i === -1) {
3046 i = len-1;
31 }
32
33366 if(next === start) {
344 return undefined;
35 }
36 }
37
38183 return next;
39};

array/sort.js

100%
5
5
0
LineHitsSource
1/**
2* Sort
3* (c) 2013 Bill, BunKat LLC.
4*
5* Sorts an array in natural ascending order, placing zero at the end
6* if zeroIsLast is true.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.array.sort = function (arr, zeroIsLast) {
144 arr.sort(function(a,b) {
1540 return +a - +b;
16 });
17
184 if(zeroIsLast && arr[0] === 0) {
191 arr.push(arr.shift());
20 }
21};

compat/indexof.js

4%
23
1
22
LineHitsSource
1// indexOf compares searchElement to elements of the Array using strict
2// equality (the same method used by the ===, or triple-equals, operator).
3//
4// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
5//
61if (!Array.prototype.indexOf) {
70 Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
80 "use strict";
90 if (this == null) {
100 throw new TypeError();
11 }
120 var t = Object(this);
130 var len = t.length >>> 0;
140 if (len === 0) {
150 return -1;
16 }
170 var n = 0;
180 if (arguments.length > 1) {
190 n = Number(arguments[1]);
200 if (n != n) { // shortcut for verifying if it's NaN
210 n = 0;
220 } else if (n != 0 && n != Infinity && n != -Infinity) {
230 n = (n > 0 || -1) * Math.floor(Math.abs(n));
24 }
25 }
260 if (n >= len) {
270 return -1;
28 }
290 var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
300 for (; k < len; k++) {
310 if (k in t && t[k] === searchElement) {
320 return k;
33 }
34 }
350 return -1;
36 }
37}

compat/trim.js

33%
3
1
2
LineHitsSource
1// The trim method returns the string stripped of whitespace from both ends.
2// trim does not affect the value of the string itself.
3//
4// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/Trim
5//
61if(!String.prototype.trim) {
70 String.prototype.trim = function () {
80 return this.replace(/^\s+|\s+$/g,'');
9 };
10}

constraint/day.js

100%
16
16
0
LineHitsSource
1/**
2* Day Constraint (D)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a day of month (date) constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.day = later.D = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'day',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 86400,
23
24 /**
25 * The day value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
30104249 return d.D || (d.D = later.date.getDate.call(d));
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
403600 return later.D.val(d) === (val || later.D.extent(d)[1]);
41 },
42
43 /**
44 * The minimum and maximum valid day values of the month specified.
45 * Zero to specify the last day of the month.
46 *
47 * @param {Date} d: The date indicating the month to find the extent of
48 */
49 extent: function(d) {
5045462 if(d.DExtent) return d.DExtent;
51
5235934 var month = later.M.val(d),
53 max = later.DAYS_IN_MONTH[month-1];
54
5535934 if(month === 2 && later.dy.extent(d)[1] === 366) {
56607 max = max+1;
57 }
58
5935934 return (d.DExtent = [1, max]);
60 },
61
62 /**
63 * The start of the day of the specified date.
64 *
65 * @param {Date} d: The specified date
66 */
67 start: function(d) {
6820568 return d.DStart || (d.DStart = later.date.next(
69 later.Y.val(d), later.M.val(d), later.D.val(d)));
70 },
71
72 /**
73 * The end of the day of the specified date.
74 *
75 * @param {Date} d: The specified date
76 */
77 end: function(d) {
786079 return d.DEnd || (d.DEnd = later.date.prev(
79 later.Y.val(d), later.M.val(d), later.D.val(d)));
80 },
81
82 /**
83 * Returns the start of the next instance of the day value indicated. Returns
84 * the first day of the next month if val is greater than the number of
85 * days in the following month.
86 *
87 * @param {Date} d: The starting date
88 * @param {int} val: The desired value, must be within extent
89 */
90 next: function(d, val) {
911245 val = val > later.D.extent(d)[1] ? 1 : val;
921245 var month = later.date.nextRollover(d, val, later.D, later.M),
93 DMax = later.D.extent(month)[1];
94
951245 val = val > DMax ? 1 : val || DMax;
96
971245 return later.date.next(
98 later.Y.val(month),
99 later.M.val(month),
100 val
101 );
102 },
103
104 /**
105 * Returns the end of the previous instance of the day value indicated. Returns
106 * the last day in the previous month if val is greater than the number of days
107 * in the previous month.
108 *
109 * @param {Date} d: The starting date
110 * @param {int} val: The desired value, must be within extent
111 */
112 prev: function(d, val) {
1131029 var month = later.date.prevRollover(d, val, later.D, later.M),
114 DMax = later.D.extent(month)[1];
115
1161029 return later.date.prev(
117 later.Y.val(month),
118 later.M.val(month),
119 val > DMax ? DMax : val || DMax
120 );
121 }
122
123};

constraint/dayofweek.js

100%
10
10
0
LineHitsSource
1/**
2* Day of Week Constraint (dw)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a day of week constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.dayOfWeek = later.dw = later.d = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'day of week',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 86400,
23
24 /**
25 * The day of week value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
30111085 return d.dw || (d.dw = later.date.getDay.call(d)+1);
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
401974 return later.dw.val(d) === (val || 7);
41 },
42
43 /**
44 * The minimum and maximum valid day of week values. Unlike the standard
45 * Date object, Later day of week goes from 1 to 7.
46 */
47 extent: function() {
482300 return [1, 7];
49 },
50
51 /**
52 * The start of the day of the specified date.
53 *
54 * @param {Date} d: The specified date
55 */
56 start: function(d) {
57417 return later.D.start(d);
58 },
59
60 /**
61 * The end of the day of the specified date.
62 *
63 * @param {Date} d: The specified date
64 */
65 end: function(d) {
66346 return later.D.end(d);
67 },
68
69 /**
70 * Returns the start of the next instance of the day of week value indicated.
71 *
72 * @param {Date} d: The starting date
73 * @param {int} val: The desired value, must be within extent
74 */
75 next: function(d, val) {
7630253 val = val > 7 ? 1 : val || 7;
77
7830253 return later.date.next(
79 later.Y.val(d),
80 later.M.val(d),
81 later.D.val(d) + (val - later.dw.val(d)) + (val <= later.dw.val(d) ? 7 : 0));
82 },
83
84 /**
85 * Returns the end of the previous instance of the day of week value indicated.
86 *
87 * @param {Date} d: The starting date
88 * @param {int} val: The desired value, must be within extent
89 */
90 prev: function(d, val) {
91414 val = val > 7 ? 7 : val || 7;
92
93414 return later.date.prev(
94 later.Y.val(d),
95 later.M.val(d),
96 later.D.val(d) + (val - later.dw.val(d)) + (val >= later.dw.val(d) ? -7 : 0));
97 }
98};

constraint/dayofweekcount.js

100%
17
17
0
LineHitsSource
1/**
2* Day of Week Count Constraint (dc)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a day of week count constraint type. This constraint is used
6* to specify schedules like '2nd Tuesday of every month'.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
121later.dayOfWeekCount = later.dc = {
13
14 /**
15 * The name of this constraint.
16 */
17 name: 'day of week count',
18
19 /**
20 * The rough amount of seconds between start and end for this constraint.
21 * (doesn't need to be exact)
22 */
23 range: 604800,
24
25 /**
26 * The day of week count value of the specified date.
27 *
28 * @param {Date} d: The date to calculate the value of
29 */
30 val: function(d) {
312966 return d.dc || (d.dc = Math.floor((later.D.val(d)-1)/7)+1);
32 },
33
34 /**
35 * Returns true if the val is valid for the date specified.
36 *
37 * @param {Date} d: The date to check the value on
38 * @param {Integer} val: The value to validate
39 */
40 isValid: function(d, val) {
411095 return (later.dc.val(d) === val) ||
42 (val === 0 && later.D.val(d) > later.D.extent(d)[1] - 7);
43 },
44
45 /**
46 * The minimum and maximum valid day values of the month specified.
47 * Zero to specify the last day of week count of the month.
48 *
49 * @param {Date} d: The date indicating the month to find the extent of
50 */
51 extent: function(d) {
522316 return d.dcExtent || (d.dcExtent = [1, Math.ceil(later.D.extent(d)[1] /7)]);
53 },
54
55 /**
56 * The first day of the month with the same day of week count as the date
57 * specified.
58 *
59 * @param {Date} d: The specified date
60 */
61 start: function(d) {
62180 return d.dcStart || (d.dcStart =
63 later.date.next(
64 later.Y.val(d),
65 later.M.val(d),
66 Math.max(1, ((later.dc.val(d) - 1) * 7) + 1 || 1)));
67 },
68
69 /**
70 * The last day of the month with the same day of week count as the date
71 * specified.
72 *
73 * @param {Date} d: The specified date
74 */
75 end: function(d) {
76427 return d.dcEnd || (d.dcEnd =
77 later.date.prev(
78 later.Y.val(d),
79 later.M.val(d),
80 Math.min(later.dc.val(d) * 7, later.D.extent(d)[1])));
81 },
82
83 /**
84 * Returns the next earliest date with the day of week count specified.
85 *
86 * @param {Date} d: The starting date
87 * @param {int} val: The desired value, must be within extent
88 */
89 next: function(d, val) {
90256 val = val > later.dc.extent(d)[1] ? 1 : val;
91256 var month = later.date.nextRollover(d, val, later.dc, later.M),
92 dcMax = later.dc.extent(month)[1];
93
94256 val = val > dcMax ? 1 : val;
95
96256 var next = later.date.next(
97 later.Y.val(month),
98 later.M.val(month),
99 val === 0 ? later.D.extent(month)[1] - 6 : 1 + (7 * (val - 1))
100 );
101
102256 if(next.getTime() <= d.getTime()) {
1032 month = later.M.next(d, later.M.val(d)+1);
104
1052 return later.date.next(
106 later.Y.val(month),
107 later.M.val(month),
108 val === 0 ? later.D.extent(month)[1] - 6 : 1 + (7 * (val - 1))
109 );
110 }
111
112254 return next;
113 },
114
115 /**
116 * Returns the closest previous date with the day of week count specified.
117 *
118 * @param {Date} d: The starting date
119 * @param {int} val: The desired value, must be within extent
120 */
121 prev: function(d, val) {
122221 var month = later.date.prevRollover(d, val, later.dc, later.M),
123 dcMax = later.dc.extent(month)[1];
124
125221 val = val > dcMax ? dcMax : val || dcMax;
126
127221 return later.dc.end(later.date.prev(
128 later.Y.val(month),
129 later.M.val(month),
130 1 + (7 * (val - 1))
131 ));
132 }
133
134};

constraint/dayofyear.js

100%
14
14
0
LineHitsSource
1/**
2* Day of Year Constraint (dy)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a day of year constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.dayOfYear = later.dy = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'day of year',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 86400,
23
24 /**
25 * The day of year value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
3019188 return d.dy || (d.dy =
31 Math.ceil(1 + (later.D.start(d).getTime() - later.Y.start(d).getTime()) / later.DAY));
32 },
33
34 /**
35 * Returns true if the val is valid for the date specified.
36 *
37 * @param {Date} d: The date to check the value on
38 * @param {Integer} val: The value to validate
39 */
40 isValid: function(d, val) {
419568 return later.dy.val(d) === (val || later.dy.extent(d)[1]);
42 },
43
44 /**
45 * The minimum and maximum valid day of year values of the month specified.
46 * Zero indicates the last day of the year.
47 *
48 * @param {Date} d: The date indicating the month to find the extent of
49 */
50 extent: function(d) {
5134831 var year = later.Y.val(d);
52
53 // shortcut on finding leap years since this function gets called a lot
54 // works between 1901 and 2099
5534831 return d.dyExtent || (d.dyExtent = [1, year % 4 ? 365 : 366]);
56 },
57
58 /**
59 * The start of the day of year of the specified date.
60 *
61 * @param {Date} d: The specified date
62 */
63 start: function(d) {
644810 return later.D.start(d);
65 },
66
67 /**
68 * The end of the day of year of the specified date.
69 *
70 * @param {Date} d: The specified date
71 */
72 end: function(d) {
734810 return later.D.end(d);
74 },
75
76 /**
77 * Returns the start of the next instance of the day of year value indicated.
78 *
79 * @param {Date} d: The starting date
80 * @param {int} val: The desired value, must be within extent
81 */
82 next: function(d, val) {
834784 val = val > later.dy.extent(d)[1] ? 1 : val;
844784 var year = later.date.nextRollover(d, val, later.dy, later.Y),
85 dyMax = later.dy.extent(year)[1];
86
874784 val = val > dyMax ? 1 : val || dyMax;
88
894784 return later.date.next(
90 later.Y.val(year),
91 later.M.val(year),
92 val
93 );
94
95 },
96
97 /**
98 * Returns the end of the previous instance of the day of year value indicated.
99 *
100 * @param {Date} d: The starting date
101 * @param {int} val: The desired value, must be within extent
102 */
103 prev: function(d, val) {
1044784 var year = later.date.prevRollover(d, val, later.dy, later.Y),
105 dyMax = later.dy.extent(year)[1];
106
1074784 val = val > dyMax ? dyMax : val || dyMax;
108
1094784 return later.date.prev(
110 later.Y.val(year),
111 later.M.val(year),
112 val
113 );
114 }
115
116};

constraint/fulldate.js

100%
8
8
0
LineHitsSource
1/**
2* Full date (fd)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for specifying a full date and time.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.fullDate = later.fd = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'full date',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 1,
23
24 /**
25 * The time value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
3062 return d.fd || (d.fd = d.getTime());
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
408 return later.fd.val(d) === val;
41 },
42
43 /**
44 * The minimum and maximum valid time values.
45 */
46 extent: function() {
4734 return [0, 32503680000000];
48 },
49
50 /**
51 * Returns the specified date.
52 *
53 * @param {Date} d: The specified date
54 */
55 start: function(d) {
566 return d;
57 },
58
59 /**
60 * Returns the specified date.
61 *
62 * @param {Date} d: The specified date
63 */
64 end: function(d) {
652 return d;
66 },
67
68 /**
69 * Returns the start of the next instance of the time value indicated.
70 *
71 * @param {Date} d: The starting date
72 * @param {int} val: The desired value, must be within extent
73 */
74 next: function(d, val) {
7513 return later.fd.val(d) < val ? new Date(val) : later.NEVER;
76 },
77
78 /**
79 * Returns the end of the previous instance of the time value indicated.
80 *
81 * @param {Date} d: The starting date
82 * @param {int} val: The desired value, must be within extent
83 */
84 prev: function(d, val) {
853 return later.fd.val(d) > val ? new Date(val) : later.NEVER;
86 }
87
88};

constraint/hour.js

100%
13
13
0
LineHitsSource
1/**
2* Hour Constraint (H)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a hour constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.hour = later.h = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'hour',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 3600,
23
24 /**
25 * The hour value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
3033989 return d.h || (d.h = later.date.getHour.call(d));
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
403023 return later.h.val(d) === val;
41 },
42
43 /**
44 * The minimum and maximum valid hour values.
45 */
46 extent: function() {
473962 return [0, 23];
48 },
49
50 /**
51 * The start of the hour of the specified date.
52 *
53 * @param {Date} d: The specified date
54 */
55 start: function(d) {
56734 return d.hStart || (d.hStart = later.date.next(
57 later.Y.val(d), later.M.val(d), later.D.val(d), later.h.val(d)));
58 },
59
60 /**
61 * The end of the hour of the specified date.
62 *
63 * @param {Date} d: The specified date
64 */
65 end: function(d) {
66707 return d.hEnd || (d.hEnd = later.date.prev(
67 later.Y.val(d), later.M.val(d), later.D.val(d), later.h.val(d)));
68 },
69
70 /**
71 * Returns the start of the next instance of the hour value indicated.
72 *
73 * @param {Date} d: The starting date
74 * @param {int} val: The desired value, must be within extent
75 */
76 next: function(d, val) {
77955 val = val > 23 ? 0 : val;
78
79955 var next = later.date.next(
80 later.Y.val(d),
81 later.M.val(d),
82 later.D.val(d) + (val <= later.h.val(d) ? 1 : 0),
83 val);
84
85 // correct for passing over a daylight savings boundry
86955 if(!later.date.isUTC && next.getTime() <= d.getTime()) {
871 next = later.date.next(
88 later.Y.val(next),
89 later.M.val(next),
90 later.D.val(next),
91 val + 1);
92 }
93
94955 return next;
95 },
96
97 /**
98 * Returns the end of the previous instance of the hour value indicated.
99 *
100 * @param {Date} d: The starting date
101 * @param {int} val: The desired value, must be within extent
102 */
103 prev: function(d, val) {
104816 val = val > 23 ? 23 : val;
105
106816 return later.date.prev(
107 later.Y.val(d),
108 later.M.val(d),
109 later.D.val(d) + (val >= later.h.val(d) ? -1 : 0),
110 val);
111 }
112
113};

constraint/minute.js

91%
12
11
1
LineHitsSource
1/**
2* Minute Constraint (m)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a minute constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.minute = later.m = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'minute',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 60,
23
24 /**
25 * The minute value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
3038147 return d.m || (d.m = later.date.getMin.call(d));
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
405226 return later.m.val(d) === val;
41 },
42
43 /**
44 * The minimum and maximum valid minute values.
45 */
46 extent: function(d) {
478538 return [0, 59];
48 },
49
50 /**
51 * The start of the minute of the specified date.
52 *
53 * @param {Date} d: The specified date
54 */
55 start: function(d) {
563110 return d.mStart || (d.mStart = later.date.next(
57 later.Y.val(d), later.M.val(d), later.D.val(d), later.h.val(d), later.m.val(d)));
58 },
59
60 /**
61 * The end of the minute of the specified date.
62 *
63 * @param {Date} d: The specified date
64 */
65 end: function(d) {
661895 return d.mEnd || (d.mEnd = later.date.prev(
67 later.Y.val(d), later.M.val(d), later.D.val(d), later.h.val(d), later.m.val(d)));
68 },
69
70 /**
71 * Returns the start of the next instance of the minute value indicated.
72 *
73 * @param {Date} d: The starting date
74 * @param {int} val: The desired value, must be within extent
75 */
76 next: function(d, val) {
772303 var m = later.m.val(d),
78 s = later.s.val(d),
79 inc = val > 59 ? 60-m : (val <= m ? (60-m) + val : val-m),
80 next = new Date(d.getTime() + (inc * later.MIN) - (s * later.SEC));
81
82 // correct for passing over a daylight savings boundry
832303 if(!later.date.isUTC && next.getTime() <= d.getTime()) {
840 next = new Date(d.getTime() + ((inc + 120) * later.MIN) - (s * later.SEC));
85 }
86
872303 return next;
88 },
89
90 /**
91 * Returns the end of the previous instance of the minute value indicated.
92 *
93 * @param {Date} d: The starting date
94 * @param {int} val: The desired value, must be within extent
95 */
96 prev: function(d, val) {
971971 val = val > 59 ? 59 : val;
98
991971 return later.date.prev(
100 later.Y.val(d),
101 later.M.val(d),
102 later.D.val(d),
103 later.h.val(d) + (val >= later.m.val(d) ? -1 : 0),
104 val);
105 }
106
107};

constraint/month.js

100%
10
10
0
LineHitsSource
1/**
2* Month Constraint (M)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a month constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.month = later.M = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'month',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 2629740,
23
24 /**
25 * The month value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
30151949 return d.M || (d.M = later.date.getMonth.call(d)+1);
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
401175 return later.M.val(d) === (val || 12);
41 },
42
43 /**
44 * The minimum and maximum valid month values. Unlike the native date object,
45 * month values in later are 1 based.
46 */
47 extent: function() {
481591 return [1, 12];
49 },
50
51 /**
52 * The start of the month of the specified date.
53 *
54 * @param {Date} d: The specified date
55 */
56 start: function(d) {
573898 return d.MStart || (d.MStart = later.date.next(later.Y.val(d), later.M.val(d)));
58 },
59
60 /**
61 * The end of the month of the specified date.
62 *
63 * @param {Date} d: The specified date
64 */
65 end: function(d) {
662196 return d.MEnd || (d.MEnd = later.date.prev(later.Y.val(d), later.M.val(d)));
67 },
68
69 /**
70 * Returns the start of the next instance of the month value indicated.
71 *
72 * @param {Date} d: The starting date
73 * @param {int} val: The desired value, must be within extent
74 */
75 next: function(d, val) {
76399 val = val > 12 ? 1 : val || 12;
77
78399 return later.date.next(
79 later.Y.val(d) + (val > later.M.val(d) ? 0 : 1),
80 val);
81 },
82
83 /**
84 * Returns the end of the previous instance of the month value indicated.
85 *
86 * @param {Date} d: The starting date
87 * @param {int} val: The desired value, must be within extent
88 */
89 prev: function(d, val) {
901262 val = val > 12 ? 12 : val || 12;
91
921262 return later.date.prev(
93 later.Y.val(d) - (val >= later.M.val(d) ? 1 : 0),
94 val);
95 }
96
97};

constraint/second.js

91%
12
11
1
LineHitsSource
1/**
2* Second Constraint (s)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a second constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.second = later.s = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'second',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 1,
23
24 /**
25 * The second value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
3031987 return d.s || (d.s = later.date.getSec.call(d));
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
405100 return later.s.val(d) === val;
41 },
42
43 /**
44 * The minimum and maximum valid second values.
45 */
46 extent: function() {
477768 return [0, 59];
48 },
49
50 /**
51 * The start of the second of the specified date.
52 *
53 * @param {Date} d: The specified date
54 */
55 start: function(d) {
563249 return d;
57 },
58
59 /**
60 * The end of the second of the specified date.
61 *
62 * @param {Date} d: The specified date
63 */
64 end: function(d) {
651997 return d;
66 },
67
68 /**
69 * Returns the start of the next instance of the second value indicated.
70 *
71 * @param {Date} d: The starting date
72 * @param {int} val: The desired value, must be within extent
73 */
74 next: function(d, val) {
752842 var s = later.s.val(d),
76 inc = val > 59 ? 60-s : (val <= s ? (60-s) + val : val-s),
77 next = new Date(d.getTime() + (inc * later.SEC));
78
79 // correct for passing over a daylight savings boundry
802842 if(!later.date.isUTC && next.getTime() <= d.getTime()) {
810 next = new Date(d.getTime() + ((inc + 7200) * later.SEC));
82 }
83
842842 return next;
85 },
86
87 /**
88 * Returns the end of the previous instance of the second value indicated.
89 *
90 * @param {Date} d: The starting date
91 * @param {int} val: The desired value, must be within extent
92 */
93 prev: function(d, val, cache) {
941723 val = val > 59 ? 59 : val;
95
961723 return later.date.prev(
97 later.Y.val(d),
98 later.M.val(d),
99 later.D.val(d),
100 later.h.val(d),
101 later.m.val(d) + (val >= later.s.val(d) ? -1 : 0),
102 val);
103 }
104
105};

constraint/time.js

92%
13
12
1
LineHitsSource
1/**
2* Time Constraint (dy)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a time of day constraint type. Stored as number of seconds
6* since midnight to simplify calculations.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
121later.time = later.t = {
13
14 /**
15 * The name of this constraint.
16 */
17 name: 'time',
18
19 /**
20 * The rough amount of seconds between start and end for this constraint.
21 * (doesn't need to be exact)
22 */
23 range: 1,
24
25 /**
26 * The time value of the specified date.
27 *
28 * @param {Date} d: The date to calculate the value of
29 */
30 val: function(d) {
3122379 return d.t || (d.t =
32 (later.h.val(d) * 3600) + (later.m.val(d) * 60) + (later.s.val(d)));
33 },
34
35 /**
36 * Returns true if the val is valid for the date specified.
37 *
38 * @param {Date} d: The date to check the value on
39 * @param {Integer} val: The value to validate
40 */
41 isValid: function(d, val) {
4210378 return later.t.val(d) === val;
43 },
44
45 /**
46 * The minimum and maximum valid time values.
47 */
48 extent: function() {
4915940 return [0, 86399];
50 },
51
52 /**
53 * Returns the specified date.
54 *
55 * @param {Date} d: The specified date
56 */
57 start: function(d) {
585285 return d;
59 },
60
61 /**
62 * Returns the specified date.
63 *
64 * @param {Date} d: The specified date
65 */
66 end: function(d) {
675116 return d;
68 },
69
70 /**
71 * Returns the start of the next instance of the time value indicated.
72 *
73 * @param {Date} d: The starting date
74 * @param {int} val: The desired value, must be within extent
75 */
76 next: function(d, val) {
775152 val = val > 86399 ? 0 : val;
78
795152 var next = later.date.next(
80 later.Y.val(d),
81 later.M.val(d),
82 later.D.val(d) + (val <= later.t.val(d) ? 1 : 0),
83 0,
84 0,
85 val);
86
87 // correct for passing over a daylight savings boundry
885152 if(!later.date.isUTC && next.getTime() < d.getTime()) {
890 next = later.date.next(
90 later.Y.val(next),
91 later.M.val(next),
92 later.D.val(next),
93 later.h.val(next),
94 later.m.val(next),
95 val + 7200);
96 }
97
985152 return next;
99 },
100
101 /**
102 * Returns the end of the previous instance of the time value indicated.
103 *
104 * @param {Date} d: The starting date
105 * @param {int} val: The desired value, must be within extent
106 */
107 prev: function(d, val) {
1085004 val = val > 86399 ? 86399 : val;
109
1105004 return later.date.next(
111 later.Y.val(d),
112 later.M.val(d),
113 later.D.val(d) + (val >= later.t.val(d) ? -1 : 0),
114 0,
115 0,
116 val);
117 }
118
119};

constraint/weekofmonth.js

100%
13
13
0
LineHitsSource
1/**
2* Week of Month Constraint (wy)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for an week of month constraint type. Week of month treats the
6* first of the month as the start of week 1, with each following week starting
7* on Sunday.
8*
9* Later is freely distributable under the MIT license.
10* For all details and documentation:
11* http://github.com/bunkat/later
12*/
131later.weekOfMonth = later.wm = {
14
15 /**
16 * The name of this constraint.
17 */
18 name: 'week of month',
19
20 /**
21 * The rough amount of seconds between start and end for this constraint.
22 * (doesn't need to be exact)
23 */
24 range: 604800,
25
26 /**
27 * The week of month value of the specified date.
28 *
29 * @param {Date} d: The date to calculate the value of
30 */
31 val: function(d) {
32788 return d.wm || (d.wm =
33 (later.D.val(d) +
34 (later.dw.val(later.M.start(d)) - 1) + (7 - later.dw.val(d))) / 7);
35 },
36
37 /**
38 * Returns true if the val is valid for the date specified.
39 *
40 * @param {Date} d: The date to check the value on
41 * @param {Integer} val: The value to validate
42 */
43 isValid: function(d, val) {
44368 return later.wm.val(d) === (val || later.wm.extent(d)[1]);
45 },
46
47 /**
48 * The minimum and maximum valid week of month values for the month indicated.
49 * Zero indicates the last week in the month.
50 *
51 * @param {Date} d: The date indicating the month to find values for
52 */
53 extent: function(d) {
541388 return d.wmExtent || (d.wmExtent = [1,
55 (later.D.extent(d)[1] + (later.dw.val(later.M.start(d)) - 1) +
56 (7 - later.dw.val(later.M.end(d)))) / 7]);
57 },
58
59 /**
60 * The start of the week of the specified date.
61 *
62 * @param {Date} d: The specified date
63 */
64 start: function(d) {
65210 return d.wmStart || (d.wmStart = later.date.next(
66 later.Y.val(d),
67 later.M.val(d),
68 Math.max(later.D.val(d) - later.dw.val(d) + 1, 1)));
69 },
70
71 /**
72 * The end of the week of the specified date.
73 *
74 * @param {Date} d: The specified date
75 */
76 end: function(d) {
77394 return d.wmEnd || (d.wmEnd = later.date.prev(
78 later.Y.val(d),
79 later.M.val(d),
80 Math.min(later.D.val(d) + (7 - later.dw.val(d)), later.D.extent(d)[1])));
81 },
82
83 /**
84 * Returns the start of the next instance of the week value indicated. Returns
85 * the first day of the next month if val is greater than the number of
86 * days in the following month.
87 *
88 * @param {Date} d: The starting date
89 * @param {int} val: The desired value, must be within extent
90 */
91 next: function(d, val) {
92184 val = val > later.wm.extent(d)[1] ? 1 : val;
93
94184 var month = later.date.nextRollover(d, val, later.wm, later.M),
95 wmMax = later.wm.extent(month)[1];
96
97184 val = val > wmMax ? 1 : val || wmMax;
98
99 // jump to the Sunday of the desired week, set to 1st of month for week 1
100184 return later.date.next(
101 later.Y.val(month),
102 later.M.val(month),
103 Math.max(1, (val-1) * 7 - (later.dw.val(month)-2)));
104 },
105
106 /**
107 * Returns the end of the previous instance of the week value indicated. Returns
108 * the last day of the previous month if val is greater than the number of
109 * days in the previous month.
110 *
111 * @param {Date} d: The starting date
112 * @param {int} val: The desired value, must be within extent
113 */
114 prev: function(d, val) {
115184 var month = later.date.prevRollover(d, val, later.wm, later.M),
116 wmMax = later.wm.extent(month)[1];
117
118184 val = val > wmMax ? wmMax : val || wmMax;
119
120 // jump to the end of Saturday of the desired week
121184 return later.wm.end(later.date.next(
122 later.Y.val(month),
123 later.M.val(month),
124 Math.max(1, (val-1) * 7 - (later.dw.val(month)-2))));
125 }
126
127};

constraint/weekofyear.js

100%
23
23
0
LineHitsSource
1/**
2* Week of Year Constraint (wy)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for an ISO 8601 week constraint type. For more information about
6* ISO 8601 see http://en.wikipedia.org/wiki/ISO_week_date.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
121later.weekOfYear = later.wy = {
13
14 /**
15 * The name of this constraint.
16 */
17 name: 'week of year (ISO)',
18
19 /**
20 * The rough amount of seconds between start and end for this constraint.
21 * (doesn't need to be exact)
22 */
23 range: 604800,
24
25 /**
26 * The ISO week year value of the specified date.
27 *
28 * @param {Date} d: The date to calculate the value of
29 */
30 val: function(d) {
318999 if (d.wy) return d.wy;
32
33 // move to the Thursday in the target week and find Thurs of target year
348669 var wThur = later.dw.next(later.wy.start(d), 5),
35 YThur = later.dw.next(later.Y.prev(wThur, later.Y.val(wThur)-1), 5);
36
37 // caculate the difference between the two dates in weeks
388669 return (d.wy = 1 + Math.ceil((wThur.getTime() - YThur.getTime()) / later.WEEK));
39 },
40
41 /**
42 * Returns true if the val is valid for the date specified.
43 *
44 * @param {Date} d: The date to check the value on
45 * @param {Integer} val: The value to validate
46 */
47 isValid: function(d, val) {
482955 return later.wy.val(d) === (val || later.wy.extent(d)[1]);
49 },
50
51 /**
52 * The minimum and maximum valid ISO week values for the year indicated.
53 *
54 * @param {Date} d: The date indicating the year to find ISO values for
55 */
56 extent: function(d) {
5712401 if (d.wyExtent) return d.wyExtent;
58
59 // go to start of ISO week to get to the right year
607953 var year = later.dw.next(later.wy.start(d), 5),
61 dwFirst = later.dw.val(later.Y.start(year)),
62 dwLast = later.dw.val(later.Y.end(year));
63
647953 return (d.wyExtent = [1, dwFirst === 5 || dwLast === 5 ? 53 : 52]);
65 },
66
67 /**
68 * The start of the ISO week of the specified date.
69 *
70 * @param {Date} d: The specified date
71 */
72 start: function(d) {
7322324 return d.wyStart || (d.wyStart = later.date.next(
74 later.Y.val(d),
75 later.M.val(d),
76 // jump to the Monday of the current week
77 later.D.val(d) - (later.dw.val(d) > 1 ? later.dw.val(d) - 2 : 6)
78 ));
79 },
80
81 /**
82 * The end of the ISO week of the specified date.
83 *
84 * @param {Date} d: The specified date
85 */
86 end: function(d) {
874274 return d.wyEnd || (d.wyEnd = later.date.prev(
88 later.Y.val(d),
89 later.M.val(d),
90 // jump to the Saturday of the current week
91 later.D.val(d) + (later.dw.val(d) > 1 ? 8 - later.dw.val(d) : 0)
92 ));
93 },
94
95 /**
96 * Returns the start of the next instance of the ISO week value indicated.
97 *
98 * @param {Date} d: The starting date
99 * @param {int} val: The desired value, must be within extent
100 */
101 next: function(d, val) {
1021424 val = val > later.wy.extent(d)[1] ? 1 : val;
103
1041424 var wyThur = later.dw.next(later.wy.start(d), 5),
105 year = later.date.nextRollover(wyThur, val, later.wy, later.Y);
106
107 // handle case where 1st of year is last week of previous month
1081424 if(later.wy.val(year) !== 1) {
109726 year = later.dw.next(year, 2);
110 }
111
1121424 var wyMax = later.wy.extent(year)[1],
113 wyStart = later.wy.start(year);
114
1151424 val = val > wyMax ? 1 : val || wyMax;
116
1171424 return later.date.next(
118 later.Y.val(wyStart),
119 later.M.val(wyStart),
120 later.D.val(wyStart) + 7 * (val-1)
121 );
122 },
123
124 /**
125 * Returns the end of the previous instance of the ISO week value indicated.
126 *
127 * @param {Date} d: The starting date
128 * @param {int} val: The desired value, must be within extent
129 */
130 prev: function(d, val) {
1311420 var wyThur = later.dw.next(later.wy.start(d), 5),
132 year = later.date.prevRollover(wyThur, val, later.wy, later.Y);
133
134 // handle case where 1st of year is last week of previous month
1351420 if(later.wy.val(year) !== 1) {
136787 year = later.dw.next(year, 2);
137 }
138
1391420 var wyMax = later.wy.extent(year)[1],
140 wyEnd = later.wy.end(year);
141
1421420 val = val > wyMax ? wyMax : val || wyMax;
143
1441420 return later.wy.end(later.date.next(
145 later.Y.val(wyEnd),
146 later.M.val(wyEnd),
147 later.D.val(wyEnd) + 7 * (val-1)
148 ));
149 }
150};

constraint/year.js

100%
8
8
0
LineHitsSource
1/**
2* Year Constraint (Y)
3* (c) 2013 Bill, BunKat LLC.
4*
5* Definition for a year constraint type.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
111later.year = later.Y = {
12
13 /**
14 * The name of this constraint.
15 */
16 name: 'year',
17
18 /**
19 * The rough amount of seconds between start and end for this constraint.
20 * (doesn't need to be exact)
21 */
22 range: 31556900,
23
24 /**
25 * The year value of the specified date.
26 *
27 * @param {Date} d: The date to calculate the value of
28 */
29 val: function(d) {
30230114 return d.Y || (d.Y = later.date.getYear.call(d));
31 },
32
33 /**
34 * Returns true if the val is valid for the date specified.
35 *
36 * @param {Date} d: The date to check the value on
37 * @param {Integer} val: The value to validate
38 */
39 isValid: function(d, val) {
403551 return later.Y.val(d) === val;
41 },
42
43 /**
44 * The minimum and maximum valid values for the year constraint.
45 * If max is past 2099, later.D.extent must be fixed to calculate leap years
46 * correctly.
47 */
48 extent: function() {
4929309 return [1970, 2099];
50 },
51
52 /**
53 * The start of the year of the specified date.
54 *
55 * @param {Date} d: The specified date
56 */
57 start: function(d) {
5834142 return d.YStart || (d.YStart = later.date.next(later.Y.val(d)));
59 },
60
61 /**
62 * The end of the year of the specified date.
63 *
64 * @param {Date} d: The specified date
65 */
66 end: function(d) {
6712002 return d.YEnd || (d.YEnd = later.date.prev(later.Y.val(d)));
68 },
69
70 /**
71 * Returns the start of the next instance of the year value indicated.
72 *
73 * @param {Date} d: The starting date
74 * @param {int} val: The desired value, must be within extent
75 */
76 next: function(d, val) {
773430 return val > later.Y.val(d) && val <= later.Y.extent()[1] ?
78 later.date.next(val) : later.NEVER;
79 },
80
81 /**
82 * Returns the end of the previous instance of the year value indicated.
83 *
84 * @param {Date} d: The starting date
85 * @param {int} val: The desired value, must be within extent
86 */
87 prev: function(d, val) {
8815471 return val < later.Y.val(d) && val >= later.Y.extent()[0] ?
89 later.date.prev(val) : later.NEVER;
90 }
91
92};

core/compile.js

100%
36
36
0
LineHitsSource
1/**
2* Compile
3* (c) 2013 Bill, BunKat LLC.
4*
5* Compiles a single schedule definition into a form from which instances can be
6* efficiently calculated from.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
121later.compile = function(schedDef) {
13
14696 var constraints = [],
15 constraintsLen = 0,
16 tickConstraint;
17
18696 for(var key in schedDef) {
191498 var nameParts = key.split('_'),
20 name = nameParts[0],
21 mod = nameParts[1],
22 vals = schedDef[key],
23 constraint = mod ? later.modifier[mod](later[name], vals) : later[name];
24
251498 constraints.push({constraint: constraint, vals: vals});
261498 constraintsLen++;
27 }
28
29 // sort constraints based on their range for best performance (we want to
30 // always skip the largest block of time possible to find the next valid
31 // value)
32696 constraints.sort(function(a,b) {
331232 return a.constraint.range < b.constraint.range;
34 });
35
36 // this is the smallest constraint, we use this one to tick the schedule when
37 // finding multiple instances
38696 tickConstraint = constraints[constraintsLen-1].constraint;
39
40 /**
41 * Returns a function to use when comparing two dates. Encapsulates the
42 * difference between searching for instances forward and backwards so that
43 * the same code can be completely reused for both directions.
44 *
45 * @param {String} dir: The direction to use, either 'next' or 'prev'
46 */
47696 function compareFn(dir) {
481338 return dir === 'next' ?
4943 function(a,b) { return a.getTime() > b.getTime(); } :
5041 function(a,b) { return b.getTime() > a.getTime(); };
51 }
52
53696 return {
54
55 /**
56 * Calculates the start of the next valid occurrence of a particular schedule
57 * that occurs on or after the specified start time.
58 *
59 * @param {String} dir: Direction to search in ('next' or 'prev')
60 * @param {Date} startDate: The first possible valid occurrence
61 */
62 start: function(dir, startDate) {
633887 var next = startDate,
64 nextVal = later.array[dir],
65 maxAttempts = 1000,
66 done;
67
683887 while(maxAttempts-- && !done && next) {
697310 done = true;
70
71 // verify all of the constraints in order since we want to make the
72 // largest jumps possible to find the first valid value
737310 for(var i = 0; i < constraintsLen; i++) {
74
7513372 var constraint = constraints[i].constraint,
76 curVal = constraint.val(next),
77 extent = constraint.extent(next),
78 newVal = nextVal(curVal, constraints[i].vals, extent);
79
8013372 if(!constraint.isValid(next, newVal)) {
813446 next = constraint[dir](next, newVal);
823446 done = false;
833446 break; // need to retest all constraints with new date
84 }
85 }
86 }
87
883887 if(next !== later.NEVER) {
893864 next = dir === 'next' ? tickConstraint.start(next) :
90 tickConstraint.end(next);
91 }
92
93 // if next, move to start of time period. needed when moving backwards
943887 return next;
95 },
96
97 /**
98 * Given a valid start time, finds the next schedule that is invalid.
99 * Useful for finding the end of a valid time range.
100 *
101 * @param {Date} startDate: The first possible valid occurrence
102 */
103 end: function(dir, startDate) {
104
1051338 var result,
106 nextVal = later.array[dir + 'Invalid'],
107 compare = compareFn(dir);
108
1091338 for(var i = constraintsLen-1; i >= 0; i--) {
1101431 var constraint = constraints[i].constraint,
111 curVal = constraint.val(startDate),
112 extent = constraint.extent(startDate),
113 newVal = nextVal(curVal, constraints[i].vals, extent),
114 next;
115
1161431 if(newVal !== undefined) { // constraint has invalid value, use that
1171425 next = constraint[dir](startDate, newVal);
1181425 if(next && (!result || compare(result, next))) {
1191365 result = next;
120 }
121 }
122 }
123
1241338 return result;
125 },
126
127 /**
128 * Ticks the date by the minimum constraint in this schedule
129 *
130 * @param {String} dir: Direction to tick in ('next' or 'prev')
131 * @param {Date} date: The start date to tick from
132 */
133 tick: function(dir, date) {
134900 return new Date(dir === 'next' ?
135 tickConstraint.end(date).getTime() + later.SEC :
136 tickConstraint.start(date).getTime() - later.SEC);
137 },
138
139 /**
140 * Ticks the date to the start of the minimum constraint
141 *
142 * @param {Date} date: The start date to tick from
143 */
144 tickStart: function(date) {
145281 return tickConstraint.start(date);
146 }
147
148 };
149};

core/schedule.js

100%
109
109
0
LineHitsSource
1/**
2* Schedule
3* (c) 2013 Bill, BunKat LLC.
4*
5* Returns an object to calculate future or previous occurrences of the
6* specified schedule.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
121later.schedule = function(sched) {
13490 if(!sched) throw new Error('Missing schedule definition.');
14490 if(!sched.schedules) throw new Error('Definition must include at least one schedule.');
15
16 // compile the schedule components
17490 var schedules = [],
18 schedulesLen = sched.schedules.length,
19 exceptions = [],
20 exceptionsLen = sched.exceptions ? sched.exceptions.length : 0;
21
22490 for(var i = 0; i < schedulesLen; i++) {
23581 schedules.push(later.compile(sched.schedules[i]));
24 }
25
26490 for(var j = 0; j < exceptionsLen; j++) {
27101 exceptions.push(later.compile(sched.exceptions[j]));
28 }
29
30 /**
31 * Calculates count number of instances or ranges for the current schedule,
32 * optionally between the specified startDate and endDate.
33 *
34 * @param {String} dir: The direction to use, either 'next' or 'prev'
35 * @param {Integer} count: The number of instances or ranges to return
36 * @param {Date} startDate: The earliest date a valid instance can occur on
37 * @param {Date} endDate: The latest date a valid instance can occur on
38 * @param {Bool} isRange: True to return ranges, false to return instances
39 */
40490 function getInstances(dir, count, startDate, endDate, isRange) {
41490 var compare = compareFn(dir), // encapsulates difference between directions
42 loopCount = count,
43 maxAttempts = 1000,
44 schedStarts = [], exceptStarts = [],
45 next, end, results = [];
46
47490 startDate = startDate ? new Date(startDate) : new Date();
48490 if(!startDate || !startDate.getTime()) throw new Error('Invalid start date.');
49
50 // Step 1: calculate the earliest start dates for each schedule and exception
51490 setNextStarts(dir, schedules, schedStarts, startDate);
52490 setRangeStarts(dir, exceptions, exceptStarts, startDate);
53
54 // Step 2: select the earliest of the start dates calculated
55490 while(maxAttempts-- && loopCount && (next = findNext(schedStarts, compare))) {
56
57 // Step 3: make sure the start date we found is in range
582077 if(endDate && compare(next, endDate)) {
5936 break;
60 }
61
62 // Step 4: make sure we aren't in the middle of an exception range
632041 if(exceptionsLen) {
641157 updateRangeStarts(dir, exceptions, exceptStarts, next);
651157 if((end = calcRangeOverlap(dir, exceptStarts, next))) {
661062 updateNextStarts(dir, schedules, schedStarts, end);
671062 continue;
68 }
69 }
70
71 // Step 5: Date is good, if range, find the end of the range and update start dates
72979 if(isRange) {
7387 var maxEndDate = calcMaxEndDate(exceptStarts, compare);
7487 end = calcEnd(dir, schedules, schedStarts, next, maxEndDate);
7587 results.push( dir === 'next' ?
76 [
77 new Date(Math.max(startDate, next)),
78 new Date(endDate ? Math.min(end, endDate) : end)
79 ] :
80 [
81 new Date(endDate ? Math.max(endDate, end.getTime()+later.SEC) : end.getTime()+later.SEC),
82 new Date(Math.min(startDate, next.getTime()+later.SEC))
83 ]
84 );
85
8687 updateNextStarts(dir, schedules, schedStarts, end);
87 }
88 // otherwise store the start date and tick the start dates
89 else {
90892 results.push( dir === 'next' ?
91 new Date(Math.max(startDate, next)) :
92 getStart(schedules, schedStarts, next, endDate)
93 );
94
95892 tickStarts(dir, schedules, schedStarts, next);
96 }
97
98979 loopCount--;
99 }
100
101490 return results.length === 0 ? later.NEVER : count === 1 ? results[0] : results;
102 }
103
104 /**
105 * Initially sets the first valid next start times
106 *
107 * @param {String} dir: The direction to use, either 'next' or 'prev'
108 * @param {Array} schedArr: The set of compiled schedules to use
109 * @param {Array} startsArr: The set of cached start dates for the schedules
110 * @param {Date} startDate: Starts earlier than this date will be calculated
111 */
112490 function setNextStarts(dir, schedArr, startsArr, startDate) {
113490 for(var i = 0, len = schedArr.length; i < len; i++) {
114581 startsArr[i] = schedArr[i].start(dir, startDate);
115 }
116 }
117
118 /**
119 * Updates the set of cached start dates to the next valid start dates. Only
120 * schedules where the current start date is less than or equal to the
121 * specified startDate need to be updated.
122 *
123 * @param {String} dir: The direction to use, either 'next' or 'prev'
124 * @param {Array} schedArr: The set of compiled schedules to use
125 * @param {Array} startsArr: The set of cached start dates for the schedules
126 * @param {Date} startDate: Starts earlier than this date will be calculated
127 */
128490 function updateNextStarts(dir, schedArr, startsArr, startDate) {
1291149 var compare = compareFn(dir);
130
1311149 for(var i = 0, len = schedArr.length; i < len; i++) {
1321293 if(startsArr[i] && !compare(startsArr[i], startDate)) {
1331149 startsArr[i] = schedArr[i].start(dir, startDate);
134 }
135 }
136 }
137
138 /**
139 * Updates the set of cached ranges to the next valid ranges. Only
140 * schedules where the current start date is less than or equal to the
141 * specified startDate need to be updated.
142 *
143 * @param {String} dir: The direction to use, either 'next' or 'prev'
144 * @param {Array} schedArr: The set of compiled schedules to use
145 * @param {Array} startsArr: The set of cached start dates for the schedules
146 * @param {Date} startDate: Starts earlier than this date will be calculated
147 */
148490 function setRangeStarts(dir, schedArr, rangesArr, startDate) {
149490 var compare = compareFn(dir);
150
151490 for(var i = 0, len = schedArr.length; i < len; i++) {
152101 var nextStart = schedArr[i].start(dir, startDate);
153
154101 if(!nextStart) {
1551 rangesArr[i] = later.NEVER;
156 }
157 else {
158100 rangesArr[i] = [nextStart, schedArr[i].end(dir, nextStart)];
159 }
160 }
161 }
162
163 /**
164 * Updates the set of cached ranges to the next valid ranges. Only
165 * schedules where the current start date is less than or equal to the
166 * specified startDate need to be updated.
167 *
168 * @param {String} dir: The direction to use, either 'next' or 'prev'
169 * @param {Array} schedArr: The set of compiled schedules to use
170 * @param {Array} startsArr: The set of cached start dates for the schedules
171 * @param {Date} startDate: Starts earlier than this date will be calculated
172 */
173490 function updateRangeStarts(dir, schedArr, rangesArr, startDate) {
1741157 var compare = compareFn(dir);
175
1761157 for(var i = 0, len = schedArr.length; i < len; i++) {
1771277 if(rangesArr[i] && !compare(rangesArr[i][0], startDate)) {
1781150 var nextStart = schedArr[i].start(dir, startDate);
179
1801150 if(!nextStart) {
1813 rangesArr[i] = later.NEVER;
182 }
183 else {
1841147 rangesArr[i] = [nextStart, schedArr[i].end(dir, nextStart)];
185 }
186 }
187 }
188 }
189
190 /**
191 * Increments all schedules with next start equal to startDate by one tick.
192 * Tick size is determined by the smallest constraint within a schedule.
193 *
194 * @param {String} dir: The direction to use, either 'next' or 'prev'
195 * @param {Array} schedArr: The set of compiled schedules to use
196 * @param {Array} startsArr: The set of cached start dates for the schedules
197 * @param {Date} startDate: The date that should cause a schedule to tick
198 */
199490 function tickStarts(dir, schedArr, startsArr, startDate) {
200892 for(var i = 0, len = schedArr.length; i < len; i++) {
201989 if(startsArr[i] && startsArr[i].getTime() === startDate.getTime()) {
202892 startsArr[i] = schedArr[i].start(dir, schedArr[i].tick(dir, startDate));
203 }
204 }
205 }
206
207 /**
208 * Determines the start date of the schedule that produced startDate
209 *
210 * @param {Array} schedArr: The set of compiled schedules to use
211 * @param {Array} startsArr: The set of cached start dates for the schedules
212 * @param {Date} startDate: The date that should cause a schedule to tick
213 * @param {Date} minEndDate: The minimum end date to return
214 */
215490 function getStart(schedArr, startsArr, startDate, minEndDate) {
216281 var result;
217
218281 for(var i = 0, len = startsArr.length; i < len; i++) {
219300 if(startsArr[i] && startsArr[i].getTime() === startDate.getTime()) {
220281 var start = schedArr[i].tickStart(startDate);
221
222281 if(minEndDate && (start < minEndDate)) {
2233 return minEndDate;
224 }
225
226278 if(!result || (start > result)) {
227278 result = start;
228 }
229 }
230 }
231
232278 return result;
233 }
234
235 /**
236 * Calculates the end of the overlap between any exception schedule and the
237 * specified start date. Returns undefined if there is no overlap.
238 *
239 * @param {String} dir: The direction to use, either 'next' or 'prev'
240 * @param {Array} schedArr: The set of compiled schedules to use
241 * @param {Array} rangesArr: The set of cached start dates for the schedules
242 * @param {Date} startDate: The valid date for which the overlap will be found
243 */
244490 function calcRangeOverlap(dir, rangesArr, startDate) {
2451157 var compare = compareFn(dir), result;
246
2471157 for(var i = 0, len = rangesArr.length; i < len; i++) {
2481277 var range = rangesArr[i];
249
2501277 if(range && !compare(range[0], startDate) &&
251 (!range[1] || compare(range[1], startDate))) {
252 // startDate is in the middle of an exception range
2531070 if(!result || compare(range[1], result)) {
2541062 result = range[1];
255 }
256 }
257 }
258
2591157 return result;
260 }
261
262 /**
263 * Calculates the earliest start of an exception schedule, this is the maximum
264 * end date of the schedule.
265 *
266 * @param {Array} exceptsArr: The set of cached exception ranges
267 * @param {Array} compare: The compare function to use to determine earliest
268 */
269490 function calcMaxEndDate(exceptsArr, compare) {
27087 var result;
271
27287 for(var i = 0, len = exceptsArr.length; i < len; i++) {
27381 if(exceptsArr[i] && (!result || compare(result, exceptsArr[i][0]))) {
27455 result = exceptsArr[i][0];
275 }
276 }
277
27887 return result;
279 }
280
281
282 /**
283 * Calculates the next invalid date for a particular schedules starting from
284 * the specified valid start date.
285 *
286 * @param {String} dir: The direction to use, either 'next' or 'prev'
287 * @param {Array} schedArr: The set of compiled schedules to use
288 * @param {Array} startsArr: The set of cached start dates for the schedules
289 * @param {Date} startDate: The valid date for which the end date will be found
290 * @param {Date} maxEndDate: The latested possible end date or null for none
291 */
292490 function calcEnd(dir, schedArr, startsArr, startDate, maxEndDate) {
29387 var compare = compareFn(dir), result;
294
29587 for(var i = 0, len = schedArr.length; i < len; i++) {
296111 var start = startsArr[i];
297
298111 if(start && start.getTime() === startDate.getTime()) {
29987 var end = schedArr[i].end(dir, start);
300
301 // if the end date is past the maxEndDate, just return the maxEndDate
30287 if(maxEndDate && compare(end, maxEndDate)) {
30330 return maxEndDate;
304 }
305
306 // otherwise, return the maximum end date that was calculated
30757 if(!result || compare(end, result)) {
30857 result = end;
309 }
310 }
311 }
312
31357 return result;
314 }
315
316 /**
317 * Returns a function to use when comparing two dates. Encapsulates the
318 * difference between searching for instances forward and backwards so that
319 * the same code can be completely reused for both directions.
320 *
321 * @param {String} dir: The direction to use, either 'next' or 'prev'
322 */
323490 function compareFn(dir) {
3244530 return dir === 'next' ?
3256409 function(a,b) { return a.getTime() > b.getTime(); } :
326892 function(a,b) { return b.getTime() > a.getTime(); };
327 }
328
329 /**
330 * Returns the next value in an array using the function passed in as compare
331 * to do the comparison. Skips over null or undefined values.
332 *
333 * @param {Array} arr: The array of values
334 * @param {Function} compare: The comparison function to use
335 */
336490 function findNext(arr, compare) {
3372083 var next = arr[0];
338
3392083 for(var i = 1, len = arr.length; i < len; i++) {
340261 if(arr[i] && compare(next, arr[i])) {
34138 next = arr[i];
342 }
343 }
344
3452083 return next;
346 }
347
348490 return {
349
350 /**
351 * Returns true if d is a valid occurrence of the current schedule.
352 *
353 * @param {Date} d: The date to check
354 */
355 isValid: function(d) {
356356 return getInstances('next', 1, d, d) !== later.NEVER;
357 },
358
359 /**
360 * Finds the next valid instance or instances of the current schedule,
361 * optionally between a specified start and end date. Start date is
362 * Date.now() by default, end date is unspecified. Start date must be
363 * smaller than end date.
364 *
365 * @param {Integer} count: The number of instances to return
366 * @param {Date} startDate: The earliest a valid instance can occur
367 * @param {Date} endDate: The latest a valid instance can occur
368 */
369 next: function(count, startDate, endDate) {
37063 return getInstances('next', count || 1, startDate, endDate);
371 },
372
373 /**
374 * Finds the previous valid instance or instances of the current schedule,
375 * optionally between a specified start and end date. Start date is
376 * Date.now() by default, end date is unspecified. Start date must be
377 * greater than end date.
378 *
379 * @param {Integer} count: The number of instances to return
380 * @param {Date} startDate: The earliest a valid instance can occur
381 * @param {Date} endDate: The latest a valid instance can occur
382 */
383 prev: function(count, startDate, endDate) {
38455 return getInstances('prev', count || 1, startDate, endDate);
385 },
386
387 /**
388 * Finds the next valid range or ranges of the current schedule,
389 * optionally between a specified start and end date. Start date is
390 * Date.now() by default, end date is unspecified. Start date must be
391 * greater than end date.
392 *
393 * @param {Integer} count: The number of ranges to return
394 * @param {Date} startDate: The earliest a valid range can occur
395 * @param {Date} endDate: The latest a valid range can occur
396 */
397 nextRange: function(count, startDate, endDate) {
3989 return getInstances('next', count || 1, startDate, endDate, true);
399 },
400
401 /**
402 * Finds the previous valid range or ranges of the current schedule,
403 * optionally between a specified start and end date. Start date is
404 * Date.now() by default, end date is unspecified. Start date must be
405 * greater than end date.
406 *
407 * @param {Integer} count: The number of ranges to return
408 * @param {Date} startDate: The earliest a valid range can occur
409 * @param {Date} endDate: The latest a valid range can occur
410 */
411 prevRange: function(count, startDate, endDate) {
4127 return getInstances('prev', count || 1, startDate, endDate, true);
413 }
414 };
415};

core/setinterval.js

100%
9
9
0
LineHitsSource
1/**
2* Set Interval
3* (c) 2013 Bill, BunKat LLC.
4*
5* Works similar to setInterval() but allows you to specify a Later schedule
6* instead of milliseconds.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.setInterval = function(fn, sched) {
14
152 var t = later.setTimeout(scheduleTimeout, sched),
16 done = false;
17
18 /**
19 * Executes the specified function and then sets the timeout for the next
20 * interval.
21 */
222 function scheduleTimeout() {
234 if(!done) {
243 fn();
253 t = later.setTimeout(scheduleTimeout, sched);
26 }
27 }
28
292 return {
30
31 /**
32 * Clears the timeout.
33 */
34 clear: function() {
352 done = true;
362 t.clear();
37 }
38
39 };
40
41};

core/settimeout.js

100%
12
12
0
LineHitsSource
1/**
2* Set Timeout
3* (c) 2013 Bill, BunKat LLC.
4*
5* Works similar to setTimeout() but allows you to specify a Later schedule
6* instead of milliseconds.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.setTimeout = function(fn, sched) {
14
158 var s = later.schedule(sched), t;
168 scheduleTimeout();
17
18 /**
19 * Schedules the timeout to occur. If the next occurrence is greater than the
20 * max supported delay (2147483647 ms) than we delay for that amount before
21 * attempting to schedule the timeout again.
22 */
238 function scheduleTimeout() {
248 var now = Date.now(),
25 next = s.next(2, now),
26 diff = next[0].getTime() - now;
27
28 // minimum time to fire is one second, use next occurrence instead
298 if(diff < 1000) {
306 diff = next[1].getTime() - now;
31 }
32
338 if(diff < 2147483647) {
347 t = setTimeout(fn, diff);
35 }
36 else {
371 t = setTimeout(scheduleTimeout, 2147483647);
38 }
39 }
40
418 return {
42
43 /**
44 * Clears the timeout.
45 */
46 clear: function() {
474 clearTimeout(t);
48 }
49
50 };
51
52};

date/constant.js

100%
7
7
0
LineHitsSource
1/**
2* Date Constants
3* (c) 2013 Bill, BunKat LLC.
4*
5* Useful constants for dealing with time conversions.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
11
12// Time to milliseconds conversion
131later.SEC = 1000;
141later.MIN = later.SEC * 60;
151later.HOUR = later.MIN * 60;
161later.DAY = later.HOUR * 24;
171later.WEEK = later.DAY * 7;
18
19// Array of days in each month, must be corrected for leap years
201later.DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
21
22// constant for specifying that a schedule can never occur
231later.NEVER = 0;

date/date.js

100%
1
1
0
LineHitsSource
11later.date = {};
2
3
4
5
6
7
8

date/next.js

100%
2
2
0
LineHitsSource
1/**
2* Next
3* (c) 2013 Bill, BunKat LLC.
4*
5* Creates a new Date object defaulted to the first second after the specified
6* values.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
13/**
14* Builds and returns a new Date using the specified values. Date
15* returned is either using Local time or UTC based on isLocal.
16*
17* @param {Int} Y: Four digit year
18* @param {Int} M: Month between 1 and 12, defaults to 1
19* @param {Int} D: Date between 1 and 31, defaults to 1
20* @param {Int} h: Hour between 0 and 23, defaults to 0
21* @param {Int} m: Minute between 0 and 59, defaults to 0
22* @param {Int} s: Second between 0 and 59, defaults to 0
23*/
241later.date.next = function(Y, M, D, h, m, s) {
25
26144440 return later.date.build(
27 Y,
28 M !== undefined ? M-1 : 0,
29 D !== undefined ? D : 1,
30 h || 0,
31 m || 0,
32 s || 0);
33};

date/nextrollover.js

100%
3
3
0
LineHitsSource
1/**
2* Next Rollover
3* (c) 2013 Bill, BunKat LLC.
4*
5* Determines if a value will cause a particualr constraint to rollover to the
6* next largest time period. Used primarily when a constraint has a
7* variable extent.
8*
9* Later is freely distributable under the MIT license.
10* For all details and documentation:
11* http://github.com/bunkat/later
12*/
13
141later.date.nextRollover = function(d, val, constraint, period) {
157893 var cur = constraint.val(d),
16 max = constraint.extent(d)[1];
17
187893 return (((val || max) <= cur) || val > max) ?
19 new Date(period.end(d).getTime() + later.SEC) :
20 period.start(d);
21};

date/prev.js

100%
8
8
0
LineHitsSource
1/**
2* Prev
3* (c) 2013 Bill, BunKat LLC.
4*
5* Creates a new Date object defaulted to the last second after the specified
6* values.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
13/**
14* Builds and returns a new Date using the specified values. Date
15* returned is either using Local time or UTC based on isLocal.
16*
17* @param {Int} Y: Four digit year
18* @param {Int} M: Month between 0 and 11, defaults to 11
19* @param {Int} D: Date between 1 and 31, defaults to last day of month
20* @param {Int} h: Hour between 0 and 23, defaults to 23
21* @param {Int} m: Minute between 0 and 59, defaults to 59
22* @param {Int} s: Second between 0 and 59, defaults to 59
23*/
241later.date.prev = function(Y, M, D, h, m, s) {
25
2651857 var len = arguments.length;
2751857 M = len < 2 ? 11 : M-1;
2851857 D = len < 3 ? later.D.extent(later.date.next(Y, M+1))[1] : D;
2951857 h = len < 4 ? 23 : h;
3051857 m = len < 5 ? 59 : m;
3151857 s = len < 6 ? 59 : s;
32
3351857 return later.date.build(Y, M, D, h, m, s);
34};

date/prevrollover.js

100%
3
3
0
LineHitsSource
1/**
2* Prev Rollover
3* (c) 2013 Bill, BunKat LLC.
4*
5* Determines if a value will cause a particualr constraint to rollover to the
6* previous largest time period. Used primarily when a constraint has a
7* variable extent.
8*
9* Later is freely distributable under the MIT license.
10* For all details and documentation:
11* http://github.com/bunkat/later
12*/
13
141later.date.prevRollover = function(d, val, constraint, period) {
157638 var cur = constraint.val(d);
16
177638 return (val >= cur || !val) ?
18 period.start(period.prev(d, period.val(d)-1)) :
19 period.start(d);
20};

date/timezone.js

100%
16
16
0
LineHitsSource
1/**
2* Timezone
3* (c) 2013 Bill, BunKat LLC.
4*
5* Configures helper functions to switch between useing local time and UTC. Later
6* uses UTC time by default.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
131later.date.timezone = function(useLocalTime) {
14
15 // configure the date builder used to create new dates in the right timezone
1641460 later.date.build = useLocalTime ?
1785178 function(Y, M, D, h, m, s) { return new Date(Y, M, D, h, m, s); } :
18111119 function(Y, M, D, h, m, s) { return new Date(Date.UTC(Y, M, D, h, m, s)); };
19
20 // configure the accessor methods
2141460 var get = useLocalTime ? 'get' : 'getUTC',
22 d = Date.prototype;
23
2441460 later.date.getYear = d[get + 'FullYear'];
2541460 later.date.getMonth = d[get + 'Month'];
2641460 later.date.getDate = d[get + 'Date'];
2741460 later.date.getDay = d[get + 'Day'];
2841460 later.date.getHour = d[get + 'Hours'];
2941460 later.date.getMin = d[get + 'Minutes'];
3041460 later.date.getSec = d[get + 'Seconds'];
31
32 // set the isUTC flag
3341460 later.date.isUTC = !useLocalTime;
34};
35
36// friendly names for available timezones
3720801later.date.UTC = function() { later.date.timezone(false); };
3820661later.date.localTime = function() { later.date.timezone(true); };
39
40// use UTC by default
411later.date.UTC();

modifier/after.js

100%
8
8
0
LineHitsSource
1/**
2* After Modifier
3* (c) 2013 Bill, BunKat LLC.
4*
5* Modifies a constraint such that all values that are greater than the
6* specified value are considered valid.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
13/**
14* Creates a new modified constraint.
15*
16* @param {Constraint} constraint: The constraint to be modified
17* @param {Integer} value: The starting value of the after constraint
18*/
191later.modifier.after = later.modifier.a = function(constraint, values) {
20
21153 var value = values[0];
22
23153 return {
24
25 /**
26 * Returns the name of the constraint with the 'after' modifier.
27 */
28 name: 'after ' + constraint.name,
29
30 /**
31 * Pass through to the constraint.
32 */
33 range: (constraint.extent(new Date())[1] - value) * constraint.range,
34
35 /**
36 * The value of the specified date. Returns value for any constraint val
37 * that is greater than or equal to value.
38 *
39 * @param {Date} d: The date to calculate the value of
40 */
41 val: constraint.val,
42
43 /**
44 * Returns true if the val is valid for the date specified.
45 *
46 * @param {Date} d: The date to check the value on
47 * @param {Integer} val: The value to validate
48 */
49 isValid: function(d, val) {
501494 return this.val(d) >= value;
51 },
52
53 /**
54 * Pass through to the constraint.
55 */
56 extent: constraint.extent,
57
58 /**
59 * Pass through to the constraint.
60 */
61 start: constraint.start,
62
63 /**
64 * Pass through to the constraint.
65 */
66 end: constraint.end,
67
68 /**
69 * Pass through to the constraint.
70 */
71 next: function(startDate, val) {
72143 if(val != value) val = constraint.extent(startDate)[0];
73105 return constraint.next(startDate, val);
74 },
75
76 /**
77 * Pass through to the constraint.
78 */
79 prev: function(startDate, val) {
8059 val = val === value ? constraint.extent(startDate)[1] : value - 1;
8159 return constraint.prev(startDate, val);
82 }
83
84 };
85
86};

modifier/before.js

100%
8
8
0
LineHitsSource
1/**
2* Before Modifier
3* (c) 2013 Bill, BunKat LLC.
4*
5* Modifies a constraint such that all values that are less than the
6* specified value are considered valid.
7*
8* Later is freely distributable under the MIT license.
9* For all details and documentation:
10* http://github.com/bunkat/later
11*/
12
13/**
14* Creates a new modified constraint.
15*
16* @param {Constraint} constraint: The constraint to be modified
17* @param {Integer} value: The starting value of the before constraint
18*/
191later.modifier.before = later.modifier.b = function(constraint, values) {
20
21141 var value = values[values.length-1];
22
23141 return {
24
25 /**
26 * Returns the name of the constraint with the 'before' modifier.
27 */
28 name: 'before ' + constraint.name,
29
30 /**
31 * Pass through to the constraint.
32 */
33 range: constraint.range * (value-1),
34
35 /**
36 * The value of the specified date. Returns value for any constraint val
37 * that is less than or equal to value.
38 *
39 * @param {Date} d: The date to calculate the value of
40 */
41 val: constraint.val,
42
43 /**
44 * Returns true if the val is valid for the date specified.
45 *
46 * @param {Date} d: The date to check the value on
47 * @param {Integer} val: The value to validate
48 */
49 isValid: function(d, val) {
50491 return this.val(d) < value;
51 },
52
53 /**
54 * Pass through to the constraint.
55 */
56 extent: constraint.extent,
57
58 /**
59 * Pass through to the constraint.
60 */
61 start: constraint.start,
62
63 /**
64 * Jump to the end of the range.
65 */
66 end: constraint.end,
67
68 /**
69 * Pass through to the constraint.
70 */
71 next: function(startDate, val) {
7281 val = val === value ? constraint.extent(startDate)[0] : value;
7381 return constraint.next(startDate, val);
74 },
75
76 /**
77 * Pass through to the constraint.
78 */
79 prev: function(startDate, val) {
8055 val = val === value ? value - 1 : constraint.extent(startDate)[1];
8155 return constraint.prev(startDate, val);
82 }
83
84 };
85
86};

modifier/modifier.js

100%
1
1
0
LineHitsSource
1
21later.modifier = {};

parse/cron.js

100%
80
80
0
LineHitsSource
1/**
2* Cron
3* (c) 2013 Bill, BunKat LLC.
4*
5* Creates a valid Later schedule from a valid cron expression.
6*
7* Later is freely distributable under the MIT license.
8* For all details and documentation:
9* http://github.com/bunkat/later
10*/
11
12/**
13* Parses a valid cron expression and produces a valid schedule that
14* can then be used with Later.
15*
16* CronParser().parse('* 5 * * * * *', true);
17*
18* @param {String} expr: The cron expression to parse
19* @param {Bool} hasSeconds: True if the expression uses a seconds field
20* @api public
21*/
221later.parse.cron = function (expr, hasSeconds) {
23
24 // Constant array to convert valid names to values
2587 var NAMES = {
26 JAN: 1, FEB: 2, MAR: 3, APR: 4, MAY: 5, JUN: 6, JUL: 7, AUG: 8,
27 SEP: 9, OCT: 10, NOV: 11, DEC: 12,
28 SUN: 1, MON: 2, TUE: 3, WED: 4, THU: 5, FRI: 6, SAT: 7
29 };
30
31 // Contains the index, min, and max for each of the constraints
3287 var FIELDS = {
33 s: [0, 0, 59], // seconds
34 m: [1, 0, 59], // minutes
35 h: [2, 0, 23], // hours
36 D: [3, 1, 31], // day of month
37 M: [4, 1, 12], // month
38 Y: [6, 1970, 2099], // year
39 d: [5, 1, 7, 1] // day of week
40 };
41
42 /**
43 * Returns the value + offset if value is a number, otherwise it
44 * attempts to look up the value in the NAMES table and returns
45 * that result instead.
46 *
47 * @param {Int,String} value: The value that should be parsed
48 * @param {Int} offset: Any offset that must be added to the value
49 */
5087 function getValue(value, offset) {
51321 return isNaN(value) ? NAMES[value] || null : +value + (offset || 0);
52 }
53
54 /**
55 * Returns a deep clone of a schedule skipping any day of week
56 * constraints.
57 *
58 * @param {Sched} sched: The schedule that will be cloned
59 */
6087 function cloneSchedule(sched) {
616 var clone = {}, field;
62
636 for(field in sched) {
6411 if (field !== 'dc' && field !== 'd') {
652 clone[field] = sched[field].slice(0);
66 }
67 }
68
696 return clone;
70 }
71
72 /**
73 * Adds values to the specified constraint in the current schedule.
74 *
75 * @param {Sched} sched: The schedule to add the constraint to
76 * @param {String} name: Name of constraint to add
77 * @param {Int} min: Minimum value for this constraint
78 * @param {Int} max: Maximum value for this constraint
79 * @param {Int} inc: The increment to use between min and max
80 */
8187 function add(sched, name, min, max, inc) {
82187 var i = min;
83
84187 if (!sched[name]) {
85163 sched[name] = [];
86 }
87
88187 while (i <= max) {
89359 if (sched[name].indexOf(i) < 0) {
90353 sched[name].push(i);
91 }
92359 i += inc || 1;
93 }
94 }
95
96 /**
97 * Adds a hash item (of the form x#y or xL) to the schedule.
98 *
99 * @param {Schedule} schedules: The current schedule array to add to
100 * @param {Schedule} curSched: The current schedule to add to
101 * @param {Int} value: The value to add (x of x#y or xL)
102 * @param {Int} hash: The hash value to add (y of x#y)
103 */
10487 function addHash(schedules, curSched, value, hash) {
105 // if there are any existing day of week constraints that
106 // aren't equal to the one we're adding, create a new
107 // composite schedule
10814 if ((curSched.d && !curSched.dc) ||
109 (curSched.dc && curSched.dc.indexOf(hash) < 0)) {
1106 schedules.push(cloneSchedule(curSched));
1116 curSched = schedules[schedules.length-1];
112 }
113
11414 add(curSched, 'd', value, value);
11514 add(curSched, 'dc', hash, hash);
116 }
117
11887 function addWeekday(s, curSched, value) {
1194 var except1 = {}, except2 = {};
1204 if (value=== 1) {
121 // cron doesn't pass month boundaries, so if 1st is a
122 // weekend then we need to use 2nd or 3rd instead
1231 add(curSched, 'D', 1, 3);
1241 add(curSched, 'd', NAMES.MON, NAMES.FRI);
1251 add(except1, 'D', 2, 2);
1261 add(except1, 'd', NAMES.TUE, NAMES.FRI);
1271 add(except2, 'D', 3, 3);
1281 add(except2, 'd', NAMES.TUE, NAMES.FRI);
129 } else {
130 // normally you want the closest day, so if v is a
131 // Saturday, use the previous Friday. If it's a
132 // sunday, use the following Monday.
1333 add(curSched, 'D', value-1, value+1);
1343 add(curSched, 'd', NAMES.MON, NAMES.FRI);
1353 add(except1, 'D', value-1, value-1);
1363 add(except1, 'd', NAMES.MON, NAMES.THU);
1373 add(except2, 'D', value+1, value+1);
1383 add(except2, 'd', NAMES.TUE, NAMES.FRI);
139 }
1404 s.exceptions.push(except1);
1414 s.exceptions.push(except2);
142 }
143
144 /**
145 * Adds a range item (of the form x-y/z) to the schedule.
146 *
147 * @param {String} item: The cron expression item to add
148 * @param {Schedule} curSched: The current schedule to add to
149 * @param {String} name: The name to use for this constraint
150 * @param {Int} min: The min value for the constraint
151 * @param {Int} max: The max value for the constraint
152 * @param {Int} offset: The offset to apply to the cron value
153 */
15487 function addRange(item, curSched, name, min, max, offset) {
155 // parse range/x
15636 var incSplit = item.split('/'),
157 inc = +incSplit[1],
158 range = incSplit[0];
159
160 // parse x-y or * or 0
16136 if (range !== '*' && range !== '0') {
16224 var rangeSplit = range.split('-');
16324 min = getValue(rangeSplit[0], offset);
164
165 // fix for issue #13, range may be single digit
16624 max = getValue(rangeSplit[1], offset) || max;
167 }
16836 add(curSched, name, min, max, inc);
169 }
170
171 /**
172 * Parses a particular item within a cron expression.
173 *
174 * @param {String} item: The cron expression item to parse
175 * @param {Schedule} s: The existing set of schedules
176 * @param {String} name: The name to use for this constraint
177 * @param {Int} min: The min value for the constraint
178 * @param {Int} max: The max value for the constraint
179 * @param {Int} offset: The offset to apply to the cron value
180 */
18187 function parse(item, s, name, min, max, offset) {
182153 var value,
183 split,
184 schedules = s.schedules,
185 curSched = schedules[schedules.length-1];
186
187 // L just means min - 1 (this also makes it work for any field)
188153 if (item === 'L') {
1892 item = min - 1;
190 }
191
192 // parse x
193153 if ((value = getValue(item, offset)) !== null) {
19499 add(curSched, name, value, value);
195 }
196 // parse xW
19754 else if ((value = getValue(item.replace('W', ''), offset)) !== null) {
1984 addWeekday(s, curSched, value);
199 }
200 // parse xL
20150 else if ((value = getValue(item.replace('L', ''), offset)) !== null) {
2026 addHash(schedules, curSched, value, min-1);
203 }
204 // parse x#y
20544 else if ((split = item.split('#')).length === 2) {
2068 value = getValue(split[0], offset);
2078 addHash(schedules, curSched, value, getValue(split[1]));
208 }
209 // parse x-y or x-y/z or */z or 0/z
210 else {
21136 addRange(item, curSched, name, min, max, offset);
212 }
213 }
214
215 /**
216 * Returns true if the item is either of the form x#y or xL.
217 *
218 * @param {String} item: The expression item to check
219 */
22087 function isHash(item) {
22133 return item.indexOf('#') > -1 || item.indexOf('L') > 0;
222 }
223
224
22587 function itemSorter(a,b) {
22628 return isHash(a) && !isHash(b) ? 1 : 0;
227 }
228
229 /**
230 * Parses each of the fields in a cron expression. The expression must
231 * include the seconds field, the year field is optional.
232 *
233 * @param {String} expr: The cron expression to parse
234 */
23587 function parseExpr(expr) {
23687 var schedule = {schedules: [{}], exceptions: []},
237 components = expr.split(' '),
238 field, f, component, items;
239
24087 for(field in FIELDS) {
241609 f = FIELDS[field];
242609 component = components[f[0]];
243609 if (component && component !== '*' && component !== '?') {
244 // need to sort so that any #'s come last, otherwise
245 // schedule clones to handle # won't contain all of the
246 // other constraints
247125 items = component.split(',').sort(itemSorter);
248125 var i, length = items.length;
249125 for (i = 0; i < length; i++) {
250153 parse(items[i], schedule, field, f[1], f[2], f[3]);
251 }
252 }
253 }
254
25587 return schedule;
256 }
257
25887 var e = expr.toUpperCase();
25987 return parseExpr(hasSeconds ? e : '0 ' + e);
260};

parse/parse.js

100%
1
1
0
LineHitsSource
11later.parse = {};

parse/recur.js

90%
92
83
9
LineHitsSource
1/**
2* Simple API for generating valid schedules for Later.js. All commands
3* are chainable.
4*
5* Example:
6*
7* Every 5 minutes between minutes 15 and 45 of each hour and also
8* at 9:00 am every day, except in the months of January and February
9*
10* recur().every(5).minute().between(15, 45).and().at('09:00:00')
11* .except().on(0, 1).month();
12*/
131later.parse.recur = function () {
14
15154 var schedules = [],
16 exceptions = [],
17 cur,
18 curArr = schedules,
19 curName,
20 values, every, modifier, applyMin, applyMax, i, last;
21
22 /**
23 * Adds values to the specified constraint in the current schedule.
24 *
25 * @param {String} name: Name of constraint to add
26 * @param {Int} min: Minimum value for this constraint
27 * @param {Int} max: Maximum value for this constraint
28 */
29154 function add(name, min, max) {
30240 name = modifier ? name + '_' + modifier : name;
31
32240 if (!cur) {
33162 curArr.push({});
34162 cur = curArr[0];
35 }
36
37240 if (!cur[name]) {
38229 cur[name] = [];
39 }
40
41240 curName = cur[name];
42
43240 if (every) {
4450 values = [];
4550 for (i = min; i <= max; i += every) {
46782 values.push(i);
47 }
48
49 // save off values in case of startingOn or between
5050 last = {n: name, x: every, c: curName.length, m: max};
51 }
52
53240 values = applyMin ? [min] : applyMax ? [max] : values;
54240 var length = values.length;
55240 for (i = 0; i < length; i += 1) {
561024 var val = values[i];
571024 if (curName.indexOf(val) < 0) {
581024 curName.push(val);
59 }
60 }
61
62 // reset the built up state
63240 values = every = modifier = applyMin = applyMax = 0;
64 }
65
66154 return {
67
68 /**
69 * Set of constraints that must be met for an occurrence to be valid.
70 *
71 * @api public
72 */
73 schedules: schedules,
74
75 /**
76 * Set of exceptions that must not be met for an occurrence to be
77 * valid.
78 *
79 * @api public
80 */
81 exceptions: exceptions,
82
83 /**
84 * Specifies the specific instances of a time period that are valid.
85 * Must be followed by the desired time period (minute(), hour(),
86 * etc). For example, to specify a schedule for the 5th and 25th
87 * minute of every hour:
88 *
89 * recur().on(5, 25).minute();
90 *
91 * @param {Int} args: One or more valid instances
92 * @api public
93 */
94 on: function () {
95108 values = arguments[0] instanceof Array ? arguments[0] : arguments;
96108 return this;
97 },
98
99 /**
100 * Specifies the recurring interval of a time period that are valid.
101 * Must be followed by the desired time period (minute(), hour(),
102 * etc). For example, to specify a schedule for every 4 hours in the
103 * day:
104 *
105 * recur().every(4).hour();
106 *
107 * @param {Int} x: Recurring interval
108 * @api public
109 */
110 every: function (x) {
11142 every = x || 1;
11242 return this;
113 },
114
115 /**
116 * Specifies the minimum valid value. For example, to specify a schedule
117 * that is valid for all hours after four:
118 *
119 * recur().after(4).hour();
120 *
121 * @param {Int} x: Recurring interval
122 * @api public
123 */
124 after: function (x) {
12531 modifier = 'a';
12631 values = [x];
12731 return this;
128 },
129
130 /**
131 * Specifies the maximum valid value. For example, to specify a schedule
132 * that is valid for all hours before four:
133 *
134 * recur().before(4).hour();
135 *
136 * @param {Int} x: Recurring interval
137 * @api public
138 */
139 before: function (x) {
14029 modifier = 'b';
14129 values = [x];
14229 return this;
143 },
144
145 /**
146 * Specifies that the first instance of a time period is valid. Must
147 * be followed by the desired time period (minute(), hour(), etc).
148 * For example, to specify a schedule for the first day of every
149 * month:
150 *
151 * recur().first().dayOfMonth();
152 *
153 * @api public
154 */
155 first: function () {
1568 applyMin = 1;
1578 return this;
158 },
159
160 /**
161 * Specifies that the last instance of a time period is valid. Must
162 * be followed by the desired time period (minute(), hour(), etc).
163 * For example, to specify a schedule for the last day of every year:
164 *
165 * recur().last().dayOfYear();
166 *
167 * @api public
168 */
169 last: function () {
17011 applyMax = 1;
17111 return this;
172 },
173
174 /**
175 * Specifies a specific time that is valid. Time must be specified in
176 * hh:mm:ss format using 24 hour time. For example, to specify
177 * a schedule for 8:30 pm every day:
178 *
179 * recur().time('20:30:00');
180 *
181 * @param {String} time: Time in hh:mm:ss 24-hour format
182 * @api public
183 */
184 time: function () {
185 //values = arguments;
18647 for (var i = 0, len = values.length; i < len; i++) {
18748 var split = values[i].split(':');
18892 if(split.length < 3) split.push(0);
18948 values[i] = (+split[0]) * 3600 + (+split[1]) * 60 + (+split[2]);
190 }
191
19247 add('t');
19347 return this;
194 },
195
196 /**
197 * Seconds time period, denotes seconds within each minute.
198 * Minimum value is 0, maximum value is 59. Specify 59 for last.
199 *
200 * recur().on(5, 15, 25).second();
201 *
202 * @api public
203 */
204 second: function () {
20521 add('s', 0, 59);
20621 return this;
207 },
208
209 /**
210 * Minutes time period, denotes minutes within each hour.
211 * Minimum value is 0, maximum value is 59. Specify 59 for last.
212 *
213 * recur().on(5, 15, 25).minute();
214 *
215 * @api public
216 */
217 minute: function () {
21848 add('m', 0, 59);
21948 return this;
220 },
221
222 /**
223 * Hours time period, denotes hours within each day.
224 * Minimum value is 0, maximum value is 23. Specify 23 for last.
225 *
226 * recur().on(5, 15, 25).hour();
227 *
228 * @api public
229 */
230 hour: function () {
23112 add('h', 0, 23);
23212 return this;
233 },
234
235 /**
236 * Days of month time period, denotes number of days within a month.
237 * Minimum value is 1, maximum value is 31. Specify 0 for last.
238 *
239 * recur().every(2).dayOfMonth();
240 *
241 * @api public
242 */
243 dayOfMonth: function () {
24420 add('D', 1, applyMax ? 0 : 31);
24520 return this;
246 },
247
248 /**
249 * Days of week time period, denotes the days within a week.
250 * Minimum value is 1, maximum value is 7. Specify 0 for last.
251 * 1 - Sunday
252 * 2 - Monday
253 * 3 - Tuesday
254 * 4 - Wednesday
255 * 5 - Thursday
256 * 6 - Friday
257 * 7 - Saturday
258 *
259 * recur().on(1).dayOfWeek();
260 *
261 * @api public
262 */
263 dayOfWeek: function () {
26427 add('d', 1, 7);
26527 return this;
266 },
267
268 /**
269 * Short hand for on(1,7).dayOfWeek()
270 *
271 * @api public
272 */
273 onWeekend: function() {
2741 values = [1,7];
2751 return this.dayOfWeek();
276 },
277
278 /**
279 * Short hand for on(2,3,4,5,6).dayOfWeek()
280 *
281 * @api public
282 */
283 onWeekday: function() {
2842 values = [2,3,4,5,6];
2852 return this.dayOfWeek();
286 },
287
288 /**
289 * Days of week count time period, denotes the number of times a
290 * particular day has occurred within a month. Used to specify
291 * things like second Tuesday, or third Friday in a month.
292 * Minimum value is 1, maximum value is 5. Specify 0 for last.
293 * 1 - First occurrence
294 * 2 - Second occurrence
295 * 3 - Third occurrence
296 * 4 - Fourth occurrence
297 * 5 - Fifth occurrence
298 * 0 - Last occurrence
299 *
300 * recur().on(1).dayOfWeek().on(1).dayOfWeekCount();
301 *
302 * @api public
303 */
304 dayOfWeekCount: function () {
3056 add('dc', 1, applyMax ? 0 : 5);
3066 return this;
307 },
308
309 /**
310 * Days of year time period, denotes number of days within a year.
311 * Minimum value is 1, maximum value is 366. Specify 0 for last.
312 *
313 * recur().every(2).dayOfYear();
314 *
315 * @api public
316 */
317 dayOfYear: function () {
3184 add('dy', 1, applyMax ? 0 : 366);
3194 return this;
320 },
321
322 /**
323 * Weeks of month time period, denotes number of weeks within a
324 * month. The first week is the week that includes the 1st of the
325 * month. Subsequent weeks start on Sunday.
326 * Minimum value is 1, maximum value is 5. Specify 0 for last.
327 * February 2nd, 2012 - Week 1
328 * February 5th, 2012 - Week 2
329 * February 12th, 2012 - Week 3
330 * February 19th, 2012 - Week 4
331 * February 26th, 2012 - Week 5 (or 0)
332 *
333 * recur().on(2).weekOfMonth();
334 *
335 * @api public
336 */
337 weekOfMonth: function () {
3385 add('wm', 1, applyMax ? 0 : 5);
3395 return this;
340 },
341
342 /**
343 * Weeks of year time period, denotes the ISO 8601 week date. For
344 * more information see: http://en.wikipedia.org/wiki/ISO_week_date.
345 * Minimum value is 1, maximum value is 53. Specify 0 for last.
346 *
347 * recur().every(2).weekOfYear();
348 *
349 * @api public
350 */
351 weekOfYear: function () {
35212 add('wy', 1, applyMax ? 0 : 53);
35312 return this;
354 },
355
356 /**
357 * Month time period, denotes the months within a year.
358 * Minimum value is 1, maximum value is 12. Specify 0 for last.
359 * 1 - January
360 * 2 - February
361 * 3 - March
362 * 4 - April
363 * 5 - May
364 * 6 - June
365 * 7 - July
366 * 8 - August
367 * 9 - September
368 * 10 - October
369 * 11 - November
370 * 12 - December
371 *
372 * recur().on(1).dayOfWeek();
373 *
374 * @api public
375 */
376 month: function () {
37717 add('M', 1, 12);
37817 return this;
379 },
380
381 /**
382 * Year time period, denotes the four digit year.
383 * Minimum value is 1970, maximum value is Jan 1, 2100 (arbitrary)
384 *
385 * recur().on(2011, 2012, 2013).year();
386 *
387 * @api public
388 */
389 year: function () {
39011 add('Y', 1970, 2450);
39111 return this;
392 },
393
394 /**
395 * Full date period, denotes a full date and time.
396 * Minimum value is Jan 1, 1970, maximum value is Jan 1, 2100 (arbitrary)
397 *
398 * recur().on(new Date(2013, 3, 2, 10, 30, 0)).fullDate();
399 *
400 * @api public
401 */
402 fullDate: function () {
4032 for (var i = 0, len = values.length; i < len; i++) {
4042 values[i] = values[i].getTime();
405 }
406
4072 add('fd');
4082 return this;
409 },
410
411 /**
412 * Custom modifier.
413 *
414 * recur().on(2011, 2012, 2013).custom('partOfDay');
415 *
416 * @api public
417 */
418 customModifier: function (id, vals) {
4190 var custom = later.modifier[id];
4200 if(!custom) throw new Error('Custom modifier ' + id + ' not recognized!');
421
4220 modifier = id;
4230 values = arguments[1] instanceof Array ? arguments[1] : [arguments[1]];
4240 return this;
425 },
426
427 /**
428 * Custom time period.
429 *
430 * recur().on(2011, 2012, 2013).customPeriod('partOfDay');
431 *
432 * @api public
433 */
434 customPeriod: function (id) {
4350 var custom = later[id];
4360 if(!custom) throw new Error('Custom time period ' + id + ' not recognized!');
437
4380 add(id, custom.extent(new Date())[0], custom.extent(new Date())[1]);
4390 return this;
440 },
441
442 /**
443 * Modifies a recurring interval (specified using every) to start
444 * at a given offset. To create a schedule for every 5 minutes
445 * starting on the 6th minute - making minutes 6, 11, 16, etc valid:
446 *
447 * recur().every(5).minute().startingOn(6);
448 *
449 * @param {Int} start: The desired starting offset
450 * @api public
451 */
452 startingOn: function (start) {
4534 return this.between(start, last.m);
454 },
455
456 /**
457 * Modifies a recurring interval (specified using every) to start
458 * and stop at specified times. To create a schedule for every
459 * 5 minutes starting on the 6th minute and ending on the 11th
460 * minute - making minutes 6 and 11 valid:
461 *
462 * recur().every(5).minute().between(6, 11);
463 *
464 * @param {Int} start: The desired starting offset
465 * @param {Int} end: The last valid value
466 * @api public
467 */
468 between: function (start, end) {
469 // remove the values added as part of specifying the last
470 // time period and replace them with the new restricted values
4718 cur[last.n] = cur[last.n].splice(0, last.c);
4728 every = last.x;
4738 add(last.n, start, end);
4748 return this;
475 },
476
477 /**
478 * Creates a composite schedule. With a composite schedule, a valid
479 * occurrence of any of the component schedules is considered a valid
480 * value for the composite schedule (e.g. they are OR'ed together).
481 * To create a schedule for every 5 minutes on Mondays and every 10
482 * minutes on Tuesdays:
483 *
484 * recur().every(5).minutes().on(1).dayOfWeek().and().every(10)
485 * .minutes().on(2).dayOfWeek();
486 *
487 * @api public
488 */
489 and: function () {
49015 cur = curArr[curArr.push({}) - 1];
49115 return this;
492 },
493
494 /**
495 * Creates exceptions to a schedule. Any valid occurrence of the
496 * exception schedule (which may also be composite schedules) is
497 * considered a invalid schedule occurrence. Everything that follows
498 * except will be treated as an exception schedule. To create a
499 * schedule for 8:00 am every Tuesday except for patch Tuesday
500 * (second Tuesday each month):
501 *
502 * recur().at('08:00:00').on(2).dayOfWeek().except()
503 * .dayOfWeekCount(1);
504 *
505 * @api public
506 */
507 except: function () {
50810 curArr = exceptions;
50910 cur = null;
51010 return this;
511 }
512 };
513};

parse/text.js

99%
156
155
1
LineHitsSource
1/**
2* Parses an English string expression and produces a schedule that is
3* compatible with Later.js.
4*
5* Examples:
6*
7* every 5 minutes between the 1st and 30th minute
8* at 10:00 am on tues of may in 2012
9* on the 15-20th day of march-dec
10* every 20 seconds every 5 minutes every 4 hours between the 10th and 20th hour
11*/
121later.parse.text = function(str) {
13
1495 var recur = later.parse.recur,
15 pos = 0,
16 input = '',
17 error;
18
19 // Regex expressions for all of the valid tokens
2095 var TOKENTYPES = {
21 eof: /^$/,
22 rank: /^((\d\d\d\d)|([2-5]?1(st)?|[2-5]?2(nd)?|[2-5]?3(rd)?|(0|[1-5]?[4-9]|[1-5]0|1[1-3])(th)?))\b/,
23 time: /^((([0]?[1-9]|1[0-2]):[0-5]\d(\s)?(am|pm))|(([0]?\d|1\d|2[0-3]):[0-5]\d))\b/,
24 dayName: /^((sun|mon|tue(s)?|wed(nes)?|thu(r(s)?)?|fri|sat(ur)?)(day)?)\b/,
25 monthName: /^(jan(uary)?|feb(ruary)?|ma((r(ch)?)?|y)|apr(il)?|ju(ly|ne)|aug(ust)?|oct(ober)?|(sept|nov|dec)(ember)?)\b/,
26 yearIndex: /^(\d\d\d\d)\b/,
27 every: /^every\b/,
28 after: /^after\b/,
29 before: /^before\b/,
30 second: /^(s|sec(ond)?(s)?)\b/,
31 minute: /^(m|min(ute)?(s)?)\b/,
32 hour: /^(h|hour(s)?)\b/,
33 day: /^(day(s)?( of the month)?)\b/,
34 dayInstance: /^day instance\b/,
35 dayOfWeek: /^day(s)? of the week\b/,
36 dayOfYear: /^day(s)? of the year\b/,
37 weekOfYear: /^week(s)?( of the year)?\b/,
38 weekOfMonth: /^week(s)? of the month\b/,
39 weekday: /^weekday\b/,
40 weekend: /^weekend\b/,
41 month: /^month(s)?\b/,
42 year: /^year(s)?\b/,
43 between: /^between (the)?\b/,
44 start: /^(start(ing)? (at|on( the)?)?)\b/,
45 at: /^(at|@)\b/,
46 and: /^(,|and\b)/,
47 except: /^(except\b)/,
48 also: /(also)\b/,
49 first: /^(first)\b/,
50 last: /^last\b/,
51 "in": /^in\b/,
52 of: /^of\b/,
53 onthe: /^on the\b/,
54 on: /^on\b/,
55 through: /(-|^(to|through)\b)/
56 };
57
58 // Array to convert string names to valid numerical values
5995 var NAMES = { jan: 1, feb: 2, mar: 3, apr: 4, may: 5, jun: 6, jul: 7,
60 aug: 8, sep: 9, oct: 10, nov: 11, dec: 12, sun: 1, mon: 2, tue: 3,
61 wed: 4, thu: 5, fri: 6, sat: 7, '1st': 1, fir: 1, '2nd': 2, sec: 2,
62 '3rd': 3, thi: 3, '4th': 4, 'for': 4
63 };
64
65 /**
66 * Bundles up the results of the peek operation into a token.
67 *
68 * @param {Int} start: The start position of the token
69 * @param {Int} end: The end position of the token
70 * @param {String} text: The actual text that was parsed
71 * @param {TokenType} type: The TokenType of the token
72 */
7395 function t(start, end, text, type) {
741980 return {startPos: start, endPos: end, text: text, type: type};
75 }
76
77 /**
78 * Peeks forward to see if the next token is the expected token and
79 * returns the token if found. Pos is not moved during a Peek operation.
80 *
81 * @param {TokenType} exepected: The types of token to scan for
82 */
8395 function peek(expected) {
84658 var scanTokens = expected instanceof Array ? expected : [expected],
85 whiteSpace = /\s+/,
86 token, curInput, m, scanToken, start, len;
87
88658 scanTokens.push(whiteSpace);
89
90 // loop past any skipped tokens and only look for expected tokens
91658 start = pos;
92658 while (!token || token.type === whiteSpace) {
931112 len = -1;
941112 curInput = input.substring(start);
951112 token = t(start, start, input.split(whiteSpace)[0]);
96
971112 var i, length = scanTokens.length;
981112 for(i = 0; i < length; i++) {
995424 scanToken = scanTokens[i];
1005424 m = scanToken.exec(curInput);
1015424 if (m && m.index === 0 && m[0].length > len) {
102868 len = m[0].length;
103868 token = t(start, start + len, curInput.substring(0, len), scanToken);
104 }
105 }
106
107 // update the start position if this token should be skipped
1081112 if (token.type === whiteSpace) {
109454 start = token.endPos;
110 }
111 }
112
113658 return token;
114 }
115
116 /**
117 * Moves pos to the end of the expectedToken if it is found.
118 *
119 * @param {TokenType} exepectedToken: The types of token to scan for
120 */
12195 function scan(expectedToken) {
122376 var token = peek(expectedToken);
123376 pos = token.endPos;
124376 return token;
125 }
126
127 /**
128 * Parses the next 'y-z' expression and returns the resulting valid
129 * value array.
130 *
131 * @param {TokenType} tokenType: The type of range values allowed
132 */
13395 function parseThroughExpr(tokenType) {
13444 var start = +parseTokenValue(tokenType),
135 end = checkAndParse(TOKENTYPES.through) ? +parseTokenValue(tokenType) : start,
136 nums = [];
137
13844 for (var i = start; i <= end; i++) {
13962 nums.push(i);
140 }
141
14244 return nums;
143 }
144
145 /**
146 * Parses the next 'x,y-z' expression and returns the resulting valid
147 * value array.
148 *
149 * @param {TokenType} tokenType: The type of range values allowed
150 */
15195 function parseRanges(tokenType) {
15235 var nums = parseThroughExpr(tokenType);
15335 while (checkAndParse(TOKENTYPES.and)) {
1549 nums = nums.concat(parseThroughExpr(tokenType));
155 }
15635 return nums;
157 }
158
159 /**
160 * Parses the next 'every (weekend|weekday|x) (starting on|between)' expression.
161 *
162 * @param {Recur} r: The recurrence to add the expression to
163 */
16495 function parseEvery(r) {
16522 var num, period, start, end;
166
16722 if (checkAndParse(TOKENTYPES.weekend)) {
1681 r.on(NAMES.sun,NAMES.sat).dayOfWeek();
169 }
17021 else if (checkAndParse(TOKENTYPES.weekday)) {
1712 r.on(NAMES.mon,NAMES.tue,NAMES.wed,NAMES.thu,NAMES.fri).dayOfWeek();
172 }
173 else {
17419 num = parseTokenValue(TOKENTYPES.rank);
17519 r.every(num);
17619 period = parseTimePeriod(r);
177
17819 if (checkAndParse(TOKENTYPES.start)) {
1791 num = parseTokenValue(TOKENTYPES.rank);
1801 r.startingOn(num);
1811 parseToken(period.type);
182 }
18318 else if (checkAndParse(TOKENTYPES.between)) {
1841 start = parseTokenValue(TOKENTYPES.rank);
1851 if (checkAndParse(TOKENTYPES.and)) {
1861 end = parseTokenValue(TOKENTYPES.rank);
1871 r.between(start,end);
188 }
189 }
190 }
191 }
192
193 /**
194 * Parses the next 'on the (first|last|x,y-z)' expression.
195 *
196 * @param {Recur} r: The recurrence to add the expression to
197 */
19895 function parseOnThe(r) {
19923 if (checkAndParse(TOKENTYPES.first)) {
2001 r.first();
201 }
20222 else if (checkAndParse(TOKENTYPES.last)) {
2034 r.last();
204 }
205 else {
20618 r.on(parseRanges(TOKENTYPES.rank));
207 }
208
20923 parseTimePeriod(r);
210 }
211
212 /**
213 * Parses the schedule expression and returns the resulting schedules,
214 * and exceptions. Error will return the position in the string where
215 * an error occurred, will be null if no errors were found in the
216 * expression.
217 *
218 * @param {String} str: The schedule expression to parse
219 */
22095 function parseScheduleExpr(str) {
22195 pos = 0;
22295 input = str;
22395 error = -1;
224
22595 var r = recur();
22695 while (pos < input.length && error < 0) {
227
228138 var token = parseToken([TOKENTYPES.every, TOKENTYPES.after, TOKENTYPES.before,
229 TOKENTYPES.onthe, TOKENTYPES.on, TOKENTYPES.of, TOKENTYPES["in"],
230 TOKENTYPES.at, TOKENTYPES.and, TOKENTYPES.except,
231 TOKENTYPES.also]);
232
233138 switch (token.type) {
234 case TOKENTYPES.every:
23522 parseEvery(r);
23622 break;
237 case TOKENTYPES.after:
23821 if(peek(TOKENTYPES.time).type !== undefined) {
2397 r.after(parseTokenValue(TOKENTYPES.time));
2407 r.time();
241 }
242 else {
24314 r.after(parseTokenValue(TOKENTYPES.rank));
24414 parseTimePeriod(r);
245 }
24621 break;
247 case TOKENTYPES.before:
24821 if(peek(TOKENTYPES.time).type !== undefined) {
2497 r.before(parseTokenValue(TOKENTYPES.time));
2507 r.time();
251 }
252 else {
25314 r.before(parseTokenValue(TOKENTYPES.rank));
25414 parseTimePeriod(r);
255 }
25621 break;
257 case TOKENTYPES.onthe:
25823 parseOnThe(r);
25923 break;
260 case TOKENTYPES.on:
2618 r.on(parseRanges(TOKENTYPES.dayName)).dayOfWeek();
2628 break;
263 case TOKENTYPES.of:
2645 r.on(parseRanges(TOKENTYPES.monthName)).month();
2655 break;
266 case TOKENTYPES["in"]:
2674 r.on(parseRanges(TOKENTYPES.yearIndex)).year();
2684 break;
269 case TOKENTYPES.at:
27023 r.on(parseTokenValue(TOKENTYPES.time)).time();
27123 while (checkAndParse(TOKENTYPES.and)) {
2723 r.on(parseTokenValue(TOKENTYPES.time)).time();
273 }
27423 break;
275 case TOKENTYPES.and:
2764 break;
277 case TOKENTYPES.also:
2782 r.and();
2792 break;
280 case TOKENTYPES.except:
2812 r.except();
2822 break;
283 default:
2843 error = pos;
285 }
286 }
287
28895 return {schedules: r.schedules, exceptions: r.exceptions, error: error};
289 }
290
291 /**
292 * Parses the next token representing a time period and adds it to
293 * the provided recur object.
294 *
295 * @param {Recur} r: The recurrence to add the time period to
296 */
29795 function parseTimePeriod(r) {
29870 var timePeriod = parseToken([TOKENTYPES.second, TOKENTYPES.minute,
299 TOKENTYPES.hour, TOKENTYPES.dayOfYear, TOKENTYPES.dayOfWeek,
300 TOKENTYPES.dayInstance, TOKENTYPES.day, TOKENTYPES.month,
301 TOKENTYPES.year, TOKENTYPES.weekOfMonth, TOKENTYPES.weekOfYear]);
302
30370 switch (timePeriod.type) {
304 case TOKENTYPES.second:
3056 r.second();
3066 break;
307 case TOKENTYPES.minute:
30815 r.minute();
30915 break;
310 case TOKENTYPES.hour:
3117 r.hour();
3127 break;
313 case TOKENTYPES.dayOfYear:
3143 r.dayOfYear();
3153 break;
316 case TOKENTYPES.dayOfWeek:
3175 r.dayOfWeek();
3185 break;
319 case TOKENTYPES.dayInstance:
3203 r.dayOfWeekCount();
3213 break;
322 case TOKENTYPES.day:
32310 r.dayOfMonth();
32410 break;
325 case TOKENTYPES.weekOfMonth:
3264 r.weekOfMonth();
3274 break;
328 case TOKENTYPES.weekOfYear:
3296 r.weekOfYear();
3306 break;
331 case TOKENTYPES.month:
3328 r.month();
3338 break;
334 case TOKENTYPES.year:
3353 r.year();
3363 break;
337 default:
3380 error = pos;
339 }
340
34170 return timePeriod;
342 }
343
344 /**
345 * Checks the next token to see if it is of tokenType. Returns true if
346 * it is and discards the token. Returns false otherwise.
347 *
348 * @param {TokenType} tokenType: The type or types of token to parse
349 */
35095 function checkAndParse(tokenType) {
351240 var found = (peek(tokenType)).type === tokenType;
352240 if (found) {
35328 scan(tokenType);
354 }
355240 return found;
356 }
357
358 /**
359 * Parses and returns the next token.
360 *
361 * @param {TokenType} tokenType: The type or types of token to parse
362 */
36395 function parseToken(tokenType) {
364348 var t = scan(tokenType);
365348 if (t.type) {
366344 t.text = convertString(t.text, tokenType);
367 }
368 else {
3694 error = pos;
370 }
371348 return t;
372 }
373
374 /**
375 * Returns the text value of the token that was parsed.
376 *
377 * @param {TokenType} tokenType: The type of token to parse
378 */
37995 function parseTokenValue(tokenType) {
380139 return (parseToken(tokenType)).text;
381 }
382
383 /**
384 * Converts a string value to a numerical value based on the type of
385 * token that was parsed.
386 *
387 * @param {String} str: The schedule string to parse
388 * @param {TokenType} tokenType: The type of token to convert
389 */
39095 function convertString(str, tokenType) {
391344 var output = str;
392
393344 switch (tokenType) {
394 case TOKENTYPES.time:
39540 var parts = str.split(/(:|am|pm)/),
396 hour = parts[3] === 'pm' && parts[0] < 12 ? parseInt(parts[0],10) + 12 : parts[0],
397 min = parts[2].trim();
398
39940 output = (hour.length === 1 ? '0' : '') + hour + ":" + min;
40040 break;
401
402 case TOKENTYPES.rank:
40374 output = parseInt((/^\d+/.exec(str))[0],10);
40474 break;
405
406 case TOKENTYPES.monthName:
407 case TOKENTYPES.dayName:
40817 output = NAMES[str.substring(0,3)];
40917 break;
410 }
411
412344 return output;
413 }
414
41595 return parseScheduleExpr(str.toLowerCase());
416};
make[1]: Leaving directory `/home/bill/dev/later'