﻿/*
 * GalloAjaxHandler
 * Clase javascript para gestionar llamadas AJAX;
 * Versión: 0.2
 * Última actualización: 07/01/2009
 * Formato del documento: UTF-8
 *
 * CHANGELOG:
 * 09/01/2009:
 * - Aplicado el patrón Lazy Function Definition en el constructro del XHR.
 * - Aregado el método toString() devolviendo la respuesta.
 * - Ahora se eliminan de memoria las instancias del objeto XHR.
 * - Agregadas mas capturas de errores.
 * - Agregada la referencia a la función de callback como parametro del constructor de clase.
 *
 * 07/01/2009:
 * - Agregada documentación a todo el script
 * - Agregado el método appendRemoteScript
 * 
 * NOTAS:
 * - Para todos los envios de parámetros por url o por post, los valores han de pasarse por escape() para
 *   convertir caracteres que puedan entrar en conflicto con el formato de la url.
 * 
 * ORIGEN:
 * El script fue creado entre finales de 2007 y principios de 2008 con la intención de probar tanto AJAX
 * como la buena praxis de programación en javascript, a lo largo de un año lo he recuperado en varias 
 * ocasiones para utilizarlo añadiendo pequeñas mejoras cada vez. Lo utilizo regularmente en mis aplicaciones 
 * web, en las empresas donde he trabajado y suelo recomendarlo como referencia de AJAX.
 */

/*
 * Constructor
 *
 * @cback: referencia a la funcion de callback.
 */
GalloAjaxHandler = function(cback){
	//datos a enviar
	this.data = null;
	
	//datos a recibir
	this.response = null;
	
	/* 
	 * onAjaxError
	 * Se ejecuta al producirse un error.
	 * @code: código del error.
	 * @errorText: mensaje literal del error.
	 * @errObj: objeto relacionado con el error, generalmente exceptions.
	 */
	this.onAjaxError = function(code,errorText,errObj){
		alert('Error ('+code+'): '+errorText+' - '+errObj);
	};
	
	/*
	 * XMLHttpRequestConstruct
	 * Crea y devuelve un objeto XMLHttpRequest en FF o el equivalente en IE.
	 */
	this.XMLHttpRequestConstruct = function(){
		var xhrobj;
		try {
			xhrobj = new XMLHttpRequest;
			this.XMLHttpRequestConstruct = function() {
				return new XMLHttpRequest;
			};
		}
		catch(e) {
			var msxml = [
			'MSXML2.XMLHTTP.3.0',
			'MSXML2.XMLHTTP',
			'Microsoft.XMLHTTP'
			];
			for (var i=0, len = msxml.length; i < len; ++i) {
				try {
					xhrobj = new ActiveXObject(msxml[i]);
					this.XMLHttpRequestConstruct = function() {
						return new ActiveXObject(msxml[i]);
					};
					break;
				}
				catch(e) {
					onAjaxError(1,'Error al crear el XHR',e);
				}
			}
		}
		return xhrobj;
		};
	
	/*
	 * appendRemoteScript
	 * Agrega un elemento script dinamicamente, se utiliza para hacer cross-server AJAX.
	 * El archivo se ejecuta como javascript directamente y no se interpreta como callback.
	 * 
	 * @urlScript: archivo de script remoto.
	 * @idScript: identificador que se utiliza para este script, útil en caso de que se desee 
	 *            eliminar para reemplazarlo por una nueva llamada.
	 * @strData: cadena con los datos que se envian por url.
	 */
	this.appendRemoteScript = function(urlScript,idScript,strData){
		var head = document.getElementsByTagName('head').item(0);
		var anterior = document.getElementById(idScript);
		if (anterior != null){ 
			head.removeChild(anterior);
		}
		script = document.createElement('script');
		script.src = urlScript;
		if(strData != null && strData != ''){
			script.src += '?' + strData;
		}
		script.type = 'text/javascript';
		script.defer = true;
		script.id = idScript;
		void(head.appendChild(script));
	}
	
	/*
	 * handleAjaxRequest
	 * Función de callback que se ejecuta despues de hacer la llamada AJAX, por defecto guarda el 
	 * valor en la propiedad response.
	 * 
	 * @resp: datos de respuesta de la llamada
	 */
	this.handleAjaxRequest = cback;
	
	/*
	 * doAjaxCall
	 * Realiza una llamada AJAX y ejecuta la función de callback, si no se ha definido simplemente 
	 * se guarda en la propiedad response.
	 *
	 * @url: URL a donde se realiza la llamada.
	 * @phmethod: mtodo de llamada (post o get).
	 * @strData: cadena con los datos que se envian.
	 */
	this.doAjaxCall = function(url,pgmethod,strData){
		var ajaxObj = this.XMLHttpRequestConstruct();
		if(ajaxObj){
			var gahObj = this;
			ajaxObj.onreadystatechange = function(){
				if(ajaxObj.readyState == 4){
					gahObj.response = ajaxObj.responseText;
					try{
						gahObj.handleAjaxRequest(ajaxObj.responseText);
						ajaxObj.onreadystatechange = function(){};
						ajaxObj.abort();
						ajaxObj = null;
					} catch(e){
						gahObj.onAjaxError(3,'Error al llamar la función de respuesta',e);	
					}
				}
			};
			ajaxObj.open(pgmethod, url, true);
			//ajaxObj.setRequestHeader('User-Agent', 'AJAX'); 
			ajaxObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			this.data = strData;
			ajaxObj.send(this.data);
		}
	};
	
	/*
	 * parseFormFields
	 * Devuelve una cadena formateada con todos los valores de los campos del tipo indicado como 
	 * parametros de URL para enviarla.
	 * 
	 * @fieldTipe: tipo de campo (input,select,...).
	 * @formElem: objeto HTML del formulario.
	 */
	this.parseFormFields = function(fieldTipe,formElem){
		var fieldsList = formElem.getElementsByTagName(fieldTipe);
		var strData = '';
		var n = '';
		var v = '';
		for(i=0;i<fieldsList.length;i++){
			n = fieldsList[i].name;
			if(n == ''){n = fieldsList[i].id;}
			v = escape(fieldsList[i].value);
			if(n != ''){strData += n+'='+v;}
			if((i+1)<fieldsList.length){strData += '&';}
		}
		
		return strData;
	};
	
	/* 
	 * sendForm
	 * Envia todos los campos 'input' y 'select' de un formulario mediante AJAX a la direccin indicada.
	 *
	 * @url: URL a donde se realiza la llamada.
	 * @phmethod: mtodo de llamada (post o get).
	 * @formElem: objeto HTML del formulario.
	 */
	this.sendForm = function(url,pgmethod,formElem){
		var strData = '';
		strData += this.parseFormFields('input',formElem);
		strData += '&'+this.parseFormFields('select',formElem);
		this.doAjaxCall(url,pgmethod,strData);
	};
	
	this.toString = function(){
		return this.response;	
	};
}
