function Invocation(job, fireDate, recurrenceRule, endDate) {
this.job = job;
this.fireDate = fireDate;
this.endDate = endDate;
this.recurrenceRule = recurrenceRule || DoesntRecur;
this.timerID = null;
}n/a
function Job(name, job, callback) {
// setup a private pendingInvocations variable
var pendingInvocations = [];
//setup a private number of invocations variable
var triggeredJobs = 0;
// Set scope vars
var jobName = name && typeof name === 'string' ? name : '<Anonymous Job ' + (++anonJobCounter) + '>';
this.job = name && typeof name === 'function' ? name : job;
// Make sure callback is actually a callback
if (this.job === name) {
// Name wasn't provided and maybe a callback is there
this.callback = typeof job === 'function' ? job : false;
} else {
// Name was provided, and maybe a callback is there
this.callback = typeof callback === 'function' ? callback : false;
}
// Check for generator
if (typeof this.job === 'function' &&
this.job.prototype &&
this.job.prototype.next) {
this.job = function() {
return this.next().value;
}.bind(this.job.call(this));
}
// define properties
Object.defineProperty(this, 'name', {
value: jobName,
writable: false,
enumerable: true
});
// method that require private access
this.trackInvocation = function(invocation) {
// add to our invocation list
sorted.add(pendingInvocations, invocation, sorter);
return true;
};
this.stopTrackingInvocation = function(invocation) {
var invIdx = pendingInvocations.indexOf(invocation);
if (invIdx > -1) {
pendingInvocations.splice(invIdx, 1);
return true;
}
return false;
};
this.triggeredJobs = function() {
return triggeredJobs;
};
this.setTriggeredJobs = function(triggeredJob) {
triggeredJobs = triggeredJob;
};
this.cancel = function(reschedule) {
reschedule = (typeof reschedule == 'boolean') ? reschedule : false;
var inv, newInv;
var newInvs = [];
for (var j = 0; j < pendingInvocations.length; j++) {
inv = pendingInvocations[j];
cancelInvocation(inv);
if (reschedule && inv.recurrenceRule.recurs) {
newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
if (newInv !== null) {
newInvs.push(newInv);
}
}
}
pendingInvocations = [];
for (var k = 0; k < newInvs.length; k++) {
this.trackInvocation(newInvs[k]);
}
// remove from scheduledJobs if reschedule === false
if (!reschedule) {
if (this.name) {
delete scheduledJobs[this.name];
}
}
return true;
};
this.cancelNext = function(reschedule) {
reschedule = (typeof reschedule == 'boolean') ? reschedule : true;
if (!pendingInvocations.length) {
return false;
}
var newInv;
var nextInv = pendingInvocations.shift();
cancelInvocation(nextInv);
if (reschedule && nextInv.recurrenceRule.recurs) {
newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
if (newInv !== null) {
this.trackInvocation(newInv);
}
}
return true;
};
this.reschedule = function(spec) {
var inv;
var cInvs = pendingInvocations.slice();
for (var j = 0; j < cInvs.length; j++) {
inv = cInvs[j];
cancelInvocation(inv);
}
pendingInvocations = [];
if (this.schedule(spec)) {
this.setTriggeredJobs(0);
return true;
} else {
pendingInvocations = cInvs;
return false;
}
};
this.nextInvocation = function() {
if (!pendingInvocations.length) {
return null;
}
return pendingInvocations[0].fireDate;
};
this.pendingInvocations = function() {
return pendingInvocations;
};
}...
}, 3250);
clock.tick(3250);
},
"Can cancel Jobs scheduled with Job#schedule": function(test) {
test.expect(2);
var job = new schedule.Job(function() {
test.ok(true);
});
job.schedule({
second: null
});
...function Range(start, end, step) {
this.start = start || 0;
this.end = end || 60;
this.step = step || 1;
}...
You can also use arrays to specify a list of acceptable values, and the `Range`
object to specify a range of start and end values, with an optional step parameter.
For instance, this will print a message on Thursday, Friday, Saturday, and Sunday at 5pm:
```js
var rule = new schedule.RecurrenceRule();
rule.dayOfWeek = [0, new schedule.Range(4, 6)];
rule.hour = 17;
rule.minute = 0;
var j = schedule.scheduleJob(rule, function(){
console.log('Today is recognized by Rebecca Black!');
});
```
...function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
this.recurs = true;
this.year = (year == null) ? null : year;
this.month = (month == null) ? null : month;
this.date = (date == null) ? null : date;
this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
this.hour = (hour == null) ? null : hour;
this.minute = (minute == null) ? null : minute;
this.second = (second == null) ? 0 : second;
}...
You can build recurrence rules to specify when a job should recur. For instance,
consider this rule, which executes the function every hour at 42 minutes after the hour:
```js
var schedule = require('node-schedule');
var rule = new schedule.RecurrenceRule();
rule.minute = 42;
var j = schedule.scheduleJob(rule, function(){
console.log('The answer to life, the universe, and everything!');
});
```
...function cancelJob(job) {
var success = false;
if (job instanceof Job) {
success = job.cancel();
} else if (typeof job == 'string' || job instanceof String) {
if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) {
success = scheduledJobs[job].cancel();
}
}
return success;
}...
job.cancel();
test.done();
}, 4250);
clock.tick(4250);
}
},
".cancelJob(Job)": {
"Prevents all future invocations of Job passed in": function(test) {
test.expect(2);
var job = schedule.scheduleJob({
second: null
}, function() {
test.ok(true);
...function rescheduleJob(job, spec) {
if (job instanceof Job) {
if (job.reschedule(spec)) {
return job;
}
} else if (typeof job == 'string' || job instanceof String) {
if (job in scheduledJobs && scheduledJobs.hasOwnProperty(job)) {
if (scheduledJobs[job].reschedule(spec)) {
return scheduledJobs[job];
}
}
}
return null;
}...
job.cancel();
test.done();
}, 3250);
clock.tick(3250);
}
},
".rescheduleJob(job, {...})": {
"Reschedule jobs from object based to object based": function(test) {
test.expect(3);
var job = new schedule.scheduleJob({
second: null
}, function() {
test.ok(true);
...function scheduleJob() {
if (arguments.length < 2) {
return null;
}
var name = (arguments.length >= 3 && typeof arguments[0] === 'string') ? arguments[0] : null;
var spec = name ? arguments[1] : arguments[0];
var method = name ? arguments[2] : arguments[1];
var callback = name ? arguments[3] : arguments[2];
var job = new Job(name, method, callback);
if (job.schedule(spec)) {
return job;
}
return null;
}...
```
Examples with the cron format:
```js
var schedule = require('node-schedule');
var j = schedule.scheduleJob('42 * * * *', function(){
console.log('The answer to life, the universe, and everything!');
});
```
Execute a cron job when the minute is 42 (e.g. 19:42, 20:42, etc.).
And:
...function Job(name, job, callback) {
// setup a private pendingInvocations variable
var pendingInvocations = [];
//setup a private number of invocations variable
var triggeredJobs = 0;
// Set scope vars
var jobName = name && typeof name === 'string' ? name : '<Anonymous Job ' + (++anonJobCounter) + '>';
this.job = name && typeof name === 'function' ? name : job;
// Make sure callback is actually a callback
if (this.job === name) {
// Name wasn't provided and maybe a callback is there
this.callback = typeof job === 'function' ? job : false;
} else {
// Name was provided, and maybe a callback is there
this.callback = typeof callback === 'function' ? callback : false;
}
// Check for generator
if (typeof this.job === 'function' &&
this.job.prototype &&
this.job.prototype.next) {
this.job = function() {
return this.next().value;
}.bind(this.job.call(this));
}
// define properties
Object.defineProperty(this, 'name', {
value: jobName,
writable: false,
enumerable: true
});
// method that require private access
this.trackInvocation = function(invocation) {
// add to our invocation list
sorted.add(pendingInvocations, invocation, sorter);
return true;
};
this.stopTrackingInvocation = function(invocation) {
var invIdx = pendingInvocations.indexOf(invocation);
if (invIdx > -1) {
pendingInvocations.splice(invIdx, 1);
return true;
}
return false;
};
this.triggeredJobs = function() {
return triggeredJobs;
};
this.setTriggeredJobs = function(triggeredJob) {
triggeredJobs = triggeredJob;
};
this.cancel = function(reschedule) {
reschedule = (typeof reschedule == 'boolean') ? reschedule : false;
var inv, newInv;
var newInvs = [];
for (var j = 0; j < pendingInvocations.length; j++) {
inv = pendingInvocations[j];
cancelInvocation(inv);
if (reschedule && inv.recurrenceRule.recurs) {
newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
if (newInv !== null) {
newInvs.push(newInv);
}
}
}
pendingInvocations = [];
for (var k = 0; k < newInvs.length; k++) {
this.trackInvocation(newInvs[k]);
}
// remove from scheduledJobs if reschedule === false
if (!reschedule) {
if (this.name) {
delete scheduledJobs[this.name];
}
}
return true;
};
this.cancelNext = function(reschedule) {
reschedule = (typeof reschedule == 'boolean') ? reschedule : true;
if (!pendingInvocations.length) {
return false;
}
var newInv;
var nextInv = pendingInvocations.shift();
cancelInvocation(nextInv);
if (reschedule && nextInv.recurrenceRule.recurs) {
newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
if (newInv !== null) {
this.trackInvocation(newInv);
}
}
return true;
};
this.reschedule = function(spec) {
var inv;
var cInvs = pendingInvocations.slice();
for (var j = 0; j < cInvs.length; j++) {
inv = cInvs[j];
cancelInvocation(inv);
}
pendingInvocations = [];
if (this.schedule(spec)) {
this.setTriggeredJobs(0);
return true;
} else {
pendingInvocations = cInvs;
return false;
}
};
this.nextInvocation = function() {
if (!pendingInvocations.length) {
return null;
}
return pendingInvocations[0].fireDate;
};
this.pendingInvocations = function() {
return pendingInvocations;
};
}...
}, 3250);
clock.tick(3250);
},
"Can cancel Jobs scheduled with Job#schedule": function(test) {
test.expect(2);
var job = new schedule.Job(function() {
test.ok(true);
});
job.schedule({
second: null
});
...function EventEmitter() {
EventEmitter.init.call(this);
}n/a
invoke = function () {
if (typeof this.job == 'function') {
this.setTriggeredJobs(this.triggeredJobs() + 1);
this.job();
} else {
this.job.execute();
}
}...
if (inv !== null) {
inv.job.trackInvocation(inv);
}
}
job.stopTrackingInvocation(cinv);
job.invoke();
job.emit('run');
});
}
}
function currentInvocationFinished() {
invocations.shift();
...runOnDate = function (date) {
return this.schedule(date);
}...
"Run job on specified date": function(test) {
test.expect(1);
var job = new schedule.Job(function() {
test.ok(true);
});
job.runOnDate(new Date(Date.now() + 3000));
setTimeout(function() {
test.done();
}, 3250);
clock.tick(3250);
},
...schedule = function (spec) {
var self = this;
var success = false;
var inv;
var start;
var end;
if (typeof spec === 'object' && spec.rule) {
start = spec.start || null;
end = spec.end || null;
spec = spec.rule;
if (start != null) {
if (!(start instanceof Date)) {
start = new Date(start);
}
if (!isValidDate(start) || start.getTime() < Date.now()) {
start = null;
}
}
if (end != null && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
end = null;
}
}
try {
var res = cronParser.parseExpression(spec, { currentDate: start });
inv = scheduleNextRecurrence(res, self, start, end);
if (inv !== null) {
success = self.trackInvocation(inv);
}
} catch (err) {
var type = typeof spec;
if ((type === 'string') || (type === 'number')) {
spec = new Date(spec);
}
if ((spec instanceof Date) && (isValidDate(spec))) {
if (spec.getTime() >= Date.now()) {
inv = new Invocation(self, spec);
scheduleInvocation(inv);
success = self.trackInvocation(inv);
}
} else if (type === 'object') {
if (!(spec instanceof RecurrenceRule)) {
var r = new RecurrenceRule();
if ('year' in spec) {
r.year = spec.year;
}
if ('month' in spec) {
r.month = spec.month;
}
if ('date' in spec) {
r.date = spec.date;
}
if ('dayOfWeek' in spec) {
r.dayOfWeek = spec.dayOfWeek;
}
if ('hour' in spec) {
r.hour = spec.hour;
}
if ('minute' in spec) {
r.minute = spec.minute;
}
if ('second' in spec) {
r.second = spec.second;
}
spec = r;
}
inv = scheduleNextRecurrence(spec, self, start, end);
if (inv !== null) {
success = self.trackInvocation(inv);
}
}
}
scheduledJobs[this.name] = this;
return success;
}...
inv = cInvs[j];
cancelInvocation(inv);
}
pendingInvocations = [];
if (this.schedule(spec)) {
this.setTriggeredJobs(0);
return true;
} else {
pendingInvocations = cInvs;
return false;
}
};
...function Range(start, end, step) {
this.start = start || 0;
this.end = end || 60;
this.step = step || 1;
}...
You can also use arrays to specify a list of acceptable values, and the `Range`
object to specify a range of start and end values, with an optional step parameter.
For instance, this will print a message on Thursday, Friday, Saturday, and Sunday at 5pm:
```js
var rule = new schedule.RecurrenceRule();
rule.dayOfWeek = [0, new schedule.Range(4, 6)];
rule.hour = 17;
rule.minute = 0;
var j = schedule.scheduleJob(rule, function(){
console.log('Today is recognized by Rebecca Black!');
});
```
...contains = function (val) {
if (this.step === null || this.step === 1) {
return (val >= this.start && val <= this.end);
} else {
for (var i = this.start; i < this.end; i += this.step) {
if (i === val) {
return true;
}
}
return false;
}
}...
if (matcher == null) {
return true;
}
if (typeof matcher === 'number' || typeof matcher === 'string') {
return (val === matcher);
} else if (matcher instanceof Range) {
return matcher.contains(val);
} else if (Array.isArray(matcher) || (matcher instanceof Array)) {
for (var i = 0; i < matcher.length; i++) {
if (recurMatch(val, matcher[i])) {
return true;
}
}
}
...function RecurrenceRule(year, month, date, dayOfWeek, hour, minute, second) {
this.recurs = true;
this.year = (year == null) ? null : year;
this.month = (month == null) ? null : month;
this.date = (date == null) ? null : date;
this.dayOfWeek = (dayOfWeek == null) ? null : dayOfWeek;
this.hour = (hour == null) ? null : hour;
this.minute = (minute == null) ? null : minute;
this.second = (second == null) ? 0 : second;
}...
You can build recurrence rules to specify when a job should recur. For instance,
consider this rule, which executes the function every hour at 42 minutes after the hour:
```js
var schedule = require('node-schedule');
var rule = new schedule.RecurrenceRule();
rule.minute = 42;
var j = schedule.scheduleJob(rule, function(){
console.log('The answer to life, the universe, and everything!');
});
```
...nextInvocationDate = function (base) {
base = (base instanceof Date) ? base : (new Date());
if (!this.recurs) {
return null;
}
var now = new Date();
var fullYear = now.getFullYear();
if ((this.year !== null) &&
(typeof this.year == 'number') &&
(this.year < fullYear)) {
return null;
}
var next = new CronDate(base.getTime());
next.addSecond();
while (true) {
if (this.year !== null) {
fullYear = next.getFullYear();
if ((typeof this.year == 'number') && (this.year < fullYear)) {
next = null;
break;
}
if (!recurMatch(fullYear, this.year)) {
next.addYear();
next.setMonth(0);
next.setDate(1);
next.setHours(0);
next.setMinutes(0);
next.setSeconds(0);
continue;
}
}
if (this.month != null && !recurMatch(next.getMonth(), this.month)) {
next.addMonth();
continue;
}
if (this.date != null && !recurMatch(next.getDate(), this.date)) {
next.addDay();
continue;
}
if (this.dayOfWeek != null && !recurMatch(next.getDay(), this.dayOfWeek)) {
next.addDay();
continue;
}
if (this.hour != null && !recurMatch(next.getHours(), this.hour)) {
next.addHour();
continue;
}
if (this.minute != null && !recurMatch(next.getMinutes(), this.minute)) {
next.addMinute();
continue;
}
if (this.second != null && !recurMatch(next.getSeconds(), this.second)) {
next.addSecond();
continue;
}
break;
}
return next;
}...
}
/* Recurrence scheduler */
function scheduleNextRecurrence(rule, job, prevDate, endDate) {
prevDate = (prevDate instanceof Date) ? prevDate : (new Date());
var date = (rule instanceof RecurrenceRule) ? rule.nextInvocationDate(prevDate) : rule
.next();
if (date === null) {
return null;
}
if ((endDate instanceof Date) && date.getTime() > endDate.getTime()) {
return null;
}
...