/*
	$Id: ajax.js 4294 2007-12-18 04:02:57Z mjhorne $

	This file is part of ayudaCMS
	Copyright 2007: ayuda IT
	http://www.ayuda.com.au

	For licencing details, please see LICENCE.txt in the ayudaCMS directory.
*/

	/**
	 * Usage:
	 *
	 * var ajax = new AJAX(callback, callbackArgs);
	 * ajax.request(url);
	 *
	 * var sjax = new SJAX();
	 * var xml = sjax.request(url);
	 */

	/*************************************************************************/
	/** Base "AJAX" class													**/
	/*************************************************************************/

	function JAX()
	{
		this.synchronous = false;
		this.method = "GET";
		this.payload = null;
		this.xmlHTTPRequest = this.makeXMLHTTPRequest();
		this.responseFormat = "XML";
	}


	/** 
	 * Is the ajax call synchronous (blocking)
	 */

	JAX.prototype.isSynchronous = function()
	{
		return this.synchronous;
	}


	/** 
	 * Set the POST payload
	 */

	JAX.prototype.setPayload = function(value)
	{
		this.payload = value;
	}


	/**
	 * Return the POST payload
	 */

	JAX.prototype.getPayload = function()
	{
		return this.payload;
	}


	/** 
	 * In what format should I expect the response
	 */

	JAX.prototype.setResponseFormat = function(value)
	{
		this.responseFormat = value;
	}

	/** 
	 * Set the data method
	 */

	JAX.prototype.setMethod = function(value)
	{
		this.method = value;
	}


	/**
	 * Return the data method
	 */

	JAX.prototype.getMethod = function()
	{
		return this.method;
	}


	/**
	 * Create the XMLHttpRequest object
	 */

	JAX.prototype.makeXMLHTTPRequest = function() 
	{
		if (window.XMLHttpRequest)  	// Most normal people
		{ 
			return xmlHTTPRequest = new XMLHttpRequest();
		} 
		else if (window.ActiveXObject)  // IE 6
		{
			return xmlHTTPRequest = new ActiveXObject("Microsoft.XMLHTTP");
		}
		else
		{
			return null;
		}
	}


	/**
	 * Return name value pairs of the form data
	 */

	JAX.prototype.getPayloadFromForm = function(id, button)
	{
		var element = document.getElementById(id);
		var data = "";

		if(element)
		{
			for(i = 0; i < element.childNodes.length; i++)
			{
				if(element.childNodes[i].nodeName == "FIELDSET")
				{
					for(j = 0; j < element.childNodes[i].childNodes.length; j++)
					{
						var nodeName = element.childNodes[i].childNodes[j].nodeName;
						var type = element.childNodes[i].childNodes[j].type;

						if((nodeName == "TEXTAREA") || 
						   ((nodeName == "INPUT") && (type == "text")))
						{
							var value = element.childNodes[i].childNodes[j].value;
							var name = element.childNodes[i].childNodes[j].name;

							if(data != "")
							{
								data += "&";
							}
							data += name + "=" + value;
						}
						else if((nodeName == "INPUT") && (type == "submit"))
						{
							var value = element.childNodes[i].childNodes[j].value;
							var name = element.childNodes[i].childNodes[j].name;

							if(name == button)
							{
								if(data != "")
								{
									data += "&";
								}
								data += name + "=" + value;
							}
						}
					}
				}
			}
		}
		return data;
	}


	/*************************************************************************/
	/** Asynchronous "AJAX"													**/
	/*************************************************************************/

	function AJAX(callback, callbackArgs)
	{
		this.callback = callback;

		if(!callbackArgs)
		{
			callbackArgs = new Array();
		}

		this.callbackArgs = callbackArgs;
	}

	AJAX.prototype = new JAX;


	/** 
	 * Set the callback function
	 */

	AJAX.prototype.setCallback = function(value)
	{
		this.callback = value;
	}


	/** 
	 * Set the callback arguments
	 */

	AJAX.prototype.setCallbackArgs = function(value)
	{
		this.callbackArgs = value;
	}

	/**
	 * Make the request, this function will call the callback function 
	 * with the given args when the request is completed
	 */

	AJAX.prototype.request = function(url)
	{
		if(this.xmlHTTPRequest)
		{
			var callback = this.callback;
			var callbackArgs = this.callbackArgs;
			var xmlHTTPRequest = this.xmlHTTPRequest;
			var responseFormat = this.responseFormat;

			this.xmlHTTPRequest.onreadystatechange = function()
			{
				AJAX.prototype.processStateChange(xmlHTTPRequest, callback, callbackArgs, responseFormat);
			}
			
			try
			{
				this.xmlHTTPRequest.open(this.method, url, !this.synchronous);
			}
			catch(e)
			{
				return;
			}

			try
			{
				if(this.method == "POST")
				{
					this.xmlHTTPRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
					this.xmlHTTPRequest.setRequestHeader("Content-length", this.payload.length);
					this.xmlHTTPRequest.setRequestHeader("Connection", "close");
						this.xmlHTTPRequest.send(this.payload);
				}
				else
				{
					this.xmlHTTPRequest.send(null);
				}
			}
			catch(e)
			{
				return;
			}
		}
	}


	/**
	 * Function called when the state of the request changes. Calls the callback 
	 * function on successfull completion of the request
	 */

	AJAX.prototype.processStateChange = function(xmlHTTPRequest, callback, callbackArgs, responseFormat)  
	{
		if(xmlHTTPRequest)
		{
			if (xmlHTTPRequest.readyState == 4)  	// Request complete
			{
				try 
				{
					if (xmlHTTPRequest.status == 200) 	// OK response
					{
						if(callback)
						{
							if(responseFormat == "XML")
							{
								callbackArgs['response'] = xmlHTTPRequest.responseXML;
							}
							else if((responseFormat = "XHTML") || (responseFormat = "JSON"))
							{
								callbackArgs['response'] = xmlHTTPRequest.responseText;
							}

							callback(callbackArgs);
						}
					}
				}
				catch(e)
				{
					return;
				}
			}
		}
	}


	/*************************************************************************/
	/** Synchronous "AJAX"													**/
	/*************************************************************************/

	function SJAX()
	{
		this.synchronous = true;
	}

	SJAX.prototype = new JAX;


	/**
	 * Make the request, this function will return the XML document of the response
	 */

	SJAX.prototype.request = function(url)
	{
		if(this.xmlHTTPRequest)
		{
			try
			{
				this.xmlHTTPRequest.open(this.method, url, !this.synchronous);
			}
			catch(e)
			{
				return null;
			}

			if(this.method == "POST")
			{
				this.xmlHTTPRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
				this.xmlHTTPRequest.setRequestHeader("Content-length", this.payload.length);
				this.xmlHTTPRequest.setRequestHeader("Connection", "close");
				this.xmlHTTPRequest.send(this.payload);
			}
			else
			{
				this.xmlHTTPRequest.send(null);
			}
			
			if((this.responseFormat == "XML") && this.xmlHTTPRequest.responseXML)
			{
				return this.xmlHTTPRequest.responseXML;
			}
			else if(((this.responseFormat == "XHTML") || (this.responseFormat == "JSON")) && this.xmlHTTPRequest.responseText)
			{
				return this.xmlHTTPRequest.responseText;
			}
			else
			{
				return null;
			}
		}
	}


	/*************************************************************************/
	/** XML Utility class													**/
	/*************************************************************************/

	function XMLUtils() {}

	/**
	 * Return the root element of an XML response
	 */

	XMLUtils.prototype.getRootElement = function(response, tagName)
	{	
		if(response)
		{
   			response = response.documentElement;

			if(response)
			{
				return response;
			}
		}
		return null;
	}

	
	/**
	 * Return the value of of an object with the given tag name and parent reference
	 */

	XMLUtils.prototype.getElementValue = function(parentElement, tagName)
	{
		if(parentElement)
		{
			var elements = parentElement.getElementsByTagName(tagName);

			if(elements)
			{
				var element = elements[0];
				if(element)
				{
					var elementValue = "";
					for(i = 0; i < element.childNodes.length; i++)
					{
						elementValue += element.childNodes[i].nodeValue;
					}
					return elementValue;
				}
			}
		}
		return null;
	}


	/**
	 * Return the list of the children of a particular element
	 */

	XMLUtils.prototype.getElementValues = function(parentElement, tagName)
	{
		var elementsArray = new Array();
		if(parentElement)
		{
			var elements = parentElement.getElementsByTagName(tagName);

			if(elements)
			{
				var element = elements[0];
				if(element)
				{
					for(i = 0; i < element.childNodes.length; i++)
					{
						if(element.childNodes[i].nodeName != "#text")
						{
							var elementArray = new Array();

							for(j = 0; j < element.childNodes[i].childNodes.length; j++)
							{
								if(element.childNodes[i].childNodes[j].nodeName != "#text")
								{
									if(element.childNodes[i].childNodes[j].nodeName != "")
									{
										var elementValue = "";
										for(k = 0; k < element.childNodes[i].childNodes[j].childNodes.length; k++)
										{
											elementValue += element.childNodes[i].childNodes[j].childNodes[k];
										}
										elementArray[element.childNodes[i].childNodes[j].nodeName] = elementValue;
									}
								}
							}
							elementsArray[elementsArray.length] = elementArray;
						}
					}
				}
			}
		}
		return elementsArray;
	}


	/*************************************************************************/
	/** Base64 Util class													**/
	/**																		**/ 
	/** Adapted from http://www.webtoolkit.info/javascript-base64.html		**/
	/*************************************************************************/

	function Base64Utils() {}

	Base64Utils.prototype.keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

	Base64Utils.prototype.encode =  function (input) 
	{
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		input = Base64Utils.prototype.utf8_encode(input);

		while(i < input.length)
		{
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if(isNaN(chr2))
			{
				enc3 = enc4 = 64;
			}
			else if (isNaN(chr3)) 
			{
				enc4 = 64;
			}

			output = output +
			Base64Utils.prototype.keyStr.charAt(enc1) + Base64Utils.prototype.keyStr.charAt(enc2) +
			Base64Utils.prototype.keyStr.charAt(enc3) + Base64Utils.prototype.keyStr.charAt(enc4);

		}

		return output;
	}


	Base64Utils.prototype.decode = function(input)
	{
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;

		if(input) 
		{
			input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
		}
		else
		{
			input = "";
		}

		while(i < input.length)
		{
			enc1 = Base64Utils.prototype.keyStr.indexOf(input.charAt(i++));
			enc2 = Base64Utils.prototype.keyStr.indexOf(input.charAt(i++));
			enc3 = Base64Utils.prototype.keyStr.indexOf(input.charAt(i++));
			enc4 = Base64Utils.prototype.keyStr.indexOf(input.charAt(i++));

			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;

			output = output + String.fromCharCode(chr1);

			if (enc3 != 64) 
			{
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64) 
			{
				output = output + String.fromCharCode(chr3);
			}

		}

		return Base64Utils.prototype.utf8_decode(output);
	}


	Base64Utils.prototype.utf8_encode = function (string) 
	{
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for(var n = 0; n < string.length; n++) 
		{
			var c = string.charCodeAt(n);

			if (c < 128) 
			{
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048))
			{
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else
			{
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
		}

		return utftext;
	}


	Base64Utils.prototype.utf8_decode = function (utftext) 
	{
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while(i < utftext.length ) 
		{
			c = utftext.charCodeAt(i);

			if (c < 128) 
			{
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) 
			{
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else 
			{
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
		}

		return string;
	}
