Divec has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/270315

Change subject: WIP: Refactor getNodeAndOffset
......................................................................

WIP: Refactor getNodeAndOffset

Change-Id: I8b833feee813b10004fc9f1cf862a6fdbf7e1634
---
M src/ce/ve.ce.Document.js
1 file changed, 71 insertions(+), 85 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor 
refs/changes/15/270315/1

diff --git a/src/ce/ve.ce.Document.js b/src/ce/ve.ce.Document.js
index d9f5f27..c013f99 100644
--- a/src/ce/ve.ce.Document.js
+++ b/src/ce/ve.ce.Document.js
@@ -167,9 +167,16 @@
  * @return {number} return.offset location offset within the node
  */
 ve.ce.Document.prototype.getNodeAndOffsetUnadjustedForUnicorn = function ( 
offset ) {
-       var node, startOffset, current, stack, item, $item, length, model,
+       var branchNode, position, count, step, node, textLength, 
matchingPositions, model,
                countedNodes = [],
                slug = this.getSlugAtOffset( offset );
+
+       // Cleaner method:
+       // 1. Step with ve.adjacentDomPosition until we hit a position at the 
correct offset
+       // (which is guaranteed to be the first such position in document 
order).
+       // 2. Use ve.adjacentDomPosition( ..., { skipSoft: false } ) once to 
return all
+       // subsequent positions at the same offset.
+       // 3. Look at the possible positions and pick accordingly.
 
        // If we're a block slug, or an empty inline slug, return its location
        // Start at the current branch node; get its start offset
@@ -197,94 +204,73 @@
        ) ) {
                return { node: slug, offset: 0 };
        }
-       node = this.getBranchNodeFromOffset( offset );
-       startOffset = node.getOffset() + ( ( node.isWrapped() ) ? 1 : 0 );
-       current = { $contents: node.$element.contents(), offset: 0 };
-       stack = [ current ];
-       while ( stack.length > 0 ) {
-               if ( current.offset >= current.$contents.length ) {
-                       stack.pop();
-                       current = stack[ stack.length - 1 ];
-                       if ( current && startOffset === offset ) {
-                               // The current node has no DOM children and no 
DM length (e.g.
-                               // it is a browser-generated <br/> that doesn't 
have class
-                               // ve-ce-leafNode), but the node itself is at 
the required DM
-                               // offset. Return the first offset inside this 
node (even if
-                               // it's a node type that cannot contain 
content, like br).
-                               return { node: current.$contents[ 
current.offset - 1 ], offset: 0 };
+       branchNode = this.getBranchNodeFromOffset( offset );
+       position = { node: branchNode.$element, offset: 0 };
+       count = branchNode.getOffset() + ( ( branchNode.isWrapped() ) ? 1 : 0 );
+
+       function noDescend() {
+               return this.classList.contains( 've-ce-branchNode-blockSlug' ) 
||
+                       ve.rejectsCursor( this );
+       }
+
+       while ( true ) {
+               if ( count === offset ) {
+                       break;
+               }
+               position = ve.adjacentDomPosition( position, 1, { noDescend: 
noDescend } );
+               step = position.steps[ 0 ];
+               node = step.node;
+               if ( node.nodeType === Node.TEXT_NODE ) {
+                       // this branch always breaks or skips over the text 
node; therefore it
+                       // is guaranteed that this is the first time we 
encounter the text node,
+                       // so step.type === 'enter' (we just stepped in)
+                       // TODO: what about zero-length text nodes?
+                       textLength = node.data.length;
+                       if ( offset <= count + textLength ) {
+                               // match the appropriate offset in the text node
+                               matchingPositions.push( { node: node, offset: 
offset - count } );
+                               break;
+                       } else {
+                               // skip over the text node
+                               count += textLength;
+                               position = { node: node, offset: textLength };
+                               continue;
                        }
+               } // else is an element node (TODO: handle comment etc)
+
+               if ( !(
+                       node.classList.contains( 've-ce-branchNode' ) ||
+                       node.classList.contains( 've-ce-leafNode' )
+               ) ) {
+                       // Nodes like b, inline slug, browser-generated br that 
doesn't have
+                       // class ve-ce-leafNode: continue walk without 
incrementing
                        continue;
                }
-               item = current.$contents[ current.offset ];
-               if ( item.nodeType === Node.TEXT_NODE ) {
-                       length = item.textContent.length;
-                       if ( offset >= startOffset && offset <= startOffset + 
length ) {
-                               return {
-                                       node: item,
-                                       offset: offset - startOffset
-                               };
-                       } else {
-                               startOffset += length;
-                       }
-               } else if ( item.nodeType === Node.ELEMENT_NODE ) {
-                       $item = current.$contents.eq( current.offset );
-                       if ( $item.hasClass( 've-ce-unicorn' ) ) {
-                               if ( offset === startOffset ) {
-                                       // Return if empty unicorn pair at the 
correct offset
-                                       if ( $( $item[ 0 ].previousSibling 
).hasClass( 've-ce-unicorn' ) ) {
-                                               return {
-                                                       node: $item[ 0 
].parentNode,
-                                                       offset: current.offset 
- 1
-                                               };
-                                       } else if ( $( $item[ 0 ].nextSibling 
).hasClass( 've-ce-unicorn' ) ) {
-                                               return {
-                                                       node: $item[ 0 
].parentNode,
-                                                       offset: current.offset 
+ 1
-                                               };
-                                       }
-                                       // Else algorithm will/did descend into 
unicorned range
-                               }
-                               // Else algorithm will skip this unicorn
-                       } else if ( $item.is( '.ve-ce-branchNode, 
.ve-ce-leafNode' ) ) {
-                               model = $item.data( 'view' ).model;
-                               // DM nodes can render as multiple elements in 
the view, so check
-                               // we haven't already counted it.
-                               if ( countedNodes.indexOf( model ) === -1 ) {
-                                       length = model.getOuterLength();
-                                       countedNodes.push( model );
-                                       if ( offset >= startOffset && offset < 
startOffset + length ) {
-                                               stack.push( { $contents: 
$item.contents(), offset: 0 } );
-                                               current.offset++;
-                                               current = stack[ stack.length - 
1 ];
-                                               continue;
-                                       } else {
-                                               startOffset += length;
-                                       }
-                               }
-                       } else if ( $item.hasClass( 
've-ce-branchNode-blockSlug' ) ) {
-                               // This is unusual: generated wrappers usually 
mean that the return
-                               // value of getBranchNodeFromOffset will not 
have block slugs or
-                               // block slug ancestors before the offset 
position. However, there
-                               // are some counterexamples; e.g., if the DM 
offset is just before
-                               // the internalList then the start node will be 
the document node.
-                               //
-                               // Skip contents without incrementing offset.
-                               current.offset++;
-                               continue;
-                       } else if ( $item.hasClass( 've-ce-nail' ) ) {
-                               // Skip contents without incrementing offset.
-                               current.offset++;
-                               continue;
-                       } else {
-                               // Any other node type (e.g. b, inline slug, 
browser-generated br
-                               // that doesn't have class ve-ce-leafNode): 
descend
-                               stack.push( { $contents: $item.contents(), 
offset: 0 } );
-                               current.offset++;
-                               current = stack[ stack.length - 1 ];
-                               continue;
-                       }
+
+               if ( step.type === 'leave' ) {
+                       // Below we'll guarantee that 
.ve-ce-branchNode/.ve-ce-leafNode elements
+                       // are only enteres if their open/close tags take up a 
model offset, so
+                       // we can increment unconditionally here
+                       count++;
+                       continue;
+               } // else step.type === 'enter' || step.type === 'cross'
+
+               model = $.data( node, 'view' ).model;
+
+               if ( countedNodes.indexOf( model ) !== -1 ) {
+                       // This DM node is rendered as multiple DOM elements, 
and we have already
+                       // counted it as part of an earlier element. Skip past 
without incrementing
+                       position = { node: node.parentNode, offset: 
ve.parentIndex( node ) + 1 };
+               } else if ( offset >= count + model.getOuterLength() ) {
+                       // Offset doesn't lie inside the node. Skip past and 
count length
+                       // skip past the whole node
+                       position = { node: node.parentNode, offset: 
ve.parentIndex( node ) + 1 };
+                       count += model.getOuterLength();
+               } else if ( step.type === 'cross' ) {
+                       count += 2;
+               } else {
+                       count += 1;
                }
-               current.offset++;
        }
        throw new Error( 'Offset could not be translated to a DOM element and 
offset: ' + offset );
 };

-- 
To view, visit https://gerrit.wikimedia.org/r/270315
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8b833feee813b10004fc9f1cf862a6fdbf7e1634
Gerrit-PatchSet: 1
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Divec <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to