/*****************************************************
* Copyright Aaron Latham-Anderson 2008
* breaks.nz@gmail.com
*
* ---------------------------------------
* KNOWN BUGS / ISSUES
* ---------------------------------------

  Bug: sending another request before the last one has returned prevents the first one from being retreivable
  Workaround: make another instance of the ajax object and use that if the requests are going to be fired in quick succession
  
  Issue: can't send args to ajax_proc_response / set load status
  Possible solution: add in extensible args or an arg array ro response funcs
  
* ---------------------------------------
* USAGE
* ---------------------------------------

<script language="javascript" type="text/javascript" src="ajax.js"></script>
<script language="javascript" type="text/javascript">
//Response Function
function ajax_proc_response(r_type,r_data){
	switch(r_type){
		case "xml":
			var r_xml = r_data;
			var names = r_xml.getElementsByTagName("name");
			alert(names[i].childNodes[0].nodeValue)
		break;
		case "txt":
			var r_txt = r_data;
			alert('r_txt')
		break;
	}
}
//Load Status function
function set_load_status(loading){
	if(loading){
		document.getElementById("load").innerHTML = "<img src='img/load.gif' class='mA' /> Loading...";
	}else{
		document.getElementById("load").innerHTML = "";	
	}										
}
//No Data function
function ajax_no_data(){
	alert('No Data')
}
//prototype in the functions
ajax.prototype.ajax_proc_response = ajax_proc_response
ajax.prototype.set_load_status = set_load_status
ajax.prototype.ajax_no_data = ajax_no_data

//Function syntax
//constructor: ajax(params, script_name, s_method, output_format, response_func_name, load_func_name, no_data_func_name)
//sendrequest: ajax.sendData(params, script_name, s_method, output_format, response_func_name, load_func_name, no_data_func_name)

//define new object
var a = new ajax();

</script>

*****************************************************/

function ajax(params, script_name, s_method, output_format, response_func_name, load_func_name, no_data_func_name){
	
	//use constructor function ajax(script_name, params) / assignment of object.script_name or object.params / object.sendData(script_name,params) to set params and script name
	//all other constructor args have defaults
	
	//init vars	/ set defaults	
	typeof output_format 		!= 'undefined' ? this.output_format 		= output_format 		: this.output_format 		= 'xml';
	typeof response_func_name 	!= 'undefined' ? this.response_func_name 	= response_func_name 	: this.response_func_name 	= 'ajax_proc_response';
	typeof load_func_name 		!= 'undefined' ? this.load_func_name	 	= load_func_name	 	: this.load_func_name	 	= 'set_load_status';
	typeof no_data_func_name 	!= 'undefined' ? this.no_data_func_name	 	= no_data_func_name	 	: this.no_data_func_name	= 'ajax_no_data';			
	typeof s_method 			!= 'undefined' ? this.s_method 				= s_method 				: this.s_method 			= 'get';
	typeof script_name 			!= 'undefined' ? this.script_name 			= script_name 			: this.script_name 			= null;
	typeof params 				!= 'undefined' ? this.params 				= params 				: this.params 				= null;
	
	this.sendData = function(params, script_name, s_method, output_format, response_func_name, load_func_name, no_data_func_name){
		
		//Allow for changing input / ouput vars on the fly
		if(typeof params 				!= 'undefined'){this.params 			= params}
		if(typeof script_name 			!= 'undefined'){this.script_name 		= script_name}
		if(typeof s_method 				!= 'undefined'){this.s_method 			= s_method}
		if(typeof response_func_name	!= 'undefined'){this.response_func_name = response_func_name}
		if(typeof load_func_name		!= 'undefined'){this.load_func_name 	= load_func_name}
		if(typeof no_data_func_name		!= 'undefined'){this.no_data_func_name 	= no_data_func_name}						
		if(typeof output_format			!= 'undefined'){this.output_format 		= output_format}
		
		this.xmlHttp=this.GetXmlHttpObject()
		
		if (this.xmlHttp==null){
			alert ("Browser does not support HTTP Request")
			return
		}
		
		// transfers the stateChanged method to the onreadystatechange method
		this.xmlHttp.onreadystatechange=this.stateChangedWrap
	
		if(this.s_method=='post'){
			//Post Syntax
			this.xmlHttp.open("POST",this.script_name,true);
			//Additional Headers for POST Method
			this.xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			this.xmlHttp.setRequestHeader("Content-length", this.params.length);
			this.xmlHttp.setRequestHeader("Connection", "close");
			//POST params
			this.xmlHttp.send(this.params);
		}else{
			//Get Synatx
			this.xmlHttp.open("GET",this.script_name+"?"+this.params,true);
			this.xmlHttp.send(null);		
		}
	}
	
	// State change function - because onreadystatechange is executed as a method of the xmlHttp object - when passing the function to xmlHttp thisArg is converted into an object reference by the wrapper function - curry()
	this.stateChanged = function(thisArg){
		if ((thisArg.xmlHttp.readyState==4 || thisArg.xmlHttp.readyState=="complete")&& thisArg.xmlHttp.status == 200){
			if(thisArg.xmlHttp.responseText==null || thisArg.xmlHttp.responseText==""){
				thisArg[thisArg.no_data_func_name]();
				thisArg[thisArg.load_func_name](false);										
				
			}else{
				if(thisArg.output_format=='txt'){
					//Process Txt Response
					thisArg[thisArg.response_func_name]('txt',thisArg.xmlHttp.responseText)
					thisArg[thisArg.load_func_name](false);										
				}else{
					//Process XML Response
					thisArg[thisArg.response_func_name]('xml',thisArg.xmlHttp.responseXML)
					thisArg[thisArg.load_func_name](false);								
				}
			}
		}else{
			thisArg[thisArg.load_func_name](true);
		}
	}
	
	// Wrapper for this.stateChanged()
	// Transferring the supporting properties and methods to the xmlHttp object doesn't work in IE - the object is locked
	// this.xmlHttp.onreadystatechange=this.stateChanged.call(this) doesn't execute more than once properly as it sets this.xmlHttp.onreadystatechange to the return value of this.stateChanged.call(this) rather than transferring the method
	// setting onreadystatechange to an anonymous function executes 'this' as xmlHttp	as you would expect with a object method assignment		
	// Solution was to make a wrapper function that fills in the thisArg var of the call method every execution - ie a curried function 
	//Currying is a javascript method where we bind the first n1 arguments in a function and return a function with n2-n1 arguments
	
	this.curry = function(fn, arg){
		return function() {
			fn.call(null, arg)
		}
	}
	
	this.stateChangedWrap = this.curry(this.stateChanged,this)
	
	this.GetXmlHttpObject = function(){ 
		var objXMLHttp=null
		if (window.XMLHttpRequest){
			objXMLHttp=new XMLHttpRequest()
		}
		else if (window.ActiveXObject){
			objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP")
		}
		return objXMLHttp
	}	
}