// threadsafe asynchronous XMLHTTPRequest code
function ajaxSend(url, callback, post, parameters){
  // we use a javascript feature here called "inner functions"
  // using these means the local variables retain their values after the outer function
  // has returned. this is useful for thread safety, so
  // reassigning the onreadystatechange function doesn't stomp over earlier requests.
  function ajaxBindCallback(){
    if (ajaxRequest.readyState == 4) {
      if (ajaxRequest.status == 200) {
        if (ajaxCallback){
          eval(ajaxCallback+'(ajaxRequest.responseXML)');
          return true;
        }
        else {
          alert('no callback defined');
          return false;
        }
      }
      else {
        alert("There was a problem retrieving the xml data");
        return false;
      }
    }
  }

  // use a local variable to hold our request and callback until the inner function is called...
  var ajaxCallback = callback;
  var now = new Date();
  var ajaxRequest = null;

  url += '&time=' + now.getTime();
  // bind our callback then hit the server...
  if (window.XMLHttpRequest) {
    ajaxRequest = new XMLHttpRequest();

    if (ajaxRequest.overrideMimeType)
      ajaxRequest.overrideMimeType('text/xml');
    //else
      //alert('Your browser does not support this function');

    // moz et al
    ajaxRequest.onreadystatechange = ajaxBindCallback;

    if (post == true) {
      ajaxRequest.open('POST', url, true);
      ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      ajaxRequest.setRequestHeader("Content-length", parameters.length);
      ajaxRequest.setRequestHeader("Connection", "close");
      ajaxRequest.send(parameters);
    }
    else {
      ajaxRequest.open("GET", url, true);
      ajaxRequest.send(null);
    }
  }

  else if (window.ActiveXObject) {
    // ie
    ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
    if (ajaxRequest)
      ajaxRequest.onreadystatechange = ajaxBindCallback;
    //else
      //alert('Your browser does not support this function');

    if (post == true) {
      ajaxRequest.open('POST', url, true);
      ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      ajaxRequest.setRequestHeader("Content-length", parameters.length);
      ajaxRequest.setRequestHeader("Connection", "close");
      ajaxRequest.send(parameters);
    }
    else {
      ajaxRequest.open("GET", url, true);
      ajaxRequest.send();
    }
  }

  else {
    //alert('Your browser does not support this function');
  }
}

function URLDecode(url)
{
   // Replace + with ' '
   // Replace %xx with equivalent character
   // Put [ERROR] in output if %xx is invalid.
   var HEXCHARS = "0123456789ABCDEFabcdef";
   var encoded = url;
   var plaintext = "";
   var i = 0;
   while (i < encoded.length) {
       var ch = encoded.charAt(i);
           if (ch == "+") {
               plaintext += " ";
                   i++;
           } else if (ch == "%") {
                        if (i < (encoded.length-2)
                                        && HEXCHARS.indexOf(encoded.charAt(i+1)) != -1
                                        && HEXCHARS.indexOf(encoded.charAt(i+2)) != -1 ) {
                                plaintext += unescape( encoded.substr(i,3) );
                                i += 3;
                        } else {
                                alert( 'Bad escape combination near ...' + encoded.substr(i) );
                                plaintext += "%[ERROR]";
                                i++;
                        }
                } else {
                   plaintext += ch;
                   i++;
                }
        } // while
   return plaintext;
}

function URLEncode(url)
{
        // The Javascript escape and unescape functions do not correspond
        // with what browsers actually do...
        var SAFECHARS = "0123456789" +                                        // Numeric
                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +        // Alphabetic
                                        "abcdefghijklmnopqrstuvwxyz" +
                                        "-_.!~*'()";                                        // RFC2396 Mark characters
        var HEX = "0123456789ABCDEF";

        var plaintext = url;
        var encoded = "";
        for (var i = 0; i < plaintext.length; i++ ) {
                var ch = plaintext.charAt(i);
            if (ch == " ") {
                    encoded += "+";                                // x-www-urlencoded, rather than %20
                } else if (SAFECHARS.indexOf(ch) != -1) {
                    encoded += ch;
                } else {
                    var charCode = ch.charCodeAt(0);
                        if (charCode > 255) {
                            alert( "Unicode Character '"
                        + ch
                        + "' cannot be encoded using standard URL encoding.\n" +
                                          "(URL encoding only supports 8-bit characters.)\n" +
                                                  "A space (+) will be substituted." );
                                encoded += "+";
                        } else {
                                encoded += "%";
                                encoded += HEX.charAt((charCode >> 4) & 0xF);
                                encoded += HEX.charAt(charCode & 0xF);
                        }
                }
        } // for

        return encoded;
}

function ajaxSendPost(form, url, callback) {
   var getstr = '';

   for (i=0; i < form.elements.length; i++) {
      el = form.elements[i];
      if (el.name != '') {
        if (el.type == "SELECT")
          getstr += el.name + "=" + URLEncode(el.options[el.selectedIndex].value) + "&";
        else
          getstr += el.name + "=" + URLEncode(el.value) + "&";
      }
   }
   ajaxSend(url, callback, true, getstr);
}