Index: trunk/extensions/VisualEditor/modules/es/bases/es.DocumentViewBranchNode.js |
— | — | @@ -117,13 +117,14 @@ |
118 | 118 | if ( args.length >= 3 ) { |
119 | 119 | var $target; |
120 | 120 | if ( index ) { |
121 | | - $target = this.$.children().eq( index ); |
| 121 | + // Get the element before the insertion point |
| 122 | + $anchor = this.$.children().eq( index - 1 ); |
122 | 123 | } |
123 | 124 | for ( i = args.length - 1; i >= 2; i-- ) { |
124 | 125 | args[i].attach( this ); |
125 | 126 | args[i].on( 'update', this.emitUpdate ); |
126 | 127 | if ( index ) { |
127 | | - $target.after( args[i].$ ); |
| 128 | + $anchor.after( args[i].$ ); |
128 | 129 | } else { |
129 | 130 | this.$.prepend( args[i].$ ); |
130 | 131 | } |
Index: trunk/extensions/VisualEditor/modules/es/models/es.DocumentModel.js |
— | — | @@ -65,11 +65,9 @@ |
66 | 66 | * Each function is called in the context of a state, and takes an operation object as a parameter. |
67 | 67 | */ |
68 | 68 | es.DocumentModel.operations = ( function() { |
69 | | - function retain( op ) { |
70 | | - annotate.call( this, this.cursor + op.length ); |
71 | | - this.cursor += op.length; |
72 | | - } |
73 | 69 | |
| 70 | + // Pure functions |
| 71 | + |
74 | 72 | function rebuild( newData, oldNodes ) { |
75 | 73 | var parent = oldNodes[0].getParent(), |
76 | 74 | index = parent.indexOf( oldNodes[0] ); |
— | — | @@ -81,15 +79,43 @@ |
82 | 80 | parent.splice.apply( parent, [index, 0].concat( newNodes ) ); |
83 | 81 | } |
84 | 82 | |
| 83 | + function scope( node, data ) { |
| 84 | + var i, |
| 85 | + length, |
| 86 | + level = 0, |
| 87 | + maxDepth = 0; |
| 88 | + for ( i = 0, length = data.length; i < length; i++ ) { |
| 89 | + if ( typeof data[i].type === 'string' ) { |
| 90 | + level += data[i].type.charAt( 0 ) === '/' ? -1 : 1; |
| 91 | + maxDepth = Math.max( maxDepth, -level ); |
| 92 | + } |
| 93 | + } |
| 94 | + if ( maxDepth > 0 ) { |
| 95 | + for ( i = 0; i < maxDepth; i++ ) { |
| 96 | + node = node.getParent(); |
| 97 | + } |
| 98 | + } |
| 99 | + return node; |
| 100 | + } |
| 101 | + |
| 102 | + // Methods (call in context of state) |
| 103 | + |
| 104 | + function retain( op ) { |
| 105 | + annotate.call( this, this.cursor + op.length ); |
| 106 | + this.cursor += op.length; |
| 107 | + } |
| 108 | + |
85 | 109 | function insert( op ) { |
86 | 110 | if ( es.DocumentModel.isStructuralOffset( this.data, this.cursor ) ) { |
87 | 111 | // TODO: Support tree updates when inserting between elements |
88 | 112 | } else { |
89 | | - // Get the node we are about to insert into |
90 | | - var node = this.tree.getNodeFromOffset( this.cursor ); |
| 113 | + // Get the node we are about to insert into at the lowest depth possible |
| 114 | + var node = scope( this.tree.getNodeFromOffset( this.cursor ), op.data ); |
91 | 115 | if ( !node ) { |
92 | | - throw 'Missing node error. A node could not not be found at the cursor.'; |
| 116 | + throw 'Missing node error. Scope could not be resolved'; |
93 | 117 | } |
| 118 | + // Figure out how deep the data goes |
| 119 | + |
94 | 120 | var offset = this.tree.getOffsetFromNode( node ); |
95 | 121 | if ( es.DocumentModel.containsElementData( op.data ) ) { |
96 | 122 | // Perform insert on linear data model |
— | — | @@ -99,8 +125,7 @@ |
100 | 126 | if ( offset === -1 ) { |
101 | 127 | throw 'Invalid offset error. Node is not in model tree'; |
102 | 128 | } |
103 | | - rebuild.call( |
104 | | - this, |
| 129 | + rebuild( |
105 | 130 | this.data.slice( offset, offset + node.getElementLength() + op.data.length ), |
106 | 131 | [node] |
107 | 132 | ); |