tree-navigation.js

"use strict";


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


//==========================================
/**
 * @function	FindRelative
 * @memberof	Node
 * @summary
 * Finds a single other node within the hierarchy of a specific relation
 * to the given node.
 * 
 * @param		{RelationshipTypes} RelationshipType - The type of node to look for.
 * 
 * @returns		{?Object} The item found by the RelationshipType, or null if not found.
 * 
 * @description
 * The RelationshipType parameter must be one of:
 * 
 *	PrevNode : Finds the immediately previous node.
 *	NextNode : Finds the immediately next node.
 *	FirstNode : Finds the very first node. This will also always be the root node.
 *	LastNode : Finds the very last node.
 *	RootNode : Finds the root node.
 *	ParentNode : Finds the parent node.
 *	PrevSibNode : Finds the previous sibling node.
 *	NextSibNode : Finds the next sibling node.
 *	FirstSibNode : Finds the first sibling node.
 *	LastSibNode : Finds the last sibling node.
 *	FirstChildNode : Finds the first child node.
 *	LastChildNode : Finds the last child node.
 *	FirstDescNode : Finds the first descendant node.
 *	LastDescNode : Finds the last descendant node.
 *
 * D3 = C2.Prev        A1
 * C3 = C2.Next         +- B1
 * A1 = C1.First        |   +- C1
 * C7 = C1.Last         |   |   +- D1
 * A1 = C1.Root         |   |   +- D2
 * B1 = C1.Parent       |   |   +- D3
 * C1 = C2.PrevSib      |   +- C2
 * C2 = C1.NextSib      |   +- C3
 * C1 = C1.FirstSib     +- B2
 * C3 = C1.LastSib      |   +- C4
 * B1 = A1.FirstChild   |   +- C5
 * B3 = A1.LastChild    |   +- C6
 * B1 = A1.FirstDesc    +- B3
 * C7 = A1.LastDesc         +- C7
 * 
 */
//==========================================

exports.FindRelative =
	function FindRelative( RelationshipType )
	{
		var node_prev = this.PrevNode;
		var node_next = this.NextNode;
		var node_root = this;
		var node_sib = this;
		var node_child = null;
		while ( true )
		{
			if ( RelationshipType === TREE_TYPES.RelationshipTypes.PrevNode )
			{
				return this.PrevNode ? this.PrevNode.Owner : null;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.NextNode )
			{
				return this.NextNode ? this.NextNode.Owner : null;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.FirstNode )
			{
				if ( node_prev === null )
				{
					return this.Owner;
				}
				if ( node_prev.PrevNode === null )
				{
					return node_prev.Owner;
				}
				node_prev = node_prev.PrevNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.LastNode )
			{
				if ( node_next === null )
				{
					return this;
				}
				if ( node_next.NextNode === null )
				{
					return node_next.Owner;
				}
				node_next = node_next.NextNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.RootNode )
			{
				if ( node_prev === null )
				{
					return node_root ? node_root.Owner : null;
				}
				if ( node_prev.Indent < node_root.Indent )
				{
					node_root = node_prev;
				}
				node_prev = node_prev.PrevNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.ParentNode )
			{
				if ( node_prev === null )
				{
					return null;
				}
				if ( node_prev.Indent < this.Indent )
				{
					return node_prev.Owner;
				}
				node_prev = node_prev.PrevNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.PrevSibNode )
			{
				if ( node_prev === null )
				{
					return null;
				}
				if ( node_prev.Indent < this.Indent )
				{
					return null;
				}
				if ( node_prev.Indent === this.Indent )
				{
					return node_prev.Owner;
				}
				node_prev = node_prev.PrevNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.NextSibNode )
			{
				if ( node_next === null )
				{
					return null;
				}
				if ( node_next.Indent < this.Indent )
				{
					return null;
				}
				if ( node_next.Indent === this.Indent )
				{
					return node_next.Owner;
				}
				node_next = node_next.NextNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.FirstSibNode )
			{
				if ( node_prev === null )
				{
					return node_sib ? node_sib.Owner : null;
				}
				if ( node_prev.Indent < this.Indent )
				{
					return node_sib ? node_sib.Owner : null;
				}
				if ( node_prev.Indent === this.Indent )
				{
					node_sib = node_prev;
				}
				node_prev = node_prev.PrevNode;
			}
			else if ( ( RelationshipType === TREE_TYPES.RelationshipTypes.LastSibNode ) )
			{
				if ( node_next === null )
				{
					return node_sib ? node_sib.Owner : null;
				}
				if ( node_next.Indent < this.Indent )
				{
					return node_sib ? node_sib.Owner : null;
				}
				if ( node_next.Indent === this.Indent )
				{
					node_sib = node_next;
				}
				node_next = node_next.NextNode;
			}
			else if ( ( RelationshipType === TREE_TYPES.RelationshipTypes.FirstChildNode ) )
			{
				if ( node_next === null )
				{
					break;
				}
				if ( node_next.Indent <= this.Indent )
				{
					break;
				}
				if ( node_next.Indent === ( this.Indent + 1 ) )
				{
					return node_next.Owner;
				}
				node_next = node_next.NextNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.LastChildNode )
			{
				if ( node_next === null )
				{
					return node_child ? node_child.Owner : null;
				}
				if ( node_next.Indent <= this.Indent )
				{
					return node_child ? node_child.Owner : null;
				}
				if ( node_next.Indent === ( this.Indent + 1 ) )
				{
					node_child = node_next;
				}
				node_next = node_next.NextNode;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.FirstDescNode )
			{
				if ( node_next )
				{
					if ( node_next.Indent > this.Indent )
					{
						return node_next.Owner;
					}
				}
				break;
			}
			else if ( RelationshipType === TREE_TYPES.RelationshipTypes.LastDescNode )
			{
				if ( node_next === null )
				{
					return node_child ? node_child.Owner : null;
				}
				if ( node_next.Indent <= this.Indent )
				{
					return node_child ? node_child.Owner : null;
				}
				if ( node_next.Indent > this.Indent )
				{
					node_child = node_next;
				}
				node_next = node_next.NextNode;
			}
			else
			{
				throw Error( "Unknown RelationshipType [" + RelationshipType + "]." );
			}
		}
		return null;
	};


//==========================================
/**
 * @function	FindPrevNode
 * @memberof	Node
 * @summary
 * Find the previous node in the hierarchy.
 */
exports.FindPrevNode =
	function FindPrevNode()
	{
		if ( this.PrevNode ) { return this.PrevNode.Owner; };
		return null;
	};


//==========================================
/**
 * @function	FindNextNode
 * @memberof	Node
 * @summary
 * Find the next node in the hierarchy.
 */
exports.FindNextNode =
	function FindNextNode()
	{
		if ( this.NextNode ) { return this.NextNode.Owner; };
		return null;
	};


//==========================================
/**
 * @function	FindFirstNode
 * @memberof	Node
 * @summary
 * Find the first node in the hierarchy.
 */
exports.FindFirstNode =
	function FindFirstNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.FirstNode );
	};


//==========================================
/**
 * @function	FindLastNode
 * @memberof	Node
 * @summary
 * Find the last node in the hierarchy.
 */
exports.FindLastNode =
	function FindLastNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.LastNode );
	};


//==========================================
/**
 * @function	FindRootNode
 * @memberof	Node
 * @summary
 * Find the root node of the hierarchy.
 */
exports.FindRootNode =
	function FindRootNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.RootNode );
	};


//==========================================
/**
 * @function	FindParentNode
 * @memberof	Node
 * @summary
 * Find the parent of this node.
 */
exports.FindParentNode =
	function FindParentNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.ParentNode );
	};


//==========================================
/**
 * @function	FindPrevSiblingNode
 * @memberof	Node
 * @summary
 * Find the previous sibling of this node.
 */
exports.FindPrevSiblingNode =
	function FindPrevSiblingNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.PrevSibNode );
	};


//==========================================
/**
 * @function	FindNextSiblingNode
 * @memberof	Node
 * @summary
 * Find the next sibling of this node.
 */
exports.FindNextSiblingNode =
	function FindNextSiblingNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.NextSibNode );
	};


//==========================================
/**
 * @function	FindFirstSiblingNode
 * @memberof	Node
 * @summary
 * Find the first sibling of this node.
 */
exports.FindFirstSiblingNode =
	function FindFirstSiblingNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.FirstSibNode );
	};


//==========================================
/**
 * @function	FindLastSiblingNode
 * @memberof	Node
 * @summary
 * Find the last sibling of this node.
 */
exports.FindLastSiblingNode =
	function FindLastSiblingNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.LastSibNode );
	};


//==========================================
/**
 * @function	FindFirstChildNode
 * @memberof	Node
 * @summary
 * Find the first child of this node.
 */
exports.FindFirstChildNode =
	function FindFirstChildNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.FirstChildNode );
	};


//==========================================
/**
 * @function	FindLastChildNode
 * @memberof	Node
 * @summary
 * Find the last child of this node.
 */
exports.FindLastChildNode =
	function FindLastChildNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.LastChildNode );
	};


//==========================================
/**
 * @function	FindFirstDescendantNode
 * @memberof	Node
 * @summary
 * Find the first descendant of this node.
 * This will always be the same as FindFirstChild().
 */
exports.FindFirstDescendantNode =
	function FindFirstDescendantNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.FirstDescNode );
	};


//==========================================
/**
 * @function	FindLastDescendantNode
 * @memberof	Node
 * @summary
 * Find the last descendant of this node.
 */
exports.FindLastDescendantNode =
	function FindLastDescendantNode()
	{
		return this.FindRelative( TREE_TYPES.RelationshipTypes.LastDescNode );
	};