/**
xhr.js

Copyright 2005 Pyramis Studios, Inc. / http://www.pyramis-studios.com

Author: 		Andrew Mace
Created: 		20050427
Last Modified:	20050725
Description: 	Generic code to handle multiple simultaneous XMLHttpRequests.
Usage:
	- Use encodeURIComponent to format GET query string
	- Send request to xml file or script
	- Script returns content type text/xml
	xhr_load('script.pl','GET','key=val&key2=val2');

LEGAL NOTICE:
THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTIES OF ANY KIND EITHER EXPRESS OR IMPLIED. TO THE FULLEST EXTENT POSSIBLE PURSUANT TO THE APPLICABLE LAW, PYRAMIS STUDIOS, INC. DISCLAIMS ALL WARRANTIES, EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT OR OTHER VIOLATION OF RIGHTS. 

LIMITATION OF LIABILITY
UNDER NO CIRCUMSTANCES, INCLUDING, BUT NOT LIMITED TO, NEGLIGENCE, SHALL PYRAMIS STUDIOS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA OR PROFIT, ARISING OUT OF THE USE, OR THE INABILITY TO USE, THE MATERIALS IN THIS FILE, EVEN IF PYRAMIS STUDIOS, INC. OR A PYRAMIS STUDIOS AUTHORIZED REPRESENTATIVE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IF YOUR USE OF MATERIALS FROM THIS FILE RESULTS IN THE NEED FOR SERVICING, REPAIR OR CORRECTION OF EQUIPMENT OR DATA, YOU ASSUME ANY COSTS THEREOF. SOME PROVINCES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES OR THE EXCLUSION OF LIABILITY IN CERTAIN CIRCUMSTANCES, SO THE ABOVE LIMITATION OR EXCLUSION MAY NOT APPLY TO YOU.

THIS FILE MAY NOT BE USED IN ANY OTHER WAY THAN ITS EXPRESS PURPOSE. REDISTRIBUTION OF THIS FILE OR THE CREATION OF DERIVATIVE WORKS IS NOT PERMITTED.

COPYRIGHT 2005 PYRAMIS STUDIOS, INC. ALL RIGHTS ARE RESERVED.

MODIFYING OR REMOVING THE CONTENTS OF THIS FILE BEFORE THIS LINE IS NOT PERMITTED.
**/

var xhr_serial = true; // whether to use serial or parallel connects
var xhr_reqs = []; // XMLHttpRequest queue; entries removed when request completed
var xhr_req_urls = []; // keep track of the urls requested
var xhr_info_ids = ["status", "status2"]; // element ids to use to display status messages; append others if necessary
var xhr_info_id = xhr_info_ids[0]; // the status element id to use by default - displays request status/messages/feedback
var xhr_info_msg = ''; // set this at each xhr_load call to display custom status message
var xhr_queue_js = [];

function xhr_error(r)
{ // handle the request error
	if(typeof(xhr_error_custom) == 'function') {
		if(xhr_error_custom(r)) { // if returns true, don't follow normal processing
			return;
		}
	}
	if(r == -1) { // XMLHttpRequest not supported
		// REDIRECT HERE
	} else {
		xhr_info(r.status); // update status message
	}
}

function xhr_info(s, id)
{ // update the status on the display
	if(id == null) id = xhr_info_id;
	if(id == null) return;
	var el = document.getElementById(id); 
	if(el == null) return;
	if(xhr_info_msg.length) {
		el.innerHTML = xhr_info_msg;
		return;
	}
	if(isNaN(s)) {
		el.innerHTML = s;
	} else {
		if(s == 0) { // sending request
			el.innerHTML = "Sending request&#8230;";
		} else if(s == 200) {
			el.innerHTML = "&nbsp;";
		} else if(s != 200 && s < 500) { // 1xx/2xx/3xx; 4xx/client errors
		 	el.innerHTML = "Error";
		} else if(s >= 500) { // 5xx/server errors
		 	el.innerHTML = "Error";
		} else {
			el.innerHTML = "&nbsp;";
		}
	}
}

function xhr_process(r) 
{ // received response from server; now process 
  // r is an XMLHttpRequest object
	var done = false;
	xhr_info_id = xhr_info_ids[0];
	xhr_info_msg = '';	
	if(typeof(xhr_process_custom) == 'function') {
		if(xhr_process_custom(r)) { // if returns true, don't follow normal processing
			done = true;
		}
	}
	if(!done) {
		var doc = r.responseXML;
		var nodes = doc.lastChild.childNodes;
		for(var i = 0; i < nodes.length; i++) {
			if(nodes[i].nodeName == 'ht') { // replace the contents of the element with specified id
				var el = document.getElementById(nodes[i].getAttribute('id'));
				if(el != null) el.innerHTML = nodes[i].firstChild.nodeValue;
			} else if(nodes[i].nodeName == 'js') { // // handle js after all page updates
				xhr_queue_js.push([nodes[i].getAttribute('id'),nodes[i].getAttribute('args')]);
			}
		}	
	}	
}

function xhr_process_js()
{
	var done = false;
	if(typeof(xhr_process_js_custom) == 'function') {
		if(xhr_process_js_custom()) { // if returns true, don't follow normal processing
			done = true;
		}
	}
	if(done) return;
	for(var i = 0; i < xhr_queue_js.length; i++) {
		var f = xhr_queue_js[i][0];
		if(f == null || !f.length) continue;
		var args = xhr_queue_js[i][1];
		if(args && args.length) {
			args = args.split(',');
		}
		var f = eval(f);
		if(typeof(f) == 'function') {
			f.apply(this, args);
		}	
	}
	xhr_queue_js.length = 0;
}


function xhr_load(url, method, data) 
{ // data should already be URI encoded at this point
	if(xhr_serial) xhr_abort_last();
	var req;
    if(window.XMLHttpRequest) { // branch for native XMLHttpRequest object
        req = new XMLHttpRequest();
    } else if(window.ActiveXObject) { // branch for IE/Windows ActiveX version
       	req = new ActiveXObject("Microsoft.XMLHTTP");
    }
	if(req) {
		xhr_info(0); // update status message
		xhr_reqs.push(req);
		if(method.charAt(0) == 'G') {
			var d = new Date(); // to make each request unique/prevent caching
			url += "?" + data + "&_t=" + d.getTime();
			data = null;
		} else if(method.charAt(0) == 'P') {
			var d = new Date(); // to make each request unique/prevent caching
			data += "&_t=" + d.getTime();
		}
		xhr_req_urls.push(url);
		req.onreadystatechange = xhr_status;	
		req.open(method, url, true);
		if(typeof req.setRequestHeader != 'undefined') req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');		
		req.send(data);
	} else {
		xhr_error(-1); // XMLHttpRequest method is not supported
	}
}

function xhr_status() 
{ // handle status changes for extant XMLHttpRequest
	for(var k = xhr_reqs.length - 1; k >= 0; k--) {
		switch(xhr_reqs[k].readyState) {
		 case 0: // uninitialized
		 case 1: // loading
		 case 2: // loaded
		 case 3: // interactive
			break;
		 case 4: // complete, only if req shows "loaded"
			if(xhr_reqs[k].status == 200) xhr_process(xhr_reqs[k]);
			else xhr_error(xhr_reqs[k]);
			if(navigator.userAgent.toLowerCase().indexOf("msie") >= 0 && !delete(xhr_reqs[k])) xhr_reqs[k] = null;
			xhr_reqs.splice(k, 1); // remove the completed request
			xhr_req_urls.splice(k, 1); // remove the url data as well
			xhr_process_js(); // now process any queue js calls
			break;
		}
    }
}

function xhr_url(r)
{
	for(var i = 0; i < xhr_reqs.length; i++) {
		if(xhr_reqs[i] == r) {
			return(xhr_req_urls[i]);
		}
	}
	return(null);
}

function xhr_abort_last()
{ // kill the last request - useful if we want serial communication only
	var n = xhr_reqs.length;
	if(n) {
		xhr_reqs[n - 1].abort();
		xhr_reqs.splice(n - 1, 1); // remove the aborted request
		xhr_req_urls.splice(n - 1, 1); // remove the url listing as well
	}
}
