Set up comprehensive frontend testing infrastructure
- Install Jest for unit testing with React Testing Library - Install Playwright for end-to-end testing - Configure Jest with proper TypeScript support and module mapping - Create test setup files and utilities for both unit and e2e tests Components: * Jest configuration with coverage thresholds * Playwright configuration with browser automation * Unit tests for LoginForm, AuthContext, and useSocketIO hook * E2E tests for authentication, dashboard, and agents workflows * GitHub Actions workflow for automated testing * Mock data and API utilities for consistent testing * Test documentation with best practices Testing features: - Unit tests with 70% coverage threshold - E2E tests with API mocking and user journey testing - CI/CD integration for automated test runs - Cross-browser testing support with Playwright - Authentication system testing end-to-end 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		
							
								
								
									
										838
									
								
								frontend/node_modules/symbol-tree/lib/SymbolTree.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										838
									
								
								frontend/node_modules/symbol-tree/lib/SymbolTree.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,838 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| /** | ||||
|  * @module symbol-tree | ||||
|  * @author Joris van der Wel <joris@jorisvanderwel.com> | ||||
|  */ | ||||
|  | ||||
| const SymbolTreeNode = require('./SymbolTreeNode'); | ||||
| const TreePosition = require('./TreePosition'); | ||||
| const TreeIterator = require('./TreeIterator'); | ||||
|  | ||||
| function returnTrue() { | ||||
|         return true; | ||||
| } | ||||
|  | ||||
| function reverseArrayIndex(array, reverseIndex) { | ||||
|         return array[array.length - 1 - reverseIndex]; // no need to check `index >= 0` | ||||
| } | ||||
|  | ||||
| class SymbolTree { | ||||
|  | ||||
|         /** | ||||
|          * @constructor | ||||
|          * @alias module:symbol-tree | ||||
|          * @param {string} [description='SymbolTree data'] Description used for the Symbol | ||||
|          */ | ||||
|         constructor(description) { | ||||
|                 this.symbol = Symbol(description || 'SymbolTree data'); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * You can use this function to (optionally) initialize an object right after its creation, | ||||
|          * to take advantage of V8's fast properties. Also useful if you would like to | ||||
|          * freeze your object. | ||||
|          * | ||||
|          * `O(1)` | ||||
|          * | ||||
|          * @method | ||||
|          * @alias module:symbol-tree#initialize | ||||
|          * @param {Object} object | ||||
|          * @return {Object} object | ||||
|          */ | ||||
|         initialize(object) { | ||||
|                 this._node(object); | ||||
|  | ||||
|                 return object; | ||||
|         } | ||||
|  | ||||
|         _node(object) { | ||||
|                 if (!object) { | ||||
|                         return null; | ||||
|                 } | ||||
|  | ||||
|                 const node = object[this.symbol]; | ||||
|  | ||||
|                 if (node) { | ||||
|                         return node; | ||||
|                 } | ||||
|  | ||||
|                 return (object[this.symbol] = new SymbolTreeNode()); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns `true` if the object has any children. Otherwise it returns `false`. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method hasChildren | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Boolean} | ||||
|          */ | ||||
|         hasChildren(object) { | ||||
|                 return this._node(object).hasChildren; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns the first child of the given object. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method firstChild | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} | ||||
|          */ | ||||
|         firstChild(object) { | ||||
|                 return this._node(object).firstChild; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns the last child of the given object. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method lastChild | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} | ||||
|          */ | ||||
|         lastChild(object) { | ||||
|                 return this._node(object).lastChild; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns the previous sibling of the given object. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method previousSibling | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} | ||||
|          */ | ||||
|         previousSibling(object) { | ||||
|                 return this._node(object).previousSibling; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns the next sibling of the given object. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method nextSibling | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} | ||||
|          */ | ||||
|         nextSibling(object) { | ||||
|                 return this._node(object).nextSibling; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Return the parent of the given object. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method parent | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} | ||||
|          */ | ||||
|         parent(object) { | ||||
|                 return this._node(object).parent; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Find the inclusive descendant that is last in tree order of the given object. | ||||
|          * | ||||
|          * * `O(n)` (worst case) where `n` is the depth of the subtree of `object` | ||||
|          * | ||||
|          * @method lastInclusiveDescendant | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} | ||||
|          */ | ||||
|         lastInclusiveDescendant(object) { | ||||
|                 let lastChild; | ||||
|                 let current = object; | ||||
|  | ||||
|                 while ((lastChild = this._node(current).lastChild)) { | ||||
|                         current = lastChild; | ||||
|                 } | ||||
|  | ||||
|                 return current; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Find the preceding object (A) of the given object (B). | ||||
|          * An object A is preceding an object B if A and B are in the same tree | ||||
|          * and A comes before B in tree order. | ||||
|          * | ||||
|          * * `O(n)` (worst case) | ||||
|          * * `O(1)` (amortized when walking the entire tree) | ||||
|          * | ||||
|          * @method preceding | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @param {Object} [options] | ||||
|          * @param {Object} [options.root] If set, `root` must be an inclusive ancestor | ||||
|          *        of the return value (or else null is returned). This check _assumes_ | ||||
|          *        that `root` is also an inclusive ancestor of the given `object` | ||||
|          * @return {?Object} | ||||
|          */ | ||||
|         preceding(object, options) { | ||||
|                 const treeRoot = options && options.root; | ||||
|  | ||||
|                 if (object === treeRoot) { | ||||
|                         return null; | ||||
|                 } | ||||
|  | ||||
|                 const previousSibling = this._node(object).previousSibling; | ||||
|  | ||||
|                 if (previousSibling) { | ||||
|                         return this.lastInclusiveDescendant(previousSibling); | ||||
|                 } | ||||
|  | ||||
|                 // if there is no previous sibling return the parent (might be null) | ||||
|                 return this._node(object).parent; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Find the following object (A) of the given object (B). | ||||
|          * An object A is following an object B if A and B are in the same tree | ||||
|          * and A comes after B in tree order. | ||||
|          * | ||||
|          * * `O(n)` (worst case) where `n` is the amount of objects in the entire tree | ||||
|          * * `O(1)` (amortized when walking the entire tree) | ||||
|          * | ||||
|          * @method following | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {!Object} object | ||||
|          * @param {Object} [options] | ||||
|          * @param {Object} [options.root] If set, `root` must be an inclusive ancestor | ||||
|          *        of the return value (or else null is returned). This check _assumes_ | ||||
|          *        that `root` is also an inclusive ancestor of the given `object` | ||||
|          * @param {Boolean} [options.skipChildren=false] If set, ignore the children of `object` | ||||
|          * @return {?Object} | ||||
|          */ | ||||
|         following(object, options) { | ||||
|                 const treeRoot = options && options.root; | ||||
|                 const skipChildren = options && options.skipChildren; | ||||
|  | ||||
|                 const firstChild = !skipChildren && this._node(object).firstChild; | ||||
|  | ||||
|                 if (firstChild) { | ||||
|                         return firstChild; | ||||
|                 } | ||||
|  | ||||
|                 let current = object; | ||||
|  | ||||
|                 do { | ||||
|                         if (current === treeRoot) { | ||||
|                                 return null; | ||||
|                         } | ||||
|  | ||||
|                         const nextSibling = this._node(current).nextSibling; | ||||
|  | ||||
|                         if (nextSibling) { | ||||
|                                 return nextSibling; | ||||
|                         } | ||||
|  | ||||
|                         current = this._node(current).parent; | ||||
|                 } while (current); | ||||
|  | ||||
|                 return null; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Append all children of the given object to an array. | ||||
|          * | ||||
|          * * `O(n)` where `n` is the amount of children of the given `parent` | ||||
|          * | ||||
|          * @method childrenToArray | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} parent | ||||
|          * @param {Object} [options] | ||||
|          * @param {Object[]} [options.array=[]] | ||||
|          * @param {Function} [options.filter] Function to test each object before it is added to the array. | ||||
|          *                            Invoked with arguments (object). Should return `true` if an object | ||||
|          *                            is to be included. | ||||
|          * @param {*} [options.thisArg] Value to use as `this` when executing `filter`. | ||||
|          * @return {Object[]} | ||||
|          */ | ||||
|         childrenToArray(parent, options) { | ||||
|                 const array   = (options && options.array) || []; | ||||
|                 const filter  = (options && options.filter) || returnTrue; | ||||
|                 const thisArg = (options && options.thisArg) || undefined; | ||||
|  | ||||
|                 const parentNode = this._node(parent); | ||||
|                 let object = parentNode.firstChild; | ||||
|                 let index = 0; | ||||
|  | ||||
|                 while (object) { | ||||
|                         const node = this._node(object); | ||||
|                         node.setCachedIndex(parentNode, index); | ||||
|  | ||||
|                         if (filter.call(thisArg, object)) { | ||||
|                                 array.push(object); | ||||
|                         } | ||||
|  | ||||
|                         object = node.nextSibling; | ||||
|                         ++index; | ||||
|                 } | ||||
|  | ||||
|                 return array; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Append all inclusive ancestors of the given object to an array. | ||||
|          * | ||||
|          * * `O(n)` where `n` is the amount of ancestors of the given `object` | ||||
|          * | ||||
|          * @method ancestorsToArray | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @param {Object} [options] | ||||
|          * @param {Object[]} [options.array=[]] | ||||
|          * @param {Function} [options.filter] Function to test each object before it is added to the array. | ||||
|          *                            Invoked with arguments (object). Should return `true` if an object | ||||
|          *                            is to be included. | ||||
|          * @param {*} [options.thisArg] Value to use as `this` when executing `filter`. | ||||
|          * @return {Object[]} | ||||
|          */ | ||||
|         ancestorsToArray(object, options) { | ||||
|                 const array   = (options && options.array) || []; | ||||
|                 const filter  = (options && options.filter) || returnTrue; | ||||
|                 const thisArg = (options && options.thisArg) || undefined; | ||||
|  | ||||
|                 let ancestor = object; | ||||
|  | ||||
|                 while (ancestor) { | ||||
|                         if (filter.call(thisArg, ancestor)) { | ||||
|                                 array.push(ancestor); | ||||
|                         } | ||||
|                         ancestor = this._node(ancestor).parent; | ||||
|                 } | ||||
|  | ||||
|                 return array; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Append all descendants of the given object to an array (in tree order). | ||||
|          * | ||||
|          * * `O(n)` where `n` is the amount of objects in the sub-tree of the given `object` | ||||
|          * | ||||
|          * @method treeToArray | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} root | ||||
|          * @param {Object} [options] | ||||
|          * @param {Object[]} [options.array=[]] | ||||
|          * @param {Function} [options.filter] Function to test each object before it is added to the array. | ||||
|          *                            Invoked with arguments (object). Should return `true` if an object | ||||
|          *                            is to be included. | ||||
|          * @param {*} [options.thisArg] Value to use as `this` when executing `filter`. | ||||
|          * @return {Object[]} | ||||
|          */ | ||||
|         treeToArray(root, options) { | ||||
|                 const array   = (options && options.array) || []; | ||||
|                 const filter  = (options && options.filter) || returnTrue; | ||||
|                 const thisArg = (options && options.thisArg) || undefined; | ||||
|  | ||||
|                 let object = root; | ||||
|  | ||||
|                 while (object) { | ||||
|                         if (filter.call(thisArg, object)) { | ||||
|                                 array.push(object); | ||||
|                         } | ||||
|                         object = this.following(object, {root: root}); | ||||
|                 } | ||||
|  | ||||
|                 return array; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Iterate over all children of the given object | ||||
|          * | ||||
|          * * `O(1)` for a single iteration | ||||
|          * | ||||
|          * @method childrenIterator | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} parent | ||||
|          * @param {Object} [options] | ||||
|          * @param {Boolean} [options.reverse=false] | ||||
|          * @return {Object} An iterable iterator (ES6) | ||||
|          */ | ||||
|         childrenIterator(parent, options) { | ||||
|                 const reverse = options && options.reverse; | ||||
|                 const parentNode = this._node(parent); | ||||
|  | ||||
|                 return new TreeIterator( | ||||
|                         this, | ||||
|                         parent, | ||||
|                         reverse ? parentNode.lastChild : parentNode.firstChild, | ||||
|                         reverse ? TreeIterator.PREV : TreeIterator.NEXT | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Iterate over all the previous siblings of the given object. (in reverse tree order) | ||||
|          * | ||||
|          * * `O(1)` for a single iteration | ||||
|          * | ||||
|          * @method previousSiblingsIterator | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} An iterable iterator (ES6) | ||||
|          */ | ||||
|         previousSiblingsIterator(object) { | ||||
|                 return new TreeIterator( | ||||
|                         this, | ||||
|                         object, | ||||
|                         this._node(object).previousSibling, | ||||
|                         TreeIterator.PREV | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Iterate over all the next siblings of the given object. (in tree order) | ||||
|          * | ||||
|          * * `O(1)` for a single iteration | ||||
|          * | ||||
|          * @method nextSiblingsIterator | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} An iterable iterator (ES6) | ||||
|          */ | ||||
|         nextSiblingsIterator(object) { | ||||
|                 return new TreeIterator( | ||||
|                         this, | ||||
|                         object, | ||||
|                         this._node(object).nextSibling, | ||||
|                         TreeIterator.NEXT | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Iterate over all inclusive ancestors of the given object | ||||
|          * | ||||
|          * * `O(1)` for a single iteration | ||||
|          * | ||||
|          * @method ancestorsIterator | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} object | ||||
|          * @return {Object} An iterable iterator (ES6) | ||||
|          */ | ||||
|         ancestorsIterator(object) { | ||||
|                 return new TreeIterator( | ||||
|                         this, | ||||
|                         object, | ||||
|                         object, | ||||
|                         TreeIterator.PARENT | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Iterate over all descendants of the given object (in tree order). | ||||
|          * | ||||
|          * Where `n` is the amount of objects in the sub-tree of the given `root`: | ||||
|          * | ||||
|          * * `O(n)` (worst case for a single iteration) | ||||
|          * * `O(n)` (amortized, when completing the iterator) | ||||
|          * | ||||
|          * @method treeIterator | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} root | ||||
|          * @param {Object} options | ||||
|          * @param {Boolean} [options.reverse=false] | ||||
|          * @return {Object} An iterable iterator (ES6) | ||||
|          */ | ||||
|         treeIterator(root, options) { | ||||
|                 const reverse = options && options.reverse; | ||||
|  | ||||
|                 return new TreeIterator( | ||||
|                         this, | ||||
|                         root, | ||||
|                         reverse ? this.lastInclusiveDescendant(root) : root, | ||||
|                         reverse ? TreeIterator.PRECEDING : TreeIterator.FOLLOWING | ||||
|                 ); | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Find the index of the given object (the number of preceding siblings). | ||||
|          * | ||||
|          * * `O(n)` where `n` is the amount of preceding siblings | ||||
|          * * `O(1)` (amortized, if the tree is not modified) | ||||
|          * | ||||
|          * @method index | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} child | ||||
|          * @return {Number} The number of preceding siblings, or -1 if the object has no parent | ||||
|          */ | ||||
|         index(child) { | ||||
|                 const childNode = this._node(child); | ||||
|                 const parentNode = this._node(childNode.parent); | ||||
|  | ||||
|                 if (!parentNode) { | ||||
|                         // In principal, you could also find out the number of preceding siblings | ||||
|                         // for objects that do not have a parent. This method limits itself only to | ||||
|                         // objects that have a parent because that lets us optimize more. | ||||
|                         return -1; | ||||
|                 } | ||||
|  | ||||
|                 let currentIndex = childNode.getCachedIndex(parentNode); | ||||
|  | ||||
|                 if (currentIndex >= 0) { | ||||
|                         return currentIndex; | ||||
|                 } | ||||
|  | ||||
|                 currentIndex = 0; | ||||
|                 let object = parentNode.firstChild; | ||||
|  | ||||
|                 if (parentNode.childIndexCachedUpTo) { | ||||
|                         const cachedUpToNode = this._node(parentNode.childIndexCachedUpTo); | ||||
|                         object = cachedUpToNode.nextSibling; | ||||
|                         currentIndex = cachedUpToNode.getCachedIndex(parentNode) + 1; | ||||
|                 } | ||||
|  | ||||
|                 while (object) { | ||||
|                         const node = this._node(object); | ||||
|                         node.setCachedIndex(parentNode, currentIndex); | ||||
|  | ||||
|                         if (object === child) { | ||||
|                                 break; | ||||
|                         } | ||||
|  | ||||
|                         ++currentIndex; | ||||
|                         object = node.nextSibling; | ||||
|                 } | ||||
|  | ||||
|                 parentNode.childIndexCachedUpTo = child; | ||||
|  | ||||
|                 return currentIndex; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Calculate the number of children. | ||||
|          * | ||||
|          * * `O(n)` where `n` is the amount of children | ||||
|          * * `O(1)` (amortized, if the tree is not modified) | ||||
|          * | ||||
|          * @method childrenCount | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} parent | ||||
|          * @return {Number} | ||||
|          */ | ||||
|         childrenCount(parent) { | ||||
|                 const parentNode = this._node(parent); | ||||
|  | ||||
|                 if (!parentNode.lastChild) { | ||||
|                         return 0; | ||||
|                 } | ||||
|  | ||||
|                 return this.index(parentNode.lastChild) + 1; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Compare the position of an object relative to another object. A bit set is returned: | ||||
|          * | ||||
|          * <ul> | ||||
|          *     <li>DISCONNECTED : 1</li> | ||||
|          *     <li>PRECEDING : 2</li> | ||||
|          *     <li>FOLLOWING : 4</li> | ||||
|          *     <li>CONTAINS : 8</li> | ||||
|          *     <li>CONTAINED_BY : 16</li> | ||||
|          * </ul> | ||||
|          * | ||||
|          * The semantics are the same as compareDocumentPosition in DOM, with the exception that | ||||
|          * DISCONNECTED never occurs with any other bit. | ||||
|          * | ||||
|          * where `n` and `m` are the amount of ancestors of `left` and `right`; | ||||
|          * where `o` is the amount of children of the lowest common ancestor of `left` and `right`: | ||||
|          * | ||||
|          * * `O(n + m + o)` (worst case) | ||||
|          * * `O(n + m)` (amortized, if the tree is not modified) | ||||
|          * | ||||
|          * @method compareTreePosition | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} left | ||||
|          * @param {Object} right | ||||
|          * @return {Number} | ||||
|          */ | ||||
|         compareTreePosition(left, right) { | ||||
|                 // In DOM terms: | ||||
|                 // left = reference / context object | ||||
|                 // right = other | ||||
|  | ||||
|                 if (left === right) { | ||||
|                         return 0; | ||||
|                 } | ||||
|  | ||||
|                 /* jshint -W016 */ | ||||
|  | ||||
|                 const leftAncestors = []; { // inclusive | ||||
|                         let leftAncestor = left; | ||||
|  | ||||
|                         while (leftAncestor) { | ||||
|                                 if (leftAncestor === right) { | ||||
|                                         return TreePosition.CONTAINS | TreePosition.PRECEDING; | ||||
|                                         // other is ancestor of reference | ||||
|                                 } | ||||
|  | ||||
|                                 leftAncestors.push(leftAncestor); | ||||
|                                 leftAncestor = this.parent(leftAncestor); | ||||
|                         } | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 const rightAncestors = []; { | ||||
|                         let rightAncestor = right; | ||||
|  | ||||
|                         while (rightAncestor) { | ||||
|                                 if (rightAncestor === left) { | ||||
|                                         return TreePosition.CONTAINED_BY | TreePosition.FOLLOWING; | ||||
|                                 } | ||||
|  | ||||
|                                 rightAncestors.push(rightAncestor); | ||||
|                                 rightAncestor = this.parent(rightAncestor); | ||||
|                         } | ||||
|                 } | ||||
|  | ||||
|  | ||||
|                 const root = reverseArrayIndex(leftAncestors, 0); | ||||
|  | ||||
|                 if (!root || root !== reverseArrayIndex(rightAncestors, 0)) { | ||||
|                         // note: unlike DOM, preceding / following is not set here | ||||
|                         return TreePosition.DISCONNECTED; | ||||
|                 } | ||||
|  | ||||
|                 // find the lowest common ancestor | ||||
|                 let commonAncestorIndex = 0; | ||||
|                 const ancestorsMinLength = Math.min(leftAncestors.length, rightAncestors.length); | ||||
|  | ||||
|                 for (let i = 0; i < ancestorsMinLength; ++i) { | ||||
|                         const leftAncestor  = reverseArrayIndex(leftAncestors, i); | ||||
|                         const rightAncestor = reverseArrayIndex(rightAncestors, i); | ||||
|  | ||||
|                         if (leftAncestor !== rightAncestor) { | ||||
|                                 break; | ||||
|                         } | ||||
|  | ||||
|                         commonAncestorIndex = i; | ||||
|                 } | ||||
|  | ||||
|                 // indexes within the common ancestor | ||||
|                 const leftIndex  = this.index(reverseArrayIndex(leftAncestors, commonAncestorIndex + 1)); | ||||
|                 const rightIndex = this.index(reverseArrayIndex(rightAncestors, commonAncestorIndex + 1)); | ||||
|  | ||||
|                 return rightIndex < leftIndex | ||||
|                         ? TreePosition.PRECEDING | ||||
|                         : TreePosition.FOLLOWING; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Remove the object from this tree. | ||||
|          * Has no effect if already removed. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method remove | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} removeObject | ||||
|          * @return {Object} removeObject | ||||
|          */ | ||||
|         remove(removeObject) { | ||||
|                 const removeNode = this._node(removeObject); | ||||
|                 const parentNode = this._node(removeNode.parent); | ||||
|                 const prevNode = this._node(removeNode.previousSibling); | ||||
|                 const nextNode = this._node(removeNode.nextSibling); | ||||
|  | ||||
|                 if (parentNode) { | ||||
|                         if (parentNode.firstChild === removeObject) { | ||||
|                                 parentNode.firstChild = removeNode.nextSibling; | ||||
|                         } | ||||
|  | ||||
|                         if (parentNode.lastChild === removeObject) { | ||||
|                                 parentNode.lastChild = removeNode.previousSibling; | ||||
|                         } | ||||
|                 } | ||||
|  | ||||
|                 if (prevNode) { | ||||
|                         prevNode.nextSibling = removeNode.nextSibling; | ||||
|                 } | ||||
|  | ||||
|                 if (nextNode) { | ||||
|                         nextNode.previousSibling = removeNode.previousSibling; | ||||
|                 } | ||||
|  | ||||
|                 removeNode.parent = null; | ||||
|                 removeNode.previousSibling = null; | ||||
|                 removeNode.nextSibling = null; | ||||
|                 removeNode.cachedIndex = -1; | ||||
|                 removeNode.cachedIndexVersion = NaN; | ||||
|  | ||||
|                 if (parentNode) { | ||||
|                         parentNode.childrenChanged(); | ||||
|                 } | ||||
|  | ||||
|                 return removeObject; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Insert the given object before the reference object. | ||||
|          * `newObject` is now the previous sibling of `referenceObject`. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method insertBefore | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} referenceObject | ||||
|          * @param {Object} newObject | ||||
|          * @throws {Error} If the newObject is already present in this SymbolTree | ||||
|          * @return {Object} newObject | ||||
|          */ | ||||
|         insertBefore(referenceObject, newObject) { | ||||
|                 const referenceNode = this._node(referenceObject); | ||||
|                 const prevNode = this._node(referenceNode.previousSibling); | ||||
|                 const newNode = this._node(newObject); | ||||
|                 const parentNode = this._node(referenceNode.parent); | ||||
|  | ||||
|                 if (newNode.isAttached) { | ||||
|                         throw Error('Given object is already present in this SymbolTree, remove it first'); | ||||
|                 } | ||||
|  | ||||
|                 newNode.parent = referenceNode.parent; | ||||
|                 newNode.previousSibling = referenceNode.previousSibling; | ||||
|                 newNode.nextSibling = referenceObject; | ||||
|                 referenceNode.previousSibling = newObject; | ||||
|  | ||||
|                 if (prevNode) { | ||||
|                         prevNode.nextSibling = newObject; | ||||
|                 } | ||||
|  | ||||
|                 if (parentNode && parentNode.firstChild === referenceObject) { | ||||
|                         parentNode.firstChild = newObject; | ||||
|                 } | ||||
|  | ||||
|                 if (parentNode) { | ||||
|                         parentNode.childrenChanged(); | ||||
|                 } | ||||
|  | ||||
|                 return newObject; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Insert the given object after the reference object. | ||||
|          * `newObject` is now the next sibling of `referenceObject`. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method insertAfter | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} referenceObject | ||||
|          * @param {Object} newObject | ||||
|          * @throws {Error} If the newObject is already present in this SymbolTree | ||||
|          * @return {Object} newObject | ||||
|          */ | ||||
|         insertAfter(referenceObject, newObject) { | ||||
|                 const referenceNode = this._node(referenceObject); | ||||
|                 const nextNode = this._node(referenceNode.nextSibling); | ||||
|                 const newNode = this._node(newObject); | ||||
|                 const parentNode = this._node(referenceNode.parent); | ||||
|  | ||||
|                 if (newNode.isAttached) { | ||||
|                         throw Error('Given object is already present in this SymbolTree, remove it first'); | ||||
|                 } | ||||
|  | ||||
|                 newNode.parent = referenceNode.parent; | ||||
|                 newNode.previousSibling = referenceObject; | ||||
|                 newNode.nextSibling = referenceNode.nextSibling; | ||||
|                 referenceNode.nextSibling = newObject; | ||||
|  | ||||
|                 if (nextNode) { | ||||
|                         nextNode.previousSibling = newObject; | ||||
|                 } | ||||
|  | ||||
|                 if (parentNode && parentNode.lastChild === referenceObject) { | ||||
|                         parentNode.lastChild = newObject; | ||||
|                 } | ||||
|  | ||||
|                 if (parentNode) { | ||||
|                         parentNode.childrenChanged(); | ||||
|                 } | ||||
|  | ||||
|                 return newObject; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Insert the given object as the first child of the given reference object. | ||||
|          * `newObject` is now the first child of `referenceObject`. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method prependChild | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} referenceObject | ||||
|          * @param {Object} newObject | ||||
|          * @throws {Error} If the newObject is already present in this SymbolTree | ||||
|          * @return {Object} newObject | ||||
|          */ | ||||
|         prependChild(referenceObject, newObject) { | ||||
|                 const referenceNode = this._node(referenceObject); | ||||
|                 const newNode = this._node(newObject); | ||||
|  | ||||
|                 if (newNode.isAttached) { | ||||
|                         throw Error('Given object is already present in this SymbolTree, remove it first'); | ||||
|                 } | ||||
|  | ||||
|                 if (referenceNode.hasChildren) { | ||||
|                         this.insertBefore(referenceNode.firstChild, newObject); | ||||
|                 } | ||||
|                 else { | ||||
|                         newNode.parent = referenceObject; | ||||
|                         referenceNode.firstChild = newObject; | ||||
|                         referenceNode.lastChild = newObject; | ||||
|                         referenceNode.childrenChanged(); | ||||
|                 } | ||||
|  | ||||
|                 return newObject; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Insert the given object as the last child of the given reference object. | ||||
|          * `newObject` is now the last child of `referenceObject`. | ||||
|          * | ||||
|          * * `O(1)` | ||||
|          * | ||||
|          * @method appendChild | ||||
|          * @memberOf module:symbol-tree# | ||||
|          * @param {Object} referenceObject | ||||
|          * @param {Object} newObject | ||||
|          * @throws {Error} If the newObject is already present in this SymbolTree | ||||
|          * @return {Object} newObject | ||||
|          */ | ||||
|         appendChild(referenceObject, newObject) { | ||||
|                 const referenceNode = this._node(referenceObject); | ||||
|                 const newNode = this._node(newObject); | ||||
|  | ||||
|                 if (newNode.isAttached) { | ||||
|                         throw Error('Given object is already present in this SymbolTree, remove it first'); | ||||
|                 } | ||||
|  | ||||
|                 if (referenceNode.hasChildren) { | ||||
|                         this.insertAfter(referenceNode.lastChild, newObject); | ||||
|                 } | ||||
|                 else { | ||||
|                         newNode.parent = referenceObject; | ||||
|                         referenceNode.firstChild = newObject; | ||||
|                         referenceNode.lastChild = newObject; | ||||
|                         referenceNode.childrenChanged(); | ||||
|                 } | ||||
|  | ||||
|                 return newObject; | ||||
|         } | ||||
| } | ||||
|  | ||||
| module.exports = SymbolTree; | ||||
| SymbolTree.TreePosition = TreePosition; | ||||
		Reference in New Issue
	
	Block a user
	 anthonyrawlins
					anthonyrawlins