Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.
From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).
From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other types.
Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more.
A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.
In computer science a daemon is a task that runs as a background process, rather than being under the direct control of an interactive user. In the JavaScript programming language, daemons are all processes created by JavaScript timers or by a Worker instantiation. Here are some code snippets that simplify and abstract the management of daemons.
The following daemons management framework is the major version of setInterval – A little framework.
Introduction
Sometimes a page uses dozens and dozens of animations. In such a condition it is difficult and unnatural to keep track of all events started and then to stop them when appropriate through the clearTimeout() function. A possible approach to solve this problem is to nest all the information needed by each animation to start, stop, etc. in different objects (daemons) and then to create a constructor for such a class of objects (Daemon) in order to standardize and simplify the instantiation of them.
MiniDaemon) is published here. This more complex version of it is nothing but a big and scalable collection of methods for the Daemon constructor. But the Daemon constructor itself is nothing but a clone of MiniDaemon with an added support for init and onstart functions declarable during the instantiation of the daemon. So the MiniDaemon framework will remain the recommended way for simple animations, because Daemon without its collection of methods is essentially a clone of it.The architecture of the Daemon constructor explicitly avoids the use of closures. It also offers an alternative way to pass the this object to the callback functions (see The "this" problem for details).
Advantages of this approch:
- abstraction
- passage of
thisobject to JavaScript timers (bothsetIntervalandsetTimeout) - optimisation (avoiding closures)
- modularity
The code
The code of this framework is split into three files:
- daemon.js (the core)
- daemon-safe.js (an extension of the core which adds a replacement of
setIntervalwith a recursive invocation ofsetTimeout) - daemon-methods.js (a wide and highly scalable collection of methods)
The only independent module is daemon.js: both the daemon-safe.js module and the daemon-methods.js module require daemon.js to work.
For a fast overview on the code proposed here you can git clone https://github.com/madmurphy/daemon.js.git. or, at your choice, directly download this .zip file.
About the “callback arguments” polyfill
In order to make this framework compatible with Internet Explorer (which doesn't support sending additional parameters to timers' callback function, neither with setTimeout() or setInterval()) we included the IE-specific compatibility code (commented code), which enables the HTML5 standard parameters' passage functionality in that browser for both timers (polyfill), at the end of the daemon.js module.
daemon.js
What follows is the module that declares the Daemon constructor (the core of this framework). The Daemon constructor itself is nothing but a clone of the little framework MiniDaemon with added support for init and onstart functions (declarable during the instantiation of the daemon). So the MiniDaemon framework will remain the recommended way for simple animations, because Daemon without its collection of methods is essentially a clone of it.
/*\
|*|
|*| *******************************
|*| * daemon.js *
|*| *******************************
|*|
|*| ver. 1.0 rev. 2
|*|
|*| daemon.js - A JAVASCRIPT HIGHLY SCALABLE DAEMONS MANAGER.
|*|
|*| https://developer.mozilla.org/en-US/docs/JavaScript/Timers/Daemons
|*| https://developer.mozilla.org/User:fusionchess
|*|
|*| This framework is released under the GNU Public License, version 3 or later.
|*| http://www.gnu.org/licenses/gpl-3.0.html
|*|
\*/
"use strict";
/****************************
* THE DAEMON SYSTEM *
****************************/
/* The global "Daemon" constructor */
function Daemon (oOwner, fTask, nRate, nLen, fInit, fOnstart) {
if (!(this && this instanceof Daemon)) { return; }
if (arguments.length < 2) { throw new TypeError("Daemon - not enough arguments"); }
if (oOwner) { this.owner = oOwner };
this.task = fTask;
if (isFinite(nRate) && nRate > 0) { this.rate = Math.floor(nRate); }
if (nLen > 0) { this.length = Math.floor(nLen); }
if (fOnstart) { this.onstart = fOnstart; }
if (fInit) {
this.onstop = fInit;
fInit.call(oOwner, this.INDEX, this.length, this.BACKW);
}
}
/* Create the Daemon.blank() constructor and the global Daemon.context object */
Daemon.blank = function () {};
Daemon.context = Daemon.blank.prototype; /* Make love with the GC :-) */
Daemon.blank.prototype = /* Important! */ Daemon.prototype;
Daemon.context.constructor = Object;
/* These properties can be manually reconfigured after the creation of the daemon */
Daemon.prototype.owner = Daemon.context;
Daemon.prototype.task = null;
Daemon.prototype.rate = 100;
Daemon.prototype.length = Infinity;
Daemon.prototype.reversals = 0;
Daemon.prototype.onstart = null;
Daemon.prototype.onstop = null;
/* These properties should be read-only after the creation of the daemon */
Daemon.prototype.SESSION = -1;
Daemon.prototype.INDEX = 0;
Daemon.prototype.PAUSED = true;
Daemon.prototype.BACKW = true;
/* SYSTEM REQUIRED Daemon global object methods */
Daemon.forceCall = function (oDmn) {
oDmn.INDEX += oDmn.BACKW ? -1 : 1;
var
bBreak = oDmn.task.call(oDmn.owner, oDmn.INDEX, oDmn.length, oDmn.BACKW) === false,
bEnd = oDmn.isAtEnd(), bInvert = oDmn.reversals > 0;
if (bEnd && !bInvert || bBreak) {
oDmn.pause();
return false;
}
if (bEnd && bInvert) {
oDmn.BACKW = !oDmn.BACKW;
oDmn.reversals--;
}
return true;
};
/* SYSTEM NOT REQUIRED Daemon global object methods */
/**
* Daemon global object optional methods (shortcuts). You could safely remove all or some of them,
* depending on your needs. If you want to remove them and are using the Daemon.safe subsystem you
* should also remove the Daemon.safe global object methods that require them.
**/
Daemon.construct = function (aArgs) {
var oDmn = new this.blank();
this.apply(oDmn, aArgs);
return oDmn;
};
Daemon.buildAround = function (oCtx, nRate, nLen) {
if (!oCtx.perform) { throw new TypeError("You cannot create a daemon around an object devoid of a \"perform\" function"); }
return new this(oCtx, oCtx.perform, nRate || null, nLen || null, oCtx.create || null, oCtx.prepare || null);
};
/* Warning! Calling Daemon.incorporate(@func) will modify the @func.prototype property! */
Daemon.incorporate = function (fConstr) {
var oLegacy = fConstr.prototype;
fConstr.prototype = new Daemon.blank();
fConstr.prototype.legacy = oLegacy;
return fConstr;
};
/* SYSTEM REQUIRED Daemon instances methods */
Daemon.prototype.isAtEnd = function () {
return this.BACKW ? isFinite(this.length) && this.INDEX < 1 : this.INDEX + 1 > this.length;
};
Daemon.prototype.synchronize = function () {
if (this.PAUSED) { return; }
clearInterval(this.SESSION);
this.SESSION = setInterval(Daemon.forceCall, this.rate, this);
};
Daemon.prototype.pause = function () {
clearInterval(this.SESSION);
this.PAUSED = true;
};
/* SYSTEM NOT REQUIRED Daemon instances methods */
/**
* Basic user interface. You could remove this method, but your daemon will be virtually unusable.
* All other optional instances methods depend on this one or on the previous ones.
**/
Daemon.prototype.start = function (bReverse) {
var bBackw = Boolean(bReverse);
if (this.BACKW === bBackw && (this.isAtEnd() || !this.PAUSED)) { return false; }
this.BACKW = bBackw;
this.PAUSED = false;
if (this.onstart) { this.onstart.call(this.owner, this.INDEX, this.length, this.BACKW); }
this.synchronize();
return true;
};
Daemon.prototype.stop = function () {
this.pause();
if (this.onstop) { this.onstop.call(this.owner, this.INDEX, this.length, this.BACKW); }
delete this.INDEX;
delete this.BACKW;
delete this.reversals;
};
/*******************************
* DAEMON IS NOW READY! *
*******************************/
/*******************************
* POLYFILLS *
*******************************/
/*\
|*|
|*| IE-specific polyfill which enables the passage of arbitrary arguments to the
|*| callback functions of JavaScript timers (HTML5 standard syntax).
|*|
|*| https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
|*|
|*| Syntax:
|*| var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
|*| var timeoutID = window.setTimeout(code, delay);
|*| var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
|*| var intervalID = window.setInterval(code, delay);
|*|
\*/
/*
if (document.all && !window.setTimeout.isPolyfill) {
var __nativeST__ = window.setTimeout;
window.setTimeout = function (vCallback, nDelay) {
var aArgs = Array.prototype.slice.call(arguments, 2);
return __nativeST__(vCallback instanceof Function ? function () {
vCallback.apply(null, aArgs);
} : vCallback, nDelay);
};
window.setTimeout.isPolyfill = true;
}
if (document.all && !window.setInterval.isPolyfill) {
var __nativeSI__ = window.setInterval;
window.setInterval = function (vCallback, nDelay) {
var aArgs = Array.prototype.slice.call(arguments, 2);
return __nativeSI__(vCallback instanceof Function ? function () {
vCallback.apply(null, aArgs);
} : vCallback, nDelay);
};
window.setInterval.isPolyfill = true;
}
*/
daemon-safe.js
What follows is the module which extends the Daemon constructor with a Daemon.safe sub-system. This library requires daemon.js to work. The Daemon.safe constructor is a dependent-clone of Daemon which uses a recursive invocation of the setTimeout timer rather than a single invocation of setInterval.
/*\
|*|
|*| *******************************
|*| * daemon-safe.js *
|*| *******************************
|*|
|*| ver. 1.0 rev. 2
|*|
|*| daemon.js - A JAVASCRIPT HIGHLY SCALABLE DAEMONS MANAGER
|*|
|*| https://developer.mozilla.org/en-US/docs/JavaScript/Timers/Daemons
|*| https://developer.mozilla.org/User:fusionchess
|*|
|*| This framework is released under the GNU Public License, version 3 or later.
|*| http://www.gnu.org/licenses/gpl-3.0.html
|*|
\*/
"use strict";
/**************************************
* THE SAFE-DAEMON SUB-SYSTEM *
**************************************/
/* The "Daemon.safe" constructor */
Daemon.safe = function () { Daemon.apply(this, arguments); };
/* Create the Daemon.safe.blank() constructor and the Daemon.safe.context object */
Daemon.safe.blank = function () {};
Daemon.safe.context = Daemon.safe.prototype;
Daemon.TO_BE_DEFINED = Daemon.safe.blank.prototype; /* Make love with the GC :-) */
Daemon.safe.blank.prototype = /* Important! */ Daemon.safe.prototype = /* Important! */ new Daemon.blank();
Daemon.safe.prototype.constructor = Daemon.safe;
Daemon.TO_BE_DEFINED.constructor = Daemon.safe.context.constructor = Object;
/* SYSTEM REQUIRED Daemon.safe global object methods */
Daemon.safe.forceCall = function (oDmn) {
oDmn.INDEX += oDmn.BACKW ? -1 : 1;
var
bBreak = oDmn.task.call(oDmn.owner, oDmn.INDEX, oDmn.length, oDmn.BACKW) === false,
bEnd = oDmn.isAtEnd(), bInvert = oDmn.reversals > 0;
if (bEnd && !bInvert || bBreak) {
oDmn.PAUSED = true;
return false;
}
if (bEnd && bInvert) {
oDmn.BACKW = !oDmn.BACKW;
oDmn.reversals--;
}
oDmn.synchronize();
return true;
};
/* SYSTEM NOT REQUIRED Daemon.safe global object methods */
/**
* Daemon.safe global object optional methods (shortcuts). You could safely remove all or some of
* them, depending on your needs.
**/
/* Warning: this method requires the global Daemon.construct() method */
Daemon.safe.construct = Daemon.construct;
/* Warning: this method requires the global Daemon.buildAround() method */
Daemon.safe.buildAround = Daemon.buildAround;
/* Warning: this method requires the global Daemon.incorporate() method */
Daemon.safe.incorporate = Daemon.incorporate;
/* SYSTEM REQUIRED Daemon.safe instances methods */
Daemon.safe.prototype.synchronize = function () {
if (this.PAUSED) { return; }
clearTimeout(this.SESSION);
this.SESSION = setTimeout(Daemon.safe.forceCall, this.rate, this);
};
Daemon.safe.prototype.pause = function () {
clearTimeout(this.SESSION);
this.PAUSED = true;
};
/* SYSTEM NOT REQUIRED Daemon.safe instances methods */
/* [inherited from Daemon.prototype] */
/****************************************
* THE SAFE-DAEMON IS NOW READY! *
*****************************************/
daemon-methods.js
What follows is a module which collects a wide and scalable list of additional methods for instances of Daemon (and Daemon.safe). This module requires daemon.js to work. You can safely remove all or some of these methods, depending on your needs (modularity). When a method requires the presence of another method (it is relatively rare), there is a comment which explains why. If there are not comments to that method it depends only on the base system.
/*\
|*|
|*| *******************************
|*| * daemon-methods.js *
|*| *******************************
|*|
|*| ver. 1.0 rev. 2
|*|
|*| daemon.js - A JAVASCRIPT HIGHLY SCALABLE DAEMONS MANAGER
|*|
|*| https://developer.mozilla.org/en-US/docs/JavaScript/Timers/Daemons
|*| https://developer.mozilla.org/User:fusionchess
|*|
|*| This framework is released under the GNU Public License, version 3 or later.
|*| http://www.gnu.org/licenses/gpl-3.0.html
|*|
\*/
"use strict";
/* SYSTEM NOT REQUIRED Daemon instances methods */
/* Movement */
Daemon.prototype.syncStart = function (bReverse) {
this.synchronize();
if (this.start(bReverse || false)) {
Daemon.forceCall(this);
return true;
}
return false;
};
Daemon.prototype.play = function (bSync) {
/* Warning: this method OPTIONALLY requires the Daemon.prototype.syncStart() method */
return this[bSync ? "start" : "syncStart"]();
};
Daemon.prototype.reversePlay = function (bSync) {
/* Warning: this method OPTIONALLY requires the Daemon.prototype.syncStart() method */
return this[bSync ? "start" : "syncStart"](true);
};
Daemon.prototype.move = function (bSync, nCycles, bDirection) {
/* Warning: this method OPTIONALLY requires the Daemon.prototype.syncStart() method */
if (arguments.length > 1 && !isNaN(nCycles)) { this.reversals = Number(nCycles); }
this[bSync ? "start" : "syncStart"](arguments.length > 2 ? bDirection : this.isAtEnd() !== this.BACKW);
};
Daemon.prototype.turn = function (bSync) {
/* Warning: this method OPTIONALLY requires the Daemon.prototype.syncStart() method */
this[bSync ? "start" : "syncStart"](!this.BACKW);
};
/* Settings tools */
Daemon.prototype.makeLoop = function () {
this.reversals = Infinity;
};
Daemon.prototype.unmakeLoop = function () {
this.reversals = 0;
};
Daemon.prototype.setRate = function (vTo) {
var nRate = Number(vTo);
if (!isFinite(nRate) || nRate < 1) { return; }
this.rate = nRate;
this.synchronize();
};
Daemon.prototype.forcePosition = function (vTo) {
if (isFinite(vTo)) { this.INDEX = Math.round(Math.abs(vTo)); }
};
Daemon.prototype.getDuration = function () {
return this.rate * this.length;
};
Daemon.prototype.getDirection = function () {
return this.isAtEnd() !== this.BACKW;
};
Daemon.prototype.getPosition = function () {
return this.INDEX;
};
/* Instantaneous movement (synchronous). */
Daemon.prototype.makeSteps = function (nHowMany, bReverse, bForce) {
if (nHowMany === 0) { return true; }
if (isNaN(nHowMany)) { return false; }
var nIdx = 0, nLen = Math.round(Math.abs(nHowMany)), bContinue = true, bBackw = nHowMany > 0 === Boolean(bReverse);
this.BACKW = bBackw;
for (nIdx; nIdx < nLen && bContinue; nIdx++) {
if (this.BACKW === bBackw && this.isAtEnd()) {
if (this.reversals > 0) { this.BACKW = bBackw = !this.BACKW; }
else { break; }
}
bContinue = Daemon.forceCall(this) || bForce;
}
return nIdx === nLen;
};
Daemon.prototype.skipTo = function (nIdx, bForce) {
/* Warning: this method requires the Daemon.prototype.makeSteps() method */
if (nIdx === this.INDEX) { return; }
if (isFinite(this.length) && nIdx < 0 || nIdx > this.length) { return; }
var
bDirection = (this.INDEX !== 0 && this.INDEX !== this.length) === this.BACKW,
bSuccess = this.makeSteps(this.INDEX - nIdx, true, bForce);
if (this.INDEX !== 0 && this.INDEX !== this.length) { this.BACKW = bDirection; }
return bSuccess;
};
Daemon.prototype.skipFor = function (nDelta, bForce) {
/* Warning: this method requires the Daemon.prototype.makeSteps() method */
return this.makeSteps(nDelta, this.isAtEnd() !== this.BACKW, bForce);
};
Daemon.prototype.close = function (bReverse, bForce) {
/* Warning: this method requires the Daemon.prototype.makeSteps() method */
if (!isFinite(this.length)) { return false; }
this.pause();
this.reversals = 0;
return this.makeSteps(bReverse ? this.INDEX : this.length - this.INDEX, bReverse, bForce);
};
Daemon.prototype.reclose = function (bForce) {
/* Warning: this method requires the Daemon.prototype.makeSteps() and Daemon.prototype.close() methods */
return this.close(this.isAtEnd() !== this.BACKW, bForce || false);
};
/* Others */
Daemon.prototype.restart = function () {
this.stop();
this.start();
};
Daemon.prototype.loopUntil = function (vDate) {
if (!isFinite(this.length)) { return; }
var nTime = vDate.constructor === Date ? vDate.getTime() : isNaN(vDate) ? Date.parse(vDate) : vDate;
if (isFinite(nTime) && nTime > Date.now()) {
this.reversals = Math.floor((nTime - Date.now() - (this.BACKW ? this.INDEX : this.length - this.INDEX) * this.rate) / (this.length * this.rate));
this.start(this.isAtEnd() !== this.BACKW);
}
};
Daemon.prototype.spread = function (nTime) {
if (!isFinite(this.length)) { throw new TypeError("Daemon.prototype.spread - the length is not a finite number. Use Daemon.prototype.adaptLength()."); }
if (nTime > 0) {
this.rate = Math.floor(nTime / this.length);
this.synchronize();
}
return this.rate;
};
Daemon.prototype.adaptLength = function (nTime) {
this.length = Math.floor(nTime / this.rate);
return this.length;
};
Daemon.prototype.playUntil = function (vDate) {
var nTime = vDate.constructor === Date ? vDate.getTime() : isNaN(vDate) ? Date.parse(vDate) : vDate;
if (isFinite(nTime) && nTime > Date.now()) {
this.length = Math.floor((nTime - Date.now()) / this.rate) + this.INDEX;
this.pause();
this.start();
}
return this.length;
};
Manual
The constructor
Syntax
var myDaemon = new Daemon(thisObject, callback[, rate[, length[, init[, onstart]]]]);
Description
Constructs a JavaScript object containing all information needed by an animation (like the this object, the callback function, the length, the frame rate, the number of cycles, and the init and onstart functions).
Arguments
thisObject- The
thisobject on which will be called the callback function. It can be an object ornull. If isnull, thethisobject will point to the globalDaemon.contextobject (see below). callback- The function which will be invoked repeatedly. It will be called with three parameters:
index(the iterative index of each invocation),length(the number of total invocations assigned to the daemon - finite orInfinity), andbackwards(a boolean expressing whether the process is going backwards or not). It will be something likecallback.call(thisObject, index, length, backwards). If thecallbackfunction returns afalsevalue, the daemon will be paused. rateOptional- The time lapse (in number of milliseconds) between each invocation. The default value is 100.
lengthOptional- The total number of invocations. It can be a positive integer or
Infinity. The default value isInfinity. initOptional- The function which will be synchronously invoked once during the creation of the daemon and then assigned to the
onstopproperty. It will be called with the same three arguments of thecallbackfunction. They will also have the same values of the last invocation of thecallbackfunction. onstartOptional- The function which will be synchronously invoked whenever the
startevent occurs. It will be assigned to theonstartproperty. It will be called with three arguments:index(the iterative index of each invocation),length(the number of total invocations assigned to the daemon - finite orInfinity), andbackwards(a boolean expressing whether the process is going backwards or not).
Daemon global object properties
Daemon.context- An empty object used as the default
thisobject when thethisObjectis not specified during the construction of the daemon. If you want yourcallbackfunction to be invoked with anullthisobject, you have to manually set theownerproperty tonull.
Daemon global object methods
Daemon.forceCall(daemonInstance)- Forces a callback to the
daemonInstance.taskfunction regardless of whether or not the end has been reached. In any case the internalINDEXproperty will be increased/decreased (depending on the actual direction of the daemon). Daemon.construct(arrayOfArguments)- Returns a new daemon constructed upon an array of arguments. It is very similar to the
Function.prototype.apply()method. Daemon.buildAround(context[, rate[, length]])- Returns a new daemon built around a
contextobject. Thecontextobject must cointain *at least* aperformproperty pointing to what you want to be thecallbackfunction of the daemon. It can also optionally contain acreateand aprepareproperty pointing respectively to the two functions you want to be theinitandonstartfunctions ofDaemonconstructor's arguments. Thecontextobject will be also thethisobject of yourcallbackfunction. So you can populate it with all your custom properties and methods. The only required one property isperform.Sample usage:
var myDaemon = Daemon.buildAround({ "create": function () { [custom code] }, // optional "prepare": function () { [custom code] }, // optional "perform": function () { [custom code] }, // required "customProperty": [custom value], "myCustomMethod": function () { [custom code] }, "anotherCustomProperty": [custom value], "etc.": "etc." }, 200, 30); Daemon.incorporate(function)- The
function.prototypeproperty will replaced with anew Daemon()object. The old replaced prototype will be preserved within a new property of the newfunction.prototypeobject namedlegacy.
Other constructors
Daemon.blank()- Constructs a new instance of
Daemonwithout own properties. This constructor is useful in order to create your own *MySomeDaemon* constructors through theMySomeDaemon.prototype = new Daemon.blank();assignment. Daemon.safe()[optional module daemon-safe.js]-
Syntax
var myDaemon = new Daemon.safe(thisObject, callback[, rate[, length[, init[, onstart]]]]);
Description
Daemon.safeis a clone of the Daemon constructor based on recursive invocations ofsetTimeoutrather than on a single invocation ofsetInterval. See the daemon-safe.js module for details.Note: TheDaemon.safeconstructor is not part of the base system. Daemon.safe.blank()[optional module daemon-safe.js]-
The same as for
Daemon.blank, but applied to theDaemon.safeconstructor.Note: TheDaemon.safeconstructor is not part of the base system.
Daemon instances properties
myDaemon.owner- The
thisobject on which is executed the daemon (read/write). It can be an object ornull. Its default value isDaemon.context. myDaemon.task- The function which will be repeatedly invoked (read/write). It will be called with three arguments:
index(the iterative index of each invocation),length(the number of total invocations assigned to the daemon - finite orInfinity), andbackwards(a boolean expressing whether the process is going backwards or not). See above. If the myDaemon.task function returns afalsevalue, the daemon will be paused. myDaemon.rate- The time lapse (in number of milliseconds) between each invocation (read/write).
myDaemon.length- The total number of invocations. It can be a positive integer or
Infinity(read/write). myDaemon.reversals- The number of times the daemon must be restarted (read/write). Set it to
Infinityfor a “loop mode”. myDaemon.onstart- The
startlistener (read/write). It will be called with three arguments:index(the iterative index of each invocation),length(the number of total invocations assigned to the daemon - finite orInfinity), andbackwards(a boolean expressing whether the process is going backwards or not). myDaemon.onstop-
The
stoplistener (read/write). It will be called with three arguments:index(the iterative index of each invocation),length(the number of total invocations assigned to the daemon - finite orInfinity), andbackwards(a boolean expressing whether the process is going backwards or not).Note: Thestop()method is not part of the base system.
Daemon instances methods
myDaemon.isAtEnd()- Returns a boolean expressing whether the daemon is at the start/end position or not.
myDaemon.synchronize()- Synchronizes the timer of a started daemon with the time of invocation of
myDaemon.synchronize(). If therateproperty has been changed, the daemon will be updated with the newratevalue. myDaemon.pause()- Pauses the daemon.
myDaemon.start([backwards])- Starts the daemon forwards (index of each invocation increasing) or backwards (index decreasing).
myDaemon.stop()- Stops the daemon. All original properties will be restored. If it exists a
stoplistener will be called with three arguments:index(the iterative index of each invocation),length(the number of total invocations assigned to the daemon - finite orInfinity), andbackwards(a boolean expressing whether the process is going backwards or not).
Additional instances methods [optional module daemon-methods.js]
myDaemon.syncStart([backwards])- Starts the daemon with the first callback synchronous.
myDaemon.play([synchronous])- Starts the daemon forward. If the daemon was running in the same direction, nothing will happen. If the argument is
true,the first callback will be synchronous. myDaemon.reversePlay([synchronous])- Starts the daemon backwards. If the daemon was running in the same direction, nothing will happen. If the argument is
true, the first callback will be synchronous. myDaemon.move([synchronous[, reversals[, backwards]]])- Starts a daemon synchronously or not. If the
reversalsargument is specified, themyDaemon.reversalsproperty will be setted on it. If thebackwardsargument is specified, the daemon will move forwards (false) or backwards (true). If it is not specified the daemon will start in the same direction in which it has been left. If the daemon was running in the same direction, only thereversalsproperty will be possibly updated. myDaemon.turn()- Inverts the direction of the daemon. The
reversalsproperty will not be decreased. myDaemon.makeLoop()- Sets the
reversalsproperty equal toInfinity. myDaemon.unmakeLoop()- Sets the
reversalsproperty equal to 0. myDaemon.setRate(milliseconds)- Sets the time lapse between calls (in milliseconds). If the daemon is running it will be immediately updated with the new property.
myDaemon.forcePosition(index)- Sets the internal
INDEXproperty equal to theindexargument without any kind of control about the range. myDaemon.getDuration()- Returns the duration of the daemon in milliseconds.
myDaemon.getPosition()- Returns the internal
INDEXproperty. myDaemon.makeSteps(howMany[, backwards[, force]])- Calls synchronously the
callbackfunctionhowManytimes. Forwards or backwards. If theforceargument is set to true, the possiblereturn falseof thecallbackfunction will be ignored. myDaemon.skipTo(index[, force])- Calls synchronously the
callbackfunction the number of times needed to reach theindexposition. If theforceargument is set to true, the possiblereturn falseof thecallbackfunction will be ignored. myDaemon.skipFor(delta[, force])- Calls synchronously the
callbackfunctionhowManytimes in the same direction in which it was oriented. If theforceargument is set to true, the possiblereturn falseof thecallbackfunction will be ignored. myDaemon.close([backwards[, force]])- Closes the daemon, doing synchronously all pending operations forward (index of each invocation increasing) or backwards (index decreasing). If the
forceargument is set to true, the possiblereturn falseof thecallbackfunction will be ignored. myDaemon.reclose([force])- Closes the daemon, doing synchronously all pending operations in the same direction in which is oriented the daemon. If the
forceargument is set to true, the possiblereturn falseof thecallbackfunction will be ignored. myDaemon.restart()- Stops and restarts the daemon.
myDaemon.loopUntil(date)- Sets the reversals property in function of a future
date. Thedateargument can be aDateobject, a string expressing the date in GMTString format, or a number expressing the number of milliseconds since January 1, 1970, 00:00:00 UTC. myDaemon.spread(milliseconds)- Sets the total duration of the daemon in milliseconds. Only the
rateproperty will be modified. myDaemon.adaptLength(milliseconds)- Sets the internal
lengthproperty equal tomilliseconds/myDaemon.rate. myDaemon.playUntil(date)- Starts the daemon forward and sets the total duration of it in function of a future
date. Thedateargument can be aDateobject, a string expressing the date in GMTString format, or a number expressing the number of milliseconds since January 1, 1970, 00:00:00 UTC. Only the length property will be modified.
Examples
Example #1: A standard instantiation – new Daemon()
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>new Daemon(…)</title>
<script type="text/javascript" src="daemon.js"></script>
<script type="text/javascript" src="daemon-methods.js"></script>
<script type="text/javascript">
function perform (nIndex, nLength, bBackw) {
// http://tyleregeto.com/text-animation-in-javascript
for (var oLetter, nLetter = 0; nLetter < aLetters.length; nLetter++) {
oLetter = aLetters[nLetter];
var nDist = nMaxDist - nMaxDist * nIndex / nLength;
oLetter.pos += 0.08;
oLetter.elem.style.top = Math.sin(oLetter.pos) * nDist + "px";
oLetter.elem.style.left = Math.cos(oLetter.pos) * nDist * 5 + "px";
}
}
function prepare () {
// build letters list
// http://tyleregeto.com/text-animation-in-javascript
this.textContent = "";
aLetters.length = 0;
for (var oSpan, oLetter, nLetter = 0, nLen = sText.length; nLetter < nLen; nLetter++) {
oSpan = document.createElement("span");
oSpan.textContent = sText[nLetter];
oLetter = { "elem": oSpan, "parent": this };
aLetters.push(oLetter);
oLetter.pos = Math.random() * 50;
oLetter.elem.style.position = "relative";
this.appendChild(oSpan);
}
}
var
nMaxDist = 25, aLetters = [], sText = "Do you feel lucky, punk?",
oRecompose = new Daemon(document.createElement("p"), perform, 33, 30, prepare);
onload = function () {
oRecompose.owner.id = "perform-me";
document.body.appendChild(oRecompose.owner);
oRecompose.play();
};
</script>
<style type="text/css">
body {
font-family: monospace, sans-serif;
background: #DDDDDD;
overflow: hidden;
}
#perform-me {
margin: 50px;
font-size: 20px;
line-height: 20px;
}
</style>
</head>
<body>
<h1>new Daemon(<em>@thisObject</em>, <em>@callback</em>[, <em>@rate</em>, <em>@length</em>, <em>@init</em>, <em>@onstart</em>])</h1>
<p><button onclick="oRecompose.skipTo(11);">skipTo(11)</button>
<button onclick="oRecompose.makeSteps(29);">makeSteps(29)</button>
<button onclick="oRecompose.fixPosition(-13);">fixPosition(-13)</button>
<button onclick="oRecompose.play();">play</button>
<button onclick="oRecompose.turn();">turn</button>
<button onclick="oRecompose.pause();">pause</button>
<button onclick="oRecompose.reversePlay();">reversePlay</button>
<button onclick="oRecompose.reversals = 2;alert('changed');">two reversals</button>
<button onclick="oRecompose.makeLoop();alert('changed');">makeLoop</button>
<button onclick="oRecompose.unmakeLoop();alert('changed');">unmakeLoop</button>
<button onclick="oRecompose.close();">close</button>
<button onclick="oRecompose.reclose();">reclose</button><br />
frame rate: <input type="text" id="vello" value="33" style="width: 40px;" onkeypress="return event.charCode===0||/\d/.test(String.fromCharCode(event.charCode));" onkeyup="if(isFinite(this.value)&&Number(this.value)>0){oRecompose.setRate(this.value);}" /></p>
</body>
</html>
Example #2: A practical instantiation – Daemon.buildAround()
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Daemon.buildAround(…)</title>
<script type="text/javascript" src="daemon.js"></script>
<script type="text/javascript" src="daemon-methods.js"></script>
<script type="text/javascript">
/*\
|*|
|*| :: Daemon.buildAround(@context[, @rate, @length]) ::
|*|
|*| Returns a new daemon built around a @context object. The @context object must contain *at least*
|*| a "perform" property pointing to what you want to be the @callback function of the daemon.
|*| It can also optionally contain "create" and "prepare" properties pointing respectively to the
|*| two functions you want to be the @init and @onstart functions of Daemon constructor's arguments.
|*| The @context object will be also the *this* object of your callback function. So you can populate
|*| it with any custom properties and methods. The only required one property is "perform".
|*|
|*| Sample usage:
|*|
|*| var myDaemon = Daemon.buildAround({
|*| "customProperty": [custom value],
|*| "myCustomMethod": function () { [custom code] },
|*| "anotherCustomProperty": [custom value],
|*| "etc.": "etc."
|*| "create": function () { [custom code] }, // optional
|*| "prepare": function () { [custom code] }, // optional
|*| "perform": function () { [custom code] }, // required
|*| }, 200, 30);
|*|
\*/
var sText = "Do you feel lucky, punk?", oUnhide = Daemon.buildAround({
// http://tyleregeto.com/text-animation-in-javascript
"letters": [],
"numletters": 0,
"clock": 0,
"interval": 0.0,
"delta": 33,
"letters": [],
"pool": ["0","1","2","3","4","5","6","7","8","9"],
"target": document.createElement("p"),
"create": function () {
// build letters list
this.target.textContent = "";
this.letters.length = 0;
for (var oSpan, oLetter, nLetter = 0, nLen = sText.length; nLetter < nLen; nLetter++) {
oSpan = document.createElement("span");
oSpan.textContent = sText[nLetter];
oLetter = { "elem": oSpan, "parent": this.target };
this.letters.push(oLetter);
oLetter.index = this.numletters;
oLetter.elem.style.position = "relative";
oLetter.val = oLetter.elem.textContent;
this.numletters++;
this.target.appendChild(oSpan);
}
},
"perform": function (nIndex, nLength, bBackw) {
for (var oLetter, nLetter = 0; nLetter < this.letters.length; nLetter++) {
oLetter = this.letters[nLetter];
if (nLength < nIndex && this.clock + this.delta < this.interval) {
clock += this.delta;
return;
}
this.clock = 0;
oLetter.elem.textContent = nIndex / nLength - oLetter.index / this.numletters >= 0 ?
oLetter.val
: this.pool[parseInt(Math.random() * this.pool.length)];
}
}
}, 33, 30);
onload = function () {
oUnhide.owner.target.id = "animate-me";
document.body.appendChild(oUnhide.owner.target);
oUnhide.play();
};
</script>
<style type="text/css">
body {
font-family: monospace, sans-serif;
background: #DDDDDD;
overflow: hidden;
}
#animate-me {
margin: 50px;
font-size: 20px;
line-height: 20px;
}
</style>
</head>
<body>
<h1>Daemon.buildAround()</h1>
<p><button onclick="oUnhide.skipTo(11);">skipTo(11)</button>
<button onclick="oUnhide.makeSteps(29);">makeSteps(29)</button>
<button onclick="oUnhide.fixPosition(-13);">fixPosition(-13)</button>
<button onclick="oUnhide.play();">play</button>
<button onclick="oUnhide.turn();">turn</button>
<button onclick="oUnhide.pause();">pause</button>
<button onclick="oUnhide.reversePlay();">reversePlay</button>
<button onclick="oUnhide.reversals = 2;alert('changed');">two reversals</button>
<button onclick="oUnhide.makeLoop();alert('changed');">makeLoop</button>
<button onclick="oUnhide.unmakeLoop();alert('changed');">unmakeLoop</button>
<button onclick="oUnhide.close();">close</button>
<button onclick="oUnhide.reclose();">reclose</button><br />
frame rate: <input type="text" id="vello" value="33" style="width: 40px;" onkeypress="return event.charCode===0||/\d/.test(String.fromCharCode(event.charCode));" onkeyup="if(isFinite(this.value)&&Number(this.value)>0){oUnhide.setRate(this.value);}" /></p>
</body>
</html>
Example #3: A safe (without setInterval) instantiation – new Daemon.safe()
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>new Daemon.safe(…)</title>
<script type="text/javascript" src="daemon.js"></script>
<script type="text/javascript" src="daemon-safe.js"></script>
<script type="text/javascript" src="daemon-methods.js"></script>
<script type="text/javascript">
function perform (nIndex, nLength, bBackw) {
// http://tyleregeto.com/text-animation-in-javascript
for (var oLetter, nLetter = 0; nLetter < aLetters.length; nLetter++) {
oLetter = aLetters[nLetter];
var nDist = nMaxDist - nMaxDist * nIndex / nLength;
oLetter.pos += 0.08;
oLetter.elem.style.top = Math.sin(oLetter.pos) * nDist + "px";
oLetter.elem.style.left = Math.cos(oLetter.pos) * nDist * 5 + "px";
}
}
function prepare () {
// build letters list
// http://tyleregeto.com/text-animation-in-javascript
this.textContent = "";
aLetters.length = 0;
for (var oSpan, oLetter, nLetter = 0, nLen = sText.length; nLetter < nLen; nLetter++) {
oSpan = document.createElement("span");
oSpan.textContent = sText[nLetter];
oLetter = { "elem": oSpan, "parent": this };
aLetters.push(oLetter);
oLetter.pos = Math.random() * 50;
oLetter.elem.style.position = "relative";
this.appendChild(oSpan);
}
}
var
nMaxDist = 25, aLetters = [], sText = "Do you feel lucky, punk?",
oRecompose = new Daemon.safe(document.createElement("p"), perform, 33, 30, prepare);
onload = function () {
oRecompose.owner.id = "perform-me";
document.body.appendChild(oRecompose.owner);
oRecompose.play();
};
</script>
<style type="text/css">
body {
font-family: monospace, sans-serif;
background: #DDDDDD;
overflow: hidden;
}
#perform-me {
margin: 50px;
font-size: 20px;
line-height: 20px;
}
</style>
</head>
<body>
<h1>new Daemon.safe(<em>@thisObject</em>, <em>@callback</em>[, <em>@rate</em>, <em>@length</em>, <em>@init</em>, <em>@onstart</em>])</h1>
<p><button onclick="oRecompose.skipTo(11);">skipTo(11)</button>
<button onclick="oRecompose.makeSteps(29);">makeSteps(29)</button>
<button onclick="oRecompose.fixPosition(-13);">fixPosition(-13)</button>
<button onclick="oRecompose.play();">play</button>
<button onclick="oRecompose.turn();">turn</button>
<button onclick="oRecompose.pause();">pause</button>
<button onclick="oRecompose.reversePlay();">reversePlay</button>
<button onclick="oRecompose.reversals = 2;alert('changed');">two reversals</button>
<button onclick="oRecompose.makeLoop();alert('changed');">makeLoop</button>
<button onclick="oRecompose.unmakeLoop();alert('changed');">unmakeLoop</button>
<button onclick="oRecompose.close();">close</button>
<button onclick="oRecompose.reclose();">reclose</button><br />
frame rate: <input type="text" id="vello" value="33" style="width: 40px;" onkeypress="return event.charCode===0||/\d/.test(String.fromCharCode(event.charCode));" onkeyup="if(isFinite(this.value)&&Number(this.value)>0){oRecompose.setRate(this.value);}" /></p>
</body>
</html>
Example #4: A practical and safe (without setInterval) instantiation – Daemon.safe.buildAround()
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Daemon.safe.buildAround(…)</title>
<script type="text/javascript" src="daemon.js"></script>
<script type="text/javascript" src="daemon-safe.js"></script>
<script type="text/javascript" src="daemon-methods.js"></script>
<script type="text/javascript">
/*\
|*|
|*| :: Daemon.safe.buildAround(@context[, @rate, @length]) ::
|*|
|*| Returns a new daemon built around a @context object. The @context object must contain *at least*
|*| a "perform" property pointing to what you want to be the @callback function of the daemon.
|*| It can also optionally contain "create" and "prepare" properties pointing respectively to the
|*| two functions you want to be the @init and @onstart functions of Daemon constructor's arguments.
|*| The @context object will be also the *this* object of your callback function. So you can populate
|*| it with any custom properties and methods. The only required one property is "perform".
|*|
|*| Sample usage:
|*|
|*| var myDaemon = Daemon.safe.buildAround({
|*| "customProperty": [custom value],
|*| "myCustomMethod": function () { [custom code] },
|*| "anotherCustomProperty": [custom value],
|*| "etc.": "etc."
|*| "create": function () { [custom code] }, // optional
|*| "prepare": function () { [custom code] }, // optional
|*| "perform": function () { [custom code] }, // required
|*| }, 200, 30);
|*|
\*/
var sText = "Do you feel lucky, punk?", oUnhide = Daemon.safe.buildAround({
// http://tyleregeto.com/text-animation-in-javascript
"letters": [],
"numletters": 0,
"clock": 0,
"interval": 0.0,
"delta": 33,
"letters": [],
"pool": ["0","1","2","3","4","5","6","7","8","9"],
"target": document.createElement("p"),
"create": function () {
// build letters list
this.target.textContent = "";
this.letters.length = 0;
for (var oSpan, oLetter, nLetter = 0, nLen = sText.length; nLetter < nLen; nLetter++) {
oSpan = document.createElement("span");
oSpan.textContent = sText[nLetter];
oLetter = { "elem": oSpan, "parent": this.target };
this.letters.push(oLetter);
oLetter.index = this.numletters;
oLetter.elem.style.position = "relative";
oLetter.val = oLetter.elem.textContent;
this.numletters++;
this.target.appendChild(oSpan);
}
},
"perform": function (nIndex, nLength, bBackw) {
for (var oLetter, nLetter = 0; nLetter < this.letters.length; nLetter++) {
oLetter = this.letters[nLetter];
if (nLength < nIndex && this.clock + this.delta < this.interval) {
clock += this.delta;
return;
}
this.clock = 0;
oLetter.elem.textContent = nIndex / nLength - oLetter.index / this.numletters >= 0 ?
oLetter.val
: this.pool[parseInt(Math.random() * this.pool.length)];
}
}
}, 33, 30);
onload = function () {
oUnhide.owner.target.id = "animate-me";
document.body.appendChild(oUnhide.owner.target);
oUnhide.play();
};
</script>
<style type="text/css">
body {
font-family: monospace, sans-serif;
background: #DDDDDD;
overflow: hidden;
}
#animate-me {
margin: 50px;
font-size: 20px;
line-height: 20px;
}
</style>
</head>
<body>
<h1>Daemon.safe.buildAround()</h1>
<p><button onclick="oUnhide.skipTo(11);">skipTo(11)</button>
<button onclick="oUnhide.makeSteps(29);">makeSteps(29)</button>
<button onclick="oUnhide.fixPosition(-13);">fixPosition(-13)</button>
<button onclick="oUnhide.play();">play</button>
<button onclick="oUnhide.turn();">turn</button>
<button onclick="oUnhide.pause();">pause</button>
<button onclick="oUnhide.reversePlay();">reversePlay</button>
<button onclick="oUnhide.reversals = 2;alert('changed');">two reversals</button>
<button onclick="oUnhide.makeLoop();alert('changed');">makeLoop</button>
<button onclick="oUnhide.unmakeLoop();alert('changed');">unmakeLoop</button>
<button onclick="oUnhide.close();">close</button>
<button onclick="oUnhide.reclose();">reclose</button><br />
frame rate: <input type="text" id="vello" value="33" style="width: 40px;" onkeypress="return event.charCode===0||/\d/.test(String.fromCharCode(event.charCode));" onkeyup="if(isFinite(this.value)&&Number(this.value)>0){oUnhide.setRate(this.value);}" /></p>
</body>
</html>