(function($) {
	$(document).ready(function(){ $(".jsKeyboard").jsKeyboard(true); });
	
	$.fn.jsKeyboard = function(enable) {
		hideKeyboard();
		if(typeof(enable)=="undefined" || enable!=false) return this.bind("mouseup", doOnMouseUp).bind("dblclick", showKeyboard).bind("contextmenu", rightClick);
		else return this.unbind("mouseup", doOnMouseUp).unbind("dblclick", showKeyboard).unbind("contextmenu", rightClick);
	};
	
	function rightClick(event, input){
		setSelection(input ? input : this);
		var kbd = $("#jsKeyboard");
		var secondClick = kbd.hasClass("secondClick");
		kbd.addClass("secondClick");
		return secondClick;
	}

	function setSelection(input) {
		if(document.selection){
			var self = $(input);
			self.attr("mySelectionStart", self.val() ? getSelectionStart(input) : 0);
			self.attr("mySelectionEnd", self.val() ? getSelectionEnd(input) : 0);
			self.attr("selectionStart", self.attr("mySelectionStart"));
			self.attr("selectionEnd", self.attr("mySelectionEnd"));
		}
	}

	function getSelectionStart(input) {
		var range = document.selection.createRange().duplicate();
		range.moveEnd('character', input.value.length);
		return range.text=="" ? input.value.length : input.value.lastIndexOf(range.text);
	}

	function getSelectionEnd(input){
		var range = document.selection.createRange().duplicate();
		range.moveStart('character', -input.value.length);
		return range.text.length;
	}

	function findPos(obj) {
		var curleft = obj.offsetLeft || 0;
		var curtop = obj.offsetTop || 0;
		while (obj = obj.offsetParent) {
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
		return {x:curleft, y:curtop};
	}

	function doOnMouseUp(event){
		setSelection(this);
		if(event.button==2 && !$(this).hasClass("jsKeyboardFocus")) showKeyboard(event, this);
	}

	function showKeyboard(event, input){
		hideKeyboard();
		var obj = input ? input : this;
		$(obj).addClass("jsKeyboardFocus");
		createKeyboard(obj);
		setSelection(obj);
		if(!input) rightClick(event, input);
	}

	function hideKeyboard(event){
		$("#jsKeyboard").remove();
		$(".jsKeyboardFocus").removeClass("jsKeyboardFocus");
	}

	function createKeyboard(input){
		var pos = findPos(input);
		var kbd = $("<div id='jsKeyboard' style='left:" + pos.x + "px;top:" + (pos.y+input.offsetHeight) + "px;'/>");
		kbd.appendTo(document.body);
		createKeys(kbd, input.lang);
		kbd.toggle("fast");
	}

	function createKeys(kbd, lang){
		kbd.attr("lang", lang).
		append(newKeys(158, 2, 118, 121, {def:'backspace'}, 10)).
		append(newKeys(146, 2, 132, 44, {min:'=', maj:'+', alt:'}'})).
		append(newKeys(2, 14, 17, 110, {def:'tab'}, 10)).
		append(newKeys(41, 14, 44, 0, {min:'e', maj:'E', min_uml:'ë', min_caret:'ê', maj_uml:'Ë', maj_caret:'Ê', min_acute:'é', maj_acute:'É', min_grave:'è', maj_grave:'È', alt:'€'})).
		append(newKeys(53, 14, 44, 22, {min:'r', maj:'R'})).
		append(newKeys(65, 14, 66, 22, {min:'t', maj:'T'})).
		append(newKeys(77, 14, 121, 22, {min:'y', maj:'Y', min_uml:'ÿ'})).
		append(newKeys(89, 14, 77, 22, {min:'u', maj:'U', min_uml:'ü', min_caret:'û', maj_uml:'Ü', maj_caret:'Û', min_acute:'ú', maj_acute:'Ú', min_grave:'ù', maj_grave:'Ù'})).
		append(newKeys(101, 14, 88, 0, {min:'i', maj:'I', min_uml:'ï', min_caret:'î', maj_uml:'Ï', maj_caret:'Î', min_acute:'í', maj_acute:'Í', min_grave:'ì', maj_grave:'Ì'})).
		append(newKeys(113, 14, 11, 22, {min:'o', maj:'O', min_uml:'ö', min_caret:'ô', maj_uml:'Ö', maj_caret:'Ô', min_tilde:'õ', maj_tilde:'Õ', min_acute:'ó', maj_acute:'Ó', min_grave:'ò', maj_grave:'Ò'})).
		append(newKeys(125, 14, 22, 22, {min:'p', maj:'P'})).
		append(newKeys(2, 26, 0, 110, {def:'caplock'}, 13)).
		append(newKeys(32, 26, 55, 22, {min:'s', maj:'S'})).
		append(newKeys(44, 26, 33, 0, {min:'d', maj:'D'})).
		append(newKeys(56, 26, 55, 0, {min:'f', maj:'F'})).
		append(newKeys(68, 26, 66, 0, {min:'g', maj:'G'})).
		append(newKeys(80, 26, 77, 0, {min:'h', maj:'H'})).
		append(newKeys(92, 26, 99, 0, {min:'j', maj:'J'})).
		append(newKeys(104, 26, 110, 0, {min:'k', maj:'K'})).
		append(newKeys(116, 26, 121, 0, {min:'l', maj:'L'})).
		append(newKeys(152, 26, 110, 110, {def:'enter'}, 16)).
		append(newKeys(2, 38, 121, 99, {def:'shift'})).
		append(newKeys(38, 38, 110, 22, {min:'x', maj:'X'})).
		append(newKeys(50, 38, 22, 0, {min:'c', maj:'C'})).
		append(newKeys(62, 38, 88, 22, {min:'v', maj:'V'})).
		append(newKeys(74, 38, 11, 0, {min:'b', maj:'B'})).
		append(newKeys(86, 38, 0, 22, {min:'n', maj:'N', min_tilde:'ñ', maj_tilde:'Ñ'})).
		append(newKeys(146, 38, 66, 121, {def:'shift'}, 22)).
		append(newKeys(46, 50, 0, 121, {min:' '}, 62)).
		append(newKeys(164, 53, 110, 121, {def:'close'}));
		if(lang=='fr'){
			kbd.
			append(newKeys(2, 2, 0, 44, {min:'²', maj:'null'})).
			append(newKeys(14, 2, 11, 44, {min:'&', maj:'1'})).
			append(newKeys(26, 2, 22, 44, {min:'é', maj:'2', alt:'~'})).
			append(newKeys(38, 2, 33, 44, {min:'"', maj:'3', alt:'#'})).
			append(newKeys(50, 2, 44, 44, {min:'\'', maj:'4', alt:'{'})).
			append(newKeys(62, 2, 55, 44, {min:'(', maj:'5', alt:'['})).
			append(newKeys(74, 2, 66, 44, {min:'-', maj:'6', alt:'|'})).
			append(newKeys(86, 2, 77, 44, {min:'è', maj:'7', alt:'`'})).
			append(newKeys(98, 2, 88, 44, {min:'_', maj:'8', alt:'\\'})).
			append(newKeys(110, 2, 99, 44, {min:'ç', maj:'9', alt:'^'})).
			append(newKeys(122, 2, 110, 44, {min:'à', maj:'0', alt:'@'})).
			append(newKeys(134, 2, 121, 44, {min:')', maj:'°', alt:']'})).
			append(newKeys(17, 14, 0, 0, {min:'a', maj:'A', min_uml:'ä', min_caret:'â', maj_uml:'Ä', maj_caret:'Â', min_tilde:'ã', maj_tilde:'Ã', min_acute:'á', maj_acute:'Á', min_grave:'à', maj_grave:'À'})).
			append(newKeys(29, 14, 132, 22, {min:'z', maj:'Z'})).
			append(newKeys(137, 14, 99, 77, {min:'^', maj:'¨'})).
			append(newKeys(149, 14, 11, 88, {min:'$', maj:'£', alt:'¤'})).
			append(newKeys(161, 14, 33, 88, {min:'*', maj:'µ'})).
			append(newKeys(20, 26, 33, 22, {min:'q', maj:'Q'})).
			append(newKeys(128, 26, 132, 0, {min:'m', maj:'M'})).
			append(newKeys(140, 26, 22, 88, {min:'ù', maj:'%'})).
			append(newKeys(14, 38, 132, 88, {min:'<', maj:'>'})).
			append(newKeys(26, 38, 99, 22, {min:'w', maj:'W'})).
			append(newKeys(98, 38, 44, 88, {min:',', maj:'?'})).
			append(newKeys(110, 38, 66, 88, {min:';', maj:'.'})).
			append(newKeys(122, 38, 99, 99, {min:':', maj:'/'})).
			append(newKeys(134, 38, 110, 88, {min:'!', maj:'§'})).
			append(newKeys(113, 50, 31, 110, {def:'alt'}, 18));
		} else {
			kbd.
			append(newKeys(2, 2, 77, 77, {min:'`', maj:'~'})).
			append(newKeys(14, 2, 11, 55, {min:'1', maj:'!'})).
			append(newKeys(26, 2, 22, 55, {min:'2', maj:'@'})).
			append(newKeys(38, 2, 33, 55, {min:'3', maj:'#'})).
			append(newKeys(50, 2, 44, 55, {min:'4', maj:'$'})).
			append(newKeys(62, 2, 55, 55, {min:'5', maj:'%'})).
			append(newKeys(74, 2, 66, 55, {min:'6', maj:'^'})).
			append(newKeys(86, 2, 77, 55, {min:'7', maj:'&'})).
			append(newKeys(98, 2, 88, 55, {min:'8', maj:'*'})).
			append(newKeys(110, 2, 99, 55, {min:'9', maj:'('})).
			append(newKeys(122, 2, 110, 55, {min:'0', maj:')'})).
			append(newKeys(134, 2, 132, 110, {min:'-', maj:'_'})).
			append(newKeys(17, 14, 33, 22, {min:'q', maj:'Q'})).
			append(newKeys(29, 14, 99, 22, {min:'w', maj:'W'})).
			append(newKeys(137, 14, 55, 77, {min:'[', maj:'{'})).
			append(newKeys(149, 14, 121, 77, {min:']', maj:'}'})).
			append(newKeys(161, 14, 88, 77, {min:'\\', maj:'|'})).
			append(newKeys(20, 26, 0, 0, {min:'a', maj:'A'})).
			append(newKeys(128, 26, 55, 99, {min:';', maj:':'})).
			append(newKeys(140, 26, 77, 99, {min:'\'', maj:'\"'})).
			append(newKeys(14, 38, 88, 77, {min:'\\', maj:'|'})).
			append(newKeys(26, 38, 132, 22, {min:'z', maj:'Z'})).
			append(newKeys(98, 38, 132, 0, {min:'m', maj:'M'})).
			append(newKeys(110, 38, 88, 99, {min:',', maj:'<'})).
			append(newKeys(122, 38, 66, 99, {min:'.', maj:'>'})).
			append(newKeys(134, 38, 99, 110, {min:'/', maj:'?'}));
		}
		kbd.find(".jsKeyboardKeys").bind("click", keyboardKeyup);
	}
	
	function newKeys(left, top, bgx, bgy, data, width){
		var isClose = data.def=='close'
		if(isClose) width = 4;
		var div = "<div class='jsKeyboardKeys' style='" + (width ? "width:" + (width+4) + "px;" : "") + (isClose ? "height:8px;cursor:pointer;" : "") + "background-position:-" + bgx + "px -" + bgy + "px;'";
		for(var i in data){
			div += " " + i + "=" + (data[i]!="'" ? "'" + data[i] + "'" : "\"'\"");
			div += " title='" + (data.def ? data.def : (data.min==" " ? "space" : data.min)) + "'";
		}
		div += " />";
		return $("<div class='jsKeyboardTextKeys' style='left:" + left + "px;top:" + top + "px;" + (width ? "width:" + width + "px;" : "") + (isClose ? "height:4px;" : "") + "' />").append($(div));
	}

	function keyboardKeyup(event){
		var input = $(".jsKeyboardFocus");
		var kbd = $("#jsKeyboard");
		if(input.length!=0){
			var self = $(this);
			var isMaj = kbd.hasClass("jsKeyboardMaj");
			var isCap = kbd.hasClass("jsKeyboardCap");
			var isAlt = kbd.hasClass("jsKeyboardAlt");
			var def = self.attr("def");
			var start = Number(document.selection ? input.attr("mySelectionStart") : input.attr("selectionStart"));
			var end = Number(document.selection ? input.attr("mySelectionEnd") : input.attr("selectionEnd"));
			switch(def){
				case "caplock": toggleShift(true);       break;
				case "shift":   toggleShift(false);      break;
				case "alt":     toggleAlt();             break;
				case "enter":   submitForm(input);       break;
				case "tab":     return focusNext(input); break;
				case "close":   return hideKeyboard();   break;
				default:
					var key = self.attr(isAlt ? "alt" : ((isMaj && !isCap) || (!isMaj && isCap) ? "maj" : "min"));
					if(!key) key = self.attr("min")==" " ? " " : "";
					if(kbd.hasClass("jsKeyboardUmlPressed"))   key = getSpecialKey(self, key, "Uml", "¨");
					if(kbd.hasClass("jsKeyboardCaretPressed")) key = getSpecialKey(self, key, "Caret", "^");
					if(kbd.hasClass("jsKeyboardTildePressed")) key = getSpecialKey(self, key, "Tilde", "~");
					if(kbd.hasClass("jsKeyboardGravePressed")) key = getSpecialKey(self, key, "Grave", "`");
					start += key.length>1 ? key.length-1 : 0;
					if(key=="¨") {
						if(isMaj) toggleShift(false);
						kbd.toggleClass("jsKeyboardUmlPressed");
					}
					if(kbd.attr("lang")=='fr'){
						if((key=="^" || key=="~" || key=="`") && kbd.hasClass("jsKeyboardAlt")) toggleAlt();
						if(key=="^") kbd.toggleClass("jsKeyboardCaretPressed");
						if(key=="~") kbd.toggleClass("jsKeyboardTildePressed");
						if(key=="`") kbd.toggleClass("jsKeyboardGravePressed");
					}
					if(!kbd.hasClass("jsKeyboardUmlPressed") && !kbd.hasClass("jsKeyboardCaretPressed") &&
					   !kbd.hasClass("jsKeyboardTildePressed") && !kbd.hasClass("jsKeyboardGravePressed") &&
					   (key!="null" || def)){
						if(def=="backspace" && start==end) start--;
						var val = input.val();
						var newVal = val.substring(0, start);
						if(!def) newVal += key;
						newVal += val.substring(end);
						start += def!="backspace";
						end = start;
						input.val(newVal);
						if(isMaj) toggleShift(false);
						if(isAlt) toggleAlt();
					}
				break;
			}
			input.focus();
			input.attr("selectionStart", start).attr("selectionEnd", end);
			if(document.selection){
				input.attr("mySelectionStart", start);
				input.attr("mySelectionEnd", end);
				var range = input[0].createTextRange();
				range.collapse();
				range.moveStart("character", start);
				range.moveEnd("character", end - start);
				range.select();
			}
		} else hideKeyboard();
	}

	function getSpecialKey(self, key, tag, symbol){
		var kbd = $("#jsKeyboard");
		var isMaj = kbd.hasClass("jsKeyboardMaj");
		var isCap = kbd.hasClass("jsKeyboardCap");
		var value = self.attr(((isMaj && !isCap) || (!isMaj && isCap) ? "maj_" : "min_") + tag.toLowerCase());
		result = value ? value : symbol + key;
		kbd.toggleClass("jsKeyboard" + tag + "Pressed");
		return result;
	}

	function toggleShift(caplock){
		var kbd = $("#jsKeyboard");
		var isMaj = kbd.hasClass("jsKeyboardMaj");
		if(caplock) kbd.toggleClass("jsKeyboardCap").removeClass("jsKeyboardMaj");
		else kbd.toggleClass("jsKeyboardMaj");
		if(!kbd.hasClass("jsKeyboardAlt") && ((caplock && !isMaj) || !caplock)){
			var isMaj = kbd.hasClass("jsKeyboardMaj");
			var isCap = kbd.hasClass("jsKeyboardCap");
			kbd.find("div[maj]").each(function(){
				var bgPos = this.style.backgroundPosition.split(" ");
				var bgPosY = Number(bgPos[1].substring(0, bgPos[1].length-2));
				var newBgPos = bgPos[0] + " ";
				newBgPos += (bgPosY - ((isMaj && !isCap) || (!isMaj && isCap) ? 11 : -11)) + "px";
				this.style.backgroundPosition = newBgPos;
				this.title = $(this).attr(isMaj || isCap ? "maj" : "min");
			});
		}
	}

	function toggleAlt(){
		var kbd = $("#jsKeyboard");
		kbd.toggleClass("jsKeyboardAlt");
		var isCap = kbd.hasClass("jsKeyboardCap");
		if(kbd.hasClass("jsKeyboardAlt")){
			var isMaj = kbd.hasClass("jsKeyboardMaj");
			var bgPos = kbd.find("div[maj=null]")[0].style.backgroundPosition.split(" ");
			var bgPosY = Number(bgPos[1].substring(0, bgPos[1].length-2));
			var newBgPos = bgPos[0] + " " + (bgPosY - (isMaj || isCap ? 0 : 11)) + "px";
			kbd.find("div[alt]").removeAttr("maj").each(function(){
				var bgPos = this.style.backgroundPosition.split(" ");
				this.style.backgroundPosition = bgPos[0] + " -77px";
				this.title = $(this).attr("alt");
			});
			kbd.find("div[maj]").css("background-position", newBgPos).removeAttr("title");
			kbd.find("div[alt=€]").css("background-position", "0px -77px");
			kbd.find("div[alt=¤]").css("background-position", "-11px -77px");
		} else {
			kbd.removeClass("jsKeyboardMaj").find("div").remove();
			createKeys(kbd, kbd.attr("lang"));
			if(isCap){
				kbd.removeClass("jsKeyboardCap");
				toggleShift(true);
			}
		}
	}

	function submitForm(input){
		var form = input[0].form;
		if(form) form.submit();
	}

	function focusNext(input){
		var found = false;
		var focused = false;
		var inputTab = $("input");
		inputTab.each(function(){
			if(found && !focused){
				hideKeyboard();
				this.focus();
				focused = true;
				return true;
			}
			if(this==input[0]) found = true;
		});
		if(!focused && inputTab[0]!=input[0]){
			hideKeyboard();
			inputTab[0].focus();
			focused = true;
		}
		return focused;
	}
})(jQuery);