mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-25 14:56:20 +00:00
Merge "The resurrection"
This commit is contained in:
commit
b3850a65e3
|
@ -42,7 +42,7 @@ ve.AnnotationAction.static.methods = ['set', 'clear', 'toggle', 'clearAll'];
|
|||
* @param {Object} [data] Additional annotation data
|
||||
*/
|
||||
ve.AnnotationAction.prototype.set = function ( name, data ) {
|
||||
this.surface.getModel().getFragment().annotateContent( 'set', name, data ).destroy();
|
||||
this.surface.getModel().getFragment().annotateContent( 'set', name, data );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -53,7 +53,7 @@ ve.AnnotationAction.prototype.set = function ( name, data ) {
|
|||
* @param {Object} [data] Additional annotation data
|
||||
*/
|
||||
ve.AnnotationAction.prototype.clear = function ( name, data ) {
|
||||
this.surface.getModel().getFragment().annotateContent( 'clear', name, data ).destroy();
|
||||
this.surface.getModel().getFragment().annotateContent( 'clear', name, data );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -68,11 +68,9 @@ ve.AnnotationAction.prototype.clear = function ( name, data ) {
|
|||
*/
|
||||
ve.AnnotationAction.prototype.toggle = function ( name, data ) {
|
||||
var fragment = this.surface.getModel().getFragment();
|
||||
fragment
|
||||
.annotateContent(
|
||||
fragment.getAnnotations().hasAnnotationWithName( name ) ? 'clear' : 'set', name, data
|
||||
)
|
||||
.destroy();
|
||||
fragment.annotateContent(
|
||||
fragment.getAnnotations().hasAnnotationWithName( name ) ? 'clear' : 'set', name, data
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -91,7 +89,6 @@ ve.AnnotationAction.prototype.clearAll = function () {
|
|||
for ( i = 0, len = arr.length; i < len; i++ ) {
|
||||
fragment.annotateContent( 'clear', arr[i].name, arr[i].data );
|
||||
}
|
||||
fragment.destroy();
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
|
|
@ -42,7 +42,7 @@ ve.ContentAction.static.methods = ['insert', 'remove', 'select'];
|
|||
* @param {boolean} annotate Content should be automatically annotated to match surrounding content
|
||||
*/
|
||||
ve.ContentAction.prototype.insert = function ( content, annotate ) {
|
||||
this.surface.getModel().getFragment().insertContent( content, annotate ).destroy();
|
||||
this.surface.getModel().getFragment().insertContent( content, annotate );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ ve.ContentAction.prototype.insert = function ( content, annotate ) {
|
|||
* @method
|
||||
*/
|
||||
ve.ContentAction.prototype.remove = function () {
|
||||
this.surface.getModel().getFragment().removeContent().destroy();
|
||||
this.surface.getModel().getFragment().removeContent();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -65,10 +65,9 @@ ve.FormatAction.prototype.convert = function ( type, attributes ) {
|
|||
}
|
||||
|
||||
for ( i = 0, length = fragments.length; i < length; i++ ) {
|
||||
fragments[i].isolateAndUnwrap( type ).destroy();
|
||||
fragments[i].isolateAndUnwrap( type );
|
||||
}
|
||||
selection = fragmentForSelection.getRange();
|
||||
fragmentForSelection.destroy();
|
||||
|
||||
txs = ve.dm.Transaction.newFromContentBranchConversion( doc, selection, type, attributes );
|
||||
surfaceModel.change( txs, selection );
|
||||
|
|
|
@ -65,10 +65,9 @@ ve.IndentationAction.prototype.increase = function () {
|
|||
this.indentListItem(
|
||||
documentModel.getNodeFromOffset( fragments[i].getRange().start + 1 )
|
||||
);
|
||||
fragments[i].destroy();
|
||||
}
|
||||
|
||||
selected.select().destroy();
|
||||
selected.select();
|
||||
|
||||
return increased;
|
||||
};
|
||||
|
@ -104,10 +103,9 @@ ve.IndentationAction.prototype.decrease = function () {
|
|||
this.unindentListItem(
|
||||
documentModel.getNodeFromOffset( fragments[i].getRange().start + 1 )
|
||||
);
|
||||
fragments[i].destroy();
|
||||
}
|
||||
|
||||
selected.select().destroy();
|
||||
selected.select();
|
||||
|
||||
return decreased;
|
||||
};
|
||||
|
@ -301,7 +299,6 @@ ve.IndentationAction.prototype.unindentListItem = function ( listItem ) {
|
|||
);
|
||||
surfaceModel.change( tx );
|
||||
}
|
||||
fragment.destroy();
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
|
|
@ -94,7 +94,7 @@ ve.ce.ImageNode.prototype.onClick = function ( e ) {
|
|||
[ selectionRange, nodeRange ], selectionRange.from > nodeRange.from
|
||||
) :
|
||||
nodeRange
|
||||
).select().destroy();
|
||||
).select();
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
|
|
@ -324,12 +324,11 @@ ve.ce.Surface.prototype.onDocumentDrop = function ( e ) {
|
|||
nodeData = originFragment.getData();
|
||||
|
||||
// Remove node from old location (auto-updates targetFragment's range)
|
||||
originFragment.removeContent().destroy();
|
||||
originFragment.removeContent();
|
||||
|
||||
// Re-insert node at new location and re-select it
|
||||
targetFragment.insertContent( nodeData );
|
||||
targetFragment.adjustRange( -nodeData.length, 0 ).select().destroy();
|
||||
targetFragment.destroy();
|
||||
targetFragment.adjustRange( -nodeData.length, 0 ).select();
|
||||
}, this ) );
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ ve.dm.Surface = function VeDmSurface( doc ) {
|
|||
this.selectedNodes = {};
|
||||
this.smallStack = [];
|
||||
this.bigStack = [];
|
||||
this.completeHistory = [];
|
||||
this.undoIndex = 0;
|
||||
this.historyTrackingInterval = null;
|
||||
this.insertionAnnotations = new ve.dm.AnnotationSet( this.documentModel.getStore() );
|
||||
|
@ -286,6 +287,10 @@ ve.dm.Surface.prototype.change = function ( transactions, selection ) {
|
|||
this.bigStack = this.bigStack.slice( 0, this.bigStack.length - this.undoIndex );
|
||||
this.undoIndex = 0;
|
||||
this.smallStack.push( transactions[i] );
|
||||
this.completeHistory.push( {
|
||||
'undo': false,
|
||||
'transaction': transactions[i]
|
||||
} );
|
||||
this.documentModel.commit( transactions[i] );
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +416,11 @@ ve.dm.Surface.prototype.undo = function () {
|
|||
for ( i = item.stack.length - 1; i >= 0; i-- ) {
|
||||
transaction = item.stack[i];
|
||||
selection = transaction.translateRange( selection, true );
|
||||
this.documentModel.rollback( item.stack[i] );
|
||||
this.completeHistory.push( {
|
||||
'undo': true,
|
||||
'transaction': transaction
|
||||
} );
|
||||
this.documentModel.rollback( transaction );
|
||||
}
|
||||
this.emit( 'unlock' );
|
||||
this.emit( 'history' );
|
||||
|
@ -420,6 +429,23 @@ ve.dm.Surface.prototype.undo = function () {
|
|||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the length of the complete history stack. This is also the current pointer.
|
||||
* @returns {number} Length of the complete history stack
|
||||
*/
|
||||
ve.dm.Surface.prototype.getCompleteHistoryLength = function () {
|
||||
return this.completeHistory.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all the items in the complete history stack since a specified pointer.
|
||||
* @param {number} pointer Pointer from where to start the slice
|
||||
* @returns {Array} Array of transaction objects with undo flag
|
||||
*/
|
||||
ve.dm.Surface.prototype.getCompleteHistorySince = function ( pointer ) {
|
||||
return this.completeHistory.slice( pointer );
|
||||
};
|
||||
|
||||
/**
|
||||
* Step forwards in history.
|
||||
*
|
||||
|
@ -434,7 +460,7 @@ ve.dm.Surface.prototype.redo = function () {
|
|||
if ( !this.enabled ) {
|
||||
return;
|
||||
}
|
||||
var selection, item, i;
|
||||
var item, i, transaction, selection;
|
||||
this.breakpoint();
|
||||
|
||||
if ( this.undoIndex > 0 && this.bigStack[this.bigStack.length - this.undoIndex] ) {
|
||||
|
@ -442,7 +468,12 @@ ve.dm.Surface.prototype.redo = function () {
|
|||
item = this.bigStack[this.bigStack.length - this.undoIndex];
|
||||
selection = item.selection;
|
||||
for ( i = 0; i < item.stack.length; i++ ) {
|
||||
this.documentModel.commit( item.stack[i] );
|
||||
transaction = item.stack[i];
|
||||
this.completeHistory.push( {
|
||||
'undo': false,
|
||||
'transaction': transaction
|
||||
} );
|
||||
this.documentModel.commit( transaction );
|
||||
}
|
||||
this.undoIndex--;
|
||||
this.emit( 'unlock' );
|
||||
|
|
|
@ -21,26 +21,23 @@ ve.dm.SurfaceFragment = function VeDmSurfaceFragment( surface, range, noAutoSele
|
|||
}
|
||||
|
||||
// Properties
|
||||
this.range = range && range instanceof ve.Range ? range : surface.getSelection();
|
||||
this.surface = surface;
|
||||
this.setRange( range && range instanceof ve.Range ? range : surface.getSelection() );
|
||||
// Short-circuit for invalid range null fragment
|
||||
if ( !this.range ) {
|
||||
return this;
|
||||
}
|
||||
this.surface = surface;
|
||||
this.document = surface.getDocument();
|
||||
this.noAutoSelect = !!noAutoSelect;
|
||||
this.onTransactHandler = ve.bind( this.onTransact, this );
|
||||
|
||||
// Events
|
||||
surface.on( 'transact', this.onTransactHandler );
|
||||
|
||||
// Initialization
|
||||
var length = this.document.data.getLength();
|
||||
this.range = new ve.Range(
|
||||
this.setRange( new ve.Range(
|
||||
// Clamp range to valid document offsets
|
||||
Math.min( Math.max( this.range.from, 0 ), length ),
|
||||
Math.min( Math.max( this.range.to, 0 ), length )
|
||||
);
|
||||
) );
|
||||
};
|
||||
|
||||
/* Static Properties */
|
||||
|
@ -55,16 +52,19 @@ ve.dm.SurfaceFragment.static = {};
|
|||
/* Methods */
|
||||
|
||||
/**
|
||||
* Handle transactions being processed on the document.
|
||||
*
|
||||
* This keeps the range of the fragment valid, even while other transactions are being processed
|
||||
* Update range based on un-applied transactions in the surface.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.Transaction[]} txs Transactions that have just been processed
|
||||
*/
|
||||
ve.dm.SurfaceFragment.prototype.onTransact = function ( txs ) {
|
||||
for ( var i = 0; i < txs.length; i++ ) {
|
||||
this.range = txs[i].translateRange( this.range );
|
||||
ve.dm.SurfaceFragment.prototype.update = function () {
|
||||
var i, length, txs;
|
||||
// Small optimisation: check history pointer is in the past
|
||||
if ( this.historyPointer < this.getSurface().getCompleteHistoryLength() ) {
|
||||
txs = this.getSurface().getCompleteHistorySince( this.historyPointer );
|
||||
for ( i = 0, length = txs.length; i < length; i++ ) {
|
||||
this.range = txs[i].transaction.translateRange( this.range, txs[i].undo );
|
||||
this.historyPointer++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -91,11 +91,26 @@ ve.dm.SurfaceFragment.prototype.getDocument = function () {
|
|||
/**
|
||||
* Get the range of the fragment within the surface.
|
||||
*
|
||||
* This method also calls update to make sure the range returned is current.
|
||||
*
|
||||
* @method
|
||||
* @param {boolean} noCopy Return the range by reference, not a copy
|
||||
* @returns {ve.Range} Surface range
|
||||
*/
|
||||
ve.dm.SurfaceFragment.prototype.getRange = function () {
|
||||
return this.range.clone();
|
||||
ve.dm.SurfaceFragment.prototype.getRange = function ( noCopy ) {
|
||||
this.update();
|
||||
return noCopy ? this.range : this.range.clone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the range of the transaction and reset the history pointer.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.Range} range New range
|
||||
*/
|
||||
ve.dm.SurfaceFragment.prototype.setRange = function ( range ) {
|
||||
this.historyPointer = this.getSurface().getCompleteHistoryLength();
|
||||
this.range = range;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -108,21 +123,6 @@ ve.dm.SurfaceFragment.prototype.isNull = function () {
|
|||
return this.surface === undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroys fragment, removing event handlers and object references, leaving the fragment null.
|
||||
*
|
||||
* Call this whenever you are done using a fragment.
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
ve.dm.SurfaceFragment.prototype.destroy = function () {
|
||||
if ( this.surface ) {
|
||||
this.surface.removeListener( 'transact', this.onTransactHandler );
|
||||
this.surface = null;
|
||||
this.document = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a new fragment with an adjusted position
|
||||
*
|
||||
|
@ -138,7 +138,7 @@ ve.dm.SurfaceFragment.prototype.adjustRange = function ( start, end ) {
|
|||
}
|
||||
return new ve.dm.SurfaceFragment(
|
||||
this.surface,
|
||||
new ve.Range( this.range.start + ( start || 0 ), this.range.end + ( end || 0 ) ),
|
||||
new ve.Range( this.getRange( true ).start + ( start || 0 ), this.getRange( true ).end + ( end || 0 ) ),
|
||||
this.noAutoSelect
|
||||
);
|
||||
};
|
||||
|
@ -157,7 +157,7 @@ ve.dm.SurfaceFragment.prototype.truncateRange = function ( limit ) {
|
|||
}
|
||||
return new ve.dm.SurfaceFragment(
|
||||
this.surface,
|
||||
this.range.truncate( limit ),
|
||||
this.getRange().truncate( limit ),
|
||||
this.noAutoSelect
|
||||
);
|
||||
};
|
||||
|
@ -174,7 +174,7 @@ ve.dm.SurfaceFragment.prototype.collapseRange = function () {
|
|||
return this;
|
||||
}
|
||||
return new ve.dm.SurfaceFragment(
|
||||
this.surface, new ve.Range( this.range.start ), this.noAutoSelect
|
||||
this.surface, new ve.Range( this.getRange( true ).start ), this.noAutoSelect
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -190,14 +190,14 @@ ve.dm.SurfaceFragment.prototype.trimRange = function () {
|
|||
return this;
|
||||
}
|
||||
// If range is only whitespace
|
||||
if ( this.document.getText( this.range ).trim().length === 0 ) {
|
||||
if ( this.document.getText( this.getRange() ).trim().length === 0 ) {
|
||||
// Collapse range
|
||||
return new ve.dm.SurfaceFragment(
|
||||
this.surface, new ve.Range( this.range.start ), this.noAutoSelect
|
||||
this.surface, new ve.Range( this.getRange( true ).start ), this.noAutoSelect
|
||||
);
|
||||
}
|
||||
return new ve.dm.SurfaceFragment(
|
||||
this.surface, this.document.data.trimOuterSpaceFromRange( this.range ), this.noAutoSelect
|
||||
this.surface, this.document.data.trimOuterSpaceFromRange( this.getRange() ), this.noAutoSelect
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -223,30 +223,30 @@ ve.dm.SurfaceFragment.prototype.expandRange = function ( scope, type ) {
|
|||
var range, node, nodes, parent;
|
||||
switch ( scope || 'parent' ) {
|
||||
case 'word':
|
||||
if ( this.range.getLength() > 0 ) {
|
||||
if ( this.getRange( true ).getLength() > 0 ) {
|
||||
range = ve.Range.newCoveringRange( [
|
||||
this.document.data.getNearestWordRange( this.range.start ),
|
||||
this.document.data.getNearestWordRange( this.range.end )
|
||||
this.document.data.getNearestWordRange( this.getRange( true ).start ),
|
||||
this.document.data.getNearestWordRange( this.getRange( true ).end )
|
||||
] );
|
||||
if ( this.range.isBackwards() ) {
|
||||
if ( this.getRange( true ).isBackwards() ) {
|
||||
range = range.flip();
|
||||
}
|
||||
} else {
|
||||
// optimisation for zero-length ranges
|
||||
range = this.document.data.getNearestWordRange( this.range.start );
|
||||
range = this.document.data.getNearestWordRange( this.getRange( true ).start );
|
||||
}
|
||||
break;
|
||||
case 'annotation':
|
||||
range = this.document.data.getAnnotatedRangeFromSelection( this.range, type );
|
||||
range = this.document.data.getAnnotatedRangeFromSelection( this.getRange(), type );
|
||||
// Adjust selection if it does not contain the annotated range
|
||||
if ( this.range.start > range.start || this.range.end < range.end ) {
|
||||
if ( this.getRange( true ).start > range.start || this.getRange( true ).end < range.end ) {
|
||||
// Maintain range direction
|
||||
if ( this.range.from > this.range.to ) {
|
||||
if ( this.getRange( true ).from > this.getRange( true ).to ) {
|
||||
range = range.flip();
|
||||
}
|
||||
} else {
|
||||
// Otherwise just keep the range as is
|
||||
range = this.range.clone();
|
||||
range = this.getRange();
|
||||
}
|
||||
break;
|
||||
case 'root':
|
||||
|
@ -254,7 +254,7 @@ ve.dm.SurfaceFragment.prototype.expandRange = function ( scope, type ) {
|
|||
break;
|
||||
case 'siblings':
|
||||
// Grow range to cover all siblings
|
||||
nodes = this.document.selectNodes( this.range, 'siblings' );
|
||||
nodes = this.document.selectNodes( this.getRange(), 'siblings' );
|
||||
if ( nodes.length === 1 ) {
|
||||
range = nodes[0].node.getOuterRange();
|
||||
} else {
|
||||
|
@ -266,7 +266,7 @@ ve.dm.SurfaceFragment.prototype.expandRange = function ( scope, type ) {
|
|||
break;
|
||||
case 'closest':
|
||||
// Grow range to cover closest common ancestor node of given type
|
||||
node = this.document.selectNodes( this.range, 'siblings' )[0].node;
|
||||
node = this.document.selectNodes( this.getRange(), 'siblings' )[0].node;
|
||||
parent = node.getParent();
|
||||
while ( parent && parent.getType() !== type ) {
|
||||
node = parent;
|
||||
|
@ -279,7 +279,7 @@ ve.dm.SurfaceFragment.prototype.expandRange = function ( scope, type ) {
|
|||
break;
|
||||
case 'parent':
|
||||
// Grow range to cover the closest common parent node
|
||||
node = this.document.selectNodes( this.range, 'siblings' )[0].node;
|
||||
node = this.document.selectNodes( this.getRange(), 'siblings' )[0].node;
|
||||
parent = node.getParent();
|
||||
if ( !parent ) {
|
||||
return new ve.dm.SurfaceFragment( null );
|
||||
|
@ -314,7 +314,7 @@ ve.dm.SurfaceFragment.prototype.getData = function ( deep ) {
|
|||
if ( !this.surface ) {
|
||||
return [];
|
||||
}
|
||||
return this.document.getData( this.range, deep );
|
||||
return this.document.getData( this.getRange(), deep );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -330,7 +330,7 @@ ve.dm.SurfaceFragment.prototype.getText = function () {
|
|||
}
|
||||
var i, length,
|
||||
text = '',
|
||||
data = this.document.getData( this.range );
|
||||
data = this.document.getData( this.getRange() );
|
||||
for ( i = 0, length = data.length; i < length; i++ ) {
|
||||
if ( data[i].type === undefined ) {
|
||||
// Annotated characters have a string at index 0, plain characters are 1-char strings
|
||||
|
@ -355,8 +355,8 @@ ve.dm.SurfaceFragment.prototype.getAnnotations = function ( all ) {
|
|||
if ( !this.surface ) {
|
||||
return new ve.dm.AnnotationSet( this.getDocument().getStore() );
|
||||
}
|
||||
if ( this.range.getLength() ) {
|
||||
return this.getDocument().data.getAnnotationsFromRange( this.range, all );
|
||||
if ( this.getRange( true ).getLength() ) {
|
||||
return this.getDocument().data.getAnnotationsFromRange( this.getRange(), all );
|
||||
} else {
|
||||
return this.surface.getInsertionAnnotations();
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ ve.dm.SurfaceFragment.prototype.getLeafNodes = function () {
|
|||
if ( !this.surface ) {
|
||||
return [];
|
||||
}
|
||||
return this.document.selectNodes( this.range, 'leaves' );
|
||||
return this.document.selectNodes( this.getRange(), 'leaves' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -395,7 +395,7 @@ ve.dm.SurfaceFragment.prototype.getCoveredNodes = function () {
|
|||
if ( !this.surface ) {
|
||||
return [];
|
||||
}
|
||||
return this.document.selectNodes( this.range, 'coveredNodes' );
|
||||
return this.document.selectNodes( this.getRange(), 'coveredNodes' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -413,7 +413,7 @@ ve.dm.SurfaceFragment.prototype.getSiblingNodes = function () {
|
|||
if ( !this.surface ) {
|
||||
return [];
|
||||
}
|
||||
return this.document.selectNodes( this.range, 'siblings' );
|
||||
return this.document.selectNodes( this.getRange(), 'siblings' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -439,7 +439,7 @@ ve.dm.SurfaceFragment.prototype.select = function () {
|
|||
if ( !this.surface ) {
|
||||
return this;
|
||||
}
|
||||
this.surface.change( null, this.range );
|
||||
this.surface.change( null, this.getRange() );
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -469,10 +469,10 @@ ve.dm.SurfaceFragment.prototype.annotateContent = function ( method, nameOrAnnot
|
|||
} else {
|
||||
annotation = ve.dm.annotationFactory.create( nameOrAnnotation, data );
|
||||
}
|
||||
if ( this.range.getLength() ) {
|
||||
if ( this.getRange( true ).getLength() ) {
|
||||
// Apply to selection
|
||||
tx = ve.dm.Transaction.newFromAnnotation( this.document, this.range, method, annotation );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.range ) );
|
||||
tx = ve.dm.Transaction.newFromAnnotation( this.document, this.getRange(), method, annotation );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.getRange() ) );
|
||||
} else {
|
||||
// Apply annotation to stack
|
||||
if ( method === 'set' ) {
|
||||
|
@ -500,7 +500,7 @@ ve.dm.SurfaceFragment.prototype.insertContent = function ( content, annotate ) {
|
|||
return this;
|
||||
}
|
||||
var tx, annotations;
|
||||
if ( this.range.getLength() ) {
|
||||
if ( this.getRange( true ).getLength() ) {
|
||||
this.removeContent();
|
||||
}
|
||||
// Auto-convert content to array of plain text characters
|
||||
|
@ -509,13 +509,13 @@ ve.dm.SurfaceFragment.prototype.insertContent = function ( content, annotate ) {
|
|||
}
|
||||
if ( content.length ) {
|
||||
if ( annotate ) {
|
||||
annotations = this.document.data.getAnnotationsFromOffset( this.range.start - 1 );
|
||||
annotations = this.document.data.getAnnotationsFromOffset( this.getRange( true ).start - 1 );
|
||||
if ( annotations.getLength() > 0 ) {
|
||||
ve.dm.Document.addAnnotationsToData( content, annotations );
|
||||
}
|
||||
}
|
||||
tx = ve.dm.Transaction.newFromInsertion( this.document, this.range.start, content );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.range ) );
|
||||
tx = ve.dm.Transaction.newFromInsertion( this.document, this.getRange( true ).start, content );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.getRange() ) );
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
@ -532,19 +532,18 @@ ve.dm.SurfaceFragment.prototype.removeContent = function () {
|
|||
return this;
|
||||
}
|
||||
var tx;
|
||||
if ( this.range.getLength() ) {
|
||||
tx = ve.dm.Transaction.newFromRemoval( this.document, this.range );
|
||||
// this.range will be translated via the onTransact event handler
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.range ) );
|
||||
if ( this.getRange( true ).getLength() ) {
|
||||
tx = ve.dm.Transaction.newFromRemoval( this.document, this.getRange() );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.getRange() ) );
|
||||
// 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() ) {
|
||||
if ( this.getRange( true ).getLength() ) {
|
||||
// Collapse the range manually
|
||||
this.range = new ve.Range( this.range.start );
|
||||
this.range = new ve.Range( this.getRange( true ).start );
|
||||
if ( !this.noAutoSelect ) {
|
||||
// Update the surface selection
|
||||
this.surface.change( null, this.range );
|
||||
this.surface.change( null, this.getRange() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -565,8 +564,8 @@ ve.dm.SurfaceFragment.prototype.convertNodes = function ( type, attr ) {
|
|||
return this;
|
||||
}
|
||||
var tx =
|
||||
ve.dm.Transaction.newFromContentBranchConversion( this.document, this.range, type, attr );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.range ) );
|
||||
ve.dm.Transaction.newFromContentBranchConversion( this.document, this.getRange(), type, attr );
|
||||
this.surface.change( tx, !this.noAutoSelect && tx.translateRange( this.getRange() ) );
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -597,10 +596,10 @@ ve.dm.SurfaceFragment.prototype.wrapNodes = function ( wrapper ) {
|
|||
if ( !ve.isArray( wrapper ) ) {
|
||||
wrapper = [wrapper];
|
||||
}
|
||||
var tx = ve.dm.Transaction.newFromWrap( this.document, this.range, [], [], [], wrapper ),
|
||||
newRange = new ve.Range( this.range.start, this.range.end + tx.getLengthDifference() );
|
||||
var tx = ve.dm.Transaction.newFromWrap( this.document, this.getRange(), [], [], [], wrapper ),
|
||||
newRange = new ve.Range( this.getRange( true ).start, this.getRange( true ).end + tx.getLengthDifference() );
|
||||
this.surface.change( tx, !this.noAutoSelect && newRange );
|
||||
this.range = newRange;
|
||||
this.setRange( newRange );
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -624,22 +623,22 @@ ve.dm.SurfaceFragment.prototype.unwrapNodes = function ( outerDepth, innerDepth
|
|||
}
|
||||
var i, tx, newRange, innerUnwrapper = [], outerUnwrapper = [];
|
||||
|
||||
if ( this.range.end - this.range.start < innerDepth * 2 ) {
|
||||
if ( this.getRange( true ).end - this.getRange( true ).start < innerDepth * 2 ) {
|
||||
throw new Error( 'cannot unwrap by greater depth than maximum theoretical depth of selection' );
|
||||
}
|
||||
|
||||
for ( i = 0; i < innerDepth; i++ ) {
|
||||
innerUnwrapper.push( this.surface.getDocument().data.getData( this.range.start + i ) );
|
||||
innerUnwrapper.push( this.surface.getDocument().data.getData( this.getRange( true ).start + i ) );
|
||||
}
|
||||
for ( i = outerDepth; i > 0; i-- ) {
|
||||
outerUnwrapper.push( this.surface.getDocument().data.getData( this.range.start - i ) );
|
||||
outerUnwrapper.push( this.surface.getDocument().data.getData( this.getRange( true ).start - i ) );
|
||||
}
|
||||
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, this.range, outerUnwrapper, [], innerUnwrapper, [] );
|
||||
newRange = new ve.Range( this.range.start - outerDepth, this.range.end + outerDepth + tx.getLengthDifference() );
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, this.getRange(), outerUnwrapper, [], innerUnwrapper, [] );
|
||||
newRange = new ve.Range( this.getRange( true ).start - outerDepth, this.getRange( true ).end + outerDepth + tx.getLengthDifference() );
|
||||
this.surface.change( tx, !this.noAutoSelect && newRange );
|
||||
|
||||
this.range = newRange;
|
||||
this.setRange( newRange );
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -676,19 +675,19 @@ ve.dm.SurfaceFragment.prototype.rewrapNodes = function ( depth, wrapper ) {
|
|||
wrapper = [wrapper];
|
||||
}
|
||||
|
||||
if ( this.range.end - this.range.start < depth * 2 ) {
|
||||
if ( this.getRange( true ).end - this.getRange( true ).start < depth * 2 ) {
|
||||
throw new Error( 'cannot unwrap by greater depth than maximum theoretical depth of selection' );
|
||||
}
|
||||
|
||||
for ( i = 0; i < depth; i++ ) {
|
||||
unwrapper.push( this.surface.getDocument().data.getData( this.range.start + i ) );
|
||||
unwrapper.push( this.surface.getDocument().data.getData( this.getRange( true ).start + i ) );
|
||||
}
|
||||
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, this.range, [], [], unwrapper, wrapper );
|
||||
newRange = new ve.Range( this.range.start, this.range.end + tx.getLengthDifference() );
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, this.getRange(), [], [], unwrapper, wrapper );
|
||||
newRange = new ve.Range( this.getRange( true ).start, this.getRange( true ).end + tx.getLengthDifference() );
|
||||
this.surface.change( tx, !this.noAutoSelect && newRange );
|
||||
|
||||
this.range = newRange;
|
||||
this.setRange( newRange );
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -724,11 +723,11 @@ ve.dm.SurfaceFragment.prototype.wrapAllNodes = function ( wrapper ) {
|
|||
wrapper = [wrapper];
|
||||
}
|
||||
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, this.range, [], wrapper, [], [] );
|
||||
newRange = new ve.Range( this.range.start, this.range.end + tx.getLengthDifference() );
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, this.getRange(), [], wrapper, [], [] );
|
||||
newRange = new ve.Range( this.getRange( true ).start, this.getRange( true ).end + tx.getLengthDifference() );
|
||||
this.surface.change( tx, !this.noAutoSelect && newRange );
|
||||
|
||||
this.range = newRange;
|
||||
this.setRange( newRange );
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -757,25 +756,25 @@ ve.dm.SurfaceFragment.prototype.rewrapAllNodes = function ( depth, wrapper ) {
|
|||
return this;
|
||||
}
|
||||
var i, tx, newRange, unwrapper = [],
|
||||
innerRange = new ve.Range( this.range.start + depth, this.range.end - depth );
|
||||
innerRange = new ve.Range( this.getRange( true ).start + depth, this.getRange( true ).end - depth );
|
||||
|
||||
if ( !ve.isArray( wrapper ) ) {
|
||||
wrapper = [wrapper];
|
||||
}
|
||||
|
||||
if ( this.range.end - this.range.start < depth * 2 ) {
|
||||
if ( this.getRange( true ).end - this.getRange( true ).start < depth * 2 ) {
|
||||
throw new Error( 'cannot unwrap by greater depth than maximum theoretical depth of selection' );
|
||||
}
|
||||
|
||||
for ( i = 0; i < depth; i++ ) {
|
||||
unwrapper.push( this.surface.getDocument().data.getData( this.range.start + i ) );
|
||||
unwrapper.push( this.surface.getDocument().data.getData( this.getRange( true ).start + i ) );
|
||||
}
|
||||
|
||||
tx = ve.dm.Transaction.newFromWrap( this.document, innerRange, unwrapper, wrapper, [], [] );
|
||||
newRange = new ve.Range( this.range.start, this.range.end + tx.getLengthDifference() );
|
||||
newRange = new ve.Range( this.getRange( true ).start, this.getRange( true ).end + tx.getLengthDifference() );
|
||||
this.surface.change( tx, !this.noAutoSelect && newRange );
|
||||
|
||||
this.range = newRange;
|
||||
this.setRange( newRange );
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -807,7 +806,7 @@ ve.dm.SurfaceFragment.prototype.isolateAndUnwrap = function ( isolateForType ) {
|
|||
startSplitNodes = [],
|
||||
endSplitNodes = [],
|
||||
fragment = this,
|
||||
newRange = new ve.Range( this.range.start, this.range.end );
|
||||
newRange = new ve.Range( this.getRange( true ).start, this.getRange( true ).end );
|
||||
|
||||
function createSplits( splitNodes, insertBefore ) {
|
||||
var i, length,
|
||||
|
@ -830,7 +829,7 @@ ve.dm.SurfaceFragment.prototype.isolateAndUnwrap = function ( isolateForType ) {
|
|||
endOffset += endOffsetChange;
|
||||
}
|
||||
|
||||
nodes = this.getDocument().selectNodes( this.range, 'siblings' );
|
||||
nodes = this.getDocument().selectNodes( this.getRange(), 'siblings' );
|
||||
|
||||
// Find start split point, if required
|
||||
startSplitNode = nodes[0].node;
|
||||
|
|
|
@ -9,67 +9,117 @@ QUnit.module( 've.FormatAction' );
|
|||
|
||||
/* Tests */
|
||||
|
||||
function runConverterTest( assert, range, type, attributes, expectedSelection, expectedData, label ) {
|
||||
var dom = ve.createDocumentFromHTML( ve.dm.example.isolationHTML ),
|
||||
function runFormatConverterTest( assert, range, type, attributes, expectedSelection, expectedData, msg ) {
|
||||
var selection,
|
||||
dom = ve.createDocumentFromHTML( ve.dm.example.isolationHTML ),
|
||||
surface = new ve.Surface( new ve.init.Target( $( '<div>' ) ), dom ),
|
||||
formatAction = new ve.FormatAction( surface ),
|
||||
data = ve.copyArray( surface.getModel().getDocument().getFullData() );
|
||||
data = ve.copyArray( surface.getModel().getDocument().getFullData() ),
|
||||
originalData = ve.copyArray( data );
|
||||
|
||||
expectedData( data );
|
||||
|
||||
surface.getModel().change( null, range );
|
||||
formatAction.convert( type, attributes );
|
||||
|
||||
expectedData( data );
|
||||
assert.deepEqual( surface.getModel().getDocument().getFullData(), data, msg + ': data models match' );
|
||||
assert.deepEqual( surface.getModel().getSelection(), expectedSelection, msg + ': selections match' );
|
||||
|
||||
assert.deepEqual( surface.getModel().getDocument().getFullData(), data, label + ': data models match' );
|
||||
assert.deepEqual( surface.getModel().getSelection(), expectedSelection, label + ': selections match' );
|
||||
selection = surface.getModel().undo();
|
||||
|
||||
assert.deepEqual( surface.getModel().getDocument().getFullData(), originalData, msg + ' (undo): data models match' );
|
||||
assert.deepEqual( selection, range, msg + ' (undo): selections match' );
|
||||
|
||||
surface.destroy();
|
||||
}
|
||||
|
||||
QUnit.test( 'convert', 12, function ( assert ) {
|
||||
var rebuilt = { 'changed': { 'rebuilt': 1 } },
|
||||
QUnit.test( 'convert', function ( assert ) {
|
||||
var i,
|
||||
rebuilt = { 'changed': { 'rebuilt': 1 } },
|
||||
created = { 'changed': { 'created': 1 } },
|
||||
createdAndRebuilt = { 'changed': { 'created': 1, 'rebuilt': 1 } };
|
||||
|
||||
runConverterTest( assert, new ve.Range( 14, 16 ), 'MWheading', { level: 2 }, new ve.Range( 14, 16 ), function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 2, { 'type': '/list' }, { 'type': 'MWheading', 'attributes': { 'level': 2 }, 'internal': created } );
|
||||
data.splice( 19, 2, { 'type': '/MWheading' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt } );
|
||||
}, 'converting partial selection of list item "Item 2" to level 2 MWheading' );
|
||||
|
||||
runConverterTest( assert, new ve.Range( 15, 50 ), 'MWheading', { level: 3 }, new ve.Range( 15, 44 ), function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 2, { 'type': '/list' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 19, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 27, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 38, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 46, 2, { 'type': '/MWheading' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': created } );
|
||||
}, 'converting partial selection across two lists surrounding a paragraph' );
|
||||
|
||||
runConverterTest( assert, new ve.Range( 4, 28 ), 'MWheading', { level: 1 }, new ve.Range( 2, 22 ), function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 0, 3, { 'type': 'MWheading', 'attributes': { 'level': 1 }, 'internal': created } );
|
||||
data.splice( 7, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 1 }, 'internal': created } );
|
||||
data.splice( 15, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 1 }, 'internal': created } );
|
||||
data.splice( 23, 3, { 'type': '/MWheading' } );
|
||||
}, 'converting partial selection of all list items to level 1 MWheadings' );
|
||||
|
||||
runConverterTest( assert, new ve.Range( 5, 26 ), 'MWpreformatted', undefined, new ve.Range( 3, 20 ), function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 0, 3, { 'type': 'MWpreformatted', 'internal': created } );
|
||||
data.splice( 7, 4, { 'type': '/MWpreformatted' }, { 'type': 'MWpreformatted', 'internal': created } );
|
||||
data.splice( 15, 4, { 'type': '/MWpreformatted' }, { 'type': 'MWpreformatted', 'internal': created } );
|
||||
data.splice( 23, 3, { 'type': '/MWpreformatted' } );
|
||||
}, 'converting partial selection of some list items to MWpreformatted text' );
|
||||
|
||||
runConverterTest( assert, new ve.Range( 146, 159 ), 'paragraph', undefined, new ve.Range( 146, 159 ), function( data ) {
|
||||
data.splice( 145, 1, { 'type': 'paragraph', 'internal': created } );
|
||||
data.splice( 159, 1, { 'type': '/paragraph' } );
|
||||
}, 'converting heading in list item to paragraph' );
|
||||
|
||||
runConverterTest( assert, new ve.Range( 165, 180 ), 'paragraph', undefined, new ve.Range( 165, 180 ), function( data ) {
|
||||
data.splice( 162, 1, { 'type': 'paragraph', 'internal': created } );
|
||||
data.splice( 183, 1, { 'type': '/paragraph' } );
|
||||
}, 'converting MWpreformatted in list item to paragraph' );
|
||||
createdAndRebuilt = { 'changed': { 'created': 1, 'rebuilt': 1 } },
|
||||
cases = [
|
||||
{
|
||||
'range': new ve.Range( 14, 16 ),
|
||||
'type': 'MWheading',
|
||||
'attributes': { level: 2 },
|
||||
'expectedSelection': new ve.Range( 14, 16 ),
|
||||
'expectedData': function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 2, { 'type': '/list' }, { 'type': 'MWheading', 'attributes': { 'level': 2 }, 'internal': created } );
|
||||
data.splice( 19, 2, { 'type': '/MWheading' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt } );
|
||||
},
|
||||
'msg': 'converting partial selection of list item "Item 2" to level 2 MWheading'
|
||||
},
|
||||
{
|
||||
'range': new ve.Range( 15, 50 ),
|
||||
'type': 'MWheading',
|
||||
'attributes': { level: 3 },
|
||||
'expectedSelection': new ve.Range( 15, 44 ),
|
||||
'expectedData': function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 2, { 'type': '/list' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 19, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 27, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 38, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 3 }, 'internal': created } );
|
||||
data.splice( 46, 2, { 'type': '/MWheading' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': created } );
|
||||
},
|
||||
'msg': 'converting partial selection across two lists surrounding a paragraph'
|
||||
},
|
||||
{
|
||||
'range': new ve.Range( 4, 28 ),
|
||||
'type': 'MWheading',
|
||||
'attributes': { level: 1 },
|
||||
'expectedSelection': new ve.Range( 2, 22 ),
|
||||
'expectedData': function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 0, 3, { 'type': 'MWheading', 'attributes': { 'level': 1 }, 'internal': created } );
|
||||
data.splice( 7, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 1 }, 'internal': created } );
|
||||
data.splice( 15, 4, { 'type': '/MWheading' }, { 'type': 'MWheading', 'attributes': { 'level': 1 }, 'internal': created } );
|
||||
data.splice( 23, 3, { 'type': '/MWheading' } );
|
||||
},
|
||||
'msg': 'converting partial selection of all list items to level 1 MWheadings'
|
||||
},
|
||||
{
|
||||
'range': new ve.Range( 5, 26 ),
|
||||
'type': 'MWpreformatted',
|
||||
'attributes': undefined,
|
||||
'expectedSelection': new ve.Range( 3, 20 ),
|
||||
'expectedData': function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 0, 3, { 'type': 'MWpreformatted', 'internal': created } );
|
||||
data.splice( 7, 4, { 'type': '/MWpreformatted' }, { 'type': 'MWpreformatted', 'internal': created } );
|
||||
data.splice( 15, 4, { 'type': '/MWpreformatted' }, { 'type': 'MWpreformatted', 'internal': created } );
|
||||
data.splice( 23, 3, { 'type': '/MWpreformatted' } );
|
||||
},
|
||||
'msg': 'converting partial selection of some list items to MWpreformatted text'
|
||||
},
|
||||
{
|
||||
'range': new ve.Range( 146, 159 ),
|
||||
'type': 'paragraph',
|
||||
'attributes': undefined,
|
||||
'expectedSelection': new ve.Range( 146, 159 ),
|
||||
'expectedData': function( data ) {
|
||||
data.splice( 145, 1, { 'type': 'paragraph', 'internal': created } );
|
||||
data.splice( 159, 1, { 'type': '/paragraph' } );
|
||||
},
|
||||
'msg': 'converting heading in list item to paragraph'
|
||||
},
|
||||
{
|
||||
'range': new ve.Range( 165, 180 ),
|
||||
'type': 'paragraph',
|
||||
'attributes': undefined,
|
||||
'expectedSelection': new ve.Range( 165, 180 ),
|
||||
'expectedData': function( data ) {
|
||||
data.splice( 162, 1, { 'type': 'paragraph', 'internal': created } );
|
||||
data.splice( 183, 1, { 'type': '/paragraph' } );
|
||||
},
|
||||
'msg': 'converting MWpreformatted in list item to paragraph'
|
||||
}
|
||||
];
|
||||
|
||||
QUnit.expect( cases.length * 4 );
|
||||
for ( i = 0; i < cases.length; i++ ) {
|
||||
runFormatConverterTest( assert, cases[i].range, cases[i].type, cases[i].attributes, cases[i].expectedSelection, cases[i].expectedData, cases[i].msg );
|
||||
}
|
||||
} );
|
||||
|
|
|
@ -9,30 +9,57 @@ QUnit.module( 've.IndentationAction' );
|
|||
|
||||
/* Tests */
|
||||
|
||||
function runIndentationTest( assert, range, method, expectedSelection, expectedData, label ) {
|
||||
var dom = ve.createDocumentFromHTML( ve.dm.example.isolationHTML ),
|
||||
function runIndentationChangeTest( assert, range, method, expectedSelection, expectedData, expectedOriginalData, msg ) {
|
||||
var selection,
|
||||
dom = ve.createDocumentFromHTML( ve.dm.example.isolationHTML ),
|
||||
surface = new ve.Surface( new ve.init.Target( $( '<div>' ) ), dom ),
|
||||
indentationAction = new ve.IndentationAction( surface ),
|
||||
data = ve.copyArray( surface.getModel().getDocument().getFullData() );
|
||||
data = ve.copyArray( surface.getModel().getDocument().getFullData() ),
|
||||
originalData = ve.copyArray( data );
|
||||
|
||||
expectedData( data );
|
||||
if ( expectedOriginalData ) {
|
||||
expectedOriginalData( originalData );
|
||||
}
|
||||
|
||||
surface.getModel().change( null, range );
|
||||
indentationAction[method]();
|
||||
|
||||
expectedData( data );
|
||||
assert.deepEqual( surface.getModel().getDocument().getFullData(), data, msg + ': data models match' );
|
||||
assert.deepEqual( surface.getModel().getSelection(), expectedSelection, msg + ': selections match' );
|
||||
|
||||
assert.deepEqual( surface.getModel().getDocument().getFullData(), data, label + ': data models match' );
|
||||
assert.deepEqual( surface.getModel().getSelection(), expectedSelection, label + ': selections match' );
|
||||
selection = surface.getModel().undo();
|
||||
|
||||
assert.deepEqual( surface.getModel().getDocument().getFullData(), originalData, msg + ' (undo): data models match' );
|
||||
assert.deepEqual( selection, range, msg + ' (undo): selections match' );
|
||||
|
||||
surface.destroy();
|
||||
}
|
||||
|
||||
QUnit.test( 'decrease', 2, function ( assert ) {
|
||||
var rebuilt = { 'changed': { 'rebuilt': 1 } },
|
||||
createdAndRebuilt = { 'changed': { 'created': 2, 'rebuilt': 1 } };
|
||||
var i,
|
||||
rebuilt = { 'changed': { 'rebuilt': 1 } },
|
||||
createdAndRebuilt = { 'changed': { 'created': 2, 'rebuilt': 1 } },
|
||||
cases = [
|
||||
{
|
||||
'range': new ve.Range( 14, 16 ),
|
||||
'method': 'decrease',
|
||||
'expectedSelection': new ve.Range( 14, 16 ),
|
||||
'expectedData': function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 2, { 'type': '/list' }, { 'type': 'paragraph' } );
|
||||
data.splice( 19, 2, { 'type': '/paragraph' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt } );
|
||||
},
|
||||
'expectedOriginalData': function( data ) {
|
||||
// generated: 'wrapper' is removed by the action and not restored by undo
|
||||
delete data[12].internal;
|
||||
},
|
||||
'msg': 'decrease indentation on partial selection of list item "Item 2"'
|
||||
}
|
||||
];
|
||||
|
||||
runIndentationTest( assert, new ve.Range( 14, 16 ), 'decrease', new ve.Range( 14, 16 ), function( data ) {
|
||||
data[0].internal = rebuilt;
|
||||
data.splice( 11, 2, { 'type': '/list' }, { 'type': 'paragraph' } );
|
||||
data.splice( 19, 2, { 'type': '/paragraph' }, { 'type': 'list', 'attributes': { 'style': 'bullet' }, 'internal': createdAndRebuilt } );
|
||||
}, 'decrease indentation on partial selection of list item "Item 2"' );
|
||||
QUnit.expect( cases.length * 4 );
|
||||
for ( i = 0; i < cases.length; i++ ) {
|
||||
runIndentationChangeTest( assert, cases[i].range, cases[i].method, cases[i].expectedSelection, cases[i].expectedData, cases[i].expectedOriginalData, cases[i].msg );
|
||||
}
|
||||
} );
|
||||
|
|
|
@ -24,23 +24,26 @@ QUnit.test( 'constructor', 8, function ( assert ) {
|
|||
assert.equal( fragment.getRange().from, 0, 'range is clamped between 0 and document length' );
|
||||
assert.equal( fragment.getRange().to, 61, 'range is clamped between 0 and document length' );
|
||||
assert.strictEqual( fragment.willAutoSelect(), false, 'noAutoSelect values are boolean' );
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'onTransact', 1, function ( assert ) {
|
||||
QUnit.test( 'update', 2, function ( assert ) {
|
||||
var doc = ve.dm.example.createExampleDocument(),
|
||||
surface = new ve.dm.Surface( doc ),
|
||||
fragment1 = new ve.dm.SurfaceFragment( surface, new ve.Range( 1, 56 ) ),
|
||||
fragment2 = new ve.dm.SurfaceFragment( surface, new ve.Range( 2, 4 ) );
|
||||
fragment2 = new ve.dm.SurfaceFragment( surface, new ve.Range( 2, 4 ) ),
|
||||
fragment3 = new ve.dm.SurfaceFragment( surface, new ve.Range( 2, 4 ) );
|
||||
fragment1.removeContent();
|
||||
assert.deepEqual(
|
||||
fragment2.getRange(),
|
||||
new ve.Range( 1, 1 ),
|
||||
'fragment ranges are auto-translated when transactions are processed'
|
||||
'fragment range collapses after removeContent'
|
||||
);
|
||||
surface.undo();
|
||||
assert.deepEqual(
|
||||
fragment3.getRange(),
|
||||
new ve.Range( 4, 4 ),
|
||||
'fragment range moved after undo'
|
||||
);
|
||||
|
||||
fragment1.destroy();
|
||||
fragment2.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'adjustRange', 3, function ( assert ) {
|
||||
|
@ -51,7 +54,6 @@ QUnit.test( 'adjustRange', 3, function ( assert ) {
|
|||
assert.ok( fragment !== adjustedFragment, 'adjustRange produces a new fragment' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 20, 21 ), 'old fragment is not changed' );
|
||||
assert.deepEqual( adjustedFragment.getRange(), new ve.Range( 1, 56 ), 'new range is used' );
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'collapseRange', 3, function ( assert ) {
|
||||
|
@ -62,8 +64,6 @@ QUnit.test( 'collapseRange', 3, function ( assert ) {
|
|||
assert.ok( fragment !== collapsedFragment, 'collapseRange produces a new fragment' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 20, 21 ), 'old fragment is not changed' );
|
||||
assert.deepEqual( collapsedFragment.getRange(), new ve.Range( 20, 20 ), 'new range is used' );
|
||||
collapsedFragment.destroy();
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'expandRange (closest)', 1, function ( assert ) {
|
||||
|
@ -76,8 +76,6 @@ QUnit.test( 'expandRange (closest)', 1, function ( assert ) {
|
|||
true,
|
||||
'closest with invalid type results in null fragment'
|
||||
);
|
||||
exapandedFragment.destroy();
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'expandRange (word)', 1, function ( assert ) {
|
||||
|
@ -111,8 +109,6 @@ QUnit.test( 'expandRange (word)', 1, function ( assert ) {
|
|||
word = cases[i].phrase.substring( range.start, range.end );
|
||||
assert.strictEqual( word, cases[i].expected, cases[i].msg + ': text' );
|
||||
assert.strictEqual( cases[i].range.isBackwards(), range.isBackwards(), cases[i].msg + ': range direction' );
|
||||
fragment.destroy();
|
||||
newFragment.destroy();
|
||||
}
|
||||
} );
|
||||
|
||||
|
@ -135,7 +131,6 @@ QUnit.test( 'removeContent', 2, function ( assert ) {
|
|||
new ve.Range( 1, 1 ),
|
||||
'removing content results in a zero-length fragment'
|
||||
);
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'insertContent', 3, function ( assert ) {
|
||||
|
@ -159,7 +154,6 @@ QUnit.test( 'insertContent', 3, function ( assert ) {
|
|||
['3', '2', '1'],
|
||||
'strings get converted into data when inserting content'
|
||||
);
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'wrapNodes/unwrapNodes', 10, function ( assert ) {
|
||||
|
@ -205,7 +199,6 @@ QUnit.test( 'wrapNodes/unwrapNodes', 10, function ( assert ) {
|
|||
fragment.unwrapNodes( 0, 2 );
|
||||
assert.deepEqual( doc.getData(), originalDoc.getData(), 'unwrapping 2 levels restores document to original state' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 55, 61 ), 'range after unwrapping is same as original range' );
|
||||
fragment.destroy();
|
||||
|
||||
// Make a 1 paragraph into 1 list with 1 item
|
||||
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 9, 12 ) );
|
||||
|
@ -234,13 +227,11 @@ QUnit.test( 'wrapNodes/unwrapNodes', 10, function ( assert ) {
|
|||
fragment.unwrapNodes( 0, 2 );
|
||||
assert.deepEqual( doc.getData(), originalDoc.getData(), 'unwrapping 2 levels restores document to original state' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 9, 12 ), 'range after unwrapping is same as original range' );
|
||||
fragment.destroy();
|
||||
|
||||
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 8, 34 ) );
|
||||
fragment.unwrapNodes( 3, 1 );
|
||||
assert.deepEqual( fragment.getData(), doc.getData( new ve.Range( 5, 29 ) ), 'unwrapping multiple outer nodes and an inner node' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 5, 29 ), 'new range contains inner elements' );
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'rewrapNodes', 4, function ( assert ) {
|
||||
|
@ -284,7 +275,6 @@ QUnit.test( 'rewrapNodes', 4, function ( assert ) {
|
|||
// The intermediate stage (plain text attached to the document) would be invalid
|
||||
// if performed as an unwrap and a wrap
|
||||
expectedData = ve.copyArray( doc.getData() );
|
||||
fragment.destroy();
|
||||
|
||||
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 59, 65 ) );
|
||||
fragment.rewrapNodes( 1, [ { 'type': 'heading', 'attributes': { 'level': 1 } } ] );
|
||||
|
@ -296,7 +286,6 @@ QUnit.test( 'rewrapNodes', 4, function ( assert ) {
|
|||
|
||||
assert.deepEqual( doc.getData(), expectedData, 'rewrapping paragraphs as headings' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 59, 65 ), 'new range contains rewrapping elements' );
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'wrapAllNodes', 10, function ( assert ) {
|
||||
|
@ -335,7 +324,6 @@ QUnit.test( 'wrapAllNodes', 10, function ( assert ) {
|
|||
fragment.unwrapNodes( 0, 2 );
|
||||
assert.deepEqual( doc.getData(), originalDoc.getData(), 'unwrapping 2 levels restores document to original state' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 55, 61 ), 'range after unwrapping is same as original range' );
|
||||
fragment.destroy();
|
||||
|
||||
// Make a 1 paragraph into 1 list with 1 item
|
||||
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 9, 12 ) );
|
||||
|
@ -364,7 +352,6 @@ QUnit.test( 'wrapAllNodes', 10, function ( assert ) {
|
|||
fragment.unwrapNodes( 0, 2 );
|
||||
assert.deepEqual( doc.getData(), originalDoc.getData(), 'unwrapping 2 levels restores document to original state' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 9, 12 ), 'range after unwrapping is same as original range' );
|
||||
fragment.destroy();
|
||||
|
||||
fragment = new ve.dm.SurfaceFragment( surface, new ve.Range( 5, 37 ) );
|
||||
|
||||
|
@ -380,7 +367,6 @@ QUnit.test( 'wrapAllNodes', 10, function ( assert ) {
|
|||
expectedData,
|
||||
'unwrapping 4 levels (table, tableSection, tableRow and tableCell)'
|
||||
);
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
QUnit.test( 'rewrapAllNodes', 6, function ( assert ) {
|
||||
|
@ -410,7 +396,6 @@ QUnit.test( 'rewrapAllNodes', 6, function ( assert ) {
|
|||
'rewrapping multiple nodes via a valid intermediate state produces the same document as unwrapping then wrapping'
|
||||
);
|
||||
assert.deepEqual( fragment.getRange(), expectedFragment.getRange(), 'new range contains rewrapping elements' );
|
||||
expectedFragment.destroy();
|
||||
|
||||
// Reverse of first test
|
||||
fragment.rewrapAllNodes(
|
||||
|
@ -434,7 +419,6 @@ QUnit.test( 'rewrapAllNodes', 6, function ( assert ) {
|
|||
'rewrapping multiple nodes via a valid intermediate state produces the same document as unwrapping then wrapping'
|
||||
);
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 5, 37 ), 'new range contains rewrapping elements' );
|
||||
fragment.destroy();
|
||||
|
||||
// Rewrap a heading as a paragraph
|
||||
// The intermediate stage (plain text attached to the document) would be invalid
|
||||
|
@ -447,7 +431,6 @@ QUnit.test( 'rewrapAllNodes', 6, function ( assert ) {
|
|||
|
||||
assert.deepEqual( doc.getData(), expectedData, 'rewrapping a heading as a paragraph' );
|
||||
assert.deepEqual( fragment.getRange(), new ve.Range( 0, 5 ), 'new range contains rewrapping elements' );
|
||||
fragment.destroy();
|
||||
} );
|
||||
|
||||
function runIsolateTest( assert, type, range, expected, label ) {
|
||||
|
@ -461,7 +444,6 @@ function runIsolateTest( assert, type, range, expected, label ) {
|
|||
expected( data );
|
||||
|
||||
assert.deepEqual( doc.getFullData(), data, label );
|
||||
fragment.destroy();
|
||||
}
|
||||
|
||||
QUnit.test( 'isolateAndUnwrap', 4, function ( assert ) {
|
||||
|
|
|
@ -2288,7 +2288,7 @@ ve.dm.example.isolationData = [
|
|||
{ 'type': '/tableSection' },
|
||||
{ 'type': '/table' },
|
||||
{ 'type': 'paragraph', 'internal': { 'generated': 'wrapper' } },
|
||||
'N', 'o', 't', ' ', 'a', 'l', 'l', 'o', 'w', 'e', 'd', ' ', 'b', 'y', ' ', 'd', 'm', ': ',
|
||||
'N', 'o', 't', ' ', 'a', 'l', 'l', 'o', 'w', 'e', 'd', ' ', 'b', 'y', ' ', 'd', 'm', ':',
|
||||
{ 'type': '/paragraph' },
|
||||
{ 'type': 'list', 'attributes': { 'style': 'bullet' } },
|
||||
{ 'type': 'listItem' },
|
||||
|
|
|
@ -81,18 +81,15 @@ ve.ui.LinkInspector.prototype.onSetup = function () {
|
|||
if ( fragment.getRange().isCollapsed() ) {
|
||||
// Expand to nearest word
|
||||
expandedFragment = fragment.expandRange( 'word' );
|
||||
fragment.destroy();
|
||||
fragment = expandedFragment;
|
||||
} else {
|
||||
// Trim whitespace
|
||||
trimmedFragment = fragment.trimRange();
|
||||
fragment.destroy();
|
||||
fragment = trimmedFragment;
|
||||
}
|
||||
if ( !fragment.getRange().isCollapsed() ) {
|
||||
// Create annotation from selection
|
||||
truncatedFragment = fragment.truncateRange( 255 );
|
||||
fragment.destroy();
|
||||
fragment = truncatedFragment;
|
||||
annotation = this.getAnnotationFromTarget( fragment.getText() );
|
||||
fragment.annotateContent( 'set', annotation );
|
||||
|
@ -101,12 +98,11 @@ ve.ui.LinkInspector.prototype.onSetup = function () {
|
|||
} else {
|
||||
// Expand range to cover annotation
|
||||
expandedFragment = fragment.expandRange( 'annotation', annotation );
|
||||
fragment.destroy();
|
||||
fragment = expandedFragment;
|
||||
}
|
||||
|
||||
// Update selection
|
||||
fragment.select().destroy();
|
||||
fragment.select();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -128,8 +124,6 @@ ve.ui.LinkInspector.prototype.onOpen = function () {
|
|||
this.targetInput.setAnnotation( annotation );
|
||||
this.targetInput.$input.focus().select();
|
||||
}, this ), 200 );
|
||||
|
||||
fragment.destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -174,7 +168,6 @@ ve.ui.LinkInspector.prototype.onClose = function ( action ) {
|
|||
// Insert default text and select it
|
||||
fragment.insertContent( target, false );
|
||||
adjustedFragment = fragment.adjustRange( -target.length, 0 );
|
||||
fragment.destroy();
|
||||
fragment = adjustedFragment;
|
||||
|
||||
// Move cursor to the end of the inserted content
|
||||
|
@ -204,8 +197,6 @@ ve.ui.LinkInspector.prototype.onClose = function ( action ) {
|
|||
);
|
||||
// Reset state
|
||||
this.isNewAnnotation = false;
|
||||
|
||||
fragment.destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -238,8 +238,6 @@ ve.ui.Context.prototype.update = function () {
|
|||
// Remember selection for next time
|
||||
this.selection = selection.clone();
|
||||
|
||||
fragment.destroy();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
|
|
@ -79,7 +79,6 @@ ve.ui.Toolbar.prototype.onContextChange = function () {
|
|||
}
|
||||
}
|
||||
this.emit( 'updateState', nodes, fragment.getAnnotations(), fragment.getAnnotations( true ) );
|
||||
fragment.destroy();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -170,7 +170,7 @@ ve.Surface.prototype.isEnabled = function () {
|
|||
* @method
|
||||
*/
|
||||
ve.Surface.prototype.resetSelection = function () {
|
||||
this.model.getFragment().select().destroy();
|
||||
this.model.getFragment().select();
|
||||
this.view.surfaceObserver.poll();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue