// Exports
var _package_;
var join;

// Avoid namespace pollution
(function () {

	// We will insert additional scripts into the <head> section of the HTML document.
	var head = document.getElementsByTagName('head')[0];

	// Load an external script into the document.
	function include(jsUrl, k) {
		// Create a tag to load the specified script.
		// It will have the form <script type="text/javascript" src="jsUrl">...</script>
		var scriptTag    = document.createElement('script');
		scriptTag.type   = 'text/javascript';
		scriptTag.src    = jsUrl;
		scriptTag.onload = k;

		// Add the script to the document, causing it to load and run asynchronously.
		head.appendChild(scriptTag);
	};

	// Keep track of the packages that have been loaded.
	var includedPackages = { };

	// Call k() after loading the named package from the given url.
	function requirePackage(name, url, k) {
		if (includedPackages[name] == true) {
		// The package has already been processed.
			if (k != null) {
				// Proceed immediately.
				k();
			}
		} else if (includedPackages[name]) {
		// The package is waiting on its dependencies.
			if (k != null) {
				// Ask the package to call us when it is ready.
				includedPackages[name].push(k);
			}
		} else {
		// The package has not been visited yet, so start processing it.
			includedPackages[name] = [];
			if (k != null) {
				// Ask the package to call us when it is ready.
				includedPackages[name].push(k);
			}
			// Load the script at the given url.
			// When it runs, if it contains the requested package, evaluation will continue.
			include(url);
		}
	}

	// Declare a new package and its dependencies.
	_package_ = function (name, deps, k) {
		var kprime = join(deps.length, function () {
		// Run this code when all our dependencies are loaded.

			// Evaluate the package contents.
			if (k != null) {
				k();
			}

			// Notify all packages which depend on us that we're done loading.
			var clients = includedPackages[name];
			includedPackages[name] = true;
			if (clients && clients != true) {
				for (var i = 0; i < clients.length; i++) {
					clients[i]();
				}
			}
		});

		// Begin loading dependencies.
		for (var i = 0; i < deps.length; i++) {
			// Expand syntactic shortcuts
			if (typeof(deps[i]) == 'string') {
				deps[i] = [deps[i]];
			}
			if (deps[i].length == 1) {
				deps[i].push(deps[i][0]+'.js');
			}

			// General case
			if (deps[i].length == 2) {
				requirePackage(deps[i][0], deps[i][1], kprime);
			}
		}
	};

	// Returns a continuation which, after it's called n times, invokes the continuation k.
	join = function (n, k) {
		if (n <= 0) {
			// Call the continuation k immediately
			// (i.e., after the continuation we returned is called zero times)
			k();
			return function () { }
		} else {
			// Call the continuation k after the continuation we return is called n times
			return function () {
				if (n > 0) {
					n--;
					if (n == 0) {
						k();
					}
				}
			}
		}
	};
})();
