|
|
|
|
|
|
|
|
|
THE CSS ARPEGGIOS
|
Without tampering with the window object namely without initializing too many functions in its space name thanks to an ULMA like approach, it is featured a function that can recursively apply user defined actions on a set of layers (or even objects, given its flexibility) after you have fully customized the layers to affect, the properties, the values to assign, and the amount of repetitions ("arpeggios") to span through.
|
|
January 2004 |
|
{ @ }
|
|
|
|
|
PURPOSE OF THE FUNCTION
|
|
Purpose of the arpeggios function and of its two ancillary functions
|
Though this function is named after CSS (namely Cascading Style Sheets), it can be lent to other usages not strictly implied with style sheets; the fact the name css entered in the title is that the most obvious usage of this function is when used to grasp layers and change their styles recursively; yet as you will see the function is not inherently limited to this, actually.
Such function, named arpeggios, comes in with two additional functions (all these three functions appear together in the codex section):
These two functions are, basically, an emulation of ULMA and a transposition of it outside of its originally strictly layer oriented nature; yet they are much shorter than ULMA (about one tenth), and you do not need to know what ULMA is or how it works in order to use these "versions".
The reason I added these two functions is this: since the arpeggios function relies on javascript timeouts in order to recursively apply a set of modifications (arpeggios), such timeouts could no longer retrieve their arguments after the first run (a well known issue with timeouts), and the only way to make them retrievable would have been to assign such arguments as globally defined variables attached to the window object. But this would have meant taking chances: a name could have overwritten a window object which already got previously assigned the same name.
In order to avoid such potential name conflicts that could have ensued (the arpeggios needs many arguments, so would have needed assigning many variables to the window object), I devised this ULMA-like encapsulation, so that as you'll see you can build arpeggios without having to populate the window object with too many new variables.
The arpeggios function works as follows: it takes in a set of layer names, and will run for a specified number of rounds.
At each round, the arpeggios will scan a specified amount of layers. If at the next round the specified amount would overstep the amount of available layers when scanning them, it is smart enough to assume it has to restart from the beginning of the list of layers until the currently undertaken amount is exhausted.
It is important that you grasp this difference between rounds and amounts: rounds specifies how many times the function will run (each time by a setTimeout), whereas the amounts specifies how many layers are inspected and therefore affected within the current round.
A Joan Miro painting: Blue 3, (1961)
|
Basically, the rounds are meant to establish how many times an action must be repeated (like painting a layer, for instance), the amounts are meant to specify on how many layers (or other objects, this function is flexible because, as you'll see, it "just" repeatedly applies a second custom function invented by yourself) are concerned with the same action performed in the round: instance, should you paint at this round with this action one layer alone, or an x amount of layers listed in the array argument named ids?
Pay attention: as just said above you then/also have to craft one custom function (or more, for that matter: but let's imagine just one, which is by far the most common case and the case after which the function arpeggios has been conceived): at each round the arpeggios will apply such custom function to each layer currently affected by the amount scan in the round. In case of multiple functions, at each round will apply a subsequent one in the array of the passed custom functions.
When this (these) function(s) is passed to affect such layers, three arguments are assigned to it, drawing such arguments from the arpeggios function arguments itself: the affected ids, the properties to affect onto them, and the values to assign to such property(ies).
These arguments can also be instructed to be arrays: you pass, for instance, the properties argument to the arpeggios and it has to be an array, then this very same argument which is an array is, entry by entry, passed to the custom function: now, if each entry was an array itself, your custom function can be tailored/meant to disaggregate the argument. It's your decision depending on what you want to do.
Thus, in order to summarize: at each round happens what follows:
- A round starts:
- An amount set of layers is inspected in the list of those passed in the ids argument (you pass say 10 layers in the ids argument, and the amounts argument is say 2? each round affects two layers then), starting each round from the ids entry after the last ids visited/affected entry.
If the end of any argument array is reached and yet there are amounts left, they are exhausted restarting from the beginning of the passed layers. Instance: 6 rounds, 10 ids, and amounts is 2:
ROUND 1 : handle ids1, ids2 (for amounts is 2)
ROUND 2 : handle ids3, ids4
ROUND 3 : handle ids5, ids6
ROUND 4 : handle ids7, ids8
ROUND 5 : handle ids9, ids10 (ids over, restart:)
ROUND 6 : handle ids1, ids2 (end of rounds, exit timeouts and arpeggios)
You can then truly arrange a variety of different behaviours.
- Is invoked your custom function(s) upon each ids (arguably layer ids)
- To this custom function, upon invoking it from within the arpeggios function, are passed 3 arguments:
- A layer ID, taken as one entry by one, from the ids array argument passed to the arpeggios.
- A property, taken as one entry by one, drawn from the argument in arpeggios named properties, which must be an array.
- A value, taken as one entry by one, drawn from the argument in arpeggios named values, which must be an array.
- Thus, depending on what your custom function will do with such arguments, the layer will be handled, and the properties and values will be manipulated accordingly to your custom function inner proceedings.
- A new round starts (if any left)
As you may intuitively guess, in a custom function the ideal settings are that each layer will find, in the custom function, the properties set as equal the passed values as your custom function says.
The arpeggios function thus will go on "harping" and "combing" the layers and applying repeatedly the custom function thus painting and repainting them or affecting them with the action imposed by your custom function, until the end of the established rounds is eventually attained.
ARGUMENTS AND INVOCATION
|
|
What the arguments are, how to pass them, and how to run the arpeggios
|
 |
 |
 |
 a Joan Miro painting: The Red Sun, (1948) |
 |
 |
 |
ARGUMENTS OVERVIEW |
IDS
The model above is Angel Cassidy
 ANGEL CASSIDY ONLINE
|
Array. Ideally, these should be strings each of them representing the ID of a layer. This is anyway the setting after which the arpeggios have been envisioned. Yet, since what the arpeggios really do is to apply your custom function and pass to it as arguments the currently inspected IDS and other two arguments (properties and values), they could be whatever elements your custom function is properly tailored to manage.
If they are layer names, remember to put them in between quotes.
If your ids are name of layers, do not build up the arpeggios function or call it before such layers have been loaded in the browser's memory: that is, preferably, to be really safe, run the arpeggios by statements put only after the html statements declaring all the passed ids.
Last but not least, I assume that you know that a set of square brackets represents a syntax for an Array in javascript (and java, actually).
So, example:
["id1", "id2", "id3"]
If your custom function would then do something like:
function myFunction(id){
document.getElementById(id).style.color='red';
}
I insist: be sure that a layer with such id exists, and that it is already loaded into the browser's memory.
|
SPEEDS |
Array.
Any speed, as usual in javascript, is expressed in milliseconds. Thus, for instance: 1000 means one second.
Though most likely you won't want to modify the speeds at each different round, yet the arpeggios can accept different speeds at each round they perform: this feature is reflected in the fact you can pass multiple speeds as an array. Of course, if your speed is only one, just pass an array of one entry, as for instance:
[100] Defaults to 250 milliseconds. |
FUNCTIONS
Above, Van Gogh: Two peasants on a snow covered field, 1890
|
Array. They should be your custom function(s), collected in one array.
Be sure such function(s) do exist, namely that you have crafted/ defined them somewhere in your page before you run the arpeggios. Calling an undefined function obviously enough triggers errors.
Each item of this argument array can be either an existing function - that is, the name of the function passed without quotes, such as:
myFunction
or it can be the name of an existing function, namely in between quotes as:
"myFunction"
In this last case the arpeggios will assume such function(s) exist as a dependance of the window object - whereas if you'd pass the bare word, you could pass it even as a reference to a full fledged object within which such function has been defined (by the way yes, javascript has pointers).
If you have defined only one function, just pass an array of only one function, such as:
[myFunction]
|
PROPERTIES
Above, Van Gogh: Corn field behind the hospital, 1889
|
Array. As described for all the previous arguments, each entry should be a string, yet it depends on what your custom function will do with the passed parameter.
Please note this: if you pass as each entry of the array another array holding more properties, you can surely instruct your custom function to disaggregate: that is, if your properties are passed for instance as:
[ ['prop1','prop2','prop3'], ['prop4','prop5','prop6'] ]
you have an array of 2 entries whose each entry is an array of 3 elements. At each amount iteration the arpeggios will pass to your custom function an entry of the outmost array too:
this.functions[this.currentFunction](
this.ids[this.currentId],
this.properties[this.currentProperty],
this.values[this.currentValue]
);
which in case of array of arrays passed as the properties argument, means having passed at one round the mere array:
['prop1','prop2','prop3']
and at the second round the mere array:
['prop4','prop5','prop6']
If then your custom function is instructed within its body to grasp such entries, it can manipulate them all at each amount iteration, instance:
function myFunction(ids, props, values){
var ob=document.getElementById(ids);
ob.style[props[0]]=values;
ob.style[props[1]]=values;
ob.style[props[2]]=values;
}
In such example the values are assumed as static, yet the very same trick just explained for the properties can be applied to the values argument of the arpeggios function, thus yielding perfectly matched props[x]=values[x] pairs!
|
VALUES |
Array. These are the values that should be matched with the properties.
Please do read the section above for these two arguments are basically symmetrical and what applies to one applies to the other too. Anyway, instance:
function myFunction(ids, props, values){
var ob=document.getElementById(ids);
ob.style[props[0]]= values[0];
ob.style[props[1]]= values[1];
ob.style[props[2]]= values[2];
} |
AMOUNTS |
Array. Actually you need only one amount (an array of one entry) but to add flexibility even the amounts can be passed as an array, and will change at each round, so that each round could potentially affect different amounts of layers. As you may guess, it all depends on how savvy you are in passing ids and the amounts and the rounds, to achieve a plurality of different arpeggios. Defaults to [1]. |
ROUNDS |
Number. This can not be an array. It obviously sets the rounds that must be performed. Defaults to 1.
If passed as an array by mistake, will grab the first entry of the array and use it - if such entry is a number (I devised this procedure to avoid such apparently possibly common mistake); otherwise will default still to 1.
For infinite rounds, pass it as -1 (a negative number, that is). |
JUMPS
Above, Van Gogh: Desperate Old Man, 1890 [ I find it a moving subject ]
|
Array. This is optional, and actually it could even be better not to set it at all: I added this feature anyway because it was not detrimental for the performance of the function. Defaults to [1].
When at each round you reiterate through the layers by the specified amounts you obviously loop them by subsequential increases of 1.
By setting the jump argument to an array of numbers you can override this obvious behaviour; for instance say you pass a jump argument of two entries as:
[1,3]
well, if so at each iteration will be applied a specific jump, so that for instance at round 1 the arpeggios could loop through the layers with an increase of 1, whereas at the second loop it would iterate through the layers with a sequential increase of 3, and then 1 again at the next round, and then three, and so on....
Actually, a savvy arrangement of the array of the passed ids could implement this jumping feature as well, because if at each round your ids are arranged in such a way to pass the ids with interval already not subsequential with the order of the actual layers, this would already tantamount to perform a jumping.
Jumps should be numbers lower than the scanned amounts. If higher, the jumps will reshape themselves by subsequent subtractions until they achieve the first valid index instance available to be grasped within the ids array. Truly unusual arpeggios can be triggered by passing jumps as arrays of more than just one value. |
GEN5BROWSERS |
Number. Wholly optional. If passed, checks whether
document.getElementById
is an existing document method for the browser currently reading the page, and if not does not trigger the arpeggios. Note that you could make such check also within your custom function. |
 |
 |
 |
Now, in order to actually use the arpeggios, you need to proceed as follows. Remember that there are two other functions named emptyShell and shellGuest whose purpose is to be called in so to avoid that all those arguments outlined above will be assigned to the window object, thus jeopardizing the window and the functionality of the timeouts both.
Now, since the procedure is similar to ULMA, I won't detail the whys and whereofs here: just trust me and follow me.
Firstly, initialize a variable with whatever name you prefer; let's say you choose as name foo:
var foo;
Now set:
foo=new emptyShell('foo');
Note that you have to pass one argument to this constructor, and such argument must be the very same name of your chosen variable name in between quotes.
Then write:
foo.enable('arpeggios');
You have assigned to foo the method named arpeggios.
Now to set the properties of this arpeggio, you have to do something rather unusual in javascript (though rather common in Java!): you have to assign the arguments by a built in method named setAttributes:
foo.arpeggios.setAttributes(
"ids", [array of ids],
"speeds", [array of speeds],
"functions", [array of functions],
"properties", [array of properties],
"values", [array of values],
"amounts", [array of amounts],
"rounds", Number,
"jumps", [Array of numbers],
"gen5browsers", 0
);
Note that in red are listed the name of the arguments themselves, and they
- Are in between quotes
- Are all lowercase
Be sure they are such also in your instance of invocation.
After each argument names is listed, as you see, its value (mostly arrays).
It is not strictly necessary they are passed in such order too, as long as the pairs argument/value are alternated matching with your intentions, and the names of the arguments match the ones outlined above and are lowercase and in between quotes. gen5browsers is entirely optional.
Now, to actually start your arpeggios, write:
foo.arpeggios.execute();
So, a global example: after you have included the 3 functions arpeggios, emptyShell, shellGuest, and created your custom function(s), you write:
var foo;
foo=new emptyShell('foo');
foo.enable('arpeggios');
foo.arpeggios.setAttributes(
"ids", [array of ids],
"speeds", [array of speeds],
"functions", [array of functions],
"properties", [array of properties],
"values", [array of values],
"amounts", [array of amounts],
"rounds", Number,
"jumps", [Array],
"gen5browsers", 0 /*last; no ending comma*/
);
foo.arpeggios.execute();
 |
 |
 |
CAUTIONS: |
Copying: |
It may be convenient to copy that code and just replace its values with your own, to enhance scribbling productivity.
If the name of your variable of choice is not foo, remember to change accordingly all the places where foo is quoted, including the emptyShell argument. A maybe more convenient, less clustered initialization draft to copy and populate with your own values is below (selects all onFocus):
For the arpeggios & emptyShell & shellGuest codes copy them from their own Codex Field. Above is only an initialization draft.
|
Nesting in a third function: |
If you are to nest those statements within a third function, remember that your foo variable initialized as a new emptyShell must be outside any function statements, that is it must exist as a globally retrievable object: you can therefore nest in a third function (to call it on events, for instance) only the statements starting with foo.enable("arpeggios") included.
Anyway my personal recommendation is to put the enable statement too out of any third function: when nesting in a third function, truly safe is only putting in such third function the setAttributes build up and the execute statement only, leaving out all the initialization phase of foo as a new emptyShell instance and its enabling of the arpeggios. |
Assessing whether an instance is still running: |
Note: to assess whether an arpeggio is still running and avoid overlappings, you can check whether, in case the variable name is foo:
foo.arpeggios.isRunning
is equal to 1.
isRunning is case sensitive, and is set to 1 as soon as the timeouts/rounds for this arpeggio start and is automatically reset to zero as soon as they are all performed and finished.
To interrupt an arpeggio you can use the method stop which is included in any shellGuest instance:
foo.arpeggios.stop();
If your "foo" variable has been initialized as a new emptyShell and the arpeggios function has been enabled for it, I suggest to you, before executing any other arpeggios by foo, to make always the above check and see whether isRunning is equal to 1; and if so stop the current arpeggios of "foo" (other variables running their own arpeggios are not concerned: in fact each initialized new emptyShell is independent of all the others) prior to executing any brand new fresh roundabout of arpeggios for the currently concerned "foo" arpeggio-enabled object (you are thus sure you'll avoid certain weird overlappings, and their consequent potential/possible javascript errors).
This is especially true if an arpeggio runs for a somewhat lengthy time and so the user might perform unpredictable actions in the meanwhile attempting to trigger it twice, thrice, four times while it's still running. |
Trying combinations: |
You should use the test form and try different type of arguments. This is why the presence of so extensive Test Forms at United Scripters, despite very minor simplifications in them, is so uniquely important a feature.
If you have understood the inner workings, you can arrange a variety of cool effects depending on the arguments. Also, do not forget that you can repeat the name of a layer more than once in the ids array in case it suits your intentions/calculations: that's legitimate and won't cause any errors.
|
 |
 |
 |
 |
 |
 |
A SIMPLE CASE: flashing fields |
We can imagine a very simple case [for complex ones, see the test form]: the flashing of one single layer or form field background and color style like when upon some action you want, say, to catch the attention of the user making one element flash a few times.
Of course, you may argue that in order to flash one single field you might craft one single very specific function for the given object meant to flash, and just arrange timeouts for it.
That can certainly be. The scripts featured here are for a so to say power use, yet they can be lent to whatever usage. The weight of the codes for the arpeggios plus emptyShell and shellGuest plus all the codes here below, amount to 7kb: faster than most jpgs or animated gifs your browser deftly downloads (and has to paint on the canvas too!) everyday by the scores and per page, and whose average weight is normally above 10kb each.
If you device your own function and window variables, you're probably going to code for about 2 or 3kb, which gives to you only a differential advantage of mere 4kb circa: truly, such small a difference is not an issue, no way.
The decision is entirely yours, yet the arpeggios is like having a variety of harping functions packed in one, and does not affect the window object compass, and is already tailored to solve the "issue" of making the lifespan of arguments passed to timeouts survive the first timeout.
Truly a lot of work is put in each of these files at UnitedScripters.
Draw your custom function as follows:
function flasher(id, props, values){
if(!id.style){return;};
id.style[props[0]]=values[0];
id.style[props[1]]=values[1];
}
That simple. Don't say drawing a custom function is difficult!
Now, initialize your foo variable:
var foo;
foo=new emptyShell("foo");
foo.enable("arpeggios");
foo.arpeggios.setAttributes(
"ids", [ document.forms[0].aFieldName ],
"speeds", [250],
"functions", [flasher],
"properties", [ ["backgroundColor", "color"], ["backgroundColor", "color"] ],
"values", [ ["#f2ffd3", "#0000cc"], ["#043c79", "#ccffff"] ],
"rounds", 8
)
foo.arpeggios.execute();
Note that in the setAttributes call I did not set the amounts of the ids to grab: this because we have one ids only, namely the only entry present in the ids passed array, and the amounts argument of ids to grab at each round, if not set, defaults to 1 already.
Test it on a field:
[ or: flash onFocus » ]
|
 |
 |
 |
THE ARPEGGIOS & EMPTYSHELL & SHELLGUEST CODEXES
|
|
All the three concerned function codes in one single textarea
|
THE TEST FORM
|
|
Test the functionalities yourself
|
THE TEST FORM |
 a Joan Miro painting: A Bird's Flight Under Moonlight, (1967) |
| td1 | td2 | td3 | td4 |
| td5 | td6 | td7 | td8 |
| td9 | td10 | td11 | td12 |
| td13 | td14 | td15 | td16 |
|
|
|
|
SET PROPERTIES:
|
|
|
A Van Gogh painting: half of an angel, 1889
|
THE COMPUTATIONAL ARPEGGIOS
|
|
A variation on the arpeggios, disengaged by timeouts
|
The arpeggios just featured, though not linked strictly to layers, are none the less linked to timeouts.
I provide here a, say, depleted version of the arpeggios which is not linked to timeouts any longer, and whose purpose is to make a mere computation thus returning only numerical indexes and affecting no layers.
Please note that this is truly a standalone function which requires nothing else but itself, namely does not resort to emptyShell and to shellGuest in the least.
You feed it with at most 4 arguments, passed exactly in the following order:
- ids: in this case still an array. Could even be an array of numbers from zero onward, if you want the output to be of arrays populated with subsequent numbers only.
- amounts
: an array. All that has been exposed for this argument in the non computational arpeggios' argument overview fully applies to the computationalArpeggios too.
- rounds
: a Number. All that has been exposed for this argument in the non computational arpeggios' argument overview fully applies to the computationalArpeggios too.
- jumps
: an array. All that has been exposed for this argument in the non computational arpeggios' argument overview fully applies to the computationalArpeggios too.
No external custom function is required, because this is a computational only version and needs not to affect external objects but only to return numerical results: in this case the output is an array of arrays: each entry of the backbone (mother, first) array is a rounds argument cycle, and being each such cycle returning a second array, this latter array is an array whose each entry is an argument ids entry as it was picked at each amounts argument cycle.
So, example:
var output=computationalArpeggios(['a','b','c','d','e'], [2], 5, [1]);
means that on a simple array like the one listed as first argument, are performed 5 rounds whose amounts (namely the ids entries picked at each round) is 2 (could have been 1, or 3...).
I specified also the last argument, named jumps, though as said it is not truly necessary.
The output is thus:
output[0]=[a,b]
output[1]=[c,d]
output[2]=[e,a]
output[3]=[b,c]
output[4]=[d,e]
So, for instance:
output[2][1]
is equal to 'a'.
|
|