<commit_before>/**
 * @author <NAME> http://www.netben.nl/ <EMAIL>
 * @license MIT
 * 
 * @version RC3
 * 
 * @todo
 * - unit tests!!!
 * - investigate alternative for domBuild/domTool
 * - investigate expecting mikado.build to always return an Object or Function.
 * - backup script element load listeners to check if a script was loaded with no 'mikado.module' call inside.
 * - More proper error messages.
 * - Security messages?
 * - Module versions?
 * - cleaner code
 * - better architecture
 * 
 */

(function() {
    
	/*---------------------------------------------------------------*
	 *           settings, variables and constants                   *
	 *---------------------------------------------------------------*/
    
    var registry = {}, scripts = {}, pending = {}, killers = {}, eventLog = [],
        
    domLoaded = 0,
        
    GLOBAL = this, CONTEXT = GLOBAL.document, ROOT = CONTEXT.documentElement,
        
    HEAD = CONTEXT.getElementsByTagName("head")[0],
    
    config = {
        
        // default time in miliseconds a module is allowed to take for loading.
        timeout: 1500,
        
        // default folder repository to attach module paths to.
        root:function() {
            var list = CONTEXT.getElementsByTagName("script"),
                i = 0,
                re = /\/mikado[^\/\\]*\.js.?$/i,
                node;
            while((node = list[i++])) {
                if(re.test(node.src)) {
                    return node.src.replace(re, "");
                }
            }
        }(),
        
        // external repository map
        repositories: {},
        
        // path override map
        force: {}
    },
        
    // used to create script elements for module loading.
    scriptFragment = CONTEXT.createElement("script");
    scriptFragment.type = "text/javascript";
        
    // used to instantiate modules.
    var empty = function(){},
        
    /*---------------------------------------------------------------*
     *                           utility                             *
     *---------------------------------------------------------------*/
        
    // no explanation needed to JS devs.
    slice = Array.prototype.slice,
    
    augment = function(receiver, provider){
        if (provider) {
            for (var key in provider) {
                receiver[key] = provider[key];
            }
        }
        return receiver;
    },
        
    // Internal abstract EventDispatcher factory method.
    listeners = {},
    dispatcher = {
        listen: function(type, listener) {
            if (!listeners[type]) {
                listeners[type] = [];
            }
            listeners[type].unshift(listener);
        },
        deafen: function(type, listener) {
            var list = listeners[type], i;
            if (list) {
                i = list.length;
                while (i--) {
                    if (list[i] === listener) {
                        list.splice(i, 1);
                    }
                }
            }
        },
        dispatch: function(type, data){
            var list = listeners[type], i;
            data = data || {};
            data.type = type;
            if (list) {
                i = list.length;
                while (i--) {
                    list[i](data);
                }
            }
        }
    },
    
    Event = function(type, data){
        this.type = type || this.type;
        this.timestamp = +new Date();
        augment(this, data);
    }
    
    Event.prototype = {
        type: 'none',
        message: null,
        resolution: null,
        path: null
    };
    
    augment(Event, {
        COMPLETE: 'complete',
        LOADING: 'loading',
        LOADED: 'loaded',
        ERROR: 'error',
        DOMREADY: 'domReady',
        RUN: 'run',
        RAN: 'ran'
    });
    
    /**Logs data and dispatches it as event.
     * 
     * @param {String} type Event type
     * @param {Object} data Key/Value hash
     */
    var log = function(type, data) {
        var e = new Event(type, data);
        eventLog.push(e);
        dispatcher.dispatch(type, e);
    },
        
    initDomReady = function(){
        if(!domLoaded) {
            domLoaded = 1;
            dispatcher.dispatch(Event.DOMREADY);
        }
    }
    
    // based on <NAME>'s work.
    // http://javascript.nwbox.com/ContentLoaded/ +
    // http://javascript.nwbox.com/IEContentLoaded/
    if(CONTEXT.addEventListener) {
        var W3CContentLoaded = function(){
            initDomReady();
            CONTEXT.removeEventListener('DOMContentLoaded', arguments.callee, false);
            CONTEXT.removeEventListener('load', arguments.callee, false);
        }
        CONTEXT.addEventListener('DOMContentLoaded', W3CContentLoaded, false);
        CONTEXT.addEventListener('load', W3CContentLoaded, false);
    } else if(CONTEXT.attachEvent){
        var size = 0;
        var poll = function() {
            try {
                // throws errors until after ondocumentready
                ROOT.doScroll('left');
                size = ROOT.outerHTML.length;
                if (size * 1.03 < CONTEXT.fileSize * 1) {
                    return setTimeout(poll, 50);
                }
            } 
            catch (e) {
                return setTimeout(poll, 50);
            }
            initDomReady();
        }
        var IEContentLoaded = function(){
            if(CONTEXT.readyState != 'complete') {
                poll();
            } else {
                initDomReady();
            }
            CONTEXT.detachEvent('onreadystatechange', arguments.callee);
            CONTEXT.detachEvent('load', arguments.callee);
        }
        CONTEXT.attachEvent('onreadystatechange', IEContentLoaded);
        CONTEXT.attachEvent('load', IEContentLoaded);
    } else {
        // from <NAME>
        var oldonload = GLOBAL.onload;
        GLOBAL.onload = function(event) {
            initDomReady();
            if (typeof oldonload == 'function') {
                oldonload(event || GLOBAL.event);
            }
        }
    }
    
    /*---------------------------------------------------------------*
     *                      loading mechanism                        *
     *---------------------------------------------------------------*/
    
    // Adding a global handler to store records once all dependencies are loaded.
    dispatcher.listen(Event.COMPLETE, function(e){
        var list = pending[e.path];
        if(!list || !list.length) {
            return;
        }
        var i = list.length, j, current;
        while((current = list[--i])) {
            j = current.fetch.length;
            while(j && j--) {
                if(current.fetch[j] != e.path) {
                    continue;
                }
                current.fetch.splice(j, 1);
                if(!current.fetch.length) {
                    storeRecord(current);
                    list.splice(i, 1);
                }
            }
        }
    });
        
    /**Creates a proper URI from the dot notated path.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @return {String} URI to module file.
     */
    var createURI = function(path) {
        for(var key in config.repositories) {
            if(RegExp("^"+key).test(path)) {
                return config.repositories[key] + path.replace(/\./g, "/") + ".js";
            }
        }    
	    return config.root + "/" + path.replace(/\./g, "/") + ".js";
    },
    
    /**Loads all dependencies defined in the module record.
     * 
     * @param {Object} record Module data and builder method.
     * @return {Boolean} true when there where dependencies that needed to be loaded.
     */
    loadDependencies = function(record){
        if(!record.fetch || !record.fetch.length){
            return false;
        }
        var list = record.fetch, i = list.length, path;
        while(i--) {
            path = list[i];
            if(registry[path]){
                list.splice(i, 1);
                continue;
            }
        }
        if(!list.length) {
            return false;
        }
        list = [].concat(list);
        i = list.length;
        while(i--) {
            path = list[i];
            if(pending[path]){
                list.splice(i, 1);
                pending[path].push(record);
            } else {
                pending[path] = [record];
            }
        }
        appendScripts(list, record.timeout);
        return true;
    },
    
    /**Iterates over a list of dot notated module paths and appends them to the scriptLocation
     * through a documentFragment.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @param {Number} timeout Timeout override in miliseconds
     */
    appendScripts = function(list, timeout){
        var i = list.length, path,
            fragment = document.createDocumentFragment();
        while(i--){
            path = list[i];
            scripts[path] = scriptFragment.cloneNode(true);
            scripts[path].src = createURI(path);
            fragment.appendChild(scripts[path]);
            log(Event.LOADING, {
                path: path,
                timeout: timeout
            });
        }
        HEAD.appendChild(fragment);
    },
    
    /*---------------------------------------------------------------*
     *                      killing mechanism                        *
     *---------------------------------------------------------------*/
    
    /**Recursively cleans up trail a timedout module leaves behind.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @param {String} message Error message, will be appended to script element in type attribute.
     * @return {void}
     */
    kill = function(path, message){
        var prefix = message ? "Dependency '"+path+"'" : "";
        message = message || "Timed out @ "+killers[path].timeout+"ms";
        log(Event.ERROR, {
            path: path,
            message: message,
            resolution: "failed silently"
        });
        var list = pending[path], i = 
            list.length;
        while(i--) {
            kill(list[i].path, message);
            delete list[i];
        }
        delete pending[path];
    }
    
    /**Initializes a timeout procedure, calling kill() when timeout is achieved.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @param {Number} timeout Timeout override in miliseconds
     * @return {Boolean} false if timeout mechanism is disabled through config.timeout
     */
    dispatcher.listen(Event.LOADING, function(e){
        if(!config.timeout) {
            return false;
        }
        killers[e.path] = {
            timeout: e.timeout,
            timeoutID: setTimeout(function(){
                kill(e.path);
            }, e.timeout)
        };
        
        return true;
    })
    
    /**Shuts down the timeout procedure once the module has loaded
     * (but not yet stored) and cleans up killer footprint.
     */
    dispatcher.listen(Event.LOADED, function(e){
        if (killers[e.path]) {
            clearInterval(killers[e.path].timeoutID);
            delete killers[e.path];
        }
    });
        
    /*---------------------------------------------------------------*
     *                       record mechanism                        *
     *---------------------------------------------------------------*/
    
    /**Filters the module name from dot notated path.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @return {String} name of the module.
     */
    var getNameFromPath = function(path){
        return path ? path.split(".").pop() : null;
    },
    
    /**Checks dependencies on module name and forces relative set module paths
     * defined in config.force map.
     * 
     * @param {Array} list List of module paths (dot notated)
     * @return {void}
     */
    enforce = function(list) {
        var i = list.length, name;
        while(i--) {
            name = getNameFromPath(list[i]);
            if(name && config.force[name]) {
                list[i] = config.force[name];
            }
        }
        return list;
    },
    
    /**Loops over an array to check for dependency descriptors.
     * If a descriptor contains the attribute 'path' and 'when' is truthy
     * the list item gets replaced by the path, otherwise it is removed from the list.
     * If 'path' wasn't a string for some reason, the list item gets removed as well.
     * 
     * @param {Array} list
     * @return {void}
     */
    prepareDependencies = function(list){
        if(list) {
            var i = list.length;
            while(i--) {
                if(typeof list[i] === 'string') {
                    continue;
                }
                if(list[i].path && list[i].when) {
                    list[i] = list[i].path;
                }
                if(typeof list[i] !== 'string') {
                    list.splice(i,1);
                }
            }
        }
    },
    
    /**Concatenates record.fetch and record.include arrays to be used for
     * dependency loading.
     * 
     * @param {Object} record Module data and builder method.
     * @return {void}
     */
    setRequired = function(record){
        prepareDependencies(record.fetch);
        prepareDependencies(record.include);
        record.fetch = record.fetch || [];
        if(record.include instanceof Array) {
            record.fetch = record.fetch.concat(record.include);
        }
        return enforce(record.fetch);
    },
    
    /**Reformats allowed array to object map.
     * 
     * @param {Object} record
     * @return {void}
     */
    setAllowed = function(record){
        if(record.allow instanceof Array) {
            var list = record.allow, path;
            record.allow = {};
            while((path = list.pop())) {
                record.allow[path] = true;
            }
        }
    },
    
    /**Builds library object containing 'include' dependency modules.
     * 
     * @param {Object} targetRecord
     * @return {void}
     */
    setLibraries = function(targetRecord) {
        var list = targetRecord.include,
            i = list.length,
            client = targetRecord.path,
            record, path;
        targetRecord.library = {};
        while(i--) {
            path = list[i];
            record = registry[path];
            if(record.allow && !record.allow[client]) {
                targetRecord.library[record.name] = "Access denied!";
                log(Event.ERROR, {
                    path: targetRecord.path,
                    message: "Disallowed acces to '"+path+"'",
                    resolution: "failed silently"
                });
                continue;
            }
            if(record.traits.domTool) {
                targetRecord.traits.domTool = true;
            }
            if (record.module) {
                targetRecord.library[record.name] = record.module;
            }
        }
    },
    
    /**Reformats record for convenience and starts dependency loading if required.
     * Disables the killer mechanism for this specific record.
     * When there are no dependencies, storeRecord() is called right away.
     * 
     * @param {Object} record
     * @return {void}
     */
    processRecord = function(record){
        record.name = record.name || getNameFromPath(record.path);
        record.traits || (record.traits = {});
        record.traits.path = record.path;
        record.traits.name = record.name;
        record.timeout = record.timeout ? Math.max(timeout, config.timeout) : config.timeout;
        setAllowed(record);
        setRequired(record);
        log(Event.LOADED, record.traits);
        if(!loadDependencies(record)) {
            storeRecord(record);
        }
    },
    
    /**Builds the final record or delays building till DOM is ready depending on record.traits.domBuild setting.
     * Notifies any module listeners that rely on current record.
     * 
     * @param {Object} record
     * @return {void}
     */
    storeRecord = function(record){
        if(record.traits.domBuild && !domLoaded) {
            dispatcher.listen(Event.DOMREADY, function(){
                storeRecord(record);
            });
            return false;<commit_msg>Added 'alias' option to include/fetch descriptor option.<commit_after>/**
 * @author <NAME> http://www.netben.nl/ <EMAIL>
 * @license MIT
 * 
 * @version 1.0 RC3.1
 * 
 * @todo
 * - unit tests!!!
 * - investigate alternative for domBuild/domTool
 * - investigate expecting mikado.build to always return an Object or Function.
 * - backup script element load listeners to check if a script was loaded with no 'mikado.module' call inside.
 * - More proper error messages.
 * - Security messages?
 * - Module versions?
 * - cleaner code
 * - better architecture
 * 
 */

(function() {
    
	/*---------------------------------------------------------------*
	 *           settings, variables and constants                   *
	 *---------------------------------------------------------------*/
    
    var registry = {}, scripts = {}, pending = {}, killers = {}, eventLog = [],
        
    domLoaded = 0,
        
    GLOBAL = this, CONTEXT = GLOBAL.document, ROOT = CONTEXT.documentElement,
        
    HEAD = CONTEXT.getElementsByTagName("head")[0],
    
    config = {
        
        // default time in miliseconds a module is allowed to take for loading.
        timeout: 1500,
        
        // default folder repository to attach module paths to.
        root:function() {
            var list = CONTEXT.getElementsByTagName("script"),
                i = 0,
                re = /\/mikado[^\/\\]*\.js.?$/i,
                node;
            while((node = list[i++])) {
                if(re.test(node.src)) {
                    return node.src.replace(re, "");
                }
            }
        }(),
        
        // external repository map
        repositories: {},
        
        // path override map
        force: {}
    },
        
    // used to create script elements for module loading.
    scriptFragment = CONTEXT.createElement("script");
    scriptFragment.type = "text/javascript";
        
    // used to instantiate modules.
    var empty = function(){},
        
    /*---------------------------------------------------------------*
     *                           utility                             *
     *---------------------------------------------------------------*/
        
    // no explanation needed to JS devs.
    slice = Array.prototype.slice,
    
    augment = function(receiver, provider){
        if (provider) {
            for (var key in provider) {
                receiver[key] = provider[key];
            }
        }
        return receiver;
    },
        
    // Internal abstract EventDispatcher factory method.
    listeners = {},
    dispatcher = {
        listen: function(type, listener) {
            if (!listeners[type]) {
                listeners[type] = [];
            }
            listeners[type].unshift(listener);
        },
        deafen: function(type, listener) {
            var list = listeners[type], i;
            if (list) {
                i = list.length;
                while (i--) {
                    if (list[i] === listener) {
                        list.splice(i, 1);
                    }
                }
            }
        },
        dispatch: function(type, data){
            var list = listeners[type], i;
            data = data || {};
            data.type = type;
            if (list) {
                i = list.length;
                while (i--) {
                    list[i](data);
                }
            }
        }
    },
    
    Event = function(type, data){
        this.type = type || this.type;
        this.timestamp = +new Date();
        augment(this, data);
    }
    
    Event.prototype = {
        type: 'none',
        message: null,
        resolution: null,
        path: null
    };
    
    augment(Event, {
        COMPLETE: 'complete',
        LOADING: 'loading',
        LOADED: 'loaded',
        ERROR: 'error',
        DOMREADY: 'domReady',
        RUN: 'run',
        RAN: 'ran'
    });
    
    /**Logs data and dispatches it as event.
     * 
     * @param {String} type Event type
     * @param {Object} data Key/Value hash
     */
    var log = function(type, data) {
        var e = new Event(type, data);
        eventLog.push(e);
        dispatcher.dispatch(type, e);
    },
        
    initDomReady = function(){
        if(!domLoaded) {
            domLoaded = 1;
            dispatcher.dispatch(Event.DOMREADY);
        }
    }
    
    // based on <NAME>'s work.
    // http://javascript.nwbox.com/ContentLoaded/ +
    // http://javascript.nwbox.com/IEContentLoaded/
    if(CONTEXT.addEventListener) {
        var W3CContentLoaded = function(){
            initDomReady();
            CONTEXT.removeEventListener('DOMContentLoaded', arguments.callee, false);
            CONTEXT.removeEventListener('load', arguments.callee, false);
        }
        CONTEXT.addEventListener('DOMContentLoaded', W3CContentLoaded, false);
        CONTEXT.addEventListener('load', W3CContentLoaded, false);
    } else if(CONTEXT.attachEvent){
        var size = 0;
        var poll = function() {
            try {
                // throws errors until after ondocumentready
                ROOT.doScroll('left');
                size = ROOT.outerHTML.length;
                if (size * 1.03 < CONTEXT.fileSize * 1) {
                    return setTimeout(poll, 50);
                }
            } 
            catch (e) {
                return setTimeout(poll, 50);
            }
            initDomReady();
        }
        var IEContentLoaded = function(){
            if(CONTEXT.readyState != 'complete') {
                poll();
            } else {
                initDomReady();
            }
            CONTEXT.detachEvent('onreadystatechange', arguments.callee);
            CONTEXT.detachEvent('load', arguments.callee);
        }
        CONTEXT.attachEvent('onreadystatechange', IEContentLoaded);
        CONTEXT.attachEvent('load', IEContentLoaded);
    } else {
        // from <NAME>
        var oldonload = GLOBAL.onload;
        GLOBAL.onload = function(event) {
            initDomReady();
            if (typeof oldonload == 'function') {
                oldonload(event || GLOBAL.event);
            }
        }
    }
    
    /*---------------------------------------------------------------*
     *                      loading mechanism                        *
     *---------------------------------------------------------------*/
    
    // Adding a global handler to store records once all dependencies are loaded.
    dispatcher.listen(Event.COMPLETE, function(e){
        var list = pending[e.path];
        if(!list || !list.length) {
            return;
        }
        var i = list.length, j, current = list[--i];
        while(current) {
            j = current.fetch.length;
            while(j && j--) {
                if(current.fetch[j] != e.path) {
                    continue;
                }
                current.fetch.splice(j, 1);
                if(!current.fetch.length) {
                    storeRecord(current);
                    list.splice(i, 1);
                }
            }
            current = list[--i];
        }
    });
        
    /**Creates a proper URI from the dot notated path.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @return {String} URI to module file.
     */
    var createURI = function(path) {
        path = path.path || path;
        for(var key in config.repositories) {
            if(RegExp("^"+key).test(path)) {
                return config.repositories[key] + path.replace(/\./g, "/") + ".js";
            }
        }    
	    return config.root + "/" + path.replace(/\./g, "/") + ".js";
    },
    
    /**Loads all dependencies defined in the module record.
     * 
     * @param {Object} record Module data and builder method.
     * @return {Boolean} true when there where dependencies that needed to be loaded.
     */
    loadDependencies = function(record){
        if(!record.fetch || !record.fetch.length){
            return false;
        }
        var list = record.fetch, i = list.length, path;
        while(i--) {
            path = list[i];
            if(path.path) {
                list[i] = path.path;
            }
            if(registry[path]){
                list.splice(i, 1);
                continue;
            }
        }
        if(!list.length) {
            return false;
        }
        list = [].concat(list);
        i = list.length;
        while(i--) {
            path = list[i];
            if(pending[path]){
                list.splice(i, 1);
                pending[path].push(record);
            } else {
                pending[path] = [record];
            }
        }
        appendScripts(list, record.timeout);
        return true;
    },
    
    /**Iterates over a list of dot notated module paths and appends them to the scriptLocation
     * through a documentFragment.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @param {Number} timeout Timeout override in miliseconds
     */
    appendScripts = function(list, timeout){
        var i = list.length, path,
            fragment = document.createDocumentFragment();
        while(i--){
            path = list[i];
            scripts[path] = scriptFragment.cloneNode(true);
            scripts[path].src = createURI(path);
            fragment.appendChild(scripts[path]);
            log(Event.LOADING, {
                path: path,
                timeout: timeout
            });
        }
        HEAD.appendChild(fragment);
    },
    
    /*---------------------------------------------------------------*
     *                      killing mechanism                        *
     *---------------------------------------------------------------*/
    
    /**Recursively cleans up trail a timedout module leaves behind.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @param {String} message Error message, will be appended to script element in type attribute.
     * @return {void}
     */
    kill = function(path, message){
        var prefix = message ? "Dependency '"+path+"'" : "";
        message = message || "Timed out @ "+killers[path].timeout+"ms";
        log(Event.ERROR, {
            path: path,
            message: message,
            resolution: "failed silently"
        });
        var list = pending[path], 
            i = list.length;
        while(i--) {
            kill(list[i].path, message);
            delete list[i];
        }
        delete pending[path];
    }
    
    /**Initializes a timeout procedure, calling kill() when timeout is achieved.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @param {Number} timeout Timeout override in miliseconds
     * @return {Boolean} false if timeout mechanism is disabled through config.timeout
     */
    dispatcher.listen(Event.LOADING, function(e){
        if(!config.timeout) {
            return false;
        }
        killers[e.path] = {
            timeout: e.timeout,
            timeoutID: setTimeout(function(){
                kill(e.path);
            }, e.timeout)
        };
        
        return true;
    })
    
    /**Shuts down the timeout procedure once the module has loaded
     * (but not yet stored) and cleans up killer footprint.
     */
    dispatcher.listen(Event.LOADED, function(e){
        if (killers[e.path]) {
            clearInterval(killers[e.path].timeoutID);
            delete killers[e.path];
        }
    });
        
    /*---------------------------------------------------------------*
     *                       record mechanism                        *
     *---------------------------------------------------------------*/
    
    /**Filters the module name from dot notated path.
     * 
     * @param {String} path Dot notated path to the module, relative to mikado root or repository
     * @return {String} name of the module.
     */
    var getNameFromPath = function(path){
        path = path.path || path;
        return path ? path.split(".").pop() : null;
    },
    
    /**Checks dependencies on module name and forces relative set module paths
     * defined in config.force map.
     * 
     * @param {Array} list List of module paths (dot notated)
     * @return {void}
     */
    enforce = function(list) {
        if(!list){
            return;
        }
        var i = list.length, name;
        while(i--) {
            if(list[i].path && 'when' in list[i] && !list[i].when) {
                list.splice(i, 1);
                continue;
            }
            if(list[i].path) {
                name = getNameFromPath(list[i].path);
                if(name && config.force[name]) {
                    list[i].path = config.force[name];
                }
                continue;
            }
            name = getNameFromPath(list[i]);
            if(name && config.force[name]) {
                list[i] = config.force[name];
            }
        }
        return list;
    },
    
    /**Concatenates record.fetch and record.include arrays to be used for
     * dependency loading.
     * 
     * @param {Object} record Module data and builder method.
     * @return {void}
     */
    setRequired = function(record){
        record.fetch = record.fetch || [];
        enforce(record.fetch);
        enforce(record.include);
        if(record.include instanceof Array) {
            record.fetch = record.fetch.concat(record.include);
        }
        return record.fetch;
    },
    
    /**Reformats allowed array to object map.
     * 
     * @param {Object} record
     * @return {void}
     */
    setAllowed = function(record){
        if(record.allow instanceof Array) {
            var list = record.allow, path;
            record.allow = {};
            while((path = list.pop())) {
                record.allow[path] = true;
            }
        }
    },
    
    /**Builds library object containing 'include' dependency modules.
     * 
     * @param {Object} targetRecord
     * @return {void}
     */
    setLibraries = function(targetRecord) {
        var list = targetRecord.include,
            i = list.length,
            client = targetRecord.path,
            record, path, name;
        targetRecord.library = {};
        while(i--) {
            alias = list[i].alias;
            path = list[i].path || list[i];
            record = registry[path];
            if(record.allow && !record.allow[client]) {
                targetRecord.library[alias||record.name] = "Access denied!";
                log(Event.ERROR, {
                    path: targetRecord.path,
                    message: "Disallowed acces to '"+path+"'",
                    resolution: "failed silently"
                });
                continue;
            }
            if(record.traits.domTool) {
                targetRecord.traits.domTool = true;
            }
            if (record.module) {
                targetRecord.library[alias||record.name] = record.module;
            }
            alias = null;
        }
    },
    
    /**Reformats record for convenience and starts dependency loading if required.
     * Disables the killer mechanism for this specific record.
     * When there are no dependencies, storeRecord() is called right away.
     * 
     * @param {Object} record
     * @return {void}
     */
    processRecord = function(record){
        
        record.name = record.name || getNameFromPath(record.path);
        record.traits || (record.traits = {});
        record.traits.path = record.path;
        record.traits.name = record.name;
        record.timeout = record.timeout ? Math.max(timeout, config.timeout) : config.timeout;
        
        setAllowed(record);
        setRequired(record);
        log(Event.LOADED, record.traits);
        if(!loadDependencies(record)) {
            storeRecord(record);
        }
    },
    
    /**Builds the final record or delays building till DOM is ready depending on record.traits.domBuild setting.
     * Notifies any module listeners that rely on current record.
     * 
     * @param {Object} record
     * @return {void}
     */
    storeRecord = function(record){
        if(record.traits.domBuild && !domLoaded) {
            dispatcher.listen(Event.DOMREADY, function(){
                storeRecord(record);
            });
            return false;
{'fraction_non_alphanumeric': 0.1496338265875049, 'fraction_numerical': 0.0017178505771375185, 'mean_word_length': 1.3612040133779264, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6498895', 'n_tokens_mistral': 8704, 'n_tokens_neox': 8429, 'n_words': 2171}