/**
 *  basic functions for AJAX (Asynchronous Javascript and XML)
 *
 *  these functions base on a tutorial that can be found at
 *  http://www.get-the-code.de/code/javascript/ajax/index.html
 *
 *  @author   Tobias Hettinger, www.hettinger.info
 *  @version  $Id: ajax.js,v 1.12 2009/02/03 15:02:56 tobias Exp $
 */
 
 
//  constants
var WAT_AJAX_REQUEST_GET = 0;
var WAT_AJAX_REQUEST_POST = 1;
var WAT_AJAX_REQUEST_HEAD = 2;
var WAT_AJAX_REQUEST_XML = 3;

//  request id that was last used (automatically incremented by the wat_ajax_get_next_request_id function)
var watAjaxLastRequestId = -1;

//  semaphore that is used in the function to get an ajax request
var watAjaxSemaphore = 0;

//  request processing hash array, indexed by the request id
//
//  for each request id, the array contains
//  [0] : XMLHttpRequest object that is used for the data transfer
//  [1] : false, if the request object is available for a new request or true if the requestor is currently in use
//  [2] : optional data e.g. an affected div element
//  [3] : parameter data of the request (useful for debuging)
var WATAjaxRequestData = new Array();


/**
 *  function to get a XMLHttpRequest object
 *
 *  @return class  the function returns the created XMLHttpRequest or false
 *                 if no object could be instantiated
 */
function watAjaxGetXmlHttpRequest()
{
  //  initialize the result of the function
  var result = false;  
  
  //  try to instantiate a new XMLHtthRequest object
  try
  {
    //  try to load the native XMLHttpRequest object
    if (window.XMLHttpRequest)
    {
      result = new XMLHttpRequest();
    }
    else
    {
	    //  the Microsoft Internet Explorer has to load the XMLHttpRequester
	    //  as an ActiveXObject. Note that this will not work if the execution
	    //  of ActiveXObjects is disabled
	    if (window.ActiveXObject)
	    {
	      for (var i=5; i>=3; i--)
	      {
	        //  try to load the newest version of the XMLHttpRequest object first
	        try
	        {
	          result = new ActiveXObject("Msxml2.XMLHTTP." + i + ".0");
	          break;
	        }
	        catch (excNotLoadable)
	        {
	          result = false;
	        }
	      }
	    }
    }
  }
  catch (excNotLoadable)
  {
    //alert("XMLHttpRequest object creation failed");
    result = false;
  }  
  
  //  return the created XMLHttpRequest object or false if the object
  //  could not be created
  return result;
}

/**
 *  function to send a request
 *
 *  @param string source                   datasource on server (e.g. ajax.php)
 *  @param string data                     optional data to be sent to the server
 *  @param int    requestType              type of the request (WAT_AJAX_REQUEST_GET, WAT_AJAX_REQUEST_POST,
 *                                                              WAT_AJAX_REQUEST_HEAD, WAT_AJAX_REQUEST_XML)
 *  @param string processDataFunctionName  name of the (application dependent) function that processes the
 *                                         result from the server. The function may only have one parameter
 *                                         'requestId' which is used to identify the request. The function
 *                                         has to check if the requester is ready (readyState has to be 4) and
 *                                         if the request succeeded (status has to be 200). By using different
 *                                         specialized functions to process data, it is possible to distinguish
 *                                         different server requests
 *  @param object localData                optional data (e.g. an affected div element)
 *
 *  @return int  the function returns the used request id or -1 in case of a race condition
 */
function watAjaxSendRequest(source, data, requestType, processDataFunctionName, localData)
{
  //  start the critical region
  if (!watAjaxSemaphore++)
  {
    //  get the next free request
    var index = 0;
    var requestId = -1;
    var requests = WATAjaxRequestData.length;
    while (index < requests && WATAjaxRequestData[index][1]) index++;
    if (index < requests && !WATAjaxRequestData[index][1])
    {
      requestId = index;
      WATAjaxRequestData[index][1] = true;
      WATAjaxRequestData[index][2] = localData;
      xmlHttpRequest = WATAjaxRequestData[index][0];
    }
    else
    {
      //  create a new request id
      watAjaxLastRequestId++;  
      //  keep the data that is later used to process the server reply 
      WATAjaxRequestData[watAjaxLastRequestId] = new Array();
      //  reserve the object. This flag has to be manually reset by the function that parses the data.
      //  Therefore it can not happen that the requestor is accidently reused before all data of the
      //  prior request was processed
      WATAjaxRequestData[watAjaxLastRequestId][1] = true;
      WATAjaxRequestData[watAjaxLastRequestId][2] = localData;
      WATAjaxRequestData[watAjaxLastRequestId][0] = watAjaxGetXmlHttpRequest();
      xmlHttpRequest = WATAjaxRequestData[watAjaxLastRequestId][0];
      requestId = watAjaxLastRequestId;
    }
  
    //  send an empty string if no data was defined
    if (!data) { data = ''; }
    //  get the request type
    if (isNaN(requestType)) { requestType = WAT_AJAX_REQUEST_GET; }
    
    //  parse the query string
    data = data.replace(/&amp;/g, '&');
    if (requestType != WAT_AJAX_REQUEST_HEAD && (data && (data.substr(0,1)=='&' || data.substr(0,1)=='?')))
      data = data.substr(1, data.length);

    //  keep the query data
    WATAjaxRequestData[watAjaxLastRequestId][3] = (data ? '?' + data : '');
      
    //  start the request
    switch(requestType)
    {
      //  XML
      case WAT_AJAX_REQUEST_XML :
        //  open the connection
        var sourceFile = source + (data ? '?' + data : '');
        xmlHttpRequest.open('GET', sourceFile, true);
        xmlHttpRequest.setRequestHeader('Content-Type', 'text/xml');
        data = null;
        break;
        
      //  POST
      case WAT_AJAX_REQUEST_POST :
        //  open the connection
        xmlHttpRequest.open('POST', source, true);
        xmlHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xmlHttpRequest.setRequestHeader('Content-Length', data.length);
        break;
        
      //  HEAD
      case WAT_AJAX_REQUEST_HEAD :
        //  open the connection
        xmlHttpRequest.open('HEAD', source, true);
        data = null;
        break;
        
      //  GET
      default :
        //  open the connection
        var sourceFile = source + (data ? '?' + data : '');
        xmlHttpRequest.open('GET', sourceFile, true);
        data = null;
        break;
    }

    //  debug:
    //if (request_id > 15) alert('warning: there are more than 15 open requests');
    
    //  set the data event handler
    xmlHttpRequest.onreadystatechange = new Function('', processDataFunctionName + '(' + requestId + ')');
    //  send the data of the request
    xmlHttpRequest.send(data);
    //  reset the semaphore
    watAjaxSemaphore--;
    //  return the used request id
    return requestId;
  }
  else
  { 
    //  debug:
    //alert('warning: there is a race condition, the request was not sent, value of the semaphore: '+watAjaxSemaphore);
  
    //  reset the semaphore
    watAjaxSemaphore--;
    return -1;
  }
}

/** 
 *  dummy ajax response handler that can be used if no reaction on an ajax request is required
 *
 *  @param int requestId  id of the request
 */
function watAjaxDefaultResponse(requestId)
{
  //  get the used xml_http_request for the request
  var xmlHttpRequest = WATAjaxRequestData[requestId][0];
  
  //  check if the request was finished
  if (xmlHttpRequest.readyState == 4)
  {
    //  check if the request succeeded
    if (xmlHttpRequest.status == 200)
    {
      //  nothing to do ...

      //  debug: show the result
      //alert(xmlHttpRequest.responseText);
    }
    else
    {
      //  there was an error
      //alert('the ajax request failed with error \n\n' + xmlHttpRequest.statusText + ' (' + xmlHttpRequest.status + ')');
    }
    
    //  release the request object (this has to be done manually)
    WATAjaxRequestData[requestId][1] = false;
  }
}

/**
 *  recursive function to get the child controls of the passed node
 *
 *  @param object node    actually selected node
 *  @param object button  button that was pressed or null if no button is available
 *
 *  @result string  parameter list for all childs of the passed node
 */
function watAjaxSerializeFormRec(node, button)
{
  //  initialize the result
  var result = null;
  
  while(node)
  {
    //  initialize a node result
    var nodeResult = null;
    //  it depends on the node name what to do
    switch(node.nodeName.toLowerCase())
    {
      //  get an input field
      case 'input' :
        var type = node.getAttribute('type');
        if (type)
        {
          //  the result depends on the type
          switch(type.toLowerCase())
          {
            //  button
            case 'button' :
              var value = node.getAttribute('value');
              var name = node.getAttribute('name');
              if (button && button == node && name)
              {
                if (value) nodeResult = name + '=' + encodeURIComponent(value);
                  else nodeResult = name + '=';
              }
              break;
          
            //  checkbox
            case 'checkbox':
            case 'radio':
              var value = node.getAttribute('value');
              var name = node.getAttribute('name');
              if (node.checked && name)
              {
                if (value) nodeResult = name + '=' + encodeURIComponent(value);
                  else nodeResult = name + '=-1';
              }
              break;
            
            //  simply add name and value of the control
            default :
              var name = node.getAttribute('name');

              //  check if the hidden field belongs to an editor field
              //  this can be done here although the editor belongs to a highter hierarchy level ;-)
              var editor = node.getAttribute('editor');
              if (editor)
              {
                eval ('var editorValue = ' + editor + '.GetHTML();');
                if (name)
                  if (editorValue) nodeResult = name + '=' + encodeURIComponent(editorValue);
                    else nodeResult = name + '=';
              }
              else
              {
                //  common input field
                if (name) if (node.value) nodeResult = name + '=' + encodeURIComponent(node.value);
                            else nodeResult = name + '=';
              }
              break;
          }
        }
        break;
        
      //  get the data of a textarea
      case 'textarea' : 
      //  get the data of a select box
      case 'select' :        
        //  get the name
        var name = node.getAttribute('name');
        //  create the parameter
        if (name)
        {
          if (node.value) nodeResult = name + '=' + encodeURIComponent(node.value);
            else nodeResult = name + '=';
        }
        break;
        
      //  no input field but look for childs of the node
      default :
        nodeResult = watAjaxSerializeFormRec(node.firstChild, button);
        break;
    }

    //  process the result
    if (nodeResult)
    {
      if (result) result = result + '&' + nodeResult;
        else result = nodeResult;
    }
   
    //  get the next node 
    node = node.nextSibling;
  }

  //  return the parameter list
  return result;
}

/**
 *  function to serialize a form in order to send form data in an AJAX request
 *
 *  @param object form    form node to serialize
 *  @param object button  button that was pressed or null if no button is available
 *
 *  @return string  parameter string of the serialized form
 */
function watAjaxSerializeForm(form, button)
{
  if (form)
  {
    //  initialize the variables
    var result = null;
    //  start the recursion
    result = watAjaxSerializeFormRec(form.firstChild, button);
    //  return the result
    return result;
  }
  else
  {
    //  no form was passed
    return null;
  }
}

/**
 *  function to get the form if only a child (e.g. a button of the form) is known
 *
 *  @param object element  child of the form
 *
 *  @return object  the function returns the searched form or null if the form was not found
 */
function watAjaxGetForm(element)
{
  //  search the form
  while(element)
  {
    if (element.nodeName && element.nodeName.toLowerCase() == 'form')
      return element;
    else element = element.parentNode;
  }
  
  //  no form was found
  return null;
}

/**
 *  recursive function to find a form which is used by the wat_ajax_find_form function
 *
 *  @param object node  node in which the form should be searched
 *
 *  @return object  the function returns the form-node or NULL if the form was not found
 */
function watAjaxFindFormRec(node)
{
  //  initialize the result
  result = null;
  
  while (!result && node)
  {
    //  check if a form was found
    if (node.nodeName.toLowerCase() == 'form') result = node;
      else result = watAjaxFindFormRec(node.firstChild);
    //  get the next child of the passed node
    node = node.nextSibling;
  }
  
  //  return the form node or NULL if no form was found
  return result;
}

/**
 *  function to search a form node within the childs of the passed node
 *
 *  @param object node  node in which the form should be searched
 *
 *  @return object  the function returns the form-node or NULL if the form was not found
 */
function watAjaxFindForm(node)
{
  //  initialize the result
  var result = null;

  // search the form
  if (node) result = watAjaxFindFormRec(node.firstChild);
  
  //  return the form or null if the node was not found
  return result;
}

