/* Camada de abstração do ajax

Versão 1.1 - 23/07/2011

Recebe como parâmetro um objeto com as seguintes propriedades (todas são opcionais):
- url: o endereço alvo. Pode conter informações GET
- funcao: a função que será executada após uma resposta do servidor.
  Ela irá receber como parâmetro o retorno do servidor
- dados: os dados a serem enviados via GET ou POST.
  Pode ser uma string na forma "var=valor&var2=valor2" ou um objeto com propriedades
- metodo: define o método de comunicação ("GET" ou "POST")
- timeout: define o tempo máximo de espera até retornar um erro (0 significa sem limite)
- retorno: define o tipo de retorno esperado ("text", "xml" ou "json")
- cache: define se permite um retorno direto do cache do browser
- funcaoErro: função que será chamada em caso de erro
- funcaoTimeout: funcao que será executada quando a conexão extrapolar o tempo limite

Retorna o objeto XMLHttpRequest

Nas funções, this se refere ao objeto window

Os valores padrões são:
{"url" : "",
"funcao" : function () {},
"dados" : "",
"metodo" : "GET",
"timeout" : 30,
"retorno" : "text",
"cache" : false,
"funcaoErro" : function () {alert("Erro na conexão")},
"funcaoTimeout" : this.funcaoErro}
*/

function Ajax(opcoes) {
	// Recebe os parâmetros
	var temp, i;
	opcoes = opcoes || {};
	opcoes.url = opcoes.url || "";
	opcoes.funcao = opcoes.funcao || function () {};
	opcoes.dados = opcoes.dados || "";
	opcoes.metodo = (opcoes.metodo || "get").toUpperCase();
	opcoes.timeout = opcoes.timeout===undefined ? 30 : opcoes.timeout;
	opcoes.retorno = (opcoes.retorno || "text").toLowerCase();
	opcoes.cache = Boolean(opcoes.cache);
	opcoes.funcaoErro = opcoes.funcaoErro || function () {alert("Erro na conexão")};
	opcoes.funcaoTimeout = opcoes.funcaoTimeout || opcoes.funcaoErro;
	
	// Prepara os dados
	if (typeof(opcoes.dados) == "object") {
		temp = [];
		for (i in opcoes.dados) {
			if (opcoes.dados.hasOwnProperty(i)) {
				temp.push(encodeURIComponent(i)+"="+encodeURIComponent(opcoes.dados[i]));
			}
		}
		opcoes.dados = temp.join("&");
	}
	
	// Prepara a url
	if (!opcoes.cache) {
		opcoes.url = opcoes.url+(opcoes.url.indexOf("?")==-1 ? "?" : "&")+"noCache="+(new Date).getTime();
	}
	if (opcoes.metodo == "GET" && opcoes.dados != "") {
		opcoes.url = opcoes.url+(opcoes.url.indexOf("?")==-1 ? "?" : "&")+opcoes.dados;
	}
	
	// Cria o objeto
	var ajax = new XMLHttpRequest();
	ajax.opcoes = opcoes;
	ajax.open(opcoes.metodo, opcoes.url, true);
	ajax.onreadystatechange = function () {
		var resposta, erro = false;
		if (this.readyState == 4) {
			clearInterval(this.intervalo);
			if (this.status == 200) {
				try {
					resposta = this.opcoes.retorno=="text" ? this.responseText : (this.opcoes.retorno=="xml" ? this.responseXML : JSON.parse(this.responseText));
				} catch (e) {
					// Erro no XML ou JSON
					this.opcoes.funcaoErro.call(null);
					erro = true;
				}
				if (!erro) {
					this.opcoes.funcao.call(null, resposta);
				}
			} else {
				this.opcoes.funcaoErro.call(null);
			}
		}
	};
	
	// Cria o timeout
	ajax.timeout = function () {
		ajax.onreadystatechange = null;
		this.opcoes.funcaoTimeout();
		this.abort();
	};
	if (opcoes.timeout) {
		ajax.intervalo = setTimeout(function () {ajax.timeout();}, opcoes.timeout*1000);
	}
	
	// Envia o pedido
	if (opcoes.metodo == "POST") {
		ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		ajax.send(opcoes.dados);
	} else {
		ajax.send();
	}
	
	// Retorna
	return ajax;
};
