/**
 * @param	source	the checkbox that was clicked
 * @param	up		whether the parent should be updated
 * @param	down		whether the children should be updated
 * @return			nothing
 */

function boxClicked( source, up, down ) {

	if( down ) { /* clicked */
		/* toggle all the children */
		var children = getChildren( source );
		var child;

		for( var i = 0; child = children[i]; i++ ) {
			child.checked = source.checked;
			boxClicked( child, false, true );
		}

	}

	if( up ) {
		var parent = tree_getParent( source );
		if( parent != null ) { // not the root node
			updateParent( parent );
		}
	}

	return true;
}

/**
 * @param	node	the parent node
 */

function updateParent( node ) {
	var children = getChildren( node );
	var allChecked = true;
	var child;

	for( var i = 0; child = children[i]; i++ ) {
		if( !child.checked ) {
			allChecked = false;
			break;
		}
	}

	if( node.checked != allChecked ) {
		node.checked = allChecked;
		boxClicked( node, true, false );
	}

}

/**
 * @param	node	the child node
 * @return	the parent of node
 */

function tree_getParent( node ) {

	if( node.parentNode.parentNode.className == "tree" ) {
		/* root node, no parent */
		return null;
	} else {
		return _firstChild( node.parentNode.parentNode.parentNode );
	}

}

/**
 * Get an array of all of node's children
 * @parem	node	the parent checkbox
 * @return	array of children
 */
function getChildren( node ) {
	var results = new Array();

	if( node.parentNode.className == "internal" ) {
		var childrenDiv = nextNode( node );
		/* all the children are inside of divs */
		if( childrenDiv.className == "branch" ) {
			var child = _firstChild( childrenDiv );
			do {
				results.push( _firstChild( child ) );
				child = nextNode( child );
			} while( child != null );
		} else {
			alert( "Error in getChildren()" );
		}
	}

	return results;
}

/**
 * @param	node	starting node
 * @return	first node text child
 */

function _firstChild( node ) {
	node = node.firstChild;
	while( node.nodeType == node.TEXT_NODE ) {
		node = node.nextSibling;
	}
	return node;
}

/**
 * @param	node	starting node
 * @param	cName	optional class name
 * @return	the previous div with branch, leaf, internal or the
 * 		specified class name
 */

function previousNode( node, cName ) {
	do {
		node = node.previousSibling;
	} while( node != null && !wanted( node, cName ) );
	alert( node.className + node.nodeName );
	return node;
}

/**
 * @param	node	starting node
 * @param	cName	optional class name
 * @return	the next div with branch, leaf, internal or the specified
 *		class name or null if there is no next sibling
 */

function nextNode( node, cName ) {
	do {
		node = node.nextSibling;
	} while( node != null && !wanted( node, cName ) );
	return node;
}

/**
 * check if this is a node we want
 * @param	node	node to check
 * @param	cName	optional class name to check
 * @return	true if it meets the criteria for nodes we want, otherwise
 *		false
 */

function wanted( node, cName ) {
	if(
		node.nodeName == "DIV" &&
		(
			(
				( cName != null ? node.className == cName : false ) ||
				node.className == "leaf" ||
				node.className == "internal" ||
				node.className == "branch"
			)
		)
	) {
		return true;
	} else {
		return false;
	}
}

/**
 *	Toggle visibility of a the branch
 * @param	source	span containing the toggle
 */

function toggleBranch( source ) {
	var branch;
	var label;

	if( source.nodeName == "A" ) {
		branch = nextNode( source.parentNode );
		label = source.firstChild;
	} else {
		branch = nextNode( source );
		label = _firstChild( source ).firstChild;
	}

	if( branch.style.display == "none" ) {
		branch.style.display = "block";
		label.nodeValue = "Collapse";
	} else {
		branch.style.display = "none";
		label.nodeValue ="Expand";
	}

}

