var http;
var InstanciaForm = null;
function Form() {
	/// <summary>
	/// Define uma URL para postar o formulário.
	/// <access mode="public"/>
	/// </summary>
	this.PostUrl = null;

	/// <summary>
	/// Define a URL que deve ser postada para carregar o formulário.
	/// <access mode="public"/>
	/// </summary>
	this.LoadUrl = null;
	
	/// <summary>
	/// Define o formulário para ser postado.
	/// <access mode="public"/>
	/// </summary>
	this.Form = null;

	/// <summary>
	/// Define a string que deve ser enviada junto à requisição.
	/// <access mode="public"/>
	/// </summary>
	this.StrPost = null;

	/// <summary>
	/// Define o nome do nó XML que contém as informações do registro.
	/// <access mode="public"/>
	/// </summary>
	this.NoRegistro = null;

	/// <summary>
	/// Define os nomes dos nós XML que contém possíveis erro na solicitação dos dados. Se estiver definido e o XML retornar qualquer nó que coincidam com os nós definidos, será considerado que existe um erro.
	/// <access mode="public"/>
	/// </summary>
	this.NoErro = Array();

	/// <summary>
	/// Define o formulário que deve ser submetido para carregar o formulário.
	/// <access mode="public"/>
	/// </summary>
	this.LoadForm = null;

	/// <summary>
	/// Armazena o estado de retorno para o carregamento do formulário. Caso o XML retornado por "LoadUrl" não contiver nós, seu valor será "false", caso contrário, seu valor será "true". Essa variável só será populada depois da chamada do método "Load".
	/// <access mode="public"/>
	/// </summary>
	this.LoadFeedback = null;

	/// <summary>
	/// Define a função que deve ser chamada quando a postagem do formulário for concluída.
	/// <access mode="public"/>
	/// </summary>
	this.callback = function() {};

	/// <summary>
	/// Define a função que deve ser chamada quando a o carregamento do formulário for concluído.
	/// <access mode="public"/>
	/// </summary>
	this.LoadCallback = function() {};

	/// <summary>
	/// Define as validações que devem ser efetuadas antes de o formulário ser postado.
	/// <access mode="private"/>
	/// </summary>
	this.Validators = Array();

	// <summary>
	/// Posta o formulário na URL especificada.
	/// <access mode="public"/>
	/// <returns>Retorna "true" se o formulário for postado com sucesso, e "false" caso haja algum erro de validação.</returns>
	/// </summary>
	this.DoPost = function() {
		if (this.Form == null && this.StrPost == null)
			throw new Error("Formulário não especificado.");
		else if (this.PostUrl == null)
			throw new Error("URL não especificada.");


		try
		{
			this.ValidarForm();

			var strPost = "";
			if (this.Form != null)
				strPost = this.GerarStrPost(this.Form);
			else if (this.StrPost != null)
				strPost = this.StrPost;
			http = this.ObterHttp();
			http.onreadystatechange = this.callback;
			http.open("POST",this.PostUrl,true);
			http.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
			http.send(strPost);
			return http;
		}
		catch (e)
		{
			alert(e.message);
			return false;
		}

	}

	/// <summary>
	/// Carrega o formulário de acordo com o XML carregado a partir de "LoadUrl";
	/// <access mode="public"/>
	/// </summary>
	this.Load = function() {
		InstanciaForm = this;
		var strPost = null;
		var method = "GET";

		http = this.ObterHttp();
		http.onreadystatechange = this.PopularForm;
		if (this.LoadForm != null) {
			strPost = this.GerarStrPost(this.LoadForm);
			method = "POST";
		}
		http.open(method,this.LoadUrl,true);
		if (this.LoadForm != null)
			http.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
		http.send(strPost);
		
	}

	/// <summary>
	/// Função de callback para popular o formulário após a execução de "Load".
	/// <access mode="private"/>
	/// </summary>
	this.PopularForm = function() {
		if (http.readyState == 4)
		{
			var xml = http.responseXML;

			// Verifica se o XML retornou algum erro
			var erro = false;
			if (InstanciaForm.NoErro.length == 0) {
				erro = xml.getElementsByTagName(InstanciaForm.NoRegistro)[0].childNodes.length > 0;
			} else {
				for (var i=0;i<InstanciaForm.NoErro.length;i++) {
					if (xml.getElementsByTagName(InstanciaForm.NoErro[i]).length > 0) {
						erro = true;
					}
				}
			}

			// Popula o formulário, caso o XML não tenha retornado nenhum erro.			
			if(!erro) {
				InstanciaForm.LoadFeedback = true;
				for(var i=0;i<InstanciaForm.Form.elements.length;i++) {
					var el = InstanciaForm.Form.elements[i];
					var valor = "";
					try {
						valor = xml.getElementsByTagName(el.name)[0].childNodes[0].nodeValue;
					} catch (e) {}
					if (el.type == "text" || el.type == "hidden" || el.type == "password") {
						el.value = valor;
					} else if (el.type == "select-multiple" || el.type == "select-one") {
						for(var j=0;j<el.options.length;j++) {
							if (el.options[j].value == valor)
								el.options[j].selected = true;
						}
					} else if (el.type == "radio") {
						if(el.value == valor)
							el.checked = true;
					} else if (el.type == "checkbox") {
						valor = xml.getElementsByTagName(el.name);
						for(var j=0;j<valor.length;j++) {
							if (valor[j].childNodes[0].nodeValue == el.value)
								el.checked = true;
						}
					}
				}
			} else {
				InstanciaForm.LoadFeedback = false;
			}
			InstanciaForm.LoadCallback();
		}
	}

	// <summary>
	/// Posta o formulário na URL especificada.
	/// <access mode="public"/>
	/// </summary>
	this.ValidarForm = function() {
		for(var i=0;i<this.Validators.length;i++) {
			var val = this.Validators[i];
			if (val.TipoValidacao == "vazio") {
				switch(this.Form.elements[val.NomeCampo].type) {
				    case "textarea":
					case "text":
						if (this.Form.elements[val.NomeCampo].value == "")
							throw new Error(val.Mensagem);
						break;
					case "password":
						if (this.Form.elements[val.NomeCampo].value == "")
							throw new Error(val.Mensagem);
						break;
					case "select-one":
						if (this.Form.elements[val.NomeCampo].options[this.Form.elements[val.NomeCampo].selectedIndex].value == "")
							throw new Error(val.Mensagem);
						break;
					case "select-multiple":
						if (this.Form.elements[val.NomeCampo].options[this.Form.elements[val.NomeCampo].selectedIndex].value == "")
							throw new Error(val.Mensagem);
						break;
					default:
						var valido = false;
						for(var j=0;j<this.Form.elements[val.NomeCampo].length;j++) {
							if (this.Form.elements[val.NomeCampo][j].checked == true) {
								valido = true;
								break;
							}
						}
						if (!valido)
							throw new Error(val.Mensagem);
						break;
				}
			} else if (val.TipoValidacao == "email") {
				if (!this.isEmail(this.Form.elements[val.NomeCampo].value))
					throw new Error(val.Mensagem);
			} else if (val.TipoValidacao == "cpf") {
				if (!this.isCPF(this.Form.elements[val.NomeCampo].value))
					throw new Error(val.Mensagem);
			} else if (val.TipoValidacao == "cnpj") {
				if (!this.isCNPJ(this.Form.elements[val.NomeCampo].value))
					throw new Error(val.Mensagem);
			}
		}
	}

	// <summary>
	/// Adiciona uma validação para ser efetuada antes de o formulário a ser postado.
	/// <access mode="public"/>
	/// <param name="NomeCampo">Nome do campo do formulário para ser validado.</param>
	/// <param name="Mensagem">Mensagem de erro para ser mostrada caso a validação não seja bem sucedida.</param>
	/// <param name="TipoValidacao">Tipo da validação para ser efetuada. O valor para esse parametro pode ser um dos abaixo:
	/// vazio: Não permite que o valor campo seja vazio.
	/// email: Verifica se o valor do campo é um endereço e-mail válido.
	/// cpf: Verifica se o valor do campo é um cpf válido.
	/// cnpj: Verifica se o valor do campo é um cnpj válido.
	/// </summary>
	this.AdicionarValidacao = function (NomeCampo, Mensagem, TipoValidacao) {
		var validacao = {
			"NomeCampo" : NomeCampo,
			"Mensagem" : Mensagem,
			"TipoValidacao" : TipoValidacao
		};
		this.Validators[this.Validators.length] = validacao;
	}

	// <summary>
	/// Obtem uma instância do objeto responsável por efetuar as chamadas Ajax.
	/// <access mode="private"/>
	/// </summary>
	this.ObterHttp = function() {
		var http = false;

		try  {
			http = new ActiveXObject("Msxml2.XMLHTTP");
		}
		catch (e) {
			try {
				http = new ActiveXObject("Microsoft.XMLHTTP");
			}
			catch (E) {
				http = false;
			}
		}

		if (!http && typeof XMLHttpRequest!='undefined') {
			http = new XMLHttpRequest();
		}

		return http;
	}

	// <summary>
	/// Verifica se o valor do parâmetro é um e-mail válido.
	/// <access mode="private"/>
	/// <param name="email">Valor para verificar se é um e-mail válido.</param>
	/// </summary>
	this.isEmail = function(email){
		var suportado = 0;
		EmailInv = false;

		if (window.RegExp) {
			var tempReg = /a/;
			if (tempReg.test("a")){
				suportado = 1;
			}
		}

		if (!suportado){
			if(((email.indexOf(".") <= 0) || (email.indexOf("@") <= 0)) || ((email.lastIndexOf(".") == (email.length - 1)) || (email.lastIndexOf("@") == (email.length -1)))){
				EmailInv = true;
			}
		}else{
			var tmp1 = /(@.*@)|(\.\.)|(@\.)|(^\.)/;
			var tmp2 = /^.+@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/;
			if(tmp1.test(email) || !tmp2.test(email)){
				EmailInv = true;
			}
		}

		return !EmailInv;
	}

	// <summary>
	/// Verifica se o valor do parâmetro é um CPF válido.
	/// <access mode="private"/>
	/// <param name="cpf">Valor para verificar se é um CPF válido.</param>
	/// </summary>
	this.isCPF = function(cpf){
		var cpf_limpo = cpf.replace(/\D/g,"");
		if (cpf_limpo.length !=11)
			return false;

		var soma = 0;
		for (var i=0; i<9; i++)
			soma += parseInt(cpf_limpo.charAt(i)) * (10-i);

		if (soma == 0)
			return false;

		primeiro_digito = 11 - soma % 11;
		if (primeiro_digito > 9)
			primeiro_digito = 0;

		if (cpf_limpo.substr(9,1) != primeiro_digito)
			return false;

		var soma = 0;
		for (var i=0; i<10; i++)
			soma += parseInt(cpf_limpo.charAt(i)) * (11-i);

		var segundo_digito = 11 - soma % 11;

		if (segundo_digito > 9)
			segundo_digito = 0;
		if (cpf_limpo.substr(10,1) != segundo_digito)
			return false;

		return true;
	}

	// <summary>
	/// Verifica se o valor do parâmetro é um CNPJ válido.
	/// <access mode="private"/>
	/// <param name="CNPJ">Valor para verificar se é um CNPJ válido.</param>
	/// </summary>
	this.isCNPJ = function(CNPJ) {
		while(/\D/.test(CNPJ))
			CNPJ = CNPJ.replace(/\D/,"");
		if (CNPJ.length < 14)
			return false;
		var a = [];
		var b = new Number;
		var c = [6,5,4,3,2,9,8,7,6,5,4,3,2];
		for (i=0; i<12; i++){
			a[i] = CNPJ.charAt(i);
			b += a[i] * c[i+1];
		}
		if ((x = b % 11) < 2) {
			a[12] = 0
		} else {
			a[12] = 11-x
		}
		b = 0;
		for (y=0; y<13; y++) {
			b += (a[y] * c[y]);
		}
		if ((x = b % 11) < 2) {
			a[13] = 0;
		} else {
			a[13] = 11-x;
		}

		if ((CNPJ.charAt(12) != a[12]) || (CNPJ.charAt(13) != a[13]))
			return false;
		else
			return true;
	}

	// <summary>
	/// Obtém uma string no formato "nome1=valor1&nome2=valor2", contendo os campos formulário passado como parâmetro.
	/// <access mode="private"/>
	/// <param name="form">Objeto "<form>" para buscar os campos e gerar a string.</param>
	/// </summary>
	this.GerarStrPost = function(form) {
		var strPost = "";
		for(var i=0;i<form.elements.length;i++)
		{
			var el = form.elements[i];
			if (el.name == "__VIEWSTATE")
			    continue;
			if (el.type == "text" || el.type == "hidden" || el.type == "password" || el.type == "textarea")
				strPost += el.id +"="+ escape(el.value) +"&";
			else if (el.type == "select-one" || el.type == "select-multiple") 
			{
			    if (el.selectedIndex > -1)
				    strPost += el.id +"="+ escape(el.options[el.selectedIndex].value) +"&";
			}
			else if (el.type == "radio" || el.type == "checkbox") {
				if(el.checked)
					strPost += el.id + (this.PostUrl.indexOf(".php") > 0 ? "[]" : "") +"="+ escape(el.value) +"&";
			}
		}

		return strPost;
	}
}