lib-tree.js

"use strict";


const TREE_TYPES = require( './tree-types.js' );
const TREE_CHILDREN = require( './tree-children.js' );
const TREE_NAVIGATION = require( './tree-navigation.js' );
const TREE_PATH = require( './tree-path.js' );
const TREE_VISITATION = require( './tree-visitation.js' );

//=====================================================================
/**
 * @namespace lib-tree
 */
/**
 * @class Node
 */

//==========================================
// Library Data Types
exports.RelationshipTypes = TREE_TYPES.RelationshipTypes;
exports.VisitationTypes = TREE_TYPES.VisitationTypes;


//==========================================
/**
 * @function	AsNode
 * @memberof	lib-tree
 * 
 * @summary
 * Creates a root node.
 * 
 * @param		{Object=} [Item = Object] - The data item of the node.
 * 					If `Item` is not an `object`, a new `Item` object will be
 * 					created with a `Data` property containing the passed in value.
 * 
 * @returns		{!Object} The data item with an added `Node` member.
 */
exports.AsNode =
	function AsNode( Item )
	{
		if ( typeof Item !== 'object' ) { Item = { Data: Item }; }

		Item.Node = {};

		//==========================================
		/**
		 * @member		{Object} Owner
		 * @memberof	Node
		 * @summary		The owner data object of the given node.
		 */
		Item.Node.Owner = Item;

		//==========================================
		/**
		 * @member		{Node} PrevNode
		 * @memberof	Node
		 * @summary		The previous node of the given node.
		 */
		Item.Node.PrevNode = null;

		//==========================================
		/**
		 * @member		{Node} NextNode
		 * @memberof	Node
		 * @summary		The next node of the given node.
		 */
		Item.Node.NextNode = null;

		//==========================================
		/**
		 * @member		{Number} Indent
		 * @memberof	Node
		 * @summary		The indent level of the given node.
		 */
		Item.Node.Indent = 0;

		//==========================================
		/**
		 * @function	AddChild
		 * @memberof	Node
		 * 
		 * @summary		Adds a new item as a child of this node.
		 * 
		 * @param		{Object=} [ChildItem = Object] - The child object to add.
		 * @param		{number=} [ChildIndex = -1] - The index at which to add the child.
		 * 											Use -1 to append.
		 * 
		 * @returns		{!Object} The newly added child item.
		 */
		Item.Node.AddChild =
			function AddChild( ChildItem, ChildIndex )
			{

				function link_node( Node, PrevNode, NextNode )
				{
					if ( Node.PrevNode || Node.NextNode )
					{
						throw new Error( "This node is already linked." );
					}
					Node.PrevNode = PrevNode;
					Node.NextNode = NextNode;
					if ( Node.PrevNode )
					{
						Node.PrevNode.NextNode = Node;
					}
					if ( Node.NextNode )
					{
						Node.NextNode.PrevNode = Node;
					}
					return Node;
				};

				if ( typeof ChildIndex === 'undefined' ) ChildIndex = -1;
				ChildItem = AsNode( ChildItem );

				var prev_node = this;
				var next_node = this.NextNode;
				while ( next_node )
				{
					if ( next_node.Indent === ( this.Indent + 1 ) )
					{
						// A direct child was found.
						if ( ChildIndex === 0 )
						{
							// This is the child we are looking for. Insert.
							link_node( ChildItem.Node, prev_node, next_node );
							ChildItem.Node.Indent = this.Indent + 1;
							return ChildItem;
						}
						ChildIndex--;
					}
					else if ( next_node.Indent <= this.Indent )
					{
						// No more child nodes. Append.
						link_node( ChildItem.Node, prev_node, next_node );
						ChildItem.Node.Indent = this.Indent + 1;
						return ChildItem;
					}
					// Update the nodes we are looking for.
					prev_node = next_node;
					next_node = next_node.NextNode;
				}

				// No more nodes. Append.
				link_node( ChildItem.Node, prev_node, null );
				ChildItem.Node.Indent = this.Indent + 1;

				return ChildItem;
			};


		//==========================================
		// Additional Children Functions
		Item.Node.ChildCount = TREE_CHILDREN.ChildCount;
		Item.Node.Children = TREE_CHILDREN.Children;
		Item.Node.Child = TREE_CHILDREN.Child;

		Item.Node.DescendantCount = TREE_CHILDREN.DescendantCount;
		Item.Node.Descendants = TREE_CHILDREN.Descendants;
		Item.Node.Descendant = TREE_CHILDREN.Descendant;

		Item.Node.AddChildren = TREE_CHILDREN.AddChildren;
		Item.Node.RemoveChild = TREE_CHILDREN.RemoveChild;
		Item.Node.RemoveChildren = TREE_CHILDREN.RemoveChildren;


		//==========================================
		// Additional Navigation Functions
		Item.Node.FindRelative = TREE_NAVIGATION.FindRelative;

		Item.Node.FindPrevNode = TREE_NAVIGATION.FindPrevNode;
		Item.Node.FindNextNode = TREE_NAVIGATION.FindNextNode;
		Item.Node.FindFirstNode = TREE_NAVIGATION.FindFirstNode;
		Item.Node.FindLastNode = TREE_NAVIGATION.FindLastNode;
		Item.Node.FindRootNode = TREE_NAVIGATION.FindRootNode;
		Item.Node.FindParentNode = TREE_NAVIGATION.FindParentNode;
		Item.Node.FindPrevSiblingNode = TREE_NAVIGATION.FindPrevSiblingNode;
		Item.Node.FindNextSiblingNode = TREE_NAVIGATION.FindNextSiblingNode;
		Item.Node.FindFirstSiblingNode = TREE_NAVIGATION.FindFirstSiblingNode;
		Item.Node.FindLastSiblingNode = TREE_NAVIGATION.FindLastSiblingNode;
		Item.Node.FindFirstChildNode = TREE_NAVIGATION.FindFirstChildNode;
		Item.Node.FindLastChildNode = TREE_NAVIGATION.FindLastChildNode;
		Item.Node.FindFirstDescendantNode = TREE_NAVIGATION.FindFirstDescendantNode;
		Item.Node.FindLastDescendantNode = TREE_NAVIGATION.FindLastDescendantNode;


		//==========================================
		// Additional Visitation Functions
		Item.Node.VisitNodes = TREE_VISITATION.VisitNodes;

		Item.Node.VisitAllNodes = TREE_VISITATION.VisitAllNodes;
		Item.Node.VisitPrevNodes = TREE_VISITATION.VisitPrevNodes;
		Item.Node.VisitNextNodes = TREE_VISITATION.VisitNextNodes;
		Item.Node.VisitParentNodes = TREE_VISITATION.VisitParentNodes;
		Item.Node.VisitSiblingNodes = TREE_VISITATION.VisitSiblingNodes;
		Item.Node.VisitPrevSibNodes = TREE_VISITATION.VisitPrevSibNodes;
		Item.Node.VisitNextSibNodes = TREE_VISITATION.VisitNextSibNodes;
		Item.Node.VisitChildNodes = TREE_VISITATION.VisitChildNodes;
		Item.Node.VisitDescendantNodes = TREE_VISITATION.VisitDescendantNodes;


		//==========================================
		// Additional Path Functions
		Item.Node.TextGraph = TREE_PATH.TextGraph;
		Item.Node.TextPath = TREE_PATH.TextPath;
		Item.Node.FindPath = TREE_PATH.FindPath;


		// Return the item.
		return Item;
	};


//==========================================
/**
 * @function	IsNode
 * @memberof	lib-tree
 * 
 * @summary
 * Determines if an item is a node.
 * 
 * @param		{Object} [Item] - The data item to inspect.
 * 
 * @returns		{boolean} Returns `true` if `Item` is a node, otherwise `false`.
 */
exports.IsNode =
	function IsNode( Item )
	{
		if ( typeof Item !== 'object' ) { return false; }
		if ( typeof Item.Node === 'undefined' ) { return false; }
		return true;
	};