/*
	Manage the page load requests, and replace the content
	of the given container id.
	
	This script will perform async javascript page requests in the
	background and update the requested container body.
*/
	// turn debugging alert info on/off.
	var debug = false;

	// all of our page content requests.
	var Requests = new Array ();
	
	// set the polling interval for updating returned calls.
	var pollInterval = 10;
	
	// boolean flag telling us that we recently had a return.
	var recentReturn = false;
	
	// set our last error string.
	var error = "";
	
	// set our abandoned all flag.
	var abandoned = false;

	// last response text value
	var responseText = "";

	// lock param for adding requests.
	var lock_request = false;

	//setTimeout ('returnCheck()', pollInterval);
	
	// get request with callback.
	function asyncGetRequest (url, callback, data) {
	    var request = new RequestContainer ("", url, "", false, "get", data, callback);
	    AddLocation (request);
	}

	// post request with callback.
	function asyncPostRequest (url, callback, data) {
		if (! data)
			data = "";
	    var request = new RequestContainer ("", url, "", false, "post", data, callback);
	    AddLocation (request);
	}

	function asyncContainer (url, id) {
		var default_contents = (arguments[2] == undefined)? "" : arguments[2];
		var request = new RequestContainer (id, url, default_contents, false, "get", null, null);
		AddLocation (request);
	}

	/*
		add a new request to the Requests container setting everything up.
		request: a valid RequestContainer with fields set up properly.
	*/
	function AddLocation (request) {
		if (lock_request) {
			setTimeout ("AddLocation (" + request + ");", 200);
		}

		lock_request = true;

		try {
			// preset our container content.
			updateContainer (request);

			// set our next available default index.
			var index = Requests.length + 1;

			/*
				find a new location in between start/end of the Requests.
				This is done to keep the Requests array as compact as possible.
				filling in gaps when available.
			*/
//			for (var i = 0; i < Requests.length; i++) {
//				// found an available location.
//				if (! Requests[i]) {
//					index = i;
//					break;
//				}
//			}

			// insert our request
			Requests[index] = request;

			// actually take care of fireing the request off in the background.
			fireRequest (index);

		} catch (E) {
			if (debug)
				alert ("AddLocation ('" + request + "');\n" + E);
				
			// error
			return false;
		}

		lock_request = false;
		
		// all is well
		return true;
	}
	
	/*
		Remove the requested site from our Requests, and abandon the
		call if necessary.
		
		container_id: the container for which the url contents are to be 
			loaded into.
		url: the location of the container contents.
		type: The type of request to perform.
	*/
	function RemoveLocation (request) {
		try {
			
		} catch (E) {
			if (debug)
				alert ("RemoveLocation ('" + request + "');\n" + E);
			
			// error
			return false;
		}
		
		// all is well 
		return true;
	}
	
	/*
		Traverse the Requests array checking each request 
		for return status. If none are ready to return 
		do not set anything yet. If we have requests that
		have finished we can update the container contents.
	*/
	function processAllRequests () {
		var request;
		
		// for each current request.
		for (var i = 0; i < Requests.length; i++) {
			try {
				// get the current request.
				request = Requests[i];
	
				// check the return status and update our containers if needed.
				if (request && request.xmlhttp.readyState==4) {
				    if (request.xmlhttp.status==200) {
					// set our response text string.
					responseText = request.xmlhttp.responseText;

					// only set contents if we need to.
					if (request.container) {
						var div = document.getElementById (request.container);
						div.innerHTML = responseText;
					}
					
					// remove ourselves from the Requests array.
					Requests.splice (i, 1);

					// perform our callback if necessaray.
					if (request.callback != "") {
						eval (request.callback + "(request);");
					}
				} else {
					// TODO: finsih up errorback call.
				}
					// set to null for gc.
					request = null;
					Requests[i] = null;
				}	
			} catch (E) {
				if (debug) {
					alert ("processAllRequests () failed: " + E);
				}
					
				// error
				return false;
			}
		}
		
		// all is well return true.
		return true;
	}
	
	/*
		Check if the given location/container id pair already 
		exists as a valid call.
	*/
	function locationExists (request) {
		var requested;

		try {
			for (var i = 0; i < Requests.length; i++) {
				// check Requests.
				requested = Requests[i];
				
				// check if we have the correct request.
				if (RequestCompare (requested, request))
					return true;
			}
		} catch (E) {
			if (debug)
				alert ("error: locationExists (" + request + ")\n" + E);
		}
		
		// no request found.
		return false;
	}
	
	/*
		Wrapper for the locationExists ().
	*/
	function requestExists (request) {
		return locationExists (request);
	}	

	/*
		Set the container_id contents the the given 
		content value.
		
		container_id: the id of the container to replace its innerHTML value.
		content: new contents or appended contents.
		append: append to current contents?
	*/
	function setContainerContents (request, contents) {
		// check that request is an object.
		if (!request || !request.container_id) {
			// set our error.
			setError ("invalid request object found: setContainerContents");
			return false;
		}
	
		// attempt to update our container contents.
		try {
			// get our container object.
			var c = document.getElementById (request.container_id);
			
			// check the append flag for contents.
			if (append == true)
				c.innerHTML += request.content;
			else
				c.innerHTML = request.content;
		
		} catch (E) {
			if (debug) 
				alert ("setContainerContent (): " + E);

			// error
			return false;
		}
		
		// all is well
		return true;
	}
	
	/*
		Update the container contents to reflect the values set
		in the given request.
	*/
	function updateContainer (request) {
		// return our set status to our caller.
		var contents = false;
		if (request.container) {
			contents = setContainerContents (request.container, request.content);
		}

		// return our status of setting the container.		
		if (contents === false)
			return false;
		else
			return true;
	}
	
	/* 
		Set the container contents to the new value.
		
		id: a string id value in the dom.
		content: the new content for the container (if it exists).
	*/
	function setContainerContentsById (id, content) {
		try {
			var d = document.getElementById (id);
			
			// set our new content.
			d.innerHTML = content;
		} catch (E) {
			if (debug)
				alert ("error: setContainerContentsById ('" + id + "', '" + content + "');");
				
			// error
			return false;
		}
	}
	
	/*
		Get the current container contents.
	*/
	function getContainerContents (id) {
		var content = "";
	
		try {
			var d = document.getElementById (id);
			content = d.innerHTML;
		} catch (E) {
			if (debug)
				alert ("error: getContainerContents ('" + id + "')");
		}
		
		return content;
	}
	
	/*
		Abandon all url content requests.
	*/
	function abortAllRequests () {
		var request;
		for (var i = 0; i < Requests.length; i++) {
			request = Requests[i];
			abortRequest (request);
		}
		
		// reinit our Requests array.
		Requests = new Array ();
	}
	
	/*
		Abort all requests that match against the regular expression
		on container name.
	*/
	function abortAllRequestsREGEX (regexp) {
		var request, count;

		count = 0;
		for (var i = 0; i < Requests.length; i++) {
			request = Requests[i];
			
			// copy our request container name to compare against.
			var tmp = request.container;
			
			/*
				if our request id matches we can abort this request.
			*/
			if (tmp.replace (regexp, "") == "") {
				abortRequest (request);
				Requests[i] = null;
				
				count++;
			}
		}
		
		// return the number of aborted requests.
		return count;
	}
	
	/*
		Abandon the given request in Requests.
		
		request can be an object or an integer.
		if request is an object, 
			we traverse the complete Requests array and
			abandon each request.
		else
			we abandon the Requests[request] entry.
	*/
	function abortRequest (request) {
		// is request an object?
		if (request && request.xmlhttp) {
			var xmlhttp = request.xmlhttp;
			xmlhttp.abort ();
			
			// all is well we were able to abort this request.
			return true;
		}
		
		// all is not well 
		return false;
	}

	/*
		Compare the given requests for equality. Only the essential request
		values are checked: container, url. Values such as append
		to body, content, type, and callback can vary from request to request.
	*/
	function RequestCompare (req1, req2) {
		/*
			check that the given requests are identical. Note that we are
			not comparing the contents and append values of the objects as 
			these may be different.	We just want to check that both requests 
			were made to the same page/container. This also means that the
			request types can be changed from get/post or vis-a-versa.
		
		*/
		if (req1.container == req2.container && req1.url == req2.url)
			return true;
		
		// inequality found
		return false;
	}
	
	/*
		Check the current status of all requests. If any have finished
		we can perform the necessary operation for the request.
	*/	
	function returnCheck () {
		/*
			have we abandoned all or our requests there is no
			need to continue processing the Requests.
		*/
//		if (abandoned)
//			return;
//		else
//			setTimeout ("returnCheck ();", pollInterval);
			
		// process all of our current requests.
		processAllRequests ();
		setTimeout ('returnCheck()', '200');
	}

	/*
		Create and return a new request container and return to the caller
		container_id: The container that needs to be updated.
		url: the url to fetch the page contents from.
		content: the default container content (usually empty).
		append: append the data to the current contents.
		type: The request type (GET|POST).
		callback: The callback function to call once the page contents
			have been updated. Note that no error checking is done here
			and it is assumed that when the callback is made the function
			has already been defined.
		
		**** NOTE ****
			use the content value to set the default error message of the 
			container if an error is encountered.
		**** NOTE ****
	*/
	function RequestContainer (container_id, url, content, append, type, data, callback) {
		// create a new object for storing properties.
		var request = new Object ();
		
		// set our container to update contents when request finishes.
		request.container = container_id;
		
		// set our 
		request.url = url;
		request.content = content;
		
		// set our append contents flag.
		if (append)
			request.append = true;
		else
			request.append = false;
			
		// set our type. Default to get on error.
		request.type = type.toUpperCase ();
		if (request.type != "POST")
			request.type = "GET";

		request.data = data;
		
		// set up our callback function.
		request.callback = callback;
		
		// return this Request object.
		return request;
	}

	function Container (url, container_id) {
		return new RequestContainer (container_id, url, "", false, "get", null, "");
	}
	
	/*
		Set the last error encountered.
	*/
	function setError (error_msg) {
		// set our last error.
		error = error_msg;
	}
	
	
	/*
		Actually make the ajax request for page contents for the given 
		Requests[index] request.
	*/
	function fireRequest (index) {
		// get our request info.
		var request = Requests[index];

		if (! request)
			return false;
	
		// normalize valid urls by replacing html entity ampersands with literal ampersands
		var url = request.url.replace (/&amp;/g, "&");
	
		/* start request setup stuff */
		var xmlhttp=false;
		/*@cc_on @*/
		/*@if (@_jscript_version >= 5) 
		// JScript gives us Conditional compilation, we can cope with old IE versions.
		// and security blocked creation of the objects.
		 try {
		  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		 } catch (e) {
		  try {
		   xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		  } catch (E) {
		   xmlhttp = false;
		  }
		 }
		@end @*/
		if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
		  xmlhttp = new XMLHttpRequest();
		}
		/* end request setup stuff */
		try {
			xmlhttp.open(request.type, url, true);
			if (request.type == "POST") {
				xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				if (request.data) {
					xmlhttp.setRequestHeader("Content-Length", request.data.length);
				}

				xmlhttp.send (request.data);
				
			} else {
				xmlhttp.send (null);			
			}
			xmlhttp.onreadystatechange=function () { if (xmlhttp.readyState==4) { processAllRequests();} }

			Requests[index].xmlhttp = new Object ();

			// set our request xmlhttp object.
			Requests[index].xmlhttp = xmlhttp;
		} catch (E) {
			alert ("fireRequest: " + E);
		}
	}
