/*
_________                __                 
\_   ___ \  ____   _____/  |______  ___  ___
/    \  \/ /  _ \ /    \   __\__  \ \  \/  /
\     \___(  <_> )   |  \  |  / __ \_>    < 
 \______  /\____/|___|  /__| (____  /__/\_ \
        \/            \/          \/      \/
		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:
  27/11/2006 - Adicionado novo layout (br-abnt 2)
  24/11/2006 - Adicionado compatibilidade para funcionar com o browser netscape, opera e firefox. (rmatuck)
*/

var VIRK_LAYOUT = 'en'; // layout padrão do teclado a ser utilizado
var VIRK_NOKBD = false;

navigator.safari = navigator.userAgent.indexOf ("Safari") >= 0;
navigator.mozilla = navigator.userAgent.indexOf ("Mozilla/5.0") > 0;
navigator.ns70 = navigator.userAgent.indexOf ("Netscape6") > 0|| navigator.userAgent.indexOf ("Netscape/7.0") > 0;
navigator.safari10 = navigator.userAgent.indexOf ("Safari/85") >= 0;
navigator.gecko  = navigator.userAgent.indexOf ("Gecko") >= 0 && !navigator.safari;
navigator.ie = navigator.appName.indexOf ("Internet Explorer") >= 0;

var noKbd = VIRK_NOKBD || navigator.ns70;
var virkCtrl = null;

ctrlAttachEvent (window, "load", createVirkCtrl);

// função construtora do teclado virtual
function createVirkCtrl () 
{
	virkCtrl = new VirkCtrl ();
	
	if (window.opener || window.dialogArguments)
		virkCtrl.attach ();
	
	ctrlAttachEvent (virkCtrl.mainWnd, "unload", destroyVirkCtrl);
}

// detroi e limpa o objeto (teclado)
function destroyVirkCtrl ()
{
	if (!virkCtrl)
		return;
	
	virkCtrl.detach ();
	window.dialogArguments && window.close();
}

// rotina para definir as propriedades padrão do teclado, incluindo a macro
// do layout do teclado 'VIRK_LAYOUT'
function VirkCtrl ()
{
	this.mainWnd    = getOpener ();
	this.onKeyPress = virkOnKeyPress;
	this.onLayoutChanged = function (layout) { this.savedLayout = layout; }
	this.attach     = virkAttach;
	this.detach     = virkDetach;
	this.setLayout  = function (layout) { this.kbd.setLayout (layout) };
	this.getLayout  = function () { return this.kbd.getLayout (); };
   
	this.hookCtrl   = virkHookCtrl;
	this.unhookCtrl = virkUnhookCtrl;
	this.updateSel  = virkUpdateSel;
	this.setActiveCtrl = virkSetActiveCtrl;
   
	var layout = VIRK_LAYOUT;
	
	if (!layout)
		layout = getQueryParam (document.location.href, "layout");
	
	this.kbd = new Keyboard (this, layout, VIRK_NOKBD);

	this.textRange  = null;
	this.activeCtrl = null;
	
	if (this.mainWnd.theVirk && this.mainWnd.theVirk.activeCtrl)
		this.setActiveCtrl (this.mainWnd.theVirk.activeCtrl);
}

function getOpener ()
{
	if (window.external && window.external.dialogArguments)
		return window.external.dialogArguments;
	if (window.dialogArguments)
		return window.dialogArguments;
	if (window.opener)
		return window.opener;
	if (window.parent != window.self)
		return window.parent;
	if (window.frameElement)
		return window.frameElement.parentElement.document.parentWindow;

	return window;
}

/* função de enumeração de objetos. identificar os elementos do formulário
* com objetivo de identificar quais elementos são editavei, ou seja, quais 
* campos permitem ser digitados o dado. A função 'isEditable' realiza essa 
* verificação. */
function virkEnumObjects (wnd, listener) 
{
	try {
      var frames = wnd.document.getElementsByTagName ("IFRAME");
      for (var i = 0; i < frames.length; ++i)
         listener.onFrame (frames [i]);

      var doc = wnd.document, forms = doc.forms;
      for (var i = 0; i != forms.length; ++i) {
         var form = forms [i];
         for (var j = 0; j != form.elements.length; ++j) {
            var ctrl = form.elements [j];
            isEditable (ctrl) && listener.onCtrl (ctrl, doc);
         }
      }
      
      if (window.showModalDialog) { // IE?
         var ctrls = wnd.document.getElementsByTagName ("DIV");
         for (var j = 0; j != ctrls.length; ++j) {
            var ctrl = ctrls [j];
            isEditable (ctrl) && listener.onCtrl (ctrl, doc);
         }
      }
      
      for (var i = 0; i != wnd.frames.length; ++i)
         virkEnumObjects (wnd.frames [i], listener);
   }
   catch (e) {
      // exception if wnd is from another domain
   }
}

//
function VirkAttacher () {}
function VirkDetacher () {}
//

VirkAttacher.prototype.onCtrl = function (ctrl, doc) 
{
	virkCtrl.hookCtrl (ctrl, doc);
	if (virkCtrl.activeCtrl == null)
		virkCtrl.setActiveCtrl (ctrl);
}

VirkAttacher.prototype.onFrame = function (frame) 
{
	if (frame.style.visibility == "hidden" || frame.style.display == "none")
		return;
	
	ctrlAttachEvent (frame, "load", virkReAttach);
}

VirkDetacher.prototype.onCtrl = function (ctrl, doc) 
{
	virkCtrl.unhookCtrl (ctrl);
}

VirkDetacher.prototype.onFrame = function (frame) 
{
	ctrlDetachEvent (frame, "load", virkReAttach);
}

function virkAttach () 
{
	virkEnumObjects (this.mainWnd, new VirkAttacher ());
}

function virkDetach () 
{
	if (!this.mainWnd || this.mainWnd.closed)
		return;

	virkEnumObjects (this.mainWnd, new VirkDetacher ());
}

function virkReAttach (e) 
{
	virkEnumObjects (virkCtrl.mainWnd, new VirkAttacher ());
}

// eventos de campo.
var HOOK_EVENTS = [
   { type: "focus",    listener: vkOnFocus     },
   { type: "click",    listener: vkOnFocus     },
   { type: "mouseup",  listener: vkOnFocus     },
   { type: "keydown",  listener: vkOnKeyDown,  skip: noKbd },
   { type: "keyup",    listener: vkOnKeyUp,    skip: noKbd },
   { type: "keypress", listener: vkOnKeyPress, skip: noKbd, handler: navigator.safari }
];
  
function virkHookCtrl (ctrl, doc) 
{
	if (!ctrl.ownerDocument)
		ctrl.ownerDocument = doc;
	if (navigator.ie && !ctrl.attachEvent) {
		ctrl.attachEvent = ctrlAttachEventIE;
		ctrl.detachEvent = ctrlDetachEventIE;
	}
	
	for (var i = 0; i < HOOK_EVENTS.length; ++i) {
		var e = HOOK_EVENTS [i];
		
		if (!e.skip)
			ctrlAttachEvent (ctrl, e.type, e.listener, e.handler);
	}
   return ctrl;
}

function virkUnhookCtrl (ctrl) 
{
	for (var i = 0; i < HOOK_EVENTS.length; ++i) 
	{
		var e = HOOK_EVENTS [i];
		if (!e.skip)
			ctrlDetachEvent (ctrl, e.type, e.listener, e.handler);
	}
}

function getEventTarget (e) 
{
	return e.srcElement || e.target;
}

// Mapeamento do evento de pressionamento de tecla no teclado virtual
function virkOnKeyPress (str) 
{
	this.updateSel (this.activeCtrl);
	if (!this.textRange) // null se o activeCtrl estive desabilitado
		return;
	if (str == "<enter>")
		str = "\n";

	switch (str) {
		case "<bs>":  this.textRange.deleteText (-1); break;
		case "<del>": this.textRange.deleteText (0); break;
		default:      this.textRange.insertText (str); break;
	}
	
	if (str.charCodeAt (0) > 0x5b0 && str.charCodeAt (0) < 0x6ff)
		this.textRange.ctrl.dir = "rtl";
}

function virkSetActiveCtrl (ctrl) 
{
	var prevCtrl = this.activeCtrl;
	this.updateSel (ctrl);
	
	if (!this.activeCtrl || this.activeCtrl == prevCtrl)
		return;
   
	if (this.activeCtrl.lang) {
		if (!this.savedLayout)
			this.savedLayout = this.getLayout ();
		
		this.setLayout (this.activeCtrl.lang);
	}
	else if (this.savedLayout) {
		this.setLayout (this.savedLayout);
		this.savedLayout = "";
	}
}

function virkUpdateSel (ctrl)
{
	try {
      var prevRange = this.textRange;
      this.activeCtrl = this.textRange = null;
      if (!ctrl)
         return;
      var range = new TextRange (ctrl);
      if (!range.ctrl)
         return;
      ctrl.focus ();
      this.activeCtrl = ctrl;
      this.textRange  = (range.ctrl == ctrl ? range : prevRange);
   }
   catch (e) {
      // falha, casa o controle esteja desabilitado ou escondindo
      // falha se a seleção pertença a outro DC (DataContext) que no nosso caso o browser
   }
}

// define a ativação do controle no campo em uso
function vkOnFocus (e) 
{
	virkCtrl.setActiveCtrl ( getEventTarget (e) );
}

// mepamento do evento onkeydown (pressionamento de tecla)
function vkOnKeyDown (e) 
{
	var ctrl = getEventTarget (e);
	
	// Código 0x8 = DEL e 0x2E = Backspace
	if (e.keyCode == 8 || e.keyCode == 46 )
		eventPreventDefault (e);
	
	virkCtrl.updateSel (ctrl);
	virkCtrl.kbd.onKeyDown (e);
}

// mapemaneot do evento onkeyup (após pressionar a tecla)
function vkOnKeyUp (e) 
{
	virkCtrl.kbd.onKeyUp (e);
}

// mapeamento do evento onkeypress (todas as teclas, incluindo setas e etc)
function vkOnKeyPress (e)
{
	// não permite o uso de setas (cima, baixo, direta e esquerda)
	if ( !navigator.ie && e.keyCode >= 33 && e.keyCode <= 40 ) 
		return;
	
	eventPreventDefault (e);
}

function TextRange (ctrl) 
{
	if ( !ctrl.ownerDocument )
		return;
	
	this.ctrl = ctrl;
	
	if ( document.selection ) 
	{
		this.range = ctrl.ownerDocument.selection.createRange ();
		this.ctrl = this.range.parentElement ();
		this.insertText = rangeInsertText;
		this.deleteText = rangeDeleteText;
	}
	else {
		this.insertText = plainInsertText;
		this.deleteText = plainDeleteText;
	}
}

function rangeInsertText (text) {
   this.range.text = text;
   this.range.collapse (false);
   this.range.select ();
   /*#if FARSI_YEH*/this.correctText ();/*#endif*/
}

function rangeDeleteText (dir) {
   if (!this.range.text) {
      var savedRange = this.range.duplicate ();
      if (dir < 0)
         this.range.moveStart ("character", -1);
      else
         this.range.moveEnd ("character", 1);
      if (/<.*>/.test (this.range.htmlText)) {
         this.range = savedRange;
         return;
      }
   }
   this.range.text = "";
   this.range.collapse (false);
   this.range.select ();
   /*#if FARSI_YEH*/this.correctText ();/*#endif*/
}

function plainInsertText (text) 
{
	this.ctrl.value = this.ctrl.value + text;
   /*#if FARSI_YEH*/this.correctText ();/*#endif*/
}

function plainDeleteText (dir) {
   if (dir < 0 && this.ctrl.value.length)
      this.ctrl.value = this.ctrl.value.substr (0, this.ctrl.value.length - 1);
   /*#if FARSI_YEH*/this.correctText ();/*#endif*/
}

TextRange.prototype.correctText = function () 
{
	if (navigator.userAgent.indexOf ("Windows 98") < 0)
		return;

	var text = "", range = null, cchRight = 0, isDirty = false;
	if (this.range) 
	{
		range = this.range.duplicate ();
		cchRight = range.moveEnd ("character", 1);
		if (/<.*>/.test (range.htmlText)) {
			range = this.range.duplicate ();
			cchRight = 0;
		}
			
		range.moveStart ("character", -2);
		if (/<.*>/.test (range.htmlText))
			return;
			
		text = range.text;
	}
	else {
		text = this.ctrl.value;
	}
   
	for ( var pos = 0; ( pos = text.indexOf ('\u06cc', pos) ) >= 0; ++pos ) 
	{
		var nextChar = text.charAt (pos + 1);
		if (nextChar && !nextChar.match (/\s/)) {
			text = text.substr (0, pos) + '\u064a' + text.substr (pos + 1);
			isDirty = true;
		}
	}
	
	if (!isDirty)
		return;
   
	if (range) {
		range.text = text; 
		range.moveEnd ("character", -cchRight);
		range.collapse (false);
		range.select ();
		this.range = range;
	}
	else {
		this.ctrl.value = text;
	}
}

function ctrlAttachEvent (ctrl, type, listener, handler) 
{
	if (ctrl.addEventListener && !handler) {
		ctrl.addEventListener (type, listener, false);
	}
	else if (ctrl.attachEvent) {
		ctrl.detachEvent ("on" + type, listener);
		ctrl.attachEvent ("on" + type, listener);
	}
	else {
		ctrl ["on" + type] = listener;
	}
}

function ctrlDetachEvent (ctrl, type, listener, handler) {
   if (handler)
      ctrl ["on" + type] = null;
   if (ctrl.removeEventListener)
      ctrl.removeEventListener (type, listener, false);
   else if (ctrl.detachEvent)
      ctrl.detachEvent ("on" + type, listener);
}

// verifica se o campo é do tipo INPUT ou TEXTAREA
function isEditable (ctrl) {
   return !ctrl.disabled &&
          (ctrl.isContentEditable ||
           ctrl.tagName == "INPUT" && ctrl.type == "text" ||
           ctrl.tagName == "TEXTAREA");
}

function eventPreventDefault (e) {
   if (e) {
      e.returnValue = false;
      if (e.preventDefault)
         e.preventDefault ();
   }
}

function getQueryParam (query, param) {
   var value = new RegExp (param + "=([^&]*)").exec (query);
   if (!value || value.length < 2)
      return "";
   return value [1];
}

function ctrlAttachEventIE (type, listener) {
   this.parentWin = this.ownerDocument.parentWindow;
   this [type + "Listener"] = listener;
   this [type] = onEventIE;
}

function ctrlDetachEventIE (type) {
   this [type] = null;
}

function cloneObject (obj) {
   var clone = new Object, prop;
   for (prop in obj)
      clone [prop] = obj [prop];
   return clone;
}

function onEventIE () {
   var e = this.parentWin.event, listener;
   if (e && (listener = this ["on" + e.type + "Listener"]))
      return listener (cloneObject (e));
}

