/**
 * Validacion de Formularios.
 *
 * Version 1.7
 *
 * Eneko Gonzalez - 09/04/2003
 *
 * Funcionamiento:
 *
 * Creamos una tabla con formato: [Nombre-Del-Campo, Nombre, Formato, Auto-Correccion]
 *
 *		- Nombre-Del-Campo: Nombre del textField, textArea o Password (desde v1.2) a comprobar.
 *			Puede ser el nombre exacto o una expresion regular (desde v1.5).
 *
 * 		- Nombre: Nombre a usar en los mensajes de error, en vez de Nombre-Del-Campo.
 *
 *		- Formato: Formato que debe cumplir el valor del campo.
 *
 *			· Siempre debe empezar y terminar con el caracter "guion bajo". Ej: _ENTERO_
 *
 * 			· Si un elemento debe cumplir mas de un formato, se puede indicar, segun orden de prioridad.
*
 *				Por ejemplo: _OBLIGATORIO_NATURAL_ comprueba primero que sea obligatorio, y solo si lo es, que sea natural.
 *
 *			· Un formato puede tener parametros, en cuyo caso apareceran tras el caracter # (Desde v1.1). Para mayor claridad, el
 *				nombre de los formatos con parametros terminara en un numero, indicando el numero de parametros necesarios.
 *
 *				Por ejemplo: _REALDEC1#2_ identifica reales de 2 (parametro #1) decimales
 * 							 _FECHA2#-#4_ identificaria fechas con '-' (param #1) como separador y 4 (param #2) digitos para el año (ojo! aun sin hacer)
 *
 *		- Auto-Correccion (desde v1.6): true si quieres que se intente corregir el fallo en el formato, false (o nada) si no.
 *
 *	 	Ejemplo:
 *
 *			<SCRIPT>
 *				var tablaFormulario = [
 *
 *					['textField1', "DNI", "_OBLIGATORIO_DNI_"],
 *					['textArea1', "EDAD", "_NATURAL_", false],
 *					['textField2', "NACIMIENTO", "_FECHA_", true],
 *
 *					];
 *			</SCRIPT>
 *
 *
 *	Para poder utilizarlo, hay que añadir la siguiente linea:
 *
 *		<SCRIPT SRC="validacion.js"></SCRIPT>
 *
 * 	Para validar un formulario, tenemos varias posibilidades:
 *
 * 		· <FORM ..... onSubmit="ValidarFormulario (this, tablaFormulario);"> (ojo! a veces, este metodo parece no funcionar)
 *
 * 		· en el boton de accion, poner: href="javascript.validar();" usando como funcion validar:
 *
 *			function validar()
 *			{
 *				if ValidarFormulario (document.formulario, tablaFormulario))
 *				{
 *					document.formulario.submit();
 *				}
 *			}
 */

/**
 * Declaracion de los formatos que se pueden utilizar.
 * Si se intenta ultilizar un formato que no aparezca aqui, se obtendra un mensaje de error del tipo:
 *
 *		"Formato-X no esta definido"
 *
 * Cada variable aqui declarada debe tener su definicion mas adelante, y una entrada en 
 * el primer 'switch' de la funcion Comprobar() 
 *
 * Los formatos que utilizan parametros, acaban en un numero que indica el numero de parametros.
 */

var _DIGITO_ ;				// DIGITO (EXACTAMENTE 1 CIFRA)
var _ENTEROPOS_ ;			// NUMERO ENTERO POSITIVO
var _ENTERO_ ;				// NUMERO ENTERO
var _REALPOS_ ;				// NUMERO REAL POSITIVO SIN LIMITACION DE NUMERO DE DECIMALES
var _REALPOSCOMA_ ;			// IDEM. CON ',' COMO SEPARADOR DECIMAL
var _REALPOSPTO_ ;			// IDEM. CON '.' COMO SEPARADOR DECIMAL
var _REALPOSDEC1_ ;			// NUMERO REAL POSITIVO CON X DECIMALES (X = PARAMETRO)
var _REALPOSCOMADEC1_ ;		// IDEM. CON ',' COMO SEPARADOR DECIMAL
var _REALPOSPTODEC1_ ; 		// IDEM. CON '.' COMO SEPARADOR DECIMAL
var _REAL_ ;				// NUMERO REAL SIN LIMITACION DE NUMERO DE DECIMALES
var _REALCOMA_ ;			// IDEM. CON ',' COMO SEPARADOR DECIMAL
var _REALPTO_ ;				// IDEM. CON '.' COMO SEPARADOR DECIMAL
var _REALDEC1_ ;			// NUMERO REAL CON X DECIMALES (X = PARAMETRO)
var _REALCOMADEC1_ ;		// IDEM. CON ',' COMO SEPARADOR DECIMAL
var _REALPTODEC1_ ;			// IDEM. CON '.' COMO SEPARADOR DECIMAL
var _LETRA_ ;				// LETRAS DEL ALFABETO CASTELLANO (ACENTOS, Ñ, DIERESIS INCLUIDAS)
var _CARACTER_ ;			// LETRAS DEL ALFABETO, SIN INCLUIR LA Ñ NI ACENTOS
var _FECHA_ ;				// FORMATO DD/MM/AAAA
var _HORA_ ;				// FORMATO HH:MM:SS
var _EMAIL_ ;				// DIRECCION DE CORREO ELECTRONICO
var _DNI_ ;					// DNI EN CUALQUIERA DE ESTOS 3 FORMATOS: 12345678, 12345678A Y 12345678-A
var _TELEFONO_ ;			// NUMERO DE TELEFONO DE 9 CIFRAS
var _MAYOR1_;				// COMPARA QUE 'DATO > X'
var _MENOR1_;				// COMPARA QUE 'DATO < X'									
var _MAYORIGUAL1_;			// COMPARA QUE 'DATO >= X'
var _MENORIGUAL1_;			// COMPARA QUE 'DATO <= X'
var _IGUAL1_;				// COMPARA QUE 'DATO == X'
var _DISTINTO1_;			// COMPARA QUE 'DATO != X'
var _MAYORFORM2_;			// COMPARA QUE 'DATO > x' (x = VALOR DEL CAMPO X DEL FORMULARIO, CUYO NOMBRE ES Y)
var _MENORFORM2_;			// COMPARA QUE 'DATO < x' (x = VALOR DEL CAMPO X DEL FORMULARIO, CUYO NOMBRE ES Y)				
var _MAYORIGUALFORM2_;		// COMPARA QUE 'DATO >= x' (x = VALOR DEL CAMPO X DEL FORMULARIO, CUYO NOMBRE ES Y)
var _MENORIGUALFORM2_;		// COMPARA QUE 'DATO <= x' (x = VALOR DEL CAMPO X DEL FORMULARIO, CUYO NOMBRE ES Y)
var _IGUALFORM2_;			// COMPARA QUE 'DATO == x' (x = VALOR DEL CAMPO X DEL FORMULARIO, CUYO NOMBRE ES Y)
var _DISTINTOFORM2_;		// COMPARA QUE 'DATO != x' (x = VALOR DEL CAMPO X DEL FORMULARIO, CUYO NOMBRE ES Y)
var _CUENTAFORM4_;			// NUMERO DE CUENTA BANCARIA 'DATO - W - X - Y', CUYO NOMBRE ES Z
var _TARJETA_;				// NUMERO DE TARJETA DE CREDITO
var _LONGMIN1_;				// LONGITUD MINIMA DEL CAMPO 

/**
 * Variables auxiliares utilizadas.
 */
var _SEPARADOR_FECHAS 		= "/" ;
var _SEPARADOR_HORAS 		= ":" ;

/**
 * Definicion de los formatos que se pueden utilizar
 * 
 * Mas informacion sobre expresiones regulares en:			
 * 		http://developer.netscape.com/docs/manuals/js/client/jsguide/regexp.htm
 *
 * NOTA: Los parametros se escriben #i donde i es un numero natural.
 * NOTA: Los formatos que utilizan parametros, acaban en un numero que indica el numero de parametros.
 * NOTA: Las barras invertidas deben ser dobles!! (\\)
 * NOTA: Las expresiones con o logicos deben ir entre parentesis (zx|zx)
 * NOTA: Los formatos expeciales, deben tener como valor "" (desde v1.3)
 */

// DIGITO = Exactamente 1 cifra
_DIGITO_ 		= "\\d";
// ENTERO POSITIVO = n cifras (n > 0)
_ENTEROPOS_		= _DIGITO_ + "+";
// ENTERO = - (opcional) ENTERO POSITIVO
_ENTERO_ 		= "-?" + _ENTEROPOS_;
// REAL POSITIVO = ENTERO POSITIVO ó ENTERO POSITIVO ',' ENTERO POSITIVO
_REALPOSCOMA_ 		= "(" + _ENTEROPOS_ + "|(" + _ENTEROPOS_ + "," + _ENTEROPOS_ + "))";
_REALPOSPTO_ 	= "(" + _ENTEROPOS_ + "|(" + _ENTEROPOS_ + "\\." + _ENTEROPOS_ + "))";
// REALPOSDEC = REALPOS con como máximo #1 decimales
_REALPOSCOMADEC1_ = "(" + _ENTEROPOS_ + "|(" + _ENTEROPOS_ + "," + _DIGITO_ + "{1,#1}" + "))";
_REALPOSPTODEC1_  = "(" + _ENTEROPOS_ + "|(" + _ENTEROPOS_ + "\\." + _DIGITO_ + "{1,#1}" + "))";
// REAL = - (opcional) REAL POSITIVO
_REALCOMA_ 			= "-?" + _REALPOSCOMA_;
_REALPTO_ 		= "-?" + _REALPOSPTO_;
// REALDEC = REAL con como máximo #1 decimales
_REALCOMADEC1_ 		= "-?" + _REALPOSCOMADEC1_;
_REALPTODEC1_	= "-?" + _REALPOSPTODEC1_;
// LETRA = Letras reconocidas en España (acentos, dieresis y ñ incluidas)
_LETRA_			= "([A-Za-z]|ñ|Ñ|á|é|í|ó|ú|Á|É|Í|Ó|Ú|Á|É|Í|Ó|Ú|ü|Ü)";
// CARACTER = Letras alfabeto, sin incluir la ñ
_CARACTER_		= "[A-Za-z]";
// FECHA = dd/mm/aaaa
_FECHA_ 		= "(" + _DIGITO_ + "{2}" + _SEPARADOR_FECHAS + "){2}" + _DIGITO_ + "{4}";
// HORA = hh:mm:ss
_HORA_ 		= "(" + _DIGITO_ + "{2}" + _SEPARADOR_HORAS + "){2}" + _DIGITO_ + "{2}";
// EMAIL = caracteres @ caracteres . caracteres (. caracteres ...) donde caracteres = A-Za-z_
_EMAIL_			= "\\w+@\\w+(\\.\\w+)+";
// DNI admite 12345678 y 12345678-f y 12345678a
_DNI_ = _DIGITO_ + "{8}((-" + _CARACTER_ + ")?|" + _CARACTER_ +"?)";
// TELEFONO = 9 cifras
_TELEFONO_ = _DIGITO_ + "{9}";

_REALPOS_ = "";
_REAL_ = "";
_REALPOSDEC1_ = "";
_REALDEC1_ = "";

_MAYOR1_ = "";
_MENOR1_ = "";				
_MAYORIGUAL1_ = "";
_MENORIGUAL1_ = "";
_IGUAL1_ = "";
_DISTINTO1_ = "";
_TARJETA_ = "";
_LONGMIN1_ = "";
_MAYORFORM2_ = "";				
_MENORFORM2_ = "";				
_MAYORIGUALFORM2_ = "";
_MENORIGUALFORM2_ = "";
_IGUALFORM2_ = "";
_DISTINTOFORM2_ = "";
_CUENTAFORM4_ = "";

/**
 * Busca en una expresion todos los parametros cuyo nombre coincida con el nombre de un campo
 *   del formulario. Si encuentra alguno, lo sustituye por el valor de dicho campo.
 *
 * @param expresion String donde sustituir los parametros
 * @param formulario Formulario del que proceden los datos
 *
 * @return String con los parametros sustituidos por el valor del campo (si lo hay).
 */
function sustituirParamsForm(expresion, formulario)
{
	var seguir = true;
	var pos = 0;					 // Posicion del siguiente #
	var posAnterior = 0;			 // Posicion del anterior #
	var expSustituida = expresion;	 // Resultado
	var longitud = expresion.length; // Longitud de la expresion
	var param;						 // Parametro leido
	var err;

	// Miramos si hay algun parametro
	posAnterior = expresion.indexOf('#', posAnterior);
	
	if (posAnterior > 0)
	{
		while (seguir)
		{
			pos = expresion.indexOf('#', posAnterior + 1);	

			if (pos > 0) // Hay mas parametros, leemos el actual
			{
				param = expresion.substring(posAnterior + 1, pos);
			}
			else // El actual es el ultimo parametro
			{
				param = expresion.substring(posAnterior + 1, longitud-1);			
				seguir = false;
			}

			// Intentamos leer el valor del formulario.
			for (i=0; i<formulario.elements.length; i++)
			{
				// Solo tratamos las cajas de texto y los textarea
				if ((formulario.elements[i].type=="text") || (formulario.elements[i].type=="textarea") || (formulario.elements[i].type=="password")  || (formulario.elements[i].type=="hidden"))
				{
					// Si es el q buscamos, sustituimos el valor del parametro
					if (param==formulario.elements[i].name)
					{		
						valor = formulario.elements[i].value;
						expSustituida = expSustituida.replace (param, valor);
					}
				}
			}

			posAnterior = pos;
		}
	}

	return expSustituida;
}

/**
 * Sustituye todos los parametros en una expresion, por su valor
 *
 * @param expresion String donde sustituir los parametros (#i)
 * @param parametros Vector donde [i] es el valor del parametro #i
 *
 * @return String con los parametros ya sustituidos por su valor.
 */
function SustituirTodos (expresion, parametros)
{
	for (var n in parametros) 
	{
    	expresion = Sustituir (expresion, n, parametros[n]);
	}
	return expresion;
}

/**
 * Sustituye un parametro en una expresion, por su valor
 *
 * @param expresion String donde sustituir el parametros (#i)
 * @param numParam Entero con el numero de parametro a sustituir (i)
 * @param parametro String con el valor del parametro #i
 *
 * @return String con el parametro ya sustituido por su valor.
 */
function Sustituir (expresion, numParam, parametro)
{
	return (expresion.replace ("#"+numParam, parametro));
}

/**
 * Sustituye los parametros de una expresion (si los tiene) por su valor.
 *
 * @param expresion String con la expresion.
 *
 * @return Una tabla con los siguientes campos:
 *
 *		tabla.expresion Expresion sin sustituir
 *		tabla.parametros Vector donde [i] es el valor del parametro #i
 *		tabla.exp_extendida Expresion con los parametros ya sustituidos		
 */
function SustituirParametros (expresion)
{
	var cabeza="";				// Cabeza de la  expresion (sin paramtros)
    var resto = expresion;		// Resto de expresion (ej: _OBLIGATORIO_FECHA_)
	var resultado;				// Resultado de la sustitucion
    var pos1 = -1;				
	var pos2;	 			  	// Pos de "#"
    var param;               	// Parametro
	var numParam = 1;			// Numero de parametro
	var seguir=true;
	var tabla = {};				// Tabla con resultados

	// Leemos la 'cabeza'
	pos1 = resto.indexOf ("#",0);		// Buscamos el primer "#"
	
	if (pos1 <=0) // No se han encontrado parametros
	{
		tabla.expresion = expresion;
		tabla.parametros = [];
		
		// Si la expresion es _OBLIGATORIO_, no hay que evaluarla
		if (expresion=="_OBLIGATORIO_") tabla.exp_extendida = expresion;
		else tabla.exp_extendida = eval (expresion);
	}
	else
	{
		cabeza = resto.substr (0, pos1) + "_";
		resultado=eval(cabeza);
		// alert (resto + " , " + cabeza + " -> " + resultado);
	
		// Guardamos la expresion			
		tabla.expresion = cabeza;
		tabla.parametros = [];
	
		// Leemos todos los parametros en 'expresion'
		while (seguir)
		{
			pos2 = resto.indexOf("#",pos1 + 1); 	// Buscamos el segundo "#"
		
			if (pos2 == -1 ) // Este es el ultimo parametro
			{
				seguir = false;
				param = resto.substr (pos1 + 1, resto.length - pos1 - 2); // 2 pq hay que quitar el _ del final tb	
			}
			else
			{
				param = resto.substr(pos1 + 1, pos2 - pos1 - 1);
			}
	
			resultado = Sustituir (resultado, numParam, param);
			tabla.parametros[numParam]=param;
			numParam++;		
			
			pos1 = pos2;
		}
	
		// alert (resultado);
	tabla.exp_extendida = resultado;
	}
	
	return tabla;
}

/**
 * Comprueba si un valor cumple un determinado formato
 *
 * @param valor String que contiene el valor a comprobar
 * @param expresion String que define la expresion
 *
 * @return True si 'valor' cumple el formato definido por 'expresion', False si no.
 */
function ComprobarExpresion (valor, expresion)
{
	if (expresion=="_OBLIGATORIO_")
	{
		if (valor=="") return false;
		else return true;
	}
	else if (expresion=="")		// Si expresion es vacia, la tratamos de manera especial
	{
		return true;
	}
	else
	{
		if (valor=="") return true; // "" cumple todos los formatos
		else
		{
			// Añadimos el principio y fin de palabra a 'expresion'
			var exp_completa = "^" + expresion + "$"; 

			// Comprobamos la expresion regular
			var expRegular = new RegExp (exp_completa);
			return (expRegular.test(valor));
		}
	}
}

/**
 * Comprueba si un valor cumple un determinado formato
 *
 * @param nombre String que contiene nombre a mostrar en caso de error
 * @param elemento Elemento del formulario a comprobar
 * @param expresion String que define la expresion
 * @param corregir True si queremos intentar corregir eautomaticamente el error, 
 * 					False si no
 *
 * @return String con el mensaje de error, "" si no ha habido ninguno.
 */
function Comprobar (nombre, elemento, expresion, corregir)
{
	var datos = SustituirParametros (expresion);
	var valor = elemento.value;

	if (!ComprobarExpresion (valor, datos.exp_extendida))
	{
	//alert ("Comprobar: Error " + nombre + " " + valor + " " + datos.exp_extendida);
		switch (datos.expresion)
       	{
			case "_OBLIGATORIO_":

				return (nombre + " es un campo obligatorio.\n");        
				break;

			case "_DIGITO_":
	
				return (nombre + ": " + valor + " no es un digito válido.\n");
                break;

			case "_ENTEROPOS_":

				return (nombre + ": " + valor + " no es un número entero positivo válido.\n");
                break;

			case "_ENTERO_":

				return (nombre + ": " + valor + " no es un número entero válido.\n");
                break;

			case "_REALPOSCOMA_":
			case "_REALPOSPTO_":
			
				return (nombre + ": " + valor + " no es un número real positivo válido.\n");
               	break;

			case "_REALPOSCOMADEC1_":
			case "_REALPOSPTODEC1_":			

				return SustituirTodos (nombre + ": " + valor + " no es un número real positivo de #1 decimales válido.\n", datos.parametros);
                break;

			case "_REALCOMA_":
			case "_REALPTO_":			

				return (nombre + ": " + valor + " no es un número real válido.\n");
                break;

			case "_REALCOMADEC1_":
			case "_REALPTODEC1_":			

				return SustituirTodos (nombre + ": " + valor + " no es un número real de #1 decimales válido.\n", datos.parametros);
                break;

			case "_LETRA_":

				return (nombre + ": " + valor + " no es un una letra válida.\n");
                break;

			case "_CARACTER_":

				return (nombre + ": " + valor + " no es un carácter válido.\n");
               	break;

			case "_FECHA_":
			
				if (corregir) return CorregirFecha(nombre, elemento);
				else return (nombre + ": " + valor + " debe tener el formato dd/mm/aaaa.\n");
                break;

			case "_HORA_":
			
				if (corregir) return CorregirHora(nombre, elemento);
				else return (nombre + ": " + valor + " debe tener el formato hh:mm:ss.\n");
                break;

			case "_EMAIL_":

				return (nombre + ": " + valor + " no es una dirección de correo electronico válida.\n");
				break;

			case "_DNI_":

				return (nombre + ": " + valor + " no es un DNI válido.\n");
				break;

			case "_TELEFONO_":

				return (nombre + ": " + valor + " debe ser un número de 9 cifras.\n");
				break;
		}
	}
	else if (valor!="") // Comprobaciones auxiliares al formato
	{
		// alert ("Comprobar: Ok " + nombre + " " + valor + " " + datos.exp_extendida);	
		switch (datos.expresion)
       	{
			case "_REALPOS_":		// Estos 4 siempre se corrigen, pq si no, no tienen sentido
			case "_REAL_":
			case "_REALPOSDEC1_":
			case "_REALDEC1_":
									
				return CorregirReales(nombre, elemento, datos); 
				break;

			case "_FECHA_":

				return validarFecha (nombre, valor);        
				break;

			case "_HORA_":

				return validarHora (nombre, valor);        
				break;

			case "_DNI_":

				return validarDNI (nombre, valor);
				break;
				
			case "_LONGMIN1_":
			
				return ComprobarLongitudMin (nombre, valor, datos.parametros[1]);
				break;
				
			case "_MAYOR1_": // Corregir?
			
				return Comparar (nombre, valor, ">", datos.parametros[1]);
				break;

			case "_MENOR1_": // Corregir?
			
				return Comparar (nombre, valor, "<", datos.parametros[1]);
				break;
				
			case "_MAYORIGUAL1_": // Corregir?
			
				return Comparar (nombre, valor, ">=", datos.parametros[1]);
				break;

			case "_MENORIGUAL1_": // Corregir?
			
				return Comparar (nombre, valor, "<=", datos.parametros[1]);
				break;
				
			case "_IGUAL1_":
			
				return Comparar (nombre, valor, "==", datos.parametros[1]);
				break;

			case "_DISTINTO1_":
			
				return Comparar (nombre, valor, "!=", datos.parametros[1]);
				break;
				
			case "_MAYORFORM2_": // Corregir?
			
				return CompararCampos (nombre, valor, ">", datos.parametros[2], datos.parametros[1]);
				break;
				
			case "_MENORFORM2_": // Corregir?
			
				return CompararCampos (nombre, valor, "<", datos.parametros[2], datos.parametros[1]);
				break;
				
			case "_MAYORIGUALFORM2_": // Corregir?
			
				return CompararCampos (nombre, valor, ">=", datos.parametros[2], datos.parametros[1]);
				break;
				
			case "_MENORIGUALFORM2_": // Corregir?
			
				return CompararCampos (nombre, valor, "<=", datos.parametros[2], datos.parametros[1]);
				break;
				
			case "_IGUALFORM2_":
			
				return CompararCampos (nombre, valor, "==", datos.parametros[2], datos.parametros[1]);
				break;
				
			case "_DISTINTOFORM2_":
			
				return CompararCampos (nombre, valor, "!=", datos.parametros[2], datos.parametros[1]);
				break;
				
			case "_CUENTAFORM4_":
			
				return ValidarCuenta (datos.parametros[4], valor, datos.parametros[1], datos.parametros[2], datos.parametros[3]);
				break;
			
			case "_TARJETA_":
			
				return ValidarTarjeta (nombre, valor);
				break;
		}
	}

	// Si llega hasta aqui, es q todo ha ido ok
	return ("");
}

/**
 * Comprueba si un valor cumple con los formatos indicados en 'expresion' y devuelve un mensaje.
 *
 * @param nombre Nombre del campo a mostrar en caso de error
 * @param elemento Elemento del formulario a comprobar
 * @param expresion La expresion que debe cumplir el campo.
 * @param corregir True si queremos intentar corregir eautomaticamente el error, 
 * 					False si no
 * 
 * @return True si todos los datos son correctos.
 *		   False si hay algun dato incorrecto. Ademas, muestra un mensaje de error explicativo.
 */

function ComprobarValidez (nombre, elemento, expresion, corregir)
{
    var resto = expresion;  // Resto de expresiones (ej: _OBLIGATORIO_FECHA_)
    var pos;                // Pos del segundo "_"
    var exp;                // Sub-Expresion
    var msg="";             // Mensaje de error

	// Comprobamos que la expresion este bien formada
	if (resto.charAt(resto.length - 1) != '_') resto += "_";

    // Leemos todos las subexpresiones en 'expresion'
    while (resto!="_" && resto!="")
    {
		pos = resto.indexOf("_",1); // Buscamos el segundo "_"
		exp = resto.substr(0,pos+1);

		// alert ("ComprobarValidez: " + nombre + " " + valor + " " + exp);
		msg = msg + Comprobar (nombre, elemento, exp, corregir);
		

		resto = resto.substring (pos);
    }

    return msg;
}

/**
 * Valida los elementos del formulario 'formulario' en funcion del contenido de 'tabla'.
 * 
 * @param formulario El formulario en que se encuentran los datos que queremos comprobar.
 * @param tabla Una tabla con los datos que queremos comprobar, con el formato definido antes.
 *
 * @return True si todos los datos son correctos.
 *		   False si hay algun dato incorrecto. Ademas, muestra un mensaje de error explicativo.
 */
function ValidarFormulario (formulario, tabla)
{
	var i, j, nombre, msg="", expresion;
	var exp_completa = "";
	var expRegular;
	var corregir;

    // Recorremos la tabla y validamos todos sus elementos

	for (j=0; j<tabla.length; j++)
	{
		nombre = tabla[j][0];
		
		for (i=0; i<formulario.elements.length; i++)

		{
			// Solo tratamos las cajas de texto y los textarea
			if ((formulario.elements[i].type=="text") || (formulario.elements[i].type=="textarea") || (formulario.elements[i].type=="password")  || (formulario.elements[i].type=="hidden"))
			{
				// Si es el q buscamos, validamos los datos
				exp_completa = "^" + nombre + "$"; 
				expRegular = new RegExp (exp_completa);

				if (expRegular.test(formulario.elements[i].name))
				{		
					// alert ("Validacion: " + nombre);
					
					// Sustituimos los parametros que vengan del formulario
					expresion = sustituirParamsForm(tabla[j][2], formulario);

					corregir = (tabla[j][3] == true);
					
					// Comprobamos el campo						
					
					msg += ComprobarValidez (tabla[j][1], formulario.elements[i], expresion, corregir);
				}
			} // del for
		} // del if
    } // del for

    if (msg=="") return true;
    else 
    {
		msg = "Se han encontrado los siguientes errores:\n\n" + msg;
		alert (msg); 
        return false;
    }
} // de la funcion

/**
 * Funciones auxiliares utilizadas para la validacion.
 *
 * Estas funciones nos dan mas flexibilidad a la hora de comprobar la validez de los datos
 * Todas las funciones auxiliares utilizadas que sean llamadas desde la funcion "Comprobar"
 *	deben tener un comportamiento similar:
 *
 *		Entrada: nombre	es el nombre del campo a mostrar en caso de error.
 *				 valor es el valor que queremos validar.
 *		
 *		Salida: Un String vacio "" en caso de que la validacion haya sido exitosa.
 *				En caso contrario, un String con un mensaje de error explicativo.
 *
 * Las funciones auxiliares que no son llamadas desde la funcion "Comprobar", y solo sean 
 *	utilizadas dentro de otras funciones auxiliares, no tienen porque cumplir lo anterior.
 */

/**
 * Devuelve el numero de dias de Febrero en un cierto año
 *
 * @param anyo Un entero con el año del que queremos conocer el numero de dias de Febrero.
 *
 * @return Un entero con el numero de dias de Febrero
 */
function DiasFebrero (anyo)
{
    return (((anyo % 4 == 0) && ( (!(anyo % 100 == 0)) || (anyo % 400 == 0))) ? 29 : 28 );
}

/**
 * Devuelve un array con el número de dias de cada mes, en cierto año
 *
 * @param anyo Un entero con el año del que queremos conocer el numero de dias de cada mes.
 *
 * @return Un array con el numero de dias de cada mes {1..12}
 */
function ArrayDias (anyo) 
{
	for (var i = 1; i <= 12; i++) 
	{
		this[i] = 31;
		if (i==4 || i==6 || i==9 || i==11) {this[i] = 30;}
		if (i==2) {this[i] = DiasFebrero(anyo);}
	} 
   	return this;
}

/** 
 * Comprueba si una fecha es valida (no si cumple un formato)
 *
 * @param nombre Un String con el nombre del campo que contiene el dato, 
 *					por si se debe mostrar un mensaje de error.
 * @param fecha Un String que contiene la fecha que queremos comprobar.
 *
 * @return 	Si el la fecha es valida, devuelve un String vacio "".
 *			Si no es una fecha valida, devuelve un String con un mensaje de error.
 */
function validarFecha (nombre, fecha)
{
	// Separamos la fecha en strDia, strMes, strAnyo
	var pos1 = fecha.indexOf(_SEPARADOR_FECHAS);
	var pos2 = fecha.indexOf(_SEPARADOR_FECHAS,pos1+1);
	var strDia = fecha.substring(0,pos1);
	var strMes = fecha.substring(pos1+1,pos2);
	var strAnyo = fecha.substring(pos2+1);
	
	// Convertimos strDia, strMes, strAnyo en enteros
	mes = parseInt(strMes);
	dia = parseInt(strDia);
	anyo = parseInt(strAnyo);
	var diasMes = ArrayDias (anyo);	

	// Comprobamos que el mes sea correcto
	if (mes < 1 || mes > 12)
	{
		return ("El mes en la fecha "+fecha+" no es valido.\n");
	}
	// Comprobamos que el numero de dias en el mes sea correcto
	else if (dia<1 || dia>31 || dia > diasMes[mes])
	{
		return ("El dia en la fecha "+fecha+" no es valido.\n");
	}
	// Comprobamos que el año sea correcto
	else if (strAnyo.length != 4 || anyo==0)
	{
		return ("El año en la fecha "+fecha+" debe tener 4 digitos.\n");
	}
	
	// Si llegamos aqui, todo ha ido OK
	return "";
}

/** 
 * Comprueba si una hora es valida (no si cumple un formato)
 *
 * @param nombre Un String con el nombre del campo que contiene el dato, 
 *					por si se debe mostrar un mensaje de error.
 * @param hora Un String que contiene la hora que queremos comprobar.
 *
 * @return 	Si el la hora es valida, devuelve un String vacio "".
 *			Si no es una hora valida, devuelve un String con un mensaje de error.
 */
function validarHora (nombre, hora)
{
	// Separamos la hora en strHora, strMin, strSeg
	var pos1 = hora.indexOf(_SEPARADOR_HORAS);
	var pos2 = hora.indexOf(_SEPARADOR_HORAS,pos1+1);
	var strHora = hora.substring(0,pos1);
	var strMin = hora.substring(pos1+1,pos2);
	var strSeg = hora.substring(pos2+1);
	
	// Convertimos strDia, strMes, strAnyo en enteros
	lahora = parseInt(strHora);
	minu = parseInt(strMin);
	seg = parseInt(strSeg);

	// Comprobamos que la hora sea correcto
	if (lahora < 0 || lahora > 23)
	{
		return ("La hora "+hora+" en el campo hora no es valido.\n");
	}
	else if (strHora.length != 2)
	{
		return ("La hora "+hora+" en el campo hora debe tener 2 digitos.\n");
	}
	// Comprobamos que el minuto sea correcto
	else if (minu < 0 || minu > 59)
	{
		return ("El minuto "+hora+" en el campo hora no es valido.\n");
	}
	else if (strMin.length != 2)
	{
		return ("El minuto "+hora+" en el campo hora debe tener 2 digitos.\n");
	}
	// Comprobamos que el minuto sea correcto
	else if (seg < 0 || seg > 59)
	{
		return ("El segundo "+hora+" en el campo hora no es valido.\n");
	}
	else if (strSeg.length != 2)
	{
		return ("El segundo "+hora+" en el campo hora debe tener 2 digitos.\n");
	}
	
	// Si llegamos aqui, todo ha ido OK
	return "";
}

/**
 * Comprueba que la letra de un DNI (si la tiene) sea la correcta
 *
 * @param nombre Un String con el nombre del campo que contiene el dato, 
 *					por si se debe mostrar un mensaje de error.
 * @param valor Un String que contiene el DNI que queremos comprobar.
 *					Puede no tener letra, pero si la tiene, debe ser el ultimo caracter.
 *
 * @return 	Si el DNI es valido, devuelve un String vacio "".
 *			Si no es un DNI valido, devuelve un String con un mensaje de error.
 */
function validarDNI (nombre, valor)
{
	var letrasDNI  = 'TRWAGMYFPDXBNJZSQVHLCKE';
	var letrasDNI2 = 'trwagmyfpdxbnjzsqvhlcke';	

	if (valor.length > 8) // Tiene letra
	{
		var indice = parseInt(valor.substring(0,8)) % 23;	
		var letra = valor.charAt(valor.length-1);			// Leemos la letra que viene en "valor"
		var letraOK = letrasDNI.charAt(indice);				// Leemos la letra que deberia ser (Mayuscula)		
		var letraOK2 = letrasDNI2.charAt(indice);			// Leemos la letra que deberia ser (Minuscula) 				
		
		if (letraOK!=letra && letraOK2!=letra) 				// Si la letra es incorrecta, nostramos un msg de error
		{
			return "La letra del DNI en " + nombre + " debería ser " + letraOK + ".\n";
		}
	}
	
	// Si llega hasta aqui, todo OK
	return "";
}

/**
 * Compara dos valores.
 *
 * @param nombre Un String con el nombre del campo que contiene el dato, 
 *					por si se debe mostrar un mensaje de error.
 * @param val1 Un String que contiene el primer valor que queremos comparar.
 * @param signo Un String que contiene el comparador.
 * @param val2 Un String que contiene el segundo valor que queremos comparar.
 *
 * @return 	Si la comparacion es cierta, devuelve un String vacio "".
 *			Si no es cierta, devuelve un String con un mensaje de error.
 */

function Comparar(nombre, val1, signo, val2)
{
	var comparador="";
	var comparacion;	

	// Si vienen numeros, los convertimos a numeros
	if ((!isNaN(val1)) && (!isNaN(val2))) {
		val1 = parseFloat(val1);
		val2 = parseFloat(val2);		
	}

	switch (signo)
	{
		case "<": 
			
			comparador = " menor que ";
			comparacion = (val1 < val2);			
			break;
			
		case ">": 
		
			comparador = " mayor que ";
			comparacion = (val1 > val2);			
			break;

		case "<=": 
		
			comparador = " menor o igual que ";
			comparacion = (val1 <= val2);			
			break;

		case ">=": 
		
			comparador = " mayor o igual que ";
			comparacion = (val1 >= val2);			
			break;
			
		case "!=": 
		
			comparador = " distinto que ";
			comparacion = (val1 != val2);			
			break;

		case "==": 
		
			comparador = " igual que ";
			comparacion = (val1 == val2);			
			break;
	}

	if (comparacion)
	{
		return "";
	}
	else	
	{
		return (nombre + ": " + val1 + " debe ser" + comparador + val2 + ".\n");
	}
}


/**
 * Compara dos valores, de dos campos del formulario
 *
 * @param nombre1 Un String con el nombre del campo que contiene el dato, 
 *					por si se debe mostrar un mensaje de error.
 * @param val1 Un String que contiene el primer valor que queremos comparar.
 * @param signo Un String que contiene el comparador.
 * @param nombre1 Un String con el nombre del segundo campo que contiene el dato, 
 *					por si se debe mostrar un mensaje de error.
 * @param val2 Un String que contiene el segundo valor que queremos comparar.
 *
 * @return 	Si la comparacion es cierta, devuelve un String vacio "".
 *			Si no es cierta, devuelve un String con un mensaje de error.
 */
function CompararCampos  (nombre1, val1, signo, nombre2, val2)
{
	var comparador="";
	var comparacion;

	// Si vienen numeros, los convertimos a numeros
	if ((!isNaN(val1)) && (!isNaN(val2))) {
		val1 = parseFloat(val1);
		val2 = parseFloat(val2);		
	}

	switch (signo)
	{
		case "<": 
			
			comparador = " menor que ";
			comparacion = (val1 < val2);
			break;
			
		case ">": 
		
			comparador = " mayor que ";
			comparacion = (val1 > val2);			
			break;

		case "<=": 
		
			comparador = " menor o igual que ";
			comparacion = (val1 <= val2);			
			break;

		case ">=": 
		
			comparador = " mayor o igual que ";
			comparacion = (val1 >= val2);			
			break;
			
		case "!=": 
		
			comparador = " distinto que ";
			comparacion = (val1 != val2);			
			break;

		case "==": 
		
			comparador = " igual que ";
			comparacion = (val1 == val2);			
			break;
	}

	if (comparacion)
	{
		return "";
	}
	else	
	{
		return (nombre1 + " debe ser" + comparador + nombre2 + ".\n");
	}
}

/**
 * Obtiene un digito de control
 *
 * @param valor Un string formado por 10 cifras
 *
 * @return Un string con una cifra, q es el digito de control bancario de la entrada
 */
function ObtenerDigitoControl (valor)
{
	var valores = new Array(1, 2, 4, 8, 5, 10, 9, 7, 3, 6);
	var control = 0;
	
	for (i=0; i<=9; i++)
    	control += parseInt(valor.charAt(i)) * parseInt(valores[i]);
	
	control = 11 - (control % 11);
	
	if (control == 11) control = 0;
  	else if (control == 10) control = 1;
	
	return control.toString();
}

/**
 * Valida un numero de cuenta
 *
 * @param nombreTodos Un string con el nombre del campo.
 * @param banco Las 4 cifras que corresponden al banco.
 * @param sucursal Las 4 cifras que corresponden a la sucursal.
 * @param control Las 2 cifras que corresponden a los digitos de control.
 * @param cuenta Las 10 cifras que corresponden a la cuenta.
 *
 * @return 	Si el numero de cuenta es valido, devuelve un String vacio "".
 *			Si no es valido, devuelve un String con un mensaje de error.
 */
function ValidarCuenta (nombreTodos, banco, sucursal, control, cuenta)
{
	if (banco == "" && sucursal == "" && control == "" && cuenta == "") return "";
	else if (isNaN(banco) || isNaN(sucursal) || isNaN(control) || isNaN(cuenta)) return (nombreTodos + " no es un número de cuenta válido.\n");
	else if (banco.length != 4) return (nombreTodos + ": el código de banco debe ser de 4 cifras.\n");
	else if (sucursal.length != 4) return (nombreTodos + ": el código de sucursal debe ser de 4 cifras.\n");
	else if (control.length != 2) return (nombreTodos + ": el código de control debe ser de 2 cifras.\n");		
	else if (cuenta.length != 10) return (nombreTodos + ": el número de cuenta debe ser de 10 cifras.\n");
	else
	{
		if ((ObtenerDigitoControl("00" + banco + sucursal) != control.toString().charAt(0)) ||
			(ObtenerDigitoControl(cuenta) != control.toString().charAt(1)))
		{
			return (nombreTodos + " no es un numero de cuenta valido.\n");
		}
		else return ("");
	}
}

/**
 * Valida un numero de tarjeta de credito
 *
 * @param nombre Un string con el nombre del campo.
 * @param valor El valor de ese campo
 *
 * @return 	Si el numero de tarjeta es valido, devuelve un String vacio "".
 *			Si no es valido, devuelve un String con un mensaje de error.
 */
function ValidarTarjeta (nombre, valor)
{
	var digito;
	var resultado = 0;
	var numero = valor;
	
	// Comprobamos que si tiene algun valor, sea un numero de 16 cifras
	if (valor == "") return "";
	else if (isNaN(valor)) return (nombre + ": " + valor + " no es un número de tarjeta de crédito válido.\n");
	else if (valor.length != 16) return (nombre + ": " + valor + " debe tener 16 cifras.\n");
	else
	{
		for (i = 0; i < 16; i++)	
		{
			digito = numero % 10;
	
			if ((i % 2) == 0)	// Par
			{
				resultado += digito;
			}
			else
			{
				digito = 2 * digito;
				
				if (digito <10)	resultado += digito;
				else resultado += ((digito % 10) + (parseInt(digito / 10)));
			}
	
			numero = parseInt(numero / 10);
		}
		
		if (resultado % 10 == 0 && resultado > 0) return "";
		else return (nombre + ": " + valor + " no es un número de tarjeta de crédito válido.\n");
	}
}

/**
 * Comprueba que un campo tenga una logintud minima
 *
 * @param nombre Un string con el nombre del campo.
 * @param valor El valor de ese campo
 * @param longMin La longitud minima que tiene q tener 'valor'
 *
 * @return 	Si el campo es lo suficientemente largo, devuelve un String vacio "".
 *			Si no, devuelve un String con un mensaje de error.
 */

function ComprobarLongitudMin (nombre, valor, longMin)
{
	if (valor.length < parseInt(longMin)) return (nombre + " debe tener una longitud mayor o igual a " + longMin + ".\n");
	return "";
}

/**
 * Intenta corregir una fecha.
 *
 * 		Sustituye los '-' por '/'
 *		Si dia o mes es de una cifra, pone un '0' delante
 *		Si año es de 2 cifras y es posterior a este en mas de 10 años, pone '19' delante 
 *		Si no, pone '20' delante 
 *
 * @param nombre Un string con el nombre del campo.
 * @param elemento Elemento del formulario a corregir
 *
 * @return 	Si se puede corregir, lo hace, y devuelve un String vacio "".
 *			Si no, devuelve un String con un mensaje de error.
 */
function CorregirFecha(nombre, elemento) {

	var valor = elemento.value;
	var pos1;
	var pos2;
	var strDia;
	var strMes;
	var strAnyo;
	var msg = "";
	var diasMes;
	
	// Cambiamos los guiones a barras, si los hay
	correccion = valor.replace ("-", _SEPARADOR_FECHAS);	
	correccion = correccion.replace ("-", _SEPARADOR_FECHAS);	

	// Corregimos que solo aparezcan 1 digito en dia y mes, y 2 en año
	if (ComprobarExpresion(correccion, "(" + _DIGITO_ + "{1,2}" + _SEPARADOR_FECHAS + "){1,2}" + "(" + _DIGITO_ + "{2}" + "|" + _DIGITO_ + "{4}" + ")")) {
	
		pos1 = correccion.indexOf(_SEPARADOR_FECHAS);
		pos2 = correccion.indexOf(_SEPARADOR_FECHAS,pos1+1);
		strDia = correccion.substring(0,pos1);
		strMes = correccion.substring(pos1+1,pos2);
		strAnyo = correccion.substring(pos2+1);	
		
		if (strDia.length==1) strDia = "0" + strDia;
		if (strMes.length==1) strMes = "0" + strMes;
		if (strAnyo.length==2) {
			// Si la fecha es de dentro de mas de 10 años, es del siglo pasado
			var fecha = new Date();
			
			if (fecha.getYear() + 10 < parseInt("20" + strAnyo)) strAnyo = "19" + strAnyo;
			else strAnyo = "20" + strAnyo;
		}

		correccion = strDia + _SEPARADOR_FECHAS + strMes + _SEPARADOR_FECHAS + strAnyo;

		// Validamos la nueva fecha, y si esta bien, corregimos el dato
		diasMes = ArrayDias (parseInt(strAnyo));	
		
		// Comprobamos que el mes sea correcto
		if (parseInt(strMes) < 1 || parseInt(strMes) > 12)
		{
			msg = "El mes en la fecha " + valor + " no es valido.\n";
		}
		// Comprobamos que el numero de dias en el mes sea correcto
		else if (parseInt(strDia)<1 || parseInt(strDia)>31 || parseInt(strDia) > diasMes[parseInt(strMes)])
		{
			msg = "El dia en la fecha " + valor + " no es valido.\n";
		}
		// Comprobamos que el año sea correcto
		else if (strAnyo.length != 4 || parseInt(strAnyo)==0)
		{
			msg = "El año en la fecha " + valor + " debe tener 4 digitos.\n";
		}

		if (msg == "") elemento.value = correccion;
		return msg;	
	}
	return (nombre + ": " + valor + " debe tener el formato dd/mm/aaaa.\n");
}

/**
 * Intenta corregir una hora.
 *
 * 		Sustituye los '-' por ':'
 *		Si hora o minuto o segundo es de una cifra, pone un '0' delante
 *
 * @param nombre Un string con el nombre del campo.
 * @param elemento Elemento del formulario a corregir
 *
 * @return 	Si se puede corregir, lo hace, y devuelve un String vacio "".
 *			Si no, devuelve un String con un mensaje de error.
 */
function CorregirHora(nombre, elemento) {

	var valor = elemento.value;
	var pos1;
	var pos2;
	var strSeg;
	var strMin;
	var strHora;
	var msg = "";
	
	// Cambiamos los guiones a puntos, si los hay
	correccion = valor.replace ("-", _SEPARADOR_HORAS);	
	correccion = correccion.replace ("-", _SEPARADOR_HORAS);	

	// Corregimos que solo aparezcan 1 digito en dia y mes, y 2 en año
	if (ComprobarExpresion(correccion, "(" + _DIGITO_ + "{1,2}" + _SEPARADOR_HORAS + "){1,2}" + "(" + _DIGITO_ + "{1,2}" + ")")) {
	
		pos1 = correccion.indexOf(_SEPARADOR_HORAS);
		pos2 = correccion.indexOf(_SEPARADOR_HORAS,pos1+1);
		strHora = correccion.substring(0,pos1);
		strMin = correccion.substring(pos1+1,pos2);
		strSeg = correccion.substring(pos2+1);	
		
		if (strSeg.length==1) strSeg = "0" + strSeg;
		if (strMin.length==1) strMin = "0" + strMin;
		if (strHora.length==1) strHora = "0" + strHora;

		correccion = strHora + _SEPARADOR_HORAS + strMin + _SEPARADOR_HORAS + strSeg;

		// Comprobamos que la hora sea correcto
		if (parseInt(strHora) < 0 || parseInt(strHora) > 23)
		{
			msg = "La hora " + valor + " en el campo hora no es valido.\n";
		}
		// Comprobamos que el minuto sea correcto
		else if (parseInt(strMin) < 0 || parseInt(strMin) > 59)
		{
			msg = "El minuto " + valor + " en el campo hora no es valido.\n";
		}
		// Comprobamos que el año sea correcto
		else if (parseInt(strSeg) < 0 || parseInt(strSeg) > 59)
		{
			msg = "El segundo " + valor + " en el campo hora no es valido.\n";
		}

		if (msg == "") elemento.value = correccion;
		return msg;	
	}
	return (nombre + ": " + valor + " debe tener el formato hh:mm:ss.\n");
}

/**
 * Intenta corregir un numero real.
 *
 * 		Sustituye las ',' por '.'
 *		Si tiene demasiados decimales, lo corta
 *
 * @param nombre Un string con el nombre del campo.
 * @param elemento Elemento del formulario a corregir
 * @param datos El resultado de la funcion SustituirParametros (expresion);
 *
 * @return 	Si se puede corregir, lo hace, y devuelve un String vacio "".
 *			Si no, devuelve un String con un mensaje de error.
 */
function CorregirReales(nombre, elemento, datos) {

	var valor = elemento.value;
	var correccion;
	var posPunto;
	var dat;
	var msg;
	var cortarDecimales = false;
	
	// Cambiamos las comas a puntos, si las hay
	correccion = valor.replace (",", ".");
	
	switch (datos.expresion) {
	
			case "_REALPOS_":
			
				dat = SustituirParametros ("_REALPOSPTO_");
				msg = nombre + ": " + valor + " no es un número real positivo válido.\n";
				break;
			
			case "_REAL_":
	
				dat = SustituirParametros ("_REALPTO_");
				msg = nombre + ": " + valor + " no es un número real válido.\n";				
				break;
			
			case "_REALPOSDEC1_":
			
				dat = SustituirParametros ("_REALPOSPTODEC1#" + datos.parametros[1] + "_");
				msg = nombre + ": " + valor + " no es un número real positivo de " + datos.parametros[1] + " decimales válido.\n";				
				cortarDecimales = true;
				break;
			
			case "_REALDEC1_":						

				dat = SustituirParametros ("_REALPTODEC1#" + datos.parametros[1] + "_");
				msg = nombre + ": " + valor + " no es un número real de " + datos.parametros[1] + " decimales válido.\n";				
				cortarDecimales = true;
				break;
	}
	
	if (cortarDecimales) {
	
		// Comprobamos el número de decimales
		posPunto = correccion.indexOf (".",0);
	
		if (posPunto >= 0) { // Tiene decimales
				correccion = correccion.substr (0, posPunto + 1);			
				for (var i=1; i<=datos.parametros[1]; i++)
					if ((posPunto + i) < valor.length) 
							correccion = correccion + valor.charAt(posPunto + i);
		}
	}
	
	// Si lo cumple con puntos, cambiamos el dato en el formulario
	if (ComprobarExpresion(correccion, dat.exp_extendida)) {
		elemento.value = correccion;
		return "";
	} else return msg;
}