function Job(args) {
  args = args || {};
  // Remove special args
  this.agenda = args.agenda;
  delete args.agenda;
  // Process args
  args.priority = parsePriority(args.priority) || 0;
  // Set attrs to args
  var attrs = {};
  for (var key in args) {
    if (args.hasOwnProperty(key)) {
      attrs[key] = args[key];
    }
  }
  // Set defaults if undefined
  attrs.nextRunAt = attrs.nextRunAt || new Date();
  attrs.type = attrs.type || 'once';
  this.attrs = attrs;
}n/a
function EventEmitter() {
  EventEmitter.init.call(this);
}n/a
function Job(args) {
  args = args || {};
  // Remove special args
  this.agenda = args.agenda;
  delete args.agenda;
  // Process args
  args.priority = parsePriority(args.priority) || 0;
  // Set attrs to args
  var attrs = {};
  for (var key in args) {
    if (args.hasOwnProperty(key)) {
      attrs[key] = args[key];
    }
  }
  // Set defaults if undefined
  attrs.nextRunAt = attrs.nextRunAt || new Date();
  attrs.type = attrs.type || 'once';
  this.attrs = attrs;
}n/a
computeNextRunAt = function () {
  var interval = this.attrs.repeatInterval;
  var timezone = this.attrs.repeatTimezone;
  var repeatAt = this.attrs.repeatAt;
  this.attrs.nextRunAt = undefined;
  if (interval) {
    computeFromInterval.call(this);
  } else if (repeatAt) {
    computeFromRepeatAt.call(this);
  }
  return this;
  function dateForTimezone (d) {
    d = moment(d);
    if(timezone) d.tz(timezone);
    return d;
  }
  function computeFromInterval() {
    var lastRun = this.attrs.lastRunAt || new Date();
    lastRun = dateForTimezone(lastRun);
    try {
      var cronTime = new CronTime(interval);
      var nextDate = cronTime._getNextDateFrom(lastRun);
      if (nextDate.valueOf() == lastRun.valueOf()) {
        // Handle cronTime giving back the same date for the next run time
        nextDate = cronTime._getNextDateFrom(dateForTimezone(new Date(lastRun.valueOf() + 1000)));
      }
      this.attrs.nextRunAt = nextDate;
    } catch (e) {
      // Nope, humanInterval then!
      try {
        if (!this.attrs.lastRunAt && humanInterval(interval)) {
          this.attrs.nextRunAt = lastRun.valueOf();
        } else {
          this.attrs.nextRunAt = lastRun.valueOf() + humanInterval(interval);
        }
      } catch (e) {}
    } finally {
      if (isNaN(this.attrs.nextRunAt)) {
        this.attrs.nextRunAt = undefined;
        this.fail('failed to calculate nextRunAt due to invalid repeat interval');
      }
    }
  }
  function computeFromRepeatAt() {
    var lastRun = this.attrs.lastRunAt || new Date();
    var nextDate = date(repeatAt).valueOf();
    var offset = Date.now();  // if you do not specify offset date for below test it will fail for ms
    if (offset === date(repeatAt,offset).valueOf()) {
      this.attrs.nextRunAt = undefined;
      this.fail('failed to calculate repeatAt time due to invalid format');
    } else if (nextDate.valueOf() == lastRun.valueOf()) {
      this.attrs.nextRunAt = date('tomorrow at ', repeatAt);
    } else {
      this.attrs.nextRunAt = date(repeatAt);
    }
  }
}...
  var self = this,
      agenda = self.agenda,
      definition = agenda._definitions[self.attrs.name];
  var setImmediate = setImmediate || process.nextTick;
  setImmediate(function() {
    self.attrs.lastRunAt = new Date();
    self.computeNextRunAt();
    self.save(function() {
      var jobCallback = function(err) {
if (err) {
  self.fail(err);
}
self.attrs.lastFinishedAt = new Date();
...disable = function () {
  this.attrs.disabled = true;
  return this;
}n/a
enable = function () {
  this.attrs.disabled = false;
  return this;
}n/a
fail = function (reason) {
  if(reason instanceof Error) {
    reason = reason.message;
  }
  this.attrs.failReason = reason;
  this.attrs.failCount = (this.attrs.failCount || 0) + 1;
  this.attrs.failedAt = new Date();
  return this;
}...
Sets `job.attrs.failedAt` to `now`, and sets `job.attrs.failReason`
to `reason`.
Optionally, `reason` can be an error, in which case `job.attrs.failReason` will
be set to `error.message`
```js
job.fail('insuficient disk space');
// or
job.fail(new Error('insufficient disk space'));
job.save();
```
### run(callback)
...isRunning = function () {
  if (!this.attrs.lastRunAt) return false;
  if (!this.attrs.lastFinishedAt) return true;
  if (this.attrs.lockedAt && this.attrs.lastRunAt.getTime() > this.attrs.lastFinishedAt.getTime()) {
    return true;
  }
  return false;
}n/a
priority = function (priority) {
  this.attrs.priority = parsePriority(priority);
  return this;
}...
### priority(priority)
Specifies the `priority` weighting of the job. Can be a number or a string from
the above priority table.
```js
job.priority('low');
job.save();
```
### unique(properties, [options])
Ensure that only one instance of this job exists with the specified properties
...remove = function (cb) {
  // refactored NF 21/04/2015
  this.agenda.cancel( {_id: this.attrs._id}, cb );
/*
  var self = this;
  this.agenda._db.remove({_id: this.attrs._id}, function(err, count) {
    if(err) {
      return cb(err);
    }
    cb(err, count);
  });
*/
}...
// or pass additional connection options:
// var agenda = new Agenda({db: {address: mongoConnectionString, collection: "jobCollectionName", options: {server:{auto_reconnect
:true}}}});
// or pass in an existing mongodb-native MongoClient instance
// var agenda = new Agenda({mongo: myMongoClient});
agenda.define('delete old users', function(job, done) {
User.remove({lastLogIn: { $lt: twoDaysAgo }}, done);
});
agenda.on('ready', function() {
agenda.every('3 minutes', 'delete old users');
// Alternatively, you could also do:
agenda.every('*/3 * * * *', 'delete old users');
...repeatAt = function (time) {
  this.attrs.repeatAt = time;
  return this;
}...
```
### repeatAt(time)
Specifies a `time` when the job should repeat. [Possible values](https://github.com/matthewmueller/date#examples)
```js
job.repeatAt('3:30pm');
job.save();
```
### schedule(time)
Specifies the next `time` at which the job should run.
...repeatEvery = function (interval, options) {
  options = options || {};
  this.attrs.repeatInterval = interval;
  this.attrs.repeatTimezone = options.timezone ? options.timezone : null;
  return this;
}...
  agenda.start();
});
```
```js
agenda.on('ready', function() {
  var weeklyReport = agenda.create('send email report', {to: 'another-guy@example.com'})
  weeklyReport.repeatEvery('1 week').save();
  agenda.start();
});
```
# Full documentation
Agenda's basic control structure is an instance of an agenda. Agenda's are
...run = function (cb) {
  var self = this,
      agenda = self.agenda,
      definition = agenda._definitions[self.attrs.name];
  var setImmediate = setImmediate || process.nextTick;
  setImmediate(function() {
    self.attrs.lastRunAt = new Date();
    self.computeNextRunAt();
    self.save(function() {
      var jobCallback = function(err) {
        if (err) {
          self.fail(err);
        }
        self.attrs.lastFinishedAt = new Date();
        self.attrs.lockedAt = null;
        self.save(function(saveErr, job) {
          cb && cb(err || saveErr, job);
          if (err) {
            agenda.emit('fail', err, self);
            agenda.emit('fail:' + self.attrs.name, err, self);
          } else {
            agenda.emit('success', self);
            agenda.emit('success:' + self.attrs.name, self);
          }
          agenda.emit('complete', self);
          agenda.emit('complete:' + self.attrs.name, self);
        });
      };
      try {
        agenda.emit('start', self);
        agenda.emit('start:' + self.attrs.name, self);
        if (!definition) {
          throw new Error('Undefined job');
        }
        if (definition.fn.length === 2) {
          definition.fn(self, jobCallback);
        } else {
          definition.fn(self);
          jobCallback();
        }
      } catch (e) {
        jobCallback(e);
      }
    });
  });
}...
### run(callback)
Runs the given `job` and calls `callback(err, job)` upon completion. Normally
you never need to call this manually.
```js
job.run(function(err, job) {
  console.log("I don't know why you would need to do this...");
});
```
### save(callback)
Saves the `job.attrs` into the database.
...save = function (cb) {
  this.agenda.saveJob(this, cb);
  return this;
}...
  agenda.start();
});
```
```js
agenda.on('ready', function() {
  var weeklyReport = agenda.create('send email report', {to: 'another-guy@example.com'})
  weeklyReport.repeatEvery('1 week').save();
  agenda.start();
});
```
# Full documentation
Agenda's basic control structure is an instance of an agenda. Agenda's are
...schedule = function (time) {
  this._scheduled = true;
  this.attrs.nextRunAt = (time instanceof Date) ? time : date(time);
  return this;
}...
  from: 'example@example.com',
  subject: 'Email Report',
  body: '...'
}, done);
});
agenda.on('ready', function() {
agenda.schedule('in 20 minutes', 'send email report', {to: '
admin@example.com'});
agenda.start();
});
```
```js
agenda.on('ready', function() {
var weeklyReport = agenda.create('send email report', {to: 'another-guy@example.com'})
...toJSON = function () { // create a persistable Mongo object -RR
    var self = this,
        attrs = self.attrs || {};
    var result = {};
    for (var prop in attrs) {
      if (attrs.hasOwnProperty(prop)) {
        result[prop] = attrs[prop];
      }
    }
    var dates = ['lastRunAt', 'lastFinishedAt', 'nextRunAt', 'failedAt', 'lockedAt'];
    dates.forEach(function(d) {
      if (result[d]) {
        result[d] = new Date(result[d]);
      }
    });
    return result;
}n/a
touch = function (cb) {
  this.attrs.lockedAt = new Date();
  this.save(cb);
}...
Resets the lock on the job. Useful to indicate that the job hasn't timed out
when you have very long running jobs.
```js
agenda.define('super long job', function(job, done) {
doSomeLongTask(function() {
  job.touch(function() {
    doAnotherLongTask(function() {
      job.touch(function() {
        finishOurLongTasks(done);
      });
    });
  });
});
...unique = function (unique, opts) {
  this.attrs.unique = unique;
  this.attrs.uniqueOpts = opts;
  return this;
}...
`options` is an optional argument which can overwrite the defaults. It can take
the following:
- `insertOnly`: `boolean` will prevent any properties from persisting if job already exists. Defaults to false.
```js
job.unique({'data.type': 'active', 'data.userId': '
;123', nextRunAt(date)});
job.save();
```
### fail(reason)
Sets `job.attrs.failedAt` to `now`, and sets `job.attrs.failReason`
to `reason`.
...