// $Id: sotel.js,v 1.1 2006/03/17 16:10:28 dante Exp $
// SOTEL (Son of Telamos) AJAX library
//
// ben.jones@superutility.net
//
// See the index.html file in this package for example usage


function sotel_CreateReqObj()
{  
  var req = false;
  // branch for native XMLHttpRequest object
  if(window.XMLHttpRequest) {
    try {
      req = new XMLHttpRequest();
    } catch(e) {
      req = false;
    }
    // branch for IE/Windows ActiveX version
  } else if(window.ActiveXObject) {
    try {
      req = new ActiveXObject('Msxml2.XMLHTTP');
    } catch(e) {
      try {
        req = new ActiveXObject('Microsoft.XMLHTTP');
      } catch(e) {
        req = false;
      }
    }
  }

  return req;
}


function sotel_SendReq(Obj, url, handler)
{  
  if (Obj) {
    Obj.onreadystatechange = handler;
    Obj.open('GET', url, true);
    Obj.send(null);
    return true;
  } else {
    return false;
  }
}


function sotel_ResponseIsReady()
{
  if (sotel_client.readyState == 4) { /* request object is done */
    if (sotel_client.status == 200) { /* request was found . . . */
      return true;
    } else {
      return false;
    }
  } else {
    return false;
  }
      
}


function sotel_Debug(msg)
{
  if (!sotel_debug_mode) {
    return;
  } else {
    window.alert(msg);
  }
}


function sotel_GetTargetNodes(xmldoc)
{
  var targets;
  
  if (xmldoc.getElementsByTagNameNS) {
    var targets = xmldoc.getElementsByTagNameNS('http://www.superutility.net/benjones/xml/SonOfTelamos', 'target');
  } else {
    var targets = xmldoc.getElementsByTagName('sotel:target');
  }
  
  if (targets.length == 0) {
    sotel_Debug('Response XML has no targets');
    return;
  }
  
  return targets;
}


function sotel_ReplaceNodes()
{
  var child_name, source_child, new_child, source_parent;
  
  var xmldoc = sotel_client.responseXML;
  
  if (!xmldoc) {
    sotel_Debug('Response XML is empty');
    return false;
  }
  
  var targets = sotel_GetTargetNodes(xmldoc);
  
  
  for (var i = 0; i < targets.length; i++) {
    
    child_name    = false;
    source_child  = false;
    new_child     = false;
    source_parent = false;
    
    // Get the name of the target node or continue with the 
    // next iteration
    if (targets[i].firstChild && targets[i].firstChild.nodeValue != '') {
      child_name  = targets[i].firstChild.nodeValue;
    } else {
      sotel_Debug('Unable to get name of target node');
      continue;
    }
    
    
    source_child = document.getElementById(child_name);
    if (!source_child) {
      sotel_Debug('Unable to get target node');
      continue;
    }
    

    // Find the parent node of the target node in the source document
    // or continue with the next iteration
    source_parent = source_child.parentNode;
    if (!source_parent) {
      sotel_Debug('Unable to get parent node');
      continue;
    }
    
    
    // Find the replacement node in the XMLHttpRequest object response
    // or contiue with the next iteration
    new_child = sotel_GetElementById(xmldoc, child_name);
    if (!new_child) {
      sotel_Debug('Unable to get replacement node');
      continue;
    } else {
      new_child = sotel_XmlNodeToHtmlNode(new_child);
    }
    
    source_parent.replaceChild(new_child.cloneNode(1), source_child);    
    
  }
  
  return true;
}


function sotel_getSpecifiedAttributes(xml_node)
{  
  var attributes = [];
  for (var i = 0; i < xml_node.attributes.length; i++) {    
    if (xml_node.attributes[i].specified) {
      attributes[attributes.length] = xml_node.attributes[i];
    }
  }

  return attributes;
}


function sotel_CreateHtmlElement_ie(xml_node)
{
  // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/createelement.asp
  //
  // This will work around IE's poor implementation of setAttribute (and it's documented)
  //
  // In particular, event handlers, style, and class attributes will all work
  // as expected
  
  var attributes  = sotel_getSpecifiedAttributes(xml_node);
  var attr_string = '';
    
  for (var i = 0; i < attributes.length; i++) {
    attr_string += ' '+attributes[i].name+'="'+attributes[i].value+'"';
  }
  
  return document.createElement('<'+xml_node.nodeName+attr_string+'>');  
  
}


function sotel_CreateHtmlElement(xml_node)
{
  if (document.attachEvent) {
    return sotel_CreateHtmlElement_ie(xml_node);
  }
  
  var new_node   = document.createElement(xml_node.nodeName);
  var attributes = sotel_getSpecifiedAttributes(xml_node);

  for (var i = 0; i < attributes.length; i++) {
    new_node.setAttribute(attributes[i].name, 
                          attributes[i].value);
  }

  return new_node;
}


function sotel_XmlNodeToHtmlNode(xml_node)
{
  // Create an HTML DOM Node based on the XML DOM node
  // FireFox does this automatically, but not IE
  //
  // I don't know if FireFox can be *expected* to do this
  // so currently it will go through the same motions as IE
  var new_node;

  if (xml_node.nodeType === 1) { 
    new_node = sotel_CreateHtmlElement(xml_node);  
  } else if (xml_node.nodeType == 3) {
    new_node = document.createTextNode(xml_node.nodeValue);
  }
  
  for (var i = 0; i < xml_node.childNodes.length; i++) {
    new_node.appendChild(sotel_XmlNodeToHtmlNode(xml_node.childNodes[i]));
  }
  
  return new_node;
}


function sotel_GetElementById(node, id) 
{
  // If only it were always this easy . . . 
  if (node.getElementById) {
    return node.getElementById(id);
  }  
  
  // To be fair, I don't think that getElementById() is actually
  // part of the XML DOM API . . . HTML DOM, yes . . . but the
  // request response is XML DOM
  if (node.nodeType == 1 && node.getAttribute('id') == id) {
    return node;
  } else if (node.hasChildNodes()) {
    
    for (var i = 0; i < node.childNodes.length; i++) {
      
      // We're only interested in element nodes
      if (node.childNodes[i].nodeType != 1) {
        continue;
      }
      
      var found = sotel_GetElementById(node.childNodes[i], id);
      if (found) {
        return found;
      }      
    }    
  }
}


function sotel_MagicHandler(url)
{
  sotel_client = sotel_CreateReqObj();
  
  if (!sotel_SendReq(sotel_client,
                     url,
                     sotel_MagicProcessor)) {
    return false;
  } else {
    return true;
  }
}


function sotel_MagicProcessor()
{
  if (sotel_ResponseIsReady() && sotel_ReplaceNodes()) {
    return true;
  }  else {
    return false;
  }
}


function sotel_EventHandler(url, processor)
{
  // An example event handler . . .

  sotel_client = sotel_CreateReqObj()

  if (!url) {
    // Where does this request go . . . depends on the nature of this
    url = '/my_server_handler/request_type';
  }

  if (!processor) {
    processor = sotel_ResponseHandler;
  }
        
  // Send you request using the default request object
  if (!XMLHttpRequest_SendReq(XMLHttpRequest_client,
                              url,
                              processor)) {
    return false;
  }
}


function sotel_ResponseHandler()
{
  // An example response handler. This doesn't actually do anything
  // useful . . .
  
  if (sotel_ResponseIsReady()) {
    // processing statements
    alert('Client was sucessful!');
  }
}


var sotel_client;
