// --------------------------------------------------------------//
// Kr.Core.js
//
// rk, 22.01.2010
//
// !!!!!!! depending on prototype framework !!!!!!!
//
// --------------------------------------------------------------//
// Classes:
// - Kr
// - Kr.Config      (configuration conatiner with default config)
// - Kr.Helper      (helper functions)
// - Kr.Script      (ondemand script loading)
// - Kr.Log         (logging with dependency injection)
// --------------------------------------------------------------//

// --------------------------------------------------------------//
// Class Kr
// rk, 22.01.2010
// Superclass for all kronomy scripts

// Instance
var Kr = Class.create({

    // Instance objects
    initialize: function() {

        Kr.throwNoInstanceError(this.constructor.className);

    }

});

// Static objects
Object.extend(Kr, {

    // throw error if any Kr class is tried to be instanced
    throwNoInstanceError: function(className) {

        var msg = 'Class "' + className + '" is not intended to be instanced!!';

        try {
            throw new function() { alert(msg); };
        }
        catch(e) {
            throw new Error(msg);
        }

    },

    init: function(parameters) {

        Kr.parameters = parameters;

        Kr.loadLogger();

    },

    // Name of this class
    className: 'Kr',

    parameters: {},

    loadLogger: function(Logger) {

        if (typeof Logger != 'object') {

            if (typeof Kr.Config.Logger == 'object') {

                Logger = Kr.Config.Logger;

            }

        }

        if (typeof Logger == 'object' && typeof Logger.enabled == 'boolean' &&
            Logger.enabled === true && Object.isString(Logger.script)) {

            var krLogScriptParams = {

                script:         Logger.script,
                varsToTest:     ['Kr.Log.info', 'Kr.Log.trace', 'Kr.Log.error'],
                forceLoad:      true

            };

            if (Object.isFunction(Kr.parameters.onSuccess)) krLogScriptParams.onSuccess = Kr.parameters.onSuccess;

            new Kr.Script(krLogScriptParams);

        }

    }

});

// End Kr
// --------------------------------------------------------------//

// --------------------------------------------------------------//
// Class Kr.Helper
// rk, 22.01.2010
// Helper functions

// Instance objects
Kr.Helper = Class.create(Kr, {

    initialize: function($super) {

        $super();

    }

});

// Static objects
Object.extend(Kr.Helper, {

    // name of this class
    // overwritten
    className: 'Kr.Helper',

    // return false if on called on live system based on Kr.config.liveDomainNames or parameter liveDomainNames
    // mostly used for console.info which should not log anything on live systems
    // @param:  array/string   liveDomainNames     [ 'domain1.com', 'domain2.com' ] or 'domain.com'
    isDev: function(liveDomainNames) {

        // return true;

        try
        {

            if (Object.isUndefined(window.console) || Object.isUndefined(document.domain)) {
                return false;
            }

            if ((Object.isArray(liveDomainNames) === false && Object.isString(liveDomainNames) === false) &&
                (Object.isArray(Kr.Config.liveDomainNames) === false && Object.isString(Kr.Config.liveDomainNames) === false)) {

                return false;
            }

            // If function parameter liveDomainNames is missing assign from Kr.config.liveDomainNames
            if (Object.isUndefined(liveDomainNames)) liveDomainNames = Kr.Config.liveDomainNames;

            // If parameter liveDomainNames is neither array nor string return false
            if (Object.isArray(liveDomainNames) === false &&
                Object.isString(liveDomainNames) === false) {

                return false;

            }

            // liveDomainNames is a string than convert to array for iteration
            if (Object.isString(liveDomainNames)) liveDomainNames = [ liveDomainNames ];

            var domainNotFound = true;

            liveDomainNames.each(function(krDomainName) {

                if (document.domain.indexOf(krDomainName) != -1) {
                    domainNotFound = false;
                }

            });

            return domainNotFound;

        }
        catch(e) {
            return false;
        }

    }


});

// End Kr.Helper
// --------------------------------------------------------------//


// --------------------------------------------------------------//
// Class Kr.Config
// rk, 22.01.2010
// Basic configuration handling

// Instance objects
Kr.Config = Class.create(Kr, {

    initialize: function($super) {

        $super();

    }

});

// Static objects
Object.extend(Kr.Config, {

    // name of this class
    // overwritten
    className: 'Kr.Config',

    // required configuration
    // ToDo: put the default settings in Kr.Config !!!!!
    
    liveDomainNames: [                  // default for kronomy / iwa
        'iwa-tech.com',
        'kronomy.com',
        'www.aws.iwa-tech'
    ],

    krScriptPath: '_jscript/kr/',       // default for kronomy / iwa
    jsScriptPath: '_jscript/'           // default for kronomy / iwa

});

Object.extend(Kr.Config, {

    Logger: {                           // default for kronomy / iwa
        enabled:    true,
        script:     Kr.Config.krScriptPath + 'Kr.Log.Console.js'
    },

    // add a config key/value pair
    // @param:  key     string
    // @param:  value   mixed
    add: function(obj) {

        if (typeof obj != 'object') throw new Error('Only objects can be added to Kr.Config!!');

        Object.extend(Kr.Config, obj);
    }

});

// --------------------------------------------------------------//
// Class Kr.Log
// rk, 22.01.2010
// Logging with dependency injection

// Instance objects
Kr.Log = Class.create(Kr, {

    initialize: function($super) {

        $super();

    }

});

// Static objects
Object.extend(Kr.Log, {

    className: 'Kr.Log',

    // Interface like function description to avoid error when called without injected logger
    info: function(message)
    {
        // console.log(message);
    },

    trace: function() {

    },

    error: function(m, url, l) {
        // console.error(m);
    }

});

// End Kr.Log
// --------------------------------------------------------------//

// --------------------------------------------------------------//
// Class Kr.Script
// rk, 22.01.2010
// ondemand script loading
// XXX Todo: Doku parameters

// Instance Objects
Kr.Script = Class.create({

    _parameters:    undefined,
    _status:        undefined,
    _result:        undefined,
    _savedVars:     undefined,

    initialize: function(parameters) {

        if (typeof parameters != 'object') {

            throw new Error('No parameters for Kr.Script defined!!');

        }

        this._parameters = parameters;
        this._status = Kr.Script.Status.START;

        this.getScript();

    },

    saveVars: function() {

        if (Object.isArray(this._parameters.varsToTest) === false) return;

        this._savedVars = [];

        this._parameters.varsToTest.each(function(varToTest, index) {

            this._savedVars[index] = eval(varToTest);

        }.bind(this));

    },

    varsChanged: function() {

        var allVarsChanged = true;

        this._savedVars.each(function(savedVar, index) {

            if (savedVar == eval(this._parameters.varsToTest[index])) allVarsChanged = false;

        }.bind(this));

        return allVarsChanged;

    },

    recurse: function(countTry) {

        // myThis = this;

        if (Object.isUndefined(countTry)) countTry = 0;

        var varsExists = Kr.Script.varsExists(this._parameters.varsToTest);
        if (this._parameters.forceLoad) var varsChanged = this.varsChanged();

        ++countTry;

        if (countTry >= this._parameters.maxNumTry &&
           ((this._parameters.forceLoad === false && varsExists === false) ||
            (this._parameters.forceLoad && varsChanged === false))) {

            this._result = Kr.Script.Status.ERROR;

            if (Object.isFunction(this._parameters.onError)) {
                this._parameters.onError(this);
            }

        }
        else if ((this._parameters.forceLoad === false && varsExists === false) ||
                 (this._parameters.forceLoad && varsChanged === false)) {

            if (countTry < this._parameters.maxNumTry) setTimeout( function() { this.recurse(countTry) }.bind(this), this._parameters.interval );

        }
        else {

            this._result = Kr.Script.Status.SUCCESS;

            if (Object.isFunction(this._parameters.onSuccess)) this._parameters.onSuccess(this);

        }

    },

    executeCallBack: function() {

        if (Object.isUndefined(this._result) || typeof this._parameters != 'object') {

            throw new Error('Kr.Script.executeCallBack cannot be executed!!');

        }

        if (Object.isArray(this._parameters.varsToTest) === false && this._parameters.forceLoad === false) {

            if (this._result === true && Object.isFunction(this._parameters.onSuccess)) {

                this._parameters.onSuccess(this);

            }

            if (this._result === false && Object.isFunction(this._parameters.onError)) {

                this._parameters.onError(this);

            }

        }

        if (this._result === true && Object.isFunction(this._parameters.onSuccess) === false) {
            return;
        }

        if (this._result === false && Object.isFunction(this._parameters.onError) === false) {
            return;
        }

        if (this._result === true && Object.isArray(this._parameters.varsToTest)) {

            this.recurse(0);

        }
        else if (this._result === false && Object.isFunction(this._parameters.onError)) {

            this._status = Kr.Script.Status.ERROR;
            this._parameters.onError(this);

        }

    },

    getStatus: function() {

        return this._status;

    },

    getScript: function() {

        if (typeof this._parameters != 'object') throw new Error('Kr.Script._parameters is not a object!!');
        if (Object.isString(this._parameters.script) === false) throw new Error('Kr.Script._parameters.script is not a string!!');

        if (Object.isNumber(this._parameters.interval) === false) this._parameters.interval = Kr.Script.defaultInterval;
        if (Object.isNumber(this._parameters.maxNumTry) === false) this._parameters.maxNumTry = Kr.Script.defaultMaxNumTry;
        if (Object.isString(this._parameters.extension) === false) this._parameters.extension = Kr.Script.defaultExtension;
        if (Object.isFunction(this._parameters.onError) === false) this._parameters.onError = Kr.Script.defaultOnError;
        if (Object.isFunction(this._parameters.onSuccess) === false) this._parameters.onSuccess = Kr.Script.defaultOnSuccess;
        if (typeof this._parameters.forceLoad != 'boolean') this._parameters.forceLoad = Kr.Script.defaultForceLoad;

        if (Kr.Script.varsExists(this._parameters.varsToTest) && this._parameters.forceLoad === false) {
            this._parameters.onSuccess(this);
            return true;
        }

        var scriptElement = Kr.Script.isScriptTagInDom(this._parameters.script, this._parameters.extension);

        if (typeof scriptElement == 'object' && this._parameters.forceLoad === false) {
            this._parameters.onSuccess(this);
            return true;
        }

        if (this._parameters.forceLoad) this.saveVars();

        this._status = Kr.Script.Status.LOADING;

        if (typeof scriptElement == 'object') {

            Kr.Script.removeScriptTagByElement(scriptElement);
            this._result = Kr.Script.insertScriptTagByElement(scriptElement);

        }
        else {

            this._result = Kr.Script.insertScriptTagByPath(this._parameters.script);
        }

        this.executeCallBack();

        return this._result;

    }

});

// Static objects
Object.extend(Kr.Script, {

    className             : 'Kr.Script',

    Status                : {
                                START   : 0,
                                LOADING : 1,
                                SUCCESS : 2,
                                ERROR   : 3
                            },

    defaultInterval       : 50,
    defaultMaxNumTry      : 50,
    defaultForceLoad      : false,
    defaultOwerWrite      : false,
    defaultExtension      : 'js',
    defaultOnSuccess      : function(krScriptObj) {
                                Kr.Log.info(krScriptObj._parameters.script + ' loaded.');
                            },
    defaultOnError        : function(krScriptObj) {
                                throw new Error(krScriptObj._parameters.script + ' could not be loaded!!');
                            },

    varsExists: function(varsToTest) {

        if (Object.isArray(varsToTest) === false) return false;

        // var allVarsExist = true;

        varsToTest.each(function(varToTest) {

            try {
                result = Object.isUndefined(eval(varToTest)) === false;
            }
            catch(e)
            {
                result = false;
            }
            // finally {
               //  if (result === false) allVarsExist = false;
            // }

        });

        return result;

    },

    getRawScriptName: function(script, extension) {

        if (Object.isString(script) === false) return '';
        if (Object.isString(extension) === false) extension = Kr.Script.defaultExtension;

        script = script.split('/');
        script = script[script.length - 1];
        script = script.split('.' + extension)[0];

        return script;

    },

    isScriptTagInDom: function(searchScript, extension) {

        var found = false;
        var foundScript = null;

        if (Object.isString(searchScript) === false) return false;
        if (Object.isString(extension) === false) extension = Kr.Script.defaultExtension;

        searchScript = Kr.Script.getRawScriptName(searchScript, extension);

        $$('script[src]').each(function(scriptElement) {

            if (scriptElement.type.indexOf('javascript') != -1) {
                foundScript = Kr.Script.getRawScriptName(scriptElement.src, extension);
            }

            if (searchScript == foundScript) {
                found = scriptElement;
            }

        });

        return found;

    },

    insertScriptTagByPath: function(script) {

        if (Object.isString(script) === false) return false;

        try {
            Element.insert($(document.getElementsByTagName('head')[0]), new Element('script', { 'type': 'text/javascript', src: script }));
            return true;
        }
        catch(e)
        {
            Kr.Log.error(e);
            return false;
        }

    },

    insertScriptTagByElement: function(element) {

        if (typeof element != 'object') return false;

        try {

            Element.insert($(document.getElementsByTagName('head')[0]), element);
            return true;

        }
        catch(e)
        {
            Kr.Log.error(e);
            return false;
        }

    },

    removeScriptTagByPath: function(script, extension) {

        if (Object.isString(script) === false) return false;

        try {
            element = Kr.Script.isScriptTagInDom(script, extension);

            $(element).parentNode.removeChild(element);
            return true;
        }
        catch(e)
        {
            Kr.Log.error(e);
            return false;
        }

    },

    removeScriptTagByElement: function(element) {

        if (typeof element != 'object') return false;

        try {
            $(element).parentNode.removeChild(element);
            return true;
        }
        catch(e)
        {
            Kr.Log.error(e);
            return false;
        }

    }

});

// End Kr.Script
// --------------------------------------------------------------//

// --------------------------------------------------------------//
// End Kr.Core.js
// --------------------------------------------------------------//
