r110404 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r110403‎ | r110404 | r110405 >
Date:16:43, 31 January 2012
Author:catrope
Status:deferred
Tags:visualeditor 
Comment:
Fix test #30: was failing because getScope() was broken and insert() didn't account for the case of inserting something like </list><list> at a structural offset. All tests are now passing, yay!

* Fix getScope()
** Drop the -1 which caused the result to be off by one level
** Prevent JS errors from occurring if bad input causes the loop to try to traverse up above the root node
* insert()
** Detect the case where the input data tries to close the containing element; in that case, we'll get scope != node
** Move the getNodeFromOffset() and getScope() calls up and out of the conditionals
** Remove unnecessary parent==model conditional, no longer needed now that getScope() can safely handle things that try to traverse too far up
** Add some comments to explain what's going on. I'll restructure this function a bit more shortly
Modified paths:
  • /trunk/extensions/VisualEditor/modules/es/es.TransactionProcessor.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js (modified) (history)

Diff [purge]

Index: trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js
@@ -103,6 +103,9 @@
104104 } else {
105105 // Return to the parent node
106106 currentNode = currentNode.getParent();
 107+ if ( currentNode == null ) {
 108+ throw 'createNodesFromData() received unbalanced data: found closing without matching opening at index ' + i;
 109+ }
107110 }
108111 } else {
109112 // It's content, let's start tracking the length
Index: trunk/extensions/VisualEditor/modules/es/es.TransactionProcessor.js
@@ -164,8 +164,8 @@
165165 }
166166 }
167167 if ( max > 0 ) {
168 - for ( i = 0; i < max - 1; i++ ) {
169 - node = node.getParent();
 168+ for ( i = 0; i < max; i++ ) {
 169+ node = node.getParent() || node;
170170 }
171171 }
172172 return node;
@@ -258,25 +258,28 @@
259259 es.TransactionProcessor.prototype.insert = function( op ) {
260260 var node,
261261 index,
262 - offset;
 262+ offset,
 263+ scope;
263264
264 - if ( es.DocumentModel.isStructuralOffset( this.model.data, this.cursor ) && this.cursor != this.model.data.length ) {
265 - // FIXME: This fails when inserting something like </list><list> between 2 list items
266 - // @see test #30 in es.TransactionProcessor.test.js
 265+ node = this.model.getNodeFromOffset( this.cursor );
 266+ scope = this.getScope( node, op.data );
 267+ // We can take a shortcut if we're inserting an enclosed piece of structural data at a structural offset
 268+ // that isn't at the end of the document. Check for scope == node to ensure the inserted data doesn't try
 269+ // to close its containing element
 270+ if ( es.DocumentModel.isStructuralOffset( this.model.data, this.cursor ) && this.cursor != this.model.data.length
 271+ && scope == node
 272+ ) {
 273+ // We're inserting an enclosed element into something else, so we don't have to rebuild
 274+ // the parent node. Just build a node from the inserted data and stick it in
267275 es.insertIntoArray( this.model.data, this.cursor, op.data );
268276 this.applyAnnotations( this.cursor + op.data.length );
269 - node = this.model.getNodeFromOffset( this.cursor );
270277 offset = this.model.getOffsetFromNode( node );
271278 index = node.getIndexFromOffset( this.cursor - offset );
272279 this.rebuildNodes( op.data, null, node, index );
273280 } else {
274 - node = this.model.getNodeFromOffset( this.cursor );
275 - if ( node.getParent() === this.model ) {
276 - offset = this.model.getOffsetFromNode( node );
277 - } else {
278 - node = this.getScope( node, op.data );
279 - offset = this.model.getOffsetFromNode( node );
280 - }
 281+ // Rebuild scope, which is the node that encloses everything we might have to rebuild
 282+ node = scope;
 283+ offset = this.model.getOffsetFromNode( node );
281284 if ( es.DocumentModel.containsElementData( op.data ) ) {
282285 // Perform insert on linear data model
283286 es.insertIntoArray( this.model.data, this.cursor, op.data );

Status & tagging log