/*
* Javascript pseudo-class to build assynchronous HTTP requests to webservers.
*
* You must inform the HTTP request Method and URL on AjaxObject's constructor,
* and, optionally, you can inform your Javascript function witch will receive
* and process the webserver response (callback function) and an array of 
* arguments that will be assigned to each function parameter.
*
* This callback function is necessary also if you (whe think so) intend to 
* process the server's response to add funcionality to your application.
* Without it, your script will not perceive anything absolutely.
* It accept parameters that you have to pass as an array, but simply consider
* as parameter on your callback function.
* 
* You can, optionally, pass request values (as key/value pairs) or request form 
* input objects that will be encoded and can be had access by the webserver.
* 
* Also it's possible to define a function witch will handle the XMLHttpRequest
* object's onreadystatechange event and another function witch will be called
* when something gone wrong, for examplo, request for Page Not Found (404).
* 
* To send an Ajax request, call load() method on your AjaxObject object.
*
* Usage examples:
* <code>
*	getObj = new AjaxObject("GET", "ajaxtest.php"); // request for some URL on GET method - try it with POST too
*	getObj.setCallBackFunction(callback); // defining the callback function
*	getObj.setLoadingFunction(loading); // setting loading function
*	getObj.addRequestValue('foo', 'bar'); // some request parameter
* 	getObj.addRequestObject(document.forms['aForm'].elements['anElement']); // other request parameter
* 	getObj.addAllRequestObjects(document.forms['anotherForm']); // pass all enabled form elements to request
* 	getObj.load(); // sending the assychronous request
* </code>
* 
* @author Jose Berardo <berardo@especializa.com.br>
* @author Eduardo Lundgren <braeker@gmail.com>
* @version 1.0
* @copyleft Jose Berardo - Especializa Treinamentos - www.especializa.com.br
* @copyleft Eduardo Lundgren - Especializa Treinamentos - www.especializa.com.br
*/ 
// class AjaxObject {

	var AjaxObject;
		AjaxObject.INVALID_REQUEST_OBJECT = 1;

	/*
	* AjaxObject Constructor
	* 
	* @param method Request method (GET or POST)
	* @param url url requested on load() function
	* @param xmlResponse enables xml format to response
	* @param callback function inform your script function whitch will receive the server response
	* @see load()
	*/ 
	function AjaxObject(method, url) {
		// setting attributes
		this.method                 = method;
		this.url                    = url;
		this.requestObjects         = new Array();
		this.requestValues          = new Array();
		this.responseFormat         = (AjaxObject.arguments[2]) ? "xml" : "text";

		// setting methods
		this.addRequestObject       = addRequestObject;
		this.addRequestValue        = addRequestValue;
		this.setCallBackFunction    = setCallBackFunction;
		this.setLoadingFunction     = setLoadingFunction;
		this.setExceptionFunction   = setExceptionFunction;
		this.buildRequestString     = buildRequestString;
		this.addAllRequestObjects   = addAllRequestObjects;
		this.load                   = load;

		// setting callback function
		this.callBackFunction       = AjaxObject.arguments[3];
		this.callBackArguments      = AjaxObject.arguments[4];
	}
	
	/*
	* AjaxObject's method that adds an input object to pass in the request
	* 
	* @param requestObject
	*/ 
	function addRequestObject(requestObject) {
		this.requestObjects[this.requestObjects.length] = requestObject;
	}
	
	/*
	* AjaxObject's method that adds a key-value pair to pass in the request as a parameter
	* 
	* @param requestObject
	*/ 
	function addRequestValue(requestKey, requestValue) {
		this.requestValues[this.requestValues.length] = new Array(requestKey, requestValue);
	}

	/*
	* AjaxObject's method that sets the Javascript's function
	* assigned to be onreadstatechange event listener
	* 
	* @param callBackFunction
	* @param arrayArguments Some arguments used by the method
	*/ 
	function setCallBackFunction(callBackFunction) {
		this.callBackFunction	= callBackFunction;
		this.callBackArguments	= setCallBackFunction.arguments[1];
	}
	
	/*
	* AjaxObject's method that sets the Javascript's function
	* called within the load() to change loading status
	* 
	* @param loadingFunction
	* @param loadingFunction Your loading function
	*/ 
	function setLoadingFunction(loadingFunction) {
		this.loadingFunction	= loadingFunction;
	}
	
	/*
	* AjaxObject's method that sets the Javascript's function
	* called within the load() when someting gone wrong
	* 
	* @param exceptionFunction
	* @param exceptionFunction Your exception function
	*/ 
	function setExceptionFunction(exceptionFunction) {
		this.exceptionFunction	= exceptionFunction;
	}
	
	/*
	* AjaxObject's method that processes requestObjects and requestValues
	* arrays and builds the return string used on GET's URL or POST's request parameters
	* 
	* @return the new URL string
	*/ 
	function buildRequestString() {
		var regexArray = /.*\[\]/;
		
		returnString = ((this.url.indexOf('?') > 0) ? "&" : "?") + "requestTime=" + new Date().getTime();
		for (x = 0; x < this.requestValues.length; x++) {
			returnString += '&' + this.requestValues[x][0] + '=' + encodeURIComponent(this.requestValues[x][1]);
		}
		for (x = 0; x < this.requestObjects.length; x++) {
			try {
				if (this.requestObjects[x].type == 'select-multiple') {	

					for (var i = 0; i < this.requestObjects[x].options.length; i++) {
						if (this.requestObjects[x].options[i].selected == true) {
							newName = regexArray.test(this.requestObjects[x].name) ? this.requestObjects[x].name : this.requestObjects[x].name + '[]';
							returnString += '&' + newName + '=' + encodeURIComponent(this.requestObjects[x].options[i].value); 
						}
					}
				}
				else{
					returnString += '&' + this.requestObjects[x].name + '=' + encodeURIComponent(this.requestObjects[x].value);
				}
			} catch (e) {
				if (this.exceptionFunction) {
					for (x in e) {alert(x)}
					this.exceptionFunction(AjaxObject.INVALID_REQUEST_OBJECT,
										   'The object ' + x + ' passed is not a valid form input object!');
				}
			}
		}
		return returnString;
	}

	/*
	* AjaxObject's method that adds all fields of a form passed as parameter
	* 
	* @param aForm HTML form object
	*/ 
	function addAllRequestObjects(aForm) {
		for (x = 0; x < aForm.elements.length; x++) {
			if (!aForm.elements[x].disabled) {
				if ((aForm.elements[x].type != 'checkbox' && aForm.elements[x].type != 'radio')
						|| aForm.elements[x].checked){
					this.addRequestObject(aForm.elements[x]);
				}
			}
		}
	}

	/*
	* AjaxObject's method that loads the new thread of XMLHttpRequest
	*/ 
	function load() {
		xmlHttpRequest = new XmlHttpRequestObject(this);
		returnString  = this.buildRequestString();
		if (this.method.toUpperCase() == "POST") {
			xmlHttpRequest.thread.open("POST", this.url, true);
			xmlHttpRequest.thread.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
			xmlHttpRequest.thread.setRequestHeader('Content-Length',returnString.length);
		} else if (this.method.toUpperCase() == "GET") {
			this.url += this.buildRequestString();
			xmlHttpRequest.thread.open("GET", this.url, true);
		}
		xmlHttpRequest.thread.send(returnString);

		xmlHttpRequest.thread.onreadystatechange = function() {
			if (xmlHttpRequest.thread.readyState == 0) { state = 'uninitialized' }
			if (xmlHttpRequest.thread.readyState == 1) { state = 'loading' }
			if (xmlHttpRequest.thread.readyState == 2) { state = 'loaded' }
			if (xmlHttpRequest.thread.readyState == 3) { state = 'interactive' }
			if (xmlHttpRequest.thread.readyState == 4) { state = 'complete' }
			if (xmlHttpRequest.ajaxObject.loadingFunction) xmlHttpRequest.ajaxObject.loadingFunction(state);
			
			if (xmlHttpRequest.thread.readyState == 4) {
				if (xmlHttpRequest.thread.status == 200) {
					params = "";
					if (xmlHttpRequest.ajaxObject.callBackArguments) {
						for (x = 0; x < xmlHttpRequest.ajaxObject.callBackArguments.length; x++) {
							params += ", xmlHttpRequest.ajaxObject.callBackArguments[" + x + "]";
						}
					}
					if (xmlHttpRequest.ajaxObject.callBackFunction) {
						if (xmlHttpRequest.ajaxObject.responseFormat == 'text')
							eval("xmlHttpRequest.ajaxObject.callBackFunction( unescape(xmlHttpRequest.thread.responseText)" + params + " )");
						else {
							eval("xmlHttpRequest.ajaxObject.callBackFunction( unescape(xmlHttpRequest.thread.responseXML)" + params + " )");
						}
					}
				} else {
					if (xmlHttpRequest.ajaxObject.exceptionFunction) {
						eval("xmlHttpRequest.ajaxObject.exceptionFunction(xmlHttpRequest.thread.status, xmlHttpRequest.thread.statusText)");
					}
				}
			}
			
		}
	}

// }

/*
* Private class to help AjaxObject in its job
*
*/
// private class XmlHttpRequestObject {

	/*
	* XmlHttpRequestObject Constructor
	* 
	* @param callBackFunction function loaded when response status code is 200 - OK
	* @param callBackArguments array of arguments passed to the callback function
	* @param loadingFunction function called on every change of request state
	*/ 
	function XmlHttpRequestObject(ajaxObject) {
		try { this.thread = new XMLHttpRequest(); }
		catch(e) { try {this.thread = new ActiveXObject("Microsoft.XMLHTTP");} catch(e) {} }
		
		this.ajaxObject	= ajaxObject;
	}

// }
