/**
 * @overview This library is the core of Archetype Javascript Framework
 * @name ${project.name}
 * @author Temsa (Florian TRAVERSE) & Swiip (Matthieu LUX)
 * @version ${project.version}
 * @license
 * # Copyright (c) 2007-2008 Florian TRAVERSE and Matthieu LUX<br>
 * #<br>
 * # Permission is hereby granted, free of charge, to any person obtaining a copy<br>
 * # of this software and associated documentation files (the "Software"), to deal<br>
 * # in the Software without restriction, including without limitation the rights<br>
 * # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<br>
 * # copies of the Software, and to permit persons to whom the Software is<br>
 * # furnished to do so, subject to the following conditions:<br>
 * #<br>
 * # The above copyright notice and this permission notice shall be included in<br>
 * # all copies or substantial portions of the Software.<br>
 * #<br>
 * # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
 * # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
 * # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<br>
 * # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
 * # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<br>
 * # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN<br>
 * # THE SOFTWARE.<br>
 */

/**
 * @namespace
 */
var Archetype = {
    Version: '${project.version}',
    ConfigFile: "archetype.conf.js",

    Loggers:{},
	
    root: window,
		
    /**
     * List of parameters passed to the script when loaded
     */
    parameters:{},
	
    /**
     * Initialise the loaded module list
     */ 
    loadedModules: {},
    /**
     * Load the Framework.
     */
    load: function() {
        if(/webkit/i.test(navigator.userAgent)) {
            //TODO : remove the test on the useragent :/
            setTimeout(function() {
                if(document.readyState == "loaded" || document.readyState == "complete"){
                    Archetype._load();
                } else {
                    setTimeout(arguments.callee, 10);
                }
            }, 10);
        } else if((/mozilla/i.test(navigator.userAgent) && !/(compati)/.test(navigator.userAgent)) || (/opera/i.test(navigator.userAgent))) {
            //TODO : remove the test on the useragent :/
            document.addEventListener("DOMContentLoaded", Archetype._load, false);
        } else if(/*@cc_on!@*/false) {
            //This is used in IE browsers
            (function(){
                var t = document.createElement('doc:rdy');
                try {
                    t.doScroll('left');
                    Archetype._load();
                    t = null;
                } catch(e) {
                    setTimeout(arguments.callee, 0);
                }
            })();
        } else {
            //If we can't be ready before onload, then we just wait onload...
            window.onload = Archetype._load;
        }
    },
    /**
     * Load the Framework.
     * Set the path used to access Archetype in Archetype.path;
     * Should not be used by user.
     * @private
     */
    _load: function()
    {
        //find his own include to get relative Path start
        var scripts=document.getElementsByTagName("script");
		
		
		
        if(typeof(scripts)!=='undefined')
        {
            for(i=0;i < scripts.length;i++)
            {
                var s=scripts[i];
                // get all parameters passed to the script itself
                if (s.src && s.src.match(/archetype\.js(\?.*)?$/)) {
                    Archetype.scriptElement = s;
                    Archetype.path = s.src.replace(/archetype\.js(\?.*)?$/, '');
                    // store options from the parameters passed to the script
                    var jsUrlParams = s.src.match(/archetype\.js\??(.*)?$/);

                    if (jsUrlParams.length == 2 && $exist(jsUrlParams[1])) {
                        var opts = jsUrlParams[1].split("&");
                        for (var j = 0; j < opts.length; j++) {
                            var opt = opts[j].split("=");
                            Archetype.parameters[opt[0]] = opt[1];
                        }
                    }
                    break;
                }
            }
        }
        else
            throw ("This is an unsupported browser :(");
            
        //document.body.innerHTML += "_load2<br/>";
        // This is mostly a fix for IE<8 (or IE8 in IE7 emulation mode) : 
        // we check if we have a full or relative path
        // We need a full path if we want some functionnalities to work as intended
        if(! Archetype.path.match(/[^?]+\:\/\/.+/)) {
       		var bases = document.getElementsByTagName("base");
       		if(bases.length > 0) {
       			// handle special case of having a base tag in the header
       			Archetype.path = bases[0].href + Archetype.path;
       		} else {
				// find the last / without it being a
				Archetype.path = location.href.replace(/([^?]+\/)(.*?)$/,'$1') + Archetype.path;
       		}
        }
        
        //document.body.innerHTML += Archetype.path + "<br/>";
        //Initialise the configuration
        if(typeof(Archetype.libraryPath) == 'undefined') {
            Archetype.addJS(Archetype.path + Archetype.ConfigFile, Archetype._loadLaunchPage);
        } else {
            Archetype._loadLaunchPage();
        }

    },
    /**
     * Load the launch page.
     * @param {String} url
     * @param {Function} callback
     */
    _loadLaunchPage: function(/*String*/ url, /*Function*/ callback) {
        url = url || location.href;
        var regexp,result,pageName;

        if(typeof Archetype.pages.rootEquivalence == "undefined")
            Archetype.pages.rootEquivalence=Archetype.path;

        if(!$exist(Archetype.parameters.page)) {
            regexp = new RegExp("^.*" + Archetype.pages.rootEquivalence + "([a-zA-Z0-9%/]*)\.?.*$","");
            result = regexp.exec(url);

            if(!result || result.length < 2) {
                throw("Archetype.pages.rootEquivalence \"" + Archetype.pages.rootEquivalence + "\" has not been found in the URL " + url + ". Please correct the configuration.");
            }

            pageName=result[1];
            if(pageName === "") {
                pageName = Archetype.pages.defaultPage;
            }
        } else {
            pageName = Archetype.parameters.page
        }
        Archetype.pages.pageName = pageName;
        Archetype.pages.urlPage = Archetype.path + Archetype.pages.relativePathToPages + pageName + ".js";
        //Logger.log("Page to load: " + Archetype.pages.urlPage);

        var onLoad = function() {
            if(typeof window[Archetype.pages.pageName] == "undefined")
                throw ("Can't find the object \"" + Archetype.pages.pageName + "\" which should have been added by \"" + Archetype.pages.urlPage + "\"");
            if(typeof window[Archetype.pages.pageName].main == "undefined")
                throw ("Can't find the function \"" + Archetype.pages.pageName + ".main()\" which should have been added by \"" + Archetype.pages.urlPage + "\"");
            
            Archetype._loadPrototype();
        }
        
        Archetype.addJS(Archetype.pages.urlPage, onLoad);
    },
    /**
     * Load Prototype.
     * @private
     */
    _loadPrototype: function() {
		Archetype.addJS($U('jquery-1\\.2\\.3\\.pack'), function(){
			window.JQ=window.$;
			Archetype.addJS($U('Archetype.libs.prototype.prototype16'), Archetype._loadArchetypeClass);
		});
    	//Archetype.addJS($U('Archetype.libs.prototype.prototype16'), Archetype._loadDeprecation);
    },
    _loadDeprecation: function() {
        Archetype.addJS($U('Archetype.libs.prototype.deprecation'), Archetype._loadArchetypeClass);
    },
    /**
     * Load Archetype Class improvements.
     * @private
     */ 
    _loadArchetypeClass: function() {
    	if(Archetype.useArchetypeClass === true)
            Archetype.addJS($U('http://preprod-sim-portail.meteo.aw.atosorigin.com/js/Archetype.Class'), Archetype._loadEvents);
        else
            Archetype._loadEvents();
        
    },
    /**
     * Load the Event system
     * @private
     */
    _loadEvents: function() {
    	if(Archetype.useEvents === true) {
            //Wait until document.body is available : Opera hack :/
            if(document.body === null) {
                setTimeout(Archetype._loadEvents, 1);
            } else {
                //seems that we cannot use subModules at this point
                //TODO: check this is not a bug ! ? !
                Archetype.addJS($U('Archetype.events.event'), Archetype._joinBasicEnvironnementLoaded, false);
            }
        }
        else
            Archetype._joinBasicEnvironnementLoaded();
    },
    /**
     * Wait the availability of all the minimal libraries needed to use the environnement.
     * @private
     */
    _joinBasicEnvironnementLoaded: function() {
        //let's add a shortcut to Prototype.emptyFunction
        window._ = Prototype.emptyFunction;

        //Prototype is ok; we override the Class definition
        if($exist(Class) && $exist(Archetype.Class) &&
            $exist(Archetype.classOverride) && Archetype.classOverride != false) {
            Object.extend(Class, Archetype.Class);
        }

        //Initialize loaderQueue
        Archetype.loaderQueue = {
            queue: [],
            currents: [],
            callbacks: []
        };

        //Time to load the logger
        Archetype.addJS($U('Archetype.log.abstractLogger'), Archetype._loadSpecificLogger);
    },
    /**
     * The abstract logger is loaded, now load the configured one
     * @private
     */
    _loadSpecificLogger: function() {
        Archetype.useLogger("Archetype.log.abstractLogger");
        Archetype.require(Archetype.logger, {
            callback: Archetype._bootstrapEnd
        });
    },
    /**
     * All dependencies of Archetype core are loaded, this method ends the bootstrap with last initialisations
     * @private
     */
    _bootstrapEnd: function() {
        //Set the configured Logger instance
        Archetype.useLogger(Archetype.logger);
		
        //Initialize templates
        if(Archetype.template != null) {
        	if(Archetype.template.indexOf("template.") == 0) {
        		//Hack because modules conf doesn't work fine for this need
        		Archetype.template = "Archetype." + Archetype.template;
        	}
            Archetype.defaultModules.push(Archetype.template);
        }
		
        //Last, load the configured defaults libraries
        Archetype.require(Archetype.defaultModules, {
            callback: Archetype._onEnvironmentLoaded
        });
    },
    /**
     * Fired when the whole environnement is available
     * @private
     */
    _onEnvironmentLoaded: function() {
    	if(Archetype.template != null)
            Archetype.useTemplate(Archetype.template);
        if(Archetype.useEvents ===true)
            Archetype.dispatchEvent("Environnement", "Loaded");

        Logger.log("Archetype Environnement is available :)");

        //Archetype.launchPage(location.href);
        window[Archetype.pages.pageName].main();
    },
    /**
     * @param {String} logger module name
     * Instanciate a Logger by it's module name.
     *
     * In order to be instanciated, a logger class must have
     *  the same name as the module name without the "log." prefix
     */
    useLogger: function(/*String*/ logger) {
    	var loggerName = logger.replace(/^(Archetype\.)?log\./,"");
        loggerName = loggerName.substr(0, 1).toUpperCase() + loggerName.substr(1);
        if(Archetype.useArchetypeClass === true)
            Logger = Archetype.Class.singleton(Archetype.Loggers[loggerName]).instance();
        else {
            //TODO: should have only one instance?
            var LoggerClass = Class.create();
            LoggerClass.prototype = Archetype.Loggers[loggerName];
            Logger = new LoggerClass();
        }
    },
    /**
     * @param {String} template
     * Instanciate a Template by it's module name.
     */
    useTemplate: function(/*String*/ template) {
        var templateName = template.replace(/^(Archetype\.)?template\./, "");
        templateName = templateName.substr(0, 1).toUpperCase() + templateName.substr(1);
        Archetype.Template = Archetype.Templates[templateName];
    },
    /**
     * Add a script file to the header.
     * Should not be used by user.
     * @param {String} file
     * @param {Function} onloadFunc
     * @param {Boolean} [defer]
     */
    addJS: function(/*String*/ file, /*Function*/ onloadFunc, /*Boolean*/ defer) {
        //should be defined when configuration has been read and when
        //the corresponding logger file has been loaded
        if(typeof Logger != 'undefined') {
            Logger.debug("Adding JS: " + file);
        }

        //creating the script dom element
        var script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = file;
        script.charset ='iso-8859-1';
        if(typeof defer == "undefined" || defer) {
            script.defer = true;
        }
        script.startLoadingTime = new Date();
	
        /**
         * Callback to know that the JavaScript has been loaded
         * @ignore
         */
        script.onload = function() {
            if (script.readyState && script.readyState != "loaded" && script.readyState != "complete") {
                return;
            }
            script.onreadystatechange = script.onload = null;
            script.loaded = true;
            var loadingTime = (new Date()).getTime() - script.startLoadingTime;
            if($exist(Archetype.maxStaticLoadingTime)) {
                if(loadingTime > Archetype.maxStaticLoadingTime) {
                    Archetype.maxStaticLoadingTime = loadingTime;
                }
            } else if(loadingTime > 2000) {
                Archetype.maxStaticLoadingTime = loadingTime;
            }
			
            if(Archetype.modules) { //Configuration ready
                Archetype._provide(file);
            }
            
            if (onloadFunc) {
                onloadFunc();
            }
        };
        
        if(navigator.appName != "Opera") {
        	script.onreadystatechange = script.onload;
        }
		
        if($exist(Archetype.maxStaticLoadingTime)) {
            window.setTimeout(function() {
                if(script.loaded != true) {
                    throw ("The script \"" + file + "\" has not been found.");
                }
            }, Archetype.maxStaticLoadingTime * 2);
        }
        
        Archetype.scriptElement.parentNode.appendChild(script);
    },
    /**
     * Add a CSS in the header.
     * @param {String} file real path
     */
    addCSS: function(/*String*/file)
    {
        var link = document.createElement('link');
        link.rel = 'stylesheet';
        link.href=file;

        var head = document.getElementsByTagName("head")[0];
        head.appendChild(link);
        //ToDo: check that there's no problem with Safari 2.0 ...
        if(Archetype.modules) { //Configuration ready
            Archetype._provide(file);
        }
    },
    /**
     * Check if the module is already loaded
     * @param {String} module
     */
    isLoadedModule: function(/*String*/ module) {
        var realPath = $U(module);
        return typeof Archetype.loadedModules[realPath] != "undefined" && Archetype.loadedModules[realPath];
    },
    /**
     * Analyse the module name by checking if it is already loaded, mark it as being loaded if is not and return the real path
     * @param {String} module name to check
     * @return {Boolean} false if the require has to be done, true if not
     * @private
     */
    _startLoadingModule: function(/*String*/ module) {
    	if(Archetype.isLoadedModule(module) && Archetype.getTypeForRealPath(module) != "html") { //In special case of require an html file, we need the content. So we emule the need to get it for getting it with browser cache
            return true; //Mean that the module was already loaded
        }
        Archetype.loadedModules[module] = false; //Mean that the module is currently loading
        return false; //Mean that the module has to be loaded
    },
    
    /**
     * Used to register modules.
     * Should not be used by user.
     * @param {String} module
     * @private
     */
    _provide: function(/*String*/ module) {
        var url = $U(module);
        if(! Archetype.isLoadedModule(url)) {
            Archetype.loadedModules[url] = true;
            return false; //Mean that the module wasn't loaded
        } else {
            return true; //Mean that the module was already loded
        }
    },
    
    /**
     * Get a list of module string parts
     * @param {String} module : module to split
     * @return {Array<String>} module and its parents modules (aka module hierarchy)
     */
    _getHierarchyOfModule: function(/*String*/module) {
    	var hierarchy=[];
    	for(var index = module.indexOf("."); index > -1 && module.substr(0, index) != ""; index = module.indexOf(".", index + 1 ))
    	{
            hierarchy.push(module.substr(0, index));
    	}
    	hierarchy.push(module);
    	return hierarchy;
    },

    /**
     * 
     */
    _getModuleUrlFromConfiguration: function(/*String*/module) {
    	var originalModule = module;
    	var moduleHierarchy = Archetype._getHierarchyOfModule(module);
    	//replace the first occurence of a configuration in the hierarchy (starting from the most detailled one)
    	for ( var i = moduleHierarchy.length - 1 ; i>=0 ; i--)
    	{
            var tag = moduleHierarchy[i];
            if(tag !== "" && typeof Archetype.modules[tag] != "undefined") {
                if(typeof Archetype.modules[tag].rPath != "undefined") {
                    module = module.replace(tag, Archetype.modules[tag].rPath);
                    if(originalModule != module)
                        module = Archetype._getModuleUrlFromConfiguration(module);
                    break;
                }
                else if (typeof Archetype.modules[tag].aPath != "undefined") {
                    module = module.replace(tag, Archetype.modules[tag].aPath);
                    if(originalModule != module)
                        module = Archetype._getModuleUrlFromConfiguration(module);
                    break;
                }
            }
    	}
    	return module;
    },
    
    /**
     * Return the URL for the module string in parameter.
     * If the module parameter is already an URL, just return it.
     * @param {String} module module name or an URL
     * @param {String} evaluation Evaluation tag for interpretation of the module
     */
    getUrl: function(/*String*/ module, /*String*/ evaluation) {
        if(module.indexOf("://") != -1) {
            //"://" indicates that there is the protocol in the module name and in this case this is already a real path
            return module;
        }
        
        
        module = Archetype._getModuleUrlFromConfiguration(module);
        
        //delete any path in common with archetype
        if(module.indexOf(Archetype.path) == 0) {
            module = module.substring(Archetype.path.length);
        }

        evaluation = (typeof evaluation == "string") ? evaluation : "js"; // default evaluation is "js"
        var type = (evaluation == "component") ? "js" : evaluation; // default type is "js" 
        var extension = false;
        
        //get the type from the extension if available
        if(/[^\\]\./.test(module))
        {
            type = Archetype.getTypeForRealPath(module)||type;//keep current type if undefined in module name
            var lastPartRegExp = /.*[^\\]\.(.*)$/;//last non escaped "." in an expression
            if( lastPartRegExp.test(module) )
            {
                var lastPart = lastPartRegExp.exec(module)[1];
                // extension is lastPart or default, we check if lastPart is a valid extension
                extension =  (Archetype.extensions[type].toString().indexOf(lastPart) >= 0) ? lastPart : false;
            }
        }
        if(! extension) {
            module += "." + ((Archetype.defaultExtensions && Archetype.defaultExtensions[type]) ? Archetype.defaultExtensions[type] : type);
        }
        var isAbsolute = ( module.indexOf("://") > -1 );//checks if module contains a protcol definition to know if a absolute path was used
        return ((!isAbsolute)? Archetype.path : "") + module.replace(/([^\\])\./g, "http://preprod-sim-portail.meteo.aw.atosorigin.com/js/$1/").replace(/\\\./g, ".").replace(/(.*[^\\])\//g, "$1.").replace(/\/\//g,"/").replace(/^(file:\/|http:)/g,"$1/");
    },



    /**
     * Find the type (html, js or css) of the real path in parameter.
     * @param {String} realPath real path of the file of which we want find the type
     */
    getTypeForRealPath: function(/*String*/ realPath) {
        if(/[^\\]\./.test(realPath)) { //If there is a "."
            var ext = /.*[^\\]\.(.*)$/.exec(realPath)[1]; //End of string after the last "."
            var types = [Archetype.defaultExtensions.js||"js", Archetype.defaultExtensions.css||"css", Archetype.defaultExtensions.html||"html"];
                
            for(var i = 0; i < types.length; i++) {
                var extensions = Archetype.extensions[types[i]];
                for(var j = 0; j < extensions.length; j++) {
                    if(extensions[j] == ext) {
                        return types[i];
                    }
                }
            }
        }
        else {
            throw "Can't find Type for realPath : " + realPath;
        }
        return null;
    },
	
    /**
     * Creates an absolute namespace for storing the values
     * Absolute indicates that namespace in parameter start in Archetype.root
     *
     * @example
     *    $N("foo.bar",{barfoo:5});
     *    -> Archetype.root.foo.bar.barfoo == 5
     * @param {String} namespace 
     * @param {baseType or Array or HashMap} values...
     * @see $N
     */
    absoluteNamespace: function(/*String namespace, baseType or Array or HashMap values ...*/) {
        var args = [];
    	for (var i = 0, length = arguments.length; i < length; i++) {
      		args.push(arguments[i]);
  		}
        var namespace = args.shift();
        var values = args;
        Archetype._namespace(true, namespace, values);
    },
    /**
     * Creates a relative namespace for storing the values
     * Relative indicates that an HashMap with keys is returned
     * @example
     *    $$N("foo.bar", {foobar:5}) == {foo: {bar: {foobar: 5}}}
     * @param {String} namespace 
     * @param {baseType or Array or HashMap} values...
     * @see $$N
     */
    relativeNamespace: function(/*String namespace, baseType or Array or HashMap values ...*/) {
        var args = [];
    	for (var i = 0, length = arguments.length; i < length; i++) {
      		args.push(arguments[i]);
  		}
        var namespace = args.shift();
        var values = args;
        return Archetype._namespace(false, namespace, values);
    },
    /**
     * Generic namespace for relative and absolute cases
     * @private
     */
    _namespace: function(/*Boolean*/ absolute, /*String*/ namespace, /*HashMap []*/ valuesList) {
        var keys = namespace.split(".");
        //root is a local HashMap for relative and Archetype.root (window by default) for absolute
        var root = {};
        if(absolute) {
            root = Archetype.root;
        }
        //assure that each key of the namespace is created
        var lastCurrent = root;
        var current = root;
        for(var i = 0; i < keys.length; i++) {
            if(typeof current != "object" || typeof current[keys[i]] == "undefined") {
                current[keys[i]] = {};
            }
            lastCurrent = current;
            current = current[keys[i]];
        }
        //populates values in parameters in the namespace initialized
        for (var i = 0; i < valuesList.length; i++) {
        	var values = valuesList[i];
        	if(typeof values == "object" && typeof values.length == "undefined") {
        		//case of an HashMap, put all keys in the namespace
		        for (var property in values) {
		            current[property] = values[property];
		        }
        	} else {
        		//cas of a baseType or an array, we put the value in the namespace
        		lastCurrent[keys[keys.length - 1]] = values;
        	}
        }
        return root;
    },
	
    /**
     * /!\Can't be used before the bootstrap.
     * Recursive method for getting all subModules in the configuration.
     * @param {String} prefix
     * @param {String} module
     * @private
     */
    _recurseOnSubModule: function(/*String*/ prefix, /*String*/ module) {
        if(typeof module == "string") {
            if(prefix !== "") {
                module = prefix + "." + module;
            }
            if(typeof Archetype.modules[module] != "undefined" && typeof Archetype.modules[module].subModules != "undefined") {
                return this._recurseOnSubModule(module, Archetype.modules[module].subModules);
            } else {
                return [module];
            }
        } else { //typeof = object -> array
            var array = [];
            module.each(function(item) {
                var recurse = this._recurseOnSubModule(prefix, item);
                array = array.concat(recurse);
            }.bind(this));
            return array;
        }
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Recursivly add and complete needed layers for loading modules
     * Manage needs and subModules configuration
     * @param {String or Array} module module or modules to load
     * @param {Array} layers already prepared
     * @private
     */
    _addDependencies: function(/*String or Array*/ module, /*Array*/ layers) {
        var modules = null;
        //Define if there is configured modules on the list
        if(typeof module == "string") {
            modules = $A([module]);
        } else {
            modules = $A(module);
        }
        //Logger.warn("modules=" + modules.inspect() + " -- layers=" + layers.inspect());

        var definedModules = $A();
        var notDefinedModules = $A();
        modules.each(function(module) {
            if(typeof Archetype.modules[module] != "undefined") {
                definedModules.push(module);
            } else {
                notDefinedModules.push(module);
            }
        });
        //Logger.warn("defined=" + definedModules.inspect() + " -- notDefined=" + notDefinedModules.inspect());
        if(definedModules.length > 0) {
            modules = notDefinedModules;
		    
            //Recursion on the module declaration for searching possibles subModules configured
            var gotSubModules = false;
            definedModules.each(function(module) {
                if(typeof Archetype.modules[module].subModules != "undefined") {
                    //TODO copy rPath and aPath information to submodules -> add a pathInherit option ?
                    var subModules = Archetype._recurseOnSubModule("", module);
                    modules = modules.concat(subModules);
                    subModules.each(function(module) {
                        if(typeof Archetype.modules[module] != "undefined") {
                            definedModules.push(module);
                        }
                    });
                    gotSubModules = true;
                } else {
                    modules = modules.concat(module);
                }
            });
            //Logger.warn("withSubModules=" + modules.inspect() + " -- definedModules=" + definedModules.inspect());
		    
            //Loading dependencies
            var needs = $A();
            var needsToLoad = $A();
            definedModules.each(function(module) {
                if(typeof Archetype.modules[module].needs != "undefined") {
                    needs = needs.concat(Archetype.modules[module].needs);
                }
            });
            needs.each(function(module) {
                if(! Archetype.isLoadedModule(module)) {
                    needsToLoad.push(module);
                }
            });
            if(needsToLoad.length > 0) {
                return Archetype._addDependencies(needsToLoad, $([modules]).concat(layers));
            }
        }
        return $([modules]).concat(layers);
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Convert all modules names by his real path.
     * @param {Array} layers layers of a require
     * @param {String} evaluation
     * @private
     */
    _layersToRealPaths: function(/*Array*/ layers, /*String*/ evaluation) {
        return layers.collect(function(layer) {
            return layer.collect(function(module) {
                return $U(module, evaluation);
            });
        });
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Remove duplicates urls in order to keep only the one in the higher layer.
     * @param {Array} layers layers of a require
     * @private
     */
    _withoutDuplicates: function(layers) {
        for(var i = 0; i < layers.length; i++) {
            var excludes = $A();
            for(var j = 0; j < i; j++) {
                excludes = excludes.concat(layers[j]);
            }
            layers[i] = layers[i].reject(function(module) {
                //Copy of the Protoype's include core method to be sure using a simple equality
                var found = false;
    			excludes.each(function(value) {
      				if (value == module) {
        				found = true;
        				throw $break;
      				}
    			});
    			return found;
            });
        }
        return layers;
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Resolve all files to load to complete the require.
     * Manage layers of loading and module to real path conversion
     * @param {String or Array} module module's name or array of modules names
     * @param {String} evaluation evaluation specified in the require
     * @private
     */
    _resolve: function(/*String or Array*/ module, /*String*/ evaluation) {
        var layers = Archetype._addDependencies(module, []);
        layers = Archetype._layersToRealPaths(layers, evaluation);
        return Archetype._withoutDuplicates(layers);
    },
	
    /**
     * /!\Can't be used before the bootstrap.
     * Tells archetype that you need a particular module.
     * @param {String or Array} module : module's name or url
     * @param {Hashmap} options options as follow :
     * 	      {Boolean} evaluation by default, eval is evaluated by the file extension of the module (js = true, other = false)
     *                             true, url loaded is evaluated
     *        {Function} callback callback used when in asynchronous mode
     *        {HashMap} storing indicate an Hashmap for storing XHR results.
     */
    require: function(/*String or Array*/ module, /*HashMap*/ options) {
        var layers = Archetype._resolve(module, options.evaluation)
        if(Archetype.debugLoadLayers === true)
            console.debug("layers resolved", layers);
        Archetype._recurseOnRequire(layers, options);
    },
	
    _recurseOnRequire: function(/*Array*/ layers, /*HashMap*/ options) {
        if(layers.length > 0) {
            var newOptions = Object.clone(options);
            newOptions.callback = Archetype._recurseOnRequire.bind(Archetype, layers.slice(1), options);
            Archetype._layerRequire(layers[0], newOptions);
        } else {
            if(typeof options.callback == "function") {
                options.callback();
            }
        }
    },
	
    _layerRequire: function(/*Array*/ modules, /*HashMap*/ options) {
        if(Archetype.debugLoadLayers === true)
            console.debug("layer require ", modules);
        //Initialize options with defaults values if necessary
        options = Object.extend({
            evaluation: true,
            asynchronous: true,
            callback: Prototype.emptyFunction,
            storing: false
        }, options || {});

        //Manage special case for evaluation component
        if(options.evaluation == "component") {
            var realPaths = $A();

            $A(modules).each(function(module, i) {
                realPaths[i] = $U(module);
            });

            new Archetype.LoadJoiner("Component", realPaths, function() {
                if(realPaths.length == 1) {
                    return options.callback(Archetype.Components.get(realPaths[0]));
                } else {
                    var components = $A();
                    realPaths.each(function(item) {
                        components.push(Archetype.Components.get(item));
                    });
                    return options.callback(components);
                }
            });

            Archetype.require(realPaths, {
                evaluation: "js"
            });

            return;
        }

        //var modules = $A(module);
        var modulesToLoad = $A();
        var allLoaded = true;
        modules.each(function(module) {
            var alreadyLoaded = this._startLoadingModule($U(module, options.evaluation));
            if(! alreadyLoaded) {
                allLoaded = false;
                modulesToLoad.push(module);
            }
        }.bind(this));
        modules = modulesToLoad;
        //Logger.warn(modules.inspect());
        if(allLoaded) {
            options.callback();
        } else {
            Archetype._loader(modules, options, null);
        }
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Archetype.require alias to load easily a component asynchronously
     *
     * @param {String} module component module name
     * @param {Function or Arguments} [callback] function to call when component is loaded.
     *     If callback is undefined, this will instanciate a new component with default constructor,
     *     (if you put anything else) as callback
     * @throws {String} Exception if no module has been defined
     * @example Archetype.requireComponent("my.components.first");
     * todo : example Archetype.requireComponent("my.components.first", 1, "hello, world", ["foo","bar"]);
     * @example Archetype.requireComponent("my.components.first", function(mycomponent){ alert("hello!");});
     *
     */
    requireComponent: function (/*String*/module, /*Function*/callback ) {
        var umn = $U(module);

        if (typeof module != "string" && umn == null)
            throw "Invalid module in Archetype.requireComponent";

		var component = Archetype.Component.get(umn);
		if(component != null) {
			return ( new component() );
		}

        if (typeof callback != "function") {
            /** @ignore */
            mycallback = function() {
                var component = Archetype.Component.get(umn);
                return ( new component() );
            };
        } else {
            mycallback = callback;
        }

        Archetype.require(module, {
            evaluation: "component",
            callback: mycallback
        });
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Sequencer for all requires. Used for add modules and as callback when modules are finished.
     * @param {String []} modules
     * @param {Hashmap} options
     * @param {String} finishedModule
     * @private
     */
    _loader: function(/*String []*/ modules, /*Hashmap*/ options, /*String*/ finishedModule) {
        if(Archetype.debugLoader === true)
            console.debug("LOADER modules : ", modules, ", options : ", options, ", finishedModule : ", finishedModule);
        var concurentXhrLimit = Archetype.concurentXhrLimit;
        var callbacks = $A();
        if(modules !== null) {
            var addition = $A();
            modules.each(function(module, i) {
                modules[i] = $U(module, options.evaluation);
                addition.push(modules[i]);
                addition.push(options.evaluation);
                addition.push(options.storing);
            });
            //Priorize the component loading in the queue
            if(options.evaluation == "component") {
                //prepend it in the queue
                Archetype.loaderQueue.queue = addition.concat(Archetype.loaderQueue.queue);
            } else {
                //postpend it in the queue
                Archetype.loaderQueue.queue = Archetype.loaderQueue.queue.concat(addition);
            }

            Archetype.loaderQueue.callbacks.push(modules);
            Archetype.loaderQueue.callbacks.push(options.callback);

            //Logger.log("LOADER add modules " + modules);
        }
        if(finishedModule !== null) {
            //Logger.log("LOADER finished module " + finishedModule);
            Archetype.loaderQueue.currents = Archetype.loaderQueue.currents.without(finishedModule);
            for(var i = 0; i < Archetype.loaderQueue.callbacks.length; i += 2) {
                Archetype.loaderQueue.callbacks[i] = Archetype.loaderQueue.callbacks[i].without(finishedModule);
                if(Archetype.loaderQueue.callbacks[i].length === 0) {
                    callbacks.push(Archetype.loaderQueue.callbacks[i + 1]);
                    Archetype.loaderQueue.callbacks.splice(i, 2);
                    i -= 2;
                }
            }
        }
        while(Archetype.loaderQueue.queue.length > 0 && Archetype.loaderQueue.currents.length < concurentXhrLimit) {
            var module = Archetype.loaderQueue.queue.shift();
            //Logger.log("LOADER require " + module + " queue " + Archetype.loaderQueue.queue);
            var evaluation = Archetype.loaderQueue.queue.shift();
            var storing = Archetype.loaderQueue.queue.shift();
            //Logger.warn(Archetype.loaderQueue.currents.inspect() + " -- " + module + " -- " + Archetype.loaderQueue.currents.member(module));
                
            //Copy of the Protoype's include core method to be sure using a simple equality
            var found = false;
			Archetype.loaderQueue.currents.each(function(current) {
  				if (current == module) {
    				found = true;
    				throw $break;
  				}
			});
            
            if(!Archetype.isLoadedModule(module) && !found) {
            	Archetype.loaderQueue.currents.push(module);
            	this._singleLoad(module, {
                    asynchronous: true,
                    evaluation: evaluation,
                    callback: this._loader.bind(this, null, null, module),
                    storing: storing
            	});
            }
        }

        $A(callbacks).invoke("call");
    },

    /**
     * /!\Can't be used before the bootstrap.
     * Launch either XHR or adding script on link tag for loading for the module in parameter
     * @param module
     * @param options
     * @private
     */
    _singleLoad: function(/*String*/ module, /*Hashmap*/ options) {
    	var umn = $U(module, options.evaluation);
        
        //console.debug("module", options, umn);
        var callback;
        //Logger.log("LOAD : " + umn.realPath + ", " + options.evaluation + ", " + options.asynchronous);
        if(options.evaluation == true || options.evaluation == "js" || options.evaluation == "component") {
            Archetype.addJS(umn, Archetype._onRequireSuccess.bind(this, module, options));
        } else if(options.evaluation == "css") {
            Archetype.addCSS(umn);
            Archetype._onRequireSuccess(module, options);
        } else { //options.evaluation == "html"
            Logger.debug("XHR : " , umn);
            var xhr = new Ajax.Request(umn, {
                asynchronous: true,
                onSuccess: function() {
                    try {
                        Archetype._onRequireSuccess.bind(this, module, options).apply(this, arguments);
                    } catch (exception) {
                        Logger.error("Callback of XHR on " + module + " failed with exception ", exception);
                    }
                },
                onException: function(transport) {
                    Logger.warn("XML HTTP REQUEST Exception : ", umn, transport);
                }
            });
        }
        return this.returnContent;
    },

    /**
     * /!\Can't be used before the bootstrap.
     * Used when require a js file.
     * Callback function of the Ajax request.
     * @param {String} module
     * @param {Hashmap} options
     * @param {Object} transport Ajax request transport object.
     * @private
     */
    _onRequireJs: function(/*String*/ module, /*Hashmap*/ options, /*Object*/ transport) {
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.id = module;
        var head = $$("head")[0];
        head.appendChild(script);
        script.innerHTML = transport.responseText;
        this._onRequireSuccess(module, options, transport);
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Used when require a js file.
     * Callback function of the Ajax request.
     * @param {String} module
     * @param {Hashmap} options
     * @param {Object} transport : Ajax request transport object.
     * @private
     */
    _onRequireCss: function(/*String*/ module, /*Hashmap*/ options, /*Object*/ transport) {
        var link = document.createElement("link");
        link.rel = "stylesheet";
        link.id = module;
        var head = $$("head")[0];
        head.appendChild(link);
        link.innerHTML = transport.responseText;
        this._onRequireSuccess(module, options, transport);
    },
    /**
     * /!\Can't be used before the bootstrap.
     * Used after all require as end callback function.
     * @param {String} module
     * @param {Hashmap} options
     * @param {Object} transport : Ajax request transport object.
     * @private
     */
    _onRequireSuccess: function(/*String*/ module, /*Hashmap*/ options, /*Object*/ transport) {
        var responseText = transport ? transport.responseText : '';
        this.returnContent = responseText;
        Archetype._provide(module);
        if(options.storing !== false) {
            options.storing[$U(module, options.evaluation)] = responseText;
        }
        if(options.asynchronous) {
            //TODO: modify the loader to manage additional parameters
            options.callback(responseText);
        }
    }
};

/**
 * TODO: Modularize i18n and complete i18n management
 * @param {Object} templateString
 */
Archetype.i18n = function(templateString) {
    return templateString.replace(/{lang}/g, navigator.language);
}

/**
 * @static $U
 * @see Archetype.getUrl
 */
var $U = Archetype.getUrl;
/**
 * @static $N
 * @see Archetype.absoluteNamespace
 */
var $N = Archetype.absoluteNamespace;
/**
 * @static $$N
 * @see Archetype.relativeNamespace
 */
var $$N = Archetype.relativeNamespace;
/**
 * @static $exist
 * checks if an element is null or undefined
 */
var $exist = function(element) {
    return ! (typeof element == "undefined" || element == null);
}

Archetype.load();


Tapestry = {};
TapestryOnDOMLoadedCallbacks = [];
Tapestry.onDOMLoaded = function(callback) {
	 TapestryOnDOMLoadedCallbacks.push(callback);
}