Validation = Class.create({
	initialize:function(options){
		var _this = this;
		this.options = options;

		//establecer opciones JSON x defecto:
		this.stopOnFirstError = (options.stopOnFirstError)?options.stopOnFirstError:true;
		this.showRequiredMark = (options.showRequiredMark)?options.showRequiredMark:true;
		this.requiredMarkTitle= (options.requiredMarkTitle)?options.requiredMarkTitle:"";
		this.requiredMark     = (options.requiredMark)?options.requiredMark:"*";
		this.validateAllOnBlur= (options.validateAllOnBlur)?options.validateAllOnBlur:false;
		this.loadingIcon      = (options.loadingIcon)?options.loadingIcon:"";
		this.submitButtonId   = (options.submitButtonId)?options.submitButtonId:"";

		this.thereWasAnError 	= false; //flag. p/indicar si hubo al menos 1 error:
		this.validateErr		= {}; //lista de pares id:true/false indicando si hay error en ese id (p/no repetir el error en caso de usar + de 1 clase de validacion)
		this.validateMsgError = {}; //lista de pares id:mensaje de error
		this.validatePrompt   = {}; //lista de pares id:prompt
		this.validateAllowedChars    = {}; //lista de pares id:caracteres permitidos
		this.validateMaxChars = {}; //lista de pares id:max. chars
		this.validateMinChars = {}; //lista de pares id:min. chars
		this.validateOneOf    = {}; //lista de pares id:array de valores
		this.validateEqualToField = {}; //lista de pares id:nombre campo
		this.validateNotEqualToField = {}; //lista de pares id:nombre campo
		this.validateMaxValue = {}; //lista de pares id:valor maximo
		this.validateMinValue = {}; //lista de pares id:valor minimo
		this.validateFunc     = {}; //lista de pares id:funcion usuario
		this.validateAjaxFunc = {}; //lista de pares id:funcion usuario via AJAX
		this.loadingIconClass = ".loadingIcon";
		this.validClasses={	valid_error   		:'.valid_error', // clase para dar estilo a mensajes de error
							valid_prompt   		:'.valid_prompt', // clase para dar estilo a mensajes de prompt
							valid_required_mark	:'.valid_required_mark', //clase para dar estilo a las marcas de los campos obligatorios (no vacios)
							valid_add     		:'.valid_add', 	//clase p/localizar campos agregados con add()
							valid_email   		:'.valid_email', //clase p/validar emails
							valid_numeric 		:'.valid_numeric',//clase p/validar numeros
							valid_integer 		:'.valid_integer', //clase p/validar enteros
							valid_username		:'.valid_username',//clase p/validar usuarios
							valid_required		:'.valid_required', //clase p/validar no vacios
							valid_date    		:'.valid_date', 		//clase p/validar fechas
							valid_radio   		:'.valid_radio',		//clase p/validar radios
							valid_url     		:'.valid_url'};		//clase p/validar urls

		//guardar en la cadena str_validClasses el json validClasses{}:
		var str_validClasses = "";
		$H(this.validClasses).each(function(pair){
			str_validClasses += pair.value+",";
		});
		str_validClasses = str_validClasses.substr(0,str_validClasses.length-1);
		this.str_validClasses = str_validClasses;

		//inicializar el array de errores en false:
		this.clearErrors();

		//si existen campos con clase "valid_required" y options.showRequiredMark=true
		if(this.options.showRequiredMark){
			this.options.requiredMarkTitle = options.requiredMarkTitle || "";
			this.options.requiredMark = options.requiredMark || "*";
			$$(this.validClasses.valid_required).each(function(elem){
				var mark = new Element("span", {className:_this.validClasses.valid_required_mark.substr(1), title:_this.options.requiredMarkTitle});
				mark.insert(_this.options.requiredMark);
				elem.insert({after: mark});
			});
		}//if(this.options.showRequiredMark)


		if(this.options.validateAllOnBlur){
			options.stopOnFirstError=true; //forzar a true
			//recorrer todas las clases y ver las opciones:
			var count_elem=0;
			$$(this.str_validClasses).each(function(elem){
				count_elem++;
				if(!elem.id && elem.className!=_this.validClasses.valid_required_mark.substr(1)){	
					alert("Error, elemento #"+count_elem+" "+elem+" sin id")
					return;
				}
				elem.stopObserving("blur");
				elem.observe("blur", _this.validateForm.bind(_this));
			});
		}//if(options.validateAllOnBlur)


		$$(this.str_validClasses).each(function(elem){
			elem.stopObserving("focus");
			elem.observe("focus", _this.createPromptBox.bind(_this, elem));
		});
	}, //initialize



	clearErrors:function(){
		var _this = this;
		this.thereWasAnError 	= false; //flag. p/indicar si hubo al menos 1 error:
		//si existen cajas de error, ocultarlas:
		//esto permite poner mas de una clase de validacion por elemento
		$$(this.validClasses.valid_error).each(function(elem){elem.hide();});

		//inicializar el array de errores en false:
		$$(this.str_validClasses).each(function(elem){
			if(elem.id) _this.validateErr[elem.id]=false;
		});

		//Si existen iconos de clase 'loadingIconClass' (solo para Ajax), ocultarlos:
		$$(this.loadingIconClass).each(function(img){img.hide();});
	}, //clearErrors



	validateForm:function(){
		//este metodo retorna true/false si el conjunto de elementos (form) es valido o no
		var _this = this;
		this.clearErrors();
		//recorrer los elementos a validar en el mismo orden en que aparecen en la pagina:
		$$("input,select,textarea").each(function(elem){
			var arr_classes = elem.classNames().toArray(); //todas las clases del elemento en un array
			//buscar las clases que esten dentro de 'this.str_validClasses' y validarlas:
			for(var i=0; i<arr_classes.length; i++){
				var classFound = (_this.str_validClasses.indexOf(arr_classes[i])>=0);
				if(classFound){ //se encontro una clase de validacion, validar elemento:
					_this.validateElemByClass(elem, "."+arr_classes[i]); //validar x clase
					if(arr_classes[i]==_this.validClasses.valid_add.substr(1)){
						_this.validateElemByAdd(elem); //validar x metodo add()
					}
				}
			}//for
		});//$$("input","select","textarea").each(function(elem)

		return !this.thereWasAnError;
	},//validateForm



	validateElemByClass : function(elem, classToValidate){
		_this = this;
		$(_this.submitButtonId).disabled=true;
		if(classToValidate==_this.validClasses.valid_email){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, email incorrecto";
			if(!_this.emailOk(elem.value))
				_this.createErrorBox(elem, msg_error);
		};
		if(classToValidate==_this.validClasses.valid_numeric){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, campo no numerico";
			if(isNaN(elem.value))
				_this.createErrorBox(elem, msg_error);
		};
		if(classToValidate==_this.validClasses.valid_integer){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, campo no numericamente entero";
			if(!_this.integerOk(elem.value))
				_this.createErrorBox(elem, msg_error);
		};
		if(classToValidate==_this.validClasses.valid_username){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, nombre de usuario incorrecto";
			if(!_this.usernameOk(elem.value))
				_this.createErrorBox(elem, msg_error);
		};
		if(classToValidate==_this.validClasses.valid_required){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, campo vacio";
			if(["text","password","textarea"].indexOf(elem.type)>=0 && $(elem).value.length==0)
				_this.createErrorBox(elem, msg_error);
			if(["select-one"].indexOf(elem.type)>=0 && !_this.selectionOk(elem))
				_this.createErrorBox(elem, msg_error);
		};
		if(classToValidate==_this.validClasses.valid_date){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, fecha incorrecta";
			if(!_this.dateOk(elem.value))
				_this.createErrorBox(elem, msg_error);
		};
		if(classToValidate==_this.validClasses.valid_url){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, direccion incorrecta";
			if(!_this.urlOk(elem.value))
				_this.createErrorBox(elem, msg_error);
		};

		var radios={};
		if(classToValidate==_this.validClasses.valid_radio){
			if(!elem.name){
				alert("Error, elemento radio sin atributo 'name'");
				return;
			}
			var radio_checked=false;
			$($(elem).form).getInputs('radio', elem.name).each(function(rb) { //el $($()) es para el IE
				if(rb.checked)
					radio_checked=true;
			})
			if(!radio_checked)
				radios[elem.name] = elem;
		};

		$H(radios).each(function(pair){
			var msg_error = _this.validateMsgError[pair.value.id]? _this.validateMsgError[pair.value.id]:"Error, debe elegir una opcion";
			_this.createErrorBox(pair.value, msg_error);
		});
		$(_this.submitButtonId).disabled=false;
	},//validateElemByClass



	validateElemByAdd : function(elem){
		_this = this;
		//elem posee allowedChars?
		if(_this.validateAllowedChars[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, caracter invalido";
			var err_found=false; var i=0;
			while (!err_found && i<$(elem).value.length){
				var test_char = $(elem).value.substr(i,1);
				if(_this.validateAllowedChars[elem.id].indexOf(test_char)<0)
					err_found=true;
				else
					i++;
			}//while
			if(err_found){
				_this.createErrorBox(elem, msg_error);
			}
		}		
		//elem posee maxChars?
		if(_this.validateMaxChars[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, no mas de "+_this.validateMinChars[elem.id]+" caracteres";
			if(_this.validateMaxChars[elem.id] && $(elem).value.length>_this.validateMaxChars[elem.id]){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee minChars?
		if(_this.validateMinChars[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, no menos de "+_this.validateMinChars[elem.id]+" caracteres";
			if(_this.validateMinChars[elem.id] && $(elem).value.length<_this.validateMinChars[elem.id]){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee oneOf?
		if(_this.validateOneOf[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, valor invalido";
			if(_this.validateOneOf[elem.id].toString().indexOf(elem.value)<0){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee equalToField?
		if(_this.validateEqualToField[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, debe ser igual a '"+$(_this.validateEqualToField[elem.id]).value+"'";
			if($(_this.validateEqualToField[elem.id]).value!=elem.value){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee notEqualToField?
		if(_this.validateNotEqualToField[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, no puede ser igual a '"+$(_this.validateNotEqualToField[elem.id]).value+"'";
			if($(_this.validateNotEqualToField[elem.id]).value==elem.value){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee minValue?
		if(_this.validateMinValue[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, el valor no puede ser menor a '"+_this.validateMinValue[elem.id]+"'";
			if(parseFloat(elem.value) < parseFloat(_this.validateMinValue[elem.id])){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee maxValue?
		if(_this.validateMaxValue[elem.id]){
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error, el valor no puede ser mayor a '"+_this.validateMaxValue[elem.id]+"'";
			if(parseFloat(elem.value) > parseFloat(_this.validateMaxValue[elem.id])){
				_this.createErrorBox(elem, msg_error);
			}
		}
		//elem posee validFunc y no hay error previo?
		if(_this.validateFunc[elem.id] && !this.thereWasAnError){
			this.thereWasAnError = !_this.validateFunc[elem.id](elem);
			if(this.thereWasAnError){
				var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error!";
				this.thereWasAnError = false; //poner en false para que se pueda ejecutar createErrorBox()
				_this.createErrorBox(elem, msg_error);
			}
		}

		//elem posee validAjaxFunc?
		if(_this.validateAjaxFunc[elem.id] && !this.thereWasAnError){
			this.thereWasAnError = true;
			$(_this.submitButtonId).disabled=true;
			//hacer visible el icono de loading:
			$(elem).next().show();
			var msg_error = _this.validateMsgError[elem.id]? _this.validateMsgError[elem.id]:"Error!";
			var ajaxOps = _this.validateAjaxFunc[elem.id];
			if(!ajaxOps.postBody)
				ajaxOps.postBody = "";
			new Ajax.Request(ajaxOps.urlserver,{method:'post', asynchronous: false,
 										postBody: ajaxOps.postBody+"&value="+$(elem).value,
										onSuccess: function(transport){
													_this.clearErrors();
													if(parseInt(transport.responseText)==0){
														this.thereWasAnError = true;
														_this.createErrorBox(elem, msg_error);
													}
													$(_this.submitButtonId).disabled=false;
												}
										});
		}
	},//validateElemByAdd


	createErrorBox:function(elem, msg_error){
	//si elem ya tiene un error, salir no mostrar un 2do. error
	if(this.validateErr[elem.id]) return;
	//si ya hubo al menos 1 error y stopOnFirstError==true => salir:
	if(this.options.stopOnFirstError && this.thereWasAnError) return;
	this.thereWasAnError = true;
	//buscar si elem tiene un error box asociado previamente x el usuario:
	this.errorBox = $(elem.id+'_err');
	//si ya existe un error box asociado a elem:
	if(this.errorBox != null && elem.id+'_err' == this.errorBox.id){
		this.errorBox.update(msg_error); //aqui update() y no insert() (para no agregar a lo que ya esta!)
		}
	else{
		//el errorBox no existe o existe pero no corresponde a elem, entonces crear uno:
			this.errorBox = new Element("div");
			this.errorBox.addClassName(this.validClasses.valid_error.substr(1));
			this.errorBox.insert(msg_error);
			if(elem.next().className==this.validClasses.valid_required_mark.substr(1))// tenia marca de required?
				elem.next().insert({after:this.errorBox}); //insertar despues de la marca
			else
				elem.insert({after:this.errorBox});
		}
	this.validateErr[elem.id]=true;
	this.errorBox.show();
	setTimeout("$('"+elem.id+"').focus()", 100);
	},//createErrorBox


	createPromptBox:function(elem){
	var msg_prompt = this.validatePrompt[elem.id];
	//Barrer todas las cajas de prompt y ocultarlas:
	$$(this.validClasses.valid_prompt).each(function(elem){elem.hide();});
	//si el elemento con foco no tiene prompt, salir:
	if(!this.validatePrompt[elem.id]) return;
	//verificar que ya exista la caja del prompt:
	this.promptBox = $(elem.id+'_prompt');
	if(this.promptBox != null && elem.id+'_prompt' == this.promptBox.id)
		this.promptBox.update(this.validatePrompt[elem.id]); //aqui update() y no insert() (para no agregar a lo que ya esta!)
	else{
		this.promptBox = new Element("div",{'id':elem.id+'_prompt'});
		this.promptBox.addClassName(this.validClasses.valid_prompt.substr(1));
		this.promptBox.insert(msg_prompt);
		elem.insert({after:this.promptBox});
	}
	this.promptBox.show();
	},//createPromptBox


	emailOk: function(email){
	var ok=true; var alfa="abcdefghijklmnopqrstuvwzyx.@-_0123456789";
	if (email.length<=6)
		ok=false
	if(ok){
		var i=0; var arroba=0; email=email.toLowerCase()
		while (ok && i<email.length){
			if (alfa.indexOf(email.charAt(i))<0)
				ok=false
			else	{
				if (email.charAt(i)=="@")
					arroba++;
				i++
				}//else
		}//while
		if(arroba!=1) ok=false;
		if(ok && (email.indexOf("@")<1 || email.lastIndexOf("@")>=email.length-1)) ok=false;
		if(ok && (email.indexOf(".")<0)) ok=false;
		if(ok && (email.indexOf(".")<1 || email.lastIndexOf(".")>=email.length-1)) ok=false;
		if(ok && email.indexOf("hotmail")>0) ok=email.indexOf(".com")>0;
		if(ok && email.indexOf("yahoo")>0) ok=email.indexOf(".com")>0;
		if(ok && email.indexOf("gmail")>0) ok=email.indexOf(".com")>0;
	} // if ok
	return ok
	},//emailOk


	usernameOk: function(username){
	if(username.length<6) return false;
	var legales="1234567890abcdefghijklmnopqrstuvwxyz_";
	var ok=true; 
	var i=0;
	while(ok && i<username.length){
		ok=(legales.indexOf(username.substr(i,1))>=0);
		if(ok) i++;
	}//while
	return ok;
	},//usernameOk


	integerOk: function(n){
	var legales="1234567890";
	var ok=true; 
	var i=0;
	var str_n = String(n);
	while(ok && i<str_n.length){
		ok=(legales.indexOf(str_n.substr(i,1))>=0);
		if(ok) i++;
	}//while
	return ok;
	},//integerOk


	dateOk:function(dt){
	var ok=true;
	//dt = dd-mm-aaaa
	//fecha vacia no es error
	if(dt.length==0) return true;
	var d=dt.substr(0,2);
	var m=dt.substr(3,2);
	var a=dt.substr(6,4);
	if(ok && (d<1 || d>31)) ok=false;
	if(ok && (m<1 || m>12)) ok=false;
	if(ok && (a<1900 || m>3000)) ok=false;
	return ok;
	},//dateOk


	selectionOk:function(selectList){
	ok = !(selectList.selectedIndex<=0);
	return ok;
	},//selectionOk


	urlOk:function(s) {
	//url vacia no es error
	if(s.length==0) return true;
	var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/
	return regexp.test(s);
	},// urlOk


	add: function(elem_id, method_options){
	_this = this;
	elem=$(elem_id);
	if(!elem) {
		alert("el id='"+elem_id+"' no existe");
		return false;
	}

	if(method_options.classNames && method_options.classNames.length){
		for(var i=0;i<method_options.classNames.length;i++)
			$(elem).addClassName(method_options.classNames[i]);
	}
	//forzar clase '.valid_add' p/identificar elementos que usaron este metodo:
	$(elem).addClassName(this.validClasses.valid_add.substr(1));

	if(method_options.msg_error)
		this.validateMsgError[elem.id] = method_options.msg_error;
	if(method_options.prompt)
		this.validatePrompt[elem.id] = method_options.prompt;
	if(method_options.allowedChars)
		this.validateAllowedChars[elem.id] = method_options.allowedChars;
	if(method_options.maxChars)
		this.validateMaxChars[elem.id] = method_options.maxChars;
	if(method_options.minChars)
		this.validateMinChars[elem.id] = method_options.minChars;
	if(method_options.oneOf)
		this.validateOneOf[elem.id] = method_options.oneOf;
	if(method_options.equalToField)
		this.validateEqualToField[elem.id] = method_options.equalToField;
	if(method_options.notEqualToField)
		this.validateNotEqualToField[elem.id] = method_options.notEqualToField;
	if(method_options.maxValue)
		this.validateMaxValue[elem.id] = method_options.maxValue;
	if(method_options.minValue)
		this.validateMinValue[elem.id] = method_options.minValue;
	if(method_options.validFunc)
		this.validateFunc[elem.id] = method_options.validFunc;

	if(method_options.validAjaxFunc){
		this.validateAjaxFunc[elem.id] = method_options.validAjaxFunc;
		var icon = new Element("img", {src:_this.loadingIcon});
		icon.addClassName(this.loadingIconClass.substr(1));
		icon.setStyle({display:'none'});
		elem.insert({after: icon});
	}

	if(method_options.validateOnBlur){
		elem.stopObserving("blur");
		elem.observe("blur", function(){
			_this.clearErrors();
			var arr_classes = elem.classNames().toArray(); //todas las clases del elemento en un array
			//buscar las clases que esten dentro de 'this.str_validClasses' y validarlas:
			for(var i=0; i<arr_classes.length; i++){
				var classFound = (_this.str_validClasses.indexOf(arr_classes[i])>=0);
				if(classFound){ //se encontro una clase de validacion, validar elemento:
					_this.validateElemByClass(elem, "."+arr_classes[i]); //validar x clase
					if(arr_classes[i]==_this.validClasses.valid_add.substr(1))
						_this.validateElemByAdd(elem); //validar x metodo add()
				}
			}//for
		});//elem.observe
	}//if(method_options.validateOnBlur)
	}//add
});// Validation Class

