Esanders has uploaded a new change for review.

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


Change subject: (bug 45423) Create SurfaceFragment.isolate method
......................................................................

(bug 45423) Create SurfaceFragment.isolate method

This method will take a selection of siblings and ensure they
are the only chlidren in their first parent which can be placed anywhere
(for example the first parent of a tableCell which can be placed anywhere
is a table, and for a listItem is a list).

The method ensures no redundant empty tags are created, so if
the selection encompasses all siblings then no action is taken.

Also in this commit are two test cases run against ve.dm.example.isolationData.

Change-Id: I783bd5ecd9d43d61f9b2685985409b4d746cbe94
---
M modules/ve/dm/ve.dm.SurfaceFragment.js
M modules/ve/test/dm/ve.dm.SurfaceFragment.test.js
2 files changed, 132 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor 
refs/changes/99/50199/1

diff --git a/modules/ve/dm/ve.dm.SurfaceFragment.js 
b/modules/ve/dm/ve.dm.SurfaceFragment.js
index fd9e095..708fef4 100644
--- a/modules/ve/dm/ve.dm.SurfaceFragment.js
+++ b/modules/ve/dm/ve.dm.SurfaceFragment.js
@@ -690,3 +690,89 @@
        // TODO: Implement
        return this;
 };
+
+/**
+ * Isolates the nodes in a fragment.
+ *
+ * The node selection is expanded to siblings and then are isolated such that 
they are the
+ * sole children of a parent element which can be placed anywhere.
+ *
+ * @method
+ * @returns {ve.dm.SurfaceFragment} This fragment
+ */
+ve.dm.SurfaceFragment.prototype.isolate = function () {
+       // Handle null fragment
+       if ( !this.surface ) {
+               return this;
+       }
+       var nodes, startSplitNode, endSplitNode, i, length, tx,
+               startSplitRequired = false,
+               endSplitRequired = false,
+               startOffset,
+               endOffset,
+               startSplitNodes = [],
+               endSplitNodes = [],
+               fragment = this;
+
+       function buildTags( splitNodes, insertBefore ) {
+               var startOffsetChange = 0, endOffsetChange = 0, data = [];
+               for( i = 0, length = splitNodes.length; i < length; i++ ) {
+
+                       data.unshift( { 'type': '/' + splitNodes[i].type } );
+                       data.push( splitNodes[i].getClonedElement() );
+
+                       if( insertBefore ) {
+                               startOffsetChange += 2;
+                               endOffsetChange += 2;
+                       }
+               }
+
+               tx = ve.dm.Transaction.newFromInsertion( fragment.document, 
insertBefore ? startOffset : endOffset, data );
+               fragment.surface.change( tx, !fragment.noAutoSelect && 
tx.translateRange( fragment.range ) );
+
+               startOffset += startOffsetChange;
+               endOffset += endOffsetChange;
+       }
+       
+       nodes = this.document.selectNodes( this.range, 'siblings' );
+
+       // Find start split point, if required
+       startSplitNode = nodes[0].node;
+       startOffset = startSplitNode.getOuterRange().start;
+       while( startSplitNode.constructor.static.parentNodeTypes !== null ) {
+               if( startSplitNode.parent.indexOf( startSplitNode ) > 0 ) {
+                       startSplitRequired = true;
+               }
+               startSplitNode = startSplitNode.parent;
+               if( startSplitRequired ) {
+                       startSplitNodes.unshift(startSplitNode);
+               } else {
+                       startOffset = startSplitNode.getOuterRange().start;
+               }
+       }
+
+       // Find end split point, if required
+       endSplitNode = nodes[nodes.length - 1].node;
+       endOffset = endSplitNode.getOuterRange().end;
+       while( endSplitNode.constructor.static.parentNodeTypes !== null ) {
+               if( endSplitNode.parent.indexOf( endSplitNode ) < 
endSplitNode.parent.getChildren().length - 1 ) {
+                       endSplitRequired = true;
+               }
+               endSplitNode = endSplitNode.parent;
+               if( endSplitRequired ) {
+                       endSplitNodes.unshift(endSplitNode);
+               } else {
+                       endOffset = endSplitNode.getOuterRange().end;
+               }
+       }
+
+       if( startSplitRequired ) {
+               buildTags( startSplitNodes, true );
+       }
+
+       if( endSplitRequired ) {
+               buildTags( endSplitNodes, false );
+       }
+
+       return this;
+};
diff --git a/modules/ve/test/dm/ve.dm.SurfaceFragment.test.js 
b/modules/ve/test/dm/ve.dm.SurfaceFragment.test.js
index a685386..728dab2 100644
--- a/modules/ve/test/dm/ve.dm.SurfaceFragment.test.js
+++ b/modules/ve/test/dm/ve.dm.SurfaceFragment.test.js
@@ -7,7 +7,7 @@
 
 QUnit.module( 've.dm.SurfaceFragment' );
 
-// Tests
+/* Tests */
 
 QUnit.test( 'constructor', 8, function ( assert ) {
        var doc = new ve.dm.Document( ve.copyArray( ve.dm.example.data ) ),
@@ -225,3 +225,48 @@
                'wrapping nodes can add multiple levels of wrapping to a single 
element'
        );
 } );
+
+function runIsolateTest( assert, range, expected, label ) {
+       var doc = new ve.dm.Document( ve.copyArray( ve.dm.example.isolationData 
) ),
+               surface = new ve.dm.Surface( doc ),
+               fragment = new ve.dm.SurfaceFragment( surface, range ),
+               data;
+
+       data = ve.copyArray( doc.getFullData() );
+       fragment.isolate();
+       expected( data );
+
+       assert.deepEqual( doc.getFullData(), data, label );
+}
+
+QUnit.test( 'isolate', 2, function ( assert ) {
+       var rebuilt = { 'changed': { 'rebuilt': 1 } },
+               created = { 'changed': { 'created': 1 } },
+               createdAndRebuilt = { 'changed': { 'created': 1, 'rebuilt': 1 } 
};
+
+       runIsolateTest( assert, new ve.Range( 11, 21 ), function( data ) {
+               data[0].internal = rebuilt;
+               data.splice( 11, 0, { 'type': '/list' }, { 'type': 'list', 
'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt });
+               data.splice( 23, 0, { 'type': '/list' }, { 'type': 'list', 
'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt });
+       }, 'isolating list item "Item 2"');
+
+       runIsolateTest( assert, new ve.Range( 88, 108 ), function( data ) {
+               data[75].internal = rebuilt;
+               data[76].internal = rebuilt;
+               data[77].internal = rebuilt;
+               data.splice( 88, 0,
+                       { 'type': '/tableRow' },
+                       { 'type': '/tableSection' },
+                       { 'type': '/table' },
+                       { 'type': 'table', 'internal': createdAndRebuilt },
+                       { 'type': 'tableSection', 'attributes': { 'style': 
'body' }, 'internal': createdAndRebuilt },
+                       { 'type': 'tableRow', 'internal': created}
+               );
+               data.splice( 115, 0,
+                       { 'type': '/tableSection' },
+                       { 'type': '/table' },
+                       { 'type': 'table', 'internal': createdAndRebuilt },
+                       { 'type': 'tableSection', 'attributes': { 'style': 
'body' }, 'internal': createdAndRebuilt  }
+               );
+       }, 'isolating table cells "Cell 2" & "Cell 3"');
+} );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I783bd5ecd9d43d61f9b2685985409b4d746cbe94
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>

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

Reply via email to