/**
@name ValidaocaoData.js
@description Valida datas nos formatos dd/mm/aaaa e mm/aaaa.
@created 30/10/2003
@author Jorge Bittencourt e Marcos Condes Lopes
*/


//*************************************************************************************************************
/**
@name AnoBissexto
@description Calcula se um determinado ano é bissexto
@created 10/08/2004
@param ano: o ano
@return true, se o ano for bissexto, false, caso contrário
@author Jorge Bittencourt
*/
function AnoBissexto(ano)
{
	// se o ano for divisível por 4, ele é bissexto se não for divisível por 100 ou,
	// caso seja, se também for divisível por 400
	if (ano % 4 == 0)
		return (ano % 100 != 0 || (ano % 100 == 0 && ano % 400 == 0));
	// se não for divisível por 4, não é bissexto
	else
		return false;
}


//*************************************************************************************************************
/**
@name CalculeNumeroAnosBissexto
@description Calcula o número de anos bissextos compreendidos entre o ano 0 (inclusive) e o ano especificado (inclusive)
@created 10/08/2004
@param ano: o ano
@return o número de anos bissextos
@author Jorge Bittencourt
*/
function CalculeNumeroAnosBissexto(ano)
{
	var ano_ = new Number(ano);
	// recupere o número de anos divisíveis por 4
	var quatro = Math.floor(ano_/4);
	// recupere o número de anos divisíveis por 100
	var cem = Math.floor(ano_/100);
	// recupere o número de anos divisíveis por 400
	var quatrocentos = Math.floor(ano_/400);
	
	// o número final é o quatro - (cem - quatrocentos)
	// adicione 1 porque o ano 0000 é bissexto
	return quatro - (cem - quatrocentos) + 1;
}


//*************************************************************************************************************
/**
@name CalculeNumeroDiasNoMes
@description Calcula o número de dias em um mês de um certo ano
@created 10/08/2004
@param mes: o mes a ser calculado
@param ano: o ano a qual o mes pertence
@return O número de dias do mês de um ano
@author Jorge Bittencourt
*/
function CalculeNumeroDiasNoMes(mes, ano)
{
	var ano_ = new Number(ano).valueOf();
	var mes_ = new Number(mes).valueOf();
	switch (mes_)
	{
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			return 31;
		case 4:
		case 6:
		case 9:
		case 11:
			return 30;
		case 2:
			return (AnoBissexto(ano_) ? 29 : 28);
	}
}

//*************************************************************************************************************
/**
@name CompareDatasUmAno
@description Compara data1 e data2 para verificar se data1 é 1 ano anterior a data2. Se for,
 confirma com o usuário se isso é desejado. Caso não seja, focaliza o campo.
@param data1: a primeira data
@param data2: a segunda data
@param mensagem: a mensagem a ser exibida para o usuário na hora da confirmação
@param inputToFocus: o input que receberá o foco se a confirmação do usuário for negativa
@param inputToClear: o input que armazena se o usuário já confirmou essa data previamente
@created 08/06/2004
@return true, se data1 for 1 ano anterior a data2, false, caso contrário
@author Jorge Bittencourt
*/
function CompareDatasUmAno(data1, data2, mensagem, inputToFocus, inputToClear)
{
	// assuma que data1 não é 1 ano menor que data2
	var resultado = false;

	// se qualquer das datas estiver em branco, saia
	if (pfnTrim(data1) == "" || pfnTrim(data2) == "")
		return true;

	// recupere e quebre as datas
	var partesDt1 = data1.split("/");
	var partesDt2 = data2.split("/");
	var dia1 = new Number(partesDt1[0]).valueOf();
	var mes1 = new Number(partesDt1[1]).valueOf();
	var ano1 = new Number(partesDt1[2]).valueOf();

	var dia2 = new Number(partesDt2[0]).valueOf();
	var mes2 = new Number(partesDt2[1]).valueOf();
	var ano2 = new Number(partesDt2[2]).valueOf();

	// primeiro valide se o ano da primeira data é anterior a um ano da segunda
	if (ano1 < ano2)
	{
		// se o ano da primeira for 2 menor do que o ano da segunda, com certeza data1 é anterior
		// a um ano da data2
		if (ano1 <= ano2 - 2)
			resultado = true;
		// caso contrário é preciso examinar o mês e o dia
		else
		{
			// aqui ano1 é um a menos do que ano2
			// se mes1 for menor do que mes2, data1 é 1 ano anterior a data2
			if (mes1 < mes2)
				resultado = true;
			// se o mês for igual, é preciso examinar o dia
			else if (mes1 == mes2)
			{
				// aqui os meses são iguais - se dia1 for menor que dia2,
				// data1 é um ano anterior a data2
				resultado = (dia1 <= dia2);
			}
		}
	}
	// se data1 é anterior a um ano da data2, confirme com o usuário se isso é desejado
	if (resultado)
	{
		// só confirme com o usuário se este já não confirmou para esta data
		if (inputToClear != null && inputToClear.value == "")
		{
			var yesNo = confirm(mensagem);

			// se o usuário não confirmar, selecione a data
			if (!yesNo)
			{
				inputToFocus.select();
				inputToFocus.focus();
			}
			else
				// armazene o fato de que o usuário já confirmou essa data
				inputToClear.value = "y";
		}
	}
	// retorne o resultado
	return resultado;
}


//*************************************************************************************************************
/**
@name pfnCalculeNumeroDias
@description Calcula o número de dias que se passaram desde 0/0/0 até a data especificada, incluindo o dia da data
@created 10/08/2004
@param data: a data da qual se deseja o número de dias
@return O número de dias que se passaram desde 0/0/0 até a data especificada, incluindo o dia da data
@author Jorge Bittencourt
*/
function pfnCalculeNumeroDias(data)
{
	var data_ = new String(data);
	var partes = data.split("/");
	var result;
	
	var dia = new Number(partes[0]).valueOf();
	var mes = new Number(partes[1]).valueOf();
	var ano = new Number(partes[2]).valueOf();
	
	// adicione o número de dias no mês atual
	result = dia;
	
	// adicione o número de dias dos meses anteriores
	var i = mes - 1;
	while (i > 0)
	{
		result += CalculeNumeroDiasNoMes(i, ano)
		i--;
	}
	
	// adicione o número de dias dos anos anteriores
	if (ano > 0)
		result += 365 * (ano) + CalculeNumeroAnosBissexto(ano - 1);
	
	// retorne o resultado
	return result;
}


//*************************************************************************************************************
/**
@name pfnCalculeNumeroMeses
@description Calcula o número de meses que se passaram desde 0/0/0 até a data especificada
@created 04/11/2004
@param data: a data da qual se deseja o número de meses
@return Conforme descrito acima.
@author Jorge Bittencourt
*/
function pfnCalculeNumeroMeses(data)
{
	var data_ = new String(data);
	var partes = data.split("/");
	var mes = new Number(partes[0]).valueOf();
	var ano = new Number(partes[1]).valueOf();

	// o número de meses é o número de meses desse ano mais 12 * o número de anos anteriores
	// a esse
	return mes + 12 * (ano - 1);
}


//*************************************************************************************************************
/**
@name pfnCompareDatas
@description Compara duas datas no formato dd/mm/aaaa e devolve a diferença em dias
@created 03/11/2003
@param inicio: a primeira data a ser comparada
@param fim: a segunda data a ser comparada
@return a diferença das duas datas, em dias (fim - inicio, se o operador "-" funcionasse com data). Se inicio < fim, 
 o resultado é positivo. Se inicio > fim, o resultado é negativo. Se fim = inicio, o resultado é 0.
@author Jorge Bittencourt
*/
function pfnCompareDatas(inicio, fim)
{	
	// calcule a diferença das duas datas, em dias
	return (pfnCalculeNumeroDias(fim) - pfnCalculeNumeroDias(inicio));
}


//*************************************************************************************************************
/**
@name pfnCompareDatasMesAno
@description Compara duas datas no formato mm/aaaa
@created 04/02/2004
@param inicio: data inicial mm/aaaa
@param fim: data final mm/aaaa
@return quantidade de diferença em meses.
*/
function pfnCompareDatasMesAno(inicio, fim)
{	
	return pfnCalculeNumeroMeses(fim) - pfnCalculeNumeroMeses(inicio);
}


//*************************************************************************************************************
/**
@name pfnDataHoje10Caracteres
@description Retorna a data de hoje no formato dd/mm/aaaa
@created 09/02/2004
@return A data de hoje, com 10 caracteres, no formato dd/mm/aaaa
@author Jorge Bittencourt
*/
function pfnDataHoje10Caracteres()
{
	var dataHoje = new Date();
	var dia, mes;
	dia = new String(dataHoje.getDate());
	mes = new String(dataHoje.getMonth() + 1);
	if (dia.length == 1)
		dia = "0" + dia;
	if (mes.length == 1)
		mes = "0" + mes;
	dataHoje = dia + "/" + mes + "/" + dataHoje.getFullYear();
	return dataHoje;
}


//*************************************************************************************************************
/**
@name pfnDataHoje7Caracteres
@description Retorna a data de hoje no formato mm/aaaa
@created 29/10/2004
@return A data de hoje, com 7 caracteres, no formato mm/aaaa
@author Jorge Bittencourt
*/
function pfnDataHoje7Caracteres()
{
	var dataHoje = new Date();
	var mes;
	mes = new String(dataHoje.getMonth() + 1);
	if (mes.length == 1)
		mes = "0" + mes;
	dataHoje = mes + "/" + dataHoje.getFullYear();
	return dataHoje;
}

//*************************************************************************************************************
/**
@name pfnDiasNoMes
@description Retorna o número de dias de um mês de um dado ano
@created 04/11/2003
@param mes: um dígito no intervalo [1,12]
@param ano: um ano de quatro dígitos
@return o número de dias no mês especificado (-1 se o mes for inválido, ou seja, 
 fora do intervalo [1,12], ou se o ano for invalido, ou seja, fora do intervalo [1, 9999])
@author Jorge Bittencourt
*/
function pfnDiasNoMes(mes, ano)
{
	if (isNaN(mes) || isNaN(ano) || mes < 1 || mes > 12 || ano < 1 || ano > 9999)
		return -1;
		
	switch(mes)
	{
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			return 31;
		case 4:
		case 6:
		case 9:
		case 11:
			return 30;
		case 2:
			return ((ano % 4 == 0) ? 29: 28);
	}
}


//*************************************************************************************************************
/**
@name pfnFormataData
@description Formata uma data à medida que os caracteres são digitados
 Inclui a barra separadora dos campos da data. Não permite a digitação de caracteres que não sejam números
 Deve ser usada no evento onKeyPress
@created 30/10/2003
@param input: o campo onde a data está sendo digitada
@return Não retorna nada
@author Jorge Bittencourt
*/
function pfnFormataData(input)
{
	if (event.keyCode < 48 || event.keyCode > 57)
		event.returnValue = false;
	else
	{
		var data = input.value;
		// verifique se todos os números fora as barras são de fato número
		if((data.length == 2)||(data.length == 5))
			input.value = input.value + "/" ;
	}
}


//*************************************************************************************************************
/**
@name pfnFormataDataMesAno
@description Formata uma data à medida que os caracteres são digitados no formato mm/aaaa
 Inclui a barra separadora dos campos da data. Não permite a digitação de caracteres que não sejam números.
 Deve ser usada no evento onKeyPress
@created 15/01/2004
@param input: o objeto que contém a data a ser validada
@param true: se o objeto passado for uma data válida
@return Não retorna nada
@author Marcos Conde Lopes
*/
function pfnFormataDataMesAno(input)
{
//formata no seguinte formato mm/aaaa
	if (event.keyCode < 48 || event.keyCode > 57)
		event.returnValue = false;
	else 
		if(input.value.length == 2)
			input.value=input.value + "/";
}


//*************************************************************************************************************
/**
@name pfnFormataData10Caracteres
@description Formata uma data no formato dd/mm/aaaa
 O input dessa função deve ser uma data de 8 a 10 dígitos no formato d[d]/m[m]/aaaa
 Ou seja, o que essa função realmente faz é preencher com zeros à esquerda para assegurar 
 que o output dessa função seja uma data com 10 caracteres
@created 04/11/2003
@param data: a string a ser validada
@return a data formatada ou o parâmetro data caso data não esteja formatada apropriadamente
@author Jorge Bittencourt
@exemplo var str10 = pfnFormataData10Caracteres("9/10/2002"); // str10 será "09/10/2002"
*/
function pfnFormataData10Caracteres(data)
{
	var partes;
	try
	{
		partes = data.split("/");
		if (partes[0].length == 1)
			partes[0] = "0" + partes[0];
		if (partes[1].length == 1)
			partes[1] = "0" + partes[1];
		
		return partes[0] + "/" + partes[1] + "/" + partes[2];
	}
	catch (e)
	{
		return data;
	}
}


//*************************************************************************************************************
/**
@name pfnFormataData7Caracteres
@description Formata uma data no formato mm/aaaa. O input dessa função deve ser uma data de 8 a 10 dígitos
 no formato d[d]/m[m]/aaaa. O que essa função realmente faz é preencher com zeros à
 esquerda para assegurar que o output dessa função seja uma data
@created 15/01/2004
@param data: a string a ser validada
@return a data formatada ou o parâmetro data caso data não esteja formatada apropriadamente
@author Marcos Conde Lopes
*/
function pfnFormataData7Caracteres(data)
{
	var partes;
	try
	{
		partes = data.split("/");
		if (partes[0].length == 1)
			partes[0] = "0" + partes[0];
		if (partes[1].length == 1)
			partes[1] = "0" + partes[1];
		
		return partes[1] + "/" + partes[2];
	}
	catch (e)
	{
		return data;
	}
}


//*************************************************************************************************************
/**
@name pfnValidaEFormataData
@description A mesma função abaixo, porém aceita o nome do form para obter o campo onde a data
 está sendo digitada
@created 04/11/2003
@param form: o nome do formulário que contém o campo cuja data será validada
@param campo: o nome do campo que contém a data a ser validada
@param mensagem: a mensagem a ser mostrada ao usuário se a data não for válida
@return true, se a validação da data tiver sucesso; false, caso contrário
@author Marcos Lopes Conde
@exemplo <input name="dataASerValidada" onBlur='pfnValidaEFormataData("formName", this.name, "Mensagem Personalizada")'></input>
*/
function pfnValidaEFormataData(form, campo, mensagem)
{
	eval("input = document." + form + "." + campo + ";");
  return pfnValideFormateData(input, mensagem);
}


//*************************************************************************************************************
/**
@name pfnValideFormateData
@description Verifica se uma data é válida, ou seja, se o dia, o mês e o ano estão corretos
 Se a data não estiver formatada no padrão dd/mm/aaaa mas for válida, formata a data nesse padrão
@created 04/11/2003
@param input: o campo onde a data está sendo digitada
@param mensagem: a mensagem a ser mostrada ao usuário se a data não for válida
@return true, se a validação da data tiver sucesso; false, caso contrário
@author Marcos Lopes Conde
*/
function pfnValideFormateData(input, mensagem)
{
	// se a validação falhar, focalize o campo que deu problema e mostre a mensagem, se houver alguma
	var sucesso = pfnValideFormateData_(input.value);
  if (sucesso == null && pfnTrim(input.value) != "")
	{
		if (mensagem != "")
			alert(mensagem);
		input.select();
		// o focus precisa estar aqui por causa de bugs nos tabs do explorer
		input.focus();
	}
	else if (sucesso != null)
	 input.value = sucesso;
	return sucesso != null;
}


//*************************************************************************************************************
/**
@name pfnValideFormateData_
@description Verifica se uma data é válida, ou seja, se o dia, o mês e o ano estão corretos
 Se a data não estiver formatada no padrão dd/mm/aaaa mas mas for válida, formata a data nesse padrão
@created 04/11/2004
@param data_: a string a ser validada
@return a data formatada, caso esta seja válida, ou null, caso contrário
*/
function pfnValideFormateData_(data_)
{
	try
	{
    var ano, mes, dia, data, partes, sucesso, resultado;
		resultado = null;
		sucesso = true;
    data = new String(data_);

		// se a data estiver em branco, a validação tem sucesso
		// isso permite ao usuário mudar sua opção de escolha no formulário
		if (data == "")
			return null;
		// uma data deve ter no mínimo 8 caracteres (d/m/aaaa ou ddmmaaaa) e no máximo 10 caracteres (dd/mm/aaaa)
		if (data.length < 8 || data.length > 10)
			throw "exception";
		// quebre a data em três partes
		partes = data.split("/");

		// se ela tiver duas barras, recupere o dia, mês e ano
		if (partes.length == 3)
		{
			// eu não uso parseInt porque ela não lê alguns dígitos com leading 0´s da maneira certa
			// por exemplo, '09' é convertido para 0
			dia = new Number(partes[0]).valueOf();
			mes = new Number(partes[1]).valueOf();
			ano = new Number(partes[2]).valueOf();

			if (isNaN(dia) || isNaN(mes) || isNaN(ano))
				throw "exception";
		}
		// se não houver duas barras, a data precisa ter exatamente 8 dígitos
		// para a validação funcionar - o dia é os 2 primeiros caracteres,
		// o mês é os 2 seguintes e o ano os 4 seguintes
		else
		{
			// se a data tiver caracteres inválidos, isso será detectado na próxima validação
			var dataSemBarras = data.replace(/\D/g, "");
			if (dataSemBarras.length != 8 || isNaN(dataSemBarras))
				throw "exception";
			else
			{
				dia = new Number(dataSemBarras.substring(0, 2)).valueOf();
				mes = new Number(dataSemBarras.substring(2, 4)).valueOf();
				ano = new Number(dataSemBarras.substring(4, 8)).valueOf();

				// preencha essa variável com as substrings porque ela será usada embaixo para
				// remontar as datas com as barras
				partes[0] = dataSemBarras.substring(0, 2);
				partes[1] = dataSemBarras.substring(2, 4);
				partes[2] = dataSemBarras.substring(4, 8);
			}
		}
		// valide o dia, mês e ano
		if ((mes < 1 || mes > 12) ||
			(ano < 1900 || ano > 2185) ||
			(dia < 1 || dia > pfnDiasNoMes(mes, ano)))
			sucesso = false;
	}
	catch(e)
	{
		sucesso = false;
	}

	// se a validação tiver sucesso, concatene as partes da data novamente, formatando-a de
	// modo que tenha 10 caracteres
	if (sucesso)
	{
		if (partes[0].length == 1)
			partes[0] = "0" + partes[0];
		if (partes[1].length == 1)
			partes[1] = "0" + partes[1];
		resultado =  partes[0] + '/' + partes[1]  + '/' + ano;
	}
	return resultado;
}

//*************************************************************************************************************
/**
@name pfnValidaEFormataDataMesAno
@description Verifica se uma data é válida, ou seja, se o mês e o ano estão corretos
 Se a data não estiver formatada no padrão mm/aaaa mas for válida, formata a data nesse padrão
@created 15/01/2004
@param input: o campo que contém a data a ser validada
@param mensagem: a mensagem a ser mostrada ao usuário se a data não for válida
@return true, se a validação da data tiver sucesso; false, caso contrário
@author Marcos Conde Lopes
*/
function pfnValidaEFormataDataMesAno(input, mensagem)
{
	// se a validação falhar, focalize o campo que deu problema e mostre a mensagem, se houver alguma
	var sucesso = pfnValideFormateDataMesAno(input.value);
  if (sucesso == null && pfnTrim(input.value) != "")
	{
		if (mensagem != "")
			alert(mensagem);
		input.select();
		// o focus precisa estar aqui por causa de bugs nos tabs do explorer
		input.focus();
	}
	return sucesso != null;
}


//*************************************************************************************************************
/**
@name pfnValideFormateDataMesAno
@description Verifica se uma data é válida, ou seja, se o mês e o ano estão corretos
 Se a data não estiver formatada no padrão mm/aaaa mas for válida, formata a data nesse padrão
@created 04/11/2004
@param data: a data a ser validada
@return a data formatada, se esta for válida, ou null, caso contrário
*/
function pfnValideFormateDataMesAno(data_)
{
	try
	{
		var partes, ano, mes, data, sucesso;
		resultado = null;
		sucesso = true;
		var data = pfnTrim(new String(data_));
		// se a data estiver em branco, a validação tem sucesso
		if (data == "")
			return null;

		// uma data deve ter no mínimo 6 caracteres (m/aaaa ou mmaaaa) e no máximo 7 caracteres (mm/aaaa)
		if (data.length < 6 || data.length > 7)
			throw "exception";
		// quebre a data em mês e ano
		partes = data.split("/");

		// se ela tiver duas partes, recupere  mês e ano
		if (partes.length == 2)
		{
			mes = new Number(partes[0]).valueOf();
			ano = new Number(partes[1]).valueOf();

			if (isNaN(mes) || isNaN(ano))
			   throw "exception";
		}
		// o mês é os 2 seguintes e o ano os 4 seguintes
		else
		{
			var dataSemBarras = data.replace("/", "");
			if (dataSemBarras.length != 6 || isNaN(dataSemBarras))
				throw "exception";

			mes = new Number(dataSemBarras.substring(0, 2)).valueOf();
			ano = new Number(dataSemBarras.substring(2, 6)).valueOf();

			// preencha essa variável com as substrings porque ela será usada embaixo para
			// remontar as datas com as barras
			partes[0] = dataSemBarras.substring(0, 2);
			partes[1] = dataSemBarras.substring(2, 6);
		}
		// valide o dia, mês e ano
		if ((mes < 1 || mes > 12) ||
			(ano < 1900 || ano > 2185))
			throw "exception";
	}
	catch(e)
	{
		sucesso = false;
	}

	// se a validação tiver sucesso, concatene as partes da data novamente, formatando-a de
	// modo que tenha 10 caracteres
	if (sucesso)
	{
		if (partes[0].length == 1)
			partes[0] = "0" + partes[0];
		resultado = partes[0]  + '/' + ano;
	}
	return resultado;
}


//*************************************************************************************************************
/**
@name pfnValideDia
@description Valida um dia entrado pelo usuário em um campo qualquer
 Não leva em consideração o mês, por isso o dia pode ser qualquer valor entre 0 e 31, inclusive.
 A função exibe a mensagem de erro passada e focaliza o campo, caso o dia seja inválido e o
 usuário tenha passado uma mensagem e um campo a ser focalizado
@created 07/01/2004
@param input: o objeto que contém o dia a ser validado. Se for passado um campo válido, caso a validação falhe, 
 o campo é focalizado
@param dia: o valor a ser validado
@param mensagem: a mensagem a ser exibida caso a validação falhe. Se for passado uma mensagem em branco,
 nenhuma mensagem é exibida
@return true, se a validação tiver sucesso; false, caso contrário
@author Jorge Bittencourt
*/
function pfnValideDia(input, diaStr, mensagemErro)
{
	// assuma que a validação falhou
	var sucesso = false;

	// prossiga se o valor digitado não for uma string vazia 
	if (diaStr != "")
	{
		// o valor digitado convertido a número
		var numero;
	
		// prossiga somente se o valor a ser validado não contiver caracteres não-numéricos
		if (diaStr.length == diaStr.replace(/\D/g, "").length)
		{
			// converta o valor digitado para um número
			numero = new Number(diaStr).valueOf();
	
			// determine se o número é um dia válido
			if (numero > 0 && numero <= 31)
				sucesso = true;
		}
		
		// se a validação falhou, exiba a mensagem de erro e focalize o campo onde o dia foi digitado
		// caso esses valores sejma válidos
		if (!sucesso)
		{
			if (mensagemErro != "")
				alert(mensagemErro);
			
			// determine se o nome do input é válido e se esse campo existe
			if (input != null)
			{
				var existe;
				eval("existe = document.all('" + input.id + "');");
				if (existe)
				{
					eval("document.all.item('" + input.id + "').focus();");
					eval("document.all.item('" + input.id + "').select();");
				}
			}
		}
	}
	return sucesso;
}

