/*******************************************************************************
v3.0.0
Using the AJAX class:

Required files (including this one):
<script src="ajax_lib/bind.js" type="text/javascript" language="JavaScript"></script>
<script src="ajax_lib/json.js" type="text/javascript" language="JavaScript"></script>
<script src="ajax_lib/ajax.js" type="text/javascript" language="JavaScript"></script>
<script src="ajax_lib/ajax_status_display.js" type="text/javascript" language="JavaScript"></script>

Request:
	Instantiate Class_Ajax()
	Manipulate the data_obj (make it whatever you want to be sent to the server).
	Set the ReadyStateDisplayHandler if needed.
		Note: if you set ReadyStateDisplayHandler to an object method (ie. an instantiated class function)
		then make sure you use the .bind method.  Using bind will force the custom display handler to retain
		its class data. Example: ajax_obj.ReadyStateDisplayHandler = some_display_obj.MyStateHandler.bind(some_display_obj).
	Run the objects SendRequest method.

Response:
	_ReadyStateHandler_JSON is ran upon return from the server.  There are
	two possible types of actions that will be taken upon return (as well as
	doing nothing):

		Special Action: Runs a function with parameters (may be used multiple times within a single response).
		Section Replace: Replaces the innerHTML of a section of the page by id (may be used multiple times within a single response).

	The response is expected to be JSON text representing an object.
	An object (referred to as response_obj) may contain the following data:
		Special Action Structure:
			response_obj.special_action_obj_array: an array of special actions objects.
			response_obj.special_action_obj_array[x].function_name: name of function to be called.
			response_obj.special_action_obj_array[x].param_array: array of params or param objects.
				params as objects
					response_obj.special_action_obj_array[x].param_array[x].value: param value.
					response_obj.special_action_obj_array[x].param_array[x].is_js_var: boolean representing if the param represents a real value or a variable name.
						if is_js_var is true, the value will be evaluated as a variable name.  Otherwise, the value will be used as normal.
				params as non-objects (native types)
					response_obj.special_action_obj_array[x].param_array[x]: the param value.
						Note: is_js_var is not available and the there are no other sub-properties.
		Section Replace Structure:
			response_obj.section_replace_obj_array: an array of section replace objects.
			response_obj.section_replace_obj_array[x].id: id of section to replace.
			response_obj.section_replace_obj_array[x].innerHTML: new innerHTML value.

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

function Class_Ajax()
{
	this._request_obj = null;
	this._in_progress = false;
	this._default_AjaxStatusDisplay = new AjaxStatusDisplay('ajax_status');

	this.data_obj = new Object();
	this.ReadyStateDisplayHandler = this._default_AjaxStatusDisplay.DefaultAjaxStatusDisplay_Handler.bind(this._default_AjaxStatusDisplay);
}
Class_Ajax.prototype._ConvertParameterArrayToURLEncodedString = function(post_parameter_array)
{
	var post_string = "";

	if(typeof(post_parameter_array) == "object" || typeof(post_parameter_array) == "array")
	{
		for(var cur_index=0; cur_index<post_parameter_array.length; cur_index++)
		{
			//alert(post_parameter_array[cur_index][0] + ':::' + post_parameter_array[cur_index][1]);
			post_string += this._StrictURLEncode(post_parameter_array[cur_index][0]) + "=" + this._StrictURLEncode(post_parameter_array[cur_index][1]) + "&";
		}
		return post_string;
	}
	else
	{
		return null;
	}

}
Class_Ajax.prototype._StrictURLEncode = function(in_string)
{
	var out_string = '';

	// cast as string
	in_string = ''+in_string;

	for(var i=0; i<in_string.length; i++)
	{
		var hex_char = in_string.charCodeAt(i).toString(16);

		if(parseInt(hex_char, 16) < 0x0f)
			hex_char = '0' + hex_char;

		out_string += '%'+hex_char;
	}

	return out_string;
}
Class_Ajax.prototype._ReadyStateHandler_JSON = function()
{
	var http_output;

	if(this.ReadyStateDisplayHandler && this.ReadyStateDisplayHandler.constructor.toString().indexOf('Function') != -1)
	{
		try
		{
			this.ReadyStateDisplayHandler(this._request_obj.readyState, this._request_obj.status);
		}
		catch(error)
		{
			try
			{
				this.ReadyStateDisplayHandler(this._request_obj.readyState, null);
			}
			catch(error2)
			{
			}
		}
	}
	// only if req shows "complete"
	if(this._request_obj.readyState == 4)
	{
		// only if "OK"
		if (this._request_obj.status == 200)
		{
			http_output = this._request_obj.responseText;
			if(http_output)
			{
				try
				{
					var ret_obj = eval('('+http_output+')');
					this._ProcessReturnObject(ret_obj);
				}
				catch(err)
				{
					alert("Error - Cannot process javascript:\n\n"+http_output);
				}
			}
		}
		else
		{
			if(!this.ReadyStateDisplayHandler || this.ReadyStateDisplayHandler.constructor.toString().indexOf('Function') == -1)
				alert("There was a problem retrieving the HTTPRequestHandler data:" + this._request_obj.statusText);
		}
		this._in_progress = false;
	}
}
Class_Ajax.prototype._ProcessReturnObject = function(ret_obj)
{
	if(ret_obj)
	{
		if(ret_obj.special_action_obj_array.constructor.toString().indexOf('Array') != -1 && ret_obj.special_action_obj_array.length > 0)
		{
			var arg_string = '';
			
			// iterate through each special action obj
			for(var special_action_obj_count=0; special_action_obj_count<ret_obj.special_action_obj_array.length; special_action_obj_count++)
			{
				// temp store the current special action obj
				var cur_special_action_obj = ret_obj.special_action_obj_array[special_action_obj_count];
				
				if(eval('typeof('+cur_special_action_obj.function_name+')') != 'undefined')
				{
					var temp_param_array = new Array()
					var temp_is_var_array = new Array();
					var eval_string = '';
					var eval_arg_string = '';

					// *** store arguments
					for(var cur_special_action_param_count=0; cur_special_action_param_count<cur_special_action_obj.param_array.length; cur_special_action_param_count++)
					{
						// temp store the current special action param data
						var cur_special_action_param = cur_special_action_obj.param_array[cur_special_action_param_count];
						var cur_special_action_param_value = '';
						var cur_special_action_param_is_js_var = false;

						// *** set values (determine if cur_special_action_param is an object or a native type)
						if(typeof(cur_special_action_param) == 'object')
						{
							cur_special_action_param_value = cur_special_action_param.value
							if(cur_special_action_param.is_js_var)
								cur_special_action_param_is_js_var = true;
						}
						else
						{
							cur_special_action_param_value = cur_special_action_param;
						}

						temp_param_array.push(cur_special_action_param_value);
						temp_is_var_array.push(cur_special_action_param_is_js_var);
					}


					// build argument string
					for(var temp_param_count=0; temp_param_count < temp_param_array.length; temp_param_count++)
					{
						eval_arg_string += (temp_param_count > 0?',':'');

						if(temp_is_var_array[temp_param_count])
						{
							eval_arg_string += temp_param_array[temp_param_count];
						}
						else
						{
							eval_arg_string += 'temp_param_array['+temp_param_count+']';
						}
					}

					// build eval
					eval_string += cur_special_action_obj.function_name+'('+eval_arg_string+')';
					eval(eval_string);
				}
			}
		}
       		if(ret_obj.section_replace_obj_array.constructor.toString().indexOf('Array') != -1 && ret_obj.section_replace_obj_array.length > 0)
		{
			// iterate through each section replace objects
			for(var section_replace_obj_count=0; section_replace_obj_count<ret_obj.section_replace_obj_array.length; section_replace_obj_count++)
			{
				var section_obj = document.getElementById(ret_obj.section_replace_obj_array[section_replace_obj_count].id);
				if(section_obj)
					section_obj.innerHTML = ret_obj.section_replace_obj_array[section_replace_obj_count].innerHTML;
			}
		}

	}
}

// SendRequest Method
// params:
//      url: the url that the request will be sent to.
//      force_sync: optional boolean value to force a synchronous/asynchronous request (true/false respectively).  asynchronous by default.
//      post_parameter_array: optional multi-dimentional array of key/value pairs that will ultimately get posted to the server during the request.
//	      array structure example:
//			post_parameter_array.push(array('myKey','myValue'))
// returns:
//      true if a request is attempted (this._in_progress is false) other wise false (this._in_progress is true).
//      false if this._in_progress is true
Class_Ajax.prototype.SendRequest = function(url, force_sync, post_parameter_array)
{
	if(!this._in_progress)
	{
		this._in_progress = true;
		
		if(force_sync != true)
			force_sync = false;

		// append a question mark if it isn't already in the url
		if(url.indexOf('?') == -1)
			url += '?';

		// append date to avoid pages being cached
		cDate = new Date;
		url = url + "&" + cDate.getTime() + "=1&";

		// branch for native XMLHttpRequest object
		if (window.XMLHttpRequest)
		{
			this._request_obj = new XMLHttpRequest();
	 	}
		else if (window.ActiveXObject)      // branch for IE/Windows ActiveX version
			this._request_obj = new ActiveXObject("Microsoft.XMLHTTP");

		if(this._request_obj)
		{
			// add initial value to post data
			var post_string = 'ajax=1&';

			// convert data obj to post string if needed
			if(this.data_obj)
		     {
		     	var s=JSONstring.make(this.data_obj);
				post_string += 'json=' + this._StrictURLEncode(s) + "&";
			}

			// add custom post vars if needed
			if(post_parameter_array)
			{
				post_string += this._ConvertParameterArrayToURLEncodedString(post_parameter_array);
			}

			// set handler
			this._request_obj.onreadystatechange = this._ReadyStateHandler_JSON.bind(this);

			// open connection and send data
			this._request_obj.open("POST", url, !force_sync);
			this._request_obj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			this._request_obj.send(post_string);
		}
		return true;
	}
	return false;
}