/************************************************************\
Richard Bateman's JS classes.  Copyright 2008-2010.  Please
don't steal anything from here without letting me know.

Requires: JQuery, JQuery.json
\************************************************************/

if (!Date.fromUnixTime)
{
    Date.fromUnixTime = function(unixTime)
    {
        return new Date(unixTime * 1000);
    };
}

if (!String.prototype.trim)
{
    String.prototype.trim = (function()
    {
        var ws = {},
            chars = ' \n\r\t\v\f\u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000';
        for(var i = 0; i < chars.length; i++ )
            ws[chars.charAt(i)] = true;

        return function( )
        {
            var s = -1,
                e = this.length;
            while( ws[this.charAt(--e)] );
            while( s++ !== e && ws[this.charAt(s)] );
            return this.substring( s, e+1 );
        };
    })();
}

if (!String.prototype.format)
{
    String.prototype.format = function()
    {
        if (arguments.length == 1 && typeof(arguments[0]) == "object") {
            var params = arguments[0];
        } else {
            var params = arguments;
        }
        var i = 0;
        var x = function() {
            if (params)
                return params[i++];
            else
                return "???";
        };
        return this.replace(/(%s)/g, x);
    };
}

/** jQuery plugins **/

jQuery.fn.inputField = function(options) {
    var field = this[0];
    var settings = jQuery.extend({    
        allowNumeric: true,
        allowAlpha: true,
        capitalize: false,
        transform: {},
        keyTriggers: {},
        allowSpecialKeys: true,
        allowChars: "",
        disallowChars: "",
    }, options);

    for (var key in settings.keyTriggers) {
        $(this).bind("keydown", key, settings.keyTriggers[key]);
    }

    var isAlpha = function(str) {
        var filter = /[A-Za-z]/;
        return filter.test(str.substring(0,1));
    };
    var isNumeric = function(str) {
        var filter = /[0-9.]/;
        return filter.test(str.substring(0,1));
    };

    var changeKey = function(evt, newKey) {
        if (field.createTextRange) {
            window.event.keyCode = newKey.charCodeAt(0);
        } else {
            var startpos = field.selectionStart;
            var endpos = field.selectionEnd;
            field.value = field.value.substr(0, startpos) + newKey + field.value.substr(endpos);
            field.setSelectionRange(startpos + 1, startpos + 1);
            evt.preventDefault();
            return false;
        }
    }

    $(this).keypress(function(evt) {
        // Don't catch special chars
        var code = evt.which;
        var char = String.fromCharCode(code);
        if (settings.allowChars.indexOf(char) == -1) {
            if (!settings.allowAlpha && isAlpha(char)) {
                evt.preventDefault();
            } else if (!settings.allowNumeric && isNumeric(char)) {
                evt.preventDefault();
            } else if (settings.disallowChars.indexOf(char) !== -1) {
                evt.preventDefault();
            }
        }
        var newChar = char;
        if (settings.transform[char]) {
            newChar = settings.transform[char];
        }
        if (settings.capitalize) {
            newChar = newChar.toUpperCase();
        }
        if (newChar != char) {
            changeKey(evt, newChar);
        }
    });
    return this;
}

jQuery.fn.selectText = function(start, len) {
    var self = this.get(0);
    var end = len+start;
    if (self.createTextRange) {
        var selRange = self.createTextRange()
        selRange.collapse(true);
        selRange.moveStart("character", start);
        selRange.moveEnd("character", len);
        selRange.select();
    } else if (self.setSelectionRange) {
        self.setSelectionRange(start, end);
    } else if (self.selectionStart) {
        self.selectionStart = start;
        self.selectionEnd = len;
    }
}

if (!RB)
{
    var RB = { AppData: { } };
}

if (!RB.$)
{
    RB.$ = function()
    {
        var ret = [];
        for (var i = 0, al = arguments.length; i < al; i++)
        {
            var e = arguments[i];
            if (typeof(e) == 'string')
            {
                e = document.getElementById(e);
            }

            if (arguments.length == 1)
            {
                return e;
            }
            ret.push(e);
        }
        return ret;
    };
}

RB.findEntryPlace = RB.findEntryPlace || function(list, compareFunc, cVal) {
    for (var i = 0; i < list.length; i++) {
        var fVal = compareFunc(list[i], cVal);
        if (fVal < 0)
            return list[i];
    }
    return null;
} //(row.find("li"), "sNo", sNo)

if (!RB.dialogs) {
    RB.dialogs = {};
}

/*
 * jQuery UI fade effect, based on pulsate
 *
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Depends:
 *  effects.core.js
 */
(function($) {

$.effects.fade = function(o) {

    return this.queue(function() {

        // Create element
        var el = $(this);

        // Set options
        var speed = o.options.speed || 230;
        var mode = o.options.mode || 'show'; // Set Mode

        // Animate
        if (mode == 'show') {
            el.fadeIn(speed);
        } else {
            el.fadeOut(speed);
        };
        el.queue('fx', function() { el.dequeue(); });
        el.dequeue();
    });
};

})(jQuery);


RB.displayDialog = RB.displayDialog || function(title, data, buttons, options) {
    if (!buttons) buttons = [["OK","check", null]];
    var dialog = $("<div></div>");
    var cbFuncs = {};
    var dlgButtons = {};
    var i = 0;
    for(var i in buttons) {
        var text = buttons[i][0];
        cbFuncs[text] = buttons[i][2];
        dlgButtons[text] = function(evt) {
            var btnText = $(evt.currentTarget).text().trim();
            var close = true;
            if (cbFuncs[btnText]) {
                close = cbFuncs[btnText](dialog);
            }
            if (close !== false) {
                dialog.dialog("close");
                // Remove from the DOM
                dialog.remove();
            }
        }
        i++;
    }
    var open = function(evt) {
        if (options) {
            if (options.noClose)
                RB.dialog.removeClose(evt);
            if (options.open)
                options.open(evt);
        }

        for (var i in buttons) {
            var text = buttons[i][0];
            if (typeof buttons[i][1] == "string") {
                RB.dialog.setButtonIcon(evt, text, buttons[i][1]);
            }
        }
    }
    var opts = {
            title: title,
            autoOpen : true,
            modal: true,
            show: "fade",
            hide: "fade",
            resizable: false,
            minHeight: 40,
            open: open,
            buttons: dlgButtons,
        };
    for (var name in options) {
        if (name == "open") continue;
        opts[name] = options[name];
    }
    dialog.append(data);
    dialog.dialog(opts);
}

RB.prompt = function(title, mask, placeholder, ifSave, ifCancel, txtSave, txtCancel) {
    if (!txtSave) txtSave = "Save";
    if (!txtCancel) txtCancel = "Cancel";
    var html = '<input type="text" name="val" placeholder="%s">'.format(placeholder);
    var open = function(evt) {
        var dialog = $(evt.currentTarget.activeElement);
        var box = dialog.find("input[name=val]");
        box.mask(mask);
        box.focus();
        box.keyup(function(evt) {
            if (evt.keyCode == 13) {
                var btn = dialog.find('button:contains("%s")'.format(txtSave));
                btn.click();
            } else if (evt.keyCode == 27) {
                var btn = dialog.find('button:contains("%s")'.format(txtCancel));
                btn.click();
            }
        });
    };
    var save = function(dialog) {
        var val = dialog.find("input[name=val]").val();
        var close = true;
        if (ifSave)
            close = ifSave(val, dialog);

        return close;
    }
    var cancel = function(dialog) {
        var close = true;
        if (ifCancel)
            close = ifCancel(dialog);

        return close;
    }

    RB.displayDialog(
        title,
        html,
        [[txtCancel, "closethick", cancel], [txtSave, "disk", save]],
        { open: open }
    );
}

RB.alert = RB.alert || function(title, message, done) {
    var alert = $("<div><audio autoplay src=\"/public/sounds/error.wav\" /><div>%s</div></div>".format(message));
    var handlerCalled = false;
    var btnClicked = function(evt) {
        evt.preventDefault();
        handlerCalled = true;
        alert.dialog("close");
        alert.remove();
        if (done) done(evt);
    }
    alert.dialog({
        title: title,
        autoOpen : true,
        modal: true,
        resizable: false,
        show: "fade",
        hide: "fade",
        minHeight: 40,
        buttons: {
            "OK": btnClicked,
        },
        beforeclose: function() { if (!handlerCalled && done) done(); },
        open: function(evt) {
            var tmp = this;
            RB.dialog.setButtonIcon(evt, "OK", "check");
            alert.find('button').focus();
            RB.dialog.removeClose(evt);
        },
    });

    RB.html.applyMaskAndPlaceholder(alert);
}

RB.confirm = RB.confirm || function(title, message, ifYes, ifNo, yesText, noText) {
    if (!yesText) yesText = "Yes";
    if (!noText) noText = "No";
    var handlerCalled = false;

    var confirm = $("<div><div>%s</div></div>".format(message));
    var buttons = {};
    buttons[noText] = function(evt) {
                handlerCalled = true;
                confirm.dialog("close");
                confirm.remove();
                if (ifNo) ifNo(confirm);
            };
    buttons[yesText] = function(evt) {
                handlerCalled = true;
                confirm.dialog("close");
                confirm.remove();
                if (ifYes) ifYes(evt);
            };
    var dialogOptions = {
        title: title,
        autoOpen : true,
        modal: true,
        show: "fade",
        hide: "fade",
        resizable: false,
        minHeight: 40,
        closeOnEscape: false,
        beforeclose: function() { if (!handlerCalled && ifNo) ifNo(); },
        open: function(evt) {
            RB.dialog.removeClose(evt);
            RB.dialog.setButtonIcon(evt, yesText, "check");
            RB.dialog.setButtonIcon(evt, noText, "closethick");
        }, buttons: buttons
    }

    confirm.dialog(dialogOptions);

    RB.html.applyMaskAndPlaceholder(confirm);
}

RB.genericAjaxError = RB.genericAjaxError || function(nil, status, error) {
    RB.alert("Error connecting to server", "There was an error attempting to connect to the server.  Please check your network connections and try again!");
}

RB.formatPhone = RB.formatPhone || function(str) {
    var phone = RB.getNumericChars(str);
    return phone.substring(0, 3) + "-" + phone.substring(3, 6) + "-" + phone.substring(6, 10);
}

RB.getNumericChars = RB.getNumericChars || function(str) {
    return str.replace(/[\D\s]/g,"");
}

if (!RB.dialog) {
    RB.dialog = {};
    // Helper function to close the dialog and call a callback
    RB.dialog.closeAndCall = function(dialog, callfunc) {
        var cbfunc = callfunc;
        var tmp = function(evt) {
            dialog.dialog("close");
            cbfunc(evt);
        };
        return tmp;
    };

    RB.dialog.removeClose = function(dialog) {
        $(dialog.currentTarget.activeElement).find(".ui-dialog-titlebar-close").hide();
    };

    RB.dialog.setButtonIcon = function(target, text, icon) {
        var btn = $(target.target).parents(".ui-dialog").find('button:contains("%s")'.format(text));
        btn.button({icons: {primary:'ui-icon-%s'.format(icon),secondary:null}});
    };
}

if (!RB.html) {
    RB.html = {};
    RB.html.strings = {};
    RB.html.strings.button = '<button id="%s">%s</button>';
    RB.html.strings.uiicon = '<span class="ui-icon ui-icon-%s" />'
    RB.html.button = function(id, text, icon, iconRight) {
        var btn = $(RB.html.strings.button.format(id, text));
        var icons;
        if (iconRight) {
            icons = {primary:null,secondary:"ui-icon-%s".format(icon)};
        } else {
            icons = {primary:"ui-icon-%s".format(icon),secondary:null};
        }
        btn.button({icons:icons});
        return btn;
    }
    RB.html.icon = function(type) {
        return RB.html.strings.uiicon.format(type);
    }

    RB.html.addHover = function(elem, cls) {
        if (!cls) cls = "ui-state-hover";
        elem.hover(
                function() { $(this).addClass(cls); },
                function() { $(this).removeClass(cls); });
    }
    RB.html.addActive = function(elem, cls) {
        if (!cls) cls = "ui-state-active";
        elem.mousedown(function() { $(this).addClass(cls); });
        elem.mouseup(function() { $(this).removeClass(cls); });
    }
    RB.html.addFocus = function(elem, cls) {
        if (!cls) cls = "ui-state-focus";
        elem.focusin(function() { $(this).addClass(cls); });
        elem.focusout(function() { $(this).removeClass(cls); });
    }
    RB.html.buttonize = function(elem, icon, iconRight) {
        var icons;
        if (iconRight) {
            icons = {primary:null,secondary:"ui-icon-%s".format(icon)};
        } else {
            icons = {primary:"ui-icon-%s".format(icon),secondary:null};
        }
        elem.button({icons:icons});
    }
    jQuery.fn.buttonize = function(icon) {
        RB.html.buttonize($(this), icon);
        return this;
    }
    RB.html.getDropdown = function(items, initval) {
        var outHTML = "";
        for (var i in items) {
            //var sel = items[i] == initval ? " selected=''" : "";
            var sel = "";
            outHTML += "<option value='%s'%s>%s</option>".format(items[i], sel, items[i]);
        }
        var output = $('<select/>').append(outHTML);
        return output;
    }
    RB.html.blurPhoneHandler = function(evt) {
        var elem = evt.currentTarget;
        $(elem).val(RB.formatPhone($(elem).val()));
    }
    RB.html.makePhoneField = function(elem) {
        elem.blur(RB.html.blurPhoneHandler);
    }
    RB.html.applyMaskAndPlaceholder = function(elem) {
        $(elem).find("input[placeholder]:not([mask])").each(__placeholderFunc);
        $(elem).find("input[mask]").each(function(i) { $(this).mask($(this).attr("mask")) } );
    }
}

if (!RB.log)
{
    RB.log = function(m)
    {
        try {
            if (console && console.log) {
                console.log(m);
            }
        } catch (e)
        {

        }
    }
    RB.warn = function(m)
    {
        try {
            if (console && console.warn) {
                console.warn(m);
            }
        } catch (e)
        {

        }
    }
    RB.info = function(m)
    {
        try {
            if (console && console.info) {
                console.info(m);
            }
        } catch (e)
        {

        }
    }
    RB.error = function(m)
    {
        try {
            if (console && console.error) {
                console.error(m);
            }
        } catch (e)
        {

        }
    }
}

if (!RB.sort) {
    RB.sort = {};
    RB.sort.numericAsc = function(a, b) { return (a-b); };
    RB.sort.numericDesc = function(a, b) { return (b-a); };
}

if (!RB.sortableObject)
{
    RB.sortableObject = function(obj, fieldlist, extraFields)
    {
        this.sortValue = "";
        for (key in fieldlist) {
            this.sortValue += " %s".format(obj[fieldlist[key]]);
            this[fieldlist[key]] = obj[fieldlist[key]];
        }
        for (key in extraFields) {
            this[extraFields[key]] = obj[extraFields[key]];
        }
    }
    RB.sortableObject.prototype.toString = function() {return this.sortValue;};
}

if (!RB.processTemplate)
{
    /**
     * processTemplate
     *
     * Accepts an array of template lines with %s as deliniators and an array of
     * objects in which the fields (specified in the template lines) can be found
     *
     * returns the generated code.
     *
     * tpl = [ ["some field value %s", ["fieldname"]], ["some other %sx%s", ["field1", "field2"]]];
     *
     * optionally a third parameter (a transform function) can be defined.
     *
     * srcObjects = [obj1, obj2, obj3, ...] - these objects will be searched in order for the named fields
     **/
    RB.processTemplate = function(tpl, srcObjects) {
        var outputHtml = "";
        for (var n in tpl) {
            var str = tpl[n][0];
            var fields = (typeof(tpl[n][1]) !== undefined) ? tpl[n][1] : [];
            var func = (typeof(tpl[n][2]) == "function") ? tpl[n][2] : function(fieldname, value, data) {
                if (typeof(value) == "object") return value.toString();
                else if (value !== null) return value; else return "";
            };

            var vals = [];
            for (var m in fields) {
                if (!fields.hasOwnProperty(m)) continue;
                var curVal = "";
                if (fields[m].substring(0,1) == "?") {
                    curVal = func(fields[m], "", srcObjects);
                } else {
                    for (var x in srcObjects) {
                        if (!srcObjects.hasOwnProperty(x)) continue;
                        if (srcObjects[x][fields[m]] !== undefined) {
                            curVal = func(fields[m], srcObjects[x][fields[m]], srcObjects);
                            break;
                        }
                    }
                }
                vals.push(curVal);
            }
            outputHtml += str.format(vals);
        }
        return outputHtml;
    }
}

if (!RB.LWClass) {
    RB.LWClass = function(parentClass) {
        var lwObject = function() {
            if (arguments[0] != "!!DONTINITFOOL!!") {
                var self = this;
                this.$ = function(method) {
                    var oldMethod = self[method];
                    return function() {
                        return oldMethod.apply(self, arguments);
                    }
                }
                if (this.__parentClasses) {
                    this.__parents = [];
                    var lastparent = null;
                    for (var i in this.__parentClasses) {
                        this.__parents[i] = new this.__parentClasses[i]("!!DONTINITFOOL!!");
                        var curParent = this.__parents[i];
                        this.__parents[i].$ = function(method) {
                            var oldMethod = curParent[method];
                            return function() {
                                return oldMethod.apply(self, arguments);
                            }
                        }
                        this.__parents[i].super = lastparent;
                        this.super = lastparent;
                        this.__parents[i].initialize.apply(this, arguments);
                        lastparent = this.__parents[i];
                    }
                    this.super = lastparent;
                }
                this.initialize.apply(this, arguments);
            }
        };
        if (parentClass) {
            lwObject.prototype = new parentClass('!!DONTINITFOOL!!');
            lwObject.prototype.initialize = function() {};
            var par = parentClass;
            if (!lwObject.prototype.__parentClasses) {
                lwObject.prototype.__parentClasses = Array();
            }
            lwObject.prototype.__parentClasses.push(par);
        }
        return lwObject;
    }
};

if (!RB.Class)
{
    RB.Class = function(parentClass)
    {
        var newObject = function() {
            if (arguments[0] != "!!DONTINITFOOL!!") {
                RB.BindMethodsFrom(this, this);
                if (this.__parentClasses) {
                    this.__parents = [];
                    var lastparent = null;
                    for (var i in this.__parentClasses) {
                        this.__parents[i] = new this.__parentClasses[i]("!!DONTINITFOOL!!");
                        //RB.BindMethodsFrom(this.__parents[i], this);
                        this.__parents[i].initialize.apply(this, arguments);
                        this.__parents[i].super = lastparent;
                        lastparent = this.__parents[i];
                    }
                    this.super = lastparent;
                }
                this.initialize.apply(this, arguments);
            }
        };
        if (parentClass) {
            newObject.prototype = new parentClass('!!DONTINITFOOL!!');
            newObject.prototype.initialize = function() {};
            var par = parentClass;
            if (!newObject.prototype.__parentClasses) {
                newObject.prototype.__parentClasses = Array();
            }
            newObject.prototype.__parentClasses.push(par);
        }
        return newObject;
    };

    /**
     *  BindMethodsFrom
     *
     * Bind methods in an object to, well, that object. =]
     * this keeps it from losing track of itself if methods
     * are used as a callback function.
     **/
    RB.BindMethodsFrom = function(obj, bindObject)
    {
        for (var par in obj) {
            var curMethod = obj[par];
            if (typeof(curMethod) == "function"
                && par != 'initialize') {
                // Convert the method to a wrapper that will let it
                // remember who it's parent is if it's used as a callback
                obj[par] = RB.ConvertToBoundMethod(bindObject, curMethod);
            }
        }
    };

    /**
     *  ConvertToBoundMethod
     *
     * Create a wrapper for the method in question
     * so that when the method is called it always
     * has a "this" that makes sense.
     **/
    RB.ConvertToBoundMethod = function(obj, methodObj)
    {
         var _realMethod = methodObj;
         if (_realMethod.__originalMethodObj) {
            _realMethod = _realMethod.__originalMethodObj;
         }

         /// This is the wrapper method
         /// -- notice that it always knows where the obj is =]
         var newMethod = function() {
            return _realMethod.apply(obj, arguments);
         };
         newMethod.__originalMethodObj = _realMethod;
         return newMethod;
    };
}

if (!RB.validate) { RB.validate = {}; }

RB.validate.validCallsign = RB.validate.validCallsign || function(callsign) {
    callsign = callsign.toUpperCase();
    var filter = /^([KNW][0-9][A-Z]{3}|[KW][A-Z][0-9][A-Z]{3}|[KNW][A-Z]?[0-9][A-Z]{1,2}|(A[A-L]|[KNW][A-Z])[0-9][A-Z]{1,2}|([KNW]L|[NW]P|[KNW]H)[0-9][A-Z]{2})$/;
    return filter.test(callsign);
}

RB.validate.validVENum = RB.validate.validVENum || function(num) {
    num = num.toUpperCase();
    var filter = /^[0-9]{2,6}G?$/;
    return filter.test(num);
}

RB.validate.validEmail = RB.validate.validEmail || function(email) {
    var filter = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
    return filter.test(email);
}

RB.validate.validFrn = RB.validFrn || function(frn) {
    return RB.getNumericChars(frn).length == 10;
}

RB.validate.validFrnPassword = RB.validFrnPassword || function(pass) {
    var groups = 0;
    if (pass.length < 6)
        return false;

    if (RB.validate.hasUpper(pass)) groups++;
    if (RB.validate.hasLower(pass)) groups++;
    if (RB.validate.hasNumber(pass)) groups++;
    if (RB.validate.hasSpecial(pass)) groups++;

    return groups >= 3;
}
RB.validate.hasUpper = RB.validate.hasUpper || function(pass) {
    for(var i = 0; i < pass.length; i++) {
        if (pass.charAt(i) >= "A" && pass.charAt(i) <= "Z") {
            return true;
        }
    }
    return false;
}

RB.validate.hasLower = RB.validate.hasLower || function(pass) {
    for(var i = 0; i < pass.length; i++) {
        if (pass.charAt(i) >= "a" && pass.charAt(i) <= "z") {
            return true;
        }
    }
    return false;
}

RB.validate.hasNumber = RB.validate.hasNumber || function(pass) {
    for(var i = 0; i < pass.length; i++) {
        if (pass.charAt(i) >= "0" && pass.charAt(i) <= "9") {
            return true;
        }
    }
    return false;
}

RB.validate.hasSpecial = RB.validate.hasSpecial || function(pass) {
    for(var i = 0; i < pass.length; i++) {
        if (!RB.validate.hasNumber(pass.charAt(i))
            && !RB.validate.hasLower(pass.charAt(i))
            && !RB.validate.hasUpper(pass.charAt(i))) {
            return true;
        }
    }
    return false;
}


