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

# Coverage

95%
799
762
37

## array/array.js

100%
1
1
0

Line | Hits | Source
--- | --- | ---
1 | 1 | later.array = {};

## array/next.js

100%
11
11
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.array.next = function (val, values, extent) {
14 |  |
15 | 9815 | var cur,
16 |  | zeroIsLargest = extent[0] !== 0,
17 |  | nextIdx = 0;
18 |  |
19 | 9815 | for(var i = values.length-1; i > -1; --i) {
20 | 29761 | cur = values[i];
21 |  |
22 | 29761 | if(cur === val) {
23 | 6579 | return cur;
24 |  | }
25 |  |
26 | 23182 | if(cur > val || (cur === 0 && zeroIsLargest && extent[1] > val)) {
27 | 21233 | nextIdx = i;
28 | 21233 | continue;
29 |  | }
30 |  |
31 | 1949 | break;
32 |  | }
33 |  |
34 | 3236 | return values[nextIdx];
35 |  | };

## array/nextinvalid.js

100%
12
12
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.array.nextInvalid = function (val, values, extent) {
14 |  |
15 | 1255 | 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 |  |
21 | 1255 | while(next === (values[i] || zeroVal)) {
22 |  |
23 | 1448 | next++;
24 | 1448 | if(next > max) {
25 | 17 | next = min;
26 |  | }
27 |  |
28 | 1448 | i++;
29 | 1448 | if(i === len) {
30 | 66 | i = 0;
31 |  | }
32 |  |
33 | 1448 | if(next === start) {
34 | 3 | return undefined;
35 |  | }
36 |  | }
37 |  |
38 | 1252 | return next;
39 |  | };

## array/prev.js

100%
11
11
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.array.prev = function (val, values, extent) {
14 |  |
15 | 3574 | var cur, len = values.length,
16 |  | zeroIsLargest = extent[0] !== 0,
17 |  | prevIdx = len-1;
18 |  |
19 | 3574 | for(var i = 0; i < len; i++) {
20 | 5660 | cur = values[i];
21 |  |
22 | 5660 | if(cur === val) {
23 | 1983 | return cur;
24 |  | }
25 |  |
26 | 3677 | if(cur < val || (cur === 0 && zeroIsLargest && extent[1] < val)) {
27 | 2897 | prevIdx = i;
28 | 2897 | continue;
29 |  | }
30 |  |
31 | 780 | break;
32 |  | }
33 |  |
34 | 1591 | return values[prevIdx];
35 |  | };

## array/previnvalid.js

100%
12
12
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.array.prevInvalid = function (val, values, extent) {
14 |  |
15 | 187 | 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 |  |
21 | 187 | while(next === (values[i] || zeroVal)) {
22 | 366 | next--;
23 |  |
24 | 366 | if(next < min) {
25 | 22 | next = max;
26 |  | }
27 |  |
28 | 366 | i--;
29 | 366 | if(i === -1) {
30 | 46 | i = len-1;
31 |  | }
32 |  |
33 | 366 | if(next === start) {
34 | 4 | return undefined;
35 |  | }
36 |  | }
37 |  |
38 | 183 | return next;
39 |  | };

## array/sort.js

100%
5
5
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.array.sort = function (arr, zeroIsLast) {
14 | 4 | arr.sort(function(a,b) {
15 | 40 | return +a - +b;
16 |  | });
17 |  |
18 | 4 | if(zeroIsLast && arr[0] === 0) {
19 | 1 | arr.push(arr.shift());
20 |  | }
21 |  | };

## compat/indexof.js

4%
23
1
22

Line | Hits | Source
--- | --- | ---
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 |  | //
6 | 1 | if (!Array.prototype.indexOf) {
7 | 0 | Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
8 | 0 | "use strict";
9 | 0 | if (this == null) {
10 | 0 | throw new TypeError();
11 |  | }
12 | 0 | var t = Object(this);
13 | 0 | var len = t.length >>> 0;
14 | 0 | if (len === 0) {
15 | 0 | return -1;
16 |  | }
17 | 0 | var n = 0;
18 | 0 | if (arguments.length > 1) {
19 | 0 | n = Number(arguments[1]);
20 | 0 | if (n != n) { // shortcut for verifying if it's NaN
21 | 0 | n = 0;
22 | 0 | } else if (n != 0 && n != Infinity && n != -Infinity) {
23 | 0 | n = (n > 0 || -1) * Math.floor(Math.abs(n));
24 |  | }
25 |  | }
26 | 0 | if (n >= len) {
27 | 0 | return -1;
28 |  | }
29 | 0 | var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
30 | 0 | for (; k < len; k++) {
31 | 0 | if (k in t && t[k] === searchElement) {
32 | 0 | return k;
33 |  | }
34 |  | }
35 | 0 | return -1;
36 |  | }
37 |  | }

## compat/trim.js

33%
3
1
2

Line | Hits | Source
--- | --- | ---
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 |  | //
6 | 1 | if(!String.prototype.trim) {
7 | 0 | String.prototype.trim = function () {
8 | 0 | return this.replace(/^\s+|\s+$/g,'');
9 |  | };
10 |  | }

## constraint/day.js

100%
16
16
0

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 104249 | 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) {
40 | 3600 | 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) {
50 | 45462 | if(d.DExtent) return d.DExtent;
51 |  |
52 | 35934 | var month = later.M.val(d),
53 |  | max = later.DAYS_IN_MONTH[month-1];
54 |  |
55 | 35934 | if(month === 2 && later.dy.extent(d)[1] === 366) {
56 | 607 | max = max+1;
57 |  | }
58 |  |
59 | 35934 | 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) {
68 | 20568 | 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) {
78 | 6079 | 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) {
91 | 1245 | val = val > later.D.extent(d)[1] ? 1 : val;
92 | 1245 | var month = later.date.nextRollover(d, val, later.D, later.M),
93 |  | DMax = later.D.extent(month)[1];
94 |  |
95 | 1245 | val = val > DMax ? 1 : val || DMax;
96 |  |
97 | 1245 | 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) {
113 | 1029 | var month = later.date.prevRollover(d, val, later.D, later.M),
114 |  | DMax = later.D.extent(month)[1];
115 |  |
116 | 1029 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 111085 | 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) {
40 | 1974 | 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() {
48 | 2300 | 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) {
57 | 417 | 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) {
66 | 346 | 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) {
76 | 30253 | val = val > 7 ? 1 : val || 7;
77 |  |
78 | 30253 | 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) {
91 | 414 | val = val > 7 ? 7 : val || 7;
92 |  |
93 | 414 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
12 | 1 | later.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) {
31 | 2966 | 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) {
41 | 1095 | 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) {
52 | 2316 | 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) {
62 | 180 | 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) {
76 | 427 | 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) {
90 | 256 | val = val > later.dc.extent(d)[1] ? 1 : val;
91 | 256 | var month = later.date.nextRollover(d, val, later.dc, later.M),
92 |  | dcMax = later.dc.extent(month)[1];
93 |  |
94 | 256 | val = val > dcMax ? 1 : val;
95 |  |
96 | 256 | 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 |  |
102 | 256 | if(next.getTime() <= d.getTime()) {
103 | 2 | month = later.M.next(d, later.M.val(d)+1);
104 |  |
105 | 2 | 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 |  |
112 | 254 | 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) {
122 | 221 | var month = later.date.prevRollover(d, val, later.dc, later.M),
123 |  | dcMax = later.dc.extent(month)[1];
124 |  |
125 | 221 | val = val > dcMax ? dcMax : val || dcMax;
126 |  |
127 | 221 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 19188 | 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) {
41 | 9568 | 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) {
51 | 34831 | 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
55 | 34831 | 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) {
64 | 4810 | 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) {
73 | 4810 | 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) {
83 | 4784 | val = val > later.dy.extent(d)[1] ? 1 : val;
84 | 4784 | var year = later.date.nextRollover(d, val, later.dy, later.Y),
85 |  | dyMax = later.dy.extent(year)[1];
86 |  |
87 | 4784 | val = val > dyMax ? 1 : val || dyMax;
88 |  |
89 | 4784 | 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) {
104 | 4784 | var year = later.date.prevRollover(d, val, later.dy, later.Y),
105 |  | dyMax = later.dy.extent(year)[1];
106 |  |
107 | 4784 | val = val > dyMax ? dyMax : val || dyMax;
108 |  |
109 | 4784 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 62 | 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) {
40 | 8 | return later.fd.val(d) === val;
41 |  | },
42 |  |
43 |  | /**
44 |  | * The minimum and maximum valid time values.
45 |  | */
46 |  | extent: function() {
47 | 34 | return [0, 32503680000000];
48 |  | },
49 |  |
50 |  | /**
51 |  | * Returns the specified date.
52 |  | *
53 |  | * @param {Date} d: The specified date
54 |  | */
55 |  | start: function(d) {
56 | 6 | return d;
57 |  | },
58 |  |
59 |  | /**
60 |  | * Returns the specified date.
61 |  | *
62 |  | * @param {Date} d: The specified date
63 |  | */
64 |  | end: function(d) {
65 | 2 | 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) {
75 | 13 | 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) {
85 | 3 | return later.fd.val(d) > val ? new Date(val) : later.NEVER;
86 |  | }
87 |  |
88 |  | };

## constraint/hour.js

100%
13
13
0

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 33989 | 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) {
40 | 3023 | return later.h.val(d) === val;
41 |  | },
42 |  |
43 |  | /**
44 |  | * The minimum and maximum valid hour values.
45 |  | */
46 |  | extent: function() {
47 | 3962 | 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) {
56 | 734 | 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) {
66 | 707 | 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) {
77 | 955 | val = val > 23 ? 0 : val;
78 |  |
79 | 955 | 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
86 | 955 | if(!later.date.isUTC && next.getTime() <= d.getTime()) {
87 | 1 | next = later.date.next(
88 |  | later.Y.val(next),
89 |  | later.M.val(next),
90 |  | later.D.val(next),
91 |  | val + 1);
92 |  | }
93 |  |
94 | 955 | 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) {
104 | 816 | val = val > 23 ? 23 : val;
105 |  |
106 | 816 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 38147 | 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) {
40 | 5226 | return later.m.val(d) === val;
41 |  | },
42 |  |
43 |  | /**
44 |  | * The minimum and maximum valid minute values.
45 |  | */
46 |  | extent: function(d) {
47 | 8538 | 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) {
56 | 3110 | 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) {
66 | 1895 | 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) {
77 | 2303 | 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
83 | 2303 | if(!later.date.isUTC && next.getTime() <= d.getTime()) {
84 | 0 | next = new Date(d.getTime() + ((inc + 120) * later.MIN) - (s * later.SEC));
85 |  | }
86 |  |
87 | 2303 | 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) {
97 | 1971 | val = val > 59 ? 59 : val;
98 |  |
99 | 1971 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 151949 | 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) {
40 | 1175 | 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() {
48 | 1591 | 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) {
57 | 3898 | 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) {
66 | 2196 | 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) {
76 | 399 | val = val > 12 ? 1 : val || 12;
77 |  |
78 | 399 | 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) {
90 | 1262 | val = val > 12 ? 12 : val || 12;
91 |  |
92 | 1262 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 31987 | 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) {
40 | 5100 | return later.s.val(d) === val;
41 |  | },
42 |  |
43 |  | /**
44 |  | * The minimum and maximum valid second values.
45 |  | */
46 |  | extent: function() {
47 | 7768 | 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) {
56 | 3249 | 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) {
65 | 1997 | 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) {
75 | 2842 | 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
80 | 2842 | if(!later.date.isUTC && next.getTime() <= d.getTime()) {
81 | 0 | next = new Date(d.getTime() + ((inc + 7200) * later.SEC));
82 |  | }
83 |  |
84 | 2842 | 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) {
94 | 1723 | val = val > 59 ? 59 : val;
95 |  |
96 | 1723 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
12 | 1 | later.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) {
31 | 22379 | 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) {
42 | 10378 | return later.t.val(d) === val;
43 |  | },
44 |  |
45 |  | /**
46 |  | * The minimum and maximum valid time values.
47 |  | */
48 |  | extent: function() {
49 | 15940 | return [0, 86399];
50 |  | },
51 |  |
52 |  | /**
53 |  | * Returns the specified date.
54 |  | *
55 |  | * @param {Date} d: The specified date
56 |  | */
57 |  | start: function(d) {
58 | 5285 | return d;
59 |  | },
60 |  |
61 |  | /**
62 |  | * Returns the specified date.
63 |  | *
64 |  | * @param {Date} d: The specified date
65 |  | */
66 |  | end: function(d) {
67 | 5116 | 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) {
77 | 5152 | val = val > 86399 ? 0 : val;
78 |  |
79 | 5152 | 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
88 | 5152 | if(!later.date.isUTC && next.getTime() < d.getTime()) {
89 | 0 | 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 |  |
98 | 5152 | 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) {
108 | 5004 | val = val > 86399 ? 86399 : val;
109 |  |
110 | 5004 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
13 | 1 | later.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) {
32 | 788 | 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) {
44 | 368 | 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) {
54 | 1388 | 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) {
65 | 210 | 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) {
77 | 394 | 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) {
92 | 184 | val = val > later.wm.extent(d)[1] ? 1 : val;
93 |  |
94 | 184 | var month = later.date.nextRollover(d, val, later.wm, later.M),
95 |  | wmMax = later.wm.extent(month)[1];
96 |  |
97 | 184 | val = val > wmMax ? 1 : val || wmMax;
98 |  |
99 |  | // jump to the Sunday of the desired week, set to 1st of month for week 1
100 | 184 | 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) {
115 | 184 | var month = later.date.prevRollover(d, val, later.wm, later.M),
116 |  | wmMax = later.wm.extent(month)[1];
117 |  |
118 | 184 | val = val > wmMax ? wmMax : val || wmMax;
119 |  |
120 |  | // jump to the end of Saturday of the desired week
121 | 184 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
12 | 1 | later.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) {
31 | 8999 | if (d.wy) return d.wy;
32 |  |
33 |  | // move to the Thursday in the target week and find Thurs of target year
34 | 8669 | 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
38 | 8669 | 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) {
48 | 2955 | 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) {
57 | 12401 | if (d.wyExtent) return d.wyExtent;
58 |  |
59 |  | // go to start of ISO week to get to the right year
60 | 7953 | 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 |  |
64 | 7953 | 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) {
73 | 22324 | 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) {
87 | 4274 | 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) {
102 | 1424 | val = val > later.wy.extent(d)[1] ? 1 : val;
103 |  |
104 | 1424 | 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
108 | 1424 | if(later.wy.val(year) !== 1) {
109 | 726 | year = later.dw.next(year, 2);
110 |  | }
111 |  |
112 | 1424 | var wyMax = later.wy.extent(year)[1],
113 |  | wyStart = later.wy.start(year);
114 |  |
115 | 1424 | val = val > wyMax ? 1 : val || wyMax;
116 |  |
117 | 1424 | 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) {
131 | 1420 | 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
135 | 1420 | if(later.wy.val(year) !== 1) {
136 | 787 | year = later.dw.next(year, 2);
137 |  | }
138 |  |
139 | 1420 | var wyMax = later.wy.extent(year)[1],
140 |  | wyEnd = later.wy.end(year);
141 |  |
142 | 1420 | val = val > wyMax ? wyMax : val || wyMax;
143 |  |
144 | 1420 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
11 | 1 | later.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) {
30 | 230114 | 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) {
40 | 3551 | 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() {
49 | 29309 | 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) {
58 | 34142 | 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) {
67 | 12002 | 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) {
77 | 3430 | 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) {
88 | 15471 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
12 | 1 | later.compile = function(schedDef) {
13 |  |
14 | 696 | var constraints = [],
15 |  | constraintsLen = 0,
16 |  | tickConstraint;
17 |  |
18 | 696 | for(var key in schedDef) {
19 | 1498 | 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 |  |
25 | 1498 | constraints.push({constraint: constraint, vals: vals});
26 | 1498 | 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)
32 | 696 | constraints.sort(function(a,b) {
33 | 1232 | 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
38 | 696 | 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 |  | */
47 | 696 | function compareFn(dir) {
48 | 1338 | return dir === 'next' ?
49 | 43 | function(a,b) { return a.getTime() > b.getTime(); } :
50 | 41 | function(a,b) { return b.getTime() > a.getTime(); };
51 |  | }
52 |  |
53 | 696 | 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) {
63 | 3887 | var next = startDate,
64 |  | nextVal = later.array[dir],
65 |  | maxAttempts = 1000,
66 |  | done;
67 |  |
68 | 3887 | while(maxAttempts-- && !done && next) {
69 | 7310 | 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
73 | 7310 | for(var i = 0; i < constraintsLen; i++) {
74 |  |
75 | 13372 | var constraint = constraints[i].constraint,
76 |  | curVal = constraint.val(next),
77 |  | extent = constraint.extent(next),
78 |  | newVal = nextVal(curVal, constraints[i].vals, extent);
79 |  |
80 | 13372 | if(!constraint.isValid(next, newVal)) {
81 | 3446 | next = constraint[dir](next, newVal);
82 | 3446 | done = false;
83 | 3446 | break; // need to retest all constraints with new date
84 |  | }
85 |  | }
86 |  | }
87 |  |
88 | 3887 | if(next !== later.NEVER) {
89 | 3864 | 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
94 | 3887 | 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 |  |
105 | 1338 | var result,
106 |  | nextVal = later.array[dir + 'Invalid'],
107 |  | compare = compareFn(dir);
108 |  |
109 | 1338 | for(var i = constraintsLen-1; i >= 0; i--) {
110 | 1431 | 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 |  |
116 | 1431 | if(newVal !== undefined) { // constraint has invalid value, use that
117 | 1425 | next = constraint[dir](startDate, newVal);
118 | 1425 | if(next && (!result || compare(result, next))) {
119 | 1365 | result = next;
120 |  | }
121 |  | }
122 |  | }
123 |  |
124 | 1338 | 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) {
134 | 900 | 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) {
145 | 281 | return tickConstraint.start(date);
146 |  | }
147 |  |
148 |  | };
149 |  | };

## core/schedule.js

100%
109
109
0

Line | Hits | Source
--- | --- | ---
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 |  | */
12 | 1 | later.schedule = function(sched) {
13 | 490 | if(!sched) throw new Error('Missing schedule definition.');
14 | 490 | if(!sched.schedules) throw new Error('Definition must include at least one schedule.');
15 |  |
16 |  | // compile the schedule components
17 | 490 | var schedules = [],
18 |  | schedulesLen = sched.schedules.length,
19 |  | exceptions = [],
20 |  | exceptionsLen = sched.exceptions ? sched.exceptions.length : 0;
21 |  |
22 | 490 | for(var i = 0; i < schedulesLen; i++) {
23 | 581 | schedules.push(later.compile(sched.schedules[i]));
24 |  | }
25 |  |
26 | 490 | for(var j = 0; j < exceptionsLen; j++) {
27 | 101 | 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 |  | */
40 | 490 | function getInstances(dir, count, startDate, endDate, isRange) {
41 | 490 | var compare = compareFn(dir), // encapsulates difference between directions
42 |  | loopCount = count,
43 |  | maxAttempts = 1000,
44 |  | schedStarts = [], exceptStarts = [],
45 |  | next, end, results = [];
46 |  |
47 | 490 | startDate = startDate ? new Date(startDate) : new Date();
48 | 490 | if(!startDate || !startDate.getTime()) throw new Error('Invalid start date.');
49 |  |
50 |  | // Step 1: calculate the earliest start dates for each schedule and exception
51 | 490 | setNextStarts(dir, schedules, schedStarts, startDate);
52 | 490 | setRangeStarts(dir, exceptions, exceptStarts, startDate);
53 |  |
54 |  | // Step 2: select the earliest of the start dates calculated
55 | 490 | while(maxAttempts-- && loopCount && (next = findNext(schedStarts, compare))) {
56 |  |
57 |  | // Step 3: make sure the start date we found is in range
58 | 2077 | if(endDate && compare(next, endDate)) {
59 | 36 | break;
60 |  | }
61 |  |
62 |  | // Step 4: make sure we aren't in the middle of an exception range
63 | 2041 | if(exceptionsLen) {
64 | 1157 | updateRangeStarts(dir, exceptions, exceptStarts, next);
65 | 1157 | if((end = calcRangeOverlap(dir, exceptStarts, next))) {
66 | 1062 | updateNextStarts(dir, schedules, schedStarts, end);
67 | 1062 | continue;
68 |  | }
69 |  | }
70 |  |
71 |  | // Step 5: Date is good, if range, find the end of the range and update start dates
72 | 979 | if(isRange) {
73 | 87 | var maxEndDate = calcMaxEndDate(exceptStarts, compare);
74 | 87 | end = calcEnd(dir, schedules, schedStarts, next, maxEndDate);
75 | 87 | 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 |  |
86 | 87 | updateNextStarts(dir, schedules, schedStarts, end);
87 |  | }
88 |  | // otherwise store the start date and tick the start dates
89 |  | else {
90 | 892 | results.push( dir === 'next' ?
91 |  | new Date(Math.max(startDate, next)) :
92 |  | getStart(schedules, schedStarts, next, endDate)
93 |  | );
94 |  |
95 | 892 | tickStarts(dir, schedules, schedStarts, next);
96 |  | }
97 |  |
98 | 979 | loopCount--;
99 |  | }
100 |  |
101 | 490 | 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 |  | */
112 | 490 | function setNextStarts(dir, schedArr, startsArr, startDate) {
113 | 490 | for(var i = 0, len = schedArr.length; i < len; i++) {
114 | 581 | 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 |  | */
128 | 490 | function updateNextStarts(dir, schedArr, startsArr, startDate) {
129 | 1149 | var compare = compareFn(dir);
130 |  |
131 | 1149 | for(var i = 0, len = schedArr.length; i < len; i++) {
132 | 1293 | if(startsArr[i] && !compare(startsArr[i], startDate)) {
133 | 1149 | 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 |  | */
148 | 490 | function setRangeStarts(dir, schedArr, rangesArr, startDate) {
149 | 490 | var compare = compareFn(dir);
150 |  |
151 | 490 | for(var i = 0, len = schedArr.length; i < len; i++) {
152 | 101 | var nextStart = schedArr[i].start(dir, startDate);
153 |  |
154 | 101 | if(!nextStart) {
155 | 1 | rangesArr[i] = later.NEVER;
156 |  | }
157 |  | else {
158 | 100 | 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 |  | */
173 | 490 | function updateRangeStarts(dir, schedArr, rangesArr, startDate) {
174 | 1157 | var compare = compareFn(dir);
175 |  |
176 | 1157 | for(var i = 0, len = schedArr.length; i < len; i++) {
177 | 1277 | if(rangesArr[i] && !compare(rangesArr[i][0], startDate)) {
178 | 1150 | var nextStart = schedArr[i].start(dir, startDate);
179 |  |
180 | 1150 | if(!nextStart) {
181 | 3 | rangesArr[i] = later.NEVER;
182 |  | }
183 |  | else {
184 | 1147 | 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 |  | */
199 | 490 | function tickStarts(dir, schedArr, startsArr, startDate) {
200 | 892 | for(var i = 0, len = schedArr.length; i < len; i++) {
201 | 989 | if(startsArr[i] && startsArr[i].getTime() === startDate.getTime()) {
202 | 892 | 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 |  | */
215 | 490 | function getStart(schedArr, startsArr, startDate, minEndDate) {
216 | 281 | var result;
217 |  |
218 | 281 | for(var i = 0, len = startsArr.length; i < len; i++) {
219 | 300 | if(startsArr[i] && startsArr[i].getTime() === startDate.getTime()) {
220 | 281 | var start = schedArr[i].tickStart(startDate);
221 |  |
222 | 281 | if(minEndDate && (start < minEndDate)) {
223 | 3 | return minEndDate;
224 |  | }
225 |  |
226 | 278 | if(!result || (start > result)) {
227 | 278 | result = start;
228 |  | }
229 |  | }
230 |  | }
231 |  |
232 | 278 | 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 |  | */
244 | 490 | function calcRangeOverlap(dir, rangesArr, startDate) {
245 | 1157 | var compare = compareFn(dir), result;
246 |  |
247 | 1157 | for(var i = 0, len = rangesArr.length; i < len; i++) {
248 | 1277 | var range = rangesArr[i];
249 |  |
250 | 1277 | if(range && !compare(range[0], startDate) &&
251 |  | (!range[1] || compare(range[1], startDate))) {
252 |  | // startDate is in the middle of an exception range
253 | 1070 | if(!result || compare(range[1], result)) {
254 | 1062 | result = range[1];
255 |  | }
256 |  | }
257 |  | }
258 |  |
259 | 1157 | 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 |  | */
269 | 490 | function calcMaxEndDate(exceptsArr, compare) {
270 | 87 | var result;
271 |  |
272 | 87 | for(var i = 0, len = exceptsArr.length; i < len; i++) {
273 | 81 | if(exceptsArr[i] && (!result || compare(result, exceptsArr[i][0]))) {
274 | 55 | result = exceptsArr[i][0];
275 |  | }
276 |  | }
277 |  |
278 | 87 | 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 |  | */
292 | 490 | function calcEnd(dir, schedArr, startsArr, startDate, maxEndDate) {
293 | 87 | var compare = compareFn(dir), result;
294 |  |
295 | 87 | for(var i = 0, len = schedArr.length; i < len; i++) {
296 | 111 | var start = startsArr[i];
297 |  |
298 | 111 | if(start && start.getTime() === startDate.getTime()) {
299 | 87 | var end = schedArr[i].end(dir, start);
300 |  |
301 |  | // if the end date is past the maxEndDate, just return the maxEndDate
302 | 87 | if(maxEndDate && compare(end, maxEndDate)) {
303 | 30 | return maxEndDate;
304 |  | }
305 |  |
306 |  | // otherwise, return the maximum end date that was calculated
307 | 57 | if(!result || compare(end, result)) {
308 | 57 | result = end;
309 |  | }
310 |  | }
311 |  | }
312 |  |
313 | 57 | 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 |  | */
323 | 490 | function compareFn(dir) {
324 | 4530 | return dir === 'next' ?
325 | 6409 | function(a,b) { return a.getTime() > b.getTime(); } :
326 | 892 | 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 |  | */
336 | 490 | function findNext(arr, compare) {
337 | 2083 | var next = arr[0];
338 |  |
339 | 2083 | for(var i = 1, len = arr.length; i < len; i++) {
340 | 261 | if(arr[i] && compare(next, arr[i])) {
341 | 38 | next = arr[i];
342 |  | }
343 |  | }
344 |  |
345 | 2083 | return next;
346 |  | }
347 |  |
348 | 490 | 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) {
356 | 356 | 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) {
370 | 63 | 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) {
384 | 55 | 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) {
398 | 9 | 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) {
412 | 7 | return getInstances('prev', count || 1, startDate, endDate, true);
413 |  | }
414 |  | };
415 |  | };

## core/setinterval.js

100%
9
9
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.setInterval = function(fn, sched) {
14 |  |
15 | 2 | 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 |  | */
22 | 2 | function scheduleTimeout() {
23 | 4 | if(!done) {
24 | 3 | fn();
25 | 3 | t = later.setTimeout(scheduleTimeout, sched);
26 |  | }
27 |  | }
28 |  |
29 | 2 | return {
30 |  |
31 |  | /**
32 |  | * Clears the timeout.
33 |  | */
34 |  | clear: function() {
35 | 2 | done = true;
36 | 2 | t.clear();
37 |  | }
38 |  |
39 |  | };
40 |  |
41 |  | };

## core/settimeout.js

100%
12
12
0

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.setTimeout = function(fn, sched) {
14 |  |
15 | 8 | var s = later.schedule(sched), t;
16 | 8 | 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 |  | */
23 | 8 | function scheduleTimeout() {
24 | 8 | 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
29 | 8 | if(diff < 1000) {
30 | 6 | diff = next[1].getTime() - now;
31 |  | }
32 |  |
33 | 8 | if(diff < 2147483647) {
34 | 7 | t = setTimeout(fn, diff);
35 |  | }
36 |  | else {
37 | 1 | t = setTimeout(scheduleTimeout, 2147483647);
38 |  | }
39 |  | }
40 |  |
41 | 8 | return {
42 |  |
43 |  | /**
44 |  | * Clears the timeout.
45 |  | */
46 |  | clear: function() {
47 | 4 | clearTimeout(t);
48 |  | }
49 |  |
50 |  | };
51 |  |
52 |  | };

## date/constant.js

100%
7
7
0

Line | Hits | Source
--- | --- | ---
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
13 | 1 | later.SEC = 1000;
14 | 1 | later.MIN = later.SEC * 60;
15 | 1 | later.HOUR = later.MIN * 60;
16 | 1 | later.DAY = later.HOUR * 24;
17 | 1 | later.WEEK = later.DAY * 7;
18 |  |
19 |  | // Array of days in each month, must be corrected for leap years
20 | 1 | later.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
23 | 1 | later.NEVER = 0;

## date/date.js

100%
1
1
0

Line | Hits | Source
--- | --- | ---
1 | 1 | later.date = {};
2 |  |
3 |  |
4 |  |
5 |  |
6 |  |
7 |  |
8 |  |

## date/next.js

100%
2
2
0

Line | Hits | Source
--- | --- | ---
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 |  | */
24 | 1 | later.date.next = function(Y, M, D, h, m, s) {
25 |  |
26 | 144440 | 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

Line | Hits | Source
--- | --- | ---
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 |  |
14 | 1 | later.date.nextRollover = function(d, val, constraint, period) {
15 | 7893 | var cur = constraint.val(d),
16 |  | max = constraint.extent(d)[1];
17 |  |
18 | 7893 | 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

Line | Hits | Source
--- | --- | ---
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 |  | */
24 | 1 | later.date.prev = function(Y, M, D, h, m, s) {
25 |  |
26 | 51857 | var len = arguments.length;
27 | 51857 | M = len < 2 ? 11 : M-1;
28 | 51857 | D = len < 3 ? later.D.extent(later.date.next(Y, M+1))[1] : D;
29 | 51857 | h = len < 4 ? 23 : h;
30 | 51857 | m = len < 5 ? 59 : m;
31 | 51857 | s = len < 6 ? 59 : s;
32 |  |
33 | 51857 | return later.date.build(Y, M, D, h, m, s);
34 |  | };

## date/prevrollover.js

100%
3
3
0

Line | Hits | Source
--- | --- | ---
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 |  |
14 | 1 | later.date.prevRollover = function(d, val, constraint, period) {
15 | 7638 | var cur = constraint.val(d);
16 |  |
17 | 7638 | 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

Line | Hits | Source
--- | --- | ---
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 |  |
13 | 1 | later.date.timezone = function(useLocalTime) {
14 |  |
15 |  | // configure the date builder used to create new dates in the right timezone
16 | 41460 | later.date.build = useLocalTime ?
17 | 85178 | function(Y, M, D, h, m, s) { return new Date(Y, M, D, h, m, s); } :
18 | 111119 | function(Y, M, D, h, m, s) { return new Date(Date.UTC(Y, M, D, h, m, s)); };
19 |  |
20 |  | // configure the accessor methods
21 | 41460 | var get = useLocalTime ? 'get' : 'getUTC',
22 |  | d = Date.prototype;
23 |  |
24 | 41460 | later.date.getYear = d[get + 'FullYear'];
25 | 41460 | later.date.getMonth = d[get + 'Month'];
26 | 41460 | later.date.getDate = d[get + 'Date'];
27 | 41460 | later.date.getDay = d[get + 'Day'];
28 | 41460 | later.date.getHour = d[get + 'Hours'];
29 | 41460 | later.date.getMin = d[get + 'Minutes'];
30 | 41460 | later.date.getSec = d[get + 'Seconds'];
31 |  |
32 |  | // set the isUTC flag
33 | 41460 | later.date.isUTC = !useLocalTime;
34 |  | };
35 |  |
36 |  | // friendly names for available timezones
37 | 20801 | later.date.UTC = function() { later.date.timezone(false); };
38 | 20661 | later.date.localTime = function() { later.date.timezone(true); };
39 |  |
40 |  | // use UTC by default
41 | 1 | later.date.UTC();

## modifier/after.js

100%
8
8
0

Line | Hits | Source
--- | --- | ---
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 |  | */
19 | 1 | later.modifier.after = later.modifier.a = function(constraint, values) {
20 |  |
21 | 153 | var value = values[0];
22 |  |
23 | 153 | 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) {
50 | 1494 | 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) {
72 | 143 | if(val != value) val = constraint.extent(startDate)[0];
73 | 105 | return constraint.next(startDate, val);
74 |  | },
75 |  |
76 |  | /**
77 |  | * Pass through to the constraint.
78 |  | */
79 |  | prev: function(startDate, val) {
80 | 59 | val = val === value ? constraint.extent(startDate)[1] : value - 1;
81 | 59 | return constraint.prev(startDate, val);
82 |  | }
83 |  |
84 |  | };
85 |  |
86 |  | };

## modifier/before.js

100%
8
8
0

Line | Hits | Source
--- | --- | ---
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 |  | */
19 | 1 | later.modifier.before = later.modifier.b = function(constraint, values) {
20 |  |
21 | 141 | var value = values[values.length-1];
22 |  |
23 | 141 | 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) {
50 | 491 | 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) {
72 | 81 | val = val === value ? constraint.extent(startDate)[0] : value;
73 | 81 | return constraint.next(startDate, val);
74 |  | },
75 |  |
76 |  | /**
77 |  | * Pass through to the constraint.
78 |  | */
79 |  | prev: function(startDate, val) {
80 | 55 | val = val === value ? value - 1 : constraint.extent(startDate)[1];
81 | 55 | return constraint.prev(startDate, val);
82 |  | }
83 |  |
84 |  | };
85 |  |
86 |  | };

## modifier/modifier.js

100%
1
1
0

Line | Hits | Source
--- | --- | ---
1 |  |
2 | 1 | later.modifier = {};

## parse/cron.js

100%
80
80
0

Line | Hits | Source
--- | --- | ---
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 |  | */
22 | 1 | later.parse.cron = function (expr, hasSeconds) {
23 |  |
24 |  | // Constant array to convert valid names to values
25 | 87 | 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
32 | 87 | 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 |  | */
50 | 87 | function getValue(value, offset) {
51 | 321 | 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 |  | */
60 | 87 | function cloneSchedule(sched) {
61 | 6 | var clone = {}, field;
62 |  |
63 | 6 | for(field in sched) {
64 | 11 | if (field !== 'dc' && field !== 'd') {
65 | 2 | clone[field] = sched[field].slice(0);
66 |  | }
67 |  | }
68 |  |
69 | 6 | 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 |  | */
81 | 87 | function add(sched, name, min, max, inc) {
82 | 187 | var i = min;
83 |  |
84 | 187 | if (!sched[name]) {
85 | 163 | sched[name] = [];
86 |  | }
87 |  |
88 | 187 | while (i <= max) {
89 | 359 | if (sched[name].indexOf(i) < 0) {
90 | 353 | sched[name].push(i);
91 |  | }
92 | 359 | 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 |  | */
104 | 87 | 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
108 | 14 | if ((curSched.d && !curSched.dc) ||
109 |  | (curSched.dc && curSched.dc.indexOf(hash) < 0)) {
110 | 6 | schedules.push(cloneSchedule(curSched));
111 | 6 | curSched = schedules[schedules.length-1];
112 |  | }
113 |  |
114 | 14 | add(curSched, 'd', value, value);
115 | 14 | add(curSched, 'dc', hash, hash);
116 |  | }
117 |  |
118 | 87 | function addWeekday(s, curSched, value) {
119 | 4 | var except1 = {}, except2 = {};
120 | 4 | 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
123 | 1 | add(curSched, 'D', 1, 3);
124 | 1 | add(curSched, 'd', NAMES.MON, NAMES.FRI);
125 | 1 | add(except1, 'D', 2, 2);
126 | 1 | add(except1, 'd', NAMES.TUE, NAMES.FRI);
127 | 1 | add(except2, 'D', 3, 3);
128 | 1 | 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.
133 | 3 | add(curSched, 'D', value-1, value+1);
134 | 3 | add(curSched, 'd', NAMES.MON, NAMES.FRI);
135 | 3 | add(except1, 'D', value-1, value-1);
136 | 3 | add(except1, 'd', NAMES.MON, NAMES.THU);
137 | 3 | add(except2, 'D', value+1, value+1);
138 | 3 | add(except2, 'd', NAMES.TUE, NAMES.FRI);
139 |  | }
140 | 4 | s.exceptions.push(except1);
141 | 4 | 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 |  | */
154 | 87 | function addRange(item, curSched, name, min, max, offset) {
155 |  | // parse range/x
156 | 36 | var incSplit = item.split('/'),
157 |  | inc = +incSplit[1],
158 |  | range = incSplit[0];
159 |  |
160 |  | // parse x-y or * or 0
161 | 36 | if (range !== '*' && range !== '0') {
162 | 24 | var rangeSplit = range.split('-');
163 | 24 | min = getValue(rangeSplit[0], offset);
164 |  |
165 |  | // fix for issue #13, range may be single digit
166 | 24 | max = getValue(rangeSplit[1], offset) || max;
167 |  | }
168 | 36 | 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 |  | */
181 | 87 | function parse(item, s, name, min, max, offset) {
182 | 153 | 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)
188 | 153 | if (item === 'L') {
189 | 2 | item = min - 1;
190 |  | }
191 |  |
192 |  | // parse x
193 | 153 | if ((value = getValue(item, offset)) !== null) {
194 | 99 | add(curSched, name, value, value);
195 |  | }
196 |  | // parse xW
197 | 54 | else if ((value = getValue(item.replace('W', ''), offset)) !== null) {
198 | 4 | addWeekday(s, curSched, value);
199 |  | }
200 |  | // parse xL
201 | 50 | else if ((value = getValue(item.replace('L', ''), offset)) !== null) {
202 | 6 | addHash(schedules, curSched, value, min-1);
203 |  | }
204 |  | // parse x#y
205 | 44 | else if ((split = item.split('#')).length === 2) {
206 | 8 | value = getValue(split[0], offset);
207 | 8 | addHash(schedules, curSched, value, getValue(split[1]));
208 |  | }
209 |  | // parse x-y or x-y/z or */z or 0/z
210 |  | else {
211 | 36 | 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 |  | */
220 | 87 | function isHash(item) {
221 | 33 | return item.indexOf('#') > -1 || item.indexOf('L') > 0;
222 |  | }
223 |  |
224 |  |
225 | 87 | function itemSorter(a,b) {
226 | 28 | 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 |  | */
235 | 87 | function parseExpr(expr) {
236 | 87 | var schedule = {schedules: [{}], exceptions: []},
237 |  | components = expr.split(' '),
238 |  | field, f, component, items;
239 |  |
240 | 87 | for(field in FIELDS) {
241 | 609 | f = FIELDS[field];
242 | 609 | component = components[f[0]];
243 | 609 | 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
247 | 125 | items = component.split(',').sort(itemSorter);
248 | 125 | var i, length = items.length;
249 | 125 | for (i = 0; i < length; i++) {
250 | 153 | parse(items[i], schedule, field, f[1], f[2], f[3]);
251 |  | }
252 |  | }
253 |  | }
254 |  |
255 | 87 | return schedule;
256 |  | }
257 |  |
258 | 87 | var e = expr.toUpperCase();
259 | 87 | return parseExpr(hasSeconds ? e : '0 ' + e);
260 |  | };

## parse/parse.js

100%
1
1
0

Line | Hits | Source
--- | --- | ---
1 | 1 | later.parse = {};

## parse/recur.js

90%
92
83
9

Line | Hits | Source
--- | --- | ---
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 |  | */
13 | 1 | later.parse.recur = function () {
14 |  |
15 | 154 | 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 |  | */
29 | 154 | function add(name, min, max) {
30 | 240 | name = modifier ? name + '_' + modifier : name;
31 |  |
32 | 240 | if (!cur) {
33 | 162 | curArr.push({});
34 | 162 | cur = curArr[0];
35 |  | }
36 |  |
37 | 240 | if (!cur[name]) {
38 | 229 | cur[name] = [];
39 |  | }
40 |  |
41 | 240 | curName = cur[name];
42 |  |
43 | 240 | if (every) {
44 | 50 | values = [];
45 | 50 | for (i = min; i <= max; i += every) {
46 | 782 | values.push(i);
47 |  | }
48 |  |
49 |  | // save off values in case of startingOn or between
50 | 50 | last = {n: name, x: every, c: curName.length, m: max};
51 |  | }
52 |  |
53 | 240 | values = applyMin ? [min] : applyMax ? [max] : values;
54 | 240 | var length = values.length;
55 | 240 | for (i = 0; i < length; i += 1) {
56 | 1024 | var val = values[i];
57 | 1024 | if (curName.indexOf(val) < 0) {
58 | 1024 | curName.push(val);
59 |  | }
60 |  | }
61 |  |
62 |  | // reset the built up state
63 | 240 | values = every = modifier = applyMin = applyMax = 0;
64 |  | }
65 |  |
66 | 154 | 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 () {
95 | 108 | values = arguments[0] instanceof Array ? arguments[0] : arguments;
96 | 108 | 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) {
111 | 42 | every = x || 1;
112 | 42 | 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) {
125 | 31 | modifier = 'a';
126 | 31 | values = [x];
127 | 31 | 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) {
140 | 29 | modifier = 'b';
141 | 29 | values = [x];
142 | 29 | 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 () {
156 | 8 | applyMin = 1;
157 | 8 | 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 () {
170 | 11 | applyMax = 1;
171 | 11 | 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;
186 | 47 | for (var i = 0, len = values.length; i < len; i++) {
187 | 48 | var split = values[i].split(':');
188 | 92 | if(split.length < 3) split.push(0);
189 | 48 | values[i] = (+split[0]) * 3600 + (+split[1]) * 60 + (+split[2]);
190 |  | }
191 |  |
192 | 47 | add('t');
193 | 47 | 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 () {
205 | 21 | add('s', 0, 59);
206 | 21 | 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 () {
218 | 48 | add('m', 0, 59);
219 | 48 | 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 () {
231 | 12 | add('h', 0, 23);
232 | 12 | 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 () {
244 | 20 | add('D', 1, applyMax ? 0 : 31);
245 | 20 | 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 () {
264 | 27 | add('d', 1, 7);
265 | 27 | return this;
266 |  | },
267 |  |
268 |  | /**
269 |  | * Short hand for on(1,7).dayOfWeek()
270 |  | *
271 |  | * @api public
272 |  | */
273 |  | onWeekend: function() {
274 | 1 | values = [1,7];
275 | 1 | 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() {
284 | 2 | values = [2,3,4,5,6];
285 | 2 | 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 () {
305 | 6 | add('dc', 1, applyMax ? 0 : 5);
306 | 6 | 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 () {
318 | 4 | add('dy', 1, applyMax ? 0 : 366);
319 | 4 | 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 () {
338 | 5 | add('wm', 1, applyMax ? 0 : 5);
339 | 5 | 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 () {
352 | 12 | add('wy', 1, applyMax ? 0 : 53);
353 | 12 | 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 () {
377 | 17 | add('M', 1, 12);
378 | 17 | 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 () {
390 | 11 | add('Y', 1970, 2450);
391 | 11 | 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 () {
403 | 2 | for (var i = 0, len = values.length; i < len; i++) {
404 | 2 | values[i] = values[i].getTime();
405 |  | }
406 |  |
407 | 2 | add('fd');
408 | 2 | 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) {
419 | 0 | var custom = later.modifier[id];
420 | 0 | if(!custom) throw new Error('Custom modifier ' + id + ' not recognized!');
421 |  |
422 | 0 | modifier = id;
423 | 0 | values = arguments[1] instanceof Array ? arguments[1] : [arguments[1]];
424 | 0 | 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) {
435 | 0 | var custom = later[id];
436 | 0 | if(!custom) throw new Error('Custom time period ' + id + ' not recognized!');
437 |  |
438 | 0 | add(id, custom.extent(new Date())[0], custom.extent(new Date())[1]);
439 | 0 | 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) {
453 | 4 | 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
471 | 8 | cur[last.n] = cur[last.n].splice(0, last.c);
472 | 8 | every = last.x;
473 | 8 | add(last.n, start, end);
474 | 8 | 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 () {
490 | 15 | cur = curArr[curArr.push({}) - 1];
491 | 15 | 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 () {
508 | 10 | curArr = exceptions;
509 | 10 | cur = null;
510 | 10 | return this;
511 |  | }
512 |  | };
513 |  | };

## parse/text.js

99%
156
155
1

Line | Hits | Source
--- | --- | ---
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 |  | */
12 | 1 | later.parse.text = function(str) {
13 |  |
14 | 95 | var recur = later.parse.recur,
15 |  | pos = 0,
16 |  | input = '',
17 |  | error;
18 |  |
19 |  | // Regex expressions for all of the valid tokens
20 | 95 | 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
59 | 95 | 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 |  | */
73 | 95 | function t(start, end, text, type) {
74 | 1980 | 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 |  | */
83 | 95 | function peek(expected) {
84 | 658 | var scanTokens = expected instanceof Array ? expected : [expected],
85 |  | whiteSpace = /\s+/,
86 |  | token, curInput, m, scanToken, start, len;
87 |  |
88 | 658 | scanTokens.push(whiteSpace);
89 |  |
90 |  | // loop past any skipped tokens and only look for expected tokens
91 | 658 | start = pos;
92 | 658 | while (!token || token.type === whiteSpace) {
93 | 1112 | len = -1;
94 | 1112 | curInput = input.substring(start);
95 | 1112 | token = t(start, start, input.split(whiteSpace)[0]);
96 |  |
97 | 1112 | var i, length = scanTokens.length;
98 | 1112 | for(i = 0; i < length; i++) {
99 | 5424 | scanToken = scanTokens[i];
100 | 5424 | m = scanToken.exec(curInput);
101 | 5424 | if (m && m.index === 0 && m[0].length > len) {
102 | 868 | len = m[0].length;
103 | 868 | token = t(start, start + len, curInput.substring(0, len), scanToken);
104 |  | }
105 |  | }
106 |  |
107 |  | // update the start position if this token should be skipped
108 | 1112 | if (token.type === whiteSpace) {
109 | 454 | start = token.endPos;
110 |  | }
111 |  | }
112 |  |
113 | 658 | 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 |  | */
121 | 95 | function scan(expectedToken) {
122 | 376 | var token = peek(expectedToken);
123 | 376 | pos = token.endPos;
124 | 376 | 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 |  | */
133 | 95 | function parseThroughExpr(tokenType) {
134 | 44 | var start = +parseTokenValue(tokenType),
135 |  | end = checkAndParse(TOKENTYPES.through) ? +parseTokenValue(tokenType) : start,
136 |  | nums = [];
137 |  |
138 | 44 | for (var i = start; i <= end; i++) {
139 | 62 | nums.push(i);
140 |  | }
141 |  |
142 | 44 | 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 |  | */
151 | 95 | function parseRanges(tokenType) {
152 | 35 | var nums = parseThroughExpr(tokenType);
153 | 35 | while (checkAndParse(TOKENTYPES.and)) {
154 | 9 | nums = nums.concat(parseThroughExpr(tokenType));
155 |  | }
156 | 35 | 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 |  | */
164 | 95 | function parseEvery(r) {
165 | 22 | var num, period, start, end;
166 |  |
167 | 22 | if (checkAndParse(TOKENTYPES.weekend)) {
168 | 1 | r.on(NAMES.sun,NAMES.sat).dayOfWeek();
169 |  | }
170 | 21 | else if (checkAndParse(TOKENTYPES.weekday)) {
171 | 2 | r.on(NAMES.mon,NAMES.tue,NAMES.wed,NAMES.thu,NAMES.fri).dayOfWeek();
172 |  | }
173 |  | else {
174 | 19 | num = parseTokenValue(TOKENTYPES.rank);
175 | 19 | r.every(num);
176 | 19 | period = parseTimePeriod(r);
177 |  |
178 | 19 | if (checkAndParse(TOKENTYPES.start)) {
179 | 1 | num = parseTokenValue(TOKENTYPES.rank);
180 | 1 | r.startingOn(num);
181 | 1 | parseToken(period.type);
182 |  | }
183 | 18 | else if (checkAndParse(TOKENTYPES.between)) {
184 | 1 | start = parseTokenValue(TOKENTYPES.rank);
185 | 1 | if (checkAndParse(TOKENTYPES.and)) {
186 | 1 | end = parseTokenValue(TOKENTYPES.rank);
187 | 1 | 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 |  | */
198 | 95 | function parseOnThe(r) {
199 | 23 | if (checkAndParse(TOKENTYPES.first)) {
200 | 1 | r.first();
201 |  | }
202 | 22 | else if (checkAndParse(TOKENTYPES.last)) {
203 | 4 | r.last();
204 |  | }
205 |  | else {
206 | 18 | r.on(parseRanges(TOKENTYPES.rank));
207 |  | }
208 |  |
209 | 23 | 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 |  | */
220 | 95 | function parseScheduleExpr(str) {
221 | 95 | pos = 0;
222 | 95 | input = str;
223 | 95 | error = -1;
224 |  |
225 | 95 | var r = recur();
226 | 95 | while (pos < input.length && error < 0) {
227 |  |
228 | 138 | 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 |  |
233 | 138 | switch (token.type) {
234 |  | case TOKENTYPES.every:
235 | 22 | parseEvery(r);
236 | 22 | break;
237 |  | case TOKENTYPES.after:
238 | 21 | if(peek(TOKENTYPES.time).type !== undefined) {
239 | 7 | r.after(parseTokenValue(TOKENTYPES.time));
240 | 7 | r.time();
241 |  | }
242 |  | else {
243 | 14 | r.after(parseTokenValue(TOKENTYPES.rank));
244 | 14 | parseTimePeriod(r);
245 |  | }
246 | 21 | break;
247 |  | case TOKENTYPES.before:
248 | 21 | if(peek(TOKENTYPES.time).type !== undefined) {
249 | 7 | r.before(parseTokenValue(TOKENTYPES.time));
250 | 7 | r.time();
251 |  | }
252 |  | else {
253 | 14 | r.before(parseTokenValue(TOKENTYPES.rank));
254 | 14 | parseTimePeriod(r);
255 |  | }
256 | 21 | break;
257 |  | case TOKENTYPES.onthe:
258 | 23 | parseOnThe(r);
259 | 23 | break;
260 |  | case TOKENTYPES.on:
261 | 8 | r.on(parseRanges(TOKENTYPES.dayName)).dayOfWeek();
262 | 8 | break;
263 |  | case TOKENTYPES.of:
264 | 5 | r.on(parseRanges(TOKENTYPES.monthName)).month();
265 | 5 | break;
266 |  | case TOKENTYPES["in"]:
267 | 4 | r.on(parseRanges(TOKENTYPES.yearIndex)).year();
268 | 4 | break;
269 |  | case TOKENTYPES.at:
270 | 23 | r.on(parseTokenValue(TOKENTYPES.time)).time();
271 | 23 | while (checkAndParse(TOKENTYPES.and)) {
272 | 3 | r.on(parseTokenValue(TOKENTYPES.time)).time();
273 |  | }
274 | 23 | break;
275 |  | case TOKENTYPES.and:
276 | 4 | break;
277 |  | case TOKENTYPES.also:
278 | 2 | r.and();
279 | 2 | break;
280 |  | case TOKENTYPES.except:
281 | 2 | r.except();
282 | 2 | break;
283 |  | default:
284 | 3 | error = pos;
285 |  | }
286 |  | }
287 |  |
288 | 95 | 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 |  | */
297 | 95 | function parseTimePeriod(r) {
298 | 70 | 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 |  |
303 | 70 | switch (timePeriod.type) {
304 |  | case TOKENTYPES.second:
305 | 6 | r.second();
306 | 6 | break;
307 |  | case TOKENTYPES.minute:
308 | 15 | r.minute();
309 | 15 | break;
310 |  | case TOKENTYPES.hour:
311 | 7 | r.hour();
312 | 7 | break;
313 |  | case TOKENTYPES.dayOfYear:
314 | 3 | r.dayOfYear();
315 | 3 | break;
316 |  | case TOKENTYPES.dayOfWeek:
317 | 5 | r.dayOfWeek();
318 | 5 | break;
319 |  | case TOKENTYPES.dayInstance:
320 | 3 | r.dayOfWeekCount();
321 | 3 | break;
322 |  | case TOKENTYPES.day:
323 | 10 | r.dayOfMonth();
324 | 10 | break;
325 |  | case TOKENTYPES.weekOfMonth:
326 | 4 | r.weekOfMonth();
327 | 4 | break;
328 |  | case TOKENTYPES.weekOfYear:
329 | 6 | r.weekOfYear();
330 | 6 | break;
331 |  | case TOKENTYPES.month:
332 | 8 | r.month();
333 | 8 | break;
334 |  | case TOKENTYPES.year:
335 | 3 | r.year();
336 | 3 | break;
337 |  | default:
338 | 0 | error = pos;
339 |  | }
340 |  |
341 | 70 | 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 |  | */
350 | 95 | function checkAndParse(tokenType) {
351 | 240 | var found = (peek(tokenType)).type === tokenType;
352 | 240 | if (found) {
353 | 28 | scan(tokenType);
354 |  | }
355 | 240 | 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 |  | */
363 | 95 | function parseToken(tokenType) {
364 | 348 | var t = scan(tokenType);
365 | 348 | if (t.type) {
366 | 344 | t.text = convertString(t.text, tokenType);
367 |  | }
368 |  | else {
369 | 4 | error = pos;
370 |  | }
371 | 348 | 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 |  | */
379 | 95 | function parseTokenValue(tokenType) {
380 | 139 | 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 |  | */
390 | 95 | function convertString(str, tokenType) {
391 | 344 | var output = str;
392 |  |
393 | 344 | switch (tokenType) {
394 |  | case TOKENTYPES.time:
395 | 40 | 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 |  |
399 | 40 | output = (hour.length === 1 ? '0' : '') + hour + ":" + min;
400 | 40 | break;
401 |  |
402 |  | case TOKENTYPES.rank:
403 | 74 | output = parseInt((/^\d+/.exec(str))[0],10);
404 | 74 | break;
405 |  |
406 |  | case TOKENTYPES.monthName:
407 |  | case TOKENTYPES.dayName:
408 | 17 | output = NAMES[str.substring(0,3)];
409 | 17 | break;
410 |  | }
411 |  |
412 | 344 | return output;
413 |  | }
414 |  |
415 | 95 | return parseScheduleExpr(str.toLowerCase());
416 |  | };

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