/* $Id: OnDemandLoader.js,v 1.2 2009/05/06 19:18:12 cburck Exp $ */

var OnDemandLoader = {

    defaultPrefix : "/jscript/",
  
    loadedScripts : new Array(), // keeps track of what scripts have already been loaded
    loadingScripts : new Array(), // keeps track of what scripts are loading
    
    OutstandingLoads : 0,

    eventsRegister : new Array(), // keeps track of registered call back functions

    eventTypes : {
        Loads_Complete : "Called when all outstanding loads finish and no more are queued."
    },


    loadRequiredLibs : function (config) {

        var src;

        /* go through all the required libraries */
        for (var key in config) {

            /* get the expected location of the library */
            src = config[key].src;

            /* load the library */
            OnDemandLoader.loadScript(src);

        }

    },
    
    unloadScripts : function (scripts) {

        /* ensure we got an array here */
        if (typeof(scripts) != typeof(new Array())) {
            scripts = new Array(scripts);
        }

        var scriptElements = document.getElementsByTagName("script");        
        var src;

        /* loop through all script tags on the page */
        for (var i = 0; i < scriptElements.length; i++) {

            /* get the file of the script */
            src = scriptElements[i].getAttribute("src");

            /* check if the script tag is an external tag, or embedded script */
            if (src != null) {

                /* script tag is an external tag, so it may need to be removed */
                for (var j = 0; j < scripts.length; j++) {

                    /* see if the specified script matches */
                    if (src == scripts[j]) {

                        //                        console.log("Unloading " + src);
                        /* drop the element from the DOM */
                        scriptElements[i].parentNode.removeChild(scriptElements[i]);

                        /* tracking the changes */
                        OnDemandLoader.loadedScripts[src] = false;

                        j = scripts.length; /* only need to find one match */
                        
                    }
                }                
            }
        }
    },
    
    loadScriptAsync : function (scripts) { // loads the specified script into a script tag if the script is not already loaded

        // if the input object is not an array, make it into one
        if (typeof(script) != typeof(new Array())) {
            scripts = new Array(scripts);
        }

        for (var i = 0; i < scripts.length; i++) {
            
            /* spin off javascript 'thread' */
            setTimeout("OnDemandLoader.loadScript('" + scripts[i] + "');", 0);
                
        }
        
        
    },

    loadScript : function (scripts) {

        // if the input object is not an array, make it into one
        if (typeof(scripts) != typeof(new Array())) {
            scripts = new Array(scripts);
        }
        
        //console.log(scripts);
        //console.log(" Size of load: " + scripts.length);

        for (var i = 0; i < scripts.length; i++) {

            //console.log("Looking at scipt #" + i + " (" + scripts[i] + ")");
            OnDemandLoader.loadScript_Internal(scripts[i]);
          
        }
    },

    loadScript_Internal : function (script) {

            /* get full path to script */
            script = OnDemandLoader.defaultPrefix + script;

          /* make sure it is not already loaded or in the process of loading */
            if (OnDemandLoader.isScriptLoaded(script) === false && OnDemandLoader.isScriptLoading(script) === false) {

                //console.log("Loading: " + script);

                /* set the script as in the process of loading */
                OnDemandLoader.setLoading(script);

                // get the head tag from the document
                var head= document.getElementsByTagName('head')[0];

                // create a new script tag
                var scriptTag= document.createElement('script');

                // set the script location
                scriptTag.src= script;

                // set the script type
                scriptTag.setAttribute("language", "JavaScript"); // Keep IE happy
                scriptTag.type= 'text/JavaScript';

                // make sure IE and some other browsers get this notification
                scriptTag.onreadystatechange = function () {

                    if (this.readyState == 'loaded') {
                        //console.log("On ready state change: " + script);
                        /* wait a minute in case the loaded script starts spinning off additional loads */
                        setTimeout('OnDemandLoader.SignalLoaded("' + script + '");', 100);

                        return;
                    }

                    return;
                }

                // make sure every other browser gets the notice
                scriptTag.onload = function () {
                    //console.log("onLoad " + script);
                    /* wait a minute in case the loaded script starts spinning off additional loads */
                    setTimeout('OnDemandLoader.SignalLoaded("' + script + '");', 100);

                    return;
                }

                // keep track of how many outstanding loads are pending
                OnDemandLoader.OutstandingLoads++;
                //console.log("Outstanding Loads: " + OnDemandLoader.OutstandingLoads);

                // append the tag into the dom inside the head tag
                head.appendChild(scriptTag);

            }




    },

    SignalLoaded : function (script) {

        //console.log("SignalLoaded: " + script);

        /* script is loaded */
        OnDemandLoader.setLoaded(script);
        
        if (OnDemandLoader.isScriptLoading(script) === true) {

            /* clear the loading value so no other event can signal loaded */
            OnDemandLoader.resetLoading(script);

            //console.log("Script Ready: " + script + "@" + new Date());

            /* keeps track of total number of loading scripts */
            OnDemandLoader.OutstandingLoads--;

            //console.log("Remaining scripts: " + OnDemandLoader.OutstandingLoads);

            /* all outstanding loads have completed */
            if (OnDemandLoader.OutstandingLoads < 1) {

                /* error checking */
                if (OnDemandLoader.OutstandingLoads < 0) {

                    //console.log("Outstanding Loads count was less than 0: " + OnDemandLoader.OutstandingLoads);
                    OnDemandLoader.OutstandingLoads = 0;
                    
                }

                //console.log("All outstanding loads complete @" + new Date());
                OnDemandLoader.triggerEvent('Loads_Complete');
            }
            
        }
    },

    registerEventCallBack : function (eventName, parameters) {

        if (typeof OnDemandLoader.eventTypes[eventName] == 'undefined') {
            console.log("Warning: registering " + parameters['call'] + " for an event that is not valid: " + eventName);
        }

        if (typeof OnDemandLoader.eventsRegister[eventName] != typeof new Array()) {
            OnDemandLoader.eventsRegister[eventName] = new Array();
        }
        
        OnDemandLoader.eventsRegister[eventName].push(parameters);
        
    },

    triggerEvent : function (event) {

        /* make it a proper event */
        if (typeof event == 'string') {
            event = {
                name : event
            };
        }

        //console.log("Triggered event " + event.name);

        if (typeof OnDemandLoader.eventTypes[event['name']] == 'undefined') {
            console.log("Warning: triggered event is not valid: " + event['name']);
        }

        var eventCallBacks = OnDemandLoader.eventsRegister[event['name']];

        /* this will be repopulated with registrations that specify notification for future event instances */
        delete OnDemandLoader.eventsRegister[event['name']];

        /* no call backs */
        if (eventCallBacks == null || typeof eventCallBacks != typeof new Array()) {
            return;
        }

        /* loop through all the callbacks */
        for (var id = 0; id < eventCallBacks.size(); id++) {

            if (typeof eventCallBacks[id]['call'] == 'string') {

                eventCallBacks[id]['call'] = eval(eventCallBacks[id]['call']);
            }

            // trigger call back
            eventCallBacks[id]['call'](event);

            

            // reregister only if call back requests more than once
            if (eventCallBacks[id]['once'] == null || eventCallBacks[id]['once'] != true) {

                /* re-register event */
                OnDemandLoader.registerEventCallBack(event['name'], eventCallBacks[id]);

            }
            
        }

        return;
    },

    isScriptLoading : function (script) {
        
        /* see if it is in our list of loading scripts */
        if (typeof(OnDemandLoader.loadingScripts[script]) == "undefined" || OnDemandLoader.loadingScripts[script] == null) {

            /* if not in our list, assume the script is not loading, and cache that value */
            OnDemandLoader.resetLoading(script);

            //console.log("isScriptLoading: " + script + " undefined false");
            return false;
        }

        //console.log("isScriptLoading: " + script + " defined " + OnDemandLoader.loadingScripts[script]);
        /* return the cached loading status */
        return OnDemandLoader.loadingScripts[script];
        
    },

    isScriptLoaded : function (script) { // determines if a script is already loaded                 

        /* see if it is a script we know about yet */
        if (typeof(OnDemandLoader.loadedScripts[script]) == "undefined" || OnDemandLoader.loadedScripts[script] == null) {

            //console.log("isScriptLoaded: " + script + " undefined false");
            return false;
        }

        //console.log("isScriptLoaded: " + script + " defined " + OnDemandLoader.loadedScripts[script]);
        return OnDemandLoader.loadedScripts[script];
    },

    setLoaded : function (script) { // sets a script as loaded
        //console.log("Set loaded: " + script);
        OnDemandLoader.loadedScripts[script] = true;
    },

    setUnloaded : function (script) {
        //console.log("Set unloaded: " + script);
        OnDemandLoader.loadedScripts[script] = false;
    },

    setLoading : function (script) { // sets a script as loaded
        //console.log("Set loading: " + script);
        OnDemandLoader.loadingScripts[script] = true;
    },

    resetLoading : function (script) {
        //console.log("Reset loading: " + script);
        OnDemandLoader.loadingScripts[script] = false;
    },

    checkExistingScripts : function () { // determines if a script is already loaded
        

            /* look at all loaded scripts to see if it was loaded without the use of OnDemandLoader. */
            var elements = document.getElementsByTagName("script");
            var src;

            for (var i = 0; i < elements.length; i++) {

                src = elements.item(i).getAttribute("src");

                /* see if the src attribute is set */
                if (src != null) {
                    
                    /* as long as we are looking at it, might as well track it as if we loaded it */
                    OnDemandLoader.setLoaded(src);
                    
                }
            }

        
    },


    VersionCheck : {

        lib: function(libName, requiredVersion) {

            var libRef = window[libName];

            if((typeof(libRef) == 'undefined') || (OnDemandLoader.VersionCheck.convertVersionString(libRef.Version) <= OnDemandLoader.VersionCheck.convertVersionString(requiredVersion))) {
                //throw("Required: the " + libName + " JavaScript library >= " + requiredVersion);

                /* version is not OK */
                return false;
            }

            /* version is OK */
            return true;
        },

        requiredLibs : function (config) {

            var success = true;
            var versionRequired;

            /* go through all the required libraries */
            for (var key in config) {

                /* check the config for the specified required version */
                versionRequired = config[key].version;

                /* check the version */
                if (!OnDemandLoader.VersionCheck.lib(key, versionRequired)) {

                    /* at least one lib could not be found or could not have version verified */
                    success = false;
                }

            }

            return success;
        },

        convertVersionString : function (version) {

            /* get rid of any alpha numeric characters and periods */
            var versionNumber = version.replace(/_.*|\./g, '');

            /* make sure correct number of digits in version number */
            var padding;
            for (var i = 0; i < (8-versionNumber.length); i++) {
                padding = padding + '0';
            }

            /* pad with up to eight zeroes */
            versionNumber = parseInt(versionNumber + padding);

            /* return that number baby */
            return versionNumber;
            
        }
    }

};

// If the JavascriptDirectory is set, redefine the default script directory in the loader.
if (typeof(JavascriptDirectory) != "undefined") {
    OnDemandLoader.defaultPrefix = JavascriptDirectory;
}
OnDemandLoader.checkExistingScripts();