/**
 * File: jLoad.js
 * Version: 0.1 BETA
 * Author: Jens Franke
 * Copyright: Sandstein Neue Medien GmbH
 *
 * Mit jLoad koennen Javascripte bzw. JS-Klassen automatisch geladen werden.
 * Mittels des Include-Waechters werden die Scripte bei Bedarf nachgeladen.
 */

var jLoad = {
    baseUrl: "/",
    includeFuncs: new Array(),

    /**
     * Funktion zum Hinzufuegen der zu ladenden Scripte.
     */
    addScript: function (_id, _jsSource, funcs, _options) {
        if (!_id || _id === null || _id == "undefined" || _id == "") {
            _id = "S" + (Date.parse() / 1000);
        }

        if (!funcs || funcs === null || funcs == "undefined") {
            funcs = new Array();
        }

        if (!_options || _options === null || _options == "undefined") {
            _options = new Object();
        }

        // Parameter mergen.
        var options = {
            fullload:false,
            autoload:false,
            makeTag:false,
            loaded:false
        };

        var mergeOptions = new Array("fullload", "autoload", "makeTag", "loaded");

        for (var i = 0; i < mergeOptions.length; i++) {
            var name = mergeOptions[i];
            options[name] = (_options !== undefined && _options[name] !== undefined) ? _options[name] : options[name];
        }

        // Statusobjekt anlegen.
        var status = {
            loaded:false
        };

        // Checken, ob Script schon existiert.
        for (i = 0; i < this.includeFuncs.length; i++) {
            if (this.includeFuncs[i].jsSource == _jsSource) {
                return false;
            }
        }

        // Script der Klassen hinzufuegen.
        S = {
            jsId:_id,
            jsSource:_jsSource,
            jsFuncs:funcs,
            jsOptions:options,
            jsStatus:status
        };

        this.includeFuncs.push(S);


        // Bei Option autoload, den Script gleich laden.
        if (options.autoload != "undefined") {
            if (options.autoload === true) {
                this.__loadScript(S, function () {}, true);
            }
        }
        return true;
    },

    /**
     * Holt ein Scriptobjekt aus dem Stack anhand des Sources.
     */
    getScriptBySource: function (_jsSource) {
        for (var i = 0; i < this.includeFuncs.length; i++) {
            if (this.includeFuncs[i].jsSource == _jsSource) {
                return this.includeFuncs[i];
            }
        }
        return false;
    },

    /**
     * Request-Funktion zum dynamischen Nachladen.
     */
    __getXmlHttpObj: function () {
        if (typeof(XMLHttpRequest) != "undefined") {
            return new XMLHttpRequest();
        }

        var axO = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];
        var i;

        for (i=0; i<axO.length; i++) {
            try {
                return new ActiveXObject(axO[i]);
            }
            catch(e) {
            }
        }
        return null;
    },

    /**
     * Funktion erzeugt Requests auf Scripte und uebersetzt sie.
     */
    __loadScript: function (S, onReadyCode) {
        if (S === false || S.jsStatus.loaded === true) {
            return false;
        }

        oXML = this.__getXmlHttpObj();

        try {
            var thisBaseUrl = "";

            if (S.jsSource.charAt(0) != this.baseUrl) {
                thisBaseUrl = this.baseUrl;
            }

            oXML.open("GET", thisBaseUrl + S.jsSource, false);
            oXML.send(null);
        }
        catch (e) {
            return false;
        }

        //oXML.onreadystatechange = function () {
            //if (oXML.readyState == 4) {
                if (S.jsOptions.makeTag === false) {
                    eval(oXML.responseText);
                }
                else {
                    SE = document.createElement("script");
                    SE.setAttribute("type", "text/javascript");
                    SE.setAttribute("charset", "UTF-8");
                    //SE.innerHTML = oXML.responseText;
                    //SE.appendChild(document.createTextNode(oXML.responseText));
                    SE.text = oXML.responseText;
                    HE = document.getElementsByTagName("head")[0];
                    HE.appendChild(SE);
                }

                S.jsStatus.loaded = true;

                for (var i = 0; i < S.jsFuncs.length; i++) {
                    window[S.jsFuncs[i].name] = eval(S.jsFuncs[i].name);
                }
                return onReadyCode();
            //}
        //};
    },

    /**
     * Funktion holt sich die noch unbekannte Funktion und laed dazu das Scriptfile, falls dieses noch nicht da nicht.
     */
    exec: function (funcsToLoad, finishCode) {
        if (!finishCode || finishCode === null || finishCode == "undefined") {
            finishCode = new Function();
        }

        for (var sc = 0; sc < this.includeFuncs.length; sc++) {
            var S = this.includeFuncs[sc];
            // Fullload bei Angabe des 

            if (S.jsFuncs.length == 0) {
                for (var j = 0; j < funcsToLoad.length; j++) {
                    if (funcsToLoad[j] == S.jsId) {
                        this.__loadScript(S, function () {});
                    }
                }
            }            

            // Suche nach Definition.
            for (var i = 0; i < S.jsFuncs.length; i++) {
                var F = S.jsFuncs[i];
                // Checken, ob der Script geladen werden muss.
                var mustLoadScript = false;

                for (var j = 0; j < funcsToLoad.length; j++) {
                    if (funcsToLoad[j] == F.name) {
                        mustLoadScript = true;
                    }
                }

                if (mustLoadScript === true) {
                    // Vorerst entfernt. Problem: Die anonyme Funktion bekommt nicht die
                    // erforderlichen Parameter. Zudem muss ein Function-Stack her, um die
                    // schon geladenen Scripte / Objekte / Funktionen zwischenzuspeichern.
                    // Hinweis: Alle Attribute des THIS-Objects muessen.
                    //window[F.name] = function() {

                        var loadedCode = this.__loadScript(S, function () {
                            /*
                            var execCode = F.name + "(";
                            for (var i = 0; i < arguments.length; i++) {
                                execCode += "arguments[" + i + "],";
                            }

                            if (arguments.length > 0) {
                                execCode = execCode.substr(0, execCode.length - 1);
                            }

                            return eval(
                                (F.type == "object" ? "new " : "") +
                                execCode +
                                ")"
                            );
                            */
                        });
                        eval(loadedCode);
                    //};
                }
            }
        }
        finishCode();
    }
};
