/*
 * Validador Generico de formularios 
 *
 * Version: 1.00
 * Autor: Alexander Concha Abarca <alex@buayacorp.com>
 * URI: http://www.buayacorp.com/
 * 
 * Si deseas utilizar este script puedes hacerlo con la condicin de que permanezcan los crditos.
 * Cualquier cambio y/o modificacion por favor hazmelo saber, para hacer las mejoras respectivas...
 *
 * Este script est bajo licencia de Creative Commons 
 * http://creativecommons.org/licenses/by/2.0/
 *
 * TODO: Incrementar mas elementos para validar, actualmente soporta:
 * input 'text'
 *	textarea
 *	input 'password'
 *	select 'select-one'
 *	select 'select-multiple'
 */

/* 
	Funciones Personalizadas, deberian ir en otro script para mantener un orden
	Deben devolver verdadero o falso.
*/
isNumber = function (n) {	
	return isNaN(n) ? false : true;
}
checkEmail = function (s) {
	return s.search(/^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/gi) ? false : true;
}
/******************************************************************************************/

// Funciones comunes
getElement = function (id) {
	return document.getElementById(id);
}
getEvent = function (evt) {
	return (!evt) ? event : evt;
}
getEventSource = function (evt) {
	return evt.srcElement ?  evt.srcElement : evt.target;
}
/* Basado en el script: http://www.scottandrew.com/weblog/articles/cbs-events
	TODO: Arreglar esta funcion, omitido por problemas con el evento keypress
	y navegadores como Firefox, Opera (Sus comentarios/soluciones seran agradecidos) 
	Se "soluciona" con event.preventDefault();
*/
addEvent = function (obj, evType, fn){
	if (typeof obj == 'string') obj = this.getElement(obj);
	if (!obj) return;
	obj['on' + evType] = fn;
	/*
	if (obj.addEventListener){
		obj.addEventListener(evType, fn, true); 
	   return true;
 	} 
	else if (obj.attachEvent){ 
	   var r = obj.attachEvent("on"+evType, fn); 
	   return r; 
	} 
	else { 
  		return false; 
	}*/
}
// Almacena y crea las instancias de los validadores
var instances = []
function getInstance(frmid) {
	instances [frmid] = new Validator();
	instances[frmid].form = frmid;
	return instances [frmid];	
}
// Funcion basada en el trabajo de JavierB: http://www.forosdelweb.com [Foro Javascript]
testPattern = function (evt, pattern) {
	evt = getEvent (evt);
	tecla = (document.all) ? evt.keyCode : evt.which;
	if (tecla==8) return true;
	if (!pattern) pattern = '\\d';
	pattern = new RegExp(pattern);
	return pattern.test(String.fromCharCode(tecla)); 
}

Validator = function () {}
/* 
	TODO: Refinar los atributos
*/	
//	Define como se van a mostrar los errores encontrados	
Validator.prototype.showSummary = false;
Validator.prototype.summaryId = 'summary'; 
Validator.prototype.summaryDiv;
Validator.prototype.showMessageBox = false;
Validator.prototype.summaryText;
Validator.prototype.title = 'Review following errors';
// Vlido para la depuracin
Validator.prototype.DEBUG = false;
Validator.prototype.debugId = 'debug';
Validator.prototype.debugField;
// Se utiliza si no se trabaja con CSS
Validator.prototype.bgcolor = '#ff0000';
Validator.prototype.color = '#ffffff';
// Permite trabajar con clases CSS
Validator.prototype.useCss = true;
Validator.prototype.cssClass = 'required';
Validator.prototype.classNames = [];
// Los controles que necesitan validacin
Validator.prototype.controls = [];
Validator.prototype.patterns = [];
Validator.prototype.form;
Validator.prototype.functions = [];
// Almacena las funciones personalizadas para la validacin

// Aade un mensaje de depuracion
Validator.prototype.addDebugMsg = function (msg) {
	try {
		if (this.DEBUG && this.debugField) {
			this.debugField.value += (msg + '\n\n');
		}
	}
	catch (ex) {
		alert('this.addDebugMsg -> Fall la depuracin. \n' + ex)
	}
}
// Aade un mensaje de error
Validator.prototype.addErrorMsg = function (id, msg) {
	try {
		if (!id || !msg) return;
		if (this.showMessageBox) this.summaryText += ('  ' + msg + '\n');
		else this.summaryText += ('<li><a href="#' + id + '" onclick="document.getElementById(\'' + id + '\').focus()">' + msg + '</a></li>\n');
	}
	catch (ex) {
		this.addDebugMsg('this.addErrorMsg -> No se puede aadir el mensaje de error: ' + id + '\n' + ex);
	}
}
/* 
	Aade un evento a un elemento usando una expresion regular
	TODO: Hacer que esta funcion se pueda inicializar antes de la declaracion del elemento
*/
Validator.prototype.addRegExpEvent = function (id, evType, pattern){
	try {
		if (!id || !evType || !pattern) return;
		control = getElement(id);
		if (!control) return;
		this.patterns [id] = pattern;
		var tempInst = instances [this.form];
		fn = function (evt){
			instance = eval(tempInst);
			pattern = instance.patterns[ getEventSource(getEvent(evt)).id ];			
			return testPattern(evt, pattern);
		}
		addEvent(control, evType, fn);
	}
	catch (ex) {
		this.addDebugMsg('this.addRegExpEvent -> No se pudo asignar el evento onkeypress al control: ' + id + '\n' + ex);
	}
}
// Aade funciones personalizadas
Validator.prototype.addCustomFunction = function (id, fn) {
	if (!fn) return;
	this.functions[id] = fn;
}
// Ejecuta la funcion
Validator.prototype.evalCustomFunction = function (ctrl) {
	try {
		if (typeof ctrl == 'string') ctrl = this.getElement(ctrl);
		if (!ctrl) return;
		fn = this.functions[ctrl.id];
		if (!fn) return true;
		// TODO: Encontrar una forma mejor de evaluar las funciones
		return eval(fn+'(\'' + ctrl.value + '\')');
	}
	catch (ex) {
		this.addDebugMsg('this.evalCustomFunction -> No se pudo ejecutar la funcin personalizada para el control: ' + ctrl.id + '\n' + ex);
		return true;
	}
}
// Determina si un elemento es valido o no
Validator.prototype.isValid = function (ctrl, default_val){
	try {
		if (!ctrl) return;
		if (default_val == null) default_val = '';
		if (typeof ctrl == 'string') ctrl = getElement(ctrl);	// Tratar de obtener el elemento

		// Validar si el atributo title no es vacio
		if	(ctrl.title != '' && 
			(ctrl.value == default_val || !this.evalCustomFunction(ctrl))) {
			if (!this.useCss || this.cssClass == '') {
				ctrl.style.backgroundColor = this.bgcolor;
				ctrl.style.color = this.color;
			}
			else {
				if ( ctrl.className != '' )	this.classNames [ ctrl.id ] =  this.ctrl.className ;
				ctrl.className = this.cssClass;
			}			
			if(!this.showSummary){
				alert(ctrl.title);
				ctrl.focus();
			}
			else{
				this.addErrorMsg(ctrl.id, ctrl.title);
			}
			return false;
		}	
		return true;
	}
	catch (ex) {
		this.addDebugMsg('this.isValid -> No se pudo validar el control: ' + ctrl.id + '\n' + ex);
	}
}
// Inicializa el validador
Validator.prototype.init = function (frm) {		
	try {
		if (typeof frm == 'string') frm=getElement(frm);
		if (!frm) return;
		this.form = frm;

		if (this.DEBUG) {
			this.debugField = getElement(this.debugId);
			if (!this.debugField) {
				alert('Debe agregar un "textarea" con id "' + this.debugId + '" para habilitar la depuracion.')
				this.DEBUG = false;
			}
		}
		if (this.showSummary && !this.showMessageBox) {
			this.summaryDiv = getElement(this.summaryId);
			if (!this.summaryDiv) {
				alert('Debe agregar un "div" con id "' + this.summaryId + '" para poder mostrar los mensajes.')
				this.showSummary = false;
			}
		}
		for(i=0; i < frm.elements.length; i++){
			if (frm.elements[i].title != ''){
				switch( frm.elements[i].type ){
					case 'text':
					case 'textarea':
					case 'password':
					case 'select-one':
					case 'select-multiple':
						if ( frm.elements[i].id == '' )	frm.elements[i].id = frm.elements[i].name;
						this.controls[frm.elements[i].id] = frm.elements[i];
						var tempInst_ = instances [frm.id];
						fn = function (evt) {
							instance = eval(tempInst_);
							ctrl = getEventSource(getEvent(evt));
							if ( instance.isValid (ctrl.id) ) {				
								if (!instance.useCss) {
									ctrl.style.backgroundColor = '';
									ctrl.style.color = '';
								}
								else {
									ctrl.className = ! instance.classNames [ ctrl.id ] || instance.classNames [ ctrl.id ] == '' ? '' : instance.classNames [ ctrl.id ] ;
								}
							}
						}
						addEvent(frm.elements[i], 'blur', fn);
						break;
				}
			}
		}	
	}
	catch (ex) {
		this.addDebugMsg ('prepare -> No se pudo recuperar los elementos para la validaci&oacute;n: ' + frm.id + '\n' + ex);
		return true;
	}
}
// Limpia los valores de anteriores validaciones
Validator.prototype.clearValidation = function () {
	this.controls = new Array ();
	this.custom = new Array ();
}
// Valida el formulario, invocado de preferencia en el evento submit
Validator.prototype.validate = function (frm) {
	if (typeof frm == 'string') frm = getElement(frm);
	if (!frm || typeof frm.elements == 'undefined') return;
	
	this.summaryText = '';
	
	if (!this.form || this.form.id != frm.id) {			
		this.clearValidation();
		this.init(frm);
	}

	var valid = true;
	var tmp;
	for (i in this.controls) {		
		tmp = this.isValid(this.controls[i]);
		if (!this.showSummary && !tmp) return false;
		valid &= tmp;
	}
	if (this.summaryText != null){
		if (this.showMessageBox){
			alert(this.title + '\n' + this.summaryText)
		}
		else{
			this.summaryDiv.innerHTML = '<h3>' + this.title + '</h3>\n<ul>\n' + this.summaryText + '</ul>';
		}
	}
	return valid == 0 ? false : true;
}