https://www.mediawiki.org/wiki/Special:Code/MediaWiki/113425

Revision: 113425
Author:   catrope
Date:     2012-03-08 23:21:26 +0000 (Thu, 08 Mar 2012)
Log Message:
-----------
Implement prepareWrap and add tests for it

Modified Paths:
--------------
    trunk/extensions/VisualEditor/modules/ve/dm/nodes/ve.dm.DocumentNode.js
    trunk/extensions/VisualEditor/tests/ve/ve.dm.DocumentNode.test.js
    trunk/extensions/VisualEditor/tests/ve/ve.dm.TransactionProcessor.test.js

Modified: 
trunk/extensions/VisualEditor/modules/ve/dm/nodes/ve.dm.DocumentNode.js
===================================================================
--- trunk/extensions/VisualEditor/modules/ve/dm/nodes/ve.dm.DocumentNode.js     
2012-03-08 23:21:24 UTC (rev 113424)
+++ trunk/extensions/VisualEditor/modules/ve/dm/nodes/ve.dm.DocumentNode.js     
2012-03-08 23:21:26 UTC (rev 113425)
@@ -1145,6 +1145,87 @@
  * 
  */
 ve.dm.DocumentNode.prototype.prepareWrap = function( range, unwrapOuter, 
wrapOuter, unwrapEach, wrapEach ) {
+       // Function to generate arrays of closing elements in reverse order
+       function closingArray( openings ) {
+               var closings = [], i, len = openings.length;
+               for ( i = 0; i < len; i++ ) {
+                       closings[closings.length] = { 'type': '/' + 
openings[len - i - 1].type };
+               }
+               return closings;
+       }
+       
+       // TODO sanity checks:
+       // * range.start > unwrapOuter.length
+       // * unwraps actually match
+       // * result is valid
+       
+       var tx = new ve.dm.Transaction();
+       range.normalize();
+       if ( range.start > unwrapOuter.length ) {
+               // Retain up to the first thing we're unwrapping
+               // The outer unwrapping takes place *outside*
+               // the range, so compensate for that
+               tx.pushRetain ( range.start - unwrapOuter.length );
+       }
+       
+       // Replace the opening elements for the outer unwrap&wrap
+       if ( wrapOuter.length > 0 || unwrapOuter.length > 0 ) {
+               tx.pushReplace( unwrapOuter, wrapOuter );
+       }
+       
+       if ( wrapEach.length > 0 && unwrapEach.length > 0 ) {
+               var     closingUnwrapEach = closingArray( unwrapEach ),
+                       closingWrapEach = closingArray( wrapEach ),
+                       depth = 0,
+                       startOffset,
+                       i;
+               // Visit each top-level child and wrap/unwrap it
+               // TODO figure out if we should use the tree/node functions here
+               // rather than iterating over offsets, it may or may not be 
faster
+               for ( i = range.start; i < range.end; i++ ) {
+                       if ( this.data[offset].type === undefined ) {
+                               // This is a content offset, skip
+                       } else {
+                               // This is a structural offset
+                               if ( this.data[offset].type.charAt( 0 ) != '/' 
) {
+                                       // This is an opening element
+                                       if ( depth == 0 ) {
+                                               // We are at the start of a 
top-level element
+                                               // Replace the opening elements
+                                               tx.pushReplace( unwrapEach, 
wrapEach );
+                                               // Store this offset for later
+                                               startOffset = i;
+                                       }
+                                       depth++;
+                               } else {
+                                       // This is a closing element
+                                       depth--;
+                                       if ( depth == 0 ) {
+                                               // We are at the end of a 
top-level element
+                                               // Retain the contents of what 
we're wrapping
+                                               tx.pushRetain( i - startOffset 
+ 1 - unwrapEach.length*2 );
+                                               // Replace the closing elements
+                                               tx.pushReplace( 
closingUnwrapEach, closingWrapEach );
+                                       }
+                               }
+                       }
+               }
+       } else {
+               // There is no wrapEach/unwrapEach to be done, just retain
+               // up to the end of the range
+               tx.pushRetain( range.end - range.start );
+       }
+       
+       if ( wrapOuter.length > 0 || unwrapOuter.length > 0 ) {
+               tx.pushReplace( closingArray( unwrapOuter ), closingArray( 
wrapOuter ) );
+       }
+       
+       // Retain up to the end of the document
+       if ( range.end < this.data.length ) {
+               tx.pushRetain( this.data.length - range.end - 
unwrapOuter.length );
+       }
+       
+       return tx;
 };
 
 

Modified: trunk/extensions/VisualEditor/tests/ve/ve.dm.DocumentNode.test.js
===================================================================
--- trunk/extensions/VisualEditor/tests/ve/ve.dm.DocumentNode.test.js   
2012-03-08 23:21:24 UTC (rev 113424)
+++ trunk/extensions/VisualEditor/tests/ve/ve.dm.DocumentNode.test.js   
2012-03-08 23:21:26 UTC (rev 113425)
@@ -800,3 +800,19 @@
                'prepareInsertion throws exception for malformed input'
        );
 } );
+
+test( 've.dm.DocumentNode.prepareWrap', 1, function() {
+       var documentModel = ve.dm.DocumentNode.newFromPlainObject( veTest.obj );
+       
+       // Test 1
+       deepEqual(
+               documentModel.prepareWrap( new ve.Range( 1, 4 ), [ { 'type': 
'paragraph' } ], [ { 'type': 'heading', 'level': 2 } ], [], [] 
).getOperations(),
+               [
+                       { 'type': 'replace', 'remove': [ { 'type': 'paragraph' 
} ], 'replacement': [ { 'type': 'heading', 'level': 2 } ] },
+                       { 'type': 'retain', 'length': 3 },
+                       { 'type': 'replace', 'remove': [ { 'type': '/paragraph' 
} ], 'replacement': [ { 'type': '/heading' } ] },
+                       { 'type': 'retain', 'length': 29 }
+               ],
+               'prepareWrap changes a paragraph to a heading'
+       );
+} );
\ No newline at end of file

Modified: 
trunk/extensions/VisualEditor/tests/ve/ve.dm.TransactionProcessor.test.js
===================================================================
--- trunk/extensions/VisualEditor/tests/ve/ve.dm.TransactionProcessor.test.js   
2012-03-08 23:21:24 UTC (rev 113424)
+++ trunk/extensions/VisualEditor/tests/ve/ve.dm.TransactionProcessor.test.js   
2012-03-08 23:21:26 UTC (rev 113425)
@@ -1,6 +1,6 @@
 module( 've/dm' );
 
-test( 've.dm.TransactionProcessor', 33, function() {
+test( 've.dm.TransactionProcessor', 35, function() {
        var documentModel = ve.dm.DocumentNode.newFromPlainObject( veTest.obj );
 
        // FIXME: These tests shouldn't use prepareFoo() because those functions
@@ -404,4 +404,34 @@
                ],
                'rollback restores content'
        );
+       
+       var paragraphToHeading = documentModel.prepareWrap( new ve.Range( 1, 4 
), [ { 'type': 'paragraph' } ], [ { 'type': 'heading', 'level': 2 } ], [], [] );
+       
+       // Test 33
+       ve.dm.TransactionProcessor.commit( documentModel, paragraphToHeading );
+       deepEqual(
+               documentModel.getData( new ve.Range( 0, 5 ) ),
+               [
+                       { 'type': 'heading', 'level': 2 },
+                       'a',
+                       ['b', { 'type': 'textStyle/bold', 'hash': 
'{"type":"textStyle/bold"}' }],
+                       ['c', { 'type': 'textStyle/italic', 'hash': 
'{"type":"textStyle/italic"}' }],
+                       { 'type': '/heading' }
+               ],
+               'changing paragraph to heading'
+       );
+       
+       // Test 34
+       ve.dm.TransactionProcessor.rollback( documentModel, paragraphToHeading 
);
+       deepEqual(
+               documentModel.getData( new ve.Range( 0, 5 ) ),
+               [
+                       { 'type': 'paragraph' },
+                       'a',
+                       ['b', { 'type': 'textStyle/bold', 'hash': 
'{"type":"textStyle/bold"}' }],
+                       ['c', { 'type': 'textStyle/italic', 'hash': 
'{"type":"textStyle/italic"}' }],
+                       { 'type': '/paragraph' }
+               ],
+               'rollback puts paragraph back'
+       );
 } );


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

Reply via email to