var authProxy = "/pixetell_displays/pwsProxy/PixetellAuth_proxy.php";
var loginProxy = "/pixetell_displays/pwsProxy/PixetellLogin_proxy.php";
var usingOpenID = true;
var takingLongTimeID;
var savedUsername;
var savedPW;
var upgradingFromBasic = false;

function newSignUp(){
	window.location.href = "/download";
	/*$("#PixetellBanner_Home").css("left", "-100%");
	$.get("pixetell_displays/PackageOptions.html", function(data) {
			$(data).filter("#PriceGrid").appendTo("#PixetellBanner_Replace");
			$("#PixetellBanner_Replace").css("left", "50%");
	});*/
}

function usernameCheckForEnter(e){
	if(e && e.keyCode==13){
		if(usingOpenID) login();
	}
}

function passwordCheckForEnter(e){
	if(e && e.keyCode==13){
		if(!usingOpenID) login();
	}
}

function loginCheckForEnter(e){
	if(e && e.keyCode==13){
		login();
	}
}

function login(){
	$("#spinner").css({visibility:"visible"});
	
	savedUsername = $("#fieldUsername")[0].value;
	
	if(usingOpenID){
		if($("#fieldUsername")[0].value && $("#fieldUsername")[0].value.length > 3){
			//var soapMsg = createSoapReqFor("checkUserStatusForPreLogin", [$("#fieldUsername")[0].value], true);
		
			$.ajax({
				url: authProxy,
				type: "POST",
				data: {email:savedUsername},
				dataType: "json",
				error: openIDLoginError,
				success: openIDLoginResult
			});
			checkForLongWait();
		} else {
			setLoginErrorMsg('Must be more than 3 characters.', true);
			$("#fieldUsername").focus();
		}
	} else {
		//var soapMsg = createSoapReqFor("login", [$("#fieldUsername")[0].value, MD5()], false);
		savedPW = MD5($("#fieldPassword")[0].value);
		
		$.ajax({
			url: loginProxy,
			type: "POST",
			data: {email:savedUsername, pw:savedPW},
			dataType: "json",
			error: loginError,
			success: loginResult
		});
		checkForLongWait();
	}
	
	return false;
}

function checkForLongWait(){
	killLongWaitCheck();
	takingLongTimeID = setTimeout(takingLongTime, 6000);	
}

function killLongWaitCheck(){
	if(takingLongTimeID) clearTimeout(takingLongTimeID);
}

function takingLongTime(){
	setLoginErrorMsg('Login is taking longer than usual, still trying, please wait...', false);
}

function setLoginErrorMsg(errorMsg, hideSpinner){
	killLongWaitCheck();
	$("#errorMsg").html(errorMsg);
	if(hideSpinner) $("#spinner").css({visibility:"hidden"});
}

function openIDLoginError(XMLHttpRequest, textStatus, errorThrown){
	killLongWaitCheck();
	showPasswordArea();
}

function openIDLoginResult(data){
	killLongWaitCheck();
	
	var loginmethod = 0;
	
	loginmethod = data["return"].loginMethod;
	
	switch(parseInt(loginmethod)){	
		case 2:
		setLoginErrorMsg('', true);
		// Need to route to google login and redirect in
		alert("You provided an Open ID login, likely from Google Apps. We are redirecting you to google login. You will be returned here after logging in with Google.");
		window.location.href = "https://p001.pixetell.com/openIDAuth?from=upgradePage&domain=" + encodeURIComponent(savedUsername) + "&openid=true";
		break;
		
		default:
		showPasswordArea();
		break;
	}
}

function showPasswordArea(){
	usingOpenID = false;
	$("#labelForgotPassword").css({visibility:"visible"});
	$("#usernameArea").css({paddingTop:"0px"});
	$("#labelPassword").css({display:"inline"});
	$("#passwordArea").css({display:"inline"});
	$("#fieldPassword").focus();
	setLoginErrorMsg('Please enter your password', true);
	
	$("#fieldUsername").attr("tabindex", "100");
	$("#fieldPassword").attr("tabindex", "101");
	$("#loginBtn").attr("tabindex", "102");
	
	/*document.onkeydown = function(e) {handleKeys(e)}
	document.onkeypress = function(e) {handleKeys(e)}*/
}

function loginError(XMLHttpRequest, textStatus, errorThrown){
	setLoginErrorMsg('An error occurred, please try again.', true);
}

function loginResult(data){
	killLongWaitCheck();
	// reset upgrade flag
	upgradingFromBasic = false;
	
	var returnObj = data["return"];
	
	if(returnObj && returnObj.displayName){
		setLoginErrorMsg('', true);
		
		var fullName = returnObj.displayName;
		if(fullName) $("#DisplayNameArea").html("Welcome <strong>" + fullName + "</strong>, here are your upgrade options.");
		$("#DisplayNameArea").css({display:"block"});
		
		if(packageSelection){
			// Check if basic user who selected pro from pricing page
			if(returnObj.productInfo && returnObj.productInfo.productName){
				var currentPackage = returnObj.productInfo.productName;
				if(currentPackage && currentPackage.indexOf("Basic") > -1){
					// Display message that request has been sent, do not allow basic user to go through pro path
					var dataString = 'username='+ savedUsername;   
         			$.ajax({ type:"POST", url:"/basicToProEmail.php", data: dataString });
					upgradingFromBasic = true;
					setLoginErrorMsg('', true);
					showAvailablePackages(true, false, true);
					return;
				}
			}
			// Logged in, go to flash store
			window.location.href = "https://" + baseHost + ".pixetell.com/buy-pixetell?pk=" + packageSelection + 
			(savedUsername ? "&email=" + savedUsername : "") +
			(savedPW ? "&ck=" + savedPW : "");
			return;
		}
		
		if(returnObj.productInfo && returnObj.productInfo.productName){
			var currentPackage = returnObj.productInfo.productName;
			
			if(currentPackage && currentPackage.indexOf("Basic") > -1){
				upgradingFromBasic = true;
				setLoginErrorMsg('', true);
				showAvailablePackages(true, false, false);
				return;
			} 
			
			if(currentPackage && (currentPackage.indexOf("Pro") > -1 || currentPackage.indexOf("Onterian") > -1 || 
									currentPackage.indexOf("Unlimited") > -1 || currentPackage.indexOf("Platinum") > -1)){
				setLoginErrorMsg('', true);
				showAvailablePackages(true, true, false);
				return;
			} 
		} 
		showAvailablePackages(false, false, false);
		
	} else {
		setLoginErrorMsg('Check your password and try again.', true);
		$("#fieldPassword").focus();
	}
}

function showAvailablePackages(disableBasic, disablePro, lockOutPro){
	if(!isIE){
		$("#LoginBox").css("opacity", "0");
		$("#SignUpBox").css("opacity", "0");
		setTimeout(removeBoxes, 300);
	} else {
		removeBoxes();
	}
	
	$.get("/pixetell_displays/PackageOptions.html", function(data) {
			$(data).filter("#PriceGrid").appendTo("#UpgradePackageArea");
			$("#PriceGrid").css({width:"470px"});
			$("#UpgradePackageArea").css({display:"block"});
			$("#UpgradePackageWrapper").css({paddingRight:"100px"});
			$("#backPackage1").css({display:"none"});
			$("#backPackage3").css({display:"none"});
			$("#backPackage2").css({left:"0px"});
			$("#backPackage2").css({top:"20px"});
			$("#backPackageFront").css({left:"230px"});
			$("#backPackageFront").css({top:"0px"});
			$("#backPackageFront").css({zIndex:"10"});
			
			if(disableBasic && !disablePro){
				// Create basic overlay
				$("#UpgradePackageArea").append("<div id='OverlayPackage'><div id='OverlayPackageText'>You currently subscribe to this package.</div></div>");
				if(isIE) $('#OverlayPackage').css("filter", "alpha(opacity=70)");	
				if(lockOutPro) displayBasicToProMsg();
			} else if(disableBasic && disablePro){
				// Create basic and pro overlay
				$("#UpgradePackageArea").append("<div id='OverlayPackage'><div id='OverlayPackageText'></div></div><div id='OverlayPackagePro'><div id='OverlayPackageProText'>You currently have Pro.<br/>Please call (888) 802-0684<br/>to learn more.</div></div>");
				if(isIE) $('#OverlayPackage').css("filter", "alpha(opacity=70)");	
				if(isIE) $('#OverlayPackagePro').css("filter", "alpha(opacity=80)");	
			}
	});	
}

function removeBoxes(){
	$("#LoginBox").css("display", "none");
	$("#SignUpBox").css("display", "none");
}

function createSoapReqFor(serviceName, params, useNS){
	var soapMsg = createSoapMsgFor(serviceName, params, useNS);
	
	return soapMsg;
}
	
function createSoapMsgFor(serviceName, params, useNS){
	var reqFormat = getSoapReqFormatFor(serviceName, params);
	var soapMsg = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
	 "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">" +
	 "<soap:Body>" +
	 "<" + (useNS ? "ns:" : "") + serviceName + " xmlns" + (useNS ? ":ns" : "") + "=\"http://ws.pixetell.ontier.com\">" +
	 reqFormat +
	 "</" + (useNS ? "ns:" : "") + serviceName + ">" +
	 "</soap:Body>" +
	 "</soap:Envelope>";
	
	return soapMsg;
}

function getSoapReqFormatFor(serviceName, params){
	var reqFormat;
	switch (getIntForServiceName(serviceName)) {
		case 1:
			reqFormat = "<login>" + params[0] + "</login>" +
					 "<password>" + params[1] + "</password>" +
					 "<propertyStrongNames>BillingServer.LicensedComponents</propertyStrongNames>" +
					 "<currentClientVersion>1.4.0.0</currentClientVersion>";
			break;
			
		case 2:
			reqFormat = "<ns:email>" + params[0] + "</ns:email>";
			break;
			
		default:
			reqFormat = "<none></none>";
			break;
	}
	return reqFormat;
}

function getIntForServiceName(serviceName){
	// Using string to int definitions here
	if (serviceName=="login") {
		return 1;
	}
	
	if(serviceName=="checkUserStatusForPreLogin"){
		return 2;
	}
		
	return 0;
}

function forgotPassword(){
	window.location.href = "https://" + baseHost + ".pixetell.com/rd/ForgotPassword";
	return false;
}

var MD5 = function (string) {
 
	function RotateLeft(lValue, iShiftBits) {
		return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
	}
 
	function AddUnsigned(lX,lY) {
		var lX4,lY4,lX8,lY8,lResult;
		lX8 = (lX & 0x80000000);
		lY8 = (lY & 0x80000000);
		lX4 = (lX & 0x40000000);
		lY4 = (lY & 0x40000000);
		lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
		if (lX4 & lY4) {
			return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
		}
		if (lX4 | lY4) {
			if (lResult & 0x40000000) {
				return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
			} else {
				return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
			}
		} else {
			return (lResult ^ lX8 ^ lY8);
		}
 	}
 
 	function F(x,y,z) { return (x & y) | ((~x) & z); }
 	function G(x,y,z) { return (x & z) | (y & (~z)); }
 	function H(x,y,z) { return (x ^ y ^ z); }
	function I(x,y,z) { return (y ^ (x | (~z))); }
 
	function FF(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};
 
	function GG(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};
 
	function HH(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};
 
	function II(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};
 
	function ConvertToWordArray(string) {
		var lWordCount;
		var lMessageLength = string.length;
		var lNumberOfWords_temp1=lMessageLength + 8;
		var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
		var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
		var lWordArray=Array(lNumberOfWords-1);
		var lBytePosition = 0;
		var lByteCount = 0;
		while ( lByteCount < lMessageLength ) {
			lWordCount = (lByteCount-(lByteCount % 4))/4;
			lBytePosition = (lByteCount % 4)*8;
			lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
			lByteCount++;
		}
		lWordCount = (lByteCount-(lByteCount % 4))/4;
		lBytePosition = (lByteCount % 4)*8;
		lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
		lWordArray[lNumberOfWords-2] = lMessageLength<<3;
		lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
		return lWordArray;
	};
 
	function WordToHex(lValue) {
		var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
		for (lCount = 0;lCount<=3;lCount++) {
			lByte = (lValue>>>(lCount*8)) & 255;
			WordToHexValue_temp = "0" + lByte.toString(16);
			WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
		}
		return WordToHexValue;
	};
 
	function Utf8Encode(string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
 
		for (var n = 0; n < string.length; n++) {
 
			var c = string.charCodeAt(n);
 
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
 
		}
 
		return utftext;
	};
 
	var x=Array();
	var k,AA,BB,CC,DD,a,b,c,d;
	var S11=7, S12=12, S13=17, S14=22;
	var S21=5, S22=9 , S23=14, S24=20;
	var S31=4, S32=11, S33=16, S34=23;
	var S41=6, S42=10, S43=15, S44=21;
 
	string = Utf8Encode(string);
 
	x = ConvertToWordArray(string);
 
	a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
 
	for (k=0;k<x.length;k+=16) {
		AA=a; BB=b; CC=c; DD=d;
		a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
		d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
		c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
		b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
		a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
		d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
		c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
		b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
		a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
		d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
		c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
		b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
		a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
		d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
		c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
		b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
		a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
		d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
		c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
		b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
		a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
		d=GG(d,a,b,c,x[k+10],S22,0x2441453);
		c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
		b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
		a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
		d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
		c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
		b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
		a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
		d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
		c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
		b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
		a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
		d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
		c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
		b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
		a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
		d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
		c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
		b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
		a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
		d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
		c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
		b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
		a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
		d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
		c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
		b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
		a=II(a,b,c,d,x[k+0], S41,0xF4292244);
		d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
		c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
		b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
		a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
		d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
		c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
		b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
		a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
		d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
		c=II(c,d,a,b,x[k+6], S43,0xA3014314);
		b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
		a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
		d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
		c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
		b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
		a=AddUnsigned(a,AA);
		b=AddUnsigned(b,BB);
		c=AddUnsigned(c,CC);
		d=AddUnsigned(d,DD);
	}
 
	var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
 
	return temp.toLowerCase();
}

var nonChar = false;
function handleKeys(e) {
    var char;
    var evt = (e) ? e : window.event;       //IE reports window.event not arg
    if (evt.type == "keydown") {
        char = evt.keycode;
        if (char <16 ||                    // non printables
            (char> 16 && char <32) ||     // avoid shift
            (char> 32 && char <41) ||     // navigation keys
            char == 46) {                   // Delete Key (Add to these if you need)
            handleNonChar(char);            // function to handle non Characters
            nonChar = true;
        } else
            nonChar = false;
    } else {                                // This is keypress
        if (nonChar) return;                // Already Handled on keydown
        char = (evt.charCode) ?
                   evt.charCode : evt.keyCode;
        if (char> 31 && char <256)        // safari and opera
            handleChar(char);               //
    }
    if (e)                                  // Non IE
        Event.stop(evt);                    // Using prototype
    else if (evt.keyCode == 8)              // Catch IE backspace
        evt.returnValue = false;            // and stop it!
}

shortcut = {
	'all_shortcuts':{},//All the shortcuts are stored in this array
	'add': function(shortcut_combination,callback,opt) {
		//Provide a set of default options
		var default_options = {
			'type':'keydown',
			'propagate':false,
			'disable_in_input':false,
			'target':document,
			'keycode':false
		}
		if(!opt) opt = default_options;
		else {
			for(var dfo in default_options) {
				if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
			}
		}

		var ele = opt.target;
		if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
		var ths = this;
		shortcut_combination = shortcut_combination.toLowerCase();

		//The function to be called at keypress
		var func = function(e) {
			e = e || window.event;
			
			if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
				var element;
				if(e.target) element=e.target;
				else if(e.srcElement) element=e.srcElement;
				if(element.nodeType==3) element=element.parentNode;

				if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
			}
	
			//Find Which key is pressed
			if (e.keyCode) code = e.keyCode;
			else if (e.which) code = e.which;
			var character = String.fromCharCode(code).toLowerCase();
			
			if(code == 188) character=","; //If the user presses , when the type is onkeydown
			if(code == 190) character="."; //If the user presses , when the type is onkeydown

			var keys = shortcut_combination.split("+");
			//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
			var kp = 0;
			
			//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
			var shift_nums = {
				"`":"~",
				"1":"!",
				"2":"@",
				"3":"#",
				"4":"$",
				"5":"%",
				"6":"^",
				"7":"&",
				"8":"*",
				"9":"(",
				"0":")",
				"-":"_",
				"=":"+",
				";":":",
				"'":"\"",
				",":"<",
				".":">",
				"/":"?",
				"\\":"|"
			}
			//Special Keys - and their codes
			var special_keys = {
				'esc':27,
				'escape':27,
				'tab':9,
				'space':32,
				'return':13,
				'enter':13,
				'backspace':8,
	
				'scrolllock':145,
				'scroll_lock':145,
				'scroll':145,
				'capslock':20,
				'caps_lock':20,
				'caps':20,
				'numlock':144,
				'num_lock':144,
				'num':144,
				
				'pause':19,
				'break':19,
				
				'insert':45,
				'home':36,
				'delete':46,
				'end':35,
				
				'pageup':33,
				'page_up':33,
				'pu':33,
	
				'pagedown':34,
				'page_down':34,
				'pd':34,
	
				'left':37,
				'up':38,
				'right':39,
				'down':40,
	
				'f1':112,
				'f2':113,
				'f3':114,
				'f4':115,
				'f5':116,
				'f6':117,
				'f7':118,
				'f8':119,
				'f9':120,
				'f10':121,
				'f11':122,
				'f12':123
			}
	
			var modifiers = { 
				shift: { wanted:false, pressed:false},
				ctrl : { wanted:false, pressed:false},
				alt  : { wanted:false, pressed:false},
				meta : { wanted:false, pressed:false}	//Meta is Mac specific
			};
                        
			if(e.ctrlKey)	modifiers.ctrl.pressed = true;
			if(e.shiftKey)	modifiers.shift.pressed = true;
			if(e.altKey)	modifiers.alt.pressed = true;
			if(e.metaKey)   modifiers.meta.pressed = true;
                        
			for(var i=0; k=keys[i],i<keys.length; i++) {
				//Modifiers
				if(k == 'ctrl' || k == 'control') {
					kp++;
					modifiers.ctrl.wanted = true;

				} else if(k == 'shift') {
					kp++;
					modifiers.shift.wanted = true;

				} else if(k == 'alt') {
					kp++;
					modifiers.alt.wanted = true;
				} else if(k == 'meta') {
					kp++;
					modifiers.meta.wanted = true;
				} else if(k.length > 1) { //If it is a special key
					if(special_keys[k] == code) kp++;
					
				} else if(opt['keycode']) {
					if(opt['keycode'] == code) kp++;

				} else { //The special keys did not match
					if(character == k) kp++;
					else {
						if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
							character = shift_nums[character]; 
							if(character == k) kp++;
						}
					}
				}
			}
			
			if(kp == keys.length && 
						modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
						modifiers.shift.pressed == modifiers.shift.wanted &&
						modifiers.alt.pressed == modifiers.alt.wanted &&
						modifiers.meta.pressed == modifiers.meta.wanted) {
				callback(e);
	
				if(!opt['propagate']) { //Stop the event
					//e.cancelBubble is supported by IE - this will kill the bubbling process.
					e.cancelBubble = true;
					e.returnValue = false;
	
					//e.stopPropagation works in Firefox.
					if (e.stopPropagation) {
						e.stopPropagation();
						e.preventDefault();
					}
					return false;
				}
			}
		}
		this.all_shortcuts[shortcut_combination] = {
			'callback':func, 
			'target':ele, 
			'event': opt['type']
		};
		//Attach the function with the event
		if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
		else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
		else ele['on'+opt['type']] = func;
	},

	//Remove the shortcut - just specify the shortcut and I will remove the binding
	'remove':function(shortcut_combination) {
		shortcut_combination = shortcut_combination.toLowerCase();
		var binding = this.all_shortcuts[shortcut_combination];
		delete(this.all_shortcuts[shortcut_combination])
		if(!binding) return;
		var type = binding['event'];
		var ele = binding['target'];
		var callback = binding['callback'];

		if(ele.detachEvent) ele.detachEvent('on'+type, callback);
		else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
		else ele['on'+type] = false;
	}
}