/*
 * Wrapper function library for XMLHttpRequest (ie AJAX).
 *
 * This library encapsulates the machinery required to set up callbacks to
 * user functions in a manner that is threadsafe (handles multiple
 * simultaneous requests), makes minimal use of global variables (one is
 * required to ensure memory cleanup in IE), and does not leak memory in IE
 * due to lost circular references that cannot be garbage-collected.
 *
 * There are two main entry points, get_request and post_form, which
 * implement HTTP GET and POST, respectively.  post_form is intended for
 * use with an HTML form and encodes its elements as the body of the
 * request in the usual manner.
 *
 * The basic usage pattern is very simple; you call get_request with a url
 * and a callback function you would like to be invoked when the request
 * completes.  Additionally as a convenience you may pass a single argument
 * to be passed along to your callback.
 *
 * IE cleanup: to be totally neat and clean you must call xmlhttp_cleanup()
 * in your onunload handler (usually you would define
 * onunload="xmlhttp_cleanup()" as an attribute of the page's BODY
 * element). It is safe to do this in other browsers too, and might even
 * help their garbage collectors to be more efficient. If you forget to do
 * this, users will leak some memory if they leave the calling page before
 * a request completes.
 *
 */

var xmlhttprequest_stack=[];
var xmlhttprequest_count=0;

var latest = [];

// entry point: HTTP GET url; invokes callback on success w/result and userdata
// signature of callback is: callback(req, userdata) where req is the
// XmlHTTP Request object; req.responseText contains the server's response
function get_request (url, callback, userdata)
{
  invoke_request ("GET", url, callback, null, userdata);
}

// entry point: HTTP POST url with form's elements data as body
// invokes callback on success w/result and userdata
// Currently only checkbox and text input form elements are supported
// correctly.
// signature of callback is: callback(req, userdata) where req is the
// XmlHTTP Request object; req.responseText contains the server's response
function post_form (url, callback, form, userdata)
{
   var body = encode_form_data (form);
   invoke_request ("POST", url, callback, body, userdata);
}

function invoke_request (method, url, callback, body, userdata)
{
  var req;
  function closure () {
  	debug_info('closing ' + callback.name);
    handle_response (callback, req, userdata);
  }
  /* W3C version */
  if (window.XMLHttpRequest) {
    req = new XMLHttpRequest();
  	req.sent_time = new Date().getTime();
  }
  else if (window.ActiveXObject) {
    req = new ActiveXObject("Microsoft.XMLHTTP");
  }
  if (req) {
	debug_info('starting req');
	document.getElementById("loading").style.display="block";
	xmlhttprequest_count++;
    xmlhttprequest_stack.push (req);
    debug_info(callback.name);
    debug_info(callback.length);
    req.onreadystatechange = closure;
    req.open(method, url, true);
    if (method == "POST") {
      req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      req.setRequestHeader("Content-length", body.length);
      req.setRequestHeader("Connection", "close");
    }
  }
  debug_info('sending');
  req.send(body);
}

function encode_form_data (form)
{
  var encoded_pairs = [];
  for (var i = 0; i < form.elements.length; i++) {
    var element = form.elements[i];
    if (!element || !element.tagName)
      continue;
    var type = element.tagName.toLowerCase();
    var value = null;
    if (type == "input") {
        if (element.type == "hidden" || element.type == "text")
          value = element.value;
        else if (element.type == "checkbox") {
          value = element.checked ? element.value : null;
        }
        else if (element.type == "submit" || element.type == "button" || element.type == "reset") {
          // do nothing
        }
        else {
          alert (element.type + " not currently supported");
          // raise an error
          element.type = null;
          return null;
        }
    } else if (type == "textarea") {
        value = element.value;
    } else if (type == "select") {
      value = element.options[element.selectedIndex].value;
    } 
    // else alert (type + " not currently supported");

    if (value) {
      //alert (element.type + " " + element.name + "=" + value);
      encoded_pairs.push (element.name + "=" + encodeURIComponent(value));
    }
  }
  return encoded_pairs.join ("&");
}

function handle_response(callback, req, userdata) {
    // only if req shows "loaded"
    debug_info('req.readyState: ' + req.readyState);
    if (req.readyState == 4) {
      // break the circular reference to avoid IE memory leaks!
        xmlhttp_cleanup_req (req);
    	debug_info('req.status: ' + req.status);        
        if (req.status == 200 || status == 0) {
            // only if "OK"
            debug_info('latest: ' + latest[callback.name]);
            debug_info('sent_time: ' + req.sent_time);
            if (is_latest(req, callback)){
            	debug_info('callback ' + callback.name);
	            callback (req, userdata);
            } else {
            	debug_info('not calling back: ' + callback.name);
            }
        } else {
            alert("There was a problem retrieving the XML data:\n" +
                  req.statusText + ", status=" + req.status);
        }
    }
}

function is_latest(req, callback){
	debug_info("got :" + callback.name);
	if(!req.sent_time){
		return true;
	} else {
		var time = latest[callback.name];
		if (!time || req.sent_time >= time){
			latest[callback.name] = req.sent_time;
			return true;
		} else {
			debug_info("dropping: " + callback.name);
			return false;
		}
	}
}

function empty() {
}

function xmlhttp_cleanup () {
  for (var i in xmlhttprequest_stack) {
    var req = xmlhttprequest_stack[i];
    req.onreadystatechange = empty;
    delete xmlhttprequest_stack[i];
  }
  document.getElementById("loading").style.display="none";
}

function xmlhttp_cleanup_req (req) {
  req.onreadystatechange = empty;
  for (var i=0; i< xmlhttprequest_stack.length; i++) {
    var xreq = xmlhttprequest_stack[i];
    if (req == xreq) {
      delete xmlhttprequest_stack[i];
      xmlhttprequest_count--;
      //alert('removed:' + xmlhttprequest_count);
      break;
    }
  }
  if (xmlhttprequest_count == 0){
  	document.getElementById("loading").style.display="none";
  }
}

