mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-27 12:16:51 +00:00
Made pushRetain do nothing if you give it 0 and throw an exception if you give it a negative length
Change-Id: Ib9955660b05a04503325ddb20f9e9a525b4d6832
This commit is contained in:
parent
9111e34a0b
commit
b0f6f64d90
|
@ -27,15 +27,11 @@ ve.dm.Transaction.newFromInsertion = function( doc, offset, insertion ) {
|
|||
// Fix up the insertion
|
||||
insertion = doc.fixupInsertion( insertion, offset );
|
||||
// Retain up to insertion point, if needed
|
||||
if ( offset ) {
|
||||
tx.pushRetain( offset );
|
||||
}
|
||||
tx.pushRetain( offset );
|
||||
// Insert data
|
||||
tx.pushReplace( [], insertion );
|
||||
// Retain to end of document, if needed (for completeness)
|
||||
if ( offset < data.length ) {
|
||||
tx.pushRetain( data.length - offset );
|
||||
}
|
||||
tx.pushRetain( data.length - offset );
|
||||
return tx;
|
||||
};
|
||||
|
||||
|
@ -46,13 +42,13 @@ ve.dm.Transaction.newFromInsertion = function( doc, offset, insertion ) {
|
|||
* 1. Remove content only
|
||||
* - Occurs when the range starts and ends on elements of different type, depth or ancestry
|
||||
* 2. Remove entire elements and their content
|
||||
* - Occurs when the range spans across an entire element
|
||||
* - Occurs when the range spans accross an entire element
|
||||
* 3. Merge two elements by removing the end of one and the beginning of another
|
||||
* - Occurs when the range starts and ends on elements of similar type, depth and ancestry
|
||||
*
|
||||
* This function uses the following logic to decide what to actually remove:
|
||||
* 1. Elements are only removed if range being removed covers the entire element
|
||||
* 2. Elements can only be merged if ve.dm.Node.canBeMergedWith() returns true
|
||||
* 2. Elements can only be merged if {ve.dm.Node.canBeMergedWith} returns true
|
||||
* 3. Merges take place at the highest common ancestor
|
||||
*
|
||||
* @method
|
||||
|
@ -72,52 +68,44 @@ ve.dm.Transaction.newFromRemoval = function( doc, range ) {
|
|||
return tx;
|
||||
}
|
||||
// Select nodes and validate selection
|
||||
var selection = doc.selectNodes( range, 'leaves' ),
|
||||
nodeRange;
|
||||
var selection = doc.selectNodes( range, 'leaves' );
|
||||
if ( selection.length === 0 ) {
|
||||
// Empty selection? Something is wrong!
|
||||
throw 'Invalid range, cannot remove from ' + range.start + ' to ' + range.end;
|
||||
throw 'Invalid range, can not remove from ' + range.start + ' to ' + range.end;
|
||||
}
|
||||
// Decide whether to merge or strip
|
||||
if ( selection[0].node.canBeMergedWith( selection[selection.length - 1].node ) ) {
|
||||
// If only one node was selected, ignore anything past this node
|
||||
if ( selection.length === 1 ) {
|
||||
// Include the parent's wrapping (if any - there should always be, but let's be safe)
|
||||
var wrapping = selection[0].node.getParent().isWrapped() ? 1 : 0;
|
||||
// Only reduces the range to cover the selected node if it's shorter
|
||||
range.start = Math.max( range.start, selection[0].nodeRange.start - wrapping );
|
||||
// Only reduces the range to cover the selected node if it's shorter
|
||||
range.end = Math.min( range.end, selection[0].nodeRange.end + wrapping );
|
||||
}
|
||||
// Retain to the start of the range
|
||||
if ( range.start > 0 ) {
|
||||
tx.pushRetain( range.start );
|
||||
}
|
||||
// Remove all data in a given range.
|
||||
|
||||
var firstNode = selection[0].node,
|
||||
lastNode = selection[selection.length - 1].node;
|
||||
|
||||
if ( firstNode.canBeMergedWith( lastNode ) ) {
|
||||
// Single node selection or mergable multiple node selection
|
||||
tx.pushRetain( range.start );
|
||||
tx.pushReplace( data.slice( range.start, range.end ), [] );
|
||||
// Retain up to the end of the document, if needed (for completeness)
|
||||
if ( range.end < data.length ) {
|
||||
tx.pushRetain( data.length - range.end );
|
||||
}
|
||||
tx.pushRetain( data.length - range.end );
|
||||
} else {
|
||||
// Unmergable multiple node selection
|
||||
var offset = 0;
|
||||
for ( var i = 0; i < selection.length; i++ ) {
|
||||
nodeRange = selection[i].nodeRange;
|
||||
// Retain up to where the next removal starts
|
||||
if ( nodeRange.start > offset ) {
|
||||
var current = selection[i],
|
||||
node = current.node,
|
||||
nodeRange = current.nodeRange,
|
||||
nodeOuterRange = current.nodeOuterRange;
|
||||
if ( range.start <= nodeOuterRange.start && range.end >= nodeOuterRange.end ) {
|
||||
// Drop the whole node
|
||||
tx.pushRetain( nodeOuterRange.start - offset );
|
||||
tx.pushReplace( data.slice( nodeOuterRange.start, nodeOuterRange.end ), [] );
|
||||
offset = nodeOuterRange.end;
|
||||
} else {
|
||||
// Strip content out of the node
|
||||
tx.pushRetain( nodeRange.start - offset );
|
||||
if ( nodeRange.start !== nodeRange.end ) {
|
||||
tx.pushReplace( data.slice( nodeRange.start, nodeRange.end ), [] );
|
||||
}
|
||||
offset = nodeRange.end;
|
||||
}
|
||||
// Remove data
|
||||
if ( nodeRange.getLength() ) {
|
||||
tx.pushReplace( data.slice( nodeRange.start, nodeRange.end ), [] );
|
||||
}
|
||||
// Advance to the next node
|
||||
offset = nodeRange.end;
|
||||
}
|
||||
// Retain up to the end of the document, if needed (for completeness)
|
||||
if ( offset < data.length ) {
|
||||
tx.pushRetain( data.length - offset );
|
||||
}
|
||||
tx.pushRetain( data.length - offset );
|
||||
}
|
||||
return tx;
|
||||
};
|
||||
|
@ -147,17 +135,13 @@ ve.dm.Transaction.newFromAttributeChange = function( doc, offset, key, value ) {
|
|||
throw 'Can not set attributes on closing element';
|
||||
}
|
||||
// Retain up to element
|
||||
if ( offset ) {
|
||||
tx.pushRetain( offset );
|
||||
}
|
||||
tx.pushRetain( offset );
|
||||
// Change attribute
|
||||
tx.pushReplaceElementAttribute(
|
||||
key, 'attributes' in data[offset] ? data[offset].attributes[key] : undefined, value
|
||||
);
|
||||
// Retain to end of document
|
||||
if ( offset < data.length ) {
|
||||
tx.pushRetain( data.length - offset );
|
||||
}
|
||||
tx.pushRetain( data.length - offset );
|
||||
return tx;
|
||||
};
|
||||
|
||||
|
@ -187,9 +171,7 @@ ve.dm.Transaction.newFromAnnotation = function( doc, range, method, annotation )
|
|||
if ( data[i].type !== undefined ) {
|
||||
// Element
|
||||
if ( on ) {
|
||||
if ( span ) {
|
||||
tx.pushRetain( span );
|
||||
}
|
||||
tx.pushRetain( span );
|
||||
tx.pushStopAnnotating( method, annotation );
|
||||
span = 0;
|
||||
on = false;
|
||||
|
@ -200,9 +182,7 @@ ve.dm.Transaction.newFromAnnotation = function( doc, range, method, annotation )
|
|||
if ( ( covered && method === 'set' ) || ( !covered && method === 'clear' ) ) {
|
||||
// Skip annotated content
|
||||
if ( on ) {
|
||||
if ( span ) {
|
||||
tx.pushRetain( span );
|
||||
}
|
||||
tx.pushRetain( span );
|
||||
tx.pushStopAnnotating( method, annotation );
|
||||
span = 0;
|
||||
on = false;
|
||||
|
@ -210,9 +190,7 @@ ve.dm.Transaction.newFromAnnotation = function( doc, range, method, annotation )
|
|||
} else {
|
||||
// Cover non-annotated content
|
||||
if ( !on ) {
|
||||
if ( span ) {
|
||||
tx.pushRetain( span );
|
||||
}
|
||||
tx.pushRetain( span );
|
||||
tx.pushStartAnnotating( method, annotation );
|
||||
span = 0;
|
||||
on = true;
|
||||
|
@ -222,15 +200,11 @@ ve.dm.Transaction.newFromAnnotation = function( doc, range, method, annotation )
|
|||
span++;
|
||||
i++;
|
||||
}
|
||||
if ( span ) {
|
||||
tx.pushRetain( span );
|
||||
}
|
||||
tx.pushRetain( span );
|
||||
if ( on ) {
|
||||
tx.pushStopAnnotating( method, annotation );
|
||||
}
|
||||
if ( range.end < data.length ) {
|
||||
tx.pushRetain( data.length - range.end );
|
||||
}
|
||||
tx.pushRetain( data.length - range.end );
|
||||
return tx;
|
||||
};
|
||||
|
||||
|
@ -261,16 +235,22 @@ ve.dm.Transaction.prototype.getLengthDifference = function() {
|
|||
*
|
||||
* @method
|
||||
* @param {Integer} length Length of content data to retain
|
||||
* @throws 'Invalid retain length, can not retain backwards: {length}'
|
||||
*/
|
||||
ve.dm.Transaction.prototype.pushRetain = function( length ) {
|
||||
var end = this.operations.length - 1;
|
||||
if ( this.operations.length && this.operations[end].type === 'retain' ) {
|
||||
this.operations[end].length += length;
|
||||
} else {
|
||||
this.operations.push( {
|
||||
'type': 'retain',
|
||||
'length': length
|
||||
} );
|
||||
if ( length < 0 ) {
|
||||
throw 'Invalid retain length, can not retain backwards:' + length;
|
||||
}
|
||||
if ( length ) {
|
||||
var end = this.operations.length - 1;
|
||||
if ( this.operations.length && this.operations[end].type === 'retain' ) {
|
||||
this.operations[end].length += length;
|
||||
} else {
|
||||
this.operations.push( {
|
||||
'type': 'retain',
|
||||
'length': length
|
||||
} );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue