/**
 * deferedJsLoader - load js after onload
 * 
 * (c) 2010 Jan Y. Brueckner <jyb@gmx.com>
 *
 * integration is done by:
 * <head>
 *                                            // v--- load 1st!! 
 *  <script type="text/javascript" src="path_to/deferedJsLoader.js"></script>
 *  <script  type="text/javascript">   
 *      derferLoader.deferLoadOf('path_to/script.js')  
 *      derferLoader.deferLoadOf('path_to/script2.js','functionname(args)')
 *      .
 *      .
 *  </script>
 * </head>                  // v------ call loader
 * <body onload="deferLoader.onBoot();">
 * ...
 *  
 * 'functionname' specifies a name of an function to call after downloading was
 * triggerd. The function can be contained within one of the js to load. If
 * more than one script is loaded with a functionname given, the function that
 * becomes available in the first place is executed first (read on). 
 *
 * The loader works as follows: after being called on onload event, the loader
 * triggers download of the scripts specified. Since downloading may occur in 
 * parallel and it may need some time until the sourced functions become ready,
 * the downloader triggers a background process. This backgroud process watches
 * wheter functionnames specified become available (every 300ms) and executes
 * them. The process stops when all functions were executable. The process 
 * stops with an error message after 5 minutes if there are functions remaining
 * that were not executable. 
 * 
 */
function deferedJsLoader() {
	this.scripts = []; // scripts to load
	this.evals = new Array();   // function names to evaluate
	this.myinterval = null;     // pointer to Interval for evaluation

	/** 
	 * register a function to call after loading of the scripts by name 
	 * @param {String} func - function name
	 */
	this.registerOnLoad = function(func) {
		var k = this.evals.length;
		this.evals[k] = func;
	};

	/**
	 * register a script to load defered
	 * @param {String} jscript - path to script
	 * @param {String} func - function name
	 */
	this.deferLoadOf = function(jscript, func) {
		var k = this.scripts.length;
		this.scripts[k] = jscript;
		if ( func !== undefined ) { this.registerOnLoad(func); }
	};

	/**
	 * function called in backgroud, tests whether a function is available
	 * and executes it 
	 */
	this.runEvals = function() {
		// iterate on this.evals to eval() registered function names
		while (this.evals.length > 0) {
			if (typeof window[this.evals[0].replace(/\(.*$/,"")] != 'function') {
				// abort
				break;	
			} else {
				eval(this.evals.shift() + ";");
			}
		}
		// clear Interval
		if (this.evals.length === 0) { window.clearInterval(this.myInterval); }
	};

	/**
	 * function called after a certain time to clean up 
	 */
	this.clearInterval = function() {
		if (this.evals.length !== 0) {
			window.clearInterval(this.myInterval);
			throw new Error("Could not call function \"" + 
					this.evals[0] + "\" \n page may not work");
		}
	};

	/**
	 * bootstrap function
	 */
	this.onBoot = function() {
		// download defered js
		var headID = document.getElementsByTagName("head")[0];         
		for (i=0; i<this.scripts.length; i++) {
			var newScript = document.createElement('script');
			newScript.async = true;
			newScript.type = 'text/javascript';
			newScript.src = this.scripts[i];
			headID.appendChild(newScript);
		}
		// call runEvals every 300ms
		this.myinterval = window.setInterval('deferLoader.runEvals()', 300);
		// cancel Interval after 5 minutes for security reasons
		window.setTimeout('deferLoader.clearInterval()',300000);
	};


}	
// set up a default loader
var deferLoader = new deferedJsLoader();


