/*
 * Javascript Loader
 *   by Zach Ashman
 *  Air Line Pilots Association
 */
if (typeof window.jsLoader === "undefined") jsLoader = window.jsLoader = (function() {
	
	var scripts = {
		jQuery: {
			key: "jQuery",
			store: "jQuery",
			url: "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
			loadCheck: "jQuery"
		}
	};

	var jsStore = {};
	var meta = {};
	
	var log = function(text) {
		if (typeof ZCA !== "undefined" && typeof ZCA.Util !== "undefined" && typeof ZCA.Util.log === "function") {
			ZCA.Util.log(text, true, "jsLoader");
		}
	}
	
	var extend = function(extended, options) {
		if (typeof extended === "object" && typeof options === "object") {
			for (var i in options) {
				extended[i] = options[i];
			}
		}
		return extended;
	};

	var typeOf = function(value) {
		var s = typeof value;
		if (s === 'object') {
			if (value) {
				if (typeof value.length === 'number' && !(value.propertyIsEnumerable('length')) && typeof value.splice === 'function') {
					s = 'array';
				}
			} else {
				s = 'null';
			}
		}
		return s;
	};
	
	var saveGlobals = function(variables) {
		var saved = {};
		if (typeof variables === "string") {
			variables = [variables];
		}
		if (typeOf(variables) === "array") {
			for (var i = 0; i < variables.length; i++) {
				if (typeof variables[i] === "string") {
					saved[variables[i]] = window[variables[i]];
					//window[variables[i]] = undefined;	--No need to null out - will restore previous value
				}
			}
		}
		return saved;
	};
	
	var restoreGlobals = function(saved) {
		if (typeof saved !== "undefined") {
			for (var i in saved) {
				window[i] = saved[i];
			}
		}
	};
	
	var checkLoaded = function(variables) {
		if (typeof variables === "string") {
			variables = [variables];
		}
		var loadCheck = true;
		var segments, obj;
		for (var i = 0; i < variables.length; i++) {
			segments = variables[i].split(".");
			obj = window;
			for (var j = 0; j < segments.length; j++) {
				if (typeof obj[segments[j]] === "undefined") {
					loadCheck = false;
					break;
				}
				obj = obj[segments[j]];
			}
			if (!loadCheck) {
				break;
			}
		}
		return loadCheck;
	};
	
	var getStore = function(key) {
		if (typeof jsStore[key] !== "undefined") {
			var store = jsStore[key];
			var size = 0, i;
			for (i in store) {
				if (store.hasOwnProperty(i)) {
					size++;
				}
			}
			if (size === 1) {
				store = store[i];
			}
			return store;
		} else {
			return null;
		}
	};
	
	var completeLoad = function(options) {
		var appended = false;
		if (typeof options.append === "string") {
			if (typeof meta[options.append] === "object") {
				if (meta[options.append].loading === true) {
					log("(" + options.key + ") " + options.append + " Still loading - Added to chain stack");
					meta[options.append].chain.push(function() { completeLoad(options); });
					return;
				} else if (meta[options.append].inuse === true) {
					log("(" + options.key + ") " + options.append + " In use - Added to chain stack");
					meta[options.append].chain.push(function() { completeLoad(options); });
					return;
				} else if (typeof jsStore[options.append] === "object") {
					log("(" + options.key + ") Appending to " + options.append);
					meta[options.append].inuse = true;
					options.noConflict = options.noConflict || [];
					for (var i in jsStore[options.append]) {
						options.noConflict.push(i);
					}
					appended = true;
				} else {
					log("(" + options.key + ") Append object not stored: " + options.append);
				}
			} else {
				log("(" + options.key + ") Append object not found: " + options.append);
			}
		}
		var noConflict = saveGlobals(options.noConflict);
		if (appended) {
			restoreGlobals(jsStore[options.append]);
		}
		var head = document.getElementsByTagName("head")[0] || document.documentElement;
		var script = document.createElement("script");
		script.src = options.url;
		script.charSet = "utf-8";
		head.appendChild(script);
		
		var timeout = 500;
		log("(" + options.key + ") Timer Init");
		log("(" + options.key + ") Interval code: " + window.setInterval);
		var waits = 0;
		var check = window.setInterval(function() {
			waits++;
			if (checkLoaded(options.loadCheck)) {
				log("(" + options.key + ") Waits: " + waits);
				log("(" + options.key + ") Loaded");
				clearInterval(check);
				head.removeChild(script);
				if (typeof options.store === "string") {
					options.store = [options.store];
				}
				var store = {};
				if (typeOf(options.store) === "array") {
					for (var i = 0; i < options.store.length; i++) {
						if (typeof options.store[i] === "string") {
							store[options.store[i]] = window[options.store[i]];
						}
					}
				} else {
					store = true;
				}
				jsStore[options.key] = store;
				restoreGlobals(noConflict);
				
				var returnStore = options.key;
				if (appended)
				{
					meta[options.append].inuse = false;
					log("(" + options.key + ") " + meta[options.append].chain.length + " Chain functions for " + options.append);
					if (meta[options.append].chain.length > 0) {
						meta[options.append].chain.shift()();
					}
					if (options.returnAppend === true) {
						returnStore = options.append;
					}
				}
				
				meta[options.key].loading = false;
				log("(" + options.key + ") " + meta[options.key].chain.length + " Chain functions");
				if (meta[options.key].chain.length > 0) {
					meta[options.key].chain.shift()();
				}
				
				log("(" + options.key + ") " + meta[options.key].complete.length + " Complete functions");
				log("(" + options.key + ") All functions called");
				for (var i = 0; i < meta[options.key].complete.length; i++) {
					meta[options.key].complete[i].call(jsStore[returnStore], getStore(returnStore));
				}
				meta[options.key].complete = [];
			}
			timeout--;
			if (timeout === 0) {
				log("(" + options.key + ") Waits: " + waits);
				log("(" + options.key + ") Timeout");
				meta[options.key].loading = false;
				meta[options.key].complete = [];
				clearInterval(check);
				head.removeChild(script);
				restoreGlobals(noConflict);
				jsStore[options.key] = false;
			}
		}, 10);
		log("(" + options.key + ") Interval Id: " + check);
	};

	var fn = {
		scripts: scripts,
		load: function(options, extended) {
			log("Load called");
			if (typeof options === "string") {
				options = scripts[options];
			}
			
			if (typeof options === "undefined") {
				options = {};
			}
			
			if (typeof extended === "object") {
				options = extend(options, extended);
			}
			
			if (typeof options.key !== "string" || typeof options.url !== "string" || typeof options.loadCheck !== "string") {
				log("Aborting");
				return;
			}
			log("Loading: " + options.key);
			log("(" + options.key + ") Url: " + options.url);
			
			if (typeof meta[options.key] === "undefined") {
				log("(" + options.key + ") First Load");
				meta[options.key] = {};
				meta[options.key].loading = true;
				meta[options.key].inuse = false;
				meta[options.key].complete = [];
				meta[options.key].chain = [];
				if (typeof options.complete === "function") {
					meta[options.key].complete.push(options.complete);
				}
				if (typeof options.noConflict === "string") {
					options.noConflict = [options.noConflict];
				}
				completeLoad(options);
			} else {
				log("(" + options.key + ") Already Defined");
				if (typeof options.complete === "function") {
					if (meta[options.key].loading === false) {
						var returnStore = options.key;
						if (options.returnAppend === true) {
							returnStore = options.append;
						}
						log("(" + options.key + ") Loaded complete function called");
						options.complete.call(jsStore[returnStore], getStore(returnStore));
					}
					else {
						log("(" + options.key + ") Added to complete stack");
						meta[options.key].complete.push(options.complete);
					}
				}
			}
		},
		isLoaded: function(key) {
			return typeof meta[key] !== "undefined" && typeof meta[key].loading !== "undefined" && !meta[key].loading;
		},
		get: getStore
	};
	
	return fn;
})();