String.prototype.trim = function () 
{
  return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1");
};

function XmlDOM(domImplIn, domDocIn, currentNodeIn, resultIn, lengthIn)
{
   var domImpl = domImplIn;
   var domDoc = domDocIn;
   var currentNode = currentNodeIn;
   var result = resultIn;
   var length = lengthIn;
   
    this.createDOM = createDOM;
    this.show = show;
    this.toString = toString;
    this.toStringDebug = toStringDebug;
    this.append = append;
    this.parent = parent;
    this.cloneDOM = cloneDOM;
    this.cloneDOM2 = cloneDOM2;
    this.cloneDOM3 = cloneDOM3;
    this.cloneDOM4 = cloneDOM4;
    this.find = find;
    this.length = length;
    this.clone = clone;
    this.importElement = importElement;
    this.text = text;
    this.html = html;
    this.each = each;
    this.remove = remove;
    this.empty = empty;
    this.result = result;
    this.parse = parse;
    this.innerHtml = innerHtml;
    this.domDoc = domDoc;
    this.currentNode = currentNode;
    this.getAllChildren = getAllChildren;
    
    if (domImpl == null)
      createDOM();
    
    function createDOM()
	{
	  //log('createDOM');
	  domImpl = new DOMImplementation();
	  domDoc = domImpl.loadXML("<root/>");
	  currentNode = domDoc.getDocumentElement();
	}
	
	function cloneDOM()
	{
	   return new XmlDOM(domImpl, domDoc, currentNode, result, length);
	}
	
	function cloneDOM2(curNode)
	{
	   return new XmlDOM(domImpl, domDoc, curNode, result, length);
	}
	function cloneDOM3(curNode, len)
	{
	   return new XmlDOM(domImpl, domDoc, curNode, result, len);
	}
	
	function cloneDOM4(curNode, res, len)
	{
	   return new XmlDOM(domImpl, domDoc, curNode, res, len);
	}
	
	function append(str)
	{		
	    //alert('append: '+ str.length + ', ' + str);
	    var newImpl = new DOMImplementation().loadXML("<root>" + str + "</root>");
	    var element = newImpl.getDocumentElement();
	    var childNodes = element.getChildNodes();
	    for (var i=0; i < childNodes.getLength(); i++)
	    {
	       var childNode = childNodes.item(i);
	       childNode = domDoc.importNode(childNode, true);
	       currentNode.appendChild(childNode, true);
	    }
		return cloneDOM();
	}
	
	function parent()
	{ 
	   return cloneDOM2(currentNode.getParentNode());
	}
	
	function getAllChildren()
	{
	   result = "";
	   children = currentNode.getChildNodes();
	   for (i=0; i < children.getLength(); i++)
	   {
	      result += cloneDOM2(children.item(i)).toString(); 
	   }
	   return result;
	}
	
	function each(f)
	{
	   if (result == null)
	     return;
	     
	   for (var i=0; i < result.getLength(); i++)
	   {
	      f(i, cloneDOM2(result.item(i)));
	   }
	}
	
	//creates a dom node in the current dom impl but makes sure it has no parent just yet.
	function clone()
	{
	  var str = this.toString();
	  return parse(str);
	}
	
	//will create a dom node in the doc but not put under a parent
	function parse(str)
	{
	   var element = new DOMImplementation().loadXML(str).getDocumentElement();
	   return importElement(element);
	}
	
	function importElement(element)
	{
	   var node = domDoc.importNode(element, true);
	   return cloneDOM2(node);
	}
	
	function show()
	{
	   alert(this.toStringDebug());
	}
	
	function toString()
	{ 
	  if (currentNode == null)
	    return "";
	  return currentNode.toString();
	}
	
	function toStringDebug()
	{
	   return "DOC: " + domDoc.toString() + " CURRENT NODE: " + currentNode.toString() + " RESULT: " + result;
	}
	
	function find(query)
	{
	   //log('find: ' + query);
	   resultHere = currentNode.selectNodeSet(query);
	   var result = null;
	   if (resultHere != null && resultHere.getLength() > 0)
	   {
	   	result= cloneDOM4(resultHere.item(0), resultHere, resultHere.getLength());
	   }
	   else
	   {
	     result= cloneDOM4(null, null, 0);
	   }
	   //log('find: end');
	   return result;
	}
	
	function innerHtml()
	{
	   if (currentNode == null)
	      return "";
	   var html = "";
	   for (var i=0; i < currentNode.getChildNodes().getLength(); i ++)
	      html += currentNode.getChildNodes().item(i).toString();
	   
	   return html;
	}
	
	
	function text(val)
	{
	  //set
	  if (val != null)
	  {
	    //alert("pre setNode: " + currentNode.selectNodeSet("text()").getLength());
	    
	    if (currentNode.selectNodeSet("text()").getLength() == 0)
	      currentNode.appendChild(domDoc.createTextNode(val));
	    //alert("setNode: " + currentNode.selectNodeSet("text()").getLength());
	    var textNode = currentNode.selectNodeSet("text()").item(0).setNodeValue(val);
	    //alert("set node: " + val + ", " + currentNode);
	    //currentNode.setNodeValue(val+"");
	  }
	    
	  //get
	  if (currentNode == null)
	   return "";
	  var text = currentNode.getNodeValue();
	  if (text == '')
	  {
	    try
	    {
	    text = currentNode.selectNodeSet("text()").item(0).getNodeValue();
	    if (text == null)
	      text = '';
	    }
	    catch(e)
	    {
	     
	    }
	   
	  }
	  return text.trim(); 
	}
	
	
	
	function remove()
	{
	  if (currentNode != null)
	  {
	    currentNode.getParentNode().removeChild(currentNode);
	    currentNode = null;
	  }
	  return cloneDOM();
	}
	
	function empty()
	{
	  while (currentNode.getChildNodes().getLength() > 0)
	  {
	      currentNode.removeChild(currentNode.getFirstChild());
	  }
	  return cloneDOM();
	}
	
	
	
	function html()
	{
	  return toString();
	}
}

function parseDOM(str)
{
   try
   {
	   //put the xml into the dom
	   var dom = new XmlDOM();
	   dom = dom.append(str);
	   
	   //set the current node
	   dom = dom.find("node()");
	   return dom;
	}
	catch(e)
	{
	   //log("Could not parse xml string: " + str);
	   throw(e);
	}
}


function testDOM()
{
   //testAppendDOM();
   //testQueryDOM();
   //testChainDOM();
   //testQueryChainDOM();
   
   //testParseDOM();
   //testEachDOM();
   testLargeDOM();
}

function testLargeDOM()
{
   var str = "<root>";
   for (var i=0; i < 5000; i++)
     str+= "<val>longval</val>";
   str += "</root>";
   
   domImpl = new DOMImplementation();
   alert('pre parse: ' + str.length);
   domDoc = domImpl.loadXML(str);
   alert('post parse');
}

function testEachDOM()
{
  var dom = createTestDOM();
  dom.append("<f>f1</f><f>f2</f><f>f3</f>");
  dom.find("//f").each(function (i, domI) {
       alert(i + ", " + domI.html());
      
      });
}

function testParseDOM()
{
   var str = "<fooman>this is fooman</fooman>";
   var dom = parseDOM(str);
   dom.show();
}

function testQueryChainDOM()
{
  var dom = createTestDOM();
  dom.show();
  
  dom = dom.find("grandpa").find("dad").find("kid");
  
  dom.show();
  
  //current node is kid, so grandpa search should return nothing, current node should remain as 'kid'
  dom = dom.find("grandpa");
 
  dom.show();
}

function testChainDOM()
{
  var dom = createTestDOM();
  dom.append("<chain1>chainVal</chain1>").append("<chain2>chainVal</chain2>");
  dom.show();
}

function createTestDOM()
{
  var dom = new XmlDOM();
  dom.append("<child>fooChild</child><grandpa><dad><kid></kid></dad></grandpa>");
  
  return dom;
}

function testQueryDOM()
{
   var dom = createTestDOM();
   
   var result = dom.find("//child").result;
   
   dom.show();
   
}


function testAppendDOM()
{
   var dom = createTestDOM();
   
   dom.append("<child>fooChild</child>");
   
   dom.show();
   
   dom.append("<parent><child1>val1</child1><child2>val2</child2></parent>");
   
   dom.show();
   
   
   dom.append("<brother1>val1</brother1><brother2>val2</brother2>");
   
   dom.show();
   
   
   
}