function __llModule(moduleAlias,modulePath,depArray,depInstances)
{
	this.__moduleAlias = moduleAlias;
	this.__modulePath = modulePath;
	this.__depArray = depArray;
	this.__loaded = false;
	this.__loading = false;
	this.__ancestorsLoaded = 0;
	this.__ancestors = [];
	this.__descendants  = [];
	this.__evals = depInstances;
}

function ll(ifDebug)
{
	if(!Array.indexOf){
	    Array.prototype.indexOf = function(obj){
	        for(var i=0; i<this.length; i++){
	            if(this[i]==obj){
	                return i;
	            }
	        }
	        return -1;
	    }
	}

	this.__ifDebug = ifDebug ? true : false;
	this.__moduleCount = 0;
	this.__rootModules = [];
	this.__pendingModules = [];
	this.__loadedModules = [];
	this.__id = __loaderObjects.length;
	__loaderObjects.push(this);
	this.__modules = [];
	this.__events = {};
	this.__events.onModuleAdd = [];
	this.__events.onModuleLoading = [];
	this.__events.onModuleLoaded = [];
	this.__loadIntervalHandler = 0;
	this.__addingModules = false;
	this.__callback = null;
	this.loaded = false;
	this.addEventListener('onModuleAdd',null,this.__logger);
	this.addEventListener('onModuleLoading',null,this.__logger);
	this.addEventListener('onModuleLoaded',null,this.__logger);
}

__loaderObjects = new Array();

ll.prototype.__logger = function(msg)
{
	if (this.__ifDebug)
	{
		console.log("Type:"+msg.type+", Data:"+msg.data);
		console.log(msg.data);
	}

}

ll.prototype.__fireEvents = function(eventType,module,llEventListeners)
{
	for (var i=0; i<llEventListeners.length; i++)
	{
		var evHandler = llEventListeners[i];
		if ((evHandler.moduleAlias == null) || (evHandler.moduleAlias == module.__moduleAlias))
		{
			evHandler.callback.call(this,
			{
				'type': eventType,
				'data': module
			});
		}
	}
}

ll.prototype.__traceModuleDeps = function()
{
	for (var moduleAlias in this.__modules)
	{
		var module = this.__modules[moduleAlias];
		if (typeof module == 'object')
		{
			for (var i = 0; i < module.__depArray.length; i++)
			{
				var ancestorModule = this.__modules[module.__depArray[i]];
				module.__ancestors.push(ancestorModule);
				if (typeof ancestorModule != 'undefined')
				{
					ancestorModule.__descendants.push(module);
				}
			}
		}
	}
}



ll.prototype.addEventListener = function (eventType,moduleAlias,callback)
{
	switch(eventType)
	{
		case 'onModuleAdd':
			this.__events.onModuleAdd.push(
			{
				moduleAlias : moduleAlias,
				callback : callback
			}
			);
			break;
		case 'onModuleLoading':
			this.__events.onModuleLoading.push(
			{
				moduleAlias: moduleAlias,
				callback: callback
			}
			);
			break;
		case 'onModuleLoaded':
			this.__events.onModuleLoaded.push(
			{
				moduleAlias: moduleAlias,
				callback: callback
			}
			);
			break;
	}

}

ll.prototype.addModule = function (moduleAlias,modulePath,depArray,depEvals)
{
	var newModule = new __llModule(moduleAlias,modulePath,depArray,depEvals);

	this.__modules[moduleAlias] = newModule;

	if (depArray.length == 0)
	{
		this.__rootModules.push(newModule);
	}

	this.__moduleCount++;

	this.__fireEvents('onModuleAdd',newModule,this.__events.onModuleAdd);
}

ll.prototype.__tryLoadModules = function()
{
	var loader = this;
	if (!loader.__addingModules)
	{
		if (loader.__pendingModules.length > 0)
		{
			loader.__addingModules = true;
			for (var i = loader.__pendingModules.length - 1; i >= 0; i--)
			{
				var module = loader.__pendingModules[i];
				var ancestorsLoaded = true;
				for (var j = 0; j < module.__ancestors.length; j++)
				{
					if (!module.__ancestors[j].__loaded)
					{
						ancestorsLoaded = false;
						break;
					}
				}
				if (!ancestorsLoaded)
				{
					continue;
				}
				var depsReady = true;
				for (var j = 0; j < module.__evals.length; j++)
				{
					var evalValue = eval("typeof " + module.__evals[j] + '== "undefined"');
					if (this.__ifDebug)
					{
						console.log("cheking if typeof ("+module.__evals[j]+') is undefined? '+evalValue+' for module:'+module.__moduleAlias);
					}
					if (evalValue)
					{
						depsReady = false;
						break;
					}
				}
				if (depsReady)
				{
					loader.__pendingModules.splice(i, 1);
					module.__loading = false;
					module.__loaded = true;
					loader.__fireEvents('onModuleLoaded', module, loader.__events.onModuleLoaded);
					for (var j = 0; j < module.__descendants.length; j++)
					{
						loader.__load(module.__descendants[j]);
					}
				}
			}
			loader.__addingModules = false;
		}
		else if (!loader.loaded)
		{
			loader.loaded = true;
			loader.__callback();
			window.clearInterval(loader.__loadIntervalHandler);
		}
	}

}

ll.include = function(jsFile)
{
    var html_doc = document.getElementsByTagName('head').item(0);
    var js = document.createElement('script');
    js.setAttribute('language', 'javascript');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', jsFile);
    html_doc.appendChild(js);
}

ll.prototype.__load = function(module)
{
	if (!((module.__loaded) || (this.__pendingModules.indexOf(module)>=0)))
	{
		ll.include(module.__modulePath);
		this.__pendingModules.push(module)
		module.__loading = true;

		this.__fireEvents('onModuleLoading', module, this.__events.onModuleLoading);
	}
}

ll.prototype.load = function(callback)
{
 	this.__callback = callback;
	this.__traceModuleDeps();
	for (var i=0; i<this.__rootModules.length; i++)
	{
		this.__load(this.__rootModules[i]);
	}
	this.__loadIntervalHandler = window.setInterval("__loaderObjects["+this.__id+"].__tryLoadModules()",500);
}

var $ll = new ll(false);
