/**
|
|
* conrad.js is a tiny JavaScript jobs scheduler,
|
|
*
|
|
* Version: 0.1.0
|
|
* Sources: http://github.com/jacomyal/conrad.js
|
|
* Doc: http://github.com/jacomyal/conrad.js#readme
|
|
*
|
|
* License:
|
|
* --------
|
|
* Copyright © 2013 Alexis Jacomy, Sciences-Po médialab
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* The Software is provided "as is", without warranty of any kind, express or
|
|
* implied, including but not limited to the warranties of merchantability,
|
|
* fitness for a particular purpose and noninfringement. In no event shall the
|
|
* authors or copyright holders be liable for any claim, damages or other
|
|
* liability, whether in an action of contract, tort or otherwise, arising
|
|
* from, out of or in connection with the software or the use or other dealings
|
|
* in the Software.
|
|
*/
|
|
(function(global) {
|
|
'use strict';
|
|
|
|
// Check that conrad.js has not been loaded yet:
|
|
if (global.conrad)
|
|
throw new Error('conrad already exists');
|
|
|
|
|
|
/**
|
|
* PRIVATE VARIABLES:
|
|
* ******************
|
|
*/
|
|
|
|
/**
|
|
* A flag indicating whether conrad is running or not.
|
|
*
|
|
* @type {Number}
|
|
*/
|
|
var _lastFrameTime;
|
|
|
|
/**
|
|
* A flag indicating whether conrad is running or not.
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
var _isRunning = false;
|
|
|
|
/**
|
|
* The hash of registered jobs. Each job must at least have a unique ID
|
|
* under the key "id" and a function under the key "job". This hash
|
|
* contains each running job and each waiting job.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
var _jobs = {};
|
|
|
|
/**
|
|
* The hash of currently running jobs.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
var _runningJobs = {};
|
|
|
|
/**
|
|
* The array of currently running jobs, sorted by priority.
|
|
*
|
|
* @type {Array}
|
|
*/
|
|
var _sortedByPriorityJobs = [];
|
|
|
|
/**
|
|
* The array of currently waiting jobs.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
var _waitingJobs = {};
|
|
|
|
/**
|
|
* The array of finished jobs. They are stored in an array, since two jobs
|
|
* with the same "id" can happen at two different times.
|
|
*
|
|
* @type {Array}
|
|
*/
|
|
var _doneJobs = [];
|
|
|
|
/**
|
|
* A dirty flag to keep conrad from starting: Indeed, when addJob() is called
|
|
* with several jobs, conrad must be started only at the end. This flag keeps
|
|
* me from duplicating the code that effectively adds a job.
|
|
*
|
|
* @type {Boolean}
|
|
*/
|
|
var _noStart = false;
|
|
|
|
/**
|
|
* An hash containing some global settings about how conrad.js should
|
|
* behave.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
var _parameters = {
|
|
frameDuration: 20,
|
|
history: true
|
|
};
|
|
|
|
/**
|
|
* This object contains every handlers bound to conrad events. It does not
|
|
* requirea any DOM implementation, since the events are all JavaScript.
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
var _handlers = Object.create(null);
|
|
|
|
|
|
/**
|
|
* PRIVATE FUNCTIONS:
|
|
* ******************
|
|
*/
|
|
|
|
/**
|
|
* Will execute the handler everytime that the indicated event (or the
|
|
* indicated events) will be triggered.
|
|
*
|
|
* @param {string|array|object} events The name of the event (or the events
|
|
* separated by spaces).
|
|
* @param {function(Object)} handler The handler to bind.
|
|
* @return {Object} Returns conrad.
|
|
*/
|
|
function _bind(events, handler) {
|
|
var i,
|
|
i_end,
|
|
event,
|
|
eArray;
|
|
|
|
if (!arguments.length)
|
|
return;
|
|
else if (
|
|
arguments.length === 1 &&
|
|
Object(arguments[0]) === arguments[0]
|
|
)
|
|
for (events in arguments[0])
|
|
_bind(events, arguments[0][events]);
|
|
else if (arguments.length > 1) {
|
|
eArray =
|
|
Array.isArray(events) ?
|
|
events :
|
|
events.split(/ /);
|
|
|
|
for (i = 0, i_end = eArray.length; i !== i_end; i += 1) {
|
|
event = eArray[i];
|
|
|
|
if (!_handlers[event])
|
|
_handlers[event] = [];
|
|
|
|
// Using an object instead of directly the handler will make possible
|
|
// later to add flags
|
|
_handlers[event].push({
|
|
handler: handler
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the handler from a specified event (or specified events).
|
|
*
|
|
* @param {?string} events The name of the event (or the events
|
|
* separated by spaces). If undefined,
|
|
* then all handlers are removed.
|
|
* @param {?function(Object)} handler The handler to unbind. If undefined,
|
|
* each handler bound to the event or the
|
|
* events will be removed.
|
|
* @return {Object} Returns conrad.
|
|
*/
|
|
function _unbind(events, handler) {
|
|
var i,
|
|
i_end,
|
|
j,
|
|
j_end,
|
|
a,
|
|
event,
|
|
eArray = Array.isArray(events) ?
|
|
events :
|
|
events.split(/ /);
|
|
|
|
if (!arguments.length)
|
|
_handlers = Object.create(null);
|
|
else if (handler) {
|
|
for (i = 0, i_end = eArray.length; i !== i_end; i += 1) {
|
|
event = eArray[i];
|
|
if (_handlers[event]) {
|
|
a = [];
|
|
for (j = 0, j_end = _handlers[event].length; j !== j_end; j += 1)
|
|
if (_handlers[event][j].handler !== handler)
|
|
a.push(_handlers[event][j]);
|
|
|
|
_handlers[event] = a;
|
|
}
|
|
|
|
if (_handlers[event] && _handlers[event].length === 0)
|
|
delete _handlers[event];
|
|
}
|
|
} else
|
|
for (i = 0, i_end = eArray.length; i !== i_end; i += 1)
|
|
delete _handlers[eArray[i]];
|
|
}
|
|
|
|
/**
|
|
* Executes each handler bound to the event.
|
|
*
|
|
* @param {string} events The name of the event (or the events separated
|
|
* by spaces).
|
|
* @param {?Object} data The content of the event (optional).
|
|
* @return {Object} Returns conrad.
|
|
*/
|
|
function _dispatch(events, data) {
|
|
var i,
|
|
j,
|
|
i_end,
|
|
j_end,
|
|
event,
|
|
eventName,
|
|
eArray = Array.isArray(events) ?
|
|
events :
|
|
events.split(/ /);
|
|
|
|
data = data === undefined ? {} : data;
|
|
|
|
for (i = 0, i_end = eArray.length; i !== i_end; i += 1) {
|
|
eventName = eArray[i];
|
|
|
|
if (_handlers[eventName]) {
|
|
event = {
|
|
type: eventName,
|
|
data: data || {}
|
|
};
|
|
|
|
for (j = 0, j_end = _handlers[eventName].length; j !== j_end; j += 1)
|
|
try {
|
|
_handlers[eventName][j].handler(event);
|
|
} catch (e) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes the most prioritary job once, and deals with filling the stats
|
|
* (done, time, averageTime, currentTime, etc...).
|
|
*
|
|
* @return {?Object} Returns the job object if it has to be killed, null else.
|
|
*/
|
|
function _executeFirstJob() {
|
|
var i,
|
|
l,
|
|
test,
|
|
kill,
|
|
pushed = false,
|
|
time = __dateNow(),
|
|
job = _sortedByPriorityJobs.shift();
|
|
|
|
// Execute the job and look at the result:
|
|
test = job.job();
|
|
|
|
// Deal with stats:
|
|
time = __dateNow() - time;
|
|
job.done++;
|
|
job.time += time;
|
|
job.currentTime += time;
|
|
job.weightTime = job.currentTime / (job.weight || 1);
|
|
job.averageTime = job.time / job.done;
|
|
|
|
// Check if the job has to be killed:
|
|
kill = job.count ? (job.count <= job.done) : !test;
|
|
|
|
// Reset priorities:
|
|
if (!kill) {
|
|
for (i = 0, l = _sortedByPriorityJobs.length; i < l; i++)
|
|
if (_sortedByPriorityJobs[i].weightTime > job.weightTime) {
|
|
_sortedByPriorityJobs.splice(i, 0, job);
|
|
pushed = true;
|
|
break;
|
|
}
|
|
|
|
if (!pushed)
|
|
_sortedByPriorityJobs.push(job);
|
|
}
|
|
|
|
return kill ? job : null;
|
|
}
|
|
|
|
/**
|
|
* Activates a job, by adding it to the _runningJobs object and the
|
|
* _sortedByPriorityJobs array. It also initializes its currentTime value.
|
|
*
|
|
* @param {Object} job The job to activate.
|
|
*/
|
|
function _activateJob(job) {
|
|
var l = _sortedByPriorityJobs.length;
|
|
|
|
// Add the job to the running jobs:
|
|
_runningJobs[job.id] = job;
|
|
job.status = 'running';
|
|
|
|
// Add the job to the priorities:
|
|
if (l) {
|
|
job.weightTime = _sortedByPriorityJobs[l - 1].weightTime;
|
|
job.currentTime = job.weightTime * (job.weight || 1);
|
|
}
|
|
|
|
// Initialize the job and dispatch:
|
|
job.startTime = __dateNow();
|
|
_dispatch('jobStarted', __clone(job));
|
|
|
|
_sortedByPriorityJobs.push(job);
|
|
}
|
|
|
|
/**
|
|
* The main loop of conrad.js:
|
|
* . It executes job such that they all occupate the same processing time.
|
|
* . It stops jobs that do not need to be executed anymore.
|
|
* . It triggers callbacks when it is relevant.
|
|
* . It starts waiting jobs when they need to be started.
|
|
* . It injects frames to keep a constant frapes per second ratio.
|
|
* . It stops itself when there are no more jobs to execute.
|
|
*/
|
|
function _loop() {
|
|
var k,
|
|
o,
|
|
l,
|
|
job,
|
|
time,
|
|
deadJob;
|
|
|
|
// Deal with the newly added jobs (the _jobs object):
|
|
for (k in _jobs) {
|
|
job = _jobs[k];
|
|
|
|
if (job.after)
|
|
_waitingJobs[k] = job;
|
|
else
|
|
_activateJob(job);
|
|
|
|
delete _jobs[k];
|
|
}
|
|
|
|
// Set the _isRunning flag to false if there are no running job:
|
|
_isRunning = !!_sortedByPriorityJobs.length;
|
|
|
|
// Deal with the running jobs (the _runningJobs object):
|
|
while (
|
|
_sortedByPriorityJobs.length &&
|
|
__dateNow() - _lastFrameTime < _parameters.frameDuration
|
|
) {
|
|
deadJob = _executeFirstJob();
|
|
|
|
// Deal with the case where the job has ended:
|
|
if (deadJob) {
|
|
_killJob(deadJob.id);
|
|
|
|
// Check for waiting jobs:
|
|
for (k in _waitingJobs)
|
|
if (_waitingJobs[k].after === deadJob.id) {
|
|
_activateJob(_waitingJobs[k]);
|
|
delete _waitingJobs[k];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if conrad still has jobs to deal with, and kill it if not:
|
|
if (_isRunning) {
|
|
// Update the _lastFrameTime:
|
|
_lastFrameTime = __dateNow();
|
|
|
|
_dispatch('enterFrame');
|
|
setTimeout(_loop, 0);
|
|
} else
|
|
_dispatch('stop');
|
|
}
|
|
|
|
/**
|
|
* Adds one or more jobs, and starts the loop if no job was running before. A
|
|
* job is at least a unique string "id" and a function, and there are some
|
|
* parameters that you can specify for each job to modify the way conrad will
|
|
* execute it. If a job is added with the "id" of another job that is waiting
|
|
* or still running, an error will be thrown.
|
|
*
|
|
* When a job is added, it is referenced in the _jobs object, by its id.
|
|
* Then, if it has to be executed right now, it will be also referenced in
|
|
* the _runningJobs object. If it has to wait, then it will be added into the
|
|
* _waitingJobs object, until it can start.
|
|
*
|
|
* Keep reading this documentation to see how to call this method.
|
|
*
|
|
* @return {Object} Returns conrad.
|
|
*
|
|
* Adding one job:
|
|
* ***************
|
|
* Basically, a job is defined by its string id and a function (the job). It
|
|
* is also possible to add some parameters:
|
|
*
|
|
* > conrad.addJob('myJobId', myJobFunction);
|
|
* > conrad.addJob('myJobId', {
|
|
* > job: myJobFunction,
|
|
* > someParameter: someValue
|
|
* > });
|
|
* > conrad.addJob({
|
|
* > id: 'myJobId',
|
|
* > job: myJobFunction,
|
|
* > someParameter: someValue
|
|
* > });
|
|
*
|
|
* Adding several jobs:
|
|
* ********************
|
|
* When adding several jobs at the same time, it is possible to specify
|
|
* parameters for each one individually or for all:
|
|
*
|
|
* > conrad.addJob([
|
|
* > {
|
|
* > id: 'myJobId1',
|
|
* > job: myJobFunction1,
|
|
* > someParameter1: someValue1
|
|
* > },
|
|
* > {
|
|
* > id: 'myJobId2',
|
|
* > job: myJobFunction2,
|
|
* > someParameter2: someValue2
|
|
* > }
|
|
* > ], {
|
|
* > someCommonParameter: someCommonValue
|
|
* > });
|
|
* > conrad.addJob({
|
|
* > myJobId1: {,
|
|
* > job: myJobFunction1,
|
|
* > someParameter1: someValue1
|
|
* > },
|
|
* > myJobId2: {,
|
|
* > job: myJobFunction2,
|
|
* > someParameter2: someValue2
|
|
* > }
|
|
* > }, {
|
|
* > someCommonParameter: someCommonValue
|
|
* > });
|
|
* > conrad.addJob({
|
|
* > myJobId1: myJobFunction1,
|
|
* > myJobId2: myJobFunction2
|
|
* > }, {
|
|
* > someCommonParameter: someCommonValue
|
|
* > });
|
|
*
|
|
* Recognized parameters:
|
|
* **********************
|
|
* Here is the exhaustive list of every accepted parameters:
|
|
*
|
|
* {?Function} end A callback to execute when the job is ended. It is
|
|
* not executed if the job is killed instead of ended
|
|
* "naturally".
|
|
* {?Integer} count The number of time the job has to be executed.
|
|
* {?Number} weight If specified, the job will be executed as it was
|
|
* added "weight" times.
|
|
* {?String} after The id of another job (eventually not added yet).
|
|
* If specified, this job will start only when the
|
|
* specified "after" job is ended.
|
|
*/
|
|
function _addJob(v1, v2) {
|
|
var i,
|
|
l,
|
|
o;
|
|
|
|
// Array of jobs:
|
|
if (Array.isArray(v1)) {
|
|
// Keep conrad to start until the last job is added:
|
|
_noStart = true;
|
|
|
|
for (i = 0, l = v1.length; i < l; i++)
|
|
_addJob(v1[i].id, __extend(v1[i], v2));
|
|
|
|
_noStart = false;
|
|
if (!_isRunning) {
|
|
// Update the _lastFrameTime:
|
|
_lastFrameTime = __dateNow();
|
|
|
|
_dispatch('start');
|
|
_loop();
|
|
}
|
|
} else if (typeof v1 === 'object') {
|
|
// One job (object):
|
|
if (typeof v1.id === 'string')
|
|
_addJob(v1.id, v1);
|
|
|
|
// Hash of jobs:
|
|
else {
|
|
// Keep conrad to start until the last job is added:
|
|
_noStart = true;
|
|
|
|
for (i in v1)
|
|
if (typeof v1[i] === 'function')
|
|
_addJob(i, __extend({
|
|
job: v1[i]
|
|
}, v2));
|
|
else
|
|
_addJob(i, __extend(v1[i], v2));
|
|
|
|
_noStart = false;
|
|
if (!_isRunning) {
|
|
// Update the _lastFrameTime:
|
|
_lastFrameTime = __dateNow();
|
|
|
|
_dispatch('start');
|
|
_loop();
|
|
}
|
|
}
|
|
|
|
// One job (string, *):
|
|
} else if (typeof v1 === 'string') {
|
|
if (_hasJob(v1))
|
|
throw new Error(
|
|
'[conrad.addJob] Job with id "' + v1 + '" already exists.'
|
|
);
|
|
|
|
// One job (string, function):
|
|
if (typeof v2 === 'function') {
|
|
o = {
|
|
id: v1,
|
|
done: 0,
|
|
time: 0,
|
|
status: 'waiting',
|
|
currentTime: 0,
|
|
averageTime: 0,
|
|
weightTime: 0,
|
|
job: v2
|
|
};
|
|
|
|
// One job (string, object):
|
|
} else if (typeof v2 === 'object') {
|
|
o = __extend(
|
|
{
|
|
id: v1,
|
|
done: 0,
|
|
time: 0,
|
|
status: 'waiting',
|
|
currentTime: 0,
|
|
averageTime: 0,
|
|
weightTime: 0
|
|
},
|
|
v2
|
|
);
|
|
|
|
// If none of those cases, throw an error:
|
|
} else
|
|
throw new Error('[conrad.addJob] Wrong arguments.');
|
|
|
|
// Effectively add the job:
|
|
_jobs[v1] = o;
|
|
_dispatch('jobAdded', __clone(o));
|
|
|
|
// Check if the loop has to be started:
|
|
if (!_isRunning && !_noStart) {
|
|
// Update the _lastFrameTime:
|
|
_lastFrameTime = __dateNow();
|
|
|
|
_dispatch('start');
|
|
_loop();
|
|
}
|
|
|
|
// If none of those cases, throw an error:
|
|
} else
|
|
throw new Error('[conrad.addJob] Wrong arguments.');
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Kills one or more jobs, indicated by their ids. It is only possible to
|
|
* kill running jobs or waiting jobs. If you try to kill a job that does not
|
|
* exist or that is already killed, a warning will be thrown.
|
|
*
|
|
* @param {Array|String} v1 A string job id or an array of job ids.
|
|
* @return {Object} Returns conrad.
|
|
*/
|
|
function _killJob(v1) {
|
|
var i,
|
|
l,
|
|
k,
|
|
a,
|
|
job,
|
|
found = false;
|
|
|
|
// Array of job ids:
|
|
if (Array.isArray(v1))
|
|
for (i = 0, l = v1.length; i < l; i++)
|
|
_killJob(v1[i]);
|
|
|
|
// One job's id:
|
|
else if (typeof v1 === 'string') {
|
|
a = [_runningJobs, _waitingJobs, _jobs];
|
|
|
|
// Remove the job from the hashes:
|
|
for (i = 0, l = a.length; i < l; i++)
|
|
if (v1 in a[i]) {
|
|
job = a[i][v1];
|
|
|
|
if (_parameters.history) {
|
|
job.status = 'done';
|
|
_doneJobs.push(job);
|
|
}
|
|
|
|
_dispatch('jobEnded', __clone(job));
|
|
delete a[i][v1];
|
|
|
|
if (typeof job.end === 'function')
|
|
job.end();
|
|
|
|
found = true;
|
|
}
|
|
|
|
// Remove the priorities array:
|
|
a = _sortedByPriorityJobs;
|
|
for (i = 0, l = a.length; i < l; i++)
|
|
if (a[i].id === v1) {
|
|
a.splice(i, 1);
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
throw new Error('[conrad.killJob] Job "' + v1 + '" not found.');
|
|
|
|
// If none of those cases, throw an error:
|
|
} else
|
|
throw new Error('[conrad.killJob] Wrong arguments.');
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Kills every running, waiting, and just added jobs.
|
|
*
|
|
* @return {Object} Returns conrad.
|
|
*/
|
|
function _killAll() {
|
|
var k,
|
|
jobs = __extend(_jobs, _runningJobs, _waitingJobs);
|
|
|
|
// Take every jobs and push them into the _doneJobs object:
|
|
if (_parameters.history)
|
|
for (k in jobs) {
|
|
jobs[k].status = 'done';
|
|
_doneJobs.push(jobs[k]);
|
|
|
|
if (typeof jobs[k].end === 'function')
|
|
jobs[k].end();
|
|
}
|
|
|
|
// Reinitialize the different jobs lists:
|
|
_jobs = {};
|
|
_waitingJobs = {};
|
|
_runningJobs = {};
|
|
_sortedByPriorityJobs = [];
|
|
|
|
// In case some jobs are added right after the kill:
|
|
_isRunning = false;
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns true if a job with the specified id is currently running or
|
|
* waiting, and false else.
|
|
*
|
|
* @param {String} id The id of the job.
|
|
* @return {?Object} Returns the job object if it exists.
|
|
*/
|
|
function _hasJob(id) {
|
|
var job = _jobs[id] || _runningJobs[id] || _waitingJobs[id];
|
|
return job ? __extend(job) : null;
|
|
}
|
|
|
|
/**
|
|
* This method will set the setting specified by "v1" to the value specified
|
|
* by "v2" if both are given, and else return the current value of the
|
|
* settings "v1".
|
|
*
|
|
* @param {String} v1 The name of the property.
|
|
* @param {?*} v2 Eventually, a value to set to the specified
|
|
* property.
|
|
* @return {Object|*} Returns the specified settings value if "v2" is not
|
|
* given, and conrad else.
|
|
*/
|
|
function _settings(v1, v2) {
|
|
var o;
|
|
|
|
if (typeof a1 === 'string' && arguments.length === 1)
|
|
return _parameters[a1];
|
|
else {
|
|
o = (typeof a1 === 'object' && arguments.length === 1) ?
|
|
a1 || {} :
|
|
{};
|
|
if (typeof a1 === 'string')
|
|
o[a1] = a2;
|
|
|
|
for (var k in o)
|
|
if (o[k] !== undefined)
|
|
_parameters[k] = o[k];
|
|
else
|
|
delete _parameters[k];
|
|
|
|
return this;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if conrad is currently running, and false else.
|
|
*
|
|
* @return {Boolean} Returns _isRunning.
|
|
*/
|
|
function _getIsRunning() {
|
|
return _isRunning;
|
|
}
|
|
|
|
/**
|
|
* Unreference every job that is stored in the _doneJobs object. It will
|
|
* not be possible anymore to get stats about these jobs, but it will release
|
|
* the memory.
|
|
*
|
|
* @return {Object} Returns conrad.
|
|
*/
|
|
function _clearHistory() {
|
|
_doneJobs = [];
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns a snapshot of every data about jobs that wait to be started, are
|
|
* currently running or are done.
|
|
*
|
|
* It is possible to get only running, waiting or done jobs by giving
|
|
* "running", "waiting" or "done" as fist argument.
|
|
*
|
|
* It is also possible to get every job with a specified id by giving it as
|
|
* first argument. Also, using a RegExp instead of an id will return every
|
|
* jobs whose ids match the RegExp. And these two last use cases work as well
|
|
* by giving before "running", "waiting" or "done".
|
|
*
|
|
* @return {Array} The array of the matching jobs.
|
|
*
|
|
* Some call examples:
|
|
* *******************
|
|
* > conrad.getStats('running')
|
|
* > conrad.getStats('waiting')
|
|
* > conrad.getStats('done')
|
|
* > conrad.getStats('myJob')
|
|
* > conrad.getStats(/test/)
|
|
* > conrad.getStats('running', 'myRunningJob')
|
|
* > conrad.getStats('running', /test/)
|
|
*/
|
|
function _getStats(v1, v2) {
|
|
var a,
|
|
k,
|
|
i,
|
|
l,
|
|
stats,
|
|
pattern,
|
|
isPatternString;
|
|
|
|
if (!arguments.length) {
|
|
stats = [];
|
|
|
|
for (k in _jobs)
|
|
stats.push(_jobs[k]);
|
|
|
|
for (k in _waitingJobs)
|
|
stats.push(_waitingJobs[k]);
|
|
|
|
for (k in _runningJobs)
|
|
stats.push(_runningJobs[k]);
|
|
|
|
stats = stats.concat(_doneJobs);
|
|
}
|
|
|
|
if (typeof v1 === 'string')
|
|
switch (v1) {
|
|
case 'waiting':
|
|
stats = __objectValues(_waitingJobs);
|
|
break;
|
|
case 'running':
|
|
stats = __objectValues(_runningJobs);
|
|
break;
|
|
case 'done':
|
|
stats = _doneJobs;
|
|
break;
|
|
default:
|
|
pattern = v1;
|
|
}
|
|
|
|
if (v1 instanceof RegExp)
|
|
pattern = v1;
|
|
|
|
if (!pattern && (typeof v2 === 'string' || v2 instanceof RegExp))
|
|
pattern = v2;
|
|
|
|
// Filter jobs if a pattern is given:
|
|
if (pattern) {
|
|
isPatternString = typeof pattern === 'string';
|
|
|
|
if (stats instanceof Array) {
|
|
a = stats;
|
|
} else if (typeof stats === 'object') {
|
|
a = [];
|
|
|
|
for (k in stats)
|
|
a = a.concat(stats[k]);
|
|
} else {
|
|
a = [];
|
|
|
|
for (k in _jobs)
|
|
a.push(_jobs[k]);
|
|
|
|
for (k in _waitingJobs)
|
|
a.push(_waitingJobs[k]);
|
|
|
|
for (k in _runningJobs)
|
|
a.push(_runningJobs[k]);
|
|
|
|
a = a.concat(_doneJobs);
|
|
}
|
|
|
|
stats = [];
|
|
for (i = 0, l = a.length; i < l; i++)
|
|
if (isPatternString ? a[i].id === pattern : a[i].id.match(pattern))
|
|
stats.push(a[i]);
|
|
}
|
|
|
|
return __clone(stats);
|
|
}
|
|
|
|
|
|
/**
|
|
* TOOLS FUNCTIONS:
|
|
* ****************
|
|
*/
|
|
|
|
/**
|
|
* This function takes any number of objects as arguments, copies from each
|
|
* of these objects each pair key/value into a new object, and finally
|
|
* returns this object.
|
|
*
|
|
* The arguments are parsed from the last one to the first one, such that
|
|
* when two objects have keys in common, the "earliest" object wins.
|
|
*
|
|
* Example:
|
|
* ********
|
|
* > var o1 = {
|
|
* > a: 1,
|
|
* > b: 2,
|
|
* > c: '3'
|
|
* > },
|
|
* > o2 = {
|
|
* > c: '4',
|
|
* > d: [ 5 ]
|
|
* > };
|
|
* > __extend(o1, o2);
|
|
* > // Returns: {
|
|
* > // a: 1,
|
|
* > // b: 2,
|
|
* > // c: '3',
|
|
* > // d: [ 5 ]
|
|
* > // };
|
|
*
|
|
* @param {Object+} Any number of objects.
|
|
* @return {Object} The merged object.
|
|
*/
|
|
function __extend() {
|
|
var i,
|
|
k,
|
|
res = {},
|
|
l = arguments.length;
|
|
|
|
for (i = l - 1; i >= 0; i--)
|
|
for (k in arguments[i])
|
|
res[k] = arguments[i][k];
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* This function simply clones an object. This object must contain only
|
|
* objects, arrays and immutable values. Since it is not public, it does not
|
|
* deal with cyclic references, DOM elements and instantiated objects - so
|
|
* use it carefully.
|
|
*
|
|
* @param {Object} The object to clone.
|
|
* @return {Object} The clone.
|
|
*/
|
|
function __clone(item) {
|
|
var result, i, k, l;
|
|
|
|
if (!item)
|
|
return item;
|
|
|
|
if (Array.isArray(item)) {
|
|
result = [];
|
|
for (i = 0, l = item.length; i < l; i++)
|
|
result.push(__clone(item[i]));
|
|
} else if (typeof item === 'object') {
|
|
result = {};
|
|
for (i in item)
|
|
result[i] = __clone(item[i]);
|
|
} else
|
|
result = item;
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns an array containing the values of an object.
|
|
*
|
|
* @param {Object} The object.
|
|
* @return {Array} The array of values.
|
|
*/
|
|
function __objectValues(o) {
|
|
var k,
|
|
a = [];
|
|
|
|
for (k in o)
|
|
a.push(o[k]);
|
|
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* A short "Date.now()" polyfill.
|
|
*
|
|
* @return {Number} The current time (in ms).
|
|
*/
|
|
function __dateNow() {
|
|
return Date.now ? Date.now() : new Date().getTime();
|
|
}
|
|
|
|
/**
|
|
* Polyfill for the Array.isArray function:
|
|
*/
|
|
if (!Array.isArray)
|
|
Array.isArray = function(v) {
|
|
return Object.prototype.toString.call(v) === '[object Array]';
|
|
};
|
|
|
|
|
|
/**
|
|
* EXPORT PUBLIC API:
|
|
* ******************
|
|
*/
|
|
var conrad = {
|
|
hasJob: _hasJob,
|
|
addJob: _addJob,
|
|
killJob: _killJob,
|
|
killAll: _killAll,
|
|
settings: _settings,
|
|
getStats: _getStats,
|
|
isRunning: _getIsRunning,
|
|
clearHistory: _clearHistory,
|
|
|
|
// Events management:
|
|
bind: _bind,
|
|
unbind: _unbind,
|
|
|
|
// Version:
|
|
version: '0.1.0'
|
|
};
|
|
|
|
if (typeof exports !== 'undefined') {
|
|
if (typeof module !== 'undefined' && module.exports)
|
|
exports = module.exports = conrad;
|
|
exports.conrad = conrad;
|
|
}
|
|
global.conrad = conrad;
|
|
})(this);
|