//*************************************************************************
// CLASE PARA EL INPUT CON SUGGEST (lista de valores posibles)
//
//*************************************************************************

//-------------------------------------------------------------------------
// Clase ssiPopupSuggestRemoteProvider, una clase que maneja las sujerencias
//-------------------------------------------------------------------------
function ssiPopupSuggestRemoteProvider(sAddress, sTable, sField, iBegin, iLimit) {
	this.http = zXmlHttp.createRequest();
	this.address = sAddress;
	this.table = sTable;
	this.field = sField;
	this.limit = iLimit;
	this.begin = iBegin;
	
	this.items = null
	this.numItems = 0;
	
	this.timeoutId = null;
}

ssiPopupSuggestRemoteProvider.prototype.setControl = function (oControl) {
	this.control = oControl;
}

ssiPopupSuggestRemoteProvider.prototype.hasLocalData= function (oControl) {
	return false;
}

ssiPopupSuggestRemoteProvider.prototype.getRowContent = function(rowNum) {
	return this.items[rowNum].content;
};

ssiPopupSuggestRemoteProvider.prototype.getRowContent = function(rowNum) {
	return this.items[rowNum].content;
};

ssiPopupSuggestRemoteProvider.prototype.getRowText = function(rowNum) {
	return this.items[rowNum].text;
}

ssiPopupSuggestRemoteProvider.prototype.getRowValue = function(rowNum) {
	return  this.items[rowNum].value;
}

ssiPopupSuggestRemoteProvider.prototype.getValueRow = function(value) {
	for (var i=0;i<this.numItems;i++)
		{
			if (this.items[i].value == value)
					return i;
		}
	return -1;
};

ssiPopupSuggestRemoteProvider.prototype.getFiltered = function(partial) {
	return partial;
}

ssiPopupSuggestRemoteProvider.prototype.requestSuggestions = function (oControl, bTypeAhead) {

	var oThis = this;
	var control = this.control;
	var newText = this.control.activeText(); //control.textbox.value;
	
	if (newText == null || newText == undefined || newText == "")
		{
			control.textbox.focus();
			control.postHideSuggest();
			return;
		}
	
	// Cancelamos la consulta anterior, si había alguna
	if (this.http.readyState != 0)
			this.http.abort();

	// Definimos los datos a pasar al Source
	var aData = { 
		table: this.table, 
		field: this.field,
		begin: this.begin,
		text: newText,
		limit: this.limit 
		
	};
	
	// Abrimos conexión al servidor
	this.http.open("post", this.address, true);
	this.http.onreadystatechange = function () {
		if (oThis.http.readyState == 4)
			{
				//alert('oThis.http.responseText = "' + oThis.http.responseText + '"');
				// Obtenemos los valores del churro de retorno
				oThis.items = JSON.parse(oThis.http.responseText);
				oThis.numItems = oThis.items.length;
				if (oThis.numItems > 0)
					{
						// Ponemos los nuevos valores en el control
						control.fillinSuggest();
						// Mostramos el Suggest
						control.showSuggest();
						//@@@
						//control.updateSelected(0,true,true);
					}
				else
					{
						// Si queremos mostrar el Suggest con "No hay coincidencias..."
						//control.fillinSuggestNoResults();
						//control.showSuggest();
						// Si queremos ocultar sin más el Suggest...
						control.textbox.focus(); 
						control.postHideSuggest(); 

					}
			}    
	};

	// Enviamos el comando
	this.http.send(JSON.stringify(aData));
};

ssiPopupSuggestRemoteProvider.prototype.lookUp = function (iKeyCode) {

	// Obtenemos el input y el this
	var textbox = this.control.textbox;
	var oThis = this;
	
	// ACENTOS (debemos dejarlos pasar para que pueda ir detrás la letra)
	if ((iKeyCode == 222 /*´*/) || (iKeyCode == 186 /*`*/))
			return true;
	
	// Quitamos el timer si ya estaba
	clearTimeout(this.timeoutId);
	
	// Si no hay texto a buscar, pues nada
	if (textbox.value == "")
		{
			this.control.hideSuggest();
			return;
		}

	// Ponemos un tiempo de espera y lanzamos el requestSuggestions
	this.timeoutId = setTimeout( function () {
				if (textbox.value != "")
					{
						oThis.requestSuggestions(oThis, false);
					}
			}, 500);
}


//-------------------------------------------------------------------------
// Clase ssiPopupSuggest, un control de texto con valores incorporado
//
// Clase principal que implementa el SuggestBox
//-------------------------------------------------------------------------
function ssiPopupSuggest(oTextbox, oProvider, oOnEnter) {

	// El TextBox
	this.textbox /*:HTMLInputElement*/ = oTextbox;

	// Nombre de la capa
	this.layerName = this.textbox.id + "SuggestContainer";
	
	// Nombre (plantilla) de los items (van seguidos del índice del item)
	this.itemTemplateName = this.textbox.id + "_RowDiv";

	// La capa con el Suggest
	this.layer = null;

	// La máscara para capturar clics de ratón debajo del Suggest
	this.mask = null;
	
	// Funcion de Enter
	this.onEnter = oOnEnter;
	
	// El contenido proviene de un proveedor
	this.provider = oProvider;
	this.provider.setControl(this);

	// Índice del item seleccionado
	this.selIndex = -1;
	
	// Índice del item highlighted
	this.highIndex = -1;
	
	// Inicializamos el control
	this.init();
}

// Inicialización del control
ssiPopupSuggest.prototype.init = function () {

  //save a reference to this object
  var oThis = this;
  
  // Asignamos los eventos
  this.textbox.onkeydown = function (oEvent) { return oThis.onInputKeyDown(oEvent); };
  this.textbox.onkeyup = function (oEvent) { return oThis.onInputKeyUp(oEvent); };
  this.textbox.onkeypress = function (oEvent) { return oThis.onInputKeyPress(oEvent); };
  this.textbox.onclick = function () { oThis.onInputClick(); };
  this.textbox.onblur = function () { oThis.onInputBlur(); };
  
  //create the suggestions dropdown
  this.createSuggest();
};

ssiPopupSuggest.prototype.reset = function () {

	// Quito los index
	this.updateHighlighted(-1);
	this.updateSelected(-1);
	// Pongo el scroll del layer a 0
	this.layer.scrollTop = 0;
};

ssiPopupSuggest.prototype.getFiltered = function(partial) {
	// Primero lo paso a mayúsculas.
	var res = partial.toUpperCase();
	// Ahora cambio los caracteres
	res = res.replace("Á","A");
	res = res.replace("É","E");
	res = res.replace("Í","I");
	res = res.replace("Ó","O");
	res = res.replace("Ú","U");
	res = res.replace("À","A");
	res = res.replace("È","E");
	res = res.replace("Ì","I");
	res = res.replace("Ò","O");
	res = res.replace("Ù","U");
	return res;
	
};

ssiPopupSuggest.prototype.highLightItem = function (text, highText) {
	text = this.getFiltered(text);
	highText = this.getFiltered(highText);
	
	return text.replace(highText,"<span class='highlight'>" + highText + "</span>");
};

ssiPopupSuggest.prototype.getCursorPos = function() {
	var el = this.textbox;
	
  if (el.selectionStart) { 
    return el.selectionStart; 
  } else if (document.selection) { 
    el.focus(); 

    var r = document.selection.createRange(); 
    if (r == null) { 
      return 0; 
    } 

    var re = el.createTextRange(), 
        rc = re.duplicate(); 
    re.moveToBookmark(r.getBookmark()); 
    rc.setEndPoint('EndToStart', re); 

    return rc.text.length; 
  }  
  return 0; 
}

ssiPopupSuggest.prototype.setCursorPos = function(newPos) {
	var el = this.textbox;
	if (el.selectionStart) {
		el.focus();
		el.setSelectionRange(newPos,newPos);
	} else if (document.selection) {
		var range = el.createTextRange(); 
		range.move("character", newPos); 
		range.select(); 
	}
}

ssiPopupSuggest.prototype.activeText = function () {
	var textbox = this.textbox;
	var position = this.getCursorPos();
	// Buscamos ';' antes o después...
	var totalText = textbox.value;
	var parts = totalText.split(';');
	var partIndex = 0;
	for (;partIndex<parts.length;partIndex++) {
		if (parts[partIndex].charAt(0) == '+' || parts[partIndex].charAt(0) == '-')
				position--;
		if (position <= parts[partIndex].length)
			break;
		position -= parts[partIndex].length+1; // añadimos el ; de la parte 
	}
	if (partIndex > parts.length) { // Si me he pasado...
		return "";
	} else {
		if (parts[partIndex].charAt(0) == '+' || parts[partIndex].charAt(0) == '-')
			return parts[partIndex].substr(1);
		else
			return parts[partIndex];
	}
}



// Crea la división para el Suggest y la máscara de popup
ssiPopupSuggest.prototype.createSuggest = function () {

	var oThis = this;
	
	// Creamos la máscara para cuando me pican fuera
	var maskId = this.textbox.id + "SuggestMask";
	this.mask = new ssiPopupMask(maskId, function() { oThis.onMaskClick(); } );
	// Cuando el mouse pasa por la máscara, desactivamos el elemento highlighted del Suggest,
	// pues estamos fuera de éste.
	this.mask.mask.onmouseover = function(oEvent) { oThis.updateHighlighted(-1); };
 	
	// Creamos la capa para el Suggest (el estilo lo mantiene oculto: display:none;)
	this.layer = document.createElement("div");
	this.layer.className = "ssiSuggestcontainer";
	this.layer.id = this.layerName;
	
	// Asignamos los eventos
	this.layer.onmousedown = function (oEvent) { oThis.onDivMouseDown(oEvent); };
	this.layer.onmouseup = function (oEvent) { oThis.onDivMouseUp(oEvent); };
	this.layer.onmouseover = function (oEvent) { oThis.onDivMouseOver(oEvent); };
	
	// Añado la capa al documento
	document.body.appendChild(this.layer);
	
	// Ahora relleno el Suggest con los valores, etc...
	this.fillinSuggest();
};

// Pone el contenido del Suggest en función del estado
ssiPopupSuggest.prototype.fillinSuggest = function () {

	// Si hay más de 20 items, ponemos un límite a la altura (200px)
	if (this.provider.numItems > 12)
			this.layer.style.height = "200px";
	else
			this.layer.style.height = "";

	// Limpiamos la capa
	this.layer.innerHTML = "";

	// Hago un reset, puesto que si lo relleno, no hay nada seleccionado
	this.reset();
	
	var activeText = this.activeText();

	// La rellenamos con los items que toque
	for (var i=0; i < this.provider.numItems; i++)
		{
			oDiv = document.createElement("div");
			oDiv.id = this.itemTemplateName + i;
			oDiv.innerHTML = this.highLightItem(this.provider.getRowContent(i),activeText/*this.textbox.value*/);
			//oDiv.innerHTML = this.provider.getRowContent(i);
			this.layer.appendChild(oDiv);
		}
	
};

// Pone el contenido del Suggest en función del estado
ssiPopupSuggest.prototype.fillinSuggestNoResults = function () {

	this.layer.style.height = "";

	// Limpiamos la capa
	this.layer.innerHTML = "<span class=\"noresults\">No hay coincidencias...</span>";

	// Hago un reset, puesto que si lo relleno, no hay nada seleccionado
	this.reset();

};

// Evento de pulsado de tecla
ssiPopupSuggest.prototype.onInputKeyDown = function (oEvent) {

	// Obtengo el evento
	oEvent = oEvent || window.event;
	
  // TAB (o shift-TAB): acepto el valor y cierro el Suggest
	if (oEvent.keyCode == 9) // Tab
		{
			// Primero miramos si está vacío
			if (this.textbox.value == "")
					this.selIndex = -1;
			// Damos por buena la selección - NO. EL TEXTO PUEDE SER LIBRE
			// this.setValue(this.selIndex);
			// Ocultamos el Suggest
			this.hideSuggest();
		}
	// ESC: rechazo el valor y cierro el Suggest
	else if (oEvent.keyCode == 27) // Esc
		{
			// Restauro lo que había antes de cambiar el valor
			// Pero sólo si los datos son locales, puesto que es la única forma
			// de "restaurar" los valores.
			this.textbox.focus(); 
			this.postHideSuggest(); 
		}
	// DEL o SUPR: dejo que pasen para que borre la letra
	else if (oEvent.keyCode == 8 || oEvent.keyCode == 46) // Borrar o Supr
		{
			return true;
		}
	// LEFT, RIGHT, HOME, END: oculto el suggest, pues está editando el contenido textual presente
	else if (oEvent.keyCode == 37 || oEvent.keyCode == 39 || oEvent.keyCode == 35 || oEvent.keyCode == 36)
		{
			// Primero miramos si está vacío
			this.selIndex = -1;
			// Ocultamos el Suggest
			this.hideSuggest();
		}
	// UP: subo un item en la selección, si el Suggest es visible
	else if (oEvent.keyCode == 38) // arrow up
		{
			// Si es visible y hay items
			if (this.isSuggestVisible() && this.provider.numItems > 0)
				{
					if (this.selIndex == -1) // Si no se ha seleccionado ningún item
							return;
					if (this.selIndex > 0)
							this.updateSelected(this.selIndex-1,true);
					return false;
				}
		}
	// DOWN: bajo un item en la selección, si el Suggest es visible.
	// Si no es visible y los datos son locales, muestro el Suggest con el item seleccionado
	else if (oEvent.keyCode == 40)
		{
			// Si es visible, bajo un item
			if (this.isSuggestVisible())
				{
					if (this.provider.numItems > 0) // Si hay items...
						{
							if (this.selIndex == -1) // Si no se ha seleccionado ningún item
									this.updateSelected(0,true);
							else if (this.selIndex < (this.provider.numItems-1))
									this.updateSelected(this.selIndex+1,true);
							return false;
						}
				}
			// Si no es visible, es que tenemos que mostrar el Suggest con el item seleccionado visible,
			// pero sólo si el provider tiene los datos locales
			else if (this.provider.hasLocalData())
				{
					this.showSuggest();
					// Si tenemos algún item seleccionado, lo ilumino
					if (this.selIndex != -1)
							this.updateSelected(this.selIndex,true);
					return false;
				}
		}
	// Página arriba
	else if (oEvent.keyCode == 33)
		{
			if (this.isSuggestVisible() && this.provider.numItems > 0)
				{
					var selIndex = this.selIndex - 12;
					if (selIndex < 0)
							selIndex = 0;
					if (selIndex != this.selIndex)
							this.updateSelected(selIndex,true);
				}
		}
	// Página abajo
	else if (oEvent.keyCode == 34)
		{
			if (this.isSuggestVisible() && this.provider.numItems > 0)
				{
					var selIndex = this.selIndex + 13;
					if (selIndex < this.provider.numItems)
						{
							this.updateSelected(selIndex,true);
						}
				}
		}
	// ENTER: Aceptación de valor y cerramos el Suggest
	else if (oEvent.keyCode == 13)
		{
			/*
			var sIndex = this.selIndex;
			if (sIndex != -1)
					this.setValue(sIndex);
			*/
			var throwOnEnter = (this.selIndex == -1);
			// Ocultamos el suggest
			this.hideSuggest();
			this.postFocus();
			// Si hay callback para Enter
			if (throwOnEnter == true && this.onEnter != null)
					this.onEnter();
			return false;
		}
	// Si no es ninguno de estos casos
	else
		{
			// Ahora miro la letra para hacer el lookup (1º miro las que no y me las como,
			// luego muestro el Suggest y paso el testigo al keyup).
			// El caso 0 es especial. Algunos caracteres especiales (la Ñ/ñ en Mac) primero mandan
			// un 0 y luego mandan el código correcto (en el keypress)
			if ((oEvent.keyCode > 0 && oEvent.keyCode < 32) /*|| (oEvent.keyCode >= 33 && oEvent.keyCode < 46) || (oEvent.keyCode >= 112 && oEvent.keyCode <= 123)*/)
				{
					return false;
				}
			else // letra corriente
				{
					if (this.provider.hasLocalData())
							this.showSuggest();
					return true; // para que la procese en el keyup
				}
		}
};

// Evento de liberado de tecla
ssiPopupSuggest.prototype.onInputKeyUp = function (oEvent) {

	// Obtengo el evento
	oEvent = oEvent || window.event;
	
	// Me como los caracteres no válidos:
	// <32 menos el 8 (DEL)
	// >=33 y < 46
	// >= 112 y <= 123
	if ((oEvent.keyCode < 32 && oEvent.keyCode != 8) || (oEvent.keyCode >= 33 && oEvent.keyCode < 46) || (oEvent.keyCode >= 112 && oEvent.keyCode <= 123))
		{
			; // NO HACEMOS NADA
		}
	else
		{
			// miro y hago typeahead
			this.provider.lookUp(oEvent.keyCode);
		}
}

// Evento de liberado de tecla
ssiPopupSuggest.prototype.onInputKeyPress = function (oEvent) {

	// Obtengo el evento
	oEvent = oEvent || window.event;
	
	// Miro el caso especial de la 'ñ' para Mac
	var keyCode = oEvent.keyCode;
	if (keyCode == 0)
			keyCode = oEvent.which;
			
	if (keyCode == 209 ||keyCode == 241) // Ñ ó ñ
			this.provider.lookup(keyCode);
	
}


// Evento de click del Input
ssiPopupSuggest.prototype.onInputClick = function () {

	// Si el provider tiene los datos locales, mostramos el Suggest
	if (this.provider.hasLocalData())
		{
  		// Intento mostrar el Suggest
  			this.showSuggest();
			// Pongo la selección que toque
			this.updateSelected(this.selIndex,true);
		}
};

// Evento Blur (pérdida de foco) del Input
ssiPopupSuggest.prototype.onInputBlur = function () {

	// Si el layer no es visible y no hay valor vaciamos el input
	/*
	if (!this.isSuggestVisible())
			this.textbox.value = "";
	*/
}

// Evento Click de la máscara
ssiPopupSuggest.prototype.onMaskClick = function () {
	/*
	if (this.selIndex != -1)
		{
			// Primero miramos si está vacío
			if (this.textbox.value == "")
					this.selIndex = -1;
			// Damos por buena la selección
			this.setValue(this.selIndex);
		}
	*/
	this.textbox.focus(); 
	this.postHideSuggest(); 
}

// Evento de mousedown de la capa
ssiPopupSuggest.prototype.onDivMouseDown = function (oEvent) {

	// Obtengo el evento y el destino
	oEvent = oEvent || window.event;
	oTarget = oEvent.target || oEvent.srcElement;

	// Si no hay resultados no hacemos nada
	if (this.provider.numItems <= 0)
			return;

	// TEMA SCROLL. En cada navegador retorna un item diferente si se pica en el scroll del popup.
	// La forma de mirarlo es que el Parent del elemento sea de primer nivel (BODY o HTML), que seguro
	// hace referencia al layer principal, y no debemos procesar como un click en un item.
	if (oTarget == null || oTarget.offsetParent.tagName == "BODY" || oTarget.offsetParent.tagName == "HTML")
		{
			this.postFocus();
			return;
		}
		
	// Obtenemos la fila, la seleccionamos y ponemos el valor
	var rowid = this.getRowIndex(oTarget);
	this.updateSelected(rowid, false);
	/* NO LO HACEMOS SI PICAN "FUERA"
	this.setValue(rowid);
	*/
	
	// Ocultamos el Suggest
	this.hideSuggest();
	this.postFocus();
};

// Evento de mouseup de la capa
ssiPopupSuggest.prototype.onDivMouseUp = function (oEvent) {

	// Obtengo el evento y el destino
	oEvent = oEvent || window.event;
	oTarget = oEvent.target || oEvent.srcElement;

	// Si no hay resultados no hacemos nada
	if (this.provider.numItems <= 0)
			return;

	this.postFocus();
};

// Evento de mousemove de la capa
ssiPopupSuggest.prototype.onDivMouseOver = function (oEvent) {

	// Obtengo el evento y el destino
	oEvent = oEvent || window.event;
	oTarget = oEvent.target || oEvent.srcElement;
	
	// Si no hay resultados no hacemos nada
	if (this.provider.numItems <= 0)
			return;

	// Miro el item que donde está el ratón y actualizo
	var newSel = this.getRowIndex(oTarget);
	this.updateHighlighted(newSel);
};


// Obtener el LEFT del INPUT para colocar el Suggest
ssiPopupSuggest.prototype.getLeft = function () /*:int*/ {

  var oNode = this.textbox;
  var iLeft = 0;
  
  while(oNode && oNode.tagName != "BODY") {
      iLeft += oNode.offsetLeft;
      oNode = oNode.offsetParent;        
  }
  
  return iLeft;
};

// Obtener el TOP del INPUT para colocar el Suggest
ssiPopupSuggest.prototype.getTop = function () /*:int*/ {

  var oNode = this.textbox;
  var iTop = 0;
  
  while(oNode && oNode.tagName != "BODY") {
      iTop += oNode.offsetTop;
      oNode = oNode.offsetParent;
  }
  
  return iTop;
};

ssiPopupSuggest.prototype.getWidth = function() /*:int*/ {

	var oNode = this.textbox;
	return oNode.offsetWidth;
};

ssiPopupSuggest.prototype.getRowTop = function(row) {

  var oNode = row;
  var iTop = 0;
  
  while(oNode && oNode.id != this.layerName) {
      iTop += oNode.offsetTop;
      oNode = oNode.offsetParent;
  }
  
  return iTop;
};

ssiPopupSuggest.prototype.isRowVisible = function(row) {
	var bottom = this.layer.scrollTop + this.layer.offsetHeight;
	var rowTop = this.getRowTop(row);
	var rowBottom = rowTop + row.offsetHeight;
	// Si está por debajo
	if (rowBottom > bottom)
			return false;
	// Si está por encima
	if (rowTop < this.layer.scrollTop)
			return false;
	return true;
};

// Actualizar el item highlighted (por el ratón)
ssiPopupSuggest.prototype.updateHighlighted = function(NewSel) {

	// Desactivamos el antiguo (si no es el seleccionado)
	if (this.highIndex != -1 && this.highIndex != this.selIndex)
		{
			var item = document.getElementById(this.itemTemplateName + this.highIndex);
			if (item)
					item.className = "";
		}
	// Asignamos el highIndex
	this.highIndex = NewSel;
	// A por el nuevo item...
	if (this.highIndex != -1)
		{
			var item = document.getElementById(this.itemTemplateName + this.highIndex);
			if (item)
					item.className = "current";
		}
};

// Actualizar el item seleccionado
ssiPopupSuggest.prototype.updateSelected = function(NewSel, Scroll, DontUpdateTextBox) {

	// Miramos el item antiguo y lo desactivamos
	// alert("updateSelected: NewSel = " + NewSel + ", Scroll = " + Scroll);
	if (this.selIndex != -1)
		{
			var item = document.getElementById(this.itemTemplateName + this.selIndex);
			if (item)
					item.className = "";
		}
	// Asignamos el selIndex
	this.selIndex = NewSel;
	// A por el nuevo item...
	if (this.selIndex != -1)
		{
			var item = document.getElementById(this.itemTemplateName + this.selIndex);
			if (item)
					item.className = "current";
			// Movemos el scroll si toca
			if ((this.isRowVisible(item) == false) && (Scroll == true))
					this.layer.scrollTop = this.getRowTop(item);
			// Ahora ponemos el texto en el Suggest...
			if (DontUpdateTextBox != true)
				{
					this.setValue(this.selIndex);
					//this.textbox.value = this.provider.getRowText(this.selIndex);
				}
		}
	else
		{
			if (Scroll == true)
					this.layer.scrollTop = 0;
		}
};

ssiPopupSuggest.prototype.getRowIndex = function(oNode) {
	var row = oNode;
	var rowIdBegin = this.itemTemplateName;
	var rowIdBeginLen = rowIdBegin.length;
	while (row != null && row.parentNode/*offsetParent*/ != null)
		{
			// Si tiene id, este id comienza por rowIdBegin
			if (row.id && row.id.substring(0,rowIdBeginLen) == rowIdBegin)
				{
					return Number(row.id.substring(rowIdBeginLen));
				}
			row = row.parentNode/*offsetParent*/;
		}
	// Si hemos llegado aquí, na de na
	return -1;
};

ssiPopupSuggest.prototype.setValue = function(iNewSel) {
	if (iNewSel == -1) {
		// Da igual... nunca se llega
		this.textbox.value = "";
	}
	else {
		//alert('Valor nuevo: ' + this.provider.getRowText(iNewSel));
		//this.textbox.value = this.provider.getRowText(iNewSel);

		var textbox = this.textbox;
		var position = this.getCursorPos();
		// Buscamos ';' antes o después...
		var totalText = textbox.value;
		var parts = totalText.split(';');
		var partIndex = 0;
		var finalText = "";
		var finalPos = 0;
		for (; partIndex < parts.length; partIndex++) {
			if (position <= parts[partIndex].length) {
				if (parts[partIndex].charAt(0) == '+' || parts[partIndex].charAt(0) == '-')
					finalText += parts[partIndex].charAt(0) + this.provider.getRowText(iNewSel);
				else
					finalText += this.provider.getRowText(iNewSel);
				finalPos = finalText.length;
				if (partIndex < (parts.length - 1))
					finalText += ';';
				// Pongo position al "final" para que no "moleste" más
				position = 100000;
			}
			else {
				finalText += parts[partIndex];
				if (partIndex < (parts.length - 1))
					finalText += ';';
			}
			position -= parts[partIndex].length + 1; // añadimos el ; de la parte 
		}
		// Finalmente, volcamos el resultado a la caja y ponemos el cursos donde toca
		this.textbox.value = finalText;
		this.setCursorPos(finalPos);

		// Lanzamos un evento de keypress con un espacio y luego un retroceso, de manera
		// que forzamos a que sea visible el cursor
		this.textbox.focus();
		if (typeof (document.createEvent) != 'undefined') { // Mozilla, WebKit
			var evt = document.createEvent("KeyboardEvent");
			if (typeof (evt.initKeyboardEvent) != 'undefined') // WebKit
				evt.initKeyboardEvent("keypress", false, true, null, false, false, false, false, 32, 32 /* space */);
			else // IE y Firefox
				evt.initKeyEvent("keypress", false, true, null, false, false, false, false, 0, 32 /* space */);
			this.textbox.dispatchEvent(evt);
			evt = document.createEvent("KeyboardEvent");
			if (typeof (evt.initKeyboardEvent) != 'undefined') // WebKit
				evt.initKeyboardEvent("keypress", false, true, null, false, false, false, false, 8, 8 /* backspace */);
			else // IE y Firefox
				evt.initKeyEvent("keypress", false, true, null, false, false, false, false, 8, 0 /* backspace */);
			this.textbox.dispatchEvent(evt);
		} else { // Internet Explorer
			;
		}
	}
}

// Rutina para poner el tamaño y posición definitivo del layer
ssiPopupSuggest.prototype.setPosition = function () {
	// Lo pongo arriba a la izquierda para que no saque scrolls en el documento
	this.layer.style.left = "0px";
	this.layer.style.top = "0px";
	// Lo habilito para los cálculos pero lo mantengo oculto
	this.layer.style.visibility = "hidden";
	this.layer.style.display = "block";
	
	var docWidth = document.body.offsetWidth;
	var layerWidth = this.layer.offsetWidth;
	
	// Si la anchura es inferior al control, ambplío la capa hasta el tamaño del control
	if (layerWidth < this.getWidth())
			this.layer.style.width = this.getWidth() + "px";
		
	// Ahora hago los cálculos.
	if (this.getLeft() + layerWidth > docWidth) // Si se pasa por la derecha
			this.layer.style.left = (this.getLeft() + this.getWidth() - layerWidth) + "px";
	else
			this.layer.style.left = this.getLeft() + "px";
		
	this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px";
	
	// Restauro el estilo...
	this.layer.style.display = "none";
	this.layer.style.visibility = "visible";
}

// Rutina para mostrar el Suggest
ssiPopupSuggest.prototype.showSuggest = function () {

	// Reposicionamos el layer con las sugerencias
  this.setPosition();
  
  // Si ya es visible, no hacemos nada más
	if (this.layer.style.display == "block")
			return;

  // Mostramos la máscara para capturar el ratón
  this.mask.showMask();

	// Mostramos las sugerencias
  this.layer.style.display = "block";
  
};

// Rutina para ocultar el Suggest
ssiPopupSuggest.prototype.hideSuggest = function () {
	if (this.layer.style.display == "none")
			return;
			
  this.mask.hideMask();
  this.layer.style.display = "none";
  
  // Quito el high, puesto que ya no es visible
  this.updateHighlighted(-1);
  
  // Quito la selección, puesto que ya no es válida
  this.selIndex = -1;
};

// Rutina para saber si es visible el Suggest
ssiPopupSuggest.prototype.isSuggestVisible = function() {
	// Si vale "" ó "none" es que no es visible.
	if (this.layer.style.display == "none" || this.layer.style.display == "")
			return false;
	else
			return true;
};

ssiPopupSuggest.prototype.selectTextRange = function (iStart /*:int*/, iEnd /*:int*/) {

  // Miramos de usar TextRange (para IE)
  if (this.textbox.createTextRange) {
      var oRange = this.textbox.createTextRange(); 
      oRange.moveStart("character", iStart); 
      oRange.moveEnd("character", iEnd - this.textbox.value.length);      
      oRange.select();
      
  // Miramos de usar setSelectionRange() para Mozilla
  } else if (this.textbox.setSelectionRange) {
      this.textbox.setSelectionRange(iStart, iEnd);
  }     

  // Ponemos el foco en el input
  this.textbox.focus();      
};

ssiPopupSuggest.prototype.selectTextAll = function () {

  // Miramos de usar TextRange (para IE)
  if (this.textbox.createTextRange) {
      var oRange = this.textbox.createTextRange(); 
      oRange.moveStart("character", 0); 
      oRange.moveEnd("character", this.textbox.value.length);
      oRange.select();
      
  // Miramos de usar setSelectionRange() para Mozilla
  } else if (this.textbox.setSelectionRange) {
      this.textbox.setSelectionRange(0, this.textbox.value.length);
  }     
}


// Rutina para ocultar el Suggest con cierto retardo
// Esto se debe hacer porque cuando se cierra el calendario
// Explorer vuelve a enviar un "onfocus" al INPUT y vuelve
// a aparecer. Al retardar la ocultación, el efecto ya no se nota.
ssiPopupSuggest.prototype.postHideSuggest = function () {
	var oThis = this;
	setTimeout(function () { oThis.hideSuggest(); },100);
};

// Rutina para poner el foco en el input con cierto retardo
ssiPopupSuggest.prototype.postFocus = function () {
	var oThis = this;
	setTimeout(function () { oThis.textbox.focus(); },100);
}

