/** 
 * Functions for dynamic loading of Javascript files.
 * @author Rebecca Younes
 * @date 08/15/2007
 * 
 * Dynamic script loading servers two purposes:
 * (1) Load a script that attaches event listeners to elements that are inserted into the DOM 
 * via an Ajax request. Elements can have event listeners attached to them only after they are
 * inserted into the DOM, so once they are inserted into the document another script can be loaded
 * to attach the event listeners. (There are alternate solutions to this problem, such as YUI's
 * Element Utility, which is still in beta, and YUI's delayed event listener attachment, which I
 * haven't gotten to work.)
 * (2) Keep Javascript logic self-contained, so each file loads the scripts it's dependent on. Then
 * html pages don't have to track the dependencies and be updated when these change.
 * 
 * Use loadScriptsOnce() to prevent a script from being loaded more than once, either through
 * multiple Ajax requests or circular dependencies. Use loadScripts() for improved performance
 * if that is not a possibility.
 * 
 * Both functions append scripts to the document, body since they may contain executing
 * code as well as function definitions. I think this will handle all cases, but if not 
 * a member of the opts object could be used to control where the script is inserted.
 */

ILR.namespace("util.scriptLoader");

ILR.util.scriptLoader = {
	
	/** 
	 * @description Find an item in an array. This is a repeat of Array.find() defined in 
	 * /codelib/js/ilr/prototype/Array.js. Repeat here to reduce the number of dependent scripts
	 * that must be loaded with html script tags. 
	 * @method arrayFind
	 * @param {Array} array The array to search
	 * @param {Object} item The item to search for in the array
	 * @return {Integer} the index of item in array, or 0 if not found
	 */
	arrayFind : function(array, item) {
	
		var arrayLen = array.length;
		for (var i = 0; i < arrayLen; i++) {
			if (item == array[i]) {
				return i;
			}
		}
		return 0;
	},
	
	/**
	 * @method loadScripts
	 * @param {String|Array} newScripts Script file or array of script files to be loaded
	 * @param {Object} opts (optional) Additional arguments, to be defined as needed.
	 * @return {void}
	 */
	loadScripts : function(newScripts, opts) {
	
		var i, 
			oScript;
	
		if (typeof newScripts == "string") {
			newScripts = [newScripts];
		}
		
		for (i = 0; i < newScripts.length; i++) {
			oScript = document.createElement("script");
			oScript.type = "text/javascript";
			oScript.src = newScripts[i];
			document.body.appendChild(oScript);
		}	
	},
	
	/**
	 * @method loadScriptsOnce
	 * @param {String|Array} newScripts Script file or array of script files to be loaded
	 * @param {Object} opts (optional) Additional arguments, to be defined as needed.
	 * @return {void}
	 */
	loadScriptsOnce : function(newScripts, opts) {
	
		var i, 
			oScript,
			// get all the script element in the document
			scripts = document.getElementsByTagName("script"),
			scriptSrc = [],
			pos;
	
		if (typeof newScripts == "string") {
			newScripts = [newScripts];
		}
		
		// Create an array of the script src attributes
		for (i = 0; i < scripts.length; i++) {
			scriptSrc.push(scripts[i].src);
		}
	
		for (i = 0; i < newScripts.length; i++) {
			oScript = document.createElement("script"); 
			// This gives us the fully qualified url to compare against, so it doesn't
			// matter whether a relative or absolute path was passed in. If we just do
			// if (!scriptSrc.find(arguments[i]) we can only look at the literal string,
			// so e.g. "test.js" will not be recognized as the same src value as
			// "http://www.ilr.cornell.edu/test/test.js"
			oScript.src = newScripts[i];
			// If this script is already loaded, unload it and then reload it.
			// This seems odd, but it's critical for the attachment of event listeners to 
			// new elements put into the DOM by an xhr request. The event listeners must be
			// reattached AFTER those elements have been inserted into the DOM, even if they have
			// the same id as elements previously in the DOM that have had those event listeners
			// atached to them.
			// Array.find returns the index of the found item.
			pos = this.arrayFind(scriptSrc, oScript.src);
			if (pos) {				
				scripts[pos].parentNode.removeChild(scripts[pos]);	
			}
			else {
				// IN CASE the same script is passed more than once in one function call...
				// Could happen if the arguments list is assembled dynamically.
				// NB we don't have to modify scripts (nor can we), because it's a (read-only)
				// NodeList rather than an Array. It gets updated automatically as script elements
				// are removed from and added to the DOM.
				scriptSrc.push(oScript.src);			
			}
			oScript.type = "text/javascript";
			document.body.appendChild(oScript);
		}	
	}

};

