/*
 * Author: Trinithis
 *
 * Last Updated: 12-21-2007
 *
 * Detailed explanation at: http://www.dynamicdrive.com/forums/showthread.php?t=27061
 */

function TimeoutChainer(args) {
	args = Object.combine(args, TimeoutChainer.defaultArgs);
	if(args.callback.constructor == String)
		args.callback = args.context
			? args.context[args.callback]
			: window[args.callback];
	this.context = args.context;
	if(args.delay < 0)
		args.delay = args.interval;
	this.delay = args.delay || 1;
	this.interval = args.interval;
	this.numTimes = args.numTimes
	this.completed = 0;
	this.remaining = args.numTimes;
	this.callback = args.callback;
	this.args = args.args;
	for(var i = this.args.length; i >= 0; --i)
		if(this.args[i] == TimeoutChainer.SELF)
			this.args[i] = this;
	this.currTimeout = null;
	this.runOnComplete = [];
	if(args.numTimes == 0)
		return;
	if(args.runAfter)
		args.runAfter.runOnComplete.push(this);
	else
		this.chainTimeout();
}

TimeoutChainer.SELF = {};

TimeoutChainer.prototype = {
	timeoutFunc: function() {
		--this.remaining;
		++this.completed;
		this.callback.apply(this.context, this.args);
		this.delay = false;
		if(this.remaining > 0)
			this.chainTimeout();
		else
			this.complete();
	},
	chainTimeout: function() {
		this.currTimeout = setTimeout(
			Function.bundle(this, this.timeoutFunc), 
			this.delay? this.delay: this.interval
		);
	},
	complete: function() {
		for(var a = this.runOnComplete, i = a.length - 1; i >= 0; --i)
			a[i].chainTimeout();
		this.runOnComplete = [];
	},
	clear: function() {
		window.clearTimeout(this.currTimeout);
		this.remaining = 0;
		this.complete();
	}
};

TimeoutChainer.defaultArgs = {
	context: null,
	interval: 0,
	delay: -1, //translates to this.interval
	numTimes: 1,
	callback: null,
	args: [],
	runAfter: null
};

Object.combine = function() {
	for(var r = {}, i = arguments.length - 1, x; i >= 0; --i)
		for(x in arguments[i])
			if(arguments[i].hasOwnProperty(x))
				r[x] = arguments[i][x];
	return r;
};

Function.bundle = function(context, f, args) {
	if(f.constructor == String && context)
		f = context[f];
	if(arguments.length < 3)
		args = [];
	else if(!(args instanceof Array))
		args = Array.prototype.slice.call(arguments, 2);
	return function() {
		return f.apply(context, args);
	};
};

