/**
 * This file contains utility functions and other things that will come in
 * handy for almost all applications. Full feature inclusion configurability 
 * included!
 * 
 * If adding functionality to this file:
 * <ul>
 *   <li>
 *     Make sure the user (programmer) can easily choose to omit features by 
 *     using booleans in the 'utils' object.
 *   </li>
 *   <li>
 *     Be sure to add functions to the proper namespace, or preferrably put them
 *     in a prototype if it serves the purpose.</li>
 *   </li>
 * </ul>
 * 
 * @requires querystring_parameters.js
 * @requires include_keyhandler_strict.js
 * @requires keyhandler_*.js
 * @author NBJ
 */

// If accedobroadband namespace isn't declared, declare it
if (typeof(accedobroadband) === 'undefined'){
    accedobroadband = {};
}


// The utils object contains functionality that can be used generally in Accedo
// js applications.
accedobroadband.utils = {

    // Define customizable, box-safe debug output operations using 'out.log()'?
    useCustomDebugOutput:              true,
    
    // Automatically include a keyhandler using include_keyhandler_strict.js?
    autoincludeKeyhandler:             false,
    
    // Use objectified keyhandler?
    useObjectifiedKeyhandler:          false, // NOT YET IMPLEMENTED!
    
    // Automatically include querystring_parameters.js?
    autoincludeQuerystringParameters:  true,
    
    // Always instantiate all querystring parameters in a global 'params'
    // scope for easy access (if possible)?
    useAutoParams:                     true,
        
    // Use the $('myElement') abstraction?
    // (Will try and return document['all']['myElement'] if getElementById doesn't
    // exist, else document.images['myImg'] is returned)
    useDollarGELAbstraction:           true,
    
    // Extend the Object prototype with useful functions such as an 
    // object examiner and an attribute printer?
    useObjectExtensions:               true,
    
    // Add some Dom manipulation functions to accedobroadband.utils?
    useDomAbstractions:                true,
    
    // Implement functions that are useful for working with namespaces?
    useNamespaceAbstractions:          true,
    
    
    /**
     * Initiates the utils object
     * @author NBJ
     */
    init: function(){        
        
        
        
        // If user wants to use the advanced debug/output module, implement it
        if (this.useCustomDebugOutput){
            
            // The debug object, 
            this.debug = {
                
                // List of log modules (functions that take a String)
                logModules:            [],
                
                
                /**
                 * Add a custon output module for debug messages. The latest added
                 * module will be given the highest priority automatically.
                 * 
                 * @param fun [Function] the function to call with the log message
                 * @author NBJ
                 */
                addLogModule: function(fun){
                    this.logModules.unshift(fun);
                },
                
                // Debug message priority levels
                levels: {
                    INFO:           -50,
                    DEBUG:          -40,
                    WARNING:        -30,
                    ERROR:          -20,
                    FATAL:          -10,
                    OFF:              0
                },
                
                // What is the current debug level? (default: DEBUG)
                level:                 null
                
            }
            
            // Create a global 'debug' namespace for easy access
            if (typeof(debug) === 'undefined'){
                window.debug = accedobroadband.utils.debug;
            }
            
            // Set DEBUG as default debug level
            this.debug.level = this.debug.levels.DEBUG;
            
                    
            // Add default log modules if they exist
            if (typeof(alert) !== 'undefined'){
                this.debug.addLogModule(function(msg){ alert(msg); });
            }

            if (typeof(console) !== 'undefined'){
                this.debug.addLogModule(console.log);
            }
            
            
            // Fire up the output module
            if (typeof(out) === 'undefined'){
                window.out = {};
            }
            
            /**
             * Debug log, can be switched off by setting the degub.level to 
             * debug.levels.OFF.
             * 
             * This function will send the log message to the highest 
             * prioritized working output module in the debugOutputModules
             * list.
             * 
             * @param data [String|Object] message to log, Object to print
             * @param priority [int] priority of log message (default is DEBUG)
             * @author NBJ
             */ 
            window.out.log = function(data, priority){
                
                // If msg is an object, let's examine and print it
            	if (typeof(data) === 'object') {
                    data = data.examine();
            	}
                
                // If logging is off, don't do anything
                if (accedobroadband.utils.debug.level === accedobroadband.utils.debug.levels.OFF){
                    return;
                }
                
                if (typeof(priority) === 'undefined'){
                    priority = accedobroadband.utils.debug.levels.DEBUG;
                }
                
                if (priority >= accedobroadband.utils.debug.level){
                    for (var outputModule in accedobroadband.utils.debug.logModules){
                        if (typeof(outputModule) !== 'undefined'){
                            try {
                                accedobroadband.utils.debug.logModules[outputModule](data);
                                return;
                            } catch(ex) {}
                        }
                    }                        
                }
                
            };
            
        }
        
        



                
        // If user wants to extend the Object prototype with some handy 
        // functions, install them here.
        if (this.useObjectExtensions){
            
            /**
             * Print all key-value pairs in an object
             */
            Object.prototype.examine = function() {
                var properties = "";
                for (name in this) {
                    var val = this[name];
                    
                    if (typeof(val) === "function"){
                        val = "function()"
                    }
                    
                    properties += name + ": " + val + "\n";
                }
                
                return properties;
            };
            
            /**
             * Print all attributes in an object
             */
            Object.prototype.printAttributes = function() {
                for (var name in this) {
                    out.log(name, debug.levels.DEBUG);
                }
            };
        }
        
        
        
        // If user wants functions for working with namespaces, install them
        // here.
        if (this.useNamespaceAbstractions){
            
            // Define addNamespace utility function if not already present
            if (typeof(window.addNamespace) === "undefined") {
                window.addNamespace = function(namespace) {
                    var o = window;
                    var d = namespace.split(".");
                    
                    for (var j=0 ; j<d.length; j=j+1) {
                        o[d[j]] = o[d[j]] || {};
                        o = o[d[j]];
                    }
                };
            }
            
            
            /**
             * Add a <i>parent</i> variable to all child objects in order to facilitate
             * calls upward in namespace hierarchies. This function is recursive 
             * and will go down all the way to the bottom of a namespace hierarchy
             * and add a <i>parent</i> variable to each object's child.
             * 
             * @author NBJ
             */
            Object.prototype.useParentVariable = function(){
                for (var child in this){
                    // Check for null, as null is also an object
                    if (this[child] !== null){
                        // It's not null...
                        if (typeof(this[child]) !== 'undefined'){
                            // It's not undefined...
                            if (typeof(this[child]) === 'object'){
                                // It's an object!
                                if (typeof(this[child].splice) === 'undefined'){
                                    // It's not an array...
                                    if (typeof(this[child].parent) === 'undefined'){
                                        // Its parent has not yet been set...
                                        if (this[child] !== this.parent){
                                            // It's not the child's parent (this)
                                            
                                            // Now we're pretty sure the current child 
                                            // is an object we want to add the parent 
                                            // variable to
                                            this[child].parent = this;
                                            this[child].useParentVariable();
                                        }                                
                                    }
                                }                               
                            }
                        }
                    }
                }
            };
            
        }


        
        // If user wants the $('elm') abstraction, implement it
        if (this.useDollarGELAbstraction){
            /**
             * getElementById abstraction deluxe
             * (will use document.all[] if getElementById doesn't exist,
             * or document.images[] for dom0)
             * 
             * Usage: 
             * <p>var anElement = $('myElement');</p>
             * insead of:
             * <p>var anElement = document.getElementById('myElement');</p>
             * 
             * @param [String(s) | DOMObject(s)] Any number of strings or objects that provide 
             *                                   element id's or elements.
             * 
             * @return [DOMObject | Array] The element (if only one specified) or array of 
             *                             elements (if multiple).
             *
             * @author NBJ
             */
            window.$ = function() {
                var elements = new Array();
                for (var i = 0; i < arguments.length; i++) {
                    
                    var element = arguments[i];
                    
                    if (typeof(element) === 'string'){
                        if (typeof(document.getElementById) !== 'undefined') {
                            element = document.getElementById(element);
                        } else if (typeof(document.all) !== 'undefined') {
                            element = document.all[element];
                        } else if (typeof(document.images) !== 'undefined') {
                            element = document.images[element];
                        }
                    }
                    
                    if (arguments.length === 1){
                        return element;
                    }
                    
                    elements.push(element);
                }
                
                return elements;
            };
        }


    
     
        // If we want to use some handy DOM methods...
        if (this.useDomAbstractions){
            
            /**
             * Create an element and append it to parentNode if possible, else document.write it.
             * @param parentNode [DOMObject] the node to which the element will be added
             * @param type [String] element type (div, p, img, etc...)
             * @param attributes [HashMap] hash of the attributes to add to the element
             * @return [DOMObject | String] the created object (or generated string if
             *                                                  createElement fails)
             */
            window.createElement = function(parentNode, type, attributes){
                
                if (document.createElement !== 'undefined'){
                    var elm = document.createElement(type);
                    for (var attr in attributes){
                        if (typeof(attributes[attr]) !== 'function'){
                            elm.setAttribute(attr, attributes[attr]);
                        }
                    }
                    if (typeof(parentNode) !== 'undefined'){
                        parentNode.appendChild(elm);
                        return elm;
                    }
                } else {
                    // If document.createElement failed, build a html tag string
                    var elm = '<' + type + ' ';
                    for (var attr in attributes){
                        if (typeof(attributes[attr]) !== 'function'){
                            elm += attr + '="' + attributes[attr] + '" ';
                        }
                    }
                    elm += '/>';
                    
                    // Add the string to the parent node
                    if (parentNode.innerHTML !== 'undefined'){
                        parentNode.innerHTML = parentNode.innerHTML + elm;
                    } else {
                        // If innerHTML failed, just write the element onto the document
                        document.write(elm);
                    }
                    
                    return elm;
                }
            }; 
        }
        
        
        // Do we want to auto-include querystring_parameters.js?
        if (this.autoincludeQuerystringParameters){
            var head = document.getElementsByTagName('head')[0];

            if (typeof(head) !== 'undefined'){
                createElement(head, 'script', {
                    'type':    'text/javascript',
                    'src':     '../../js/querystring_parameters.js'
                });
            } else {
                // We're not working with html... Manual inclusion must be made,
                // so let's abort here.
            }
        }

        
        
        // Initiate the keyhandler namespace
        accedobroadband.keyhandler = {};

        // Do we want to auto-include keyhandler?
        if (this.autoincludeKeyhandler){
                        
            // Create a shortcut to it
            window.keyhandler = accedobroadband.keyhandler;
            
            var includeKeyhandler = '../../js/include_keyhandler_strict.js';
            
            if (this.useObjectifiedKeyhandler){
                includeKeyhandler = '../../js/include_keyhandler_object.js';
            }
            
            var head = document.getElementsByTagName('head')[0];
            
            if (typeof(head) !== 'undefined'){        
                createElement(head, 'script', {
                    'type':    'text/javascript',
                    'src':     includeKeyhandler
                });
            
                if (this.useObjectifiedKeyhandler){
                    keyhandler.init();
                } else {
                    initiateKeyHandler();
                }
            } else {
                // We're not working with html... Manual inclusion must be made,
                // so let's abort here.
            }
        }
        
        
        // If user wants to use the global params for querystring parameters,
        // make it happen!
        if (this.useAutoParams){
            if (typeof(accedobroadband.querystring) !== 'undefined'){
                // If using a querystring_parameters.js with support for useValues(), for ease 
                // of fetching parameters when writing code, always initiate a params scope 
                // that contains all parameters
                if (typeof(accedobroadband.querystring.useValues) !== 'undefined'){
                    window.params = accedobroadband.querystring.useValues(null, {});
                    // All parameters are now available in the global params scope, e.g.
                    // params.score;
                    // params.lang; etc.
                    // no more: var lang = accedobroadband.querystring.getValue("lang");
                }
            }
        }
    }
        
};


accedobroadband.utils.init();
