// Dependo.js
// recursive, order-preserving, duplicate-skipping JS lazy-loading
// Lawrence Wang (lawrence@levityisland.com)
//
//
// Requires jQuery.
// Use thusly: 
//
// 	1.	Dependo.require(['file1.js', 'file2.js'], callback);
//		This will fetch all specified files in order, plus any dependencies they specify with Dependo, before running the callback.
//
// 	2.	Look at Dependo.loaded if you want to see what's been loaded already.
//
//
//  FIXME: will break if you call require() a second time while the first is in progress


var Dependo = (function() {
	var deps = [], next = 0, funcs = {}, fetching = false, loaded = {}, pathPrefix = '/', currentLoad = null;

	// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
	var globalEval = function(src) {
	    if (window.execScript) {
	        window.execScript(src);
	        return;
	    }
	    var fn = function() {
	        window.eval.call(window, src);
	    };
	    fn();
	};
	
	var fetch = function() {
		if (next >= deps.length) {
			fetching = false;
			return;
		}
		var file = deps[next];
		next++;
		var startlen = deps.length;
		currentLoad = file;
		$.ajax({
			url: pathPrefix + file,
			cache: true,
			dataType: 'text',
			success: function(resp) {
				currentLoad = null;
				globalEval(resp);
				loaded[file] = true;
				// if this file has requested more dependencies, reassign callbacks to the newest one
				if (startlen < deps.length) {
					if (funcs[file]) {
						var filePos = $.inArray(file, deps);
						var lastDep = deps[filePos + deps.length - startlen];
						if (!funcs[lastDep])
							funcs[lastDep] = [];
						funcs[lastDep] = funcs[lastDep].concat(funcs[file])
						funcs[file] = undefined;
					}
				}
				if (funcs[file]) {
					for (var i=0; i<funcs[file].length; i++)
						funcs[file][i]();
				}
				fetch();
			}
		});
	};

	return {
		require: function(files, onReady) {
			if (currentLoad) {
				throw('Concurrent access: ' + files + ' while ' + currentLoad);
			}
			var lastDep = null;
			for (var i=files.length-1; i>=0; i--) {
				var file = files[i];
				if (!loaded[file]) {
					if (!lastDep)
						lastDep = file;
					deps.splice(next, 0, file);
				}
			}
			if (onReady) {
				if (lastDep) {
					funcs[lastDep] = [onReady];
				} else if (deps.length > next + 1) {
					funcs[deps[next]] = onReady;
				} else
					onReady();
			}
			if (!fetching && next < deps.length) {
				fetching = true;
				fetch();
			}
		},
		safeRequire: function(files, onReady) {
			if (currentLoad) {
				setTimeout(function() {Dependo.safeRequire(files, onReady);}, 500);
			} else {
				Dependo.require(files, onReady);
			}
		},
		trivialRequire: function(files, onReady) {
			var load = function(index) {
				if (index >= files.length) {
					onReady();
				} else {
					$.getScript(files[index], function() {
						load(index+1);
					});
				}
			}
		},
		setPathPrefix: function(prefix) {
			pathPrefix = prefix + (prefix.charAt(prefix.length-1) == '/'? '' : '/');
		}
	}
})();

