
var testWindow = window.parent.frames['test'];
var treeWindow = window.parent.frames['tree'];
var currentNode;
//var loaded = true;

function init()
{
	if(testWindow.document) {
		currentNode = testWindow.document.documentElement;
		nodeStatus( currentNode ); 
		redraw();
	}
	else
		alert('DOM functions not avaialble on this file');
}


/* newTree()
 *
 * Opens the tree frame for writing, inserts the html and body tags, 
 * calls the printTree() function, then closes the document.
 */
function newTree( element )
{
	treeWindow.document.open();
	treeWindow.document.write("<html id='me'>");
	treeWindow.document.write("<body>");
	
	printTree( element );
	
	treeWindow.document.write("</body>");
	treeWindow.document.write("</html>");
	treeWindow.document.close();
			
	if (navigator.appName == "Microsoft Internet Explorer")
		treeWindow.location.href = "#current";
}

/* printTree()
 *
 * Traverses the DOM tree and uses document.write calls to print the tree
 * to the tree frame.
 */
function printTree( element )
{
	var child;
	
	
	// Print the node, then its children

	// highlight the current node
	if( element == currentNode )
		treeWindow.document.write( "<a name='current'></a><span style='background-color: #FFFFCC'>" );

	// make text nodes red
	if( element.nodeType == 3 )
	{	
		if(!is_ignorable(element))
			treeWindow.document.write( "<span style='color: red'>" + element.nodeValue + "</span>");
	}
	else
		treeWindow.document.write( "&lt;" + element.tagName + "&gt;" + "<br>");
	
	if( element == currentNode )
		treeWindow.document.write( "</span>" );
		
	// print the children of the current node
	if( element.hasChildNodes() )
	{
		child = element.firstChild;

		while( child  != null )
		{
			treeWindow.document.write( "<ul>" );
			printTree( child );
			child = child.nextSibling;
		}
	}
	
	if( element.nodeType == 3 )
		if(!is_ignorable(element))
			treeWindow.document.write( "<br></ul>");
		else
			treeWindow.document.write( "</ul>");
	else
		treeWindow.document.write( "&lt;/" + element.tagName + "&gt;" + "<br></ul>");
		
}

/* nodeStatus()
 *
 * Displays the tag name, node type, number of children, and number of siblings of the
 * current node. Called whenever the current node changes.
 */
function nodeStatus(element)
{	
	if( element.nodeType == 3 )
		(document.getElementById("name_field")).value = "TEXT_NODE";
	else if( element.nodeType == 8 )
		(document.getElementById("name_field")).value = "COMMENT_NODE";
	else if( element.nodeType == 9 )
		(document.getElementById("name_field")).value = "DOCUMENT_NODE";

	else
		(document.getElementById("name_field")).value = element.tagName;
	(document.getElementById("type_field")).value = element.nodeType;
	(document.getElementById("children_field")).value = element.childNodes.length;
	
	if( element.parentNode )
		(document.getElementById("siblings_field")).value = element.parentNode.childNodes.length - 1;
	else
		(document.getElementById("siblings_field")).value = 0;
}

/* gotoParent()
 *
 * Sets the new current node to the parent of the previous current node,
 * then updates the status and redraws the tree.
 */
function gotoParent()
{
	if( currentNode.parentNode && !is_ignorable( currentNode.parentNode ))
	{
		currentNode = currentNode.parentNode;
		nodeStatus( currentNode );
		redraw();
	}
	else
	{
		alert( "This node does not have a parent!" );
	}
}

/* gotoFirstChild()
 *
 * Sets the new current node to the first child of the previous current node,
 * then updates the status and redraws the tree.
 */
function gotoFirstChild()
{
	if( currentNode.hasChildNodes() && !is_ignorable( currentNode.firstChild ))
	{
		currentNode = currentNode.firstChild;
		nodeStatus( currentNode );
		redraw();
	}
	else if(currentNode.hasChildNodes() && currentNode.firstChild.nextSibling)
	{
		currentNode = currentNode.firstChild.nextSibling;
		nodeStatus( currentNode );
		redraw();
	}
	else
	{
		alert( "This node does not have any children!" );
	}
}

/* gotoLastChild()
 *
 * Sets the new current node to the last child of the previous current node,
 * then updates the status and redraws the tree.
 */
function gotoLastChild()
{
	if( currentNode.hasChildNodes() && !is_ignorable( currentNode.lastChild ))
	{
		currentNode = currentNode.lastChild;
		nodeStatus( currentNode );
		redraw();
	}
	else if(currentNode.hasChildNodes() && currentNode.lastChild.previousSibling)
	{
		currentNode = currentNode.lastChild.previousSibling;
		nodeStatus( currentNode );
		redraw();
	}
	else
	{
		alert( "This node does not have any children!" );
	}
}

/* gotoPrevSib()
 *
 * Sets the new current node to the previous sibling of the previous current node,
 * then updates the status and redraws the tree.
 */
function gotoPrevSib()
{
	if( currentNode.previousSibling && !is_ignorable( currentNode.previousSibling ))
	{
		currentNode = currentNode.previousSibling;
		nodeStatus( currentNode );
		redraw();
	}
	else if(currentNode.previousSibling && currentNode.previousSibling.previousSibling)
	{
		currentNode = currentNode.previousSibling.previousSibling;
		nodeStatus( currentNode );
		redraw();
	}
	else
	{
		alert( "This node does not have any more siblings!" );
	}
}

/* gotoNextSib()
 *
 * Sets the new current node to the next sibling of the previous current node,
 * then updates the status and redraws the tree.
 */
function gotoNextSib()
{
	if( currentNode.nextSibling && !is_ignorable( currentNode.nextSibling ))
	{
		currentNode = currentNode.nextSibling;
		nodeStatus( currentNode );
		redraw();
	}
	else if(currentNode.nextSibling && currentNode.nextSibling.nextSibling)
	{
		currentNode = currentNode.nextSibling.nextSibling;
		nodeStatus( currentNode );
		redraw();
	}
	else
	{
		alert( "This node does not have any more siblings!" );
	}
}

/* redraw()
 *
 * Calls newTree() to redraw the tree.
 */
function redraw()
{
	newTree( testWindow.document.documentElement );
}

/* gotoRoot()
 *
 * Sets the new current node to the root of the tree,
 * then updates the status and redraws the tree.
 */
function gotoRoot()
{
	currentNode = testWindow.document.documentElement;
	nodeStatus( currentNode );
	redraw();
}

/* deleteDeep()
 *
 * Deletes the current node and all its children, then updates status and redraws the tree.
 * The current node is set to the parent of the node to delete.
 */
function deleteDeep()
{
	var newCurrent = currentNode.parentNode;
	if( newCurrent )
	{
		newCurrent.removeChild( currentNode );
		if( newCurrent.firstChild )
			currentNode = newCurrent.firstChild;
		else
			currentNode = newCurrent;
		redraw();
	}
	else
		alert( "Cannot delete tag with no parent!" );
}

/* deleteShallow()
 *
 * Deletes the current node but not its children, then updates status and redraws the tree.
 * The current node is set to the parent of the node to delete, and the children of the deleted
 * node are adopted by their grandparents.
 */
function deleteShallow()
{
	var newCurrent;
	var nodeParent = currentNode.parentNode;
	
	if( nodeParent == null )
	{
		alert( "Cannot delete tag with no parent!" );
		return;
	}

	var child = currentNode.firstChild;
	
	 if ( child )
		newCurrent = child;
	else
		newCurrent = currentNode.parentNode;
	
	// move all children up one level
	while( child )
	{
		nodeParent.insertBefore( child, currentNode );
		child = currentNode.firstChild;
	}
		
	nodeParent.removeChild( currentNode );
	currentNode = newCurrent;
	nodeStatus( currentNode );
	redraw();
}

/* addElement()
 *
 * Adds an element to the DOM tree with the type and value specified by the user.
 * The element is inserted before or after the current element, as specified by
 * the user.
 */
function addElement( str )
{
  var newElt = testWindow.document.createElement( (document.getElementById( "type" )).value );
  if( newElt == null )
  	alert( "Invalid element type! Try again." );
  else
  { 
  	var newText = testWindow.document.createTextNode( (document.getElementById( "value" )).value );
	newElt.appendChild( newText );

	if( str == "before" )
		currentNode.parentNode.insertBefore( newElt, currentNode );
	else
		currentNode.parentNode.insertBefore( newElt, currentNode.nextSibling );

	redraw();
  }
}

/* addText()
 *
 * Creates a text node (user specifies the value) and appends it to the current node.
 */
function addText()
{
	var newText = testWindow.document.createTextNode( (document.getElementById( "text" )).value );
	try
	{
		currentNode.appendChild( newText );
	}
	catch( e )
	{	
		alert( "This node may not have a text node as a child." );
	}
	if( e == null )
	{
		redraw();
	}
}

/* getAttrs()
 *
 * Gets the attributes of the current node and displays them in the attribute text area,
 * where the user can see them and edit them.
 */
function getAttrs()
{
	var text = document.getElementById( "attrs" );
	
	str="";
	var attribs = currentNode.attributes;
	for ( i = 0; i < currentNode.attributes.length; i++)
	{
		if (navigator.appName == "Microsoft Internet Explorer")
		{
			if (currentNode.attributes.item(i).specified)
				str += ((currentNode.attributes.item(i).nodeName) + " -- " + (currentNode.attributes.item(i).nodeValue) + "\n");
		}else			
		{
			if (currentNode.attributes.item(i).specified)
				str += ((currentNode.attributes.item(i).name) + " -- " + (currentNode.attributes.item(i).value) + "\n");
		}
	}
	
	text.value = str;
}

/* setAttrs()
 *
 * Sets the attributes of the current node. The user enters attributes in the text area, and
 * these attributes are then parsed and set.
 */
function setAttrs()
{
	var text = document.getElementById( "attrs" );
	var tempText = text.value;
	var attName;

	
	while (tempText.length > 0)
	{
		attName = tempText.substring(0, tempText.indexOf(" -- "));
		attValue = tempText.substring(tempText.indexOf(" -- ") + 4, tempText.indexOf("\n"));
		tempText = tempText.substring(tempText.indexOf("\n") + 1, tempText.length);

		currentNode.setAttribute(attName, attValue);
	}
	
}

/* loadFile()
 *
 * Loads a file from the users local drive.
 */
 function loadFile()
 {
 	var file = (document.getElementById( "filename" )).value;
	
	testWindow.location.href = file;
	treeWindow.location.href = "tree.html";
 }
 
/* is_ignorable()
 *
 * Helper function that determines if a node is ignorable so that Netscape won't print
 * empty text nodes.
 */
function is_ignorable( nod )
{
    return ( nod.nodeType == 8) || // A comment node
           ( (nod.nodeType == 3) && is_all_ws(nod) ); // a text node, all ws
}

/* is_all_ws()
 *
 * Helper function that checks if a text node contains only whitespace.
 */
function is_all_ws( nod )
{
    // Use ECMA-262 Edition 3 String and RegExp features
    return !(/[^\t\n\r ]/.test(nod.data));
}

