/**********************************************************************
 *
 * Classe responsavel pela validacao de um formulario pelo lado
 * cliente, ou seja, atraves do javascript
 *
 **********************************************************************/
function ValidateForm(){

	// Objeto formulario que sera validado
	this.objForm = new Object();
	
	// Array de elementos a serem validados
	this.arrValidateElements = new Array();
	
	// Array de resultados da validacao
	this.arrValidateResults = new Array();
	
	// Constantes de substituicao
	this.TEXT_REPLACE_FIELD = "##FIELD##";
	this.TEXT_REPLACE_MAXLENGTH = "##MAXLENGTH##";
	
	// Constantes referentes as mensagens de erro da validacao
	this.MSG_ERROR_EMPTY = "O campo \"" + this.TEXT_REPLACE_FIELD + "\" não pode estar vazio.";
	this.MSG_ERROR_MAXLENGTH = "O campo \"" + this.TEXT_REPLACE_FIELD + "\" não pode ter mais de " + this.TEXT_REPLACE_MAXLENGTH + " caractere(s).";
	this.MSG_ERROR_ONLYNUMBER = "O campo \"" + this.TEXT_REPLACE_FIELD + "\" somente pode receber valor numérico.";
	this.MSG_ERROR_ONLYSTRING = "O campo \"" + this.TEXT_REPLACE_FIELD + "\" somente pode receber valor texto.";
	this.MSG_ERROR_EMAIL = "O \"E-mail\" não é válido.";
	this.MSG_ERROR_CNPJ = "O \"CNPJ\" informado não é um número válido.";
	this.MSG_ERROR_CPF = "O \"CPF\" informado não é um número válido.";
	this.MSG_ERROR_DATE = "O campo \"" + this.TEXT_REPLACE_FIELD + "\" não é uma data válida.";	
	
	/**********************************************************************
	 * Metodo que adiciona o formulario a ser validado no atributo 
	 * da classe
	 *
	 * @param mix _mixForm
	 * @return bool
	 **********************************************************************/
	this.addValidateForm = function(_mixForm){
	
		// Obrigando a passagem de todos os parametros
		if(_mixForm == undefined){
			return(false);
		}
		
		// Capturando informacoes do formulario parametrizado
		if(isObject(_mixForm)){
			var objForm = _mixForm;
		} else {
			var objForm = document.getElementById(_mixForm);
		}
		
		// Adicionando o formulario no atributo da classe
		this.objForm = objForm;
		
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que adiciona um elemento a ser validados, 
	 * com a sua respectiva regra de validacao
	 *
	 * @param mix _mixForm
	 * @param string _strValidateRule
	 * @param string _strSpecialParam
	 * @return bool
	 **********************************************************************/
	this.addValidateElement = function(_mixElement, _strValidateRule, _strSpecialParam){
		
		// Obrigando a passagem de todos os parametros
		if((_mixElement == undefined)||(_strValidateRule == undefined)){
			return(false);
		}
		
		// Capturando informacoes do elemento parametrizado
		if(isObject(_mixElement)){
			var objElement = _mixElement;
			var strIdElement = objElement.getAttribute("id");
		} else {
			var objElement = document.getElementById(_mixElement);
			var strIdElement = _mixElement;
		}
		var strValueElement = ( objElement != undefined ) ? objElement.value : "";
		
		// Adicionando todas as informacoes do elemento no array global
		var arrInfo = new Array();
		arrInfo.push(objElement);
		arrInfo.push(strIdElement);
		arrInfo.push(strValueElement);
		arrInfo.push(_strValidateRule);
		arrInfo.push(_strSpecialParam);
		this.arrValidateElements.push(arrInfo);
		
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que atualiza os valores dentro do array global de 
	 * elementos a serem validados
	 *
	 * @return bool
	 **********************************************************************/
	this.refreshValuesFromArrValidateElements = function(){
		
		// Criando novo array de elementos a serem validados
		var arrValidateElementsNew = new Array();
		
		// Varrendo o array de elementos a serem validados
		for(var i = 0; i < this.arrValidateElements.length; ++i){
			arrInfo = this.arrValidateElements[i];
			objElement = arrInfo[0];
			strIdElement = arrInfo[1];
			strValueElement = arrInfo[2];
			strValidateRule = arrInfo[3];
			strSpecialParam = arrInfo[4];
			
			arrInfoNew = new Array();
			arrInfoNew.push(objElement);
			arrInfoNew.push(strIdElement);
			arrInfoNew.push(objElement.value);
			arrInfoNew.push(strValidateRule);
			arrInfoNew.push(strSpecialParam);
			arrValidateElementsNew.push(arrInfoNew);
		}
		
		// Atribuindo novos valores no array de elementos
		this.arrValidateElements = arrValidateElementsNew;
	
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que submete a validacao
	 *
	 * @return bool
	 **********************************************************************/
	this.submitValidate = function(){
	
		// Atualizando valores do array de elementos a serem validados
		this.refreshValuesFromArrValidateElements();
	
		// Limpando array de resultados de outras validacoes
		this.arrValidateResults = new Array();
	
		// Varrendo o array de elementos a serem validados
		for(var i = 0; i < this.arrValidateElements.length; ++i){
		
			// Capturando informacoes
			arrInfo = this.arrValidateElements[i];
			objElement = arrInfo[0];
			strIdElement = arrInfo[1];
			strValueElement = arrInfo[2];
			strValidateRule = arrInfo[3];
			strSpecialParam = arrInfo[4];
			
			// Delegando e aplicando a regra de validacao determinada
			// caso o elemento nao esteja oculto
			if(objElement.style.display != 'none'){
			
				// Delegando a validacao
				mixValidate = this.delegateValidate(strValueElement, strValidateRule, strSpecialParam);
				if(!isBoolean(mixValidate)){
//					alert(getLabelFromElement(strIdElement));
					mixValidate = replaceAll(mixValidate, this.TEXT_REPLACE_FIELD, getLabelFromElement(strIdElement));
					mixValidate = replaceAll(mixValidate, this.MSG_ERROR_MAXLENGTH, strSpecialParam);
				}
				
				// Adicionando resultados no seu respectivo array global
				arrResult = new Array();
				arrResult.push(objElement);
				arrResult.push(strIdElement);
				arrResult.push(strValueElement);
				arrResult.push(mixValidate);
				this.arrValidateResults.push(arrResult);
			}
		}

		// Retorno do metodo
		return(true);	
	}
	
	/**********************************************************************
	 * Metodo que delega as validacoes
	 *
	 * @param string _strValueElement
	 * @param string _strValidateRule
	 * @param string _strSpecialParam
	 * @return bool
	 **********************************************************************/
	this.delegateValidate = function(_strValueElement, _strValidateRule, _strSpecialParam){
	
		// Obrigando a passagem de todos os parametros
		if((_strValueElement == undefined)||(_strValidateRule == undefined)){
			return(false);
		}
		
		// Delegando cada regra de validacao
		switch(_strValidateRule.toLowerCase()){
			case "empty":{
				return(this.validateEmpty(_strValueElement));
				break;
			}
			case "maxlength":{
				return(this.validateMaxLength(_strValueElement, _strSpecialParam));
				break;
			}
			case "onlynumber":{
				return(this.validateOnlyNumber(_strValueElement));
				break;
			}
			case "onlystring":{
				return(this.validateOnlyString(_strValueElement));
				break;
			}
			case "email":{
				return(this.validateEmail(_strValueElement));
				break;
			}
			case "cnpj":{
				return(this.validateCnpj(_strValueElement));
				break;
			}
			case "cpf":{
				return(this.validateCpf(_strValueElement));
				break;
			}
			case "date":{
				return(this.validateDate(_strValueElement));
				break;
			}
		}		
		// Retorno do metodo
		return(false);	
	}
	
	/**********************************************************************
	 * Metodo que valida se o campo esta vazio ou nao
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateEmpty = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja vazio
		if(isEmpty(_strValueElement)){
			return(this.MSG_ERROR_EMPTY);
		}
		
		// Retorno do metodo
		return(true);	
	}
	
	/**********************************************************************
	 * Metodo que valida o tamanho maximo de caracteres
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateMaxLength = function(_strValueElement, _intMaxLength){
	
		// Obrigando a passagem de todos os parametros
		if((_strValueElement == undefined)||(_intMaxLength == undefined)){
			return(false);
		}
		
		// Caso o tamanho tenha ultrapassado o maximo de caracteres
		if(_strValueElement.length > _intMaxLength){
			return(this.MSG_ERROR_MAXLENGTH);
		}
		
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que valida um campo somente para numero
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateOnlyNumber = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja igual a vazio
		if(_strValueElement == ""){
			return(true);		
		}
		
		// Caso o valor do campo seja somente numerico, 
		// senao mostra erro
		var strRegex = /^\d+$/;
		if(_strValueElement.match(strRegex)){
			return(true);
		} else {
			return(this.MSG_ERROR_ONLYNUMBER);
		}
	}

	/**********************************************************************
	 * Metodo que valida um campo somente para string
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateOnlyString = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja igual a vazio
		if(_strValueElement == ""){
			return(true);		
		}		
		
		// Caso o valor do campo nao seja somente texto
		if(!isString(_strValueElement)){
			return(this.MSG_ERROR_ONLYSTRING);
		}
		
		// Retorno do metodo
		return(true);
	}

	/**********************************************************************
	 * Metodo que valida um campo email
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateEmail = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja igual a vazio
		if(_strValueElement == ""){
			return(true);		
		}
		
		// Verificando se existe arroba
		var intCaracArroba = _strValueElement.indexOf("@");
		if(intCaracArroba == -1){
			return(this.MSG_ERROR_EMAIL);
		}
		
		// Verificando se existe algum ponto apos a arroba
		var strFinalPart = _strValueElement.substring(intCaracArroba, _strValueElement.length);
		var intCaracDotAfterArroba = strFinalPart.indexOf(".");
		if(intCaracDotAfterArroba == -1){
			return(this.MSG_ERROR_EMAIL);
		}
	
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que valida um campo cnpj
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateCnpj = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja igual a vazio
		if(_strValueElement == ""){
			return(true);		
		}		
		
		// Aplicado tambem a regra de maxlength no cnpj
		var mixMaxLength = this.validateMaxLength(_strValueElement, 18);
		if(mixMaxLength != true){
			return(this.MSG_ERROR_CNPJ);
		}
		
		// Realizando rotina que valida o digito verificador do cnpj
		var strMaskNumber = "";
		var strLpad = "00000000000000";
		for(var i = 0; i < _strValueElement.length; ++i){
			var strChr = _strValueElement.substring(i, i + 1);
			if (strChr >= "0" && strChr <= "9"){
		      	strMaskNumber += strChr;
		   	}
		}
      	strMaskNumber = strLpad.substring(0, 14 - strMaskNumber.length) + strMaskNumber;
     	strMaskNumber = strMaskNumber.substring((strMaskNumber.length - 14), strMaskNumber.length);
      	strSumBlock1 = 0;
      	strRemBlock1 = 0;
      	strSumBlock2 = 0;
      	strRemBlock2 = 0;
		for(var i = 0; i < (strMaskNumber.length - 2); ++i){
		   	var strChr = strMaskNumber.substring(i, i + 1);
		   	if(i <= 3){
				strSumBlock1 += eval(strChr * (5 - i));
			} else {
				strSumBlock1 += eval(strChr * (13 - i));
			}
		}	
	    strRemBlock1 = eval(strSumBlock1 - (parseInt(strSumBlock1 / 11) * 11));
	    if(strRemBlock1 <= 1){
         	strRemBlock1 = 0;
      	} else {
         	strRemBlock1 = eval(11 - strRemBlock1);
      	}
		for(var i = 0; i < (strMaskNumber.length - 2); ++i){
		   	var strChr = strMaskNumber.substring(i, i + 1);
		   	if(i <= 4){
				strSumBlock2 += eval(strChr * (6 - i));
			} else {
				strSumBlock2 += eval(strChr * (14 - i));
			}
		}
		strSumBlock2 += eval(2 * strRemBlock1);
		strRemBlock2 = eval(strSumBlock2 - (parseInt(strSumBlock2 / 11) * 11));
		if(strRemBlock2 <= 1){
		   strRemBlock2 = 0;
		} else {
		   strRemBlock2 = eval(11 - strRemBlock2);
		}
		if((strRemBlock1 == strMaskNumber.substring(12,13)) && (strRemBlock2 == strMaskNumber.substring(13,14))){
 			return true;
		} else {
			return(this.MSG_ERROR_CNPJ);
		}
	
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que valida um campo cpf
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateCpf = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja igual a vazio
		if(_strValueElement == ""){
			return(true);		
		}
		
		// Aplicado tambem a regra de maxlength no cpf
		var mixMaxLength = this.validateMaxLength(_strValueElement, 14);
		if(mixMaxLength != true){
			return(this.MSG_ERROR_CPF);
		}	
		
		// Realizando rotina que valida o digito verificador do cpf
	   	var strMaskNumber = "";
	   	var strLpad = "00000000000";
	   	for(var i = 0; i < _strValueElement.length; ++i){
	      	var strChr = _strValueElement.substring(i, i + 1);
	    	if(strChr >= "0" && strChr <= "9"){
	        	strMaskNumber += strChr;
	      	}
	   	}
	   	strMaskNumber = strLpad.substring(0, 11 - strMaskNumber.length) + strMaskNumber;
	   	strMaskNumber = strMaskNumber.substring((strMaskNumber.length - 11), strMaskNumber.length);
	   	strSumBlock1 = 0,
	   	strRemBlock1 = 0;
	   	strSumBlock2 = 0;
	   	strRemBlock2 = 0;
	   	for(var i = 0; i < (strMaskNumber.length - 2); ++i){
	      	var strChr = strMaskNumber.substring(i, i + 1);
	      	strSumBlock1 += eval(strChr * (10 - i));
	   	}	   
	   	strRemBlock1 = eval(strSumBlock1 - (parseInt(strSumBlock1 / 11) * 11));
	   	if(strRemBlock1 == 0 || strRemBlock1 == 1){
	      	strRemBlock1 = 0;
	   	} else {
	      	strRemBlock1 = eval(11 - strRemBlock1);
	   	}	   
	   	for(var i = 0; i < (strMaskNumber.length - 1); ++i){
	      	var strChr = strMaskNumber.substring(i, i + 1);
	      	strSumBlock2 += eval(strChr * (11 - i));
	   	}	   
	   	strRemBlock2 = eval(strSumBlock2 - (parseInt(strSumBlock2 / 11) * 11));
	   	if(strRemBlock2 == 0 || strRemBlock2 == 1){
	      	strRemBlock2 = 0;
	   	} else {
	      	strRemBlock2 = eval(11 - strRemBlock2);
	   	}	   
		if(strRemBlock1 == parseInt(strMaskNumber.substring(9,10)) && strRemBlock2 == parseInt(strMaskNumber.substring(10,11))){
			if(strMaskNumber != '00000000000' && strMaskNumber != '11111111111' && strMaskNumber != '22222222222' && strMaskNumber != '33333333333' && strMaskNumber != '44444444444' && strMaskNumber != '55555555555' && strMaskNumber != '66666666666' && strMaskNumber != '77777777777' && strMaskNumber != '88888888888' && strMaskNumber != '99999999999'){
				return(true);
			} else {
				return(this.MSG_ERROR_CPF);
			}
		 } else {
			return(this.MSG_ERROR_CPF);
		 }
		 
		// Retorno do metodo
		return(false);
	}
	
	/**********************************************************************
	 * Metodo que valida um campo data
	 *
	 * @param string _strValueElement
	 * @return bool
	 **********************************************************************/
	this.validateDate = function(_strValueElement){
	
		// Obrigando a passagem de todos os parametros
		if(_strValueElement == undefined){
			return(false);
		}
		
		// Caso o valor seja igual a vazio
		if(_strValueElement == ""){
			return(true);		
		}		
		
		// Expressao regular que valida uma data
		var strRegex = /^(?:(?:[0-2]\d|3[0-1])\/(?:01|03|05|07|08|10|12)|(?:[0-2]\d|30)\/(?:04|06|09|11)|(?:[0-1]\d|2[0-9])\/02)\/(?:19\d{2}|20\d{2})/;
		if(!_strValueElement.match(strRegex)){
			return(this.MSG_ERROR_DATE);
		}
		
		// Retorno do metodo
		return(true);
	}
	
	/**********************************************************************
	 * Metodo que retorna o resultado da validacao
	 *
	 * @return array
	 **********************************************************************/
	this.returnValidateResult = function(){
		return(this.arrValidateResults);
	}
	
	/**********************************************************************
	 * Metodo que visualiza o resultado da validacao
	 *
	 * @return bool
	 **********************************************************************/
	this.showValidateResult = function(){
		
		var strResultAlert = "";
		
		// Varrendo array resultante da validacao
		for(var i = 0; i < this.arrValidateResults.length; ++i){
			arrResult = this.arrValidateResults[i];
			objElement = arrResult[0];
			strIdElement = arrResult[1];
			strValueElement = arrResult[2];
			mixValidate = arrResult[3];
			
			// Caso possua mensagem escrita de erro
			if(!isBoolean(mixValidate)){
				strResultAlert += "&nbsp;&raquo;&nbsp;" + mixValidate + "<br />";
			}
		}
		
		// Janela modal do prototype para visualizar os erros
		if(strResultAlert != ""){
			alerta(strResultAlert, 'Erro(s) na aplicação', 240, 120);
			return(false);
		} else {
			return(true);
		}
	}
	
	/**********************************************************************
	 * Metodo que valida o formulario utilizando a classe 
	 * ValidateForm
	 *
	 * @return bool
	 **********************************************************************/
	this.validateNow = function(){
		// Aplicando metodos de execucao da validacao e mostragem dos resultados
		this.submitValidate(); 
		return(this.showValidateResult());
	}
}
