function XMLRPCResponse(xmlResponseDoc)
{
  this.xmlDoc = xmlResponseDoc||null;   // XmlDocument
  if (this.xmlDoc == null)
    return (this);
};
XMLRPCResponse.prototype.setResponseByDoc = function(xmlResponseDoc)
{
  if (!xmlResponseDoc) return;
  this.xmlDoc = xmlResponseDoc;
};
XMLRPCResponse.prototype.setResponseByStr = function(xmlResponseStr)
{
  if (!xmlResponseStr) return;
  if (this.xmlDoc == null)
    this.xmlDoc = XmlDocument.create();
  this.xmlDoc.loadXML(xmlResponseStr);
};
XMLRPCResponse.prototype.isFault = function()
{
  if (this.xmlDoc == null) return true;
  try
  {
    var fNode = (this.xmlDoc.documentElement.childNodes.item(0).nodeType == 1)?this.xmlDoc.documentElement.childNodes.item(0):this.xmlDoc.documentElement.childNodes.item(1);
    return(fNode.nodeName == 'fault');
  }
  catch(e){ return(true); }
};
XMLRPCResponse.prototype.getFaultCode = function()
{
  if (this.xmlDoc == null) return (-1);
  if (!this.isFault()) return (-1);
  var members = this.xmlDoc.getElementsByTagName("member");
  for (var lcv = 0;lcv < members.length; lcv++)
  {
    var nameElem = members[lcv].getElementsByTagName("name").item(0);
    if ((nameElem == null) || (nameElem.childNodes.length == 0))  // should never happen
      return (-1);
    var nameStrNode = nameElem.childNodes.item(0);  // grab the text node
    if (nameStrNode.nodeValue == 'faultCode')
    {
      var codeNode = members[lcv].getElementsByTagName("int").item(0);  // node holding the value
      if ((codeNode == null) || (codeNode.childNodes.length == 0)) // should never happen
        return (-1);
      return (codeNode.childNodes.item(0).nodeValue);
    }
  }
  return (-1);
};
XMLRPCResponse.prototype.getFaultString = function()
{
  if (this.xmlDoc == null) return ("");
  if (!this.isFault()) return ("");
  var members = this.xmlDoc.getElementsByTagName("member");
  for (var lcv = 0;lcv < members.length; lcv++)
  {
    var nameElem = members[lcv].getElementsByTagName("name").item(0);
    if ((nameElem == null) || (nameElem.childNodes.length == 0))  // should never happen
      return ("");
    var nameStrNode = nameElem.childNodes.item(0);  // grab the text node
    if (nameStrNode.nodeValue == 'faultString')
    {
      var codeNode = members[lcv].getElementsByTagName("string").item(0);  // node holding the value
      if ((codeNode == null) || (codeNode.childNodes.length == 0)) // should never happen
        return ("");
      return (codeNode.childNodes.item(0).nodeValue);
    }
  }
  return ("");
};
XMLRPCResponse.prototype.getObject = function()
{
  if (this.xmlDoc == null) return null;
  return (this.getObjectRecurse(this.xmlDoc.getElementsByTagName("value").item(0)));
};
XMLRPCResponse.prototype.getObjectRecurse = function(theNode)
{
  if (this.xmlDoc == null) return null;
  if (theNode == null) return null;  
  switch (theNode.nodeName)
  {
    case 'value': // loop thru all tags on this level
      return(this.getObjectRecurse(theNode.childNodes[0])); break;
    case 'string': return(this.decodeSTRING(theNode)); break;
    case 'i4':
    case 'int': return(this.decodeINT(theNode)); break;
    case 'boolean': return(this.decodeBOOLEAN(theNode)); break;
    case 'double': return(this.decodeDOUBLE(theNode)); break;
    case 'dateTime.iso8601': return(this.decodeDATETIME(theNode)); break;
    case 'base64': return(this.decodeBASE64(theNode)); break;
    case 'array': return(this.decodeARRAY(theNode)); break;
    case 'struct': return(this.decodeSTRUCT(theNode)); break;
    default: alert('unknown node type: '+theNode.nodeName); break;
  }
};
XMLRPCResponse.prototype.unescapeHTML = function(newString)
{
  return newString.replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&quot;/g,'"').replace(/&amp;/g,'&');
};
XMLRPCResponse.prototype.decodeSTRING = function(theNode)
{
  if (theNode.childNodes.length > 0) // has textNode
  {
    try{
    if (!xIE4Up)
      theNode.normalize();
    }catch(e)
    {
      try{
      if (!is.ie)
        theNode.normalize();
      }catch(e){}      
    }
    return (this.unescapeHTML(theNode.childNodes[0].nodeValue));
  }
  else return ('');
};
XMLRPCResponse.prototype.decodeINT = function(theNode) // leaf node
{
  var theSTRING = this.decodeSTRING(theNode);
  return (parseInt(theSTRING));
};
XMLRPCResponse.prototype.decodeBOOLEAN = function(theNode)
{
  var theSTRING = this.decodeSTRING(theNode);
  return (theSTRING == '1');
};
XMLRPCResponse.prototype.decodeDOUBLE = function(theNode)
{
  var theSTRING = this.decodeSTRING(theNode);
  return (parseFloat(theSTRING));
};
XMLRPCResponse.prototype.decodeDATETIME = function(theNode) {};
XMLRPCResponse.prototype.decodeBASE64 = function(theNode) {};
XMLRPCResponse.prototype.decodeSTRUCT = function(theNode)
{
  var thisStruct = new Object();
  for (var lcv = 0; lcv < theNode.childNodes.length; lcv++)
  {
    var memberNode = theNode.childNodes[lcv];
    if (memberNode.nodeName == "member")
    {
      var valueNameNode = memberNode.getElementsByTagName("name").item(0);
      var valueNameStr = this.decodeSTRING(valueNameNode);
      thisStruct[valueNameStr] = this.getObjectRecurse(memberNode.getElementsByTagName("value").item(0));
    }
  }
  return (thisStruct);
};
XMLRPCResponse.prototype.__helper__getNextElementChildNode = function(aNode,firstpos)
{
  var foundpos = -1;
  for (var lcv=firstpos;(lcv < aNode.childNodes.length) && (foundpos == -1); lcv++)  
    if (aNode.childNodes[lcv].nodeType == 1)
      foundpos = lcv;
  return (foundpos);
};
XMLRPCResponse.prototype.__helper__getNextElementNamedChildNode = function(aNode,firstpos,nodeName)
{
  for (var lcv=firstpos;lcv < aNode.childNodes.length; lcv++)
    if ((aNode.childNodes[lcv].nodeType == 1) && (aNode.childNodes[lcv].nodeName == nodeName))
      return(lcv);
  return (-1);
};
XMLRPCResponse.prototype.decodeARRAY = function(theNode)
{
  var thisArray = new Array();
  var thisArrayPos = 0;

  var dataNode = theNode.childNodes[this.__helper__getNextElementNamedChildNode(theNode,0,"data")];
  var valueNodePos = 0;
  while (valueNodePos >= 0)
  {
    valueNodePos = this.__helper__getNextElementNamedChildNode(dataNode,valueNodePos,"value");
    if (valueNodePos >= 0)
    {
      valueNode = dataNode.childNodes[valueNodePos];
      thisArray[thisArrayPos] = this.getObjectRecurse(valueNode.firstChild);
      thisArrayPos++;
    }
    if (valueNodePos >= 0) valueNodePos++;
  }
  return (thisArray);
};
/*********************************************************/
function getXMLRPCResponseObject(serverReplyDoc)
{
  //returns a valid object or null
  var clientReply = null;
  var response={data:null,'XMLRPC_FAULT':false,'XMLRPC_FAULT_CODE':null,'XMLRPC_FAULT_MESSAGE':''};
  var validDoc = (serverReplyDoc != null);
  //begin error checks
  if (validDoc)
  {
    var xmlstr = (typeof(serverReplyDoc.xml)=='function')?serverReplyDoc.xml():serverReplyDoc.xml;
    validDoc = (xmlstr != '');
  }
  if (validDoc){
    if (serverReplyDoc.documentElement.nodeName == 'parsererror')
    {
      validDoc = false;
      response.XMLRPC_FAULT_MESSAGE = 'A response was received from the server but could not be processed - the format was invalid.';
    }
  }
  if (validDoc)
  {
    clientReply = new XMLRPCResponse();
    clientReply.setResponseByDoc(serverReplyDoc);
    if (clientReply.isFault())
    {
      response.XMLRPC_FAULT = true;
      response.XMLRPC_FAULT_CODE = clientReply.getFaultCode();
      response.XMLRPC_FAULT_MESSAGE = clientReply.getFaultString();
    }
    else
    {
      response.data = clientReply.getObject(); 
    }
  }
  else
  {
    response.XMLRPC_FAULT = true;
    response.XMLRPC_FAULT_CODE = -1;
    if (response.XMLRPC_FAULT_MESSAGE=='')
      response.XMLRPC_FAULT_MESSAGE = 'An invalid response was received from the server.\n';
    return response;
  }
  return response;
};

