

function loadOptionalXMLParam(basenode, param)	{

	var nodes = basenode.getElementsByTagName(param);
	
	if(nodes==null || nodes.length==0)	return('');

	if(nodes[0].firstChild==null)	return('');

	return(nodes[0].firstChild.data);

}


///////////// simul-request ajax stuff
 
// constants
var HTTP_STATUS_OK = 200;
var READYSTATE_COMPLETED = 4;
var NODE_TYPE_ELEMENT = 1;


/**
 * This is a quick class to get around JavaScript's
 * poor support for associative arrays. Specifically, 
 * without this class, properties of the object would
 * be included as keys in the associative array. This
 * class ensures that we deal only with array elements 
 * we added to the array and not any properties of the
 * container we're using. See Side Bar 1.
 */
function _RCHashtable()
{
   this.data = new Object();
   this.keys = new Array();
} // end constructor


function _RCHashtable_getKey(raw)
{
   // python style naming convention, 
   // to avoid conflicts with actual 
   // attributes (see Side Bar 1).
   return '__'+ raw +'__';
} // end function _RCHashtable_getKey


function _RCHashtable_get(nam)
{
   var key = this.getKey(nam);
   // retreive value if exists, else null
   var val = (this.data[key]) ? this.data[key] : null;
   return val;
} // end function _RCHashtable_get


function _RCHashtable_put(nam, val)
{
   // if missing arg
   if (!nam) return false;
   
   var key = this.getKey(nam);
   
   // if key doesn't already exist add 
   // to keys array.
   var exists = true;
   if (!this.data[key])
   {
      exists = false;
      this.keys[this.keys.length] = key;
   }
   
   // return old value if set, or else null
   var oldval = exists ? this.data[key] : null;
   
   this.data[key] = val;
    
   return oldval;
} // end function _RCHashtable_put


function _RCHashtable_keys()
{
   // return a copy of the array, otherwise
   // the user of this class could accidentally
   // modify our array, since we’d be passing
   // them a reference.
   return keys.slice(0);
} // end function _RCHashtable_keys


function _RCHashtable_containsKey(nam)
{
   // get() returns null if not found 
   // or if found and is null. In both
   // conditions, we want to return false.
   // Otherwise, we want to return true.
   return (this.get(nam) != null);
} // end function _RCHashtable_containsKey

// creating the class this way is more memory efficient
// if we are using multiple instances of this data 
// structure. We’re only using one, so there’s really 
// no memory gain. However, I generally use this method
// with data structures for consistency’s sake.
_RCHashtable.prototype.getKey = _RCHashtable_getKey;
_RCHashtable.prototype.get = _RCHashtable_get;
_RCHashtable.prototype.put = _RCHashtable_put;
_RCHashtable.prototype.keys = _RCHashtable_keys;
_RCHashtable.prototype.containsKey = 
   _RCHashtable_containsKey;





/**
 * This is the class that deals with remote requests.
 * This implementation uses XMLHttpRequest, but the 
 * idea is that you could swap implementations without 
 * changing any code that uses this class.
 */
function RemoteConnection(recurseOnChildren)
{
   // array where we’ll store our request objects
   this.aRequests = new Array();
   this.aRequests[0] = null;

   this.aActive = new Array();
   
   this.aRequestURLs = new Array();
   this.aRequestURLs[0] = null;
   
   
   
   // our container to map elements to handling 
   // functions
   this.hRespHandlers = new _RCHashtable();
   // flag to track whether a wildcard handler 
   // has been set.
   this.isWildcardSet = false;
   // whether or not to recurse on children of a 
   // matched subtree that has already been passed
   // to an appropriate handler function.
   // the code is such because it is an optional
   // parameter and might not be defined (but if
   // it is defined and isn’t false then we also know 
   // its value. So we just assign it literally.
   this.recurseOnChildren = 
      recurseOnChildren ? true : false;
    

    
   this.request = function(url, method, requestxml)
      {

         // set defaults omitted optional arguments
         if (!method) method = "GET";
         if (!requestxml) requestxml = null;

		window.status = "";  // clear status
         
         // request object
         var req = null;
         
         // look for an empty spot in requests 
         // array due to a deleted request. You 
         // might consider moving this 
         // functionality to library code and 
         // wrapping the functionality in a 
         // data structure.
         
         // default spot is at end
         var openIndex = this.aRequests.length;
         

         // look for closest spot
         for (var i=0; i<this.aRequests.length; i++)
         {
            if (this.aRequests[i] == null || !this.aActive[i]) {
                openIndex = i;
                break; 
            }
         }
         
         // check to make sure this url is unique
         
         var compare_url = url;
         
         var index = compare_url.indexOf("?");

		 if(index!=-1)	{
		 
		 	compare_url = compare_url.substring(0, index);
		 }
         
         for (var i=0; i<this.aRequestURLs.length; i++)
         {
            if (this.aActive[i] && this.aRequestURLs[i] == compare_url)	{

				window.status = "[" + i + "] Already waiting on this request: " + compare_url; 
				return;
            }
         }
         
         // check total count

         if(openIndex > 8)	{
         
         	window.status = "Error making request: too many open requests"; 
		 }

		 // ok, do the request

	     this.aActive[openIndex] = true;
		
				
         try	{

			if(this.aRequests[openIndex]==null)	{

	        
	            req = createXMLHttpRequest();
	            
	            if(req==null)	return(false);  // could not create call
	            
	      
	            // add the element to the array before 
	            // doing anything that will fire 
	            // readyStateChange event. If we didn’t
	            // do this now, we could be getting event 
	            // firings from request objects that we 
	            // can’t find in our requsts array, when we 
	            // go to handle the readyStateChange.
	            
	            this.aRequests[openIndex] = req;
			}
			else	{
			
				req = this.aRequests[openIndex];
			}


		    // this might look odd, but it is
            // necessary because ‘this’ in event 
            // handlers refers to the owner of the 
            // fired event. See: 
            // www.quirksmode.org/js/this.html
            
            var self = this;
            
            req.onreadystatechange = 
               function() {self.handle()};


            this.aRequestURLs[openIndex] = compare_url;
            req.open(method, url, true);
            req.send(requestxml);
      
	        return true; // indicate no errors

		} catch(error)	{
		
		    this.aActive[openIndex] = false;
			this.aRequests[openIndex] = null;

			this.aRequestURLs[openIndex] = null;

			window.status = "Error creating request: " + error.name + " - " + error.message; 
		}

      }; // end method request
    
   
    function createXMLHttpRequest() {
	 
	   try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
	 
	   try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
	 
	   try { return new XMLHttpRequest(); } catch(e) {}
	 
	   return null;
    }



   this.handle = function()
      {
      
         // cycle through request objects to see 
         // if any are ready with a response. we 
         // keep looping even after we find one, 
         // because it might not be the one that 
         // fired the event (there could be 
         // multiple that are ready.
         for (var i=0; i<this.aRequests.length; i++)
         {
    
    		if(!this.aActive[i])	continue;
    
         	try	{
	         
	            // if state is "complete"
	            if (this.aRequests[i] != null)	{
	            
		            if(this.aRequests[i].readyState == READYSTATE_COMPLETED)
		            {
		               if (this.aRequests[i].status == HTTP_STATUS_OK)
		               {
		               
		               	  //alert(this.aRequests[i].responseText);
		               
		                  // pass this off to the xml parser
		                  this.parseResponse(
		                     this.aRequests[i].responseXML);
		                  // remove object. this is 
		                  // important because Opera 
		                  // sometimes refires the 
		                  // readyStateChange event.
		                  // plus, this might not be the
		                  // one that fired the event, and 
		                  // this method might be running 
		                  // twice at the same time (setting
		                  // this to null is about as ‘thread
		                  // safe’ as we can get).
		               } // end of HTTP OK

					
				       this.aActive[i] = false;  // keep the object, so it can be reused later
		               this.aRequestURLs[i] = null;

					   this.aRequests[i].abort();  // we are done with this request

		            } // end if completed
				}

			} catch(error)	{

				this.aActive[i] = false;
				this.aRequestURLs[i] = null;
			
				window.status = "Error processing xml: " + error.name + " - " + error.message; 
			}

         } // end for
      }; // end method handle
   
   
   
    
   this.setRespHandler = 
      function(sElementName, funcHandler)
      {
         // flip flag if wildcard
         if (sElementName == '*')
            this.isWildcardSet = true;
         // end if
         
         // add the element handler to the hashtable
         return this.hRespHandlers.put(
            sElementName, funcHandler);
      }; // end method setRespHandler
    
    
   this.parseResponse = function(oNode)
      {
         if (!oNode) return;
         // base case (oNode is a leaf element)
         if (!oNode.hasChildNodes()) return;
         
         // else... recurse through children
         var children = oNode.childNodes;
         
         for (var i=0; i<children.length; i++)
         {
             // all nodes (element, attribute, text, 
             // etc.) are returned as children, but 
             // we only want to act on children that 
             // are element nodes
             if (children[i].nodeType == 
                NODE_TYPE_ELEMENT)
             {
                // check to see if a handler exists 
                // specifically for this element
                var elementName = children[i].nodeName;
                

                if ( this.hRespHandlers.containsKey(
                   elementName) )
                {
                   // if so, fire handler, and pass 
                   // subtree starting with the node 
                   // of interest
                
                   // retreive the handler
                   var funcHandler = 
                     this.hRespHandlers.get(elementName);
                   // fire the handler and pass the 
                   // subtree as its argument
                   funcHandler(children[i]);
                           
                   // a match was found. conditionally 
                   // recurse on the subtree. 
                   // conditionally, because this subtree
                   // has already been handed off to one 
                   // function.
                   if (this.recurseOnChildren) 
                      this.parseResponse(children[i]);
                }
                else if (this.isWildcardSet)
                {
                   // retreive the handler
                   var funcHandler = 
                      this.hRespHandlers.get('*');
                   // fire the handler and pass the 
                   // subtree as its argument
                   funcHandler(children[i]);
                   
                   // a match was found. conditionally 
                   // recurse on the subtree. 
                   // conditionally, because this subtree
                   // has already been handed off to one 
                   // function.
                   if (this.recurseOnChildren) 
                      this.parseResponse(children[i]);
                }
                else
                {
                   // not match yet found on this 
                   // subtree. keep digging.
                   this.parseResponse(children[i]);
                }
                
             } // end if element node type
         } // end for loop of children
      }; // end method parseResponse
    
    

} // end class RemoteConnection



// create universal remote connection object

var xmlconn = new RemoteConnection();



