/**
* Objeto combobox realizado en ECMAScript - DOM 1 compliant
*
* Pretende reemplazar al objeto combobox (<SELECT>) de HTML, con muchas 
* más opciones gráficas
*
* @project 		flatComboBox
* @author			Carlos de León Fragoso
* @company		SolucionesNet
* @date				27.12.04
* @since			27.12.04
* @version		0.1.0
* @w3c				HTML 4.01 Transitional - DOM 1- ECMA 262
*
* @TODO				- Nada
*
* 	tab:2
*/

FLATCOMBO_ELEMENTOS_MAX_DEF = 6;      // opciones mostradas como máximo. Valor por defecto
FLATCOMBO_VELOCIDAD_SCROLL_DEF = 220;	// Velocidad a la que se mueve el scroll, valor por defecto
FLATCOMBO_FLECHASUPERIOR_ACTIVA = 'js/img/flechascrollcombo.gif';  // flecha superior del scroll
FLATCOMBO_FLECHASUPERIOR_INACTIVA = 'js/img/flechascrollcombo_.gif'; 
FLATCOMBO_FLECHAINFERIOR_ACTIVA = 'js/img/flechascrollcombo2.gif'; // flecha inferior del scroll
FLATCOMBO_FLECHAINFERIOR_INACTIVA = 'js/img/flechascrollcombo_2.gif'; 
FLATCOMBO_FLECHA_ACTIVADA = 'js/img/flechacombo2.gif';  // flecha de despliegue del combo
FLATCOMBO_FLECHA_DESACTIVADA = 'js/img/flechacombo.gif';
FLATCOMBO_TEXTO_INICIAL = '-= Seleccione una opción =-'; // Texto que muestra el combo cuando no hay opción seleccionada


/**
* Devuelve la posición absoluta izquierda en píxeles dado un elemento
*
* @param		object	el 			Elemento del que queremos la posición izquierda absoluta
* @return		int 		x 			Posición inzquierda del elemento
*/
function offsetLeftPag(el) {
  var x = el.offsetLeft;
  if (el.offsetParent != null)
	x += offsetLeftPag(el.offsetParent);
  return x;
} // offsetLeftPag()


/**
* Devuelve la posición absoluta superior en píxeles dado un elemento
*
* @param		object	el 			Elemento del que queremos la posición superior absoluta
* @return		int 		x 			Posición inzquierda del elemento
*/
function offsetTopPag(el) {
  var x = el.offsetTop;
  if (el.offsetParent != null)
	x += offsetTopPag(el.offsetParent);
  return x;
} // offsetLeftPag()


/**
* Método que añade una opción en el combo
*
* @param		sting		nombre	Cadena de texto que representa el valor de la opción en el combo
* @param		variant	valor		Valor de la opción añadida
* @access 	public
*/
function flatComboBox_addOption(nombre, valor) {
	this._opciones.push(new Array(nombre,valor));
}	//flatComboBox_addOption()


/**
* Método que dibuja una opción en la tabla
*
* @param	int		numOpcion 	Número de la opción (en el array de opciones)
* @param	int 	posicion		Posición donde ser dibujada dentro de la tabla
* @access private
*/
function flatComboBox_anexaOpcionTabla(numOpcion, posicion) {
	var nuevaOpcion = this._tabla.insertRow(posicion);
	var nuevaCelda = nuevaOpcion.insertCell(0);
	nuevaCelda.appendChild(document.createTextNode(this._opciones[numOpcion][0]));
	nuevaCelda._padre = this;
	if (numOpcion != this._selIndex) {
		nuevaCelda.className = this._claseOpciones;
		nuevaCelda.onmouseout = function () {this.className = this._padre._claseOpciones}
	}
	else {
		nuevaCelda.className = this._claseOpcionesActual;
		nuevaCelda.onmouseout = function () {this.className = this._padre._claseOpcionesActual}
	}
	
	nuevaCelda.onmouseover = function () {this.className = this._padre._claseOpcionesActivas}
	nuevaCelda.onclick = function () {this._padre._desplega(); this._padre.setSelectedIndex(numOpcion, true)}
	nuevaCelda.colSpan = 2;
}	// flatComboBox_anexaOpcionTabla()


/**
* Método que Incluye en la tabla de opciones la flecha superior del scroll
*
* Las flechas superior e inferior de scroll sólo se incluyen si no caben todos los elementos, 
* si estos si caben simplemente no se dibujan. Ambas flechas tienen dos estados - activo e inactivo -
* el primero si hay más elementos en esa dirección y el segundo de los casos en en caso contrario.
* esta función se llama sin importar si ya la flecha está incluida o no, pues ya ese caso se tiene 
* en cuenta
*
* @param	bool	activa	Determina si la flecha ha de estar activa o no
* @access private
*/
function flatComboBox_anexaFlechaSuperior(activa) {
	// Si hay más de dos celdas en el combo, la flecha superior ha sido incluida anteriormente
	if (this._tabla.rows.length >= 2) 
		this._tabla.deleteRow(1);
		
	this.flechaArriba = this._tabla.insertRow(1);
	var nuevaCelda = this.flechaArriba.insertCell(0);
	this.imgFlechaArriba = document.createElement('IMG');
	nuevaCelda.appendChild(this.imgFlechaArriba);
	nuevaCelda.className = this._claseScrolls;
	nuevaCelda.colSpan = 2;	

	if (activa) {
		this.imgFlechaArriba.src = FLATCOMBO_FLECHASUPERIOR_ACTIVA;
		this.flechaArriba._padre = this;	
		this.flechaArriba.onmouseover = this._scrollup;
		this.flechaArriba.onmouseout = this._paraMovimiento;
	}
	else {
		this.imgFlechaArriba.src = FLATCOMBO_FLECHASUPERIOR_INACTIVA;  
	}
} // flatComboBox_anexaFlechaSuperior()


/**
* Método que incluye en la tabla de opciones la flecha inferior del scroll
*
* Más info en flatComboBox_anexaFlechaSuperior()
*
* @param	bool	activa	Determina si la flecha ha de estar activa o no
* @access private
*/
function flatComboBox_anexaFlechaInferior(activa) {
	// ¿está ya anexada la felcha de abajo?
	if (this._tabla.rows.length >= this.elementosMostrados + 3)
		this._tabla.deleteRow(this.elementosMostrados + 2);
		
	this.flechaAbajo = this._tabla.insertRow(this.elementosMostrados + 2);
	var nuevaCelda = this.flechaAbajo.insertCell(0);
	nuevaCelda._padre = this;
	this.imgFlechaAbajo = document.createElement('IMG');
	nuevaCelda.appendChild(this.imgFlechaAbajo);
	nuevaCelda.className = this._claseScrolls;
	nuevaCelda.colSpan = 2;	
	
	if (activa) {
		this.imgFlechaAbajo.src = FLATCOMBO_FLECHAINFERIOR_ACTIVA;
		this.flechaAbajo._padre = this;		
		this.flechaAbajo.onmouseover = this._scrolldown; //this._scrolldown;
		this.flechaAbajo.onmouseout = this._paraMovimiento;
	}
	else {
		this.imgFlechaAbajo.src = FLATCOMBO_FLECHAINFERIOR_INACTIVA;
	}
}	// flatComboBox_anexaFlechaInferior()


/**
* Método que se encarga de desplegar (y volver a plegar) el combo
*
* La adición y eliminación de las opciones se hace simplemente añadiendo y eliminando filas de la
* tabla que contiene el combo. Esta función también controla el hecho de añadir los scrolls en el 
* cuando en el momento del despliegue haga falta.
*
* @access private
*/
function flatComboBox_desplega() {
	if (this._padre._desplegado < 0){
		if (this._padre._opciones.length > this._padre.elementosMostrados) {
			this._padre._primeraFila = 2;
		}
		var pos = 0;
				
		if (this._padre._selIndex >= 0 && this._padre._opciones.length > this._padre.elementosMostrados) {
			if (this._padre._selIndex + this._padre.elementosMostrados <= this._padre._opciones.length)
				pos = this._padre._selIndex;
			else
				pos = this._padre._opciones.length - this._padre.elementosMostrados;
		}
		if (this._padre.elementosMostrados < this._padre._opciones.length) {
			this._padre._anexaFlechaSuperior(pos != 0);
		}
		var limite = Math.min(this._padre._opciones.length, pos + this._padre.elementosMostrados);
		for (var i = 0, index = pos; index < limite; i++, index++) {
			this._padre._anexaOpcionTabla(index, i + this._padre._primeraFila);
		}
		this._padre._flecha.src = FLATCOMBO_FLECHA_ACTIVADA;
		this._padre._desplegado = pos;
		if (this._padre.elementosMostrados < this._padre._opciones.length) {
			this._padre._anexaFlechaInferior(pos != this._padre._opciones.length - this._padre.elementosMostrados);			
		}
		else {
			this._padre._paraMovimiento();
		}
	}
	else {
		var numfilas = this._padre._tabla.rows.length;
		for (var i = 1; i < numfilas; i++)
			this._padre._tabla.deleteRow(1);
		this._padre._desplegado = -1;
		this._padre._flecha.src = 		this._padre._flecha.src = FLATCOMBO_FLECHA_DESACTIVADA;
	}
} // flatComboBox_desplega()


/**
* Método que realiza un scroll hacia abajo de las opciones
*
* Simula un scroll de opciones añadiendo y eliminando filas de la tabla del combo. El scroll 
* está temporizado y continúa en ejecución mientras no se llame al método _paraMovimiento () 
* o se llegue al final del scroll.
*
* @access private
*/
function flatComboBox_scrolldown() {
	if ((this._padre._desplegado + this._padre.elementosMostrados) < (this._padre._opciones.length)) {
		this._padre._tabla.deleteRow(this._padre._primeraFila);
		this._padre._anexaOpcionTabla(this._padre._desplegado + this._padre.elementosMostrados,this._padre.elementosMostrados + (this._padre._primeraFila-1));
		this._padre._desplegado ++;
		this._padre._anexaFlechaSuperior(true);
		if((this._padre._desplegado + this._padre.elementosMostrados) == (this._padre._opciones.length ))
		{
			this._padre._anexaFlechaInferior(false);
			this._padre._paraMovimiento();
		}
		else
			this._padre._accionScrollAbajo = setTimeout(this._padre._nombre + '._scrolldown()',this._padre.velocidadScroll)
	}
} // flatComboBox_scrolldown()

/**
* Método que realiza un scroll hacia arriba de las opciones
*
* Simula un scroll de opciones añadiendo y eliminando filas de la tabla del combo. El scroll 
* está temporizado y continúa en ejecución mientras no se llame al método _paraMovimiento () 
* o se llegue al final del scroll.
*
* @access private
*/
function flatComboBox_scrollup() {
	if (this._padre._desplegado > 0) {
		this._padre._tabla.deleteRow(this._padre.elementosMostrados + (this._padre._primeraFila-1));
		this._padre._desplegado --;
		this._padre._anexaOpcionTabla(this._padre._desplegado,this._padre._primeraFila);
		this._padre._anexaFlechaInferior(true);
		if (this._padre._desplegado == 0) {
			this._padre._anexaFlechaSuperior(false);
			this._padre._paraMovimiento();
		}
		else
			this._padre._accionScrollArriba = setTimeout(this._padre._nombre + '._scrollup()',this._padre.velocidadScroll)
	}
}	// flatComboBox_scrollup()


/**
* Método que para los movimientos de scroll
*
* Para lo moivimientos de scroll, tanto superior como inferior
*
* @access private
*/
function flatComboBox_paraMovimiento () {
	clearTimeout(this._padre._accionScrollArriba);
	clearTimeout(this._padre._accionScrollAbajo);	
	this._padre._accionScrollArriba = null;
	this._padre._accionScrollAbajo = null;	
}	// flatComboBox_paraMovimiento ()


/**
* Método que activa una opción en el combo dando el índice de la misma
*
* @param	int		index	El índice de la opción a seleccionar
* @param	bool	raiseEvent (opcional = false) Indica si se ha de producir el evento onchange
*
* @throw	onchange(viejoIndice, nuevoIndice, nuevoNombre, nuevoValor)
*
* @access public
*/
function flatComboBox_setSelectedIndex(index, raiseEvent) {
	var oldIndex = this._selIndex;
	if (oldIndex != index) {
		this._selIndex = index;
		this._celdaExplicacion.removeChild(this._celdaExplicacion.firstChild);
		this._celdaExplicacion.appendChild(document.createTextNode(this._opciones[index][0]));
		if (raiseEvent && this.onchange != null)
			this.onchange(oldIndex, this._selIndex, this._opciones[index][0], this._opciones[index][1]);
	}		
} // flatComboBox_setSelectedIndex()


/**
* Método que devuelve el índice de la opción elegida actualmente
*
* @return	int	Índice de la opción elegida actualmente
*
* @access public
*/
function flatComboBox_selectedIndex () {
	return this._selIndex;
}	// flatComboBox_selectedIndex ()


/**
* Constructor de la clase flatComboBox
*
* Crea un nuevo objeto flatComboBox y lo inicializa.
*
* @const int		FLATCOMBO_ELEMENTOS_MAX_DEF       	Opciones mostradas como máximo. Valor por defecto
* @const int		FLATCOMBO_VELOCIDAD_SCROLL_DEF    	Velocidad a la que se mueve el scroll, valor por defecto
* @const string	FLATCOMBO_FLECHASUPERIOR_ACTIVA 		Flecha superior del scroll
* @const string	FLATCOMBO_FLECHASUPERIOR_INACTIVA		Flecha superior del scroll
* @const string	FLATCOMBO_FLECHAINFERIOR_ACTIVA 		Flecha inferior del scroll
* @const string	FLATCOMBO_FLECHAINFERIOR_INACTIVA		Flecha inferior del scroll	
* @const string	FLATCOMBO_FLECHA_ACTIVADA 					Flecha de despliegue del combo
* @const string	FLATCOMBO_FLECHA_DESACTIVADA 				Flecha de despliegue del combo
* @const string FLATCOMBO_TEXTO_INICIAL 						Texto que muestra el combo cuando no hay opción seleccionada
*
*	@param	string 	nombre						Id de la imagen de situación
*	@param	string 	claseContenedor		Clase del contenedor
*	@param	string 	clasePrincipal		Clase de la fila principal que siempre esta visible
*	@param	string	claseOpciones			Clase normal de las opciones
*	@param	string	claseOpcionesActivas	Clase de las opciones cuando se activan
*	@param	string	claseOpcionesActual		Clase de la opción que está seleccionada en el momento del despliegue
*	@param	string	claseScrolls			Clase de las lineas de scroll
*
* @access public
*/
function flatComboBox(nombre, claseContenedor, clasePrincipal, claseOpciones, claseOpcionesActivas, claseOpcionesActual, claseScrolls) {
	this.elementosMostrados = FLATCOMBO_ELEMENTOS_MAX_DEF;
	this.velocidadScroll = FLATCOMBO_VELOCIDAD_SCROLL_DEF;
	this.addOption = flatComboBox_addOption;
	this.setSelectedIndex = flatComboBox_setSelectedIndex;
	this.selectedIndex = flatComboBox_selectedIndex;
	this.onchange = null;  // usar el siguiente prototipo  function combo1_onchange(oldIndex, newIndex, newName, newValue)
	
	this._padre = this; // El _padre del principal es el propio principal
	this._primeraFila = 1; // Primera fila de la tabla de opciones que es una opcion (si caben todas es 1 si se necesita scroll 2)
	this._scrolldown = flatComboBox_scrolldown;  // método que realiza un scroll hacia abajo de las opciones
	this._scrollup = flatComboBox_scrollup; // método que realiza un scroll hacia arriba de las opciones
	this._anexaFlechaSuperior = flatComboBox_anexaFlechaSuperior;  // método que añade el elemento de scroll superior
	this._anexaFlechaInferior = flatComboBox_anexaFlechaInferior;  // método que añade el elemento de scroll inferior
	this._clasePrincipal = clasePrincipal; // Clase principal del combo
	this._claseOpciones = claseOpciones; // Clase de las opciones
	this._claseOpcionesActivas = claseOpcionesActivas;	// Clase de las opciones activas
	this._claseOpcionesActual = claseOpcionesActual; // Clase de las opcion actual
	this._claseScrolls = claseScrolls; // Clase de las lineas de scroll
	this._selIndex = -1;  // Índice del elemento seleccionado
	this._opciones = new Array();  // Array de las opciones
	this._desplega = flatComboBox_desplega; // metodo que desplega (o plega) el combo
	this._desplegado = -1; // la primera opción visible en la tabla (al principio no hay ninguna visible)
	this._anexaOpcionTabla = flatComboBox_anexaOpcionTabla; // añade una opción a la tabla dados el numero de opción y la posición en la tabla.
	this._paraMovimiento = flatComboBox_paraMovimiento; // método que para el movimiento de scroll
	this._nombre = nombre; // nombre de la variable que acoje el combo
	this._accionScrollArriba = null; // Mango para el scroll temporizado hacia arriba
	this._accionScrollAbajo = null;	// Mango para el scroll temporizado hacia abajo
	
	this._imagen = document.getElementById(nombre);  // imagen que marca la posición del combo

	this._capaCombo =  document.createElement('DIV');  // capa principal del combo
	this._capaCombo.className = claseContenedor;
	this._capaCombo.style.position = 'absolute';
	this._capaCombo.style.top = offsetTopPag(this._imagen)
	this._capaCombo.style.left = offsetLeftPag(this._imagen);
	document.body.appendChild(this._capaCombo);	
	
	this._tabla =  document.createElement('TABLE');  // Tabla que variará y contendrá las distintas filas del combo, tanto la principal como las de scroll y opciones
	this._tabla.cellPadding = 0;
	this._tabla.cellSpacing = 0;
	this._capaCombo.appendChild(this._tabla);
	
	this._filaFija = this._tabla.insertRow(0);  // Fila Principal de la tabla (la que está siempre visible y creada)
	this._filaFija.onclick = this._desplega;
	this._filaFija._padre = this;
	
	this._celdaExplicacion= this._filaFija.insertCell(0);  // celda donde se coloca la explicación de la opción seleccionada
	this._celdaExplicacion.width = '100%';
	this._celdaExplicacion.className = this._clasePrincipal;
	
	this._celdaExplicacion.appendChild(document.createTextNode(FLATCOMBO_TEXTO_INICIAL));

	this._flecha = document.createElement('IMG'); // imagen de la flecha del combo
	this._flecha.src = FLATCOMBO_FLECHA_DESACTIVADA;
	
	this._celdaFlecha= this._filaFija.insertCell(1);  // Celda de la flecha del combo
	this._celdaFlecha.appendChild(this._flecha);
	this._celdaFlecha.className = clasePrincipal;
}	// flatComboBox();