


var maxId = -1;

function Tree( name, id ) {

	this.name     = name || "";
	this.id       = id || maxId++;
	this.children = [];

}

Tree.prototype.constructor = Tree;

Tree.prototype.traverse = function ( func ) {

	var children = this.children;

	func ( this );

	for ( var i = 0; i < children.length; i++ ) {

		children[ i ].traverse( func );

	}

}

Tree.prototype.forEachChild = function ( func, recurse ) {

	var children = this.children;
	var child;

	for ( var i = 0; i < children.length; i++ ) {

		child = children[ i ];

		func( child );
		if ( recurse === true ) child.forEachChild( func, true );

	}

}

Tree.prototype.addById = function ( name, id, parentId, properties ) {

	var parentNode = this.findById( parentId );

	if ( parentNode ) {

		var node = new Tree( name, id );

		if ( properties !== undefined ) Object.assign( node, properties );

		parentNode.children.push ( node );
		maxId = Math.max( maxId, id );

		return id;

	}

	return null;

}

Tree.prototype.findById = function ( id ) {

	if ( this.id == id ) return this;

	for ( var i = 0, l = this.children.length; i < l; i++ ) {

		var child = this.children[ i ];

		var found = child.findById( id );

		if ( found ) return found;

	}

	return undefined;

}

Tree.prototype.getByPath = function ( path ) {

	var node  = this;
	var search = true;

	while ( search && path.length > 0 ) {

		search = false;

		for ( var i = 0, l = node.children.length; i < l; i++ ) {

			var child = node.children[ i ];

			if ( child.name === path[ 0 ] ) {

				node = child;
				path.shift();
				search = true;

				break;

			}

		}

	}

	return node;

}

Tree.prototype.addPath = function ( path, properties ) {

	var node = this;
	var newNode;

	// find part of path that exists already

	node = this.getByPath( path );

	if ( path.length === 0 ) return node.id;

	// add remainder of path to node

	while ( path.length > 0 ) {

		newNode = new Tree( path.shift() );

		node.children.push( newNode );
		node = newNode;

	}

	if ( properties !== undefined ) Object.assign( node, properties );

	return node.id;

}

Tree.prototype.getSubtreeIds = function ( id, idSet ) {

	var node = this.findById( id );

	node.traverse( _getId );

	function _getId( node ) {

		idSet.add( node.id );

	}

}

Tree.prototype.reduce = function ( name ) {

	var node = this;

	// remove single child nodes from top of tree.
	while ( node.children.length === 1 ) node = node.children[ 0 ];

	if ( !node.name ) node.name = name;

	return node;

}

Tree.prototype.getIdByPath = function ( path ) {

	var node = this.getByPath ( path );

	if ( path.length === 0 ) {

		return node.id;

	} else {

		return undefined;

	}

}

export { Tree };

// EOF
@@ -1,22 +1,4 @@
-%0A%0A%0Avar maxId = -1;
 %0A%0Afu
@@ -22,73 +22,255 @@
 , id
- ) %7B%0A%0A%09this.name     = name %7C%7C %22%22;%0A%09this.id       = id %7C%7C maxId++
+, root ) %7B // root parameter only used internally%0A%0A%09if ( root === undefined ) %7B%0A%0A%09%09this.id = 0;%0A%09%09this.maxId = 0;%0A%09%09this.root = this;%0A%0A%09%7D else %7B%0A%0A%09%09this.root = root;%0A%09%09this.id =  ( id === null ) ? ++root.maxId : id;%0A%09%7D%0A%0A%09this.name     = name %7C%7C %22%22
 ;%0A%09t
@@ -944,24 +944,35 @@
 ee( name, id
+, this.root
  );%0A%0A%09%09if ( 
@@ -1071,18 +1071,50 @@
 node );%0A
+%0A
 %09%09
+var root = this.root;%09%09%0A%09%09root.
 maxId = 
@@ -1123,16 +1123,21 @@
 th.max( 
+root.
 maxId, i
@@ -2140,16 +2140,33 @@
 .shift()
+, null, this.root
  );%0A%0A%09%09n

Move MaxId to root of tree.