tree-visitation.js

"use strict";


const TREE_TYPES = require( './tree-types.js' );


//==========================================
/**
 * @function	VisitNodes
 * @memberof	Node
 * 
 * @summary
 * Visits every one of a class of nodes defined by VisitationType.
 * 
 * @param		{VisitationTypes} VisitationType - Identifies which nodes to visit.
 * @param		{!function} Visitor - The function to invoke on each node visited.
 * 									If the Visitor returns a value, the visitation process
 * 									is aborted and `VisitNodes` will retun that value.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * 
 * @returns		{boolean} Any return value from `Visitor`, otherwise `undefined`.
 * 
 * @description
 * The VisitationType parameter must be one of:
 * 
 *	AllNodes : Visit all node in the hierarchy (depth-first).
 *	PrevNodes : Visits all the nodes previous to this one (backwards).
 *	NextNodes : Visits all the nodes after this one (forewards).
 *	ParentNodes : Visits all the parents of this node (upwards).
 *	SiblingNodes : Visits all the siblings of this node (sideways).
 *	PrevSibNodes : Visits all the siblings previous to this one.
 *	NextSibNodes : Visits all the siblings after this one.
 *	ChildNodes : Visits all the child nodes of this one. (downwards)
 *	DecendantNodes : Visits all the descendant nodes of this one. (downwards)
 */
//==========================================

exports.VisitNodes =
	function VisitNodes( VisitationType, Visitor, IncludeThis )
	{
		if ( typeof Visitor !== 'function' ) { return false; }
		if ( typeof IncludeThis === 'undefined' ) IncludeThis = false;

		var start_relative = null;
		var next_relative = null;
		var visit_descendants_only = null;

		// Set the visitation parameters.
		if ( VisitationType == TREE_TYPES.VisitationTypes.AllNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.FirstNode;
			next_relative = TREE_TYPES.RelationshipTypes.NextNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.PrevNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.PrevNode;
			next_relative = TREE_TYPES.RelationshipTypes.PrevNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.NextNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.NextNode;
			next_relative = TREE_TYPES.RelationshipTypes.NextNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.ParentNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.ParentNode;
			next_relative = TREE_TYPES.RelationshipTypes.ParentNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.SiblingNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.FirstSibNode;
			next_relative = TREE_TYPES.RelationshipTypes.NextSibNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.PrevSibNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.PrevSibNode;
			next_relative = TREE_TYPES.RelationshipTypes.PrevSibNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.NextSibNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.NextSibNode;
			next_relative = TREE_TYPES.RelationshipTypes.NextSibNode;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.ChildNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.FirstChildNode;
			next_relative = TREE_TYPES.RelationshipTypes.NextSibNode;
			visit_descendants_only = true;
		}
		else if ( VisitationType == TREE_TYPES.VisitationTypes.DescendantNodes )
		{
			start_relative = TREE_TYPES.RelationshipTypes.FirstChildNode;
			next_relative = TREE_TYPES.RelationshipTypes.NextNode;
			visit_descendants_only = true;
		}
		else
		{
			throw Error( "Unknown VisitationType [" + VisitationType + "]." );
		}

		// Visit this node first.
		let visitor_return = null;
		if ( IncludeThis && ( VisitationType !== TREE_TYPES.VisitationTypes.AllNodes ) )
		{
			visitor_return = Visitor( this.Owner );
			if ( typeof visitor_return !== 'undefined' )
			{
				return visitor_return;
			}
		}

		// Get the starting node.
		let item = this.FindRelative( start_relative );
		if ( !item ) { return; }
		let node = item.Node;
		while ( node )
		{
			// Check if we are visiting only descendant nodes.
			if ( visit_descendants_only )
			{
				// Exit if we have run out of descendant nodes to visit.
				if ( node.Indent <= this.Indent )
				{
					return;
				}
			}

			// Visit the node.
			visitor_return = Visitor( node.Owner );
			if ( typeof visitor_return !== 'undefined' )
			{
				return visitor_return;
			}

			// Get the next node to visit.
			item = node.FindRelative( next_relative );
			if ( !item ) { return; }
			node = item.Node;
		}

		return;
	};


//==========================================
/**
 * @function	VisitAllNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @summary
 * Invoke `Visitor` on all the nodes.
 */
exports.VisitAllNodes =
	function VisitAllNodes( Visitor )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.AllNodes, Visitor, true );
	};


//==========================================
/**
 * @function	VisitPrevNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on previous nodes.
 */
exports.VisitPrevNodes =
	function VisitPrevNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.PrevNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitNextNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on next nodes.
 */
exports.VisitNextNodes =
	function VisitNextNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.NextNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitParentNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on parent nodes.
 */
exports.VisitParentNodes =
	function VisitParentNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.ParentNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitSiblingNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on sibling nodes.
 */
exports.VisitSiblingNodes =
	function VisitSiblingNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.SiblingNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitPrevSibNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on previous sibling nodes.
 */
exports.VisitPrevSibNodes =
	function VisitPrevSibNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.PrevSibNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitNextSibNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on next sibling nodes.
 */
exports.VisitNextSibNodes =
	function VisitNextSibNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.NextSibNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitChildNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on child nodes.
 */
exports.VisitChildNodes =
	function VisitChildNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.ChildNodes, Visitor, IncludeThis );
	};


//==========================================
/**
 * @function	VisitDescendantNodes
 * @memberof	Node
 * @param		{!function} Visitor - The function to invoke on each node visited. See `VisitNodes`.
 * @param		{?boolean} [IncludeThis = false] - This node will be included and visited first.
 * @summary
 * Invoke `Visitor` on descendant nodes.
 */
exports.VisitDescendantNodes =
	function VisitDescendantNodes( Visitor, IncludeThis )
	{
		return this.VisitNodes( TREE_TYPES.VisitationTypes.DescendantNodes, Visitor, IncludeThis );
	};