make[1]: Entering directory `/home/bill/dev/later'
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | later.array = {}; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | } |
| 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 | } |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | later.date = {}; |
| 2 | ||
| 3 | ||
| 4 | ||
| 5 | ||
| 6 | ||
| 7 | ||
| 8 |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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 | }; |
| 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(); |
| 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 | }; |
| 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 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | ||
| 2 | 1 | later.modifier = {}; |
| 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 | }; |
| Line | Hits | Source |
|---|---|---|
| 1 | 1 | later.parse = {}; |
| 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 | }; |
| 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 | }; |