On an unrelated note, I notice you're ending the double quoted string to concatenate $i. You can interpolate variables directly into double quoted and heredoc strings. The PHP code can thus be made more readable:
PHP Code:
"...
timer('ff$i', " . rand(1,100) . ");
..."
Or even:
PHP Code:
$r = rand(1,100);
# or however the JS code is output
echo <<<EOS
...
timer('ff$i', $r);
...
EOS;
You can make the JS slightly more efficient by using setInterval rather than setTimeout and by using anonymous functions and closures rather than strings containing JS code.
Code:
// 'map' is used for added functionality: display countdown timer fields
Array.prototype.map = function(f) {
var arr = [];
for (var i=0; i<this.length; ++i) {
arr[i] = f(this[i]);
}
}
var running = [];
/* timer: Start counting down time. When done, render the stack (?) and (optionally) invoke an arbitrary function.
* Arguments:
* - data: HTML element or element ID. Contains countdown clock.
* - onDone (optional): function to invoke when countdown finishes.
*/
function timer(data, onDone) {
var countdownIval, time, done;
if (typeof data == 'string') {
data=document.getElementById(data);
}
/* decrTime: Decrement time.
* Returns 'true' if time is out, 'false' otherwise
*/
function decrTime() {
if (time[2]>0) time[2]--;
else {
time[2]=59;
if (time[1]>0) time[1]--;
else {
time[1]=59;
if (time[0]>0) time[0]--;
else {
return true;
}
}
}
return false;
}
/* countdown: Count down display 1 second. Self canceling when timer reaches 0,
* at which point optional done handler is invoked and stack is rendered.
* Returns 'true' if not yet done.
*/
function countdown() {
done=decrTime();
data.innerHTML=time.map(function(n) {if (n<10) return '0'+n; return n;}).join(':');
if (done) {
clearInterval(countdownIval);
running[data.id] = 0;
onDone();
renderStack();
return false;
}
return true;
}
if (data.innerHTML == null) {
if (console) {
console.log("countdown "+data.id+"'s content is unexpectedly null.");
}
} else {
time=data.innerHTML.split(":");
running[data.id] = 1;
if (countdown()) {
countdownIval = setInterval(countdown, 1000);
}
}
}
Or with simpler decrementing:
Code:
var running = [];
/* timer: Start counting down time. When done, render the stack (?) and (optionally) invoke an arbitrary function.
* Arguments:
* - data: HTML element or element ID. Contains countdown clock.
* - onDone (optional): function to invoke when countdown finishes.
*/
function timer(data, onDone) {
var countdownIval, time, done;
if (typeof data == 'string') {
data=document.getElementById(data);
}
/* s2hms: Convert seconds to hours, minutes, seconds.
* Arguments:
* - secs: integer representing seconds.
* Returns string formatted as HH:MM:SS.
*/
function s2hms(secs) {
return [secs/3600 , (secs / 60) % 60, secs % 60].map(function(n) {if (n<10) return '0'+n; return n;}).join(':');
}
/* countdown: Count down display 1 second. Self canceling when timer reaches 0,
* at which point optional done handler is invoked and stack is rendered.
* Returns 'true' if not yet done.
*/
function countdown() {
if (time > 0) {
--time;
}
data.innerHTML=s2hms(time);
if (time == 0) {
clearInterval(countdownIval);
running[data.id] = 0;
onDone();
renderStack();
return false;
}
return true;
}
if (data.innerHTML == null) {
if (console) {
console.log("countdown "+data.id+"'s content is unexpectedly null.");
}
} else {
time = data.innerHTML.split(":");
time = ((time[0] * 60) + time[1]) * 60 + time[2];
running[data.id] = 1;
if (countdown()) {
countdownIval = setInterval(countdown, 1000);
}
}
}
Both still need a little more error handling to cover cases where the 'data' element doesn't contain a properly formatted time string, and could potentially use an optional 'time' argument so the 'data' element doesn't need to hold a time string.