/**
 * Singular CORE
 *
 * Client application
 */
var App = {
    version: '1.1.3',
    
    config: {
        basePath: '/',

        ajax: {
            useDefaultEventHandlers: true,

            before: function (xhr, settings) {
                preloader = $('<div id="default-preloader">Loading...</div>');

                $(preloader).css({
                    display: 'none',
                    position: 'fixed',
                    zIndex: '100000000000',
                    width: '200px',
                    border: '1px solid #777',
                    backgroundImage: 'url(' + App.assets.theme.image('/preloader/default-preloader-bg.gif') + ')',
                    textAlign: 'center',
                    padding: '5px 0 5px 0',
                    color: '#fff',
                    fontSize: '14px',
                    fontWeight: 'bold'
                });
                
                $(preloader).css({
                    top: $(window).height() / 2 - $(preloader).height() / 2,
                    left: $(window).width() / 2 - $(preloader).width() / 2
                });

                $(preloader).appendTo('body').fadeIn('slow');
            },

            after: function (xhr, status) {
                $('#default-preloader').fadeOut('slow', function(){$(this).remove()});
            },

            error: function (xhr, textStatus, errorThrown) {
                App.dialog.error('Remote request error', 'Reason: ' + textStatus);
            }
        },

        theme: null,

        assets: {
            paths: {
                module: ':basePath/public/assets/modules/:moduleName/:resource/:path',
                theme:  ':basePath/public/themes/:theme/assets/:resource/:path',
                common: ':basePath/public/assets/:resource/:path'
            }
        }
    },

    translations: {},

    params: {},

    moduleName: null,

    controllerName: null,

    actionName: null,

    baseUrl: function (path) {
        return App.config.basePath + path;
    },

    t: function() {
        var regex = /%%|%(\d+\$)?([-+#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
        var a = arguments, i = 0, format = a[i++];

        if (typeof App.translations[format] === 'undefined') {
            return format;
        } else {
            format = App.translations[format];
        }

        // pad()
        var pad = function(str, len, chr, leftJustify) {
            var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
            return leftJustify ? str + padding : padding + str;
        };

        // justify()
        var justify = function(value, prefix, leftJustify, minWidth, zeroPad) {
            var diff = minWidth - value.length;
            if (diff > 0) {
                if (leftJustify || !zeroPad) {
                    value = pad(value, minWidth, ' ', leftJustify);
                } else {
                    value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
                }
            }
            return value;
        };

        // formatBaseX()
        var formatBaseX = function(value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
            // Note: casts negative numbers to positive ones
            var number = value >>> 0;
            prefix = prefix && number && {'2': '0b', '8': '0', '16': '0x'}[base] || '';
            value = prefix + pad(number.toString(base), precision || 0, '0', false);
            return justify(value, prefix, leftJustify, minWidth, zeroPad);
        };

        // formatString()
        var formatString = function(value, leftJustify, minWidth, precision, zeroPad) {
            if (precision != null) {
                value = value.slice(0, precision);
            }
            return justify(value, '', leftJustify, minWidth, zeroPad);
        };

        // finalFormat()
        var doFormat = function(substring, valueIndex, flags, minWidth, _, precision, type) {
            if (substring == '%%') return '%';

            // parse flags
            var leftJustify = false, positivePrefix = '', zeroPad = false, prefixBaseX = false;
            for (var j = 0; flags && j < flags.length; j++) switch (flags.charAt(j)) {
                case ' ':
                    positivePrefix = ' ';
                    break;
                case '+':
                    positivePrefix = '+';
                    break;
                case '-':
                    leftJustify = true;
                    break;
                case '0':
                    zeroPad = true;
                    break;
                case '#':
                    prefixBaseX = true;
                    break;
            }

            // parameters may be null, undefined, empty-string or real valued
            // we want to ignore null, undefined and empty-string values
            if (!minWidth) {
                minWidth = 0;
            } else if (minWidth == '*') {
                minWidth = +a[i++];
            } else if (minWidth.charAt(0) == '*') {
                minWidth = +a[minWidth.slice(1, -1)];
            } else {
                minWidth = +minWidth;
            }

            // Note: undocumented perl feature:
            if (minWidth < 0) {
                minWidth = -minWidth;
                leftJustify = true;
            }

            if (!isFinite(minWidth)) {
                throw new Error('sprintf: (minimum-)width must be finite');
            }

            if (!precision) {
                precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : void(0);
            } else if (precision == '*') {
                precision = +a[i++];
            } else if (precision.charAt(0) == '*') {
                precision = +a[precision.slice(1, -1)];
            } else {
                precision = +precision;
            }

            // grab value using valueIndex if required?
            var value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];

            switch (type) {
                case 's':
                    return formatString(String(value), leftJustify, minWidth, precision, zeroPad);
                case 'c':
                    return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
                case 'b':
                    return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'o':
                    return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'x':
                    return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'X':
                    return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
                case 'u':
                    return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
                case 'i':
                case 'd':
                {
                    var number = parseInt(+value);
                    var prefix = number < 0 ? '-' : positivePrefix;
                    value = prefix + pad(String(Math.abs(number)), precision, '0', false);
                    return justify(value, prefix, leftJustify, minWidth, zeroPad);
                }
                case 'e':
                case 'E':
                case 'f':
                case 'F':
                case 'g':
                case 'G':
                {
                    var number = +value;
                    var prefix = number < 0 ? '-' : positivePrefix;
                    var method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
                    var textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
                    value = prefix + Math.abs(number)[method](precision);
                    return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
                }
                default:
                    return substring;
            }
        };

        return format.replace(regex, doFormat);
    },

    addParam: function (name, value) {
        App.params[name] = value;
    },
    
    addParams: function (params) {
        if ($.isPlainObject(params)) {
            $.each(params, function(key, value) {
                App.params[key] = value;
            });
        }
    },

    getParam: function (name, _default) {
        if (App.hasParam(name)) {
            return App.params[name];
        }
        if (_default) {
            return _default;
        }

        return null;
    },

    getAllParams: function () {
        return App.params;
    },

    hasParam: function (name) {
        if (typeof(App.params[name]) == 'undefined') {
            return false;
        }

        return true;
    },

    getModuleName: function () {
        return App.moduleName;
    },

    getControllerName: function () {
        return App.controllerName;
    },

    getActionName: function () {
        return App.actionName;
    },

    getVersion: function () {
        return App.version;
    },

    utils: {
        xml2Json: function(xml, tab) {
            var X = {
                toObj: function(xml) {
                    var o = {};
                    //debugger;
                    if (xml.nodeType == 1) {   // element node ..
                        if (xml.attributes.length)   // element with attributes  ..
                            for (var i = 0; i < xml.attributes.length; i++)
                                o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
                        if (xml.firstChild) { // element has child nodes ..
                            var textChild = 0, cdataChild = 0, hasElementChild = false;
                            for (var n = xml.firstChild; n; n = n.nextSibling) {
                                if (n.nodeType == 1) hasElementChild = true;
                                else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // non-whitespace text
                                else if (n.nodeType == 4) cdataChild++; // cdata section node
                            }
                            if (hasElementChild) {
                                if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
                                    X.removeWhite(xml);
                                    for (var n = xml.firstChild; n; n = n.nextSibling) {
                                        if (n.nodeType == 3)  // text node
                                            o["#text"] = X.escape(n.nodeValue);
                                        else if (n.nodeType == 4)  // cdata node
                                            o["#cdata"] = X.escape(n.nodeValue);
                                        else if (o[n.nodeName]) {  // multiple occurence of element ..
                                            if (o[n.nodeName] instanceof Array)
                                                o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
                                            else
                                                o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
                                        }
                                        else  // first occurence of element..
                                            o[n.nodeName] = X.toObj(n);
                                    }
                                }
                                else { // mixed content
                                    if (!xml.attributes.length)
                                        o = X.escape(X.innerXml(xml));
                                    else
                                        o["#text"] = X.escape(X.innerXml(xml));
                                }
                            }
                            else if (textChild) { // pure text
                                if (!xml.attributes.length)
                                    o = X.escape(X.innerXml(xml));
                                else
                                    o["#text"] = X.escape(X.innerXml(xml));
                            }
                            else if (cdataChild) { // cdata
                                if (cdataChild > 1)
                                    o = X.escape(X.innerXml(xml));
                                else
                                    for (var n = xml.firstChild; n; n = n.nextSibling)
                                        o["#cdata"] = X.escape(n.nodeValue);
                            }
                        }
                        if (!xml.attributes.length && !xml.firstChild) o = null;
                    }
                    else if (xml.nodeType == 9) { // document.node
                        o = X.toObj(xml.documentElement);
                    }
                    else
                        alert("unhandled node type: " + xml.nodeType);
                    return o;
                },
                toJson: function(o, name, ind) {
                    var json = name ? ("\"" + name + "\"") : "";
                    if (o instanceof Array) {
                        for (var i = 0,n = o.length; i < n; i++)
                            o[i] = X.toJson(o[i], "", ind + "\t");
                        json += (name ? ":[" : "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
                    }
                    else if (o == null)
                        json += (name && ":") + "null";
                    else if (typeof(o) == "object") {
                        var arr = [];
                        for (var m in o)
                            arr[arr.length] = X.toJson(o[m], m, ind + "\t");
                        json += (name ? ":{" : "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
                    }
                    else if (typeof(o) == "string")
                        json += (name && ":") + "\"" + o.toString() + "\"";
                    else
                        json += (name && ":") + o.toString();
                    return json;
                },
                innerXml: function(node) {
                    var s = ""
                    if ("innerHTML" in node)
                        s = node.innerHTML;
                    else {
                        var asXml = function(n) {
                            var s = "";
                            if (n.nodeType == 1) {
                                s += "<" + n.nodeName;
                                for (var i = 0; i < n.attributes.length; i++)
                                    s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
                                if (n.firstChild) {
                                    s += ">";
                                    for (var c = n.firstChild; c; c = c.nextSibling)
                                        s += asXml(c);
                                    s += "</" + n.nodeName + ">";
                                }
                                else
                                    s += "/>";
                            }
                            else if (n.nodeType == 3)
                                s += n.nodeValue;
                            else if (n.nodeType == 4)
                                s += "<![CDATA[" + n.nodeValue + "]]>";
                            return s;
                        };
                        for (var c = node.firstChild; c; c = c.nextSibling)
                            s += asXml(c);
                    }
                    return s;
                },
                escape: function(txt) {
                    return txt.replace(/[\\]/g, "\\\\")
                            .replace(/[\"]/g, '\\"')
                            .replace(/[\n]/g, '\\n')
                            .replace(/[\r]/g, '\\r');
                },
                removeWhite: function(e) {
                    e.normalize();
                    for (var n = e.firstChild; n;) {
                        if (n.nodeType == 3) {  // text node
                            if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
                                var nxt = n.nextSibling;
                                e.removeChild(n);
                                n = nxt;
                            }
                            else
                                n = n.nextSibling;
                        }
                        else if (n.nodeType == 1) {  // element node
                            X.removeWhite(n);
                            n = n.nextSibling;
                        }
                        else                      // any other node
                            n = n.nextSibling;
                    }
                    return e;
                }
            };
            if (xml.nodeType == 9) // document node
                xml = xml.documentElement;
            var nws = X.removeWhite(xml);
            var obj = X.toObj(nws);
            var json = X.toJson(obj, xml.nodeName, "\t");
            //debugger;
            return "{\n" + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
        },

        scrollTo: function (element, duration) {
            duration = (duration && typeof(duration) == 'number') ? duration : 300;
            $('html, body').stop().animate({
                scrollTop: $(element).offset().top
            }, {queue: false, duration: duration});
        },

        timer: function (funct, msec) {
            if (typeof(funct) == 'function' && msec && typeof(msec) == 'number') {
                setTimeout(funct, msec);
            }
        }
    },

    init: function (config) {
        // Override config via init()
        if (config && typeof(config) == 'object') {
            $.extend(App.config, config);
        }

        mvcModule = eval('App.module.' + App.moduleName);

        if (mvcModule && typeof(mvcModule) == 'object') {
            mvcController = eval('mvcModule.' + 'controllers[\'' + App.controllerName + '\']');
            if (mvcController && typeof(mvcController) == 'object') {
                if (mvcController._init && typeof(mvcController._init) == 'function') {
                    mvcController._init();
                }
                mvcAction = eval('mvcController.' + App.actionName);
                if (mvcAction && typeof(mvcAction) == 'function') {
                    mvcAction();
                }
            }
        }

        // Fire events on document ready
        App.onReady(function(){
            // Notification events
            $('div.b-info-line:visible').find('div.mid').live('click', function(event){
                $(this).parents('div.b-info-line').fadeOut(300, function(){
                    $(this).remove();
                });
                event.preventDefault();
            });
        });
    },

    // Wrapper for init() without config
    reinit: function () {
        App.init();
    },

    onReady: function (eventFunction) {
        if (typeof(eventFunction) == 'function') {
            $(eventFunction);
        } else if (typeof(eventFunction) == 'string' && eventFunction in window) {
            if (_ef = eval(eventFunction.replace('()', ''))) {
                _ef();
            }
        }
    },

    // Logger
    log: function (variable) {
        if (variable) {
            console.log(variable);
        }
    },

    assets: {
        module: {
            image: function (moduleName, path) {
                return App.assets.module._assemblePath('images', moduleName, path);
            },

            javascript: function (moduleName, path) {
                return App.assets.module._assemblePath('js', moduleName, path);
            },

            stylesheet: function (moduleName, path) {
                return App.assets.module._assemblePath('css', moduleName, path);
            },

            flash: function (moduleName, path) {
                return App.assets.module._assemblePath('flash', moduleName, path);
            },

            common: function (moduleName, path) {
                return App.assets.module._assemblePath('common', moduleName, path);
            },

            _assemblePath: function (resType, moduleName, path) {
                _path = App.config.assets.paths.module;

                return _path.replace(':basePath', App.config.basePath)
                        .replace(':moduleName', moduleName)
                        .replace(':resource', resType)
                        .replace(':path', path)
                        .replace(new RegExp(/\/+/g), '/');
            }
        },

        theme: {
            image: function (path) {
                return App.assets.theme._assemblePath('images', path);
            },

            javascript: function (path) {
                return App.assets.theme._assemblePath('js', path);
            },

            stylesheet: function (path) {
                return App.assets.theme._assemblePath('css', path);
            },

            flash: function (path) {
                return App.assets.theme._assemblePath('flash', path);
            },

            common: function (path) {
                return App.assets.theme._assemblePath('common', path);
            },

            _assemblePath: function (resType, path) {
                _path = App.config.assets.paths.theme;

                return _path.replace(':basePath', App.config.basePath)
                        .replace(':theme', App.config.theme)
                        .replace(':resource', resType)
                        .replace(':path', path)
                        .replace(new RegExp(/\/+/g), '/');
            }
        },

        common: {
            image: function (path) {
                return App.assets.common._assemblePath('images', path);
            },

            javascript: function (path) {
                return App.assets.common._assemblePath('js', path);
            },

            stylesheet: function (path) {
                return App.assets.common._assemblePath('css', path);
            },

            flash: function (path) {
                return App.assets.common._assemblePath('flash', path);
            },

            common: function (path) {
                return App.assets.common._assemblePath('common', path);
            },

            _assemblePath: function (resType, path) {
                _path = App.config.assets.paths.common;

                return _path.replace(':basePath', App.config.basePath)
                        .replace(':resource', resType)
                        .replace(':path', path)
                        .replace(new RegExp(/\/+/g), '/');
            }
        }
    },

    // Dialog
    dialog: {
        // App.dialog.notice
        notice: function (title, message, options) {
            if (!$.isPlainObject(title)) {
                if ('object' != typeof(options)) {
                    options = {};
                }
                options.title   = title;
                options.message = message;
            }
            options.type = 'notice';

            return App.dialog.open(options);
        },

        // App.dialog.info
        info: function (title, message, options) {
            if (!$.isPlainObject(title)) {
                if ('object' != typeof(options)) {
                    options = {};
                }
                options.title   = title;
                options.message = message;
            }
            options.type = 'info';

            return App.dialog.open(options);
        },

        // App.dialog.error
        error: function (title, message, options) {
            if (!$.isPlainObject(title)) {
                if ('object' != typeof(options)) {
                    options = {};
                }
                options.title   = title;
                options.message = message;
            }
            options.type = 'error';

            return App.dialog.open(options);
        },

        // App.dialog.yesNo
        yesNo: function (title, message, onYes, onNo, options) {
            if (!$.isPlainObject(title)) {
                if ('object' != typeof(options)) {
                    options = {};
                }
                options.title = title;
                options.message = message;
                if ('onYes' in options) {
                    onYes = options.onYes;
                }
                if ('onNo' in options) {
                    onNo = options.onNo;
                }
            }
            options.type = 'notice';
            options.buttons = {
                Yes: function() {
                    if (jQuery.isFunction(onYes)) {
                        onYes();
                    }
                    $(this).dialog("close");
                },
                No: function() {
                    if (jQuery.isFunction(onNo)) {
                        onNo();
                    }
                    $(this).dialog("close");
                }
            }

            return App.dialog.open(options);
        },

        // App.dialog.open
        open: function (title, message, options) {
            if ($.isPlainObject(title)) {
                options = title;
            } else {
                if ('object' != typeof(options)) {
                    options = {};
                }
                
                // append "title" options property
                if (typeof(title) == 'string') {
                    if (!("title" in options)) {
                        options.title = title;
                    }
                }
                // append "message" options property
                if (typeof(message) == 'string') {
                    if (!("message" in options)) {
                        options.message = message;
                    }
                }
            }

            // dialog type
            if (!("type" in options)) {
                options.type = 'notice';
            }
            
            switch (options.type) {
                case 'notice':
                case 'info':
                case 'error':
                    imgName = 'dialog-' + options.type;
                    break;

                default:
                    imgName = 'dialog-notice';
                    break;
            }

            titleImg = '<img style="padding-right:5px; float:left; display:inline;" src="' + App.assets.common.image('icons/' + imgName + '.png') + '" />';

            // modal defaults
            defaults = {
                resizable: false,
                modal:     true,
                draggable: true,
                title:     titleImg + 'Dialog:',
                message:   '<p>Empty message...</p>',
                buttons:   {
                    Ok: function() {
                        $(this).dialog("close");
                    }
                }
            }

            // combine options
            options = $.extend(defaults, options);

            options.title = '<div style="' + 'line-height:16px;">' + titleImg + options.title + '</div>';

            // checking for right xpath or html in message or leave message as plain text
            msgContent = $(options.message).html();
            if (null === msgContent) {
                options.message = '<p>' + options.message + '</p>'
            }

            $(options.message).dialog(options);
        }
    },

    // Notification types
    notify: {
        error: function (message) { App.notify.show('error', message) },

        success: function (message) { App.notify.show('success', message) },

        info: function (message) { App.notify.show('info', message) },

        show: function (type, message) {
            switch (type) {
                case 'error':
                    _subClass = 'error-line';
                    break;
                case 'success':
                    _subClass = 'success-line';
                    break;
                case 'info':
                    _subClass = '';
                    break;
            }
            _blankBlock = $('div.b-info-line:hidden');
            _cloned = _blankBlock.clone().removeClass('hidden');
            if ('' != _subClass) _cloned.addClass(_subClass);
            _cloned.find('span.notify-msg').html(message);
            _blankBlock.before(_cloned);
            App.utils.scrollTo($('div.b-info-line:first'));

            delete _cloned, _blankBlock;
        }
    },

    // Ajax functions
    remote: {
        load: function (to, url) {
            App.remote.initPreloader();
            $('#' + to).load(url);
        },

        get: function (url, data, callback, type) {
            App.remote.initPreloader();
            
            if (jQuery.isFunction(data)) {
                callback = data;
                data = null;
            }

            return $.get(url, data, callback, type);
        },

        post: function (url, data, callback, type) {
            App.remote.initPreloader();
            
            if (jQuery.isFunction(data)) {
                callback = data;
                data = null;
            }

            return $.post(url, data, callback, type);
        },

        parseResult: function (result, asIs) {
            pType = 'unknown';
            parsed = null;

            try {
                if ($.isPlainObject(result)) {
                    parsed = result;
                } else {
                    parsed = $.parseJSON(result);
                }
                pType = 'json';
            } catch (jEx) {
                try {
                    parsed = $.parseXML(result);
                    pType = 'xml';
                } catch (xEx) {
                    parsed = null;
                    pType = 'unknown';
                }
            }

            if (null !== parsed) {
                if ('xml' == pType) {
                    parsed = App.utils.xml2Json(parsed);
                    parsed = $.parseJSON(parsed);
                }

                App.remote._result.setResponse(parsed);
                parsed = App.remote._result;
            }

            if (asIs) {
                parsed = result;
                pType = 'unknown'
            }

            return {
                status: (null !== parsed) ? 'success' : 'error',
                type: pType,
                data: parsed
            }
        },

        _result: {
            _response: {},

            setResponse: function (responseData) {
                if (responseData && typeof(responseData) == 'object') {
                    App.remote._result._response = responseData.response;

                    return App.remote._result;
                }
            },

            getServerTime: function () { return App.remote._result._get('server_time') },

            getType: function () { return App.remote._result._get('response_type') },

            isError: function () {
                if ('error' == App.remote._result.getType()) {
                    return true;
                }

                return false;
            },

            isSuccess: function () {
                if ('success' == App.remote._result.getType()) {
                    return true;
                }

                return false;
            },

            getCode: function () { return App.remote._result._get('code') },

            getMessage: function () { return App.remote._result._get('message') },

            getOutput: function () { return App.remote._result._get('output') },

            getModule: function () { return App.remote._result._get('module') },

            getController: function () { return App.remote._result._get('controller') },

            getAction: function () { return App.remote._result._get('action') },

            getParam: function (paramName) {
                _params = App.remote._result._get('params');

                if (null !== _params) {
                    _param = eval('_params.' + paramName);

                    if (_param) {
                        return _param;
                    }
                }
                
                return null;
            },

            getParams: function () { return App.remote._result._get('params') },

            _get: function (_param) {
                _param = eval('App.remote._result._response.' + _param);
                if (_param) {
                    return _param;
                }

                return null;
            }
        },

        initPreloader: function () {
            // Apply ajax event handlers
            if (true === App.config.ajax.useDefaultEventHandlers) {
                $.ajaxSetup({
                    beforeSend: App.config.ajax.before,
                    complete: App.config.ajax.after,
                    error: App.config.ajax.error
                });
            }
        }
    },

    module: {
        extend: function (module) {
            if (module && typeof(module) == 'object') {
                $.extend(App.module, module);
            }
        }
    }
}

// reinit application
App.init();

