function httpXMLQueue(docFunction,docURL,responseText,maxrx,aSync,failFunction){
/*	httpXMLQueue.js   abj3 		April 9, 2010

	Multiple requests for http xml documents are managed using a queue
	implimented by creating child objects of this public function: httpXMLQueue.

	returns: nothing
	parameters:
		docFunction - an object typeof == 'function'
			to be executed by xmlHttpRequest onreadystatechange readystate == 4 status == 200
			as docFunction(node) where node is the XML document root node
		docURL - typeof == 'string' is a URL for the XML document to be retreived
		responseText - if true then pass text, else pass DOM node
		maxrx - meaningful only if queue is 'undefined':
			number of xmlHttpRequest objects to be created for queue use:
			a) an integer between 1 and 9 will create a queuue using asynchronus xmlHttpRequest open methods.
				maxrx xmlHttpRequest objects are free for concurrent use
			b) 0 will create a "queue" using 1 synchronus xmlHttpRequest open method; the queue is empty upon return
			c) 'undefined', null or NaN or >9 will create a queue using 2 xmlHttpRequest objects as synchronous.
		aSync -	if undefined or 0 then based on maxrx>1; 
			if >0  then asynchronous request;
			if <0 then synchronous request
		failFunction - typof 'function' called when httpRequest returns fail

	tested with IE6, IE7, IE8,  Google Chrome, Mozila Firefox 3.5, Opera 10.0, Safari 4.0.3

	( Note: the queue needs to be free to the httpRequestObject onreadystatechange function.
		Child objects cannot be added to Microsoft object new ActiveXObject("Microsoft.XMLHTTP")
		so we add objects to this public function which is accessable from ActiveXObject("Microsoft.XMLHTTP").onreadystatechange )
		These objects become public and may be addressed from outside httpXMLQueue, but that is unwise.

	Example: httpXMLQueue(workFunction,url);
		if first call then establsihe a queue with 1 xmlHttpRequest object availabe for asynchronous use;
		Add request to queue and then process the queue.

		upon event asynchronous http complete, node = "XML document node", used with workFunction(node).
		node is no longer available when workFunction(node) returns;
			if node is needed then workFunction must save node.cloneNode(true).

	Example: httpXMLQueue(workFunction,url,false,3);
		if first call then establishes a queue with 3 xmlHttpRequest objects available for concurrent use;
		Add request to queue and then process the queue.

		workFunction is a type function that is executed upon xmlHttpRequest onreadystatechange readystate == 4 status == 200.

		docURL is a type string = url location for XML document to be retreived.

		false to pass responseXML

		"3" (maxrx) is the number of XMLHttpRequest objects that may be created to process multiple requests concurrently.
		default maxrx is 1 which assigns only 1 XMLHttpRequest object for processing multiple requests.

		an asynchronous http request is placed in a queue.
		order of execution is not restricted to first in first out unless using maxrx of 0.

	Example:httpXMLQueue(workFunction1,url1,false,3);httpXMLQueue(workFunction2,url2,true);httpXMLQueue(workFunction2,url3,false,3);
		if first call then establishes a queue with 3 xmlHttpRequest objects free for concurrent use.
		subsequent calls ignore maxrx parameter.
		This series of calls will push each request onto the queue.
		Requests are popped from the queue as xmlHttpRequest objects are ready for assignment
		xmlHttpRequest objects may retrieve concurrently.


	The queue is implimented by creating child objects for function httpXMLQueue:
		xmlDelayedArray as an array of requested XML documents.
		requestObjectArray as an array of XMLHttpRequest Objects or ActiveXObject("Microsoft.XMLHTTP") objects
			with associated values used to manage the queue

	Each new request is pushed to requested XML documents array httpXMLQueue.xmlDelayedArray.
	if a "free" XMLHttpRequest Object (requestObjectArray ) is found
	and if a "ready" requested XML document ( xmlDelayedArray ) is found then XMLHttpRequest Object elements are assigned:
		associated object "free" is assigned "false".
		requested XML document element in httpXMLQueue.xmlDelayedArray is noted as "busy"
		XMLHttpRequest object "onreadystatechange" is assigned an anonymous constructed function
			which passes the array index for httpXMLQueue.xmlDelayedArray requested XML document element
	then XMLHttpRequest object is opened, and sent.

	Upon completeion of asynchronous http event, the constructed "onreadystatechange" function passess the array index for
		requested XML document element in httpXMLQueue.xmlDelayedArray containing workFunction
		which is invoked with DOM node passed as a parameter.
	Upon completion of workFunction:
		xmlhttprequest object is assigned as "free"
		requested XML document is assigned as "done"
		a recursive call is made to httpXMLQueue()
*/
	if( aSync == null ) { aSync = 0; }

/*
 Initialize if object httpXMLQueue.maxrx is not defined
*/
	if( typeof httpXMLQueue.maxrx == 'undefined' ) {
		httpXMLQueue.xmlDelayedArray = [];
//--- define a fixed number of XMLHttpRequest objects that can be active; default = 2 process two concurrently
		if( maxrx == null ) { maxrx = 2; } else { maxrx = parseInt(maxrx); }
		if( isNaN(maxrx) ) { maxrx = 1; }
		if( maxrx > 9 ) { maxrx = 1; }
//--- still exploring serverside capabilities; for now run as: no queue; synchronous mode; when on server
		if (typeof Server != 'undefined' ) { maxrx = 0; }
		httpXMLQueue.maxrx = maxrx;
		if( maxrx < 1 ) { maxrx = 1; }
//--- pass ( httpXMLQueue.xmlDelayedArray[5]>-1 && httpXMLQueue.maxrx > 0 ) to httpRequestObject open function for asynchronous service
		httpXMLQueue.requestObjectArray = [];
//--- count queued request not 'done'
		httpXMLQueue.queued = 0;

		httpXMLQueue.processFunction = function(q){
//--- this function is invoked upon event asynchronous http complete
			var rx = httpXMLQueue.xmlDelayedArray[q][2];
			if(httpXMLQueue.requestObjectArray[rx]
				&& httpXMLQueue.requestObjectArray[rx].xmlRequest
				&& httpXMLQueue.requestObjectArray[rx].xmlRequest.readyState==4){
				if(httpXMLQueue.requestObjectArray[rx].xmlRequest.status!=200) {
if( typeof Server == 'object' && typeof Response == 'object' ) {
//--- running at server
	Response.write('<br>Problem retrieving xml data<br>' +
		httpXMLQueue.requestObjectArray[rx].xmlRequest.responseText +
		'<br>');
} else {
//--- runnig at browser
					var popContent = '';
					popContent += 'Problem retrieving xmlDELAYED data '+ httpXMLQueue.xmlDelayedArray[q][1];
					popContent += "\n";
					popContent += httpXMLQueue.requestObjectArray[rx].xmlRequest.responseText;
					httpXMLQueue.xmlDelayedArray[q][5](popContent);
}
				} else {
					if( httpXMLQueue.xmlDelayedArray[q][3] ) {
						httpXMLQueue.xmlDelayedArray[q][0](httpXMLQueue.requestObjectArray[rx].xmlRequest.responseText);
					} else {
						var node=httpXMLQueue.requestObjectArray[rx].xmlRequest.responseXML.documentElement;
						if( node ) {
							var failNode = node.getElementsByTagName("fail")[0];
							if( failNode && failNode.childNodes[0] ) {
//--- fail DOM is not passed to process function
if( typeof Server == 'object' && typeof Response == 'object' ) {
//--- running at server
} else {
//--- runnig at browser
								var popContent = '';
								popContent += "Failed. - " + failNode.childNodes[0].nodeValue + ' -- ' + httpXMLQueue.xmlDelayedArray[q][1];
								httpXMLQueue.xmlDelayedArray[q][5](popContent);
}
							} else {
								httpXMLQueue.xmlDelayedArray[q][0](node);
							}
						}
					}
				}
				httpXMLQueue.xmlDelayedArray[q][2] = 'done';
				httpXMLQueue.requestObjectArray[rx] = 'free';

				if( httpXMLQueue.maxrx > 0 ) {
					httpXMLQueue();
				}
			}
		}
	}

/*
	element of httpXMLQueue.xmlDelayedArray is an array
		[0] function to be invoked
		[1] string for XML url
		[2] processing status as
			-1: ready
			0..9: busy in requestObjectArray
			done: finished; may be purged
		[3] if true pass responseText, else pass DOM node from responseXML
		[4] aSynch asynchronous when -1; as synchronous when > 1; based on maxrx>1 when 0
		[5] failFunction

	Each new request is pushed to array xmlDelayedArray.
	Upon readyState 4 and status 200 of asynchronous http event,
		element's function is invoked with retreived XML DOM node passed as a parameter.
	Upon completion of the element's function,
		recursive call to httpXMLQueue is invoked to process any additional requests
		that may have been pushed while awaiting http request complete event.

*/
	if( typeof failFunction != 'function' ) { failFunction = XMLroutines_notify; }
//--- Push a request onto the queue
	if( typeof docURL == 'string' && docURL!='' && typeof docFunction == 'function' ) {
		if( responseText ) { responseText = true; } else { responseText = false; }
		httpXMLQueue.xmlDelayedArray.push([docFunction,docURL,-1,responseText,failFunction,aSync]);
	} else {
		if( typeof docFunction == 'function' ) {
			if( docURL ) { alert( 'httpXMLQueue docURL is '+ typeof docURL); }
		} else {
			if( docFunction ) { alert( 'httpXMLQueue docFunction is '+ typeof docFunction);  }
		}
	}

//--- Find a xmlHttpRequest object that is not busy
	var rx = -1;
		if( httpXMLQueue.requestObjectArray.length > 0 ) {
		for (var i=0;i<httpXMLQueue.requestObjectArray.length;i++) {
			if( httpXMLQueue.requestObjectArray[i]=='free') { rx = i; break; }
		}
	}
	if( rx == -1 
		&& ( httpXMLQueue.maxrx > httpXMLQueue.requestObjectArray.length 
			|| httpXMLQueue.requestObjectArray.length == 0 )
	) {
//--- Make a new xmlHttpRequest object. Only make as many as needed to cover concurrent requests up to limit maxrx
		rx = httpXMLQueue.requestObjectArray.length;
	}

	if( rx > -1 && httpXMLQueue.xmlDelayedArray.length>0 ) {

		var parmsArray = null;
		var q;
		for (q=0;q<httpXMLQueue.xmlDelayedArray.length;q++) {
			if( httpXMLQueue.xmlDelayedArray[q][2] == -1) {
				parmsArray = httpXMLQueue.xmlDelayedArray[q];
				break;
			}
		}

		if( parmsArray && parmsArray[1] && parmsArray[1] != '' && typeof parmsArray[0] == 'function' ) {

			httpXMLQueue.requestObjectArray[rx] = new Object;
			if (typeof Server != 'undefined') {
// MSDN Library IServerXMLHTTPRequest/ServerXMLHTTP
					httpXMLQueue.requestObjectArray[rx].xmlRequest = Server.CreateObject("Msxml2.ServerXMLHTTP.3.0");
//--- async not tested
			} else if ( window && window.XMLHttpRequest) {// code for IE7, Firefox, Mozilla, etc.
				httpXMLQueue.requestObjectArray[rx].xmlRequest=new XMLHttpRequest();
			} else if ( window && window.ActiveXObject) {// code for IE5, IE6
					httpXMLQueue.requestObjectArray[rx].xmlRequest=new ActiveXObject("Microsoft.XMLHTTP");
			}
			if (typeof httpXMLQueue.requestObjectArray[rx].xmlRequest == 'undefined') {
				alert("Your browser does not support XMLHTTP.");
			} else {

				httpXMLQueue.xmlDelayedArray[q][2] = rx;
				docURL = parmsArray[1];
				var async = parmsArray[5];

//---  get asynchronously new http XML document

//----------------------------------------------------------------------------
				httpXMLQueue.requestObjectArray[rx].xmlRequest.onreadystatechange =
					new Function( 'httpXMLQueue.processFunction("'+q+'");' );
//------------------------------------------------------------------------------

				if( docURL.indexOf('?')>0 ) { docURL += '&'; } else { docURL += '?'; }
				docURL += 'antiCache=' + (new Date()).getTime();
				var http = httpXMLQueue.requestObjectArray[rx].xmlRequest;
				var urlArray = docURL.split('?');
				if( urlArray[1].length < 1024 ) {
					httpXMLQueue.requestObjectArray[rx].xmlRequest.open("GET",docURL,(async>-1 && httpXMLQueue.maxrx>0));
					httpXMLQueue.requestObjectArray[rx].xmlRequest.send("");
				} else {
					httpXMLQueue.requestObjectArray[rx].xmlRequest.open("POST",urlArray[0],(async>-1 && httpXMLQueue.maxrx>0));
//Send the proper header information along with the request
					httpXMLQueue.requestObjectArray[rx].xmlRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
					httpXMLQueue.requestObjectArray[rx].xmlRequest.setRequestHeader("Content-length", urlArray[1].length);
					httpXMLQueue.requestObjectArray[rx].xmlRequest.setRequestHeader("Connection", "close");
					httpXMLQueue.requestObjectArray[rx].xmlRequest.send(urlArray[1]);
				}
				if( httpXMLQueue.maxrx<1 ) {
					httpXMLQueue.processFunction(q);
				}
			}
		}

//--- clean up - remove last httpXMLQueue.xmlDelayedArray if "done"
//--- array position is used by xmlHttpRequest "completed" function and may not be altered.
		while( httpXMLQueue.xmlDelayedArray.length>0
			&& httpXMLQueue.xmlDelayedArray[httpXMLQueue.xmlDelayedArray.length-1][2] == 'done' ) {
			httpXMLQueue.xmlDelayedArray.pop();
		}

//--- count queued requests not 'done'
		httpXMLQueue.queued = 0;
		if( httpXMLQueue.xmlDelayedArray.length>0 ) {
			for (var i=0;i<httpXMLQueue.xmlDelayedArray.length;i++) {
				if( httpXMLQueue.xmlDelayedArray[i][2]!='done') { httpXMLQueue.queued = httpXMLQueue.queued + 1; }
			}
		}
	}
}

function XMLroutines_notify( message ){
	alert(message);
}

function serializeNode(node) {
	if (typeof XMLSerializer != 'undefined') {
		return new XMLSerializer().serializeToString(node);
	}
	else if (typeof node.xml != 'undefined') {
		return node.xml;
	}
	return( 'no serializeNode function' );
}

function xml2htmlTable(title,nodeArray,nRows){
/*
	xml2htmlTable.js    abj3  September 16, 2009

	format XML document as an HTML table

	returns typeof 'string' HTML

	title - text displayed in the first row of the table before column headers
		if title.substring(0,7) == 'nothrow' then no Table Header row
	nodeArray - typeof 'array' a list of document nodes, each representing a row of data
	nRows - maximum number of rows placed into html table; if <1 then unlimited

*/

	if( !title ) { title = ''; }
	if( nRows ) { nRows = parseInt(nRows) } else { nRows = 0; }
	var htmlResult = '';
	var htmlTable = '';
	var rowCount = 0;
	var columnCount = 0;
	var tableHDArray = new Array();
	if( nodeArray && nodeArray[0] ) {
		for (var i=0;i<nodeArray.length;i++){
			childArray = nodeArray[i].childNodes;
			if( childArray[0] && (rowCount<nRows || nRows<1) ) {
				htmlTable += '<' + 'tr>';
				k = 0;
				for (var j=0;j<childArray.length;j++){
					if( childArray[j]
						&& childArray[j].nodeType == 1
					) {

						var hdName = '';
						var hdNameArray = (childArray[j].nodeName).split('_');
						for ( var n=0;n<hdNameArray.length;n++ ) { hdName += ' '+hdNameArray[n].substr(0,1).toUpperCase() + hdNameArray[n].substr(1); }
						tableHDArray[k] = hdName;
						k ++;
						htmlTable += '<' + 'td>';

						if(childArray[j].childNodes[0]) {

							htmlResult += xml2htmlTable('',childArray[j]);

							if( childArray[j].getAttribute('click') && childArray[j].getAttribute('click')!='' ) {
								htmlTable += '<a onclick="';
								htmlTable += childArray[j].getAttribute('click');
								htmlTable += '(' + childArray[j].childNodes[0].nodeValue + ')">';
							}
							htmlTable += childArray[j].childNodes[0].nodeValue;
							if( childArray[j].getAttribute('click') && childArray[j].getAttribute('click')!='' ) {
								htmlTable += '<' + '/a>';
							}
						}
						htmlTable += '<' + '/td>';
					}
				}
				if( k > columnCount ) { columnCount = k; }
				htmlTable += '<' + '/tr>';
				rowCount ++;
			}
		}
	}

	if( htmlTable != '' ) {

		if( title != '' && title != 'nothrow' ) {
			htmlResult = '<' + 'table class="xmlTable">';
			htmlResult += '<' + 'tr><' + 'th colspan="' + columnCount + '">';
			if( title.substr(0,7)=='nothrow' ) { htmlResult += title.substr(7); } else { htmlResult += title; }
			htmlResult += ' <' + 'span style="margin-left: 2em;">(displaying ' + rowCount + ' rows)<' + '/span><' + '/th><' + '/tr>';
		} else {
			htmlResult = '<' + 'table class="xmlTable" style="margin-top:0;padding-top:0;">';
		}
		if( title.length >= 7 && title.substr(0,7) != 'nothrow' ) {
			htmlResult += '<' + 'tr><' + 'th>' + tableHDArray.join('<' + '/th><' + 'th>') + '<' + '/th><' + '/tr>';
		}
		htmlResult += htmlTable + '<' + '/table>';
	}

	return( htmlResult );
}

function xml2TableString(nodeArray,columnDelimiter,rowDelimiter,nRows){
/*
	xml2TableString.js    abj3  July 19, 2010   abj3

	format XML document as a table

	returns typeof 'string'

	nodeArray - [collection](almost an array) a list of document nodes, each representing a row of data
	columndelimiter
	rowDelimiter
	nRows - maximum number of rows placed into html table; if <1 then unlimited

*/

	if( !columnDelimiter ) { columnDelimiter = '\t'; }
	if( !rowDelimiter ) { rowDelimiter = '\n'; }
	if( nRows ) { nRows = parseInt(nRows) } else { nRows = 0; }
	var includeColumnNames = 0;
	var result = '';
	var table = '';
	var rowCount = 0;
	var columnCount = 0;
	var tableHDArray = new Array();
	if( nodeArray && nodeArray[0] ) {
		for (var i=0;i<nodeArray.length;i++){
			childArray = nodeArray[i].childNodes;
			if( childArray[0] && (rowCount<nRows || nRows<1) ) {
				k = 0;
				var row = '';
				for (var j=0;j<childArray.length;j++){
					if( childArray[j]
						&& childArray[j].nodeType == 1
					) {

						var hdName = '';
						var hdNameArray = (childArray[j].nodeName).split('_');
						for ( var n=0;n<hdNameArray.length;n++ ) { hdName += ' '+hdNameArray[n].substr(0,1).toUpperCase() + hdNameArray[n].substr(1); }
						tableHDArray[k] = hdName;
						k ++;

						if(childArray[j].childNodes[0]) {

							result += xml2htmlTable('',childArray[j]);
							row += columnDelimiter;
							row += childArray[j].childNodes[0].nodeValue;
						}
					}
				}
				if( k > columnCount ) { columnCount = k; }
				if( row != '' ) { row = row.substr(1); }
				table += row + rowDelimiter;
				rowCount ++;
			}
		}
	}

	if( table != '' ) {

		if( includeColumnNames == 1 ) {
			result += tableHDArray.join(columnDelimiter);
		}
		result += table;
	}

	return( result );
}
