/*
_________                __                 
\_   ___ \  ____   _____/  |______  ___  ___
/    \  \/ /  _ \ /    \   __\__  \ \  \/  /
\     \___(  <_> )   |  \  |  / __ \_>    < 
 \______  /\____/|___|  /__| (____  /__/\_ \
        \/            \/          \/      \/
		Contact Center - http://www.contax.com.br

  $Teclado virtual. CTXTVR.JS, 24/11/2006: 12:15:20
  Release por Rodrigo M. Roque (rmatuck@contax.com.br)
  
  Changelog:
  24/11/2006 - Adicionado compatibilidade para funcionar com o browser netscape, opera e firefox. (rmatuck)
*/

// macros das teclas - corresponde ao mepamento do windows
var VK_SPACE = 49, VK_BACK_SPACE = 50, VK_TAB = 52, VK_DELETE = 53, VK_ENTER = 55;
var VK_CAPS_LOCK = 54, VK_LEFT_SHIFT = 56, VK_RIGHT_SHIFT = 57, VK_ALTGR = 58;
var STATE_NORMAL = 0x0, STATE_SHIFT = 0x1, STATE_CAPS = 0x2, STATE_ALTGR = 0x4;

function Keyboard (listener, layout, noKbd)
{
	this.listener = listener;
	this.imgLayout = document.getElementById ("layout");

	/* no-layouts,  no-layouts
	 * key-layouts, key-layouts, sel-layouts 
	*/
 
	this.layoutsCtrl = new LayoutsCtrl (document.getElementById ("layouts"), this); /*sel-layouts*/
	this.elem = document.getElementById ("virk");
	this.elem.ref = this;
	this.elem.ondragstart = function (e) { return false; }
	
	if (!noKbd) {
		this.elem.onkeydown = function (e) { this.ref.onKeyDown (e); }
		this.elem.onkeyup   = function (e) { this.ref.onKeyUp (e); }
	}

	this.elem.onmousedown = function (e) { this.ref.onMouseDown (e); }
	this.elem.onmouseup   = function (e) { this.ref.onMouseUp (e); }
	var pos = getElemPos (this.elem);
	this.elem.clientX = pos.x; this.elem.clientY = pos.y;
   
   	// métodos
	this.createKbd = this.imgLayout ? createImgKbd : createHtmlKbd;
	this.init = kbdInit;
	this.setLayout = kbdSetLayout;
	this.getLayout = function () { return this.layoutsCtrl.getValue (); };
	this.updateState = kbdUpdateState;
	this.translateKey = kbdTranslateKey;
	this.adjustSize = kbdAdjustSize;
	this.nextLayout = kbdNextLayout;
   
 	// propriedades
	this.state = STATE_NORMAL;
	this.stateMask = 0;
	this.keys = "";
	this.deadChar = -1;
	this.layout = null;
	
	// eventos
	this.onKeyDown   = kbdOnKeyDown;
	this.onKeyUp     = kbdOnKeyUp;
	this.onMouseDown = kbdOnMouseDown;
	this.onMouseUp   = kbdOnMouseUp;
	this.onLayoutChanged = kbdOnLayoutChanged;

	// processos inicais do teclado, ajusta tamaho, instancia o objeto e define o layout   
	this.adjustSize ();
	this.createKbd ();
	this.init ();
	this.setLayout (layout);
}

// mapeamento de estado de teclas especiais, shit, capslock e etc. Macros que possuem um valor inteiro
var KEY_STATES = [
	{ key: VK_LEFT_SHIFT,  state: STATE_SHIFT, img: "imgLShift" },
	{ key: VK_RIGHT_SHIFT, state: STATE_SHIFT, img: "imgRShift" },
	{ key: VK_CAPS_LOCK,   state: STATE_CAPS,  img: "imgCaps"   },
	{ key: VK_ALTGR,       state: STATE_ALTGR, img: "imgAltGr"  }
];

// mapeamento de teclas especiais, enter, backspace, delete e espaço
var KEY_CHARS = [
	{ key: VK_SPACE,      ch: ' '     },
	{ key: VK_ENTER,      ch: '\n'    },
	{ key: VK_BACK_SPACE, ch: "<bs>"  },
	{ key: VK_DELETE,     ch: "<del>" }
];

// mapeamento para fixar os problemas de teclas no firefox
var KEY_SUB = 189, KEY_EQ = 187, KEY_SEM = 186;

if (navigator.gecko)
	KEY_SUB = 109, KEY_EQ = 61,  KEY_SEM = 59;

var KEY_CODES = [
   192, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, KEY_SUB, KEY_EQ,
   81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 219, 221,
   65, 83, 68, 70, 71, 72, 74, 75, 76, KEY_SEM, 222, 220,
   226, 90, 88, 67, 86, 66, 78, 77, 188, 190, 191, 193,
   32 /*space*/, 8 /*bs*/, 9 /*tab*/, 46 /*del*/, 13 /*enter*/,
   20 /*caps*/, 16 /*lshift*/, 16 /*rshift*/, 18 /*alt*/
];
   
var KEY_IDS = null;

// 0)tonos 1)/ 2).. 3)^ 4)\ 5)~ 6)dialytika_tonos 7)o 8)v 9)ogonek a). b)u c)// d), e)+
var DEAD_TRANS = [
   ":\u2500\u0384:\u03b1\u03ac:\u03b5\u03ad:\u03b9\u03af:\u03bf\u03cc:\u03b7\u03ae:\u03c5\u03cd:\u03c9\u03ce"
   + ":\u0391\u0386:\u0395\u0388:\u0399\u038a:\u039f\u038c:\u0397\u0389:\u03a5\u038e:\u03a9\u038f",
   ":\u2501\xb4:a\xe1:e\xe9:i\xed:o\xf3:u\xfa:y\xfd:A\xc1:E\xc9:I\xcd:O\xd3:U\xda:Y\xdd"
   + ":c\u0107:C\u0106:l\u013a:L\u0139:n\u0144:N\u0143:r\u0155:R\u0154:s\u015b:S\u015a:z\u017a:Z\u0179",
   ":\u2502\xa8:a\xe4:e\xeb:i\xef:o\xf6:u\xfc:y\xff:A\xc4:E\xcb:I\xcf:O\xd6:U\xdc"
   + ":\u03c5\u03cb:\u03b9\u03ca:\u03a5\u03ab:\u0399\u03aa",
   ":\u2503\x5e:a\xe2:e\xea:i\xee:o\xf4:u\xfb:A\xc2:E\xca:I\xce:O\xd4:U\xdb"
   + ":\u0131\xee:\u0130\xce:g\u011d:G\u011c",
   ":\u2504\x60:a\xe0:e\xe8:i\xec:o\xf2:u\xf9:A\xc0:E\xc8:I\xcc:O\xd2:U\xd9",
   ":\u2505\x7e:a\xe3:n\xf1:o\xf5:A\xc3:N\xd1:O\xd5",
   ":\u2506\u0385:\u03c5\u03b0:\u03b9\u0390",
   ":\u2507\xb0:a\xe5:A\xc5:u\u016f;U\u016e",
   ":\u2508\u02c7:c\u010d:C\u010c:d\u010f:D\u010e:e\u011b:E\u011a:l\u013e:L\u013d:n\u0148:N\u0147"
   + ":r\u0159:R\u0158:s\u0161:S\u0160:t\u0165:T\u0164;z\u017e:Z\u017d",
   ":\u2509\u02db:A\u0104:a\u0105:E\u0118:e\u0119:i\u012f:I\u012e:u\u0173:U\u0172",
   ":\u250a\u02d9:e\u0117:E\u0116:i\u0131:I\u0130:z\u017c:Z\u017b",
   ":\u250b\u02d8:A\u0102:a\u0103:G\u011f:g\u011e",
   ":\u250c\u02dd:O\u0150:o\u0151:U\u0170:u\u0171",
   ":\u250d\xb8:C\xc7:c\xe7:g\u0123:G\u0122:k\u0137:K\u0136:l\u013c:L\u013b"
   + ":n\u0146:N\u0145:r\u0157:R\u0156:S\u015e:s\u015f:T\u0162:t\u0163"
];

function getDeadKeyIndex (ch) 
{
	var index = ch.charCodeAt (0) - 0x2500;
	return (index >= 0 && index < DEAD_TRANS.length) ? index : -1;
}

var LIGATURES = [ // started from 0xe000
   "\u0638\u064b", "\u0644\u0627", "\u0644\u0625", 
   "\u0644\u0623", "\u0644\u0622", "\u0631\u064a\u0627\u0644",
   "\u094d\u0930", "\u0930\u094d", "\u091c\u094d\u091e", "\u0924\u094d\u0930",
   "\u0915\u094d\u0937", "\u0936\u094d\u0930", "\u0644\u0627\u064b",
];

// layouts do teclado
var LAYOUTS = [
   { id: "en", name: "US",
     normal: "`1234567890-=qwertyuiop´[asdfghjklç~]\\zxcvbnm,.;/",
     shift: "\"!@#$%¨&*()_+QWERTYUIOP`{ASDFGHJKLÇ^}|ZXCVBNM<>:?",
     caps: "`1234567890-=QWERTYUIOP´[ASDFGHJKLÇ~]\\ZXCVBNM,.;/" }
];

function kbdSetLayout (layout) 
{
	if (this.layout && this.layout.id == layout) // se não houve mudança, retorna
		return;
      
	for (var i = 0; i != LAYOUTS.length; ++i) 
	{
		if (LAYOUTS [i].id == layout) {
			this.layout = LAYOUTS [i];
			break;
		}
  	}
   	
	if (this.layout == null)
      	this.layout = LAYOUTS [0];
	if (this.layoutsCtrl.getValue () != this.layout.id)
      	this.layoutsCtrl.setValue (this.layout.id);
   
	this.deadChar = -1;
	this.updateState ();
}

function kbdOnLayoutChanged (layout)
{
	this.setLayout (layout);
	
	if (this.listener)
		this.listener.onLayoutChanged (layout);
}

function kbdUpdateState (key, value)
{
	for (var i = 0; i != KEY_STATES.length; ++i) 
	{
		if (key == KEY_STATES [i].key) {
			var state = KEY_STATES [i].state;
			if (value > 0)
				this.state |= state;
			else if (value == 0)
				this.state &= ~state;
			else
				this.state ^= state;
			break;
		}
	}

	
	this.showKey (this.imgKey, false);
	this.state &= this.stateMask;
	
	for (var i = 0; i != KEY_STATES.length; ++i) 
	{
		var state = KEY_STATES [i].state;
		var img = KEY_STATES [i].img;
		
		if (this [img])
			this.showKey (this [img], this.state & state);
	}

	var state = "normal";
	
	if (this.layout ["altgr"] && (this.state & STATE_ALTGR))
      		state = "altgr";
	else if ((this.state & STATE_SHIFT) && (this.state & STATE_CAPS))
		state = "normal";
	else if (this.state & STATE_CAPS)
		state = (this.layout ["caps"] ? "caps" : "shift");
	else if (this.state & STATE_SHIFT)
		state = "shift";
	if (!this.layout [state])
		state = "normal";
   
	this.keys = this.layout [state];
	this.loadLayout (this.layout, state);
}

// rotina OnKeyUp
function kbdOnKeyUp (e)
{
	if (!e) e = event;

	this.showKey (this.imgKey, false);
	
	if (e.type == "mouseup")
		return;
	
	var key = getKeyIDByCode (e.keyCode);

	if (key <= VK_CAPS_LOCK)
		return;
   
	// emula o pressionamento da tecla
	this.updateState (key, 0 /* desliga */);
}

// rotina OnKeyDown
function kbdOnKeyDown (e)
{
	if (!e)
		e = event;

	var key = -1, isMouse = false;
	
	if (e.type == "mousedown") {
		isMouse = true;
		if (e.button == 2) // botão direito do mouse, não faz nada
         		return;

		var pos = getEventPos (e, this.elem);
		key = getKeyID (pos.x, pos.y);
	}
	else {
		key = getKeyIDByCode (e.keyCode);
		if (e.keyCode == 16 && (e.altKey || e.ctrlKey)) {
			this.nextLayout ();
			return;
		}
	}
   
	var keyInfo = getKeyInfo (key);
	if (!keyInfo)
      		return;

	this.setKey  (this.imgKey, keyInfo);
	this.showKey (this.imgKey, true);

	if (key >= VK_CAPS_LOCK) {
		var value = -1; // -1 (mudando) 0 (desliga) e 1 (liga)

		if (key != VK_CAPS_LOCK && !isMouse)
			value = 1;
		this.updateState (key, value);
		this.isMouse = isMouse;
		return;
	}

	var charInfo = null;
	if (key < this.keys.length) {
		charInfo = this.translateKey (key);
	}
	else {
		charInfo = { ch: "", deadChar: -1 };
		for (var i = 0; i < KEY_CHARS.length; ++i) 
		{
			if (KEY_CHARS [i].key == key) {
				charInfo.ch = KEY_CHARS [i].ch;
				break;
			}
		}
	}

	if (charInfo.deadChar != this.deadChar) {
		this.deadChar = charInfo.deadChar;
		this.updateState ();
	}
	
	if (charInfo.ch && this.listener)
		this.listener.onKeyPress (charInfo.ch);
      
	if ((this.state & (STATE_SHIFT | STATE_ALTGR)) && this.isMouse) {
		this.state &= ~(STATE_SHIFT | STATE_ALTGR);
		this.updateState ();
	}
}

function getElemPos (elem) {
   var pos = { x: 0, y: 0 };
   for (var parent = elem; parent; parent = parent.offsetParent) {
      pos.x += parent.offsetLeft;
      pos.y += parent.offsetTop;
   }
   return pos;
}

function getEventPos (e, ctrl) {
   return { x: e.clientX - ctrl.clientX, y: e.clientY - ctrl.clientY };
}

function kbdOnMouseDown (e) {
   if (navigator.gecko)
      eventPreventDefault (e);
   var target = getEventTarget (e ? e : event);
   if (target && target != this.layoutsCtrl.ctrl
       && target.parentNode != this.layoutsCtrl.ctrl)
      this.onKeyDown (e);
}

function kbdOnMouseUp (e) {
   if (navigator.gecko)
      eventPreventDefault ();
   var target = getEventTarget (e ? e : event);
   if (target != this.layoutsCtrl.ctrl)
      this.onKeyUp (e);
}

// mapeamento da imagem do teclado x teclas
var KEYS = [
   { key: 0,				c: 13, src: "btn", x: 0, y: 1, width: 286, height: 15 },
   { key: VK_BACK_SPACE,	c: 1, src: "btn", x: 264, y: 20, width: 22, height: 15, text: "<-" },
   { key: 13,				c: 12, src: "btn", x: 0, y: 20, width: 263, height: 15 },
   { key: 25,				c: 12, src: "btn", x: 0, y: 39, width: 263, height: 15 },
   { key: VK_ENTER,			c: 1, src: "enter", x: 264, y: 39, width: 20, height: 34, text: "Enter" },
   { key: 37,				c: 12, src: "btn", x: 0, y: 58, width: 263, height: 15 },
   { key: VK_CAPS_LOCK,		c: 1, src: "caps_shift", x: 0, y: 77, width: 64, height: 15, text: "Caps" },
   { key: VK_LEFT_SHIFT,	c: 1, src: "caps_shift", x: 66, y: 77, width: 64, height: 15, text: "Shift" },
   { key: VK_SPACE,			c: 1, src: "space", x: 132, y: 77, width: 152, height: 15 }
];

// rotina para obter o ID da tecla
function getKeyID (x, y) 
{
	var keyID = -1;
	
	for (var i = 0; i < KEYS.length; ++i) 
	{
		var key = KEYS [i];
		if (x >= key.x && x < key.x + key.width &&
			y >= key.y && y < key.y + key.height) {
				keyID = key.key;
         			if (key.c > 1)
            				keyID += Math.floor ((x - key.x) * key.c / key.width);
				break;
		}
	}
	
	return keyID;
}

function getKeyInfo (keyID)
{
	if (keyID < 0 || keyID > VK_ALTGR)
		return null;
   
	for (var i = 0; i < KEYS.length; ++i) 
	{
		var key = KEYS [i];
		if (keyID >= key.key && keyID < key.key + key.c && key.src) {
			var keyInfo = { id: keyID, src: key.src, x: key.x, y: key.y,
                         width: key.width, height: key.height, text: key.text };
		
			keyInfo.width /= key.c;
			keyInfo.x += (keyID - key.key) * keyInfo.width;
			keyInfo.src =  keyInfo.src + ".gif";
			return keyInfo;
		}
	}
	
	return null;
}

function getKeyIDByCode (keyCode) 
{
	
	if (!KEY_IDS) {
		var keyIDs = new Array (250);
      		for (var i = 0; i < keyIDs.length; ++i)
         		keyIDs [i] = -1;
      		for (var keyID = 0; keyID < KEY_CODES.length; ++keyID)
         		keyIDs [KEY_CODES [keyID]] = keyID;
      		KEY_IDS = keyIDs;
	}

	if (keyCode < 0 || keyCode >= KEY_IDS.length)
		return -1;
	
	return KEY_IDS [keyCode];
}

function kbdInit ()
{
	this.imgKey = this.createKey (null);

	for (var i = 0; i != KEY_STATES.length; ++i) 
	{
		var keyInfo = getKeyInfo (KEY_STATES [i].key);
		
		if (!keyInfo)
			continue;
		
		this.stateMask |= KEY_STATES [i].state;
		this [KEY_STATES [i].img] = this.createKey (keyInfo);
	}
}

function kbdTranslateKey (key) 
{
   var charInfo = { ch : "", deadChar: -1 };
   var ch = this.keys.charAt (key);
   if (ch == ' ')
      return charInfo;

   var deadChar = getDeadKeyIndex (ch);
   if (this.deadChar >= 0) {
      if (deadChar >= 0)
         ch = DEAD_TRANS [deadChar].charAt (2);
      var i = DEAD_TRANS [this.deadChar].indexOf (":" + ch);
      if (i >= 0)
         ch = DEAD_TRANS [this.deadChar].charAt (i + 2);
      if (i <= 0)
         ch = DEAD_TRANS [this.deadChar].charAt (2) + ch;
   }
   else {
      var chCode = ch.charCodeAt (0);
      if (deadChar >= 0) {
         charInfo.deadChar = deadChar; ch = "";
      }
      if (chCode >= 0xe000 && chCode < 0xe000 + LIGATURES.length)
         ch = LIGATURES [chCode - 0xe000];
   }
   charInfo.ch = ch;
   return charInfo;
}

function kbdNextLayout () {
   var value = this.layoutsCtrl.getValue (), i = 0;
   for (i = 0; i < LAYOUTS.length; ++i) {
      if (LAYOUTS [i].id == value)
         break;
   }
   value = LAYOUTS [(i + 1) % LAYOUTS.length].id;
   this.layoutsCtrl.setValue (value);
   this.onLayoutChanged (value);
}

function kbdAdjustSize () {
   var cx = this.elem.offsetWidth;
   var cy = this.elem.offsetHeight;
   if (!cx || !cy)
      return;
   if (window.innerWidth) {
      cx -= window.innerWidth;
      cy -= window.innerHeight;
   }
   else if (document.body.clientWidth) {
      cx -= document.body.clientWidth;
      cy -= document.body.clientHeight;
   }
   else
      return;
   if (cx > 0 || cy > 0) {
      // alert ("cx = " + cx + "; cy = " + cy);
      cx = Math.max (cx, 0);
      cy = Math.max (cy, 0);
      if (window.dialogWidth) { // it's dialog?
         window.dialogWidth  = (parseInt (window.dialogWidth)  + cx) + "px";
         window.dialogHeight = (parseInt (window.dialogHeight) + cy) + "px";
      }
      else {
         window.resizeBy (cx, cy);
      }
   }
}

function LayoutsCtrl (ctrl, listener) 
{
	this.ctrl     = ctrl;
	this.listener = listener;
	this.getValue = function () { return this.ctrl.value; }
	this.setValue = function (value) { listSetValue (this.ctrl, value); }
	ctrl.listener = listener;
	
	ctrl.onchange = function () { 
		this.listener && this.listener.onLayoutChanged (this.value);
		this.parentNode.focus && this.parentNode.focus ();
	}
   
	var options = ctrl.options;
}

function listSetValue (ctrl, value) 
{
	// "ctrl.value = value" doesn't work in safari and ie.mac
	var options = ctrl.options;

	if (options != null) {
		for (var i = options.length - 1; i >= 0; --i) 
		{
			if (options [i].value == value)
				break;
		}
	}
	ctrl.selectedIndex = i;
}

function createImgKbd () 
{
	this.loadLayout = ImgKbd_loadLayout;
	this.createKey  = ImgKbd_createKey;
	this.setKey     = ImgKbd_setKey;
	this.showKey    = function (key, show) { key.style.display = show ? "block" : "none" }
}

function ImgKbd_loadLayout (layout, state) 
{
	var src = layout.id + "/" + state;
	
	if (this.deadChar >= 0)
      		src += "-" + this.deadChar;
	
	src += ".gif";
	
	if (this.imgLayout.src != src)
		this.imgLayout.src = src;
}

function ImgKbd_createKey (keyInfo) 
{
	var key = document.createElement ("IMG");
	key.style.position = "absolute";
	key.style.display = "none";
	keyInfo && this.setKey (key, keyInfo);
	this.elem.appendChild (key);
	return key;
}

function ImgKbd_setKey (key, keyInfo) 
{
	key.src        = keyInfo.src;
	key.style.left = keyInfo.x;
	key.style.top  = keyInfo.y;
	key.width      = keyInfo.width;
	key.height     = keyInfo.height;
}

