diff --git a/modules/ve/dm/ve.dm.SurfaceFragment.js b/modules/ve/dm/ve.dm.SurfaceFragment.js index cf36527c14..9bddf55494 100644 --- a/modules/ve/dm/ve.dm.SurfaceFragment.js +++ b/modules/ve/dm/ve.dm.SurfaceFragment.js @@ -364,8 +364,7 @@ ve.dm.SurfaceFragment.prototype.annotateContent = function ( method, type, data } if ( this.range.getLength() ) { tx = ve.dm.Transaction.newFromAnnotation( this.document, this.range, method, annotation ); - this.range = tx.translateRange( this.range ); - this.surface.change( tx, this.autoSelect && this.range ); + this.surface.change( tx, this.autoSelect && tx.translateRange( this.range ) ); } return this; }; @@ -400,9 +399,8 @@ ve.dm.SurfaceFragment.prototype.insertContent = function ( content, annotate ) { ve.dm.Document.addAnnotationsToData( content, annotations ); } } - tx = ve.dm.Transaction.newFromInsertion( this.document, content ); - this.range = tx.translateRange( this.range ); - this.surface.change( tx, this.autoSelect && this.range ); + tx = ve.dm.Transaction.newFromInsertion( this.document, this.range.start, content ); + this.surface.change( tx, this.autoSelect && tx.translateRange( this.range ) ); } return this; }; @@ -421,8 +419,19 @@ ve.dm.SurfaceFragment.prototype.removeContent = function () { var tx; if ( this.range.getLength() ) { tx = ve.dm.Transaction.newFromRemoval( this.document, this.range ); - this.range = tx.translateRange( this.range ); - this.surface.change( tx, this.autoSelect && this.range ); + // this.range will be translated via the onTransact event handler + this.surface.change( tx, this.autoSelect && tx.translateRange( this.range ) ); + // Check if the range didn't get collapsed automatically - this will occur when removing + // content across un-mergable nodes because the delete only strips out content leaving + // structure at the beginning and end of the range in place + if ( this.range.getLength() ) { + // Collapse the range manually + this.range = new ve.Range( this.range.start ); + if ( this.autoSelect ) { + // Update the surface selection + this.surface.setSelection( this.range ); + } + } } return this; }; @@ -441,8 +450,7 @@ ve.dm.SurfaceFragment.prototype.convertNodes = function ( type, attr ) { return this; } var tx = ve.dm.Transaction.newFromContentBranchConversion( this.document, this.range, type, attr ); - this.range = tx.translateRange( this.range ); - this.surface.change( tx, this.autoSelect && this.range ); + this.surface.change( tx, this.autoSelect && tx.translateRange( this.range ) ); 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 a652a15fb9..27cfeb3ad0 100644 --- a/modules/ve/test/dm/ve.dm.SurfaceFragment.test.js +++ b/modules/ve/test/dm/ve.dm.SurfaceFragment.test.js @@ -70,3 +70,45 @@ QUnit.test( 'expandRange', 1, function ( assert ) { ); } ); +QUnit.test( 'removeContent', 2, function ( assert ) { + var doc = new ve.dm.Document( ve.dm.example.data ), + surface = new ve.dm.Surface( doc ), + fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 1, 56 ) ); + fragment.removeContent(); + assert.deepEqual( + doc.getData(), + ve.dm.example.data.slice( 0, 1 ) + .concat( ve.dm.example.data.slice( 4, 5 ) ) + .concat( ve.dm.example.data.slice( 55 ) + ), + 'removing content drops fully covered nodes and strips partially covered ones' + ); + assert.deepEqual( + fragment.getRange(), + new ve.Range( 1, 1 ), + 'removing content results in a zero-length fragment' + ); +} ); + +QUnit.test( 'insertContent', 3, function ( assert ) { + var doc = new ve.dm.Document( ve.dm.example.data ), + surface = new ve.dm.Surface( doc ), + fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 1, 4 ) ); + fragment.insertContent( ['1', '2', '3'] ); + assert.deepEqual( + doc.getData( new ve.Range( 1, 4 ) ), + ['1', '2', '3'], + 'inserting content replaces selection with new content' + ); + assert.deepEqual( + fragment.getRange(), + new ve.Range( 4, 4 ), + 'inserting content results in a zero-length fragment' + ); + fragment.insertContent( '321' ); + assert.deepEqual( + doc.getData( new ve.Range( 4, 7 ) ), + ['3', '2', '1'], + 'strings get converted into data when inserting content' + ); +} );