tree-path.js

"use strict";


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


//==========================================
/**
 * @function	TextGraph
 * @memberof	Node
 * @summary
 * Renders a graph of the hierarchy using text characters.
 * 
 * @param		{string} TextProperty - The property to use for the node in the graph
 *									(e.g. it's name or other text value).
 * @param		{boolean=} [IncludeThis = false] - Include the root in the output.
 * @param		{string=} [IndentText = '\t'] - The characters to use to indent each node.
 * @param		{string=} [EolText = '\n'] - The characters to use after each node.
 * 
 * @returns		{string} The text graph as a string.
 */
exports.TextGraph =
	function TextGraph( TextProperty, IncludeThis, IndentText, EolText )
	{
		if ( typeof IncludeThis == 'undefined' ) IncludeThis = false;
		if ( typeof IndentText == 'undefined' ) IndentText = '\t';
		if ( typeof EolText == 'undefined' ) EolText = '\n';

		var text_graph = '';
		if ( IncludeThis )
		{
			text_graph = this.Owner[ TextProperty ];
		}
		var root_indent = this.Indent;
		var next_node = this.NextNode;
		while ( true )
		{
			if ( !next_node )
			{
				break;
			}
			if ( next_node.Indent <= root_indent )
			{
				break;
			}
			text_graph += EolText;
			var start_index = 1;
			if ( !IncludeThis )
			{
				start_index++;
			}
			for ( var index = start_index; index <= ( next_node.Indent - root_indent ); index++ )
			{
				text_graph += IndentText;
			}
			text_graph += next_node.Owner[ TextProperty ];
			next_node = next_node.NextNode;
		}
		return text_graph;
	};


//==========================================
/**
 * @function	TextPath
 * @memberof	Node
 * @summary
 * Retrieves the text path of a node.
 * 
 * @param		{string} TextProperty - The name of the property containing the path elements.
 * @param		{boolean=} [IncludeRoot = true] - The root node will be included in the path.
 * @param		{string=} [Delimiter = '/'] - The character(s) used to delimit the path elements.
 * 
 * @returns		{string} The text path of the node.
 */
exports.TextPath =
	function TextPath( TextProperty, IncludeRoot, Delimiter )
	{
		if ( typeof IncludeRoot == 'undefined' ) IncludeRoot = true;
		if ( typeof Delimiter == 'undefined' ) Delimiter = '/';

		let items = [];
		this.VisitNodes(
			TREE_TYPES.VisitationTypes.ParentNodes,
			Item => { items.push( Item ); return; },
			true
		);
		var last_index = items.length - 1;
		if ( !IncludeRoot )
		{
			last_index--;
		}
		var text_path = '';
		for ( var index = 0; index <= last_index; index++ )
		{
			let item = items[ index ];
			text_path = Delimiter + item[ TextProperty ] + text_path;
		}
		return text_path;
	};


//==========================================
/**
 * @function	FindPath
 * @memberof	Node
 * @summary
 * Locates a node given a text path string.
 * 
 * @param		{string} TextPath - The path of the node to return.
 * @param		{string} TextProperty - The name of the property containing the path elements.
 * @param		{boolean=} [IncludeThis = false] - The path includes the `ThisNode`.
 * @param		{string=} [Delimiter = '/'] - The character(s) used to delimit the path elements.
 * 
 * @returns		{?Object} The located node, or null if not found.
 * 
 * @description
 * Given a text path to a node (e.g. `/A1/B1/C2`), this function will return that node.
 * 
 * The `TextPath` parameter is a string and is similar to strings returned by the `TextPath` function.
 * 
 * The `Delimiter` parameter separates the elements of the path within `TextPath`.
 * If `Delimiter` is empty, `ThisNode` will be returned.
 */
exports.FindPath =
	function FindPath( TextPath, TextProperty, IncludeThis, Delimiter )
	{
		if ( typeof IncludeThis == 'undefined' ) IncludeThis = false;
		if ( typeof Delimiter == 'undefined' ) Delimiter = '/';

		let text_path = TextPath;
		let node = this;
		while ( node && ( text_path.length > 0 ) )
		{
			// Remove leading delimiters
			while ( text_path.indexOf( Delimiter ) == 0 )
			{
				text_path = text_path.substr( Delimiter.length );
			}
			if ( text_path.length > 0 )
			{
				let ich = text_path.indexOf( Delimiter );
				if ( ich < 0 )
				{
					ich = text_path.length;
				}
				let text_value = text_path.substr( 0, ich );
				text_path = text_path.substr( ich );

				let next_child_item = node.VisitNodes(
					TREE_TYPES.VisitationTypes.ChildNodes,
					Item => { if ( Item[ TextProperty ] === text_value ) { return Item; } },
					true
				);
				if ( !next_child_item ) { return null; }
				node = next_child_item.Node;
			}
		}
		if ( node ) { return node.Owner; }
		else { return null; }
	};