SCRIPTING:

Omaha Beach 75 days later D-Day
Frenchmen not far from Omaha beach 75 days after D-Day!
ANIMATION MANAGERS

Imitating an ULMA approach, we feature the ULMA animation manager (AM) scripts which let you assemble linked lists (in JavaScript!) for complex Dhtml animations with a minimal coding.
If your goal is not arranging a complex animation where more threads interweave, you don't need Animation Managers.

April 2002
{ @ }



WHAT THESE SCRIPTS ARE FOR
When and Why should you use this animation manager? What a linked list is?

The scripts I feature here do not necessarily rely on my ULMA approach, but since their logics are definitely similar, a knowledge of ULMA (Universal Layer Management Approach) can be quite helpful.
As long as your Dhtml animation is a nuisance (by which I could mean a simple movement of a layer or a relocation of a couple of layers which can be executed at the same time), you should not feel encouraged at all to use ULMA AM. But there comes a time when a dedicated scripter needs to perform complex animations, whereas the requisite to assess the complexity of the task could be summarized as follows:
  1. More layers are involved
  2. Probably, but not necessarily, all these layers have to undergo different type of actions
  3. Surely, you do need to discriminate when one layer, or even a sub-group of layers animations, have to be triggered: this means that you stumbled across this necessity: some animations must be triggered only and exclusively after some other animations have been completed.
Monet
If so, Animation Managers can be your lot.
The first concept you have to understand is that in order to set groups of animations and keep them separated by others that have to be triggered only after all the previous groups have been completed, I used linked lists.
It is certainly uncommon to see linked lists implemented in javaScript, and this is why I at times (with some humorous nuance, obviously) refer to my javaScript by ULMA as javascript++:linked lists and object managers like ULMA and a pointer policy like the one I implement throughout ULMA, have never been cumulated and exploited in javaScript so far. Useless to say they do unleash some power.

We could define, by and large, a linked list like a set of structured data, like pearls in a necklace, where data (pearls) are grouped by sets and where each set is arranged in such a fashion to have sort of a hook that links (the "lace". Actually it doesn't "link": it points, like a finger which says: that one) it to the next object we want to put in the line. In such way every object has a head and a tail: the first object's head links to nothing, for that's the beginning: but its tail links to the next object in the line. The second object's head links to the first object's tail; conversely, the second object tails links to the third object's head, and so on until we reach the last object whose tail (analogously to the first leading object's head) links to nothing, which closes the list.

Would you guess this approach seems perfect to arrange groups of animations such that only after one group has been completed we can proceed to the next one?



HOW TO USE THE SCRIPT
Codes and explanations.

As you may notice perusing the code, it's apparently long: it depends on the fact it is certainly an emulation of a class and therefore is sizeable as a class is; but a few lines are just been set in order to provide you with aliases when invoking a built in functions (we're soon to see what they are for); on the whole, several aliases (such as exe as an alias of execute so that whichever you use, the triggered method is always execute; or such as removeAction or remove, which are not of actual use but I wanted to provide you with them as well in case one day you may need them and you wonder if you have them: odds are you have them) has been introduced just to keep compatibility with ULMA logics where, for instance, the method named execute has 6 aliases given its importance. In other cases aliases have been introduced to grant you some compatibility with other languages like Java: you may notice a call to execute accepts as one of its alias a call to... main()!
When a method has no aliases, it's because it's quite unlikely you're going to use it: it's a method that the object, once activated upon a call to execute or one of its aliases, would use in the background without you having to meddle with it.
The whole code weight is: less than 6 kb, namely as fast as an arrow to be downloaded! Moreover it is less than 6 kb which have not to be viewed, therefore the browser is double fast in downloading them for its rendering engine has to calculate nothing in order to paint on screen canvas! Don't be intimidated.
 
If by chance you want to shrink it (actually only non experienced scripters consider 5 kb an issue) the only methods you can safely delete are: this.removeAction, this.findIndex, this.stopThread, this.stopAll, this.status.
Let me introduce soon the code, and after it I will explain to you how to initialize the objects and how to trigger your animations:

function actionManager(actionName){
this.actionName=actionName;
if(!window.ULMAAnimations){ ULMAAnimations=new Array(); };
this.thread=actionName;
if(!ULMAAnimations[this.thread]){ ULMAAnimations[this.thread]=new Array(); }
/* For the REAL values this of the FOLLOWING 6 properties, see this.parentAction */
ULMAAnimations[this.thread][this.actionName]=this;
this.collection=ULMAAnimations[this.thread];
this.lastInLine=this;
this.firstInLine=this;
this.head=0;
this.tail=0;
this.actions=new Array(0);
for(var a=1; a<actionManager.arguments.length; a++){
this.actions[++this.actions.length-1]=actionManager.arguments[a]; };
this.addAction=new Function(""," for(var t=0; t<this.addAction.arguments.length; t++){ this.actions[++this.actions.length-1] = this.addAction.arguments[t] } ");
this.add=this.addAction;
this.removeAction=new Function("","if( !this.removeAction.arguments.length ){ return }; var ousted=new Array(0); var newly=new Array(0); outer:for(var R=0; R<this.removeAction.arguments.length; R++){ for(var r=0; r<this.actions.length; r++){ if(isNaN( parseFloat(this.removeAction.arguments[R]) )){if(r == parseFloat( this.removeAction.arguments[R])){ ousted[++ousted.length-1]=this.actions[r]; continue }} else if(r == this.removeAction.arguments[R]){ ousted[++ousted.length-1]=this.actions[r]; continue }; newly[++newly.length-1]=this.actions[r] }; }; this.actions=newly; return ousted;");
this.remove=this.removeAction;
this.findIndex=new Function("arg","for(var f=0; f<this.actions.length; f++){ if(this.actions[f]==arg){ return f } }; return -1;");
this.find = this.f = this.findIndex;
this.transmitProperty=function(propNAME, value){
for(var t in ULMAAnimations[this.thread]){
if(value || (value+"")=="0"){
ULMAAnimations[this.thread][t][propNAME]=value;
}
else{/* no value passed: by default adds 1 */
if(typeof( ULMAAnimations[this.thread][t][propNAME] )=="number"){
ULMAAnimations[this.thread][t][propNAME]++;
} } } return ULMAAnimations[this.thread][t][propNAME]; };
this.transmit = this.transmitProperty;
this.parentAction=function(arg){
if( typeof(arg)=='string' ){ arg=eval(arg) };
this.head=arg; arg.tail=this;
/*change thread name, must belong now to parent thread: */
var redrawULMA=new Array();
for(var rw in ULMAAnimations){
if(rw==this.thread){continue};
redrawULMA[rw]=ULMAAnimations[rw];
};
ULMAAnimations=redrawULMA;
this.thread=this.head.thread;
ULMAAnimations[this.thread][this.actionName]=this;
this.collection=ULMAAnimations[this.thread];
/*changed!*/
var firstNum=0; var firstObj;
for(var s in ULMAAnimations[this.thread]){
if(!firstNum){++firstNum;
firstObj=ULMAAnimations[this.thread][s]
};
ULMAAnimations[this.thread][s].firstInLine=firstObj;
ULMAAnimations[this.thread][s].lastInLine=this;
}; };
this.parent = this.p = this.parentAction;
this.rounds=0;
this.level=this.rounds%2;
this.mustRepeat=0;
this.repeat=function(times){
if(!times){
return};
times=(!isNaN(parseFloat(times)))?
Math.round(parseFloat(times)):times;
if(typeof(times)=="number" && times<=1){return};
this.transmitProperty("mustRepeat", times);
var head=this.firstInLine;
var tail=this.lastInLine;
tail.tail=head;
head.head=tail; };
this.end=true;//external End condition recursively checked...
this.booleanEnd=true;//...to see if it matches with this.
this.conclusion=function(arg, booleanValue){
this.end=arg;
if(booleanValue){
this.booleanEnd= (typeof (booleanValue) == 'string')?
eval(booleanValue): (typeof (booleanValue) == 'function')?
booleanValue(): booleanValue;}; };
this.close= this.c = this.conclude = this.conclusion;
this.done=0;
this.nowOnThis=0
this.execute=function(){
this.nowOnThis=1;
if(!this.actions.length){this.next(); return false};
if(!this.mustRepeat && this.rounds!=0 && this == this.firstInLine){
this.transmitProperty("rounds",0);
}
for(var a=0; a< this.actions.length; a++){
if(typeof( this.actions[a])== 'string'){
eval(this.actions[a])
}
else if (typeof( this.actions[a] )== 'function'){ this.actions[a]() };
};
if(!this.timer){ runner=this.actionName+".checkStatus()";
this.timer=setInterval(runner, this.speed);
};
return true; };
this.exec = this.exe = this.ex = this.x = this.X = this.e = this.execute;
this.run=function(absolute){
if(!absolute){
for(var v in ULMAAnimations[this.thread]){
if( ULMAAnimations[this.thread][v].timer ){ return false }
}; }
else{
for(var i in ULMAAnimations){
for(var ii in ULMAAnimations[i]){
if(ULMAAnimations[i][ii].timer){ return false };
} } } return this.execute(); };
this.main=this.run;
this.checkStatus=function(){ //Action Manager MAIN ENGINE!
var isFinished=(typeof(this.end) == "string")?eval(this.end):
(typeof(this.end) == "function")? this.end(): this.end;
if( isFinished == this.booleanEnd ){ this.next(); } };
this.status=function(){
var isFinished=(typeof(this.end) == "string")?eval(this.end):
(typeof(this.end) == "function")? this.end(): this.end;
if( isFinished == this.booleanEnd ){ return true; } return false;};
this.stopIt=function(){//stop only this
if(this.timer){
clearInterval(this.timer);
this.timer=0;
};
this.nowOnThis=0;
this.done=1;
if(this == this.lastInLine){
this.transmitProperty("done",0);
if(this.mustRepeat){
this.transmitProperty("rounds");
var currentLevel=this.rounds%2;
this.transmitProperty("level", currentLevel);
if(this.rounds==this.mustRepeat){
this.transmitProperty("mustRepeat",0);
this.transmitProperty("rounds",0);
this.transmitProperty("level", 0);
//unlink tail
this.lastInLine.tail=null;
} }
else{
this.transmitProperty("rounds",0);
} }; };
this.stopThread=function(){
for(var s in ULMAAnimations[this.thread]){
if(ULMAAnimations[this.thread][s].timer){
ULMAAnimations[this.thread][s].stop();};
} };
this.stop = this.stopThread;
this.stopAll=function(){
for(var a in ULMAAnimations){
for(var s in ULMAAnimations[a]){
if(ULMAAnimations[a][s].timer){ULMAAnimations[a][s].stop()}
} } };
this.timer=0;
this.speed=250;
this.next=function(){
this.stopIt();
if(this.tail){ this.tail.execute(); }; };
/* keep this comment to reuse freely
http://www.unitedscripters.com */}

The whole structure assumes that in the very same way you initialized by ULMA your layer managers by the formula (whereas foo is our usual placeholder for whatever layer manager name you prefer) var foo=new layerManager("foo", "layerIdHere"), in a fairly identical way you first initialize some animation Manager as a global variable, say:
var animation1;
stressing, by the way, that your actionManagers must always be set as global variables (no initializations in within functions of variables meant to be used at a later time as actionManagers, or their scope would be limited to the function lifespan).
So, once set your variable, you initialize it as a new animation manager (see the ULMA file if you wonder what this use of the keyword new is, or just trust me and use it by route!):
animation1= new actionManager("animation1")
where, as you may notice, the only mandatory argument is (fairly alike layerManagers and managerMethods) the very same name of the variable passed in between apex.

You can now start populating your actionManager with a set of actions that would therefore belong to its pack, for useless to say that an object whose name is actionManager lives up to its name only as long as it includes a set of actions (functions, arguably) to manage (i.e. to execute): such actions could even be passed upon initialization of the new actionManager as subsequent arguments (whatever argument after the first one is assumed by the script as an action to be added to the pool of the given actionManager) or initialized one by one by using the built in method addAction that every actionManager has once initialized: instance:


animation1.addAction("alert('Hallo World')")

This would be perfectly valid for AM: it would mean that the first action (and you can add as many as you prefer; addAction accepts just add as its only alias) which belongs to this lot is a (silly) alert saying 'Hallo World': keep in mind these rules of thumb:
  • You can pass (add) actions passing them as arguments. These arguments can be:
    1. Strings (in between apex): if so, like in the example case, remember that possible subsequent strings that have to be nested, must follow the rule of javaScript (and of whatever programming language actually): if the whole statement is surrounded by double apex, nested strings must be surrounded by single apex, or vice-versa.
    2. Such Strings will be evaluated upon execution (we'll see soon how to trigger the execution which launches the animation) of the actionManager
    3. You can pass a function without brackets: in such case the script will detect it's not a String data type and would execute it as a function.
      This is just an alternative, passing actions like strings is safer. Anyway just remember you can even pass as functions; so if you have a function like:
      function silly(){alert("Hallo World")}
      you can do:
      animation1.addAction(silly)
      where you may notice no apex are present and the function is passed without the round brackets (which would have executed its contents immediately instead of passing them as a reference to be stored for future use upon triggering of the whole animation).
  • Anyway, the best and safer way to use AM is to add their actions as names of functions (defined somewhere in your script) in between apex and with brackets: say you have a function like:
    function sillyOne(){
    alert("Hallo World")}

    you can add it as an action to an AM as:
    animation1.addAction("sillyOne()")
  • Note that passing arguments to added actions may be a problem, if such arguments are of the string type for added actions are themselves passed as strings: therefore as long as the arguments you are to pass to the given function you are about to add are either objects or numbers or variable names, no problem: you can safely pass them inside the stringed version of your function passed as the newly added action; but if your argument is a string on its own, the procedure might cause a problem: workaround: gather your functions, populated with string arguments, in another function that gets no arguments, and pass this latest one.
  • Not finished: addAction even accepts that you pass more actions as more consecutive arguments separated by commas, therefore you do have the greatest flexibility: you can pass actions as arguments upon initialization of the new actionManager object, or as independent calls to addAction or as one or a few calls to addAction passing to each of them more than just one action...!
  • An alternative way is to pass actions upon invoking the constructor: whatever argument after the first one (which must always be the name of the action manager itself), will be considered the name of an action to add and consequently initialized:
    var animation1= new actionManager("animation1", "action1Method()", "action2Method", "etc()")
  • Yes, you can even remove passed actions at a later time, if for some reasons you might need so: I included this feature to be consistent with the whole approach. To remove an action you use the built in method removeAction (unique alias: remove) and you pass to it as an argument either the index of the method (method passed as first carries index zero, as second carries index 1, as second carries index 3...), or the very same string/function you originally added it with, and the removeAction method will work out all the rest and remove the action from the set of actions that have to be triggered upon execution of the whole.
    removeAction accepts, alike addAction, that you pass more than one argument.
    Mere examples:
    animation1.removeAction(silly)
    animation1.removeAction(5)
    animation1.removeAction("alert('Hallo World')"))
    animation1.removeAction(silly, 4)
    //two arguments
  • If by chance you need to find the index position of an action, you can use findIndex (accepts two aliases: find and just f) and pass to it the object: it returns the index (rank position) it has in the list of actions that belong to this actionManager:
    var foofoo= animation1.findIndex(silly)
    //or:
    var foofoo= animation1.findIndex("alert('Hallo World')"))

    foofoo will now carry the index of the object, or would carry -1 if no such object was found.
    findIndex accepts only one argument at a time.
  • Another interesting feature is transmitProperty (one alias: transmit): it gets two arguments: property (the name of the property you want to change, in between quotes) and value: if you call this method on whatever action Manager, it will set and propagate the new assigned value for the given property to the whole thread of action managers which the action Manager upon which it has been invoked from belongs to.
    animation1.transmitProperty("rounds", 3)
    Should be useless to stress that this method performs in the expected way only if you've already concatenated the thread of action managers you want to be considered as a whole animation [you do this by parentAction() - see further on].
  • For the method parentAction there is a short table of its own further on in this page.
  • For the method conclusion there is a short table of its own further on in this page.
  • Two properties that can be useful are:
    animation1.lastInLine
    animation1.firstInLine

    they return, from the action manager they're called on, the action manager objects which are, respectively, the first one and the last one in the line this action manager partakes: so if from one action manager you want to know/communicate the name of the first or last in the line:
    animation1.lastInLine.actionName
  • Each thread of action managers, once concatenated (see further on) belongs to a global associative array named ULMAAnimations, and more specifically to the ULMAAnimations[threadName] pack, whereas the thread name is the name of the first initialized action manager for the given thread. In within such thread, each entry is indexed by the name of the action manager: so:
    var anim1=new actionManager("anim1")
    var anim2=new actionManager("anim2")
    var anim3=new actionManager("anim3")
    well, on such set, if it is concatenated by parentAction (see further on) ULMAAnimations is as follows:
    ULMAAnimations["anim1"]["anim1"]=actionManager object;
    ULMAAnimations["anim1"]["anim2"]=actionManager object;
    ULMAAnimations["anim1"]["anim3"]=actionManager object;

    If you then initialize, in a simple example, a new thread:
    newAnim1=new actionManager("newAnim1")
    newAnim2=new actionManager("newAnim2")

    well now ULMAnimation looks as follows:
    ULMAAnimations["anim1"]["anim1"]=actionManager object;
    ULMAAnimations["anim1"]["anim2"]=actionManager object;
    ULMAAnimations["anim1"]["anim3"]=actionManager object;
     
    ULMAAnimations["newAnim1"]["newAnim1"]=actionManager object;
    ULMAAnimations["newAnim1"]["newAnim2"]=actionManager object;
  • You can also set looping animations by using:
    animation1.repeat()
    Whatever argument higher than 1 you pass would make the thread start from the beginning as soon as it is finished until it has been cycled through as many times as the argument prescribes.
    If the argument is a String and carries literals, the thread will be... infinite! (that is: goes on until the user doesn't leave the page):
    animation1.repeat(7);
    animation1.repeat("x"); //passed letter: INFINITE loop!
    animation1.repeat(0); //no argument, or argument=0: does nothing, the animation if triggered runs once anyway.

    On repeat see more further on.
  • Another property you have to be aware of is:
    animation1.level
    it is tailored to return either zero or a number: each time a thread is run inside a repetitive approach, the level property, automatically propagated to all the actionManager of the thread thus yielding the same value no matter what actionManager on the thread it is read from, is set to zero if the current repeated round is an even number, and returns a positive number if it is an odd number: in other words, it flags when a repetition is going to start again...!
You can go on adding as many actions as you prefer. Once you have defined your first lot of actions (animation1 as we called its placeholder name), this would mean they will be a pool that has to be executed all at the same time (no matter whether it includes just one action or 20...). But if you're using AM, you are doing so for you want another set of actions being triggered, but only after the previous one has been completed: fine, you initialize the subsequent set of actions as another separated actionManager (are you grabbing the logics?):
var animation2= new actionManager("animation2")
animation2.addAction("alert('Hallo World 2')", "mySillyFunction()")

And no, do not tell me it's difficult! its only requirement is that you have a previous plan for a whole animation and some general knowledge of javaScript, in absence of which why should you be planning to meddle with complex animations? Correct?
If you know how to manage a form or how to loop an array, this is the knowledge level you need on the whole: moreover, an ULMA approach used in combination with AM, would help you use Dhtml without deep knowledge of Dhtml quirks and browsers oddities and without shaping a function for each layer but functions that once defined, can be reused and borrowed on each layerManager.


THE LAST STEPS
Now concatenate, condition, and trigger your animations!

You have two things left: first you have to concatenate your animations.
Then you have to set the conditions for fading from one to the other.
Eventually, you have to trigger it all.
 
Let's start from the easiest: the last point; once your animations have been initialized and populated with actions (as done before) and concatenated (as we're to do soon) to trigger them you just grab the first (leading) actionManager which is at the top (beginning) of the chain and you say:
EXECUTING
animation1.execute()
It will all start: nothing else to do. Execute accepts as aliases: exec, exe, ex, x, X, e
 
Note the particular behaviour of two pseudo-aliases of execute: run and main (this latter one is actually an alias of run).
Whereas execute triggers the action regardless of whatever adjunctive consideration, a call to run such as:
animation1.run()
would check that the animation can be executed before executing it.
In order to appraise whether the animation can be triggered, run behaves as follows: each animation Manager, as soon as it's triggered, switches on (by default) a timeout which recursively checks (by default each 250 milliseconds, or a quarter of a second) whether the condition you set to proceed with each subsequent actionManager in the thread has been met: if it has been met, the timeout gets cleared (command is: clearInterval(this.timer); this.timer=0;) and the system proceeds to triggering a timeout for the next actionManager linked in the line (if any).
Therefore, run() can verify the presence of ongoing animations by looping all the animation Managers and therefore forestalling adjunctive executions if it finds a timeout which has not been cleared yet (an action Manager which is still running, that is at the end of the road and in plainer words!). If it finds one, run() will prevent the re-triggering of the thread until it has not finished being thoroughly performing.
Such check verifies this condition in within the boundaries the linked list (the thread) this actionManager instance belongs to; on the contrary, if you do want to be sure that before running a new animation not only the specific thread this action Manager belongs to has finished, but that every possible thread of action Managers in your document have been exhausted all you have to do is to call run() passing number 1 as its only argument:
animation1.run(1)
You don't even have to bother on how run does it: let's be sufficient to know that it does it.
Why this? Well, this could be of the greatest avail when the triggering of the animation is performed by an user action/interaction such as clicking a button or selecting an option from a menu, such say a button whose onClick event handler carries a command as:
... onClick="animationX.run()"
or
... onClick="animationX.run(1)".
In such way run will guarantee that if the user selects or clicks another object which would trigger another animation chain by another call to some run, the latest command will be ignored until the running one has not been completed: in such fashion you're 100% positive that animations won't overlap!
Makes sense, doesn't it?

Now let's see how to concatenate: if you want animation1 to be concatenated with animation2 such that animation2 would follow animation1, you start your concatenation after you have initialized animation2 and to do this you just say:
 
CONCATENATING
animation2.parentAction("animation1")
You're done: you use parentAction (which accepts two aliases: p and parent) starting from the second actionManager object in the animation line, and you pass to it as an argument in between apex the name of the previous actionManager in the line. The script will work out all the rest, concatenating heads and tails.
 
It may be useful to stress again that you start building the chain only from the second animation Manager object. So if you have, say, 4 animation managers (say anim1, anim2, anim3, anim4) and they have been already initialized as new actionManagers and populated with their actions:
anim2.parentAction("anim1")
anim3.parentAction("anim2")
anim4.parentAction("anim3")
You see: the first anim (anim1) does not use parentAction for it is the starting one.

 
CONCLUDING a set
How to conclude an animation and instruct the whole "necklace" of linked animations to switch on and tackle the next one in the line?
 
The default behaviour is that as soon as you have called execute or run on the first animation by animation1.execute() or by animation1.run(), it will execute all the actions that have been added to animation1 and as soon as it triggers the execution of the last one, it switches on to execute animations2, and so on until it finds no more animations in the line.
 
You may argue this is precisely what you want (and in fact this is the default behaviour), but you're arguing wrong, to some degree.
In fact there are many cases where you pass (for instance) to animation1 some actions which involve a timeout like, say, a function that gradually moves a layer from position A to position B, and you may want to tackle the set of animation2 actionManager only after such position B has been attained.
In other words, you may want to wait for a task being completed.
Well, the script can't guess what your desire is, so you have to instruct it that you mean to pass from animation1 to animation2 only after the object has reached the position B. To accomplish this you have to condition the fading from one actionManager to the next in the chain. Here's how:

let's imagine your function that moves the layer is called moveIt: you probably have passed it to animation1 like a statement as:
animation1.addAction(moveIt)
or:
animation1.addAction("moveIt()") with quotes and brackets
To let animation1 know that it must switch to animation2 only after the moveIt function has finished moving the object and such layer object has attained position B, you need two things:

  1. A snippet function that just returns true if the object is there (has attained the condition you consider conclusive for this animation set) or false otherwise, such as in pseudo code:
    function check1(){//pseudo function
    if(object.top==50){return true}
    else{return false}
    }
  2. Then you set this function as the conclusive condition for animation1:
    animation1.conclusion("check1()", "true")
    Or, since true is the default:
    animation1.conclusion("check1()")
    (whereas conclusion accepts three aliases: close, conclude, c)
The conclusion second argument defaults to true (therefore in such case can be omitted), but if you want to pass it as false, remember to pass it as a string: "false". Failing doing so in such case can be the main source of mistakes (that is, animations that don't check right).
[note: It might even accept a function being passed as a second parameter -in between quotes: "afunc()"-, provided the outcome of such function is a returned boolean -true/false, that is- to be grabbed and set as the conclusion boolean].
 
So, on the whole: you may notice how conclusion works:
First argument can be:
  1. a String: in such case the code is evaluated and executed: if in between the strings you pass a function, include the brackets: "check1()"
    This is by far the preferred case and the next option has been added just in case.
  2. an object: in such case the object is left unaffected or if it is a function it will trigger the function.
Second argument is a string carrying either true or false, or nothing (defaults to true). Yes, actually nothing prevents you from passing to it some value (the value possibly returned by check1() then, when such value is the one that you chose to flag to the actionManagers that they may tackle the next action Manager in their line). You'd be safer if you stick to booleans though, methinks.
 
If you are still wondering why the conclusion method is necessary and the addAction method is not enough to make the linked list wait until a lot has been finished before tackling the next in the line, well, here is reprinted the reply I sent on a newsgroup after such question:
 

 
CREATING REPETITIVE LOOPS
It would be over, but imagine one case: you want to create a cycling action manager set.
You can achieve it by calling in:
anActionManager.repeat(2)
whereas values lower than 2 do nothing (each animation lasts at least ONE cycle!), and if you pass a string of letters (anim1.repeat("hi")) it triggers an infinite loop.
No matter which animation manager you call repeat() from: it will work out by itself it has to concatenate the first manager of the thread with the last of the thread (a cool feature); what matters is just that:
  1. You call the method from an action Managers that actually belongs to the thread (linked list) you want to turn into a cycle.
  2. You call it only after you have linked/concatenated all the managers of the given thread.
You have two remarkable setbacks here, for you must be aware what you're trying to accomplish is ambitious and no relevant achievement can be attained without overcoming some difficulties.
Follow me:
 
setback 1:
You start moving a layer from say [100,500], and until it has not reached position, say, [10,50] by a timeout, you don't want to launch the second animation. Therefore you set a conclusion method for the first animation that checks whether the layer position is [10,50] and if it is such, it gives green light to the second ring in the linked list (which say triggers an alert). So far so good.
 
Now imagine you made it a cycle: the second time it cycles, the condition of the first manager is already [10,50] and therefore is already met, and therefore the first action manager in the linked thread list would immediately fire the alert of the second manager in the line without the expected delay. Correct, yes? Not my fault.
 
Well, obviously to overcome this, you just have to add an action to the last action manager in the line that restores all the conditions checked by all the previous conclusion methods to their originals (please note that this is what you'd have to do anyway in most cases: if you make a cycle, when you start again the conditions should be reset).
Otherwise if you want to start from that positions [10,50] in order to go back to [100,500], you may have to craft a branching in all your added methods, so that if their
animationManagerName.level==0
something happens and is returned, otherwise if
animationManagerName.level!=0
something else happens and is returned.
In other words you may check the level property (previously shortly discussed) for the given action Manager you're adding actions to.

function AMCycleCheckExample(){
//for some animX: animX.conclusion("AMCycleCheckExample()")
if(animX.level==0){
  if(layer.top==10){return true}
  else{return false}
}
else if(animX.level!=0){
  if(layer.top==100){return true}
  else{return false} }
}

In the worst cases (which tantamount to cycles which cannot be satisfied with just a simpler set of two linked action Managers but with a whole lot of them), you may have to make such a branching for each conclusion of each actionManager instance. Not really dire, if you consider that to make a conclusion check even for non looping animations, you'd have anyway to craft a tiny snippet such as:

function AMCheckExample(){
  if(layer.top==10){return true}
  else{return false}
}

If you don't want to do this, you have anyway to reset, in our example, the layer top to 100, which can be conveniently made by adding an action to the LAST actionManager which resets it. All depends on what you want to achieve, whether a bouncing cycle, or a cycle that repeats itself resetting all the variables to the original upon re-starting.
 
setback 2:
Arguably, you have to consider specifying a similar (maybe branched, that is) conclusion method also to the last action manager in the line, say

function anotherCheck(){
if(stuff Maybe_animX.level)...return true;
else if(stuff) etc...
};
LASTAction.conclusion("anotherCheck()")

 
The last actionManager in the line would have not needed a concluding definition; in fact would it have not been a looping cycle, would have had no need to set a conclusion function for it had no further actions to tackle in the line: but since it is looping, it now has such next action Manager in the line even after the last one: in fact the next one of the last one is... the first!
 
An exercise for you:
Use actionManagers on this DHTML bouncing cycle scripting problem (envision a solution, that is):
  1. actionManagers.level==0
    A layer1 moves from [top 100, left 0] to [top 100, left 400] : func1()
  2. actionManagers.level==0
    when layer1.left==400, only then starts moving a second layer : conclus1() (hint: it is actionManager1 conclusion)
  3. actionManagers.level==0
    layer2 moves from [top 100, left 700] to [top 100, left 500] ] : func2()
  4. actionManagers.level==0
    Only when layer 2 has finished (layer2.left==500) you can tackle steps below : conclus2() (hint: it is actionManager2 conclusion):
  5. now: cycle!
  6. actionManagers.level!=0
    Now layer1 moves from [100,400] back to [100,0] ] : func3()
  7. actionManagers.level!=0
    when layer1.left==0, only then layer2 moves: rearrange conclusion as BRANCHED conclus1()
  8. actionManagers.level!=0
    layer2 moves from [100,500] to [100,700] ] : func4()
  9. actionManagers.level!=0
    when it has finished (layer2.left==500 again), repeat from #1: rearrange conclusion as BRANCHED conclus2()
  10. Big hint: func1/func3 and func2/func4 must be included into a branching Function each and as well! and these latest functions must be passed as addAction() !
    function branch1(){
    if(anim1.level==0){func1()}
    else{func3()}
    }
    anim1.addAction("branch1()"); //now anim2, make branch2 function....

    Morale: rearrange for BRANCHED versions both conclusions and added actions for each action Manager!
  11. One more cycle? Remember: now actionManagers.level==0 again...! And, arguably, nothing else to scribble, for you already prgrammed for the first bouncing, so all the subsequent ones just have to follow the previously defined pattern; actually, your script's finished! You therefore just employed:
    • Moving layer scripts you'd have to define anyway.
    • Include (paste!) the actionManager and just initialize some action managers and concatenate them.
    • Write a few additional short (branched?) snippets to nest your original scripts and pass these latest snippets as added actions and conclusions. Done.

Victoria Silvstedt
An uncommented example
[ example linked list of 3 Action Managers ]
var animation1;
var animation2;
var animation3;

animation1=new actionManager("animation1")
animation1.addAction("somefunc1()")
animation1.addAction("somefunc2()")
animation1.conclusion("myOwnCheck1()", "true")
//no parentAction, it's the first
animation2=new actionManager("animation2")
animation2.addAction("somefunc3()", "somefunc4()")
animation2.addAction("somefunc5()")
animation2.conclusion("myOwnCheck2()", "true")
animation2.parentAction("animation1");
/*all in between first and last: both parentAction & conclusion*/
animation3=new actionManager("animation3")
animation3.addAction("somefunc6()")
animation3.addAction("somefunc7()")
animation3.parentAction("animation2")
//needs no conclusion, actually: it's the last anyway...!
animation1.run(); //go!
Suggested Procedure
  1. Initialize all the variable meant to hold action Managers:
    var anim1;
    var anim2;
    var animLAST
    //etc...
  2. Initialize the action methods for each variable
    function foo1(){/*...stuff...*/}
    function foo2(){/*...stuff...*/}
  3. Initialize the relative action Manager
    anim1= new actionManager("anim1") //etc...
  4. Add the actions after initialized the current action Manager:
    anim1.addAction("foo1", "foo2")
  5. Define the conclusion function for the current action Manager:
    function conc1(){if(/*stuff*/){return false}else{return true}}
  6. Add it as the conclusion for the given manager:
    anim1.conclusion("conc1()")
  7. Create the parent relationships among all the managers (you may use AMLinkedList featured at bottom to save typing and avoid mistakes):
    AMLinkedList("anim1", "anim2") //or anim2.parentAction("anim1") etc...
  8. OPTIONAL: Now if you want to repeat, you can set it at this stage:
    anim2.repeat(3)
  9. OPTIONAL: if you repeat, be sure that:
    • Add to the last animation in the line an action that resets all the variable checked for conclusion at point 5 in order to reset the variables they checked to return false or true; if necessary define first such function:
      function resetAll(){stuff}
    • then add it to the last animation manager:
      animLAST.addAction("resetAll()")
    • You can optionally and eventually craft a function for the conclusion of the animLAST, and set it as its conclusion(), or otherwise the switching on to the animFIRST would be instantaneous.
  10. To run:
    anim1.run()

If you want a function that would produce the linked list chain for a given amount of animation Managers so that you save time (and possible mistakes by typing), the following snippet can suit your case: you pass to it as arguments the names of your animation Managers (assumes they are all variable names already properly initialized as new animation Managers) either as string (the name of the variables in between quotes, that is) or directly: so two possible invocations to build a list for initialized actionManagers such anim1, anim2, anim3, anim4:
  1. AMLinkedList(anim1, anim2, anim3, anim4)
  2. AMLinkedList("anim1", "anim2", "anim3", "anim4")
  3. Remember: you can build up your chain using this function only after (after) you've defined all the implied objects as actionManagers.
  4. The order you pass the arguments, must be the same you want your actions being checked/concatenated (that is, if anim2 goes after anim1, you can't pass [anim2,anim1] but: [anim1,anim2], for the parenthood relationships are built up depending on the order you pass such arguments).

function AMLinkedList(){
//requires actionManager
if(!AMLinkedList.arguments.length || AMLinkedList.arguments.length<=1){ return }
var thread = (typeof(linkedList.arguments[0])=="string")?
linkedList.arguments[0]:linkedList.arguments[0].actionName;
for(var i=1; i<linkedList.arguments.length; i++){
var previousIs=(typeof( linkedList.arguments[i-1] ) == "string")?
linkedList.arguments[i-1]: linkedList.arguments[i-1].actionName;
if(typeof( linkedList.arguments[i] ) == "string"){
ULMAAnimations[ linkedList.arguments[i] ][ linkedList.arguments[i] ].parentAction(previousIs);
}
else{
linkedList.arguments[i].parentAction(previousIs);
}}}

 
UMAPower Function
All right, we can do better; and this small chunk of code is so nifty if you take some time to inspect it, that you may love it.
This is the UMA (Universal Management Approaches) Power function: it can initialize all the type of objects meant to be UMA (so far either ULMA or AM) objects.
With this function as long as actionManagers are concerned you don't have anymore either to initialize or to link variables: the function will do it all on its own. Just be sure, obviously, that in your script is pasted the actionManager code somewhere prior to the call to UMAPower.
You use UMAPower for Action Managers as follows: first argument number 1:
UMAPower(1)
Now, spearated by commas and each name in between quotes, you include the variable names you want to employ as your actionManager instances: and note, you do not even need to initialize (and neither declared!) such variables: the function will initialize them from scratch!
UMAPower(1, "anAnimName1", "anAnimName2", "anAnimName3")
You can add as many varable names you prefer. After UMAPower has been called as such, you can manipulate all your objects by (example of command):
aAnimName2.addAction("blah blah")
They would be all exisiting and active variables, and since the first argument was number one, the've been all initialized as actionManagers and all linked as actionManagers, provided first passed argument is number 1.
 
Using this function, you skip steps 1,2,3,7 from the so called suggested procedure.
Using this function you avoid lots of possible typos and lots of coding.
Using this function, you do not need at all AMLinkedList (which I keep in place to meet whatever possible need might rise - that is, you have reasons to prefer initializing your variables by yourself, but you'd be glad to get help producing the linkeages).

function UMAPower(){
//Universal Manager Approach initiator
/* if used for layerManager (ULMA), pass as arguments the layers IDs */
if(!UMAPower.arguments.length){ return };
var p=0; var constructor="layerManager";
if( !isNaN( parseFloat(UMAPower.arguments[0]) ) ){
p=1;
constructor= parseFloat(UMAPower.arguments[0]);
};
switch(constructor){
case "layerManager":
 constructor=constructor;
break;
case 1:
 constructor="actionManager";
break;
}
for(var P=p; P< UMAPower.arguments.length; P++){
 var instance=UMAPower.arguments[P]
 if(typeof(instance)=="object"){/*Array?*/
  for(var C=0; C<instance.length; C++){
  window[instance[C]]=new window[constructor](instance[C]);
   if(constructor=="layerManager"){window[instance[C]].abilitate()}
   else if (constructor=="actionManager" && C>0){
   window[instance[C]].parentAction(instance[C-1]);
   }
  }
 }
 else if(typeof(instance)=="string"){
  window[instance]=new window[constructor](instance);
   if(constructor=="layerManager"){window[instance].abilitate()}
   else if (constructor=="actionManager" && P>1){
   window[instance].parentAction(UMAPower.arguments[P-1]);
   }
 }
 else{return false}
};
return true
/* keep this comment to reuse freely
http://www.unitedscripters.com */}


Top of Page