docs: function xmlParse(xml) {
    var regex_empty = /\/$/;
  
    var regex_tagname;
    var regex_attribute;
    if (xml.match(/^<\?xml/)) {
      // When an XML document begins with an XML declaration
      // VersionInfo must appear.
      if (xml.search(new RegExp(XML10_VERSION_INFO)) == 5) {
        regex_tagname = XML10_TAGNAME_REGEXP;
        regex_attribute = XML10_ATTRIBUTE_REGEXP;
      } else if (xml.search(new RegExp(XML11_VERSION_INFO)) == 5) {
        regex_tagname = XML11_TAGNAME_REGEXP;
        regex_attribute = XML11_ATTRIBUTE_REGEXP;
      } else {
        // VersionInfo is missing, or unknown version number.
        // TODO : Fallback to XML 1.0 or XML 1.1, or just return null?
        alert('VersionInfo is missing, or unknown version number.');
      }
    } else {
      // When an XML declaration is missing it's an XML 1.0 document.
      regex_tagname = XML10_TAGNAME_REGEXP;
      regex_attribute = XML10_ATTRIBUTE_REGEXP;
    }
  
    var xmldoc = new XDocument();
    var root = xmldoc;
  
    // For the record: in Safari, we would create native DOM nodes, but
    // in Opera that is not possible, because the DOM only allows HTML
    // element nodes to be created, so we have to do our own DOM nodes.
  
    // xmldoc = document.implementation.createDocument('','',null);
    // root = xmldoc; // .createDocumentFragment();
    // NOTE(mesch): using the DocumentFragment instead of the Document
    // crashes my Safari 1.2.4 (v125.12).
    var stack = [];
  
    var parent = root;
    stack.push(parent);
  
    // The token that delimits a section that contains markup as
    // content: CDATA or comments.
    var slurp = '';
  
    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 (slurp) {
        // In a "slurp" section (CDATA or comment): only check for the
        // end of the section, otherwise append the whole text.
        var end = x[i].indexOf(slurp);
        if (end != -1) {
          var data = x[i].substring(0, end);
          parent.nodeValue += '<' + data;
          stack.pop();
          parent = stack[stack.length-1];
          text = x[i].substring(end + slurp.length);
          slurp = '';
        } else {
          parent.nodeValue += '<' + x[i];
          text = null;
        }
  
      } else if (tag.indexOf('![CDATA[') == 0) {
        var start = '![CDATA['.length;
        var end = x[i].indexOf(']]>');
        if (end != -1) {
          var data = x[i].substring(start, end);
          var node = domCreateCDATASection(xmldoc, data);
          domAppendChild(parent, node);
        } else {
          var data = x[i].substring(start);
          text = null;
          var node = domCreateCDATASection(xmldoc, data);
          domAppendChild(parent, node);
          parent = node;
          stack.push(node);
          slurp = ']]>';
        }
  
      } else if (tag.indexOf('!--') == 0) {
        var start = '!--'.length;
        var end = x[i].indexOf('-->');
        if (end != -1) {
          var data = x[i].substring(start, end);
          var node = domCreateComment(xmldoc, data);
          domAppendChild(parent, node);
        } else {
          var data = x[i].substring(start);
          text = null;
          var node = domCreateComment(xmldoc, data);
          domAppendChild(parent, node);
          parent = node;
          stack.push(node);
          slurp = '-->';
        }
  
      } else if (tag.charAt(0) == '/') {
        stack.pop();
        parent = stack[stack.length-1];
  
      } else if (tag.charAt(0) == '?') {
        // Ignore XML declaration and processing instructions
      } else if (tag.charAt(0) == '!') {
        // Ignore notation and comments
      } else {
        var empty = tag.match(regex_empty);
        var tagname = regex_tagname.exec(tag)[1];
        var node = domCreateElement(xmldoc, tagname);
  
        var att;
        while (att = regex_attribute.exec(tag)) {
          var val = xmlResolveEntities(att[5] || att[7] || '');
          domSetAttribute(node, att[1], val);
        }
  
        domAppendChild(parent, node);
        if (!empty) {
          parent = node;
          stack.push(node);
        }
      }
  
      if (text && parent != root) {
        domAppendChild(parent, domCreateTextNode(xmldoc, text));
      }
    }
  
    return root;
  }