/*======================================================================================
' Copyright : Streamline Technologies, LLC - Nashville, TN
' Author	: Alex Short
' Created	: 4/20/2007
' Purpose	: Javascript functions for handling AJAX requests and responses
========================================================================================

========================================================================================
' Author    :  Matt Kruse <matt@ajaxtoolbox.com> 
' WWW       :  http://www.AjaxToolbox.com/
======================================================================================*/

function AjaxRequest() {
  var req = new Object();

  //Instance Properties
  req.timeout = 15000;					//Timeout period (in ms) until onTimeout function will be called, default 15 seconds
  req.generateUniqueUrl = true;			//Appends unique numeric value to the request URI to avoid caching
  req.url = window.location.href;		//URL that the request will be made to
  req.method = "GET";					//Method of the request, either GET, POST or HEAD
  req.async = true;						//Whether or not the request will be asynchronous
  req.username = null;					//The username used to access the URL
  req.password = null;					//The password used to access the URL
  req.parameters = new Object();		//Object that holds name/value pairs that are added to the URL for a GET request or the request content for a POST request
  req.requestIndex = AjaxRequest.numAjaxRequests++;		//Sequential index number of this request
  req.responseReceived = false;			//Indicates whether a response has been received from the server
  req.queryString = "";					//Querystring to be added to the end of a GET request
  req.responseText = null;				//Holds the text contents of the response
  req.responseXML = null;				//Holds the XML contents of the response
  req.status = null;					//Holds the status code of the response
  req.statusText = null;				//Holds the text description of the status code
  req.infoText = "";					//Added by Alex - Holds text description of the request being processed
  req.aborted = false;					//Whether or not the request has been aborted
  req.xmlHttpRequest = null;			//XMLHttpRequest object used internally

  //Event Handlers
  req.onTimeout = null;					//Function called if timeout period is reached before a response is received
  req.onLoading = null;					//Function called when readyState = 1
  req.onLoaded = null;					//Function called when readyState = 2
  req.onInteractive = null;				//Function called when readyState = 3
  req.onComplete = null;				//Function called when readyState = 4
  req.onSuccess = null;					//Function called after onComplete if the statusCode = 200
  req.onError = null;					//Function called after onComplete if the statusCode != 200
  req.onGroupBegin = null;				//Function called if this is the first request in the group to become active
  req.onGroupEnd = null;				//Function called if this is the last request in the group to complete

  //Get the XMLHttpRequest object
  req.xmlHttpRequest = AjaxRequest.getXmlHttpRequest();
  if (req.xmlHttpRequest == null) { return null; }

  //Attach the event handlers for the XMLHttpRequest object
  req.xmlHttpRequest.onreadystatechange = function() {
    if (req == null || req.xmlHttpRequest == null) { return; }
    if (req.xmlHttpRequest.readyState  ==  1) { req.onLoadingInternal(req); }
    if (req.xmlHttpRequest.readyState  ==  2) { req.onLoadedInternal(req); }
    if (req.xmlHttpRequest.readyState  ==  3) { req.onInteractiveInternal(req); }
    if (req.xmlHttpRequest.readyState  ==  4) { req.onCompleteInternal(req); }
  };
  
  //Flags to keep track if event has been handled
  req.onLoadingInternalHandled = false;
  req.onLoadedInternalHandled = false;
  req.onInteractiveInternalHandled = false;
  req.onCompleteInternalHandled = false;
  
  //Internal event that fires when readyState = 1 (loading)
  req.onLoadingInternal = function() {
    if (req.onLoadingInternalHandled) { return; }
    AjaxRequest.numActiveAjaxRequests++;
    if (AjaxRequest.numActiveAjaxRequests >= 1 && typeof(window['AjaxRequestBegin']) == 'function') {	//changed this to >= so AjaxRequestBegin is called every time a request starts
      //AjaxRequestBegin(req.requestIndex, req.infoText);		//Added req.infoText and req.requestId
      AjaxRequestBegin(req);
    }
    if (typeof(req.onLoading) == 'function') {
      req.onLoading(req);
    }
    req.onLoadingInternalHandled = true;
  };
  
  //Internal event that fires when readyState = 2 (loaded)
  req.onLoadedInternal = function() {
    if (req.onLoadedInternalHandled) { return; }
    if (typeof(req.onLoaded) == 'function') {
      req.onLoaded(req);
    }
    req.onLoadedInternalHandled = true;
  };
  
  //Internal event that fires when readyState = 3 (interactive)
  req.onInteractiveInternal = function() {
    if (req.onInteractiveInternalHandled) { return; }
    if (typeof(req.onInteractive) == 'function') {
      req.onInteractive(req);
    }
    req.onInteractiveInternalHandled = true;
  };
  
  //Internal event that fires when readyState = 4 (complete)
  req.onCompleteInternal = function() {
    if (req.onCompleteInternalHandled || req.aborted) { return; }
    req.onCompleteInternalHandled = true;
    AjaxRequest.numActiveAjaxRequests--;
    if (AjaxRequest.numActiveAjaxRequests >= 0 && typeof(window['AjaxRequestEnd']) == 'function') {		//changed this to >= so AjaxRequestEnd gets called every time a request ends
      //AjaxRequestEnd(req.requestIndex, req.infoText);
      AjaxRequestEnd(req);
    }
    req.responseReceived = true;
    req.status = req.xmlHttpRequest.status;
    req.statusText = req.xmlHttpRequest.statusText;
    req.responseText = req.xmlHttpRequest.responseText;
    req.responseXML = req.xmlHttpRequest.responseXML;
    if (typeof(req.onComplete) == 'function') {
      req.onComplete(req);
    }
    if (req.xmlHttpRequest.status == 200 && typeof(req.onSuccess) == 'function') {
      req.onSuccess(req);
    }
    else if (typeof(req.onError) == 'function') {
      req.onError(req);
    }
    delete req.xmlHttpRequest['onreadystatechange'];
    req.xmlHttpRequest = null;
  };
  
  //Internal event that fires if request timeout period is reached
  req.onTimeoutInternal = function() {
    if (req != null && req.xmlHttpRequest != null && !req.onCompleteInternalHandled) {
      req.aborted = true;
      req.xmlHttpRequest.abort();
      AjaxRequest.numActiveAjaxRequests--;
      //if (AjaxRequest.numActiveAjaxRequests == 0 && typeof(window['AjaxRequestEnd']) == 'function') {		//Removed by Alex so AjaxRequestEnd won't get called when a request times out
        //AjaxRequestEnd(req);
      //}
      if (typeof(window['AjaxRequestTimeout']) == 'function') {
        AjaxRequestTimeout(req);
      }
      //if (typeof(req.onTimeout) == 'function') {		//Removed by Alex to allow 1 standard AjaxRequestTimeout function
        //req.onTimeout(req);
      //}
      delete req.xmlHttpRequest['onreadystatechange'];
      req.xmlHttpRequest = null;
      }
    };

  //Makes the request
  req.process = function() {
    if (req.xmlHttpRequest != null) {
      if (req.generateUniqueUrl && req.method == 'GET') {
        req.parameters['AjaxRequestUniqueId'] = new Date().getTime() + '' + req.requestIndex;
      }
      var content = null; 
      for (var i in req.parameters) {
        if (req.queryString.length > 0) { req.queryString += "&"; }
        req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
      }
      if (req.method == "GET") {
        if (req.queryString.length > 0) {
          req.url += ((req.url.indexOf("?") > -1) ? '&':'?') + req.queryString;
        }
      }
      req.xmlHttpRequest.open(req.method, req.url, req.async, req.username, req.password);
      if (req.method == "POST") {
        if (typeof(req.xmlHttpRequest.setRequestHeader) != 'undefined') {
          req.xmlHttpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        }
        content = req.queryString;
      }
      if (req.timeout > 0) {
        setTimeout(req.onTimeoutInternal, req.timeout);
      }               
      req.xmlHttpRequest.send(content);
    }
  };

  //Handles object argument, which may contain either AjaxRequest field values or parameter name/values
  req.handleArguments = function(args) {
    for (var i in args) {
      if (typeof(req[i]) == 'undefined') {
        req.parameters[i] = args[i];
      }
      else {
        req[i] = args[i];
      }
    }
  };

  //Returns result of XMLHttpRequest.getAllResponseHeaders()
  req.getAllResponseHeaders = function() {
    if (req.xmlHttpRequest != null) {
      if (req.responseReceived) {
        return req.xmlHttpRequest.getAllResponseHeaders();
      }
      alert('Cannot getAllResponseHeaders because a response has not yet been received.');
    }
  };

  //Returns value of a response header as returned by XMLHttpRequest.getResponseHeader()
  req.getResponseHeader = function(headerName) {
    if (req.xmlHttpRequest != null) {
      if (req.responseReceived) {
        return req.xmlHttpRequest.getResponseHeader(headerName);
      }
      alert('Cannot getResponseHeader because a response has not yet been received.');
    }
  };

  return req;
}

//Static Class Variables
AjaxRequest.numActiveAjaxRequests = 0;
AjaxRequest.numAjaxRequests = 0;

//Returns an XMLHttpRequest object.  If an object cannot be instantiated, returns null
AjaxRequest.getXmlHttpRequest = function() {
  if (window.XMLHttpRequest) {
    return new XMLHttpRequest();
  } 
  else if (window.ActiveXObject) {
    /*@cc_on @*/
    /*@if (@_jscript_version >= 5)
    try {
      return new ActiveXObject('Msxml2.XMLHTTP');
    } catch (e) {
      try {
        return new ActiveXObject('Microsoft.XMLHTTP');
      } catch (E) {
        return null;
      }
    }
    @end @*/
  }
  else {
    return null;
  }
};

//Check if there are active requests
AjaxRequest.isActive = function() {
  return (AjaxRequest.numActiveAjaxRequests > 0);
};

//Make a GET request
AjaxRequest.get = function(args) {
  AjaxRequest.doRequest('GET', args);
};

//Make a POST request
AjaxRequest.post = function(args) {
  AjaxRequest.doRequest('POST', args);
};

//Internal method used by .get() and .post() methods
AjaxRequest.doRequest = function(method, args) {
  if (typeof(args) != 'undefined' && args != null) {
    var myRequest = new AjaxRequest();
    myRequest.method = method;
    myRequest.handleArguments(args);
    myRequest.process();
  }
};

//Submit a form.  Returns true if the submit was handled successfully
AjaxRequest.submit = function(theform, args) {
  var myRequest = new AjaxRequest();
  if (myRequest == null) { return false; }
  var serializedForm = AjaxRequest.serializeForm(theform);
  myRequest.method = theform.method.toUpperCase();
  myRequest.url = theform.action;
  myRequest.handleArguments(args);
  myRequest.queryString = serializedForm;
  myRequest.process();
  return true;
};

//Serialize a form into a format that can be sent as a GET string or a POST content.
AjaxRequest.serializeForm = function(theform) {
  var els = theform.elements;
  var len = els.length;
  var queryString = '';
  this.addField = function(name, value) { 
    if (queryString.length > 0) { 
      queryString += '&';
    }
    queryString += encodeURIComponent(name) + '=' + encodeURIComponent(value);
  };
  for (var i=0; i<len; i++) {
    var el = els[i];
    if (!el.disabled) {
      switch(el.type) {
        case 'text': case 'password': case 'hidden': case 'textarea': 
          this.addField(el.name, el.value);
          break;
        case 'select-one':
          if (el.selectedIndex >= 0) {
            this.addField(el.name, el.options[el.selectedIndex].value);
          }
          break;
        case 'select-multiple':
          for (var j=0; j<el.options.length; j++) {
            if (el.options[j].selected) {
              this.addField(el.name, el.options[j].value);
            }
          }
          break;
        case 'checkbox': case 'radio':
          if (el.checked) {
            this.addField(el.name, el.value);
          }
          break;
      }
    }
  }
  return queryString;
};