var http_response_code = new Array ();
http_response_code[404] = 'page not found';

var msie = false;
if (navigator.userAgent.match ('MSIE') !== null) msie = true;

var queue = new Queue ();
var debug_txt = '';

// time to wait before polling active requests
var DELAY = 100;

// constants
var STR_PAD_LEFT = 0;
var STR_PAD_RIGHT = 1;

// request types
var TYPE_OVERWRITE_NODE = 0;

var date_days = new Array ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
var date_months = new Array ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

function create_link (node, function_str, status_text) {
  node.style.cursor = 'pointer';
  node.onclick = function () {
    eval (function_str);
  }
  node.onmouseover = function () {
    window.status = status_text;
    return true;
  }
  node.onmouseout = function () {
    window.status = '';
    return true;
  }
}

function str_pad (input, length, padding, side) {
  input = String (input);
  padding = String (padding);
  if (input.length < length) {
    var padding_complete = '';
    var diff = length - input.length;
    for (var i = 0; i < diff; i += padding.length) {
      padding_complete += padding;
    }
    
    if (side == STR_PAD_RIGHT) {
      input = input + padding_complete.substr (0, diff);
    } else {
      input = padding_complete.substr (0, diff) + input;
    }
  }
  return input;
}

function set_class (node, class_name) {
  if (msie) {
    // trace ('Set class for ' + node.tagName + ': ' + class_name);
    node.setAttribute ('className', class_name);
  } else {
    node.setAttribute ('class', class_name);
  }
}

// Queue, Request and returned_node_to_dom can be used in later AJAX apps
function Queue () {
  this.request_queue = new Array ();
  // alert ('construct, request_queue: '+ this.request_queue.length);
  this.requester_active = false;
  this.created = true;
  
  this.request = function (method, url, action, post_data, options) {
    // trace ("Queue.request ("+method+","+url+","+action+","+further_data+")");
    // add new request
    this.request_queue.push (new Request (method, url, action, post_data, options));
    this.activate ();
  }
  
  this.activate = function () {
    debug_txt += "Queue.activate\n";
    // trace ("Queue.activate");
    if (this.requester_active != true && this.request_queue.length > 0) {
      this.requester_active = true;
      this.request_queue[this.request_queue.length - 1].send ();
      // alert ('activate, request_queue: '+ this.request_queue);
      // trace ('window.setTimeout (queue.process (),'+ DELAY+')');
      window.setTimeout ('queue.process ();', DELAY);
    }
  }
  
  this.process = function () {
    debug_txt += "Queue.process\n";
    // trace ("Queue.process");
    //alert (debug_txt + 'request_queue: '+ this.request_queue + ', created: ' + this.created);
    if (this.request_queue.length > 0) {
      
      // process request from FIFO buffer
      var first_request = this.request_queue[0];
      if (first_request.ready_state () == 4) {
        if (first_request.status () == 200) {
          
          // process response
          first_request.process ();
          
        } else {
          // trace ('Return status: ' + first_request.status ());
          var ajax_error = 'AJAX call to ' + first_request.url + ' returned server error:\n' +
            first_request.status ();
          if (http_response_code[first_request.status ()]) {
            ajax_error += ' (' + http_response_code[first_request.status ()] + ')';
          }
          alert (ajax_error);
        }
        // remove item from queue
        // alert ('this.request_queue.shift ();');
        this.request_queue.shift ();
        
        this.requester_active = false;
        this.activate ();
        
      } else {
        // keep waiting for currently active request
        window.setTimeout ('queue.process ();', DELAY);
      }
    }
  }
}

function Request (method, url, action, post_data, options) {
  this.url = url;
  this.action = action;
  this.method = method.toUpperCase ();
  this.requester = null;
  this.internet_explorer = false;
  this.node_id = null;
  if (this.method == 'POST') {
    this.post_data = String(post_data);
  } else {
    this.post_data = null;
  }
  
  switch (this.action) {
    case TYPE_OVERWRITE_NODE:
      this.node_id = options['node'];
      break;
    // provide initialisation for other actions here
  }
  
  this.ready_state = function () {
    if (this.requester == null) {
      // trace ("Request.ready_state: -1");
      return -1;
    } else {
      // trace ("Request.ready_state: " + this.requester.readyState);
      return this.requester.readyState;
    }
  }
  
  this.status = function () {
    if (this.requester == null) {
      return -1;
    } else {
      return this.requester.status;
    }
  }
  
  this.send = function () {
    // try Moz/Safari method, then IE
    if (window.XMLHttpRequest) {
      this.requester = new XMLHttpRequest ();
    } else if (this.requester = new ActiveXObject("MSXML2.XMLHTTP.3.0")) {
      this.internet_explorer = true;
    }
    
    if (this.requester != null) {
      // trace ('requester.open('+method+', '+url+', true)');
      this.requester.open(method, url, true);
      if (this.method == 'POST') {
        // trace ("this.requester.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');");
        this.requester.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        // this.requester.setRequestHeader("Content-length", post_data.length);
        // this.requester.setRequestHeader("Connection", "close");
      }
      // trace ('requester.send('+this.post_data+ ')');
      this.requester.send(this.post_data);
    } else {
      alert ('Your browser does not appear to support AJAX');
    }
  }
  
  this.process = function () {
    if (this.requester != null) {
      // check for errors
      var top_node = false;
      
      // skip any comments, whitespace, etc. and find the first real XML node
      var top_children = this.requester.responseXML.childNodes;
      var curr_child_num = 0;
      var curr_child = null;
      while (!top_node) {
        if (top_children.length <= curr_child_num) {
          break;
        }
        curr_child = top_children.item (curr_child_num);
        if (curr_child.nodeType == 1) {
          top_node = curr_child;
        } else {
          curr_child_num++;
        }
      }
      
      if (!top_node) {
        alert ('Invalid XML returned via AJAX, please contact the web site administrator');
        return false;
      }
      
      if (top_node.nodeName.toLowerCase () == 'error') {
        // alert (top_node.xml);
        alert (top_node.firstChild.data);
      } else {
        switch (this.action) {
          case TYPE_OVERWRITE_NODE:
            //alert ('XML nodes returned:' + top_node.xml);
            if (this.internet_explorer) {
              var target_node = document.getElementById (this.node_id);
              
              var span_node = document.createElement ('span');
              span_node.innerHTML = top_node.xml;
              
              //alert ('Span: ' + span_node.innerHTML);
              
              target_node.parentNode.replaceChild (span_node.firstChild, target_node);
              
            } else {
              var dom_node = returned_node_to_dom (top_node, null, null, false);
              // alert ('Node ID: ' + this.node_id);
              // alert (view_dom_tree (dom_node));
              var target_node = document.getElementById (this.node_id);
              
              target_node.parentNode.replaceChild (dom_node, target_node);
              
              // overcome firefox bug where select list with selected elements are not selected
              if (dom_node.parentNode) {
                var select_nodes = dom_node.parentNode.getElementsByTagName ('select');
                var curr_select;
                var curr_opt;
                var curr_attr;
                for (var select_count = 0; select_count < select_nodes.length; select_count++) {
                  curr_select = select_nodes.item (select_count);
                  for (var opt_count = 0; opt_count < curr_select.childNodes.length; opt_count++) {
                    curr_opt = curr_select.childNodes.item (opt_count);
                    if (curr_opt.nodeType == 1 && curr_opt.nodeName == 'OPTION') {
                      for (var attr_count = 0; attr_count < curr_opt.attributes.length; attr_count++) {
                        curr_attr = curr_opt.attributes.item (attr_count);
                        if (curr_attr.name == 'selected') {
                          curr_select.value = curr_opt.value;
                        }
                      }
                    }
                  }
                }
              }
              
            }
            break;
          // provide processing for other actions here
          default:
            alert ('Invalid AJAX query type: ' + this.action);
        }
      }
    } else {
      alert ('Request.function called on null requester');
    }
  }
}

function returned_node_to_dom (node, so_far, current_node, is_ie) {
  var attr;
  var new_node;
  if (node.tagName) {
    // trace ('Creating node ' + node.tagName);
    new_node = document.createElement (node.tagName);
    if (so_far == null) {
      so_far = new_node;
      current_node = new_node;
    } else {
      current_node.appendChild (new_node);
    }
    
    // attributes
    for (var i = 0; i < node.attributes.length; i++) {
      attr = node.attributes.item (i);
      // trace ('new_node.setAttribute ('+attr.name+', '+attr.value+');');
      if (attr.name == 'class') {
        set_class (new_node, attr.value);
      } else {
        new_node.setAttribute (attr.name, attr.value);
      }
    }
    
    // children (recursive) and further text nodes
    if (!is_ie) {
      for (var i = 0; i < node.childNodes.length; i++) {
        attr = node.childNodes.item (i);
        if (attr.nodeType == 3) {
          new_node.appendChild (document.createTextNode (attr.data));
        } else {
          so_far = returned_node_to_dom (attr, so_far, new_node, is_ie);
        }
      }
    }
  }
  return so_far;
}

function view_dom_tree (node, so_far, tab_width) {
  var attr;
  var child;
  
  if (so_far == null) {
    so_far = '';
  }
  
  for (var i = 0; i < tab_width; i++) {
    so_far += ' ';
  }
  
  if (node.nodeType != 3) {
    // trace ('Viewing node ' + node.tagName);
    so_far += '<' + node.tagName;
    if (node.attributes) {
      for (var i = 0; i < node.attributes.length; i++) {
        attr = node.attributes.item (i);
        so_far += ' ' + attr.name + '="' + attr.value + '"';
      }
    }
    so_far += ">\n";
    
    for (var i = 0; i < node.childNodes.length; i++) {
      child = node.childNodes.item (i);
      so_far += view_dom_tree (child, '', tab_width + 2);
    }
  } else {
    so_far += node.data;
  }
  
  return so_far;
}
