A minimal JavaScript library (3.7k minified) for automating practically anything in Javascript.
Annotated Source Code | Unit Tests
The purpose of an Automator is to accept an Array of steps, and to automate them for you. It's that simple.
// Press right, sleep for 1000ms, press left, sleep for 1000ms
var automator = new Automator();
automator.automate([ 'right', 1000, 'left', 1000 ]);
While trying to provide some useful defaults, the Automator leaves the behavior of each step entirely up to you. The behavior of a step is determined by the typeof
the step (number, string, function, object). Automator attempts to provide some useful default functionality:
The behavior of each of these can be customized through the options passed to the Automator constructor:
// Lets treat numbers as seconds instead of milliseconds
var automator = new Automator({
doNumber: function (n) {
var dfd = new Automator.MiniDeferred();
setTimeout(dfd.resolve, n * 1000);
return dfd.promise();
},
// doFunction: function (func) { ... },
// doString: function (str) { ... },
// doObject: function (obj) { ... }
});
automator.automate([ 'right', 1, 'left', 1 ]);
// Alternatively, you can proxy the default functions
var automator = new Automator({
doNumber: function (n) {
return Automator.doNumber(n * 1000);
}
});
automator.automate([ 'right', 1, 'left', 1 ]);
Want to run steps repeatedly? Automator supports repetition at the step level for string values:
// Any string ending in /x[0-9]+/ will be repeated that many times, so:
automator.automate([ 'rightx3', 1000, 'leftx3', 1000 ]);
// Is exactly equivalent to
automator.automate([ 'right', 'right', 'right', 1000, 'left', 'left', 'left', 1000 ]);
Want to run full automations repeatedly? This is just as easy:
function sequenceCb(i) {
console.log("Done with iteraton " + i);
}
// The optional second parameter to automate() is how many times to run the sequence
// The optional third parameter is a callback function to run after each sequence
// Run the full sequence 3 times
automator.automate([ 'right', 1000, 'left', 1000 ], 3, sequenceCb);
Want to do something at the very end of the entire, potentially repeated, sequence? The automate() function returns a Promise-esque object you can hook into:
// Run the full sequence 3 times
automator.automate([ 'right', 1000, 'left', 1000 ], 3).then(function () {
console.log("I'm all done!");
});
Note: This Promise-esque object is by no means a full-featured promise object. In order to avoid dependencies, Automator has it's own Automator.MiniDeferred implementation that simply implements basic resolve/reject/done/fail/always callbacks. If you would like to use a more full-featured implementation, simply specify that implemnentation in options.Deferred. The Deferred object is expected to implement the same API as Automator.MiniDeferred. For example:
var automator = new Automator({
Deferred: $.Deferred
});
a.automate(['left', 'right'])
// Returns a jQuery Promise
Want to be asynchronous? No problem. Any step in an Automator sequence that returns a jQuery Deferred-esque object will cause the following step to wait upon resolution or rejection of the promise.
var automator = new Automator();
function doAsync () {
var dfd = new $.Deferred();
// Do something asynchronous and resolve deferred
return dfd.promise();
}
// The 'right' step will not execute until after the asynchronous operation has completed.
automator.automate([doAsync, 'right']);
If you need to execute a few dynamic steps, potentially ones that rely on the state of your applicaton or a previous step, just return an array from an Automator step. This array of steps will be inserted into the sequence immediately after the current step.
var automator = new Automator();
function addSteps () {
return ['up', 'down'];
}
// The full sequence will run as: right, up, down, left
automator.automate(['right', addSteps, 'left']);
Any return values from Automator steps that are not a Promise or an Array will simply be passed through to the next step in the process.
var automator = new Automator();
function createVal () {
return Math.random();
}
function handleVal(randomNum) {
console.log("I got the random number: " + randomNum);
}
automator.automate([createVal, handleVal]);
The default behavior for strings (left, right, etc.) is to mimic a keydown event. For simplicity, keyup events are not sent, but it's very easy to provide a keyup event after a delay. Something like the following would work:
var automator = new Automator({
doString: function(str) {
var keyCode = Automator.keyCodeMap[str],
dfd = new Automator.MiniDeferred();
if (typeof keyCode !== 'number') { return; }
// Inherit the standard keydown behavior
Automator.simulateKeyEvent('keydown', keyCode);
// Perform the keyup event after a delay
// then tell Automator we're done with this step
setTimeout(function () {
Automator.simulateKeyEvent('keyup', keyCode);
dfd.resolve();
}, 100);
// Force subsequent steps to wait for the keyup event
return dfd.promise();
}
});