function null_func() {}

// This is a much-simplified version of what's in prototype.js.
// But it appears to be nearly full-featured.  How did they make
// theirs bloat so much??!
function send_ajax_request(opts) {
  var done = 0;

  var method    = "get";
  var url       = null;
  var async     = true;
  var onsuccess = null_func();
  var onerror   = null_func();
  var body      = null;
  var timeout   = null;

  if (null != opts["method"])    method    = opts["method"];
  if (null != opts["url"])       url       = opts["url"];
  if (null != opts["async"])     async     = opts["async"];
  if (null != opts["onsuccess"]) onsuccess = opts["onsuccess"];
  if (null != opts["onerror"])   onerror   = opts["onerror"];
  if (null != opts["body"])      body      = opts["body"];
  if (null != opts["timeout"])   timeout   = opts["timeout"];

  var client;
  try { client = new XMLHttpRequest() } catch(e) {}
  try { client = new ActiveXObject('Msxml2.XMLHTTP') } catch(e) {}
  try { client = new ActiveXObject('Microsoft.XMLHTTP') } catch(e) {}

  client.onreadystatechange = function() {
    if (client.readyState == 4 && !done++) {
      client.onreadystatechange = null_func;
      var success;
      try { success = !client.status ||
        (client.status >= 200 && client.status < 300) } catch(e) {}
      success ? onsuccess(client) : onerror(client);
    }
  };

  // Huh??
  if (url.indexOf('?') > -1 &&
    /Konqueror|Safari|KHTML/.test(navigator.userAgent))
    url += "&_=";

  client.open(method.toUpperCase(), url, async);

  if (method == "post") {
    if (!body) body = "";
    client.setRequestHeader("Content-type",
      "application/x-www-form-urlencoded;charset=UTF-8");
    client.setRequestHeader("Content-length", body.length);
    // Some weird bug in older Mozilla: see Mozilla Bugzilla #246651.
    // Newer versions not getting length right so last /n is empty request.
    if (client.overrideMimeType)
    // (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
      client.setRequestHeader("Connection", "close");
  }

  client.send(body);

  // Firefox doesn't call onreadystatechange() when done sync request.
  if (!async && client.overrideMimeType && client.onreadystatechange)
    client.onreadystatechange();

  // Set a timeout if user wants to.  Not recommended, since it can't actually
  // stop the request from being processed.  But sometimes it's not important.
  if (async && timeout)
    setTimeout(function() {
      if (!done++) onerror(client);
    }, timeout*1000);
}
