/*
  WHAT:       SIMPLE AJAX FRAMEWORK
  AUTHOR:     ERIC NORDENG
  DATE:       06-29-2006  (1.0)
  VERSION:    2.1
  FILE NAME:  AJAX_2_1.js
  
  UPDATES:  07-20-2006  (2.0)
            Enhancement: Redesign framework to support multiple threads.
            Note: This framework will not support version 1.0 (AJAX.js)
            
            08-10-2006  (2.1)
            Bug Fix: Move the send call in the 'sendXMLHttpRequest' function after the onreadystatechange assignment.
            
            06-21-2007  (2.2)
            Bug Fix: Change the character replacements for special XML characters on the addNode() function.
            
            08-15-2007  (2.3)
            Enhancement: Added 'getXMLHttpRequest' function to fetch data.
            
            12-27-2007  (2.4)
            Enhancement: Added the global variable 'glbAjaxDisabled' to capture whether or not the user's browser can run this framework.


  ***************
   DOCUMENTATION 
  ***************
  
  => This framework allows multi-threaded AJAX calls
  
    
    -------------
    POSTING DATA 
    -------------

    => The general order of tasks for using the the SEND request object framework are as follows:

       · Create the document object(s)
       · Build the document object with XML
       · Create the request object(s)
       · Send request(s) to the server
       · Use a callback function to process the data that is returned from the server


    => There are two things that need to be explained:

       · The sendXMLHttpRequest function takes 8 parameters:

         1 - The request object [object]
         2 - The URL of the server page to be called [string]
         3 - The document object [object]
         4 - The id of the div area to display server-side errors [string]
         5 - The id of the div area to display a message while the server page is running [string]
         6 - The message to be displayed while the server page is running [string]
         7 - The name of the callback function to run after the server page is done running [string]
         8 - Flag to assign whether or not the request should be asyncronous [true/false]

       · The request object is automatically sent to the callback function


    => Below is an example of how your code may look if your running 3 independent requests to the server:

       function buttonClicked()
       {
          if (!glbAjaxDisabled)
          {
             // CREATE DOCUMENT OBJECTS FOR XML
             var objXMLDoc1 = createDocumentObject('','');
             var objXMLDoc2 = createDocumentObject('','');
             var objXMLDoc3 = createDocumentObject('','');


             // BUILD XML FOR SERVER PAGE TO PARSE
             addNode(objXMLDoc1,'nodeName','nodeValue');
             addNode(objXMLDoc1,'nodeName','nodeValue');

             addNode(objXMLDoc2,'nodeName','nodeValue');
             addNode(objXMLDoc2,'nodeName','nodeValue');

             addNode(objXMLDoc3,'nodeName','nodeValue');
             addNode(objXMLDoc3,'nodeName','nodeValue');


             // CREATE THE REQUEST OBJECTS
             var objReq1 = createRequestObject();
             var objReq2 = createRequestObject();
             var objReq3 = createRequestObject();

             // SEND REQUEST TO SERVER
             sendXMLHttpRequest(objReq1,"ajaxTest1.asp",objXMLDoc1,'div_errors1','div_message1','please wait for request 1...','allDone1',true);
             sendXMLHttpRequest(objReq2,"ajaxTest2.asp",objXMLDoc2,'div_errors2','div_message2','please wait for request 2...','allDone2',true);
             sendXMLHttpRequest(objReq3,"ajaxTest3.asp",objXMLDoc3,'div_errors3','div_message3','please wait for request 3...','allDone3',true);
          }
       }

       function allDone1(objReq)
       {
         // DISPLAY XHTML THAT WAS RETURNED FROM THE SERVER
         changeInnerHTML("divID1",objReq.responseText);

         -- OR --

         // CREATE OBJECT TO HOLD XML DATA THAT WAS RETURNED FROM THE SERVER (AND PARSE IT)
         var objDOMParser = createParserObject(objReq.responseText);
         var nodeValue = getNodeVal(objDOMParser,"nodeName");
       }

       function allDone2(objReq)
       {
         // DISPLAY XHTML THAT WAS RETURNED FROM THE SERVER
         changeInnerHTML("divID2",objReq.responseText);

         -- OR --

         // CREATE OBJECT TO HOLD XML DATA THAT WAS RETURNED FROM THE SERVER (AND PARSE IT)
         var objDOMParser = createParserObject(objReq.responseText);
         var nodeValue = getNodeVal(objDOMParser,"nodeName");
       } 

       function allDone3(objReq)
       {
         // DISPLAY XHTML THAT WAS RETURNED FROM THE SERVER
         changeInnerHTML("divID3",objReq.responseText);

         -- OR --

         // CREATE OBJECT TO HOLD XML DATA THAT WAS RETURNED FROM THE SERVER (AND PARSE IT)
         var objDOMParser = createParserObject(objReq.responseText);
         var nodeValue = getNodeVal(objDOMParser,"nodeName");
       }
       
    
    ------------
    GETTING DATA
    ------------
       
    => The general order of tasks for using the the GET request object framework are as follows:

       · Create the request object(s)
       · Send request(s) to the server
       · Use a callback function to process the data that is returned from the server


    => There are two things that need to be explained:

       · The getXMLHttpRequest function takes 8 parameters:

         1 - The request object [object]
         2 - The URL of the server page to be called [string]
         3 - The id of the div area to display server-side errors [string]
         4 - The id of the div area to display a message while the server page is running [string]
         5 - The message to be displayed while the server page is running [string]
         6 - The name of the callback function to run after the server page is done running [string]
         7 - Flag to assign whether or not the request should be asyncronous [true/false]

       · The request object is automatically sent to the callback function


    => Below is an example of how your code may look if your running 3 independent requests to the server:

       function buttonClicked()
       {
          if (!glbAjaxDisabled)
          {
             // CREATE THE REQUEST OBJECTS
             objReq1 = createRequestObject();
             objReq2 = createRequestObject();
             objReq3 = createRequestObject();

             // SEND REQUEST TO SERVER
             getXMLHttpRequest(objReq1,'/path/to/file1.xml','div_error','div_message','Reading File...','allDone1',true);
             getXMLHttpRequest(objReq2,'/path/to/file2.xml','div_error','div_message','Reading File...','allDone2',true);
             getXMLHttpRequest(objReq3,'/path/to/file3.xml','div_error','div_message','Reading File...','allDone3',true);
          }
       }
       
       function allDone1(objReq)
       {
         // CREATE PARSER OBJECT
         var objDOMParser = createParserObject(objReq.responseText);

         // GRAB ROOT ELEMENT
         var root = objDOMParser.documentElement;
         
         // GET GROUP OF NODES BY NAME
         var nodes = root.getElementsByTagName("nodeName");
         
         // ITERATE THROUGH NODES
         for (var i=0; i < nodes.length; i++)
         {
           var singleNode = nodes[i];
           var childNode = singleNode.getElementsByTagName(childNodeName)[i].childNodes[0].nodeValue;
         }
       }
       
       function allDone2(objReq)
       {
         // CREATE PARSER OBJECT
         var objDOMParser = createParserObject(objReq.responseText);

         // GRAB ROOT ELEMENT
         var root = objDOMParser.documentElement;
         
         // GET GROUP OF NODES BY NAME
         var nodes = root.getElementsByTagName("nodeName");
         
         // ITERATE THROUGH NODES
         for (var i=0; i < nodes.length; i++)
         {
           var singleNode = nodes[i];
           var childNode = singleNode.getElementsByTagName(childNodeName)[i].childNodes[0].nodeValue;
         }
       }

       function allDone3(objReq)
       {
         // CREATE PARSER OBJECT
         var objDOMParser = createParserObject(objReq.responseText);

         // GRAB ROOT ELEMENT
         var root = objDOMParser.documentElement;
         
         // GET GROUP OF NODES BY NAME
         var nodes = root.getElementsByTagName("nodeName");
         
         // ITERATE THROUGH NODES
         for (var i=0; i < nodes.length; i++)
         {
           var singleNode = nodes[i];
           var childNode = singleNode.getElementsByTagName(childNodeName)[0].childNodes[0].nodeValue;
         }
       }

*/

/* ------------------------------------------------------------------------------------- */

  // GLOBAL VARIABLES
  var glbAjaxDisabled = false;

/* ------------------------------------------------------------------------------------- */

function createRequestObject()
{
  objReq = null;
  if (window.XMLHttpRequest)
  {
    objReq  = new XMLHttpRequest();
  }
  else if (window.ActiveXObject)
  {
    try
    {
      objReq = new ActiveXObject("Microsoft.XMLHTTP");
    }
    catch(e)
    {
      glbAjaxDisabled = true;
    }
  }
  return objReq;
}

/* ------------------------------------------------------------------------------------- */

function sendXMLHttpRequest(objReq,url,objXMLDoc,divAreaErrors,divAreaMessageID,message,callBackFunc,async)
{
  objReq.open("post", url, async);
  objReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;');
  objReq.onreadystatechange = function() 
                              { 
                                if (objReq.readyState == 4)  // REQUEST IS FINISHED
                                {
                                  if (objReq.status == 200)  // REQUEST WAS SUCCESSFUL
                                  {
                                    if (callBackFunc.length > 0)
                                    {
                                      callBackFunc = eval(callBackFunc);
                                      callBackFunc(objReq);
                                    }
                                    if (divAreaMessageID.length > 0 && message.length > 0)    
                                    {
                                      changeInnerHTML(divAreaMessageID,"&nbsp;");
                                    }
                                  }
                                  else if (divAreaErrors.length > 0)  // REQUEST FAILED
                                  {
                                    changeInnerHTML(divAreaErrors,objReq.responseText);
                                  }
                                }
                                else if (divAreaMessageID.length > 0 && message.length > 0)  // REQUEST IS RUNNING
                                {
                                  changeInnerHTML(divAreaMessageID,message);
                                }
                              }
  objReq.send(objXMLDoc);
}

/* ------------------------------------------------------------------------------------- */

function getXMLHttpRequest(objReq,url,divAreaErrors,divAreaMessageID,message,callBackFunc,async)
{
  objReq.open("get", url, async);
  objReq.onreadystatechange = function() 
                              { 
                                if (objReq.readyState == 4)  // REQUEST IS FINISHED
                                {
                                  if (objReq.status == 200)  // REQUEST WAS SUCCESSFUL
                                  {
                                    if (callBackFunc.length > 0)
                                    {
                                      callBackFunc = eval(callBackFunc);
                                      callBackFunc(objReq);
                                    }
                                    if (divAreaMessageID.length > 0 && message.length > 0)    
                                    {
                                      changeInnerHTML(divAreaMessageID,"&nbsp;");
                                    }
                                  }
                                  else if (divAreaErrors.length > 0)  // REQUEST FAILED
                                  {
                                    changeInnerHTML(divAreaErrors,objReq.responseText);
                                  }
                                }
                                else if (divAreaMessageID.length > 0 && message.length > 0)  // REQUEST IS RUNNING
                                {
                                  changeInnerHTML(divAreaMessageID,message);
                                }
                              }
  objReq.send(null);
}

/* ------------------------------------------------------------------------------------- */

function createParserObject(strXML)
{  
  var browser = navigator.appName;
  
  if (browser == "Microsoft Internet Explorer")
  {
    var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.loadXML(strXML);
  } 
  else
  {
    var objDOMParser = new DOMParser();
    var xmlDoc = objDOMParser.parseFromString(strXML,"text/xml");
  }
  return xmlDoc;
}

/* ------------------------------------------------------------------------------------- */

function createDocumentObject(nameSpace,rootName)
{
  var xmlDoc;
  var browser = navigator.appName;
  
  if (rootName.length == 0)
  {
    rootName = "XMLroot";
  }
  
  if (browser == "Microsoft Internet Explorer")
  {
    xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.loadXML("<" + rootName + "/>");
  } 
  else
  {  
    xmlDoc = document.implementation.createDocument(nameSpace,rootName,null);
    if (xmlDoc.documentElement == null)
    {
      xmlDoc.appendChild(xmlDoc.createElement(rootName));
    }
    if (navigator.userAgent.indexOf("Safari") == -1)
    {
      // STANDARDIZE the loadXML FUNCTION AND THE xml PROPERTY
      Document.prototype.loadXML = function(strXML)
      {
        var objDOMParser = new DOMParser();
        var objDoc = objDOMParser.parseFromString(strXML,"text/xml");
      }
  
      Document.prototype.__defineGetter__("xml", function () 
      {
         return (new XMLSerializer()).serializeToString(this);
      });
    }
  }
  return xmlDoc;
}

/* ------------------------------------------------------------------------------------- */

function addNode(XMLDoc,name,val)
{
  val = val.replace(/&/g,'\&');
  val = val.replace(/</g,'\<');
  val = val.replace(/>/g,'\>');

  var root = XMLDoc.documentElement;
  var newNode = XMLDoc.createElement(name);
  var newTextNode = XMLDoc.createTextNode(val);
  newNode.appendChild(newTextNode);
  root.appendChild(newNode);
}

/* ------------------------------------------------------------------------------------- */

function getNodeVal(objParser,nodeName)
{
  if (objParser.getElementsByTagName(nodeName)[0].firstChild)
  {
    return objParser.getElementsByTagName(nodeName)[0].firstChild.nodeValue;
  }
  else
  {
    return "";
  }
}

/* ------------------------------------------------------------------------------------- */

function changeInnerHTML(divId,html)
{
  if (document.getElementById)
  {
    document.getElementById(divId).innerHTML= html;
  }
  else
  {
    document.layers[divId].document.open();
    document.layers[divId].document.write(html);
    document.layers[divId].document.close();
  }
}

/* ------------------------------------------------------------------------------------- */