function xmlResolveEntities(s) {



  var parts = stringSplit(s, '&');



  var ret = parts[0];

  for (var i = 1; i < parts.length; ++i) {

    var rp = stringSplit(parts[i], ';');

    if (rp.length == 1) {

      ret += parts[i];

      continue;

    }

    

    var ch;

    switch (rp[0]) {

      case 'lt': 

        ch = '<';

        break;

      case 'gt': 

        ch = '>';

        break;

      case 'amp': 

        ch = '&';

        break;

      case 'quot': 

        ch = '"';

        break;

      case 'apos': 

        ch = '\'';

        break;

      case 'nbsp': 

        ch = String.fromCharCode(160);

        break;

      default:

        var span = window.document.createElement('span');

        span.innerHTML = '&' + rp[0] + '; ';

        ch = span.childNodes[0].nodeValue.charAt(0);

    }

    ret += ch + rp[1];

  }



  return ret;

}

function xmlParse(xml) {

  Timer.start('xmlparse');

  var regex_empty = /\/$/;

  var regex_tagname = /^([\w:-]*)/;

  var regex_attribute = /([\w:-]+)\s?=\s?('([^\']*)'|"([^\"]*)")/g;



  var xmldoc = new XDocument();

  var root = xmldoc;
  var stack = [];



  var parent = root;

  stack.push(parent);



  var x = stringSplit(xml, '<');

  for (var i = 1; i < x.length; ++i) {

    var xx = stringSplit(x[i], '>');

    var tag = xx[0];

    var text = xmlResolveEntities(xx[1] || '');



    if (tag.charAt(0) == '/') {

      stack.pop();

      parent = stack[stack.length-1];



    } else if (tag.charAt(0) == '?') {


    } else if (tag.charAt(0) == '!') {


    } else {

      var empty = tag.match(regex_empty);

      var tagname = regex_tagname.exec(tag)[1];

      var node = xmldoc.createElement(tagname);



      var att;

      while (att = regex_attribute.exec(tag)) {

        var val = xmlResolveEntities(att[3] || att[4] || '');

        node.setAttribute(att[1], val);

      }

      

      if (empty) {

        parent.appendChild(node);

      } else {

        parent.appendChild(node);

        parent = node;

        stack.push(node);

      }

    }



    if (text && parent != root) {

      parent.appendChild(xmldoc.createTextNode(text));

    }

  }



  Timer.end('xmlparse');

  return root;

}
function XNode(type, name, value, owner) {

  this.attributes = [];

  this.childNodes = [];



  XNode.init.call(this, type, name, value, owner);

}

XNode.init = function(type, name, value, owner) {

  this.nodeType = type - 0;

  this.nodeName = '' + name;

  this.nodeValue = '' + value;

  this.ownerDocument = owner;



  this.firstChild = null;

  this.lastChild = null;

  this.nextSibling = null;

  this.previousSibling = null;

  this.parentNode = null;

}



XNode.unused_ = [];



XNode.recycle = function(node) {

  if (!node) {

    return;

  }



  if (node.constructor == XDocument) {

    XNode.recycle(node.documentElement);

    return;

  }



  if (node.constructor != this) {

    return;

  }



  XNode.unused_.push(node);

  for (var a = 0; a < node.attributes.length; ++a) {

    XNode.recycle(node.attributes[a]);

  }

  for (var c = 0; c < node.childNodes.length; ++c) {

    XNode.recycle(node.childNodes[c]);

  }

  node.attributes.length = 0;

  node.childNodes.length = 0;

  XNode.init.call(node, 0, '', '', null);

}



XNode.create = function(type, name, value, owner) {

  if (XNode.unused_.length > 0) {

    var node = XNode.unused_.pop();

    XNode.init.call(node, type, name, value, owner);

    return node;

  } else {

    return new XNode(type, name, value, owner);

  }

}



XNode.prototype.appendChild = function(node) {

  // firstChild

  if (this.childNodes.length == 0) {

    this.firstChild = node;

  }



  // previousSibling

  node.previousSibling = this.lastChild;



  // nextSibling

  node.nextSibling = null;

  if (this.lastChild) {

    this.lastChild.nextSibling = node;

  }



  // parentNode

  node.parentNode = this;



  // lastChild

  this.lastChild = node;



  // childNodes

  this.childNodes.push(node);

}





XNode.prototype.replaceChild = function(newNode, oldNode) {

  if (oldNode == newNode) {

    return;

  }



  for (var i = 0; i < this.childNodes.length; ++i) {

    if (this.childNodes[i] == oldNode) {

      this.childNodes[i] = newNode;

      

      var p = oldNode.parentNode;

      oldNode.parentNode = null;

      newNode.parentNode = p;

      

      p = oldNode.previousSibling;

      oldNode.previousSibling = null;

      newNode.previousSibling = p;

      if (newNode.previousSibling) {

        newNode.previousSibling.nextSibling = newNode;

      }

      

      p = oldNode.nextSibling;

      oldNode.nextSibling = null;

      newNode.nextSibling = p;

      if (newNode.nextSibling) {

        newNode.nextSibling.previousSibling = newNode;

      }



      if (this.firstChild == oldNode) {

        this.firstChild = newNode;

      }



      if (this.lastChild == oldNode) {

        this.lastChild = newNode;

      }



      break;

    }

  }

}



XNode.prototype.insertBefore = function(newNode, oldNode) {

  if (oldNode == newNode) {

    return;

  }



  if (oldNode.parentNode != this) {

    return;

  }



  if (newNode.parentNode) {

    newNode.parentNode.removeChild(newNode);

  }



  var newChildren = [];

  for (var i = 0; i < this.childNodes.length; ++i) {

    var c = this.childNodes[i];

    if (c == oldNode) {

      newChildren.push(newNode);



      newNode.parentNode = this;



      newNode.previousSibling = oldNode.previousSibling;

      oldNode.previousSibling = newNode;

      if (newNode.previousSibling) {

        newNode.previousSibling.nextSibling = newNode;

      }

      

      newNode.nextSibling = oldNode;



      if (this.firstChild == oldNode) {

        this.firstChild = newNode;

      }

    }

    newChildren.push(c);

  }

  this.childNodes = newChildren;

}



XNode.prototype.removeChild = function(node) {

  var newChildren = [];

  for (var i = 0; i < this.childNodes.length; ++i) {

    var c = this.childNodes[i];

    if (c != node) {

      newChildren.push(c);

    } else {

      if (c.previousSibling) {

        c.previousSibling.nextSibling = c.nextSibling;

      }

      if (c.nextSibling) {

        c.nextSibling.previousSibling = c.previousSibling;

      }

      if (this.firstChild == c) {

        this.firstChild = c.nextSibling;

      }

      if (this.lastChild == c) {

        this.lastChild = c.previousSibling;

      }

    }

  }

  this.childNodes = newChildren;

}





XNode.prototype.hasAttributes = function() {

  return this.attributes.length > 0;

}





XNode.prototype.setAttribute = function(name, value) {

  for (var i = 0; i < this.attributes.length; ++i) {

    if (this.attributes[i].nodeName == name) {

      this.attributes[i].nodeValue = '' + value;

      return;

    }

  }

  this.attributes.push(new XNode(DOM_ATTRIBUTE_NODE, name, value));

}





XNode.prototype.getAttribute = function(name) {

  for (var i = 0; i < this.attributes.length; ++i) {

    if (this.attributes[i].nodeName == name) {

      return this.attributes[i].nodeValue;

    }

  }

  return null;

}



XNode.prototype.removeAttribute = function(name) {

  var a = [];

  for (var i = 0; i < this.attributes.length; ++i) {

    if (this.attributes[i].nodeName != name) {

      a.push(this.attributes[i]);

    }

  }

  this.attributes = a;

}





function XDocument() {

  XNode.call(this, DOM_DOCUMENT_NODE, '#document', null, this);

  this.documentElement = null;

}



XDocument.prototype = new XNode(DOM_DOCUMENT_NODE, '#document');



XDocument.prototype.clear = function() {

  XNode.recycle(this.documentElement);

  this.documentElement = null;

}



XDocument.prototype.appendChild = function(node) {

  XNode.prototype.appendChild.call(this, node);

  this.documentElement = this.childNodes[0];

}



XDocument.prototype.createElement = function(name) {

  return XNode.create(DOM_ELEMENT_NODE, name, null, this);

}



XDocument.prototype.createDocumentFragment = function() {

  return XNode.create(DOM_DOCUMENT_FRAGMENT_NODE, '#document-fragment',

                    null, this);

}



XDocument.prototype.createTextNode = function(value) {

  return XNode.create(DOM_TEXT_NODE, '#text', value, this);

}



XDocument.prototype.createAttribute = function(name) {

  return XNode.create(DOM_ATTRIBUTE_NODE, name, null, this);

}



XDocument.prototype.createComment = function(data) {

  return XNode.create(DOM_COMMENT_NODE, '#comment', data, this);

}



XNode.prototype.getElementsByTagName = function(name, list) {

  if (!list) {

    list = [];

  }



  if (this.nodeName == name) {

    list.push(this);

  }



  for (var i = 0; i < this.childNodes.length; ++i) {

    this.childNodes[i].getElementsByTagName(name, list);

  }



  return list;

}

