6/*****************************************************************************
SHAJAX - Ajax Framework
Copyright (C) 2006  Daniel Dalgo

This library is free software; you can redistribute it and/or modify it under 
the terms of the GNU Lesser General Public License as published by the 
Free Software Foundation; either version 2.1 of the License, or (at your option) 
any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY 
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along 
with this library; if not, write to the Free Software Foundation, Inc., 59 
Temple Place, Suite 330, Boston, MA 02111-1307 USA 
*****************************************************************************
*****************************************************************************
*****************************  CONTACT **************************************
							   -------
PLEASE, CONTACT US AT:
www.7bits.com.ec
ddalgo@7bits.com.ec
Quito, Ecuador
*****************************************************************************
***************************  EXTRA FILES ************************************
							 -----------
Also, please refer to the helper files annexed
- "updates.txt" 
- "bugs.txt"
for info about new features or bugs in this library, and also use it 
for post your own information. Contact us using the media referred above, in
the CONTACT section.

All the files referred here, in the EXTRA FILES section, and the new info
posted in those files are also covered for the license here mentioned.
*****************************************************************************/

// Javascript

/** Dependencias
 *
 * 	SHAJAX.config.js
 */

/** Esta clase permite controlar todas las capacidades al utilizar la tecnología AJAX
 */
function SHAJAX(url, metodo, mymeType, datos, fntControlDatos, fntError){
	/** Variable que maneja las conexiones en background (solo lectura)
	 */
	this._XMLHTTPRequestObject=null;	
	
	/** Variable privada que almacena la respuesta devuelta por el servidor en 
	 * forma de texto (solo lectura)
	 */
	this._respuestaTexto=null;
	/** Variable privada que almacena la respuesta devuelta por el servidor en 
	 * forma de un documento XML (solo lectura)
	 */
	this._respuestaXML=null;
	/** Variable privada que almacena todos los response headers devueltos (solo lectura). 
	 */
	this._respuestaHeaders=new Array();
	/** Variable privada que almacena la url del archivo a ser utilizado
	 */
	this._url=url;
	/** Variable privada que almacena el tipo de método a ser utilizado
	 */
	this._metodo=metodo || shAJAXConfig.defaultMetodo;
	/** Variable privada que almacena el tipo de los datos a ser manejados
	 */
	this._mimeType=mymeType;
	/** Variable privada que almacena los datos a ser enviados
	 */
	this._datos=datos;
	/** Variable privada que almacena una referencia a la función que será la encargada
	 * de controlar los datos de devueltos. Esta función solo deberá recibir un parámetro,
	 * que debe ser para el objeto XMLHTTPRequest
	 */
	this._fntControlDatos=fntControlDatos;
	/** Arreglo que almacena todo el conjunto de parámetros extras que se le pueden pasar a la
	 * función _fntControlDatos que se haya definido
	 */
	this._fntControlDatosParametros=new Array();
	/** Variable privada que almacena una referencia a la función que será la encargada
	 * de desplegar la información necesaria en caso de error. Esta función solo deberá recibir 
	 * un parámetro, que debe ser para el objeto XMLHTTPRequest
	 */
	this._fntError=fntError;
	/** Arreglo que almacena todo el conjunto de parámetros extras que se le pueden pasar a la
	 * función _fntError que se haya definido
	 */
	this._fntErrorParametros=new Array();
}

/** Función encargada de devolver la respuesta obtenida en forma de texto
 */
SHAJAX.prototype.getRespuestaTexto=function(){
	return this._respuestaTexto;
}

/** Función encargada de devolver la respuesta obtenida en forma de documento XML
 */
SHAJAX.prototype.getRespuestaXML=function(){
	return this._respuestaXML;
}

/** Función encargada de devolver todos los headers obtenidos junto con sus keys, en un array, dentro del
 * que cada key es indice del arreglo y cada value es valor para dicho índice en el arreglo
 */
SHAJAX.prototype.getRespuestaHeaders=function(){
	var arr=new Array();
	
	for(var i=0; i<this._respuestaHeaders.length; i++){
		arr[this._respuestaHeaders[i][0]]=this._respuestaHeaders[i][1];
	}
	
	return arr;
}

/** Función encargada de devolver un valor de header especificado. Recibe dos parámetros, el primero es
 * el índice, es decir ya sea el nombre del header ("Content-Type" por ejemplo) o un indice basado en 0
 * (3 por ejemplo). El comportamiento normal será el de buscar al elemento primero por el índice como
 * si este fuese un nombre ("Content-Type" por ejemplo) y de no encontrarlo, tomar al valor índice pasado
 * como si fuese un número y así buscarlo (3 por ejemplo). El segundo parámetro controla la dirección de este
 * análisis. Sus posibles valores son:
 * => '+' 	(por defecto, analiza índice primero como String y luego como Number), 
 * => '-'	(analiza índice primero como Number y luego como String), 
 * => 'S'	(analiza solo como que índice fuera String), 
 * => 'N'	(analiza solo como que índice fuera Number)
 * Si el valor enviado no es ninguno de los listados, la función adopta a "+" como símbolo
 */
SHAJAX.prototype.getRespuestaHeader=function(indice, flag){
	/** Inicializar variables
	 */
	var flag2=null;
	
	if(typeof flag=="undefined" || !flag){
		flag2='+';
	} else {
		if(!(flag=='+' || flag=='-' || flag=='S' || flag=='N')){
			flag2='+';
		} else {
			flag2=flag;
		}
	}
	/** FIN: Inicializar variables
	 */
	var aux=null;
	var retorno=null;
	
	/** Obtiene para trabajar, un arreglo del formato devuelto por getRespuestaHeaders
	 */
	var arr=this.getRespuestaHeaders();
	
	if(flag2=='+'){
		/** Se va a buscar al índice primero como String y luego como Number
		 */
		aux=new String(indice);
		retorno=arr[aux];
		if(typeof retorno=="undefined"){
			aux=new Number(indice);
			retorno=this._respuestaHeaders[aux][1];
		}
	}

	if(flag2=='-'){
		/** Se va a buscar al índice primero como Number y luego como String
		 */
		aux=new Number(indice);
		retorno=this._respuestaHeaders[aux][1];
		if(typeof retorno=="undefined"){
			aux=new String(indice);
			retorno=arr[aux];
		}
	}

	if(flag2=='S'){
		/** Se va a buscar al índice solo como String
		 */
		aux=new String(indice);
		retorno=arr[aux];
	}

	if(flag2=='N'){
		/** Se va a buscar al índice solo como Number
		 */
		aux=new Number(indice);
		retorno=this._respuestaHeaders[aux][1];
	}
	
	return retorno;
}

/** Función encargada de devolver un arreglo con todos los keys contenidos en el arreglo
 */
SHAJAX.prototype.getRespuestaHeaderKeys=function(){
	var arr=new Array();
	
	for(var i=0; i<this._respuestaHeaders.length; i++){
		arr[arr.length]=this._respuestaHeaders[i][0];
	}
	
	return arr;
}

/** Función encargada de devolver un key específico de acuerdo a su posición en el arreglo interno
 */
SHAJAX.prototype.getRespuestaHeaderKey=function(indice){
        var indice2=new Number(indice);

	return this._respuestaHeaders[indice2][0];
}

/** Función encargada de devolver la url a ser manejada
 */
SHAJAX.prototype.getURL=function(){
	return this._url;
}

/** Función encargada de establecer la url a ser manejada
 */
SHAJAX.prototype.setURL=function(url){
	this._url=url;
}

/** Función encargada de devolver el método a ser utilizado
 */
SHAJAX.prototype.getMetodo=function(){
	return this._metodo;
}

/** Función encargada de establecer el método a ser utilizado
 */
SHAJAX.prototype.setMetodo=function(metodo){
	this._metodo=metodo || shAJAXConfig.defaultMetodo;
}

/** Función encargada de devolver el tipo de contenido que se está manejando
 */
SHAJAX.prototype.getMimeType=function(){
	return this._mimeType;
}

/** Función encargada de establecer el tipo de contenido que se está manejando
 */
SHAJAX.prototype.setMimeType=function(mimeType){
	this._mimeType=mimeType;
}

/** Función encargada de devolver los datos que se enviarán
 */
SHAJAX.prototype.getDatos=function(){
	return this._datos;
}

/** Función encargada de establecer los datos que se enviarán
 */
SHAJAX.prototype.setDatos=function(datos){
	this._datos=datos;
}

/** Función encargada de devolver una referencia a la función encargada de manejar
 * la situación una vez que se obtenga la respuesta del servidor
 */
SHAJAX.prototype.getFntControlDatos=function(){
	return this._fntControlDatos;
}

/** Función encargada de establecer que función será la encargada de manejar
 * la situación una vez que se obtenga la respuesta del servidor
 */
SHAJAX.prototype.setFntControlDatos=function(fntControlDatos){
	this._fntControlDatos=fntControlDatos;
}

/** Función encargada de devolver un arreglo con todo el conjunto de parámetros
 * extras a ser manejados por la función _fntControlDatos definida
 */
SHAJAX.prototype.getFntControlDatosParametros=function(){
	return this._fntControlDatosParametros;
}

/** Función encargada de establecer todo el conjunto extra de parámetros a ser
 * tomados en cuenta por la función _fntControlDatos definida, a más del ya mencionado
 * XMLHttpObject.
 * Esta función recibe los valores en un arreglo. Lo que se hace es crear un arreglo 
 * interno de una posición y allí depositar lo que se envíe
 */
SHAJAX.prototype.setFntControlDatosParametros=function(arregloParametros){
	this._fntControlDatosParametros=null;
	
	/** Solo trabaja si se ha enviado algo
	 */
	if(typeof arregloParametros!="undefined" && arregloParametros){
		this._fntControlDatosParametros=new Array();
		this._fntControlDatosParametros[this._fntControlDatosParametros.length]=arregloParametros;
	}
}

/** Función encargada de devolver una referencia a la función encargada de manejar
 * la situación una vez que se obtenga una respuesta errónea desde el servidor
 */
SHAJAX.prototype.getFntError=function(){
	return this._fntError;
}

/** Función encargada de establecer que función será la encargada de manejar
 * la situación una vez que se obtenga un error desde el servidor
 */
SHAJAX.prototype.setFntError=function(fntError){
	this._fntError=fntError;
}

/** Función encargada de devolver un arreglo con todo el conjunto de parámetros
 * extras a ser manejados por la función _fntError definida
 */

SHAJAX.prototype.getFntErrorParametros=function(){
	return this._fntControlDatosParametros;
}

/** Función encargada de establecer todo el conjunto extra de parámetros a ser
 * tomados en cuenta por la función _fntError definida, a más del ya mencionado
 * XMLHttpObject.
 * Esta función recibe los valores en un arreglo. Lo que se hace es crear un arreglo 
 * interno de una posición y allí depositar lo que se envíe
 */
SHAJAX.prototype.setFntErrorParametros=function(arregloParametros){
	this._fntErrorParametros=null;
	
	/** Solo trabaja si se ha aneviado algo
	 */
	if(typeof arregloParametros!="undefined" && arregloParametros){
		this._fntErrorParametros=new Array();
		this._fntErrorParametros[this._fntErrorParametros.length]=arregloParametros;
	}
}

/** Función privada encargada de llenar de información el contenedor de headers
 */
SHAJAX.prototype._doCrearArregloDeHeaders=function(){
	if(this._XMLHTTPRequestObject){
		this._respuestaHeaders=new Array();
		
		var szHeaders=this._XMLHTTPRequestObject.getAllResponseHeaders();
		if(szHeaders && szHeaders.length){
			var arreglo=new Array();
			
			arreglo=szHeaders.split(/\n/);
			for(var i=0; i<arreglo.length; i++){
				if(arreglo[i]){
					var nPos=arreglo[i].indexOf(":");
					
					this._respuestaHeaders[this._respuestaHeaders.length]=new Array(arreglo[i].substring(0, nPos), arreglo[i].substring(nPos+1, arreglo[i].length+1));
				}
			}
		}
	}
}

/** Función privada encargada de inicializar todas las variables contenedoras de resultados, antes de 
 * intentar volverlas a llenar con nuevos datos
 */
SHAJAX.prototype._doInitVariables=function(){
	if(window.XMLHttpRequest){
		this._XMLHTTPRequestObject=new XMLHttpRequest();
	} else {
		if(window.ActiveXObject){
			this._XMLHTTPRequestObject=new ActiveXObject("Microsoft.XMLHTTP");
		}
	}
	
	this._respuestaTexto=null;
	this._respuestaXML=null;
	this._respuestaHeaders=new Array();
}

/** Función encargada de ejecutar la interacción con el servidor. 
 * La variable async sirve para indicar si la petición será asincrónica (true) o sincrónica . Por defecto
 * será asincrónica
 * La variable encoding sirve para indicar si antes de hacer la llamada al servidor con los datos, 
 * los caracteres de estos deban primero ser pasados a una forma entendible por los servidores web 
 * (codificar caracteres como " ", "ñ", "è", etc). Por defecto se realizará "encoding"
 * La variable usuario sirve para identificar al usuario del sitio al que se desea acceder. Por defecto null.
 * La variable clave sirve para identificar la clave del usuario del sitio al que se desea acceder. Por defecto null.
*/
SHAJAX.prototype.doEjecutar=function(async, encoding, usuario, clave){
	/** Inicialización de las variables contenedoras de la casa
	 */
	this._doInitVariables();
	
	/** Validación de datos de entrada
	 */
	var async2=true;
	if(typeof async!="undefined"){
		var tempAsync=new Boolean(async);
		async2=tempAsync.valueOf();
	}
	
	var encoding2=true;
	if(typeof encoding!="undefined"){
		var tempEncoding=new Boolean(encoding);
		encoding2=tempEncoding.valueOf();
	}

	var usuario2=null;
	if(typeof usuario!="undefined"){
		usuario2=usuario;
	}
	
	var clave2=null;
	if(typeof clave!="undefined"){
		clave2=clave;
	}
	/** FIN: Validación de datos de entrada
	 */

	if(this._XMLHTTPRequestObject){
		this._XMLHTTPRequestObject.open(this._metodo, this._url, async2, usuario2, clave2);
		if(this._mimeType){
			/** Si se ha establecido content type, se lo anexa. Esto por lo general solo deberá estar
			 * establecido para cuando el método es POST
			 */
			this._XMLHTTPRequestObject.setRequestHeader("Content-Type", this._mimeType);
		}

		/** Se trabaja con oThis que apunta, para poder, dentro de la función que se está
		 * definiendo para onreadystatechange, manejar las funciones de SHAJAX, ya que si
		 * dentro de la función que se define para onreadystatechange se pone this directamnete,
		 * este hará referencia con this a dicha función.
		 * Además, se realiza una verificación explícita acerca de si se está haciendo una llamada
		 * sincrónica o asincrónica. A pesar que async2 debe de controlar el hecho de como se lance
		 * la función de onreadystatechange, al ser establecida en el open del objeto XMLHttpRequest, 
		 * se noto que en algunos browser, a pesar de ponerse como sincrónica la función, aún se seguía
		 * lanzando como si fuese asincrónica
		 */
		var oThis=this;
		this._XMLHTTPRequestObject.onreadystatechange=function(){;};

		if(async2){
			this._XMLHTTPRequestObject.onreadystatechange=function(){
				oThis._myOnReadyStateChange();
			}
		}
		
		var datos=this._datos;
		if(datos && encoding2){
			datos=encodeURI(datos);
		}
		this._XMLHTTPRequestObject.send(datos);

		if(!async2){
			/** Cuando la llamada es sincrónica se llama a esta
			 * función explícitamente
			 */
			this._myOnReadyStateChange();
		}
	}
}

/** Función privada, en donde se llevan a cabo las tareas con el XMLHttpRequest Object
 */
SHAJAX.prototype._myOnReadyStateChange=function(){
    if(this._XMLHTTPRequestObject.readyState==4){
            if(this._XMLHTTPRequestObject.status==200){
                    this._respuestaTexto=this._XMLHTTPRequestObject.responseText;
                    this._respuestaXML=this._XMLHTTPRequestObject.responseXML;
                    this._doCrearArregloDeHeaders(this._XMLHTTPRequestObject);

                    if(this._fntControlDatos){
						/** Hace la llamada a la función definida
						 */
						if(!this._fntControlDatosParametros){
							this._fntControlDatos(this._XMLHTTPRequestObject);
						} else {
							this._fntControlDatos(this._XMLHTTPRequestObject, this._fntControlDatosParametros);
						}
                    } 
            } else {
                    /** Si es que existe función de control de errores se la llama
                     * a esa, sino se ejecutan las tareas predeterminadas
                     */
                    if(this._fntError){
						/** Hace la llamada a la función definida
						 */
						if(!this._fntErrorParametros){
							this._fntError(this._XMLHTTPRequestObject);
						} else {
							this._fntError(this._XMLHTTPRequestObject, this._fntErrorParametros);
						}
                    } else {
						alert("No se ha podido cargar la informacion. ERROR '"+this._XMLHTTPRequestObject.status+"'");
                    }
            }
            delete this._XMLHTTPRequestObject;
            this._XMLHTTPRequestObject=null;
    }
}

