var EVENTS_JS_USER_AGENT    = navigator.userAgent.toLowerCase();

var EVENTS_JS_WINDOWS       = (EVENTS_JS_USER_AGENT.indexOf("win") != -1);
var EVENTS_JS_MAC           = (EVENTS_JS_USER_AGENT.indexOf("mac") != -1);

var EVENTS_JS_IS_SAFARI     = (EVENTS_JS_USER_AGENT.indexOf('safari') != -1);
var EVENTS_JS_IS_MOZILLA    = (navigator.product == "Gecko");
var EVENTS_JS_IS_IE         = ((navigator.appVersion.toLowerCase().indexOf('msie') != -1) &&
							 (EVENTS_JS_USER_AGENT.indexOf('opera') == -1));

var MOUSEBUTTON_LEFT        = EVENTS_JS_IS_MOZILLA ? 0 : 1;
var MOUSEBUTTON_MIDDLE      = EVENTS_JS_IS_MOZILLA ? 1 : 4;
var MOUSEBUTTON_RIGHT       = 2

//Add an event listener for an object and event type
//objObject: Object to attach event listener too
//strEvent: String of the event type (click, keyup, etc)
//fncPrototype: Function prototype of listener
//blnIgnoreBubbledEvents: Ignore events that would normally bubble up to this listener
function addEvent(objObject, strEvent, fncFunction, blnIgnoreBubbledEvents) {
    if (!objObject || (objObject == null)) {
        var strCallee;
        if (addEvent.caller) strCallee = addEvent.caller.toString();
        else strCallee = fncFunction.toString();
        strCallee = strCallee.substring(strCallee.indexOf(' ') + 1, strCallee.indexOf('('));
        throw new Error("Cannot add event \"" + strCallee + "\" to null object.");
    }
    else if (typeof (objObject) == "string") objObject = getElementFromID(objObject);

    if (objObject.addEventListener) // Mozilla, Netscape, Firefox
    {
        if (strEvent == 'mousewheel') objObject.addEventListener('DOMMouseScroll', fncFunction, blnIgnoreBubbledEvents);
        objObject.addEventListener(strEvent, fncFunction, blnIgnoreBubbledEvents);
        return true;
    }
    else if (window.attachEvent) // IE
    {
        return objObject.attachEvent("on" + strEvent, fncFunction);
    }
    else {
        eval("objObject.on" + strEvent + " = fncFunction;");
        return true;
    }
}

//Removes an event listener for an object and event type
//objObject: Object to attach event listenner too
//strEvent: String of the event type (click, keyup, etc)
//fncPrototype: Function prototype of listener
//blnIgnoreBubbledEvents: Ignore events that would normally bubble up to this listener
function removeEvent(objObject, strEvent, fncFunction, blnIgnoreBubbledEvents) {
    if (objObject.removeEventListener) // Mozilla, Netscape, Firefox
    {
        if (strEvent == 'mousewheel') objObject.removeEventListener('DOMMouseScroll', fncFunction, blnIgnoreBubbledEvents);
        objObject.removeEventListener(strEvent, fncFunction, blnIgnoreBubbledEvents);
        return true;
    }
    else if (objObject.detachEvent) // IE
    {
        return objObject.detachEvent("on" + strEvent, fncFunction);
    }
    else {
        eval("objObject.on" + strEvent + " = null;");
        return true;
    }
}

//Cancels an event from occurring
//e: Event to cancel
function cancelEvent(e) {
    if (e.stopPropagation) { e.stopPropagation(); } else if (e.cancelBubble != null) { e.cancelBubble = true; }
    if (e.preventDefault) { e.preventDefault(); } else if (e.returnValue != null) { e.returnValue = false; }
    return false;
}

//Retrieves the KeyCode from an event
//e: Event to get the KeyCode from
function eventKeyCode(e) {
    if (e.which) return e.which;
    else if (e.keyCode) return e.keyCode;
    else return e.charCode;
}

//Retrieves the normalized scroll wheel change amount (delta)
//e: Scroll event to calculate the delta from
function eventMouseScrollDelta(e)
{
    return parseDecimal(e.detail ? e.detail * -1/3 : e.wheelDelta / 120);
}

/************************* Key Code Comparison Methods ********************************/

function eventKeyCodeOnKeyPress(e, intKeyCode) {
    var strCallee = "A function to determine a KeyCode was called that ";
    if (eventKeyCodeOnKeyPress.caller) {
        strCallee = eventKeyCodeOnKeyPress.caller.toString();
        strCallee = strCallee.substring(strCallee.indexOf(' ') + 1, strCallee.indexOf('('));
    }
    if (!eventType_KEYPRESS(e)) return eventKeyCode(e) == intKeyCode;
    else throw new Error(strCallee + " can only be called on 'KeyDown' or 'KeyUp'");
}

function eventKeyCode_SHIFT(e) { return eventKeyCodeOnKeyPress(e, 16); }
function eventKeyCode_CTRL(e) { return eventKeyCodeOnKeyPress(e, 17); }
function eventKeyCode_ALT(e) { return eventKeyCodeOnKeyPress(e, 18); }

function eventKeyCode_SPACE(e) { return eventKeyCode(e) == 32; }
function eventKeyCode_GRAVE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 126 : eventKeyCode(e) == 192; }
function eventKeyCode_TILDE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 96 : (eventKeyCode(e) == 192 && eventKey_SHIFT(e)); }

function eventKeyCode_HYPHEN(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 45 : eventKeyCode(e) == 189; }
function eventKeyCode_UNDERSCORE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 95 : (eventKeyCode(e) == 189 && eventKey_SHIFT(e)); }

function eventKeyCode_EQUALS(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 61 : eventKeyCode(e) == 187; }
function eventKeyCode_PLUS(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 43 : (eventKeyCode(e) == 187 && eventKey_SHIFT(e)); }

function eventKeyCode_LEFTBRACKET(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 91 : eventKeyCode(e) == 219; }
function eventKeyCode_LEFTBRACE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 123 : (eventKeyCode(e) == 219 && eventKey_SHIFT(e)); }

function eventKeyCode_RIGHTBRACKET(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 93 : eventKeyCode(e) == 221; }
function eventKeyCode_RIGHTBRACE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 125 : (eventKeyCode(e) == 221 && eventKey_SHIFT(e)); }

function eventKeyCode_BACKSLASH(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 92 : eventKeyCode(e) == 220; }
function eventKeyCode_BROKENBAR(e) { return eventKeyCode_PIPE(e); }
function eventKeyCode_PIPE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 124 : (eventKeyCode(e) == 220 && eventKey_SHIFT(e)); }

function eventKeyCode_SEMICOLON(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 59 : eventKeyCode(e) == 186; }
function eventKeyCode_COLON(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 58 : (eventKeyCode(e) == 186 && eventKey_SHIFT(e)); }

function eventKeyCode_APOSTROPHE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 39 : eventKeyCode(e) == 222; }
function eventKeyCode_QUOTE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 34 : (eventKeyCode(e) == 222 && eventKey_SHIFT(e)); }

function eventKeyCode_COMMA(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 44 : eventKeyCode(e) == 188; }
function eventKeyCode_LESSTHAN(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 60 : (eventKeyCode(e) == 188 && eventKey_SHIFT(e)); }

function eventKeyCode_PERIOD(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 46 : eventKeyCode(e) == 190; }
function eventKeyCode_GREATERTHAN(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 62 : (eventKeyCode(e) == 190 && eventKey_SHIFT(e)); }

function eventKeyCode_FORWARDSLASH(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 47 : eventKeyCode(e) == 191; }
function eventKeyCode_QUESTIONMARK(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 63 : (eventKeyCode(e) == 191 && eventKey_SHIFT(e)); }

function eventKeyCode_NUMLOCK(e) { return eventKeyCode_KEYPAD_NUMLOCK(e); }
function eventKeyCode_KEYPAD_NUMLOCK(e) { return eventKeyCodeOnKeyPress(e, 144); }
function eventKeyCode_KEYPAD_DIVIDE(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 47 : eventKeyCode(e) == 111; }
function eventKeyCode_KEYPAD_MULTIPLY(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 42 : eventKeyCode(e) == 106; }
function eventKeyCode_KEYPAD_MINUS(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 45 : eventKeyCode(e) == 109; }
function eventKeyCode_KEYPAD_PLUS(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 43 : eventKeyCode(e) == 107; }
function eventKeyCode_KEYPAD_ENTER(e) { return eventKeyCode_ENTER(e); }
function eventKeyCode_KEYPAD_DECIMAL(e) { return eventKeyCode_KEYPAD_PERIOD(e); }
function eventKeyCode_KEYPAD_PERIOD(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 46 : eventKeyCode(e) == 110; }

function eventKeyCode_KEYPAD_0(e) { return eventType_KEYPRESS(e) ? eventKeyCode_0(e) : eventKeyCode(e) == 96; }
function eventKeyCode_KEYPAD_1(e) { return eventType_KEYPRESS(e) ? eventKeyCode_1(e) : eventKeyCode(e) == 97; }
function eventKeyCode_KEYPAD_2(e) { return eventType_KEYPRESS(e) ? eventKeyCode_2(e) : eventKeyCode(e) == 98; }
function eventKeyCode_KEYPAD_3(e) { return eventType_KEYPRESS(e) ? eventKeyCode_3(e) : eventKeyCode(e) == 99; }
function eventKeyCode_KEYPAD_4(e) { return eventType_KEYPRESS(e) ? eventKeyCode_4(e) : eventKeyCode(e) == 100; }
function eventKeyCode_KEYPAD_5(e) { return eventType_KEYPRESS(e) ? eventKeyCode_5(e) : eventKeyCode(e) == 101; }
function eventKeyCode_KEYPAD_6(e) { return eventType_KEYPRESS(e) ? eventKeyCode_6(e) : eventKeyCode(e) == 102; }
function eventKeyCode_KEYPAD_7(e) { return eventType_KEYPRESS(e) ? eventKeyCode_7(e) : eventKeyCode(e) == 103; }
function eventKeyCode_KEYPAD_8(e) { return eventType_KEYPRESS(e) ? eventKeyCode_8(e) : eventKeyCode(e) == 104; }
function eventKeyCode_KEYPAD_9(e) { return eventType_KEYPRESS(e) ? eventKeyCode_9(e) : eventKeyCode(e) == 105; }

function eventKeyCode_1(e) { return eventKeyCode(e) == 49; }
function eventKeyCode_EXCLAMATIONPOINT(e) { return eventKeyCode_EXCLAMATIONMARK(e); }
function eventKeyCode_EXCLAMATIONMARK(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 33 : (eventKeyCode(e) == 49 && eventKey_SHIFT(e)); }
function eventKeyCode_2(e) { return eventKeyCode(e) == 50; }
function eventKeyCode_AT(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 64 : (eventKeyCode(e) == 50 && eventKey_SHIFT(e)); }
function eventKeyCode_3(e) { return eventKeyCode(e) == 51; }
function eventKeyCode_POUND(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 35 : (eventKeyCode(e) == 51 && eventKey_SHIFT(e)); }
function eventKeyCode_4(e) { return eventKeyCode(e) == 52; }
function eventKeyCode_DOLLAR(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 36 : (eventKeyCode(e) == 52 && eventKey_SHIFT(e)); }
function eventKeyCode_5(e) { return eventKeyCode(e) == 53; }
function eventKeyCode_PERCENT(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 37 : (eventKeyCode(e) == 53 && eventKey_SHIFT(e)); }
function eventKeyCode_6(e) { return eventKeyCode(e) == 54; }
function eventKeyCode_CARET(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 94 : (eventKeyCode(e) == 54 && eventKey_SHIFT(e)); }
function eventKeyCode_7(e) { return eventKeyCode(e) == 55; }
function eventKeyCode_AMPERSAND(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 38 : (eventKeyCode(e) == 55 && eventKey_SHIFT(e)); }
function eventKeyCode_8(e) { return eventKeyCode(e) == 56; }
function eventKeyCode_ASTERISK(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 42 : (eventKeyCode(e) == 56 && eventKey_SHIFT(e)); }
function eventKeyCode_9(e) { return eventKeyCode(e) == 57; }
function eventKeyCode_LEFTPARENTHESES(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 40 : (eventKeyCode(e) == 57 && eventKey_SHIFT(e)); }
function eventKeyCode_0(e) { return eventKeyCode(e) == 48; }
function eventKeyCode_RIGHTPARENTHESES(e) { return eventType_KEYPRESS(e) ? eventKeyCode(e) == 41 : (eventKeyCode(e) == 48 && eventKey_SHIFT(e)); }

function eventKeyCode_PAGEUP(e) { return eventKeyCodeOnKeyPress(e, 33); }
function eventKeyCode_PAGEDOWN(e) { return eventKeyCodeOnKeyPress(e, 34); }
function eventKeyCode_END(e) { return eventKeyCodeOnKeyPress(e, 35); }
function eventKeyCode_HOME(e) { return eventKeyCodeOnKeyPress(e, 36); }
function eventKeyCode_ARROWLEFT(e) { return eventKeyCodeOnKeyPress(e, 37); }
function eventKeyCode_ARROWUP(e) { return eventKeyCodeOnKeyPress(e, 38); }
function eventKeyCode_ARROWRIGHT(e) { return eventKeyCodeOnKeyPress(e, 39); }
function eventKeyCode_ARROWDOWN(e) { return eventKeyCodeOnKeyPress(e, 40); }

function eventKeyCode_BACKSPACE(e) { return eventKeyCodeOnKeyPress(e, 8); }
function eventKeyCode_TAB(e) { return eventKeyCodeOnKeyPress(e, 9); }
function eventKeyCode_ENTER(e) { return eventKeyCode(e) == 13; }
function eventKeyCode_CAPSLOCK(e) { return eventKeyCodeOnKeyPress(e, 20); }
function eventKeyCode_ESCAPE(e) { return eventKeyCode(e) == 27; }
function eventKeyCode_INSERTDELETE(e) { return eventKeyCodeOnKeyPress(e, 45); }
function eventKeyCode_PRINTSCREEN(e) { return eventKeyCode_SYSTEMREQUEST(e); }
function eventKeyCode_SYSTEMREQUEST(e) { if (eventType_KEYUP(e)) { return eventKeyCode(e) == 44; } else { throw new Error("eventKeyCode_PRINTSCREEN/SYSTEMREQUEST can only be called on 'KeyUp'"); } }
function eventKeyCode_SCROLLLOCK(e) { return eventKeyCodeOnKeyPress(e, 145); }
function eventKeyCode_PAUSE(e) { return eventKeyCode_BREAK(e); }
function eventKeyCode_BREAK(e) { return eventKeyCodeOnKeyPress(e, 19); }
function eventKeyCode_DELETE(e) { return eventKeyCodeOnKeyPress(e, 46); }
function eventKeyCode_MUTE(e) { return eventKeyCodeOnKeyPress(e, 173); }
function eventKeyCode_PLAY(e) { return eventKeyCode_PAUSE(e); }
function eventKeyCode_PAUSE(e) { return eventKeyCodeOnKeyPress(e, 179); }
function eventKeyCode_STOP(e) { return eventKeyCodeOnKeyPress(e, 178); }
function eventKeyCode_VOLUMEUP(e) { return eventKeyCodeOnKeyPress(e, 175); }
function eventKeyCode_VOLUMEDOWN(e) { return eventKeyCodeOnKeyPress(e, 174); }
function eventKeyCode_PREVIOUSTRACK(e) { return eventKeyCodeOnKeyPress(e, 177); }
function eventKeyCode_NEXTTRACK(e) { return eventKeyCodeOnKeyPress(e, 176); }

function eventKeyCode_MEDIA(e) { return eventKeyCodeOnKeyPress(e, 181); }
function eventKeyCode_MAIL(e) { return eventKeyCodeOnKeyPress(e, 180); }
function eventKeyCode_WEB(e) { return eventKeyCodeOnKeyPress(e, 172); }

function eventKeyCode_CALCULATOR(e) { return eventKeyCodeOnKeyPress(e, 183); }
function eventKeyCode_SPECIAL(e) { return eventKeyCodeOnKeyPress(e, 255); }

function eventKeyCode_F1(e) { return eventKeyCodeOnKeyPress(e, 112); }
function eventKeyCode_F2(e) { return eventKeyCodeOnKeyPress(e, 113); }
function eventKeyCode_F3(e) { return eventKeyCodeOnKeyPress(e, 114); }
function eventKeyCode_F4(e) { return eventKeyCodeOnKeyPress(e, 115); }
function eventKeyCode_F5(e) { return eventKeyCodeOnKeyPress(e, 116); }
function eventKeyCode_F6(e) { return eventKeyCodeOnKeyPress(e, 117); }
function eventKeyCode_F7(e) { return eventKeyCodeOnKeyPress(e, 118); }
function eventKeyCode_F8(e) { return eventKeyCodeOnKeyPress(e, 119); }
function eventKeyCode_F9(e) { return eventKeyCodeOnKeyPress(e, 120); }
function eventKeyCode_F10(e) { return eventKeyCodeOnKeyPress(e, 121); }
function eventKeyCode_F11(e) { return eventKeyCodeOnKeyPress(e, 122); }
function eventKeyCode_F12(e) { return eventKeyCodeOnKeyPress(e, 123); }

function eventKeyCode_ALPHABETIC(e) {
    var iKeyCode = eventKeyCode(e);
    if (eventType_KEYPRESS(e) && !eventKey_SHIFT(e)) iKeyCode -= 32; //lower case letter, subtract 32 to make it upper
    return (iKeyCode > 64 && iKeyCode < 91);
}
function eventKeyCode_NUMERIC(e) {
    if (eventType_KEYPRESS(e)) return (eventKeyCode(e) > 47 && eventKeyCode(e) < 58);
    else return (eventKeyCode(e) > 47 && eventKeyCode(e) < 58) || (eventKeyCode(e) > 95 && eventKeyCode(e) < 106);
}
function eventKeyCode_ALPHANUMERIC(e) { return eventKeyCode_ALPHABETIC(e) || eventKeyCode_NUMERIC(e); }
function eventKeyCode_PUNCTUATION(e) { return true; }
function eventKeyCode_NAVIGATION(e) { return eventKeyCode_PAGEUP(e) || eventKeyCode_PAGEDOWN(e) || eventKeyCode_END(e) || eventKeyCode_HOME(e) || eventKeyCode_ARROWRIGHT(e) || eventKeyCode_ARROWUP(e) || eventKeyCode_ARROWLEFT(e) || eventKeyCode_ARROWDOWN(e); }

/************************* Keystroke Comparison Methods ********************************/

function eventKey_SHIFT(e) { return e.shiftKey; }
function eventKey_CTRL(e) {
    if (eventType_KEYDOWN(e)) return e.ctrlKey;
    else throw new Error("eventKey_CTRL can only be called on 'KeyDown'");
}
function eventKey_ALT(e) {
    if (eventType_KEYDOWN(e)) return e.altKey;
    else throw new Error("eventKey_ALT can only be called on 'KeyDown'");
}

/*************************** Event Type Comparison Methods ****************************/

function eventType_KEYDOWN(e) { return e.type == "keydown"; }
function eventType_KEYUP(e) { return e.type == "keyup"; }
function eventType_KEYPRESS(e) { return e.type == "keypress"; }
function eventType_CLICK(e) { return e.type == "click"; }
function eventType_FOCUS(e) { return e.type == "focus"; }
function eventType_BLUR(e) { return e.type == "blur"; }

/**************************************************************************************/

//Retrieves the char entered from an event
//e: Event to get the char entered from
function eventCharacter(e) { return String.fromCharCode(eventKeyCode(e)); }

//Retrieves the element that triggered an event
//e: Event to get the element from
function eventElement(e) {
    var retval;

    if (e.target) retval = e.target;
    else if (e.currentTarget) retval = e.currentTarget;
    else if (e.srcElement) retval = e.srcElement;
    // Fix Safari bug
    if (retval.nodeType == 3) retval = retval.parentNode;

    return retval;
}

//Retrieves the value from the element that triggered an event
//e: Event to get the element from
function eventElementValue(e) {
    var eElement = eventElement(e);
    if (eElement.value) return eElement.value;
    else if (eElement.innerText) return eElement.innerText;
    else if (eElement.innerHTML) return eElement.innerHTML;
    else throw new Error("Could not retrieve value for \"" + eElement.id + "\".");
}