﻿/// <reference path="jquery.min.js"/>
/// <reference path="jquery-ui-1.8.2.custom.min.js"/>
/// <reference path="jquery.contextMenu.pack.js"/>
/// <reference path="jquery.validate.min.js"/>

// global object.  abVersion must be defined before script inclusion
var ab = window.ab = new AB1Application(abVersion);

// global ajax error trap
$(document).ajaxError(function(e, xhr, settings, exception) {
    flashError(sprintf('URL: %s<br />Status: %s', settings.url, xhr.status));
});

function AB1Application(abVersion) {
    this.datatypes = {
        json: 'json',
        html: 'html',
        javascript: 'text/javascript'
    };

    this.dash = null;
    this.modules = new ModuleManager();

    this.addScriptModule = function(name, options) {
        if (this.modules.isLoaded(name)) {
            var module = this.modules.getModule(name);
            module.onShowContent();
            return;
        }

        this.modules.addModule(name, options);

        var filename = '/assets/js/ab1-' + name + '.js?v=' + abVersion;
        this.addScriptReference(name, filename);
    }

    this.addScriptReference = function(name, filename) {
        var head = document.getElementsByTagName("head")[0];
        var script = document.createElement('script');
        //script.id = name;
        script.type = this.datatypes.javascript;
        script.src = filename;
        head.appendChild(script);
    }

    this.onLoadModule = function(name, contentManagerCreator) {
        var module = this.modules.getModule(name);
        var options = module.options || {};
        var manager = createContentManager(contentManagerCreator, options, module);

        this.modules.initModule(module, manager);
        if (typeof manager.runOnce !== 'undefined') {
            manager.runOnce();
        }

        if (name === 'dashboard') {
            this.dash = manager;
        } else {
            manager.dash = this.dash;
        }

        module.onShowContent();
    }

    function createContentManager(contentManagerCreator, options, module) {
        if (typeof contentManagerCreator === 'undefined') {
            throw "module must supply contentManager";
        }

        var contentManager = contentManagerCreator(options);

        if (typeof contentManager.showContent === 'undefined') {
            throw "module contentManager must supply showContent() function";
        }
        
        return contentManager;
    }

    return this;
}

function pageReady() {
    // perform any view-specific init
    if (typeof initView !== 'undefined') {
        initView();
    }
}

function ModuleManager() {
    this.states = {
        unloaded: 0,
        loaded: 1
    };
    
    this.getModule = function(name) {
        return this[name];
    };
    
    this.isLoaded = function(name) {
        var module = this.getModule(name);
        if (typeof module === 'undefined') {
            return false;
        }
        return (module.state == this.states.loaded);
    };

    this.initModule = function(module, contentManager) {
        module.onShowContent = createCallback(contentManager, contentManager.showContent);
        module.state = this.states.loaded;
    };

    this.addModule = function(name, options) {
        this[name] = {
            name: name,
            state: this.states.unloaded,
            options: options
        };
    };

    return this;
}

function redirectToDashboard() {
    window.location = '/Dashboard.mvc';
}

function createCallback(instance, method) {
    return function() {
        try {
            return method.apply(instance, arguments);
        }
        catch (ex) {
            flashError(ex);
            throw ex;
        }
    }
}

function htmlEncode(value) {
    return $('<div/>').text(value).html();
}

function htmlDecode(value) {
    return $('<div/>').html(value).text();
}

function fetch(from, id, params, selector, callback) {
    var url = buildUrl(from, id, params);

    $(selector).load(url, null, callback);
}

function fetchComplex(from, id, params, callback) {
    var url = buildUrl(from, id, params);

    var opts = {
        url: url,
        success: callback
    }
    $.ajax(opts);
}

function fetchJson(from, id, params, callback) {
    if (params == null || typeof params === 'undefined') {
        params = [];
    }

    params.push(sprintf('type=%s', ab.datatypes.json));
    var url = buildUrl(from, id, params);

    $.getJSON(url, null, callback);
}

function postAction(from, id, params, callback, datatype) {
    var url = buildUrl(from, id, params);

    $.post(url, null, callback, datatype);
}

function postActionComplex(url, form, callback, expectedType) {
    var opts = {
        url: url,
        type: "POST",
        data: $(form).serialize(),
        success: function(data, status, xhr) {
            if (expectedType !== 'html' && responseIsHtml(xhr)) {
                data = {
                    isError: false,
                    isRedirect: false,
                    isHtml: true,
                    isStatus: false,
                    text: data,
                    data: null
                };
            }
            
            callback(data, status, xhr);
        }
    }
    $.ajax(opts);
}

function responseIsHtml(xhr) {
    var ct = xhr.getResponseHeader('Content-Type');
    return (ct.indexOf('html') != -1);
}

function buildUrl(from, id, params) {
    var idString = (id == null || typeof id === 'undefined') ? null : id;
    var queryString = (params == null || typeof params === 'undefined') ? null : params.join('&');

    var url = from;
    if (idString != null) {
        url += '/' + idString;
    }

    url += '?';
    
    if (queryString != null) {
        url += queryString + '&';
    }

    url += 'abts=' + new Date().getTime();
    
    return url;
}

function hijax(form, u1, u2, successFunc, u3) {
    var opts = {
        url: form.action,
        type: form.method,
        data: $(form).serialize(),
        success: successFunc
    }
    $.ajax(opts);
}

// Convert json data to array of objects suitable for search result
function jsonToArray(data) {
    var rows = new Array();
    for (var i = 0; i < data.length; i++) {
        rows[i] = { data: data[i], value: data[i].Value, result: data[i].Value };
    }
    return rows;
}

// Add indexOf method to Array if browser doesnt support it
if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(elt /*, from*/) {
        var len = this.length;

        var from = Number(arguments[1]) || 0;
        from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
        if (from < 0)
            from += len;

        for (; from < len; from++) {
            if (from in this &&
          this[from] === elt)
                return from;
        }
        return -1;
    };
}

function toggleCheckbox(obj) {
    $(obj).attr('checked', !$(obj).attr('checked'));
}

function flashError(message) {
    $('body').append('<div class="error-flash" onclick="$(this).fadeOut();"><h2>Error</h2><p>' + message + '<p></div>');
    setTimeout(function() {
        $('.error-flash').fadeOut();
    }, 6000);
}

function flashMessage(message) {
    $('body').append('<div class="message-flash" onclick="$(this).fadeOut();"><p>' + message + '<p></div>');
    setTimeout(function() {
        $('.message-flash').fadeOut();
    }, 4000);
}

/* Ajax form validation and submission
------------------------------------------*/

function AB1AjaxForm(formID, url, onSubmitSuccess, normalSubmit) {
    var ab1Form = this;
    var validator = null;

    this.initValidation = function() {
        var opts = normalSubmit ? {} : { submitHandler: this.onSubmitForm }
        return $(formID).validate(opts);
    }

    this.submit = function() {
        $(formID).submit();
    }

    this.onSubmitForm = function(form) {
        postActionComplex(url, form, function(result) {
            if (!result.isHtml) {
                onSubmitSuccess(result);
            } else {
                $(form).parent().html(result.text);
                validator = ab1Form.initValidation();
            }
        });
    }

    this.resetForm = function() {
        validator.resetForm();
        $(formID)[0].reset();
    }
    
    validator = this.initValidation();
}

/* Dialog wrapper for form
------------------------------------------*/

function AB1FormDialog(dialogID, ab1Form) {
    this.onClose = function() {
        ab1Form.resetForm();
    }

    this.close = function() {
        var div = $(dialogID);
        div.dialog('close');
        div.dialog('destroy');
    }

    $(dialogID).dialog({
        autoOpen: true,
        resizable: false,
        draggable: false,
        modal: true,
        buttons: {
            'Ok': function() {
                ab1Form.submit();
            },
            'Cancel': this.close
        },
        close: this.onClose
    });
}


/* Facebook authentication
------------------------------------------*/

function onFBLogin() {
    onFBNotConnected();
}

function onFBNotConnected() {
    // login in if necessary
    FB.Connect.requireSession(onFBConnected, onFBCancel, true);
}

function onFBConnected() {
    // get a session key
    FB.Connect.showPermissionDialog('offline_access', onFBResponse);
}

function onFBResponse(result) {
    if (result != "") {
        window.location = '/Facebook.mvc/Connected';
    }
}

function onFBCancel() {
    redirectToDashboard();
}


/* --------------------------------------------------------------------------------- */
/**
sprintf() for JavaScript 0.6

Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of sprintf() for JavaScript nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/

function str_repeat(i, m) {
    for (var o = []; m > 0; o[--m] = i);
    return o.join('');
}

function sprintf() {
    var i = 0, a, f = arguments[i++], o = [], m, p, c, x, s = '';
    while (f) {
        if (m = /^[^\x25]+/.exec(f)) {
            o.push(m[0]);
        }
        else if (m = /^\x25{2}/.exec(f)) {
            o.push('%');
        }
        else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) {
            if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) {
                throw ('Too few arguments.');
            }
            if (/[^s]/.test(m[7]) && (typeof (a) != 'number')) {
                throw ('Expecting number but found ' + typeof (a));
            }
            switch (m[7]) {
                case 'b': a = a.toString(2); break;
                case 'c': a = String.fromCharCode(a); break;
                case 'd': a = parseInt(a); break;
                case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break;
                case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break;
                case 'o': a = a.toString(8); break;
                case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break;
                case 'u': a = Math.abs(a); break;
                case 'x': a = a.toString(16); break;
                case 'X': a = a.toString(16).toUpperCase(); break;
            }
            a = (/[def]/.test(m[7]) && m[2] && a >= 0 ? '+' + a : a);
            c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' ';
            x = m[5] - String(a).length - s.length;
            p = m[5] ? str_repeat(c, x) : '';
            o.push(s + (m[4] ? a + p : p + a));
        }
        else {
            throw ('Huh ?!');
        }
        f = f.substring(m[0].length);
    }
    return o.join('');
}

/* --------------------------------------------------------------------------------- */

