/**
 * @fileoverview XMLHttpRequest with visual textual activity indicator
 * @author (R---S)
 */

/**
 * This method provides XML HTTP Request functionality including callback.
 *
 * @param {String}  url                 The URL where to place the AJAX request
 * @param {String}  requestMethod       The way the AJAX request should be placed: one out of {POST | GET | PUT}
 * @param {String}  passedData          The data that should be passed to the AJAX request
 * @param {String}  responseType        The response type of the AJAX request: one out of {Text | XML}
 * @param {Object}  callbackFunction    The callback function which should be called upon AJAX request completion
 * @param {Object}  callbackParameters  The parameters which should be passed to the callback function
 */
function AjaxRequest(url, requestMethod, passedData, responseType, callbackFunction, callbackParameters) {
  // determines whether the activity indicator should contain debugging info or not
  var verbose = false;

  // create a random id for the activity indicator
  var randomId = getRandomId_();
//  indicateAjaxActivity_(randomId, url.substring(url.lastIndexOf("/") + 1, url.length - 4) + "...", verbose);

  // create a request object
  var ajaxRequest = getXmlHttpRequestObject_(requestMethod, url, true, passedData);

  // when done, call the callback function
  ajaxRequest.onreadystatechange = function() {
    if (ajaxRequest.readyState == 4) {
      if (ajaxRequest.status == 200) {
        indicateAjaxActivityFinished_(randomId);
        if (responseType.toLowerCase() == "text") {
          if (callbackParameters === null) {
            callbackFunction(ajaxRequest.responseText);
          } else {
            callbackFunction(ajaxRequest.responseText, callbackParameters);
          }
        } else if (responseType.toLowerCase() == "xml") {
          if (callbackParameters === null) {
            callbackFunction(ajaxRequest.responseXML);
          } else {
            callbackFunction(ajaxRequest.responseXML, callbackParameters);
          }
        }
      } else {
        indicateAjaxActivityFinished_(randomId);
        //alert('There was a problem with the XML HTTP Request.\nAjax Request Ready State: ' + ajaxRequest.readyState + '\nAjax Request Status: ' + ajaxRequest.status);
      }
    }
  };

  // place the request
  if (requestMethod.toLowerCase() == "post") {
    ajaxRequest.send(passedData);
  } else {
    ajaxRequest.send(null);
  }
};

/**
 * This method provides XML HTTP Request functionality including callback,
 * but this time the request is blocking.
 *
 * @param {String}  url            The URL where to place the AJAX request
 * @param {String}  requestMethod  The way the AJAX request should be placed: one out of {POST | GET | PUT}
 * @param {String}  passedData     The data that should be passed to the AJAX request
 * @param {String}  responseType   The response type of the AJAX request: one out of {Text | XML}
 */
function BlockingAjaxRequest(url, requestMethod, passedData, responseType) {
  // determines whether the activity indicator should contain debugging info or not
  var verbose = false;

  // create a random id for the activity indicator
  var randomId = getRandomId_();
  indicateAjaxActivity_(randomId, url.substring(url.lastIndexOf("/") + 1, url.length - 4) + "...", verbose);

  // create a request object
  var ajaxRequest = getXmlHttpRequestObject_(requestMethod, url, false, passedData);

  // place the request
  if (requestMethod.toLowerCase() == "post") {
    ajaxRequest.send(passedData);
  } else {
    ajaxRequest.send(null);
  }

  // when done, call the callback function
  if (ajaxRequest.status == 200) {
    indicateAjaxActivityFinished_(randomId);
    if (responseType.toLowerCase() == "text") {
      return ajaxRequest.responseText;
    } else if (responseType.toLowerCase() == "xml") {
      return ajaxRequest.responseXML;
    }
  } else {
    indicateAjaxActivityFinished_(randomId);
    //alert('There was a problem with the XML HTTP Request.\nAjax Request Ready State: ' + ajaxRequest.readyState + '\nAjax Request Status: ' + ajaxRequest.status);
  }
};

/**
 * This method creates an XML HTTP request object
 *
 */
function getXmlHttpRequestObject_(requestMethod, url, isAsynchronous, passedData) {
  var ajaxRequest = false;
  // create XMLHttpRequest Object for AJAX
  try {
    ajaxRequest = new XMLHttpRequest();
  } catch(e) {
    try {
      ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      try {
        ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {
        //alert("Unable to Create the XMLHttpRequest Object.\nTry Updating to a Current Web Browser.");
        return false;
      }
    }
  }

  if (requestMethod.toLowerCase() == "post") {
    // open the connection
    ajaxRequest.open(requestMethod, url, isAsynchronous);
    // some header settings required for post
    ajaxRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    ajaxRequest.setRequestHeader("Method", "POST " + url + " HTTP/1.1");
  } else {
    // open the connection, concat passed data as GET variables
    ajaxRequest.open(requestMethod, url+'?'+passedData, isAsynchronous);
    // some header settings required for get
    // this disables caching of GET operations
    ajaxRequest.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');
  }
  return ajaxRequest;
};

// global variable which holds the number of active indicators
var globalIndicatorCounter = 0;

// global array which holds the timeout ids of the ajax indicators
var globalIndicatorUpdateTimeout = new Array();

/**
 * This method provides visual textual feedback to signalize ajax activity.
 *
 * @param {String}   randomId  The random id of the activity indicator div object
 * @param {String}   url       The url where the ajax request is placed
 * @param {Boolean}  verbose   Whether the indicator should contain debugging info
 */
function indicateAjaxActivity_(randomId, url, verbose) {
  // if we do not already have an activity indicator then create one
  if (!document.getElementById(randomId)) {
    var indicator = document.createElement("div");
    indicator.style.padding = "0.2em";
    indicator.style.position = "fixed";
    indicator.style.top = 10 + globalIndicatorCounter * 4 + "px";
    indicator.style.right = 10 + globalIndicatorCounter * 4 + "px";
    indicator.style.zIndex = 1 + globalIndicatorCounter;
    indicator.style.backgroundColor = "rgb(" + createRgbComponent_() + ", " + createRgbComponent_() + ", " + createRgbComponent_() + ")";
    indicator.style.opacity = "1.0";
    // we have created one activity indicator
    globalIndicatorCounter++;
    indicator.id = randomId;
    var anchor = document.createElement("a");
    anchor.onclick = function () {
      indicateAjaxActivityFinished_(randomId);
      return false;
    };
    anchor.style.textDecoration = "none";
    anchor.style.color = "white";
    anchor.style.fontWeight = "bold";
    anchor.style.fontSize = "0.8em";
    if (verbose) {
      anchor.appendChild(document.createTextNode("Loading..."));
      anchor.appendChild(document.createElement("br"));
      anchor.appendChild(document.createTextNode("Ajax Request ID: " + randomId));
      anchor.appendChild(document.createElement("br"));
      anchor.appendChild(document.createTextNode("Currently Active Ajax Requests: " + globalIndicatorCounter));
      anchor.appendChild(document.createElement("br"));
      anchor.appendChild(document.createTextNode("Accessing: " + url));
      anchor.appendChild(document.createElement("br"));
    } else {
      anchor.appendChild(document.createTextNode("Loading " + url));
    }
    indicator.appendChild(anchor);
    document.body.insertBefore(indicator, document.getElementsByTagName("body")[0].firstChild);
  } else {
    // else if we already have an activity indicator then re-use it
    var indicator = document.getElementById(randomId);
    indicator.style.backgroundColor = "rgb(" + createRgbComponent_() + ", " + createRgbComponent_() + ", " + createRgbComponent_() + ")";
    window.clearTimeout(globalIndicatorUpdateTimeout[randomId]);
  }
  globalIndicatorUpdateTimeout[randomId] = window.setTimeout(function() { indicateAjaxActivity_(randomId, url, verbose); return false; }, 500);
};

/**
 * This method destroys the graphical feedback div
 *
 * @param {String}  randomId  The random id of the activity indicator div object
 */
function indicateAjaxActivityFinished_(randomId) {
  // we have destroyed one activity indicator
  globalIndicatorCounter--;
  window.clearTimeout(globalIndicatorUpdateTimeout[randomId]);
  try {
    document.body.removeChild(document.getElementById(randomId));
   } catch (e) {
     return false;
   }
};

/**
 * This method returns a random value between 0 and 255
 * as needed for an RGB color value
 *
 */
function createRgbComponent_() {
  return Math.round((Math.random() * 255) % 255);
};

/**
 * This method returns a random id between 0 and 10.000
 *
 */
function getRandomId_() {
  return Math.round(Math.random() * 10000);
};
