var calls = new Array();
var pendingResponseCount = 0;
var shouldDebugAjax = false;

// Browser-agnostic factory function
function createXMLHttpRequest() {
  var xmlHttpRequest;
  if (window.XMLHttpRequest) {
    return new XMLHttpRequest();
  } else if (window.ActiveXObject) {
    return new ActiveXObject('Microsoft.XMLHTTP');
  }
}

// The callback of xmlHttpRequest is a dynamically-generated function which
// immediately calls this function.
function onResponseStateChange(callKey) {

  call = calls[callKey];
  xmlHttpRequest = call.xmlHttpRequest;

  if (xmlHttpRequest.readyState < 4) { //Still waiting
    return;
  }

  if (xmlHttpRequest.readyState == 4) { //Transmit to actual callback
    callbackFunction = call.callbackFunction;
    if (!callbackFunction) { // Possibly still loading, e.g. in another JS file
      eval('f = function() { onResponseStateChange(' + callKey + '); }');
      setTimeout(f, 100); // TODO break if too many timeouts
    }
    if (call.isPlainTextResponse) {
      callbackFunction(xmlHttpRequest.responseText, call.callingContext);
    } else {
      callbackFunction(xmlHttpRequest.responseXML, call.callingContext);
    }
  }

  call = null;
  delete calls[callKey];
  pendingResponseCount--;

}

function createHTTPVarSpec(vars) {
    var varsString = "";
    for( key in vars ) {
      value = vars[key];
      escapePlusRE =  new RegExp("\\\+");
      value = value.replace(escapePlusRE, "%2B");
      varsString += '&amp;' + key + '=' + value;
    }
    if (varsString.length < 0) {
      varsString = varsString.substring(1); // chomp initial '&amp;'
    }
    return varsString;
}

/*
Provided for simple callbackFunction support
*/
function loopBack() { }

/*
Defaults all the fields of the parameter object that should be defaulted
*/
function setDefaults(paramObj) {

  if(typeof paramObj.callbackFunction != "function")
	paramObj.callbackFunction = loopBack;

  if(typeof paramObj.isPlainTextResponse == "undefined")
	paramObj.isPlainTextResponse = true;

  if(typeof paramObj.shouldGET == "undefined")
	paramObj.shouldGET = true;
	
  if(typeof paramObj.includeViewState == "undefined")
	paramObj.includeViewState = false;

  if(typeof paramObj.callingContext == "undefined")
	paramObj.callingContext = "";

  if(typeof paramObj.vars == "undefined")
	paramObj.vars = null;

}

/*
Extracts the parameter information from the custom attribute and 
turns it into a JavaScript object
*/
function getCallServerParameterObject(control) {

  var tmpObjStr = control.getAttribute("CallServerParameter");
  var tmpObj;
  eval("tmpObj = " + tmpObjStr.toString());
  
  setDefaults(tmpObj);
  
  return tmpObj;

}

/*
paramObj supports the following properties:
 url					:	[required] URL that the framework will post / get to
 controlID				:	[required] ID of the server side control, required for postback synchronisation
 callbackFunction		:	[default:loopBack] Function that is called when there is progress updates
 isPlainTextResponse	:	[default:true] Will the response from the server contain XML?
 shouldGET				:	[default:true] Determines if a GET or POST operation is required
 includeViewState		:	[default:false] Should the viewstate be sent back to the server? 
 callingContext			:	Context variable that is sent to the callBackFunction, value is ignored if callbackFunction is omitted
 vars					:	Concatenated list of variables that are sent to the server
*/
function callServer(paramObj) {

  setDefaults(paramObj);

  debugAlert("callServer() called. About to request URL\n"
          + "call key: [" + calls.length + "]\n"
          + "url: [" + paramObj.url + "]\n"
          + "callback function: [" + paramObj.callbackFunction + "]\n"
          + "returns plaintext?: [" + paramObj.isPlainTextResponse + "]\n"
          + "use GET protocol?: [" + paramObj.shouldGET + "]\n"
          + "calling context: [" + paramObj.callingContext + "]\n"
          + "vars: [" + paramObj.vars + "]");

  var xReq = createXMLHttpRequest();

  callKey = "" + calls.length;
  paramObj.xmlHttpRequest = xReq;
  calls[callKey] = paramObj;

  eval('f = function() { onResponseStateChange(' + callKey + '); }');
//  if (paramObj.shouldGET && paramObj.vars!=null) {
//    url += "?" + createHTTPVarSpec(paramObj.vars);
//  }
  xReq.onreadystatechange = f;
  xReq.open( (paramObj.shouldGET?"GET":"POST"), paramObj.url, true);
  xReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');

  var varsStringNew = "__EVENTTARGET=" + paramObj.controlID;

  if(paramObj.includeViewState && document.getElementById("__VIEWSTATE") != null) {
    varsStringNew += "&__VIEWSTATE=" + encodeURIComponent(document.getElementById("__VIEWSTATE").value);
  }

  if(paramObj.vars != null)
	varsStringNew += "&__EVENTARGUMENT=" + paramObj.vars;
  
  xReq.send(varsStringNew);
    
}

function debugAlert(message) {
  if (shouldDebugAjax) {
    alert(message);
  }
}

