Index: trunk/extensions/VisualEditor/modules/ve/dm/ve.dm.DocumentSyncronizer.js |
— | — | @@ -0,0 +1,99 @@ |
| 2 | +/** |
| 3 | + * Creates an ve.dm.DocumentSynchronizer object. |
| 4 | + * |
| 5 | + * This object is a one-time use utilitiy for collecting actions to be performed on the model tree |
| 6 | + * in multiple steps and then processing those actions in a single step. |
| 7 | + * |
| 8 | + * @class |
| 9 | + * @constructor |
| 10 | + */ |
| 11 | +ve.dm.DocumentSynchronizer = function( model, transaction ) { |
| 12 | + // Properties |
| 13 | + this.model = model; |
| 14 | + this.transaction = transaction; |
| 15 | + this.actions = []; |
| 16 | +}; |
| 17 | + |
| 18 | +/* Methods */ |
| 19 | + |
| 20 | +/** |
| 21 | + * Adds an action to the synchronizer. |
| 22 | + * |
| 23 | + * @method |
| 24 | + * @param {ve.dm.Node} node Node this action is related to |
| 25 | + * @param {Integer} offset Offset of node, improves performance if this has already been calculated |
| 26 | + * @param {String} type Type of action, can be: "insert", "delete", "rebuild", "resize" or "update" |
| 27 | + * @param {Integer} adjustment Node length adjustment, if any |
| 28 | + */ |
| 29 | +ve.dm.DocumentSynchronizer.prototype.pushAction = function( node, offset, type, adjustment ) { |
| 30 | + if ( offset === undefined ) { |
| 31 | + offset = this.model.getOffsetFromNode( node ); |
| 32 | + } |
| 33 | + this.actions.push( { |
| 34 | + 'type': type, |
| 35 | + 'node': node, |
| 36 | + 'offset': offset, |
| 37 | + 'adjustment': adjustment || 0 |
| 38 | + } ); |
| 39 | +}; |
| 40 | + |
| 41 | +/** |
| 42 | + * Applies queued actions to the model tree. |
| 43 | + * |
| 44 | + * @method |
| 45 | + */ |
| 46 | +ve.dm.DocumentSynchronizer.prototype.synchronize = function() { |
| 47 | + // TODO: Normalize the actions list to clean up nested actions |
| 48 | + // Perform all actions |
| 49 | + var adjustment = 0, |
| 50 | + action, |
| 51 | + offset, |
| 52 | + parent; |
| 53 | + for ( var i = 0, len = this.actions.length; i < len; i++ ) { |
| 54 | + action = this.actions[i]; |
| 55 | + offset = action.offset + adjustment; |
| 56 | + switch ( action.type ) { |
| 57 | + case 'insert': |
| 58 | + // Insert the new node at the given offset |
| 59 | + var target = this.model.getNodeFromOffset( offset ); |
| 60 | + if ( target === this.model ) { |
| 61 | + // Insert at the beginning of the document |
| 62 | + target.splice( 0, 0, action.node ); |
| 63 | + } else { |
| 64 | + // Insert before the element currently at the offset |
| 65 | + parent = target.getParent(); |
| 66 | + parent.splice( parent.indexOf( target ), 0, action.node ); |
| 67 | + } |
| 68 | + // Adjust proceeding offsets positively by the length of the node being inserted |
| 69 | + adjustment += action.node.getElementLength(); |
| 70 | + break; |
| 71 | + case 'delete': |
| 72 | + // Replace original node with new node |
| 73 | + parent = action.node.getParent(); |
| 74 | + parentNode.splice( parentNode.indexOf( action.node ), 1 ); |
| 75 | + // Adjust proceeding offsets negatively by the length of the node being deleted |
| 76 | + adjustment -= action.node.getElementLength(); |
| 77 | + break; |
| 78 | + case 'rebuild': |
| 79 | + // Replace original node with new node |
| 80 | + var newNode = ve.dm.DocumentNode.createNodesFromData( this.model.getData( |
| 81 | + new ve.Range( offset, action.node.getElementLength() + action.adjustment ) |
| 82 | + ) ); |
| 83 | + parent = action.node.getParent(); |
| 84 | + parentNode.splice( parentNode.indexOf( action.node ), 1, newNode ); |
| 85 | + // Adjust proceeding offsets by the difference between the original and new nodes |
| 86 | + adjustment += newNode.getElementLength() - action.node.getElementLength(); |
| 87 | + break; |
| 88 | + case 'resize': |
| 89 | + // Adjust node length - causes update events to be emitted |
| 90 | + node.adjustContentLength( adjustment ); |
| 91 | + // Adjust proceeding offsets by the amount the node is being lengthened or shortened |
| 92 | + adjustment += action.adjustment; |
| 93 | + break; |
| 94 | + case 'update': |
| 95 | + // Emit update events |
| 96 | + node.emit( 'update' ); |
| 97 | + break; |
| 98 | + } |
| 99 | + } |
| 100 | +}; |