66 */
77
88const SymbolTreeNode = require ( './SymbolTreeNode' ) ;
9+ const TreePosition = require ( './TreePosition' ) ;
910
1011function returnTrue ( ) {
1112 return true ;
1213}
1314
15+ function reverseArrayIndex ( array , reverseIndex ) {
16+ return array [ array . length - 1 - reverseIndex ] ; // no need to check `index >= 0`
17+ }
18+
1419class SymbolTree {
1520
1621 /**
@@ -528,7 +533,7 @@ class SymbolTree {
528533 *
529534 * @method childrenCount
530535 * @memberOf module:symbol-tree#
531- * @param parent
536+ * @param { Object } parent
532537 * @return {Number }
533538 */
534539 childrenCount ( parent ) {
@@ -541,6 +546,98 @@ class SymbolTree {
541546 return this . index ( parentNode . last ) + 1 ;
542547 }
543548
549+ /**
550+ * Compare the position of an object relative to another object. A bit set is returned:
551+ *
552+ * <ul>
553+ * <li>DISCONNECTED : 1</li>
554+ * <li>PRECEDING : 2</li>
555+ * <li>FOLLOWING : 4</li>
556+ * <li>CONTAINS : 8</li>
557+ * <li>CONTAINED_BY : 16</li>
558+ * </ul>
559+ *
560+ * The semantics are the same as compareDocumentPosition in DOM, with the exception that
561+ * DISCONNECTED never occurs with any other bit.
562+ *
563+ * `O(n)` (worst case)
564+ *
565+ * @method childrenCount
566+ * @memberOf module:symbol-tree#
567+ * @param {Object } left
568+ * @param {Object } right
569+ * @return {Number }
570+ */
571+ compareTreePosition ( left , right ) {
572+ // In DOM terms:
573+ // left = reference / context object
574+ // right = other
575+
576+ if ( left === right ) {
577+ return 0 ;
578+ }
579+
580+ /* jshint -W016 */
581+
582+ const leftAncestors = [ ] ; { // inclusive
583+ let leftAncestor = left ;
584+
585+ while ( leftAncestor ) {
586+ if ( leftAncestor === right ) {
587+ return TreePosition . CONTAINS | TreePosition . PRECEDING ;
588+ // other is ancestor of reference
589+ }
590+
591+ leftAncestors . push ( leftAncestor ) ;
592+ leftAncestor = this . parent ( leftAncestor ) ;
593+ }
594+ }
595+
596+
597+ const rightAncestors = [ ] ; {
598+ let rightAncestor = right ;
599+
600+ while ( rightAncestor ) {
601+ if ( rightAncestor === left ) {
602+ return TreePosition . CONTAINED_BY | TreePosition . FOLLOWING ;
603+ }
604+
605+ rightAncestors . push ( rightAncestor ) ;
606+ rightAncestor = this . parent ( rightAncestor ) ;
607+ }
608+ }
609+
610+
611+ const root = reverseArrayIndex ( leftAncestors , 0 ) ;
612+
613+ if ( ! root || root !== reverseArrayIndex ( rightAncestors , 0 ) ) {
614+ // note: unlike DOM, preceding / following is not set here
615+ return TreePosition . DISCONNECTED ;
616+ }
617+
618+ let commonAncestorIndex = 0 ;
619+ const ancestorsMinLength = Math . min ( leftAncestors . length , rightAncestors . length ) ;
620+
621+ for ( let i = 0 ; i < ancestorsMinLength ; ++ i ) {
622+ const leftAncestor = reverseArrayIndex ( leftAncestors , i ) ;
623+ const rightAncestor = reverseArrayIndex ( rightAncestors , i ) ;
624+
625+ if ( leftAncestor !== rightAncestor ) {
626+ break ;
627+ }
628+
629+ commonAncestorIndex = i ;
630+ }
631+
632+ // indexes within the common ancestor
633+ const leftIndex = this . index ( reverseArrayIndex ( leftAncestors , commonAncestorIndex + 1 ) ) ;
634+ const rightIndex = this . index ( reverseArrayIndex ( rightAncestors , commonAncestorIndex + 1 ) ) ;
635+
636+ return rightIndex < leftIndex
637+ ? TreePosition . PRECEDING
638+ : TreePosition . FOLLOWING ;
639+ }
640+
544641 /**
545642 * Remove the object from this tree.
546643 * Has no effect if already removed.
0 commit comments