diff --git a/modules/ve/dm/ve.dm.DocumentSynchronizer.js b/modules/ve/dm/ve.dm.DocumentSynchronizer.js index d659996d46..30bd0a85c1 100644 --- a/modules/ve/dm/ve.dm.DocumentSynchronizer.js +++ b/modules/ve/dm/ve.dm.DocumentSynchronizer.js @@ -19,12 +19,12 @@ ve.dm.DocumentSynchronizer = function( model ) { * Adds an action to the synchronizer. * * @method + * @param {String} type Type of action, can be: "insert", "delete", "rebuild", "resize" or "update" * @param {ve.dm.Node} node Node this action is related to * @param {Integer} offset Offset of node, improves performance if this has already been calculated - * @param {String} type Type of action, can be: "insert", "delete", "rebuild", "resize" or "update" * @param {Integer} adjustment Node length adjustment, if any */ -ve.dm.DocumentSynchronizer.prototype.pushAction = function( node, offset, type, adjustment ) { +ve.dm.DocumentSynchronizer.prototype.pushAction = function( type, node, offset, adjustment ) { if ( offset === undefined ) { offset = this.model.getOffsetFromNode( node ); } @@ -54,10 +54,13 @@ ve.dm.DocumentSynchronizer.prototype.synchronize = function() { switch ( action.type ) { case 'insert': // Insert the new node at the given offset - var target = this.model.getNodeFromOffset( offset ); + var target = this.model.getNodeFromOffset( offset + 1 ); if ( target === this.model ) { // Insert at the beginning of the document - target.splice( 0, 0, action.node ); + this.model.splice( 0, 0, action.node ); + } else if ( target === null ) { + // Insert at the end of the document + this.model.splice( this.model.getElementLength(), 0, action.node ); } else { // Insert before the element currently at the offset parent = target.getParent(); @@ -85,13 +88,13 @@ ve.dm.DocumentSynchronizer.prototype.synchronize = function() { break; case 'resize': // Adjust node length - causes update events to be emitted - node.adjustContentLength( adjustment ); + action.node.adjustContentLength( action.adjustment ); // Adjust proceeding offsets by the amount the node is being lengthened or shortened adjustment += action.adjustment; break; case 'update': // Emit update events - node.emit( 'update' ); + action.node.emit( 'update' ); break; } } diff --git a/tests/ve/index.html b/tests/ve/index.html index b6051b528e..bdbd2490e7 100644 --- a/tests/ve/index.html +++ b/tests/ve/index.html @@ -33,6 +33,7 @@ + @@ -55,6 +56,7 @@ + diff --git a/tests/ve/ve.dm.DocumentSynchronizer.test.js b/tests/ve/ve.dm.DocumentSynchronizer.test.js new file mode 100644 index 0000000000..f380584173 --- /dev/null +++ b/tests/ve/ve.dm.DocumentSynchronizer.test.js @@ -0,0 +1,62 @@ +module( 've/dm' ); + +test( 've.dm.TransactionSynchronizer', function() { + var model, + sync, + node, + data; + + // Test 1 - node resizing + + model = ve.dm.DocumentNode.newFromPlainObject( veTest.obj ); + sync = new ve.dm.DocumentSynchronizer( model ); + // Delete bold "b" from first paragraph + model.data.splice( 2, 1 ); + // Push resize action + sync.pushAction( 'resize', model.getChildren()[0], 0, -1 ); + // Sync + sync.synchronize(); + equal( model.getChildren()[0].getContentLength(), 2, 'resize actions adjust node lengths' ); + + // Test 2 - node insertion (in the middle) + + model = ve.dm.DocumentNode.newFromPlainObject( veTest.obj ); + sync = new ve.dm.DocumentSynchronizer( model ); + // Insert element after first paragraph + data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }]; + node = ve.dm.DocumentNode.createNodesFromData( data )[0]; + ve.insertIntoArray( model.data, 5, data ); + // Push insertion action + sync.pushAction( 'insert', node, 5 ); + // Sync + sync.synchronize(); + deepEqual( model.getChildren()[1].getContentData(), ['x'], 'insert actions add new nodes' ); + + // Test 3 - node insertion (at the start) + + model = ve.dm.DocumentNode.newFromPlainObject( veTest.obj ); + sync = new ve.dm.DocumentSynchronizer( model ); + // Insert element after first paragraph + data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }]; + node = ve.dm.DocumentNode.createNodesFromData( data )[0]; + ve.insertIntoArray( model.data, 0, data ); + // Push insertion action + sync.pushAction( 'insert', node, 0 ); + // Sync + sync.synchronize(); + deepEqual( model.getChildren()[0].getContentData(), ['x'], 'insert actions add new nodes' ); + + // Test 4 - node insertion (at the end) + model = ve.dm.DocumentNode.newFromPlainObject( veTest.obj ); + sync = new ve.dm.DocumentSynchronizer( model ); + // Insert element after first paragraph + data = [{ 'type': 'paragraph' }, 'x', { 'type': '/paragraph' }]; + node = ve.dm.DocumentNode.createNodesFromData( data )[0]; + ve.insertIntoArray( model.data, 34, data ); + // Push insertion action + sync.pushAction( 'insert', node, 34 ); + // Sync + sync.synchronize(); + deepEqual( model.getChildren()[3].getContentData(), ['x'], 'insert actions add new nodes' ); +} ); +