/*======================================================================================
	
	Module:		CAjax

	Purpose:	Handle remote XML documents

	Author:		Sky Stebnicki, sky.stebnicki@aereus.com
				Copyright (c) 2006 Aereus Corporation. All rights reserved.
	
	Usage:		// Create ajax object
				ajax = new CAjax();
				// Set callback once xml is loaded
				ajax.onload = function()
				{
					// Get first node
					var root = this.m_firstNode;
					var num = root.getNumChildren();
					for (i = 0; i < num; i++)
					{
						// Get child nodes
						var model = root.getChildNode(i);
						if (model.m_name == "mynode")
						{
							document.write(model.m_name);
							document.write(model.m_text);
						}
					}
				};
				// Get xml file	
				ajax.exec("/path/to/xml.xml");

======================================================================================*/

// Define constants
// -----------------------------------------------------------
var AJAX_POST = 1;
var AJAX_GET = 2;

// Node Types
var AJAX_NODE_TEXT = 3;
var AJAX_NODE_HTML = 1;

// Debugging
var AJAX_TRACE_RESPONSE = false;


/***********************************************************************************
 *
 *	Class: 		CAjax
 *
 *	Purpose:	Encapsulate AJAX functionality
 *
 ***********************************************************************************/
function CAjax()
{
	this.m_xmlLocal = null;
	this.m_response = null;
	this.m_firstNode = null;
	this.m_method = AJAX_GET;
	
	if (window.XMLHttpRequest) 
	{
		this.m_xmlLocal = new XMLHttpRequest();
	}
	else
	{
		var msxmlhttp = new Array('Msxml2.XMLHTTP.5.0',
								  'Msxml2.XMLHTTP.4.0',
								  'Msxml2.XMLHTTP.3.0',
								  'Msxml2.XMLHTTP',
								  'Microsoft.XMLHTTP');

		for (var i = 0; i < msxmlhttp.length; i++) 
		{
			try 
			{
				this.m_xmlLocal = new ActiveXObject(msxmlhttp[i]);
			} 
			catch (e) 
			{
				this.m_xmlLocal = null;
			}
		}
	}

}

/***********************************************************************************
 *
 *	Function: 	exec
 *
 *	Purpose:	Send request to server using http get
 *
 *	Arguements:	url   - string: path to xml document
 *				async - bool: defaults to true. Be careful if set to false, it can
 *						hang the browser until the xml doc is loaded. Users generally
 *						don't like that too much.
 *
 ***********************************************************************************/
CAjax.prototype.exec = function (url, args, async)
{
    var is_async = (async != null) ? async : true;
	var post_data = null;
	var xmlLocal = this.m_xmlLocal;
	var objref = this;
    
    // If this is a syncronus request we don't need callback
    if (is_async == true)
    {
	    function inlineLoaded()
	    {
		    if (xmlLocal && xmlLocal.readyState == 4) 
		    {
			    if (xmlLocal.status == 200) 
			    {
					// If a valid xml document has not been loaded then exit gracefully
					if (xmlLocal.responseXML == null)
					{
						if (ALib.m_debug == true)
						{
							ALib.trace("XML Failed: " + xmlLocal.responseText);
						}
					}
					else if (xmlLocal.responseXML.documentElement == null)
					{
						if (ALib.m_debug == true)
						{
							ALib.trace("XML Failed: " + xmlLocal.responseText);
						}
					}
					else
					{
						if (AJAX_TRACE_RESPONSE)
							ALib.trace("Response: " + xmlLocal.responseText);

						// Get the parent node
						objref.m_response  = xmlLocal.responseXML.documentElement;
						
						objref.m_firstNode = new CAjaxNode("root", "");
						objref.m_firstNode.m_xmlcld = objref.m_response;

						// Parse tree
						objref.parseNodes(objref.m_firstNode, objref.m_response);
					}
    				
					// Populate text
					objref.responseText = xmlLocal.responseText;
					//ALib.m_debug = true;
					//ALib.trace(objref.responseText);

				    // Call user defined loaded
				    objref.onload();
    				
				    // Clear reference
				    objref = null;
			    }
		    }
	    }

	    this.m_xmlLocal.onreadystatechange = inlineLoaded;
	}	

	if (this.m_method == AJAX_GET)
	{
		this.m_xmlLocal.open("GET", url, is_async);
	}
	else if (this.m_method == AJAX_POST)
	{
		// Get arguments
		var numargs = 0;
		if (typeof args != "undefined" && args!=null)
		{
			numargs = args.length;
			if (numargs)
			{
				// Arguments are pass as {name, value}
				for (i = 0; i < numargs; i++)
				{
					if (post_data) 
						post_data += "&";
					else
						post_data = "";

					post_data += args[i][0] + "=";
					if (typeof args[i][1] != "undefined" && args[i][1] != null)
					 post_data += escape_utf8(args[i][1]);
				}
			}
		}

		this.m_xmlLocal.open("POST", url, is_async);
		//Send the proper header information along with the request
		this.m_xmlLocal.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		//this.m_xmlLocal.setRequestHeader("Content-length", post_data.length);
		//this.m_xmlLocal.setRequestHeader("Connection", "close");
	}

    // If this is a syncronus request we don't need callback
    if (is_async == false)
    {
        this.parseXml();
    }

	this.m_xmlLocal.send(post_data);
}

/***********************************************************************************
 *
 *	Function: 	readyStateChange
 *
 *	Purpose:	Private function that handles readystate change for request.
 *
 ***********************************************************************************/
CAjax.prototype.readyStateChange = function ()
{
	var xmlLocal = this.m_xmlLocal;
	if (xmlLocal && xmlLocal.readyState == 4) 
	{
		if (xmlLocal.status == 200) 
		{
			// Get the parent node
			this.m_response  = xmlLocal.responseXML.documentElement;
			
			this.m_firstNode = new CAjaxNode(this.m_firstNode.nodeName, "");
			this.m_firstNode.m_type = AJAX_NODE_HTML;

			// Parse tree
			this.parseNodes(this.m_firstNode, m_response);
			
			// Call user defined loaded
			this.onload();
		}
	}
}

/***********************************************************************************
 *
 *	Function: 	parseXml
 *
 *	Purpose:	Private function that parses the xml document once it is loaded
 *
 ***********************************************************************************/
CAjax.prototype.parseXml = function ()
{
	var xmlLocal = this.m_xmlLocal;
	alert(xmlLocal);
	alert(xmlLocal.status);
	if (xmlLocal.status == 200) 
    {
	    // Get the parent node
	    this.m_response  = xmlLocal.responseXML.documentElement;
		
	    this.m_firstNode = new CAjaxNode("root", "");

	    // Parse tree
	    this.parseNodes(this.m_firstNode, this.m_response);
    }
}

/***********************************************************************************
 *
 *	Function: 	parseNodes
 *
 *	Purpose:	Private function that parses each xml node
 *
 *	Arguements:	ajax_node   - CAjaxNode: Branch to parse
 *				xml_child	- xml_node: xml object node to copy in CAjaxNode
 *
 ***********************************************************************************/
CAjax.prototype.parseNodes = function (ajax_node, xml_child)
{
	if (!ajax_node || !xml_child)
		return 0;
	
	var iNumSubNodes = ajax_node.m_children.length;
	var children  = xml_child.childNodes;
	var iNewIndex = 0;

	if (children)
	{
		var num = children.length;
		for(var i = 0; i < num; i++)
		{
			var child = children[i];

			// Element Node
			if (child.nodeType == AJAX_NODE_HTML)
			{
				ajax_node.m_children[iNumSubNodes + iNewIndex] = new CAjaxNode(child.nodeName, "");
				ajax_node.m_children[iNumSubNodes + iNewIndex].m_xmlcld = child;

				if (child.childNodes && child.childNodes.length)
					this.parseNodes(ajax_node.m_children[iNumSubNodes + iNewIndex], child);

				iNewIndex++;
			}

			// Text Node
			if (child.nodeType == AJAX_NODE_TEXT)
			{
				ajax_node.m_text += child.nodeValue;
			}
		}
	}
}

/***********************************************************************************
 *
 *	Function: 	onload
 *
 *	Purpose:	This function should be redefined by the public calling procedure.
 *
 ***********************************************************************************/
CAjax.prototype.onload = function ()
{
}


/***********************************************************************************
 *
 *	Class: 		CAjaxNode
 *
 *	Purpose:	This is the node linked list
 *
 *	Arguements:	name	- string: the name of the node
 *				text	- string: the value of the node
 *
 ***********************************************************************************/
function CAjaxNode(name, text)
{
	this.m_name = name;
	this.m_text = text;
	this.m_attributes = new Array();
	this.m_children = new Array();
}

/***********************************************************************************
 *
 *	Function: 	getNumChildren
 *
 *	Purpose:	Get number of children (try not to access vars directly)
 *
 ***********************************************************************************/
CAjaxNode.prototype.getNumChildren = function ()
{
	if (this.m_children)
		return this.m_children.length;
	else
		return 0;
}

/***********************************************************************************
 *
 *	Function: 	getChildNode
 *
 *	Purpose:	Retrieve a node at a specific index
 *
 *	Arguements:	iIndex	- integer: index of node to retrieve
 *
 ***********************************************************************************/
CAjaxNode.prototype.getChildNode = function (iIndex)
{
	return this.m_children[iIndex];
}

/***********************************************************************************
 *
 *	Function: 	getChildNodesByName
 *
 *	Purpose:	Retrieve nodes by name
 *
 *	Arguements:	name	- string: name of nodes to retrieve
 *
 ***********************************************************************************/
CAjaxNode.prototype.getChildNodesByName = function (name)
{
	if (this.m_query_res && this.m_query_res.length)
		delete this.m_query_res;
	
	// mres is used as a temporary storage array of node references
	this.m_query_res = new Array();
	
	// Loop through children looking for 'name'
	var num = this.getNumChildren();
	var iFound = 0;
	for (i = 0; i < num; i++)
	{
		if (this.getChildNode(i).m_name == name)
		{
			this.m_query_res[iFound] = this.getChildNode(i);
			iFound++;
		}
	}

	return this.m_query_res;
}

/***********************************************************************************
 *
 *	Function: 	getChildNodeByName
 *
 *	Purpose:	Retrieve a single node by name
 *
 *	Arguements:	name	- string: name of nodes to retrieve
 *
 ***********************************************************************************/
CAjaxNode.prototype.getChildNodeByName = function (name)
{
	var val = null;

	// Loop through children looking for 'name'
	for (var p = 0; p < this.getNumChildren(); p++)
	{
		if (this.getChildNode(p).m_name == name)
		{
			val = this.getChildNode(p);
			break;
		}
	}

	return val;
}

/***********************************************************************************
 *
 *	Function: 	getChildNodesValByName
 *
 *	Purpose:	Retrieve node value by name. If more than one node with that name
 *				is found it will return the first value. This is best used where
 *				you know for sure there will only be one child node with that name.
 *
 *	Arguements:	name	- string: name of nodes to retrieve
 *
 ***********************************************************************************/
CAjaxNode.prototype.getChildNodeValByName = function(name)
{
	var val = null;

	// Loop through children looking for 'name'
	for (var p = 0; p < this.getNumChildren(); p++)
	{
		if (this.getChildNode(p).m_name == name)
		{
			val = this.getChildNode(p).m_text;
			break;
		}
	}

	return val;
}

/***********************************************************************************
 *
 *	Function: 	getAttribute
 *
 *	Purpose:	Get node attribute by name
 *
 *	Arguements:	name	- string: name of attribute to retrieve
 *
 ***********************************************************************************/
CAjaxNode.prototype.getAttribute = function(name)
{
	return this.m_xmlcld.getAttribute(name);
}
