/*global ActiveXObject */

var PHEAA, 

$ = function (sName) {
  return document.getElementById(sName);
}, 

Modal = function () {
  var oModalFunctions = {}, 

  fnRenderIFrame = function (oElem) {  
    var oIFrame = oElem.iframe || null;
   
    // Create iFrame if browser is Internet Explorer if IFrame isn't present;
    if (document.all && !window.opera && !oIFrame) {
      oIFrame = document.createElement("iframe");
      oIFrame.setAttribute("src", "javascript:false");
      oIFrame.className = "windowIFrame";
      oElem.appendChild(oIFrame);
      
      // Save reference to iFrame;
      oElem.iframe = oIFrame;
    }
    
    // Return iFrame;
    return oIFrame;  
  }, 

  fnPositionModalContent = function () {
    /* 
    This method positions the modal content div;

    Found in: pageExamples/testing/modalTest.html;
    Hook in HTML: 
      div#modalWindow: Modal window with "modalWindow" id;
    Variables: none;
    Referenced in: Modal.fnSpawnModal method;
    Activated when: when modal window is spawned and when browser windows with active modal windows are scrolled;
    */

    var iTopPosition, iLeftPosition, 
    
    oModal = $("modalWindow"); // Get reference to modal window;
    
    // Do not proceed if there are not saved references to the content div;
    if (!oModal || !oModal.oContent) { 
      return false; 
    }

    // Compute position of modal content div;
    iTopPosition = (((window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - oModal.oContent.offsetHeight) / 2)  + (document.body.scrollTop || document.documentElement.scrollTop);  
    iLeftPosition = (((window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - oModal.oContent.offsetWidth) / 2) + (document.body.scrollLeft || document.documentElement.scrollLeft);    

    // Position div;
    oModal.oContent.style.top = iTopPosition > 5 ? iTopPosition +  "px" : "5px";
    oModal.oContent.style.left = iLeftPosition > 5 ? iLeftPosition + "px" : "5px";
    
  },
    
  fnResizeModalWindow = function () {
    /* 
    This method resizes and positions modal windows and modal contents;

    Found in: pageExamples/testing/modalTest.html;
    Hook in HTML: 
      div#modalWindow: Modal Window with "modalWindow" id;
    Variables: none;
    Referenced in: Modal.fnSpawnModal method;
    Activated when: when modal window is spawned and when browser windows with active modal windows are resized;
    */
    
    var oModal = $("modalWindow"); // Get reference to modal window;
    
    // Do not proceed if there are not saved references to the background div;
    if (!oModal || typeof oModal.oBackground === "undefined") {
      return false;
    }
  
    // Size height of background;
    oModal.oBackground.style.height = document.body.clientHeight >= document.documentElement.clientHeight ?
      document.body.clientHeight  + "px" : document.documentElement.clientHeight  + "px"; 
    // Size width of background;
    oModal.oBackground.style.width = document.documentElement.clientWidth >= document.body.clientWidth ?
      document.documentElement.clientWidth + "px" : document.body.clientWidth + "px"; 
  
    if (!oModal.oIframe) { // Check to see if the iFrame has been created;
      oModal.oIframe = fnRenderIFrame(oModal.oBackground);
    }
    fnPositionModalContent(); // Position content div;
  },
  
  fnSpawnModal = function (vModalText) {
    /* 
    This method draws a new modal window;

    Found in: pageExamples/testing/modalTest.html;
    Hook in HTML: none;
    Variables: 
      sModalText: text for including into modal window (String or DOM Object);
    Referenced in: Modal.fnCreateModalWindow method;
    Activated when: when invoked by fnCreateModalWindow method;
    */

    var i, iLength, oCloseAnchor, aFunctionList, 
    
    fnStopBubble = function (e) {
      e = e || window.event || {};
      
      if (e.stopPropagation) {
        e.stopPropagation();
      }
      e.cancelBubble = true;
    };
    
    // Create modal background div;
    this.oBackground = document.createElement("div");
    this.oBackground.setAttribute("id", "modalBackground");
    this.appendChild(this.oBackground);
    if (this.oBackground.addEventListener) {
      this.oBackground.addEventListener("click", fnStopBubble, false);
    } else if (this.oBackground.attachEvent) {
      this.oBackground.attachEvent("onclick", fnStopBubble);
    }
    
    // Create the modal content div;
    this.oContent = document.createElement("div");
    this.oContent.setAttribute("id", "modalContent");
    this.oContent.style.visibility = "hidden";
    this.appendChild(this.oContent);
    this.oContent.innerHTML = "";
    
    if (typeof vModalText === "string") {
      this.oContent.innerHTML = vModalText; // Transfer contents of AJAX text into modal content div;
    } else {
      this.oContent.appendChild(vModalText); // Insert HTML Fragment into DOM;
    }

    // Get all close links in modal content;
    if (this.bShowClose) {
      // Create the div#tooltip a.tooltipClose node (only for windows wo/close links);
      oCloseAnchor = document.createElement("a");
      oCloseAnchor.setAttribute("href", "#");
      oCloseAnchor.className = "tooltipClose";
      oCloseAnchor.innerHTML = "Close";
      oCloseAnchor.onfocus = function () {
        if (this.blur) { 
          this.blur(); 
        }
      };
      this.oContent.appendChild(oCloseAnchor);
      if (oCloseAnchor.addEventListener) {
        oCloseAnchor.addEventListener("click", Modal.closeModal, false);
      } else if (oCloseAnchor.attachEvent) {
        oCloseAnchor.attachEvent("onclick", Modal.closeModal);
      }
    }

    fnResizeModalWindow(); // Size and position modal window components;
    this.oContent.style.visibility = "visible";
    window.focus(); // Bring current browser window to the top of the browser window stack;

    if (window.addEventListener) {
      window.addEventListener("resize", fnResizeModalWindow, false); // Attach resize function to resize event; 
      window.addEventListener("scroll", fnPositionModalContent, false); // Attach content positioning behavior to scrolling event;
    } else if (window.attachEvent) {
      window.attachEvent("onresize", fnResizeModalWindow); // Attach resize function to resize event; 
      window.attachEvent("onscroll", fnPositionModalContent); // Attach content positioning behavior to scrolling event;
    }
    
    if (this.sFuncName) {
      aFunctionList = this.sFuncName.split(",");
      for (i = 0, iLength = aFunctionList.length; i < iLength; i += 1) {
        if (oModalFunctions.hasOwnProperty(aFunctionList[i]) && oModalFunctions[aFunctionList[i]].constructor === Function) {
          oModalFunctions[aFunctionList[i]].call(this);
        }
      }
    }
  },
  
  fnCreateModalWindow = function (e) {
    /* 
    This method creates a new modal window when a modal link is clicked;

    Found in: pageExamples/testing/modalTest.html;
    Hook in HTML: 
      target href: href of link selected;
    Variables: 
      oObject: onClick event (Event Objectd);
    Referenced in: Modal.fnInit method;
    Activated when: when link is clicked;
    */
    
    var oLinkTarget, oModal, that, 
    reClassMatch = /[a-zA-Z_{}\[\]]*_class\{([^{}]*)\}[a-zA-Z_{}\[\]]*/, // Reg Exp to match class names passed in rel attribute;
    reFuncMatch = /[a-zA-Z_{}\[\]]*_func\[([^\[\]]*)\][a-zA-Z_{}\[\]]*/, // Reg Exp to match function names passed in rel attribute;
    reAjaxMatch = /#[^?]+/, // Reg Exp to extract message id request from rel attribute;
    sPathname = window.location.pathname, // Save a reference to the window's pathname;
    
    oObject = e || window.event,
    oObjectTarget = oObject.currentTarget || oObject.srcElement;
    
    if (!oObject.vMessage && (!oObjectTarget || typeof oObjectTarget.href === "undefined")) { // End function if there is no href attribute;
      return false; 
    } else if (oObject.preventDefault) {
      oObject.preventDefault(); // Prevent default action of link for w3c;
    }
    oObject.returnValue = false; // Prevent default action of link for IE;
    
    // Normalize the pathname across browsers if location is local;
    if (window.location.protocol === "file:") { 
      sPathname = sPathname.replace(/\\/g, "/"); 
    } 
    
    if ($("modalWindow")) { // Check for existance of an open modal window...;
      Modal.closeModal();
    }
    
    oModal = document.createElement("div");
    oModal.setAttribute("id", "modalWindow");
    document.body.appendChild(oModal);
    
    // Detect if there are any class names to append to the modal div;
    if (oObject.sClassNames) { 
      oModal.className = oObject.sClassNames; // Apply existing classes;
    } else if (oObject.currentTarget && oObject.currentTarget.rel.indexOf("_class{") !== -1) { // Parse classes and apply;
      oModal.className = oObjectTarget.rel.replace(reClassMatch, function (a, b) {
        return b;
      });
    }
    
    // Detect if there are any class names to append to the modal div;
    if (oObject.sFunctionNames) {
      oModal.sFuncName = oObject.sFunctionNames; // Apply existing functions;
    } else if (oObjectTarget && oObjectTarget.rel.indexOf("_func[") !== -1) { // Parse functions and apply;
      oModal.sFuncName = oObjectTarget.rel.replace(reFuncMatch, function (a, b) {
        return b;
      });
    }
    
    // Detect if the default close link should appear;
    if (oObject.bShowClose || (oObjectTarget && oObjectTarget.rel.indexOf("_showClose") !== -1)) { 
      oModal.bShowClose = true;
    }
    
    // Detect if the content node should be cloned;
    if (oObject.bCloneNode || (oObjectTarget && oObjectTarget.rel.indexOf("_cloneNode") !== -1)) {
      oModal.bCloneNode = true;
    }

    if (oObject.vMessage) { // If window is being invoked dynamically by the Modal.invokeModal method...;
      if (oModal.bCloneNode) {
        fnSpawnModal.call(oModal, oObject.vMessage.cloneNode(true)); // Spawn modal window with cloned content;
      } else {
        oModal.oReturnNode = oObject.vMessage.parentNode; // Else remember original parent node;
        fnSpawnModal.call(oModal, oObject.vMessage); // Spawn modal window with original content;
      }
    
    // Target anchor points to an interior div;
    } else if (oObjectTarget.href.indexOf(sPathname) !== -1 &&
               sPathname !== "/" && 
               oObjectTarget.href.indexOf("#") !== -1) { 
      oLinkTarget = $(oObjectTarget.href.slice(oObjectTarget.href.indexOf("#") + 1)); // Get target div;
      if (!oLinkTarget) { // If the target div doesn't exit, close modal window;
        Modal.closeModal();
      } else {
        oModal.oReturnNode = oLinkTarget.parentNode; // Else remember original parent node;
      }
      if (oModal.bCloneNode) {
        fnSpawnModal.call(oModal, oLinkTarget.cloneNode(true)); // Spawn modal window with cloned content;
      } else {
        fnSpawnModal.call(oModal, oLinkTarget); // Spawn modal window with original content;
      }
    } else { // Target anchor points to an external page;
      that = oObjectTarget;
      PHEAA.ajax({ // Make AJAX call to content;
        url: that.href.replace(reAjaxMatch, ""),
        data: "html",
        type: "GET",
        onSuccess: function (sData) {
          var iEndOfString = that.href.indexOf("?") !== -1 ? that.href.indexOf("?") : that.href.length, 
          iHash = that.href.slice(that.href.indexOf("#") + 1, iEndOfString),
          iBegin = sData.indexOf("<div id=\"" + iHash + "\""), // Get the beginning of the selected area;
          iEnd = sData.indexOf("<!--end div#" + iHash + "-->"),  // Get the end of the selected area;
          sTip = sData.slice(iBegin, iEnd); // Grab all text in between;

          if (sTip.length <= 0) { // Close modal window if there is no message;
            Modal.closeModal();
          }
	
          fnSpawnModal.call(oModal, sTip); // Spawn modal window;
        }
      });
    }
  };
  
  return {
    /**
    * @function addModalFunction
    * @param {String} sName Name of function to register with the modal window method
    * @param {Function} fnFunc Function to register with the modal window method
    * @return {None} 
    * @describe <p>This method registers functions for modal windows to call when launched. All functions are stored in a private Modal object. When the function is subsequently launched, "this" refers to the modal window div element. Multiple function names can be passed as comma-separated values.</p>
    * @example
// Registering a function with the modal window funciton object...
Modal.addModalFunction("fnSomeFunction", function () {
  alert(this.nodeName);
});

// Associating a function with a modal link within the page...
<a href="#modalTest" rel="modal_func[fnSomeFunction]">Launch Modal Window</a>
    */

    //<source>
    addModalFunction: function (sName, fnFunc) {
      if (!oModalFunctions.hasOwnProperty(sName) && !oModalFunctions[sName]) {
        oModalFunctions[sName] = fnFunc;
      }
    },
    //</source>

    /**
    * @function closeModal
    * @param {Object} e (Optional) Event Object
    * @return {None} 
    * @describe <p>This method closes an active modal window.</p>
    * @example
Modal.closeModal();
    */

    //<source>
    closeModal: function (e) {
      var oModal = $("modalWindow"); // Find modal window;
      
      e = e || window.event || {}; // Normalize event object;
      
      if (e.preventDefault) {
        e.preventDefault(); // Disactivate link for w3c;
      }
      e.returnValue = false; // Disactivate link for IE;
      
      if (oModal) { // If modal window is present...;
      
        // If the content was not cloned but has a return node;
        if (!oModal.bCloneNode && oModal.oReturnNode) { 
          // Return content to original parent node;
          oModal.oReturnNode.appendChild(oModal.oContent.firstChild); 
        }
        
        oModal.parentNode.removeChild(oModal); // Remove modal window from DOM;
    
        if (window.removeEventListener) {
          window.removeEventListener("resize", fnResizeModalWindow, false); // Remove resize function from resize event; 
          window.removeEventListener("scroll", fnPositionModalContent, false); // Remove position function from scroll event;
        } else if (window.detachEvent) {
          window.detachEvent("onresize", fnResizeModalWindow); // Remove resize function from resize event; 
          window.detachEvent("onscroll", fnPositionModalContent); // Remove position function from scroll event;
        }
      } 
      
      return false;
    },
    
    //</source>
  
    /**
    * @function initModal
    * @param {None} 
    * @return {Array} - Array of modal links
    * @describe <p>This method initializes each modal link.</p>
    <p>Optional settings can be passed inline by the link's rel attribute:</p>
    <ul>
      <li>"_class{someClass}": Applys class name(s) contained within the curly braces.</li>
      <li>"_showClose": Turns on an automatic "Close Link" link in the modal window.</li>
      <li>"_func[someFunction, someOtherFunction]": References functions saved to the modal window using the Modal.addModalFunction method. Format is a comma-deliminated string.</li>
    </ul>
    * @example
// Example of a link pointing to hidden, inline content...
<a href="#modalTest" rel="modal">Launch Modal Window containing hidden div</a>
      
// Example of a link pointing to an external page...
<a href="../../lib/js/modalContent.html#modalTest" rel="modal">Launch Modal Window 
  containing exterior page</a>

// External link with custom classname passed...
<a href="../../lib/js/modalContent.html#modalTest" rel="modal_class{someClass}">
  Launch Modal Window containing exterior page</a>

// External link with automatic "close link" activated...
<a href="../../lib/js/modalContent.html#modalTest" rel="modal_showClose">Launch 
  Modal Window containing exterior page</a>

// Clone,target, rather than move in the DOM, the target content...
<a href="../../lib/js/modalContent.html#modalTest" rel="modal_cloneNode">Launch 
  Modal Window containing exterior page</a>

// External link with associated functions (see Modal.addModalFunction 
// method for more details)...
<a href="../../lib/js/modalContent.html#modalTest" rel="modal_func[fnSomeFunction]">
  Launch Modal Window containing exterior page</a>
      */
    
    //<source>
    initModal: function () {
      // Test for w3c methods;
      if (!document.getElementById || !document.createElement) { 
        return false; 
      }
      
      var i, iLength, 
      
      // Instantiate array to store all matching links;
      aModalLinksReturned = [], 
      
      // Get links;
      cModalLinkArray = document.body.getElementsByTagName("a"); 
      
      // Iterate through links;
      for (i = 0, iLength = cModalLinkArray.length; i < iLength; i += 1) { 
        // Test to see if the link is an uninitialized modal link;
        if (cModalLinkArray[i].rel && cModalLinkArray[i].rel.match(/^modal/) && 
          !cModalLinkArray[i].bModalInit) {
          // Store reference that element has been initialized;
          cModalLinkArray[i].bModalInit = true;
          // Invoke fnCreateModalWindow on click event;
          if (cModalLinkArray[i].addEventListener) {
            cModalLinkArray[i].addEventListener("click", fnCreateModalWindow, false);
          } else if (cModalLinkArray[i].attachEvent) {
            cModalLinkArray[i].attachEvent("onclick", fnCreateModalWindow);
          }
          aModalLinksReturned.push(cModalLinkArray[i]); // Add link to return array;
        }
      }
      return aModalLinksReturned; // Return array of matching links;
    },
    
    //</source>
  
    /**
    * @function invokeModal
    * @param {Object} oPrefs Contains options for the new modal window
    * @return {None} 
    * @describe <p>This method allows client-side scripting to generate and launch a dynamic modal window.</p>
    * @example
var modalObject = {
  message: "Some Message", // Required, can be String or HTML Object;
  functionName: "fnSomeFunction, fnSomeOtherFunction", // Optional, comma-separated String of 
    function name(s) registered using the Modal.addModalFunction method;
  showClose: true, // Optional, true or false;
  className: "someClass", // Optional, String;
  cloneNode: false // Optional, true or false;
};
    
Modal.invokeModal(modalObject);
    */

    //<source>
    invokeModal: function (oPrefs) {
      if (!oPrefs.message) { // Test for required message attribute;
        return false;
      }
      
      var oModalObject = { // Build object to create modal window from;
        vMessage: oPrefs.message,
        sClassNames: oPrefs.className || null, 
        sFunctionNames: oPrefs.functionName || null, 
        bShowClose: oPrefs.showClose || false, 
        bCloneNode: oPrefs.cloneNode || false
      };
    
      fnCreateModalWindow(oModalObject); // Create modal window;
    }
    
  //</source>
  
  };
}();

if (window.addEventListener) {
  window.addEventListener("load", Modal.initModal, false);
} else if (window.attachEvent) {
  window.attachEvent("onload", Modal.initModal);
}

if (typeof PHEAA === "undefined") {
  PHEAA = {};	
}

PHEAA.ajax = function () {
	
  var fnGetXHR = function () { // Get XMLHttpRequest Method;

    var i, iLength, 
    aFactory = [ // XMLHttpRequest Factory functions;
      function () { 
        return new XMLHttpRequest(); 
      },
      function () { 
        return new ActiveXObject("Msxml2.XMLHTTP"); 
      },
      function () { 
        return new ActiveXObject("Microsoft.XMLHTTP"); 
      }
    ];
		
    for (i = 0, iLength = aFactory.length; i < iLength; i += 1) { // Process XMLHttpRequest Factory;
      try {
        aFactory[i]();
      } catch (e) {
        continue;
      }
      fnGetXHR = aFactory[i]; // Memorize the function;
      return aFactory[i]();
    }
		
    // If we get here than none of the methods worked;
    throw new Error("XMLHttpRequest object cannot be created.");
		
  }, 
	
  // Determine the success of the HTTP response;
  fnHttpSuccess = function (oResponse) {
    try {
      // If no server status is provided and we're actually requesting a local file then it was successful;
      return !oResponse.status && location.protocol === "file:" ||
	  
      // Any status in the 200 range is good;
      (oResponse.status >= 200 && oResponse.status < 300) ||
		
      // Successful if the document has not been modified;
      oResponse.status === 304 ||
		
      // Safari returns an empty status if the file has not been modified
      navigator.userAgent.indexOf("Safari") >= 0 && typeof oResponse.status === "undefined";
		
    } catch (e) {}
    
    // If checking the status failed then assume that the request failed;
    return false;
    
  }, 
  
  // Extract the correct data from the HTTP response;
  fnHttpData = function (oResponse, oOptions) {

    // Get the content-type header;
    var vData, 
    sContentType = oResponse.getResponseHeader("content-type");

    // If a HEAD request was made, determine which header name/value pair to return (or all of them) and exit function;
    if (oOptions.type === "HEAD" && oOptions.header) {
      return oOptions.header === "all" ? oResponse.getAllResponseHeaders() : oResponse.getResponseHeader(oOptions.header);
    }

    // Get the xml document object if xml was returned from the server, otherwise return the text contents;
    vData = oOptions.data === "xml" || (!oOptions.data && sContentType && sContentType.indexOf("xml") >= 0) ? 
      oResponse.responseXML : oResponse.responseText;

    // If the specified type is "json", execute the returned text response as if it were javascript;
    if (oOptions.data === "json") {
      return PHEAA.jsonParse(vData);
    }

    /* Return the response data (either an xml document or a text string) & pass the custom object along 
       if one was defined (else the callback function, i.e. onSuccess, won't have access to any variables 
       passed by the 'parent' for loop) */
    return oOptions.customObj ? [vData, oOptions.customObj] : vData;
  };

  /*****************************************************************************************************************/
  /*****************************************************************************************************************/
  /**
    * @function PHEAA.ajax
    * @param {Object} options An object literal.
    * @return {String/XML/JSON}
    * @describe <p>Originally taken from a John Resig book but has since been significantly altered.</p>
    * @example
PHEAA.ajax({
  url: sURL,
  data: "html",
  type: "POST",
  asynch: false,
  onSuccess: function (sResponse) {
    $("myDiv").innerHTML = sResponse.HTMLify();
  }
});
  */
  //<source>
  return function (oOptions) {

    // Load the options object w/ defaults if no values were provided;
    oOptions = {
      // The type of HTTP request;
      type: oOptions.type || "POST",
      postvars: oOptions.postvars || "",
	
      // The header that will be returned (for HEAD requests only);
      header: oOptions.header || "",
	
      // The url the request will be made to;
      url: oOptions.url || "",
	
      // How long to wait before considering the request to be a timeout;
      timeout: oOptions.timeout || 5000,
	
      /* Functions to call when the request fails, succeeds,
         or completes (either fail or succeed) */
      onComplete: oOptions.onComplete || function () {},
      onError: oOptions.onError || function () {},
      onSuccess: oOptions.onSuccess || function () {},
	
      // Allow for the inclusion of a custom object;
      customObj: oOptions.customObj || "",
	
      /* Allow for the inclusion of an animated gif to be 
      displayed while readyState > 4 */
      image: oOptions.image || "",
      // And the elem to put it in;
      elem: oOptions.elem || "",
	
      /* The data type that'll be returned from the server;
         The default is simply to determine what data was returned 
         and act accordingly */
      data: oOptions.data || "",
	
      /* i.e., Tooltip and the JavaScript library on page load need
         a synchronous request, all others should default to asynchronous*/
      asynch: oOptions.asynch === false ? false : true, 

      // Optional custom user agent for request header;
      userAgent: oOptions.userAgent || null
			
    };

    // Create the request object;
    var timeoutLength, i, n, iLength, nLength, oElem, 
    formData = "", 
    requestDone = false, 
    xml = fnGetXHR();

    // Open the asynchronous request (based upon the type of request);
    if (oOptions.type === "HEAD") {
      xml.open(oOptions.type, oOptions.url);
    } else {
      xml.open(oOptions.type, oOptions.url, oOptions.asynch);
    }
  
    // We're going to wait for a request for 5 seconds before giving up;
    timeoutLength = oOptions.timeout;
  
    // Keep track of whent the request has been successfully completed;
    requestDone = false;
  
    /* Initialize a callback which will fire 5 seconds from now, 
    canceling the request if it has not already occurred */
    setTimeout(function () { 
      requestDone = true;
    }, timeoutLength);
  
    // Watch for when the state of the document gets updated;
    xml.onreadystatechange = function () {
      /* Wait until the data is fully loaded and make sure that the request
         hasn't already timed out*/
      /* Display the image only if there's an image and a provided element
         to place it into*/
      if (oOptions.image && oOptions.elem) {
        if (xml.readyState <= 4) {
          var myObj = oOptions.elem.constructor === String ? 
	  document.getElementById(oOptions.elem) : oOptions.elem;
          myObj.innerHTML = "<img src=\"" + oOptions.image + "\" alt=\"\" />";
        }
      }

      if (xml.readyState === 4 && !requestDone) {
        // Check to see if the request was successful;
        if (fnHttpSuccess(xml)) {
          // Execute the success callback w/ the data returned from the server;
          oOptions.onSuccess(fnHttpData(xml, oOptions));
		
        // Otherwise, an error occurred so execute the error callback;
        } else {
          oOptions.onError();
        }
			
        // Call the completion callback;
        oOptions.onComplete();
	  
        // Clean up after ourselves to avoid memory leaks;
        xml = null;
      }
    };
    
    if (oOptions.userAgent) { // If a custom userAgent is sent...;
      // Set request user-agent;
      xml.setRequestHeader("User-Agent", oOptions.userAgent); 
    }

    // Establish the connection to the server;
    if (oOptions.type === "POST") {
      for (i = 0, iLength = oOptions.postvars.length; i < iLength; i += 1) {
        oElem = oOptions.postvars[i];
        switch (oElem.nodeName.toLowerCase()) {
        case "input":
          if (oElem.type === "checkbox") {
            formData += oElem.name + "=" + encodeURIComponent(oElem.checked) + "&";
          } else if (oElem.type === "radio") {
            if (oElem.checked) {
              formData += oElem.name + "=" + encodeURIComponent(oElem.value) + "&";
            }
          } else {
            formData += oOptions.postvars[i].name + "="; 
            formData += encodeURIComponent(oOptions.postvars[i].value) + "&";
          }
          break;
		
        case "select":
          if (oElem.multiple) {
            for (n = 0, nLength = oElem.length; n < nLength; n += 1) {
              formData += oElem.value + "=" + encodeURIComponent(oElem.selected) + "&";
            }
          } else {
            formData += oElem.name + "=" + encodeURIComponent(oElem.value) + "&";
          }
          break;
		  
        default:
          formData += oOptions.postvars[i].name + "=";
          formData += encodeURIComponent(oOptions.postvars[i].value) + "&";
        } //end switch;
      }
      xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      xml.send(formData);
    } else {
      xml.send(null);
    }

  };
  //</source>
  
}();

PHEAA.jsonParse = function () {
  /* This is a function that can parse a JSON text, producing a JavaScript 
     data structure. It is a simple, recursive descent parser. */
  
  var iAt, // The index of the current character;
  sChar, // The current character;
  oEscapee = {
    '"': '"', 
    '\\': '\\', 
    '/': '/', 
    b: 'b', 
    f: '\f', 
    n: '\n', 
    r: '\r', 
    t: '\t'
  }, 
  sText, 
      
  // Call error when something is wrong;
  fnError = function (sMessage) {
    throw {
      name: 'SyntaxError', 
      message: sMessage, 
      at: iAt, 
      text: sText
    };
  }, 
    
  // If a sNextChar parameter is provide, verify that it matches the current character;
  fnNext = function (sNextChar) { 
    if (sNextChar && sNextChar !== sChar) {
      fnError("Expected '" + sNextChar + "' instead of '" + sChar + "'");
    }
      
    // Get the next character. When there are no more charcters, return the empty string;
    sChar = sText.charAt(iAt);
    iAt += 1;
    return sChar;
  }, 
      
  fnNumber = function () { // Parse a number value;
    var iNumber, sString = '';
    
    if (sChar === '-') {
      sString = '-';
      fnNext('-');
    } 
    while (sChar >= '0' && sChar <= '9') {
      sString += sChar;
      fnNext();
    }
    if (sChar === '.') {
      sString += '.';
      while (fnNext() && sChar >= '0' && sChar <= '9') {
        sString += sChar;
      }
    }
    if (sChar === 'e' || sChar === 'E') {
      sString += sChar;
      fnNext();
      if (sChar === '-' || sChar === '+') {
        sString += sChar;
        fnNext();
      }
      while (sChar >= '0' && sChar <= '9') {
        sString += sChar;
        fnNext();
      }
    }
    iNumber = +sString;
    if (isNaN(iNumber)) {
      fnError("Bad Number");
    } else {
      return iNumber;
    }
  },
      
  fnString = function () { // Parse a string value;
    var iHex, i, uffff, 
    sString = '';
      
    // When parsing for string values, we must look for " and \ characters;
    if (sChar === '"') {
      while (fnNext()) {
        if (sChar === '"') {
          fnNext();
          return sString;
        } else if (sChar === '\\') {
          fnNext();
          if (sChar === 'u') {
            uffff = 0;
            for (i = 0; i < 4; i += 1) {
              iHex = parseInt(fnNext(), 16);
              if (!isFinite(iHex)) {
                break;
              }
              uffff = uffff * 16 + iHex;
            }
          } else if (typeof oEscapee[sChar] === 'string') {
            sString += oEscapee[sChar];
          } else {
            break;
          }
        } else {
          sString += sChar;
        }
      }
    }
    fnError("Bad String");
  },
      
  fnWhite = function () { // Skip whitespace;
    while (sChar && sChar <= ' ') {
      fnNext();
    }
  }, 
      
  fnWord = function () { // true, false, or null;
    switch (sChar) {
    case 't':
      fnNext('t');
      fnNext('r');
      fnNext('u');
      fnNext('e');
      return true;
	
    case 'f':
      fnNext('f');
      fnNext('a');
      fnNext('l');
      fnNext('s');
      fnNext('e');
      return false;

    case 'n':
      fnNext('n');
      fnNext('u');
      fnNext('l');
      fnNext('l');
      return null;
    }
    fnError("Unexpected '" + sChar + "'");
  }, 
      
  fnValue, // Place holder for the value function;
      
  fnArray = function () { // Parse an array value;
    var aArray = [];
        
    if (sChar === '[') {
      fnNext('[');
      fnWhite();
      if (sChar === ']') {
        fnNext(']');
        return aArray; // Empty array;
      }
      while (sChar) {
        aArray.push(fnValue());
        fnWhite();
        if (sChar === ']') {
          fnNext(']');
          return aArray;
        }
        fnNext(',');
        fnWhite();
      }
    }
    fnError("Bad Array");
  },
      
  fnObject = function () { // Parse an object value;
    var sKey, oObject = {};
        
    if (sChar === '{') {
      fnNext('{');
      fnWhite();
      if (sChar === '}') {
        fnNext('}');
        return oObject; // Empty object;
      }
      while (sChar) {
        sKey = fnString();
        fnWhite();
        fnNext(':');
        oObject[sKey] = fnValue();
        fnWhite();
        if (sChar === '}') {
          fnNext('}');
          return oObject;
        }
        fnNext(',');
        fnWhite();
      }
    }
    fnError("Bad Object");
  };
        
  // Parse a JSON value. It could be an object, an array, a string, a number, or a word;
  fnValue = function () { 
    fnWhite();
    switch (sChar) {
    case '{':
      return fnObject();
     
    case '[':
      return fnArray();
     
    case '"':
      return fnString();
      
    case '-':
      return fnNumber();
      
    default:
      return sChar >= '0' && sChar <= '9' ? fnNumber() : fnWord();
    }
  };
  
  /*****************************************************************************************************************/
  /*****************************************************************************************************************/
  /**
    * @function PHEAA.jsonParse
    * @param {String} sSource String JSON value to be parsed
    * @param {Function} fnRevivier (Optional) Function to loop through against parsed JSON object
    * @return {Object}
    * @describe <p>Originally taken from Douglas Crockford's json_parse method.</p>
    * @example
var oData = PHEAA.jsonParse(sJSONString);
  */
  /* Return the jsonParse function. It will have access to all of the above 
     functions and variables */
  //<source>
  return function (sSource, fnRevivier) {
    var sResult;
    
    sText = sSource;
    iAt = 0;
    sChar = ' ';
    sResult = fnValue();
    fnWhite();
    if (sChar) {
      fnError("Syntax Error");
    }
    
    /* If there is a revivier function, we recursively walk the new structure, 
       passing each name/value pair to the revivier function for possible 
       transformation, starting with a temporary boot object that holds the 
       result in an empty key. If there is not a reviver function, we simply 
       return the result */
    
    return typeof fnRevivier === 'function' ?
      function fnWalk(oHolder, sKey) {
        var k, v, vValue = oHolder[sKey];
        if (vValue && typeof vValue === 'object') {
          for (k in vValue) {
            if (Object.hasOwnProperty.call(vValue, k)) {
              v = fnWalk(vValue, k);
              if (v !== undefined) {
                vValue[k] = v;
              } else {
                delete vValue[k];
              }
            }
          }
        }
        return fnRevivier.call(oHolder, sKey, vValue);
      }({'': sResult}, '') : sResult;
  };
  //</source>
}();