mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-24 22:35:41 +00:00
Fixing Pre-Annotations
AnnotationAction and SurfaceFragment now use insertAnnotations. ve.dm.Surface.test * Removed test for annotate method (not needed anymore) ve.dm.SurfaceFragment * Now using getInsertionAnnotations method * Added support for modifying insertion annotations when annotating a zero-length selection ve.dm.Surface * Moved in insertion annotations state from document model * Added insertion annotation interface (enable, disable, areEnabled, get, set, add, and remove) * Simplified handling of annotations on change * Removed annotate method (not used anymore) ve.dm.Document * Removed insertion annotations (moved it to surface model) ve.ce.Surface * Cleaned up handleInsertion and changed it to use the insertion annotations interface on the surface model ve.AnnotationAction * Moved insertion annotation handling out of here since it's now included in the surface fragment Change-Id: I047d656acf7fa1c63f726ca2b0801e1476f84f96
This commit is contained in:
parent
96fcbbd695
commit
9787166bab
|
@ -83,34 +83,38 @@ ve.ce.Surface.static.$phantomTemplate = $( '<div class="ve-ce-phantom" draggable
|
|||
/* Methods */
|
||||
|
||||
ve.ce.Surface.prototype.handleInsertion = function () {
|
||||
var slug, data, range, annotations,
|
||||
var slug, data, range, annotations, insertionAnnotations,
|
||||
selection = this.model.getSelection();
|
||||
|
||||
// Handles removing expanded selection before inserting new text
|
||||
if ( selection.isCollapsed() === false ) {
|
||||
annotations = this.model.documentModel.getAnnotationsFromRange( new ve.Range( selection.start, selection.start + 1 ) );
|
||||
if ( !selection.isCollapsed() ) {
|
||||
// Pull annotations from the first character in the selection
|
||||
annotations = this.model.documentModel.getAnnotationsFromRange(
|
||||
new ve.Range( selection.start, selection.start + 1 )
|
||||
);
|
||||
this.model.change(
|
||||
ve.dm.Transaction.newFromRemoval(
|
||||
this.documentView.model,
|
||||
selection
|
||||
),
|
||||
ve.dm.Transaction.newFromRemoval( this.documentView.model, selection ),
|
||||
new ve.Range( selection.start )
|
||||
);
|
||||
this.surfaceObserver.clear();
|
||||
selection = this.model.getSelection();
|
||||
this.model.documentModel.insertAnnotations = annotations;
|
||||
this.model.setInsertionAnnotations( annotations );
|
||||
}
|
||||
|
||||
insertionAnnotations = this.model.getInsertionAnnotations() || new ve.AnnotationSet();
|
||||
if ( selection.isCollapsed() ) {
|
||||
slug = this.documentView.getSlugAtOffset( selection.start );
|
||||
// is this a slug or are the annotations to the left different than the insertAnnotations?
|
||||
if ( slug || (
|
||||
selection.start > 0 &&
|
||||
!ve.compareObjects (
|
||||
this.model.getDocument().getAnnotationsFromOffset( selection.start - 1 ),
|
||||
this.model.documentModel.insertAnnotations
|
||||
) ) ) {
|
||||
this.model.insertingAnnotations = true;
|
||||
// Is this a slug or are the annotations to the left different than the insertion
|
||||
// annotations?
|
||||
if (
|
||||
slug || (
|
||||
selection.start > 0 &&
|
||||
!ve.compareObjects (
|
||||
this.model.getDocument().getAnnotationsFromOffset( selection.start - 1 ),
|
||||
insertionAnnotations
|
||||
)
|
||||
)
|
||||
) {
|
||||
this.model.enableInsertionAnnotations();
|
||||
// is this a slug and if so, is this a block slug?
|
||||
if ( slug && ve.dm.Document.isStructuralOffset(
|
||||
this.documentView.model.data, selection.start
|
||||
|
@ -118,13 +122,13 @@ ve.ce.Surface.prototype.handleInsertion = function () {
|
|||
range = new ve.Range( selection.start + 1, selection.start + 2 );
|
||||
data = [
|
||||
{ 'type' : 'paragraph' },
|
||||
['\u2659', this.model.documentModel.insertAnnotations],
|
||||
['\u2659', insertionAnnotations],
|
||||
{ 'type' : '/paragraph' }
|
||||
];
|
||||
} else {
|
||||
range = new ve.Range( selection.start, selection.start + 1 );
|
||||
data = [
|
||||
['\u2659', this.model.documentModel.insertAnnotations]
|
||||
['\u2659', insertionAnnotations]
|
||||
];
|
||||
}
|
||||
this.model.change(
|
||||
|
@ -184,8 +188,11 @@ ve.ce.Surface.prototype.onContentChange = function ( node, previous, next ) {
|
|||
previous.range.start - nodeOffset - 1,
|
||||
next.range.start - nodeOffset - 1
|
||||
).split( '' );
|
||||
// Apply insertAnnotations
|
||||
ve.dm.Document.addAnnotationsToData( data, this.model.getDocument().insertAnnotations );
|
||||
// Apply insertion annotations
|
||||
annotations = this.model.getInsertionAnnotations();
|
||||
if ( annotations instanceof ve.AnnotationSet ) {
|
||||
ve.dm.Document.addAnnotationsToData( data, this.model.getInsertionAnnotations() );
|
||||
}
|
||||
this.lock();
|
||||
this.model.change(
|
||||
ve.dm.Transaction.newFromInsertion(
|
||||
|
@ -223,12 +230,12 @@ ve.ce.Surface.prototype.onContentChange = function ( node, previous, next ) {
|
|||
data = next.text.substring( fromLeft, next.text.length - fromRight ).split( '' );
|
||||
|
||||
// Get annotations to the left of new content and apply
|
||||
annotations = this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 + fromLeft );
|
||||
if ( annotations.getLength() > 0 ) {
|
||||
annotations =
|
||||
this.model.getDocument().getAnnotationsFromOffset( nodeOffset + 1 + fromLeft );
|
||||
if ( annotations.getLength() ) {
|
||||
ve.dm.Document.addAnnotationsToData( data, annotations );
|
||||
}
|
||||
if ( data.length > 0)
|
||||
{
|
||||
if ( data.length > 0 ) {
|
||||
this.model.change(
|
||||
ve.dm.Transaction.newFromInsertion(
|
||||
this.documentView.model, nodeOffset + 1 + fromLeft, data
|
||||
|
@ -313,7 +320,7 @@ ve.ce.Surface.prototype.onCompositionStart = function () {
|
|||
|
||||
ve.ce.Surface.prototype.onCompositionEnd = function () {
|
||||
this.inIme = false;
|
||||
this.model.insertingAnnotations = false;
|
||||
this.model.disableInsertionAnnotations();
|
||||
this.surfaceObserver.start();
|
||||
};
|
||||
|
||||
|
@ -645,14 +652,11 @@ ve.ce.Surface.prototype.onKeyPress = function ( e ) {
|
|||
if ( ve.ce.Surface.isShortcutKey( e ) || e.which === 13 || e.which === 8 || e.which === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.handleInsertion();
|
||||
|
||||
var view = this;
|
||||
setTimeout( function () {
|
||||
view.model.insertingAnnotations = false;
|
||||
view.surfaceObserver.start();
|
||||
}, 0 );
|
||||
setTimeout( ve.bind( function () {
|
||||
this.surfaceObserver.start();
|
||||
this.model.disableInsertionAnnotations();
|
||||
}, this ), 0 );
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,7 +48,6 @@ ve.dm.Document = function VeDmDocument( data, parentDocument ) {
|
|||
currentStack = stack[1],
|
||||
parentStack = stack[0],
|
||||
currentNode = this.documentNode;
|
||||
this.insertAnnotations = new ve.AnnotationSet();
|
||||
this.documentNode.setDocument( doc );
|
||||
this.documentNode.setRoot( root );
|
||||
for ( i = 0; i < this.data.length; i++ ) {
|
||||
|
|
|
@ -24,6 +24,8 @@ ve.dm.Surface = function VeDmSurface( doc ) {
|
|||
this.bigStack = [];
|
||||
this.undoIndex = 0;
|
||||
this.historyTrackingInterval = null;
|
||||
this.insertionAnnotations = new ve.AnnotationSet();
|
||||
this.useInsertionAnnotations = true;
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
@ -76,6 +78,80 @@ ve.dm.Surface.prototype.getHistory = function () {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables insertion annotations.
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
ve.dm.Surface.prototype.enableInsertionAnnotations = function () {
|
||||
this.useInsertionAnnotations = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables insertion annotations.
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
ve.dm.Surface.prototype.disableInsertionAnnotations = function () {
|
||||
this.useInsertionAnnotations = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if insertion annotations are enabled.
|
||||
*
|
||||
* @method
|
||||
* @returns {Boolean} Insertion annotations are enabled
|
||||
*/
|
||||
ve.dm.Surface.prototype.areInsertionAnnotationsEnabled = function () {
|
||||
return this.useInsertionAnnotations;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets annotations that will be used upon insertion.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.AnnotationSet|null} Insertion anotations or null if not being used
|
||||
*/
|
||||
ve.dm.Surface.prototype.getInsertionAnnotations = function () {
|
||||
return this.insertionAnnotations.clone();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets annotations that will be used upon insertion.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.AnnotationSet|null} Insertion anotations to use or null to disable them
|
||||
* @emits 'annotationChange'
|
||||
*/
|
||||
ve.dm.Surface.prototype.setInsertionAnnotations = function ( annotations ) {
|
||||
this.insertionAnnotations = annotations.clone();
|
||||
this.emit( 'annotationChange' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds an annotation to the insertion annotations.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.AnnotationSet} Insertion anotation to add
|
||||
* @emits 'annotationChange'
|
||||
*/
|
||||
ve.dm.Surface.prototype.addInsertionAnnotation = function ( annotation ) {
|
||||
this.insertionAnnotations.push( annotation );
|
||||
this.emit( 'annotationChange' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an annotation from the insertion annotations.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.AnnotationSet} Insertion anotation to remove
|
||||
* @emits 'annotationChange'
|
||||
*/
|
||||
ve.dm.Surface.prototype.removeInsertionAnnotation = function ( annotation ) {
|
||||
this.insertionAnnotations.remove( annotation );
|
||||
this.emit( 'annotationChange' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if there is a state to redo.
|
||||
*
|
||||
|
@ -136,7 +212,7 @@ ve.dm.Surface.prototype.getFragment = function ( range, noAutoSelect ) {
|
|||
* @param {ve.Range|undefined} selection
|
||||
*/
|
||||
ve.dm.Surface.prototype.change = function ( transactions, selection ) {
|
||||
var i, leftOffset, contentOffset, annotations;
|
||||
var i, offset;
|
||||
if ( transactions ) {
|
||||
if ( transactions instanceof ve.dm.Transaction ) {
|
||||
transactions = [transactions];
|
||||
|
@ -160,56 +236,23 @@ ve.dm.Surface.prototype.change = function ( transactions, selection ) {
|
|||
this.emit( 'transact', transactions );
|
||||
}
|
||||
|
||||
// Clear and add annotations to stack if insertingAnnotations isn't happening
|
||||
if ( !this.insertingAnnotations ) {
|
||||
leftOffset = this.getSelection().start - 1;
|
||||
if ( leftOffset === -1 ) {
|
||||
leftOffset = 0;
|
||||
// Clear and add annotations to stack if insertion annotations aren't being used
|
||||
if ( this.useInsertionAnnotations ) {
|
||||
offset = this.documentModel.getNearestContentOffset(
|
||||
Math.max( 0, this.getSelection().start - 1 ), -1
|
||||
);
|
||||
if ( offset === -1 ) {
|
||||
// Document is empty, use empty set
|
||||
this.insertionAnnotations = new ve.AnnotationSet();
|
||||
} else {
|
||||
this.insertionAnnotations = this.documentModel.getAnnotationsFromOffset( offset );
|
||||
}
|
||||
contentOffset = this.documentModel.getNearestContentOffset( leftOffset, -1 );
|
||||
// contentOffset may be -1 if the document is empty
|
||||
annotations = contentOffset > - 1 ?
|
||||
this.documentModel.getAnnotationsFromOffset( contentOffset ) :
|
||||
new ve.AnnotationSet();
|
||||
|
||||
// Reset insertAnnotations
|
||||
this.documentModel.insertAnnotations = new ve.AnnotationSet();
|
||||
this.documentModel.insertAnnotations.addSet( annotations );
|
||||
|
||||
this.emit( 'annotationChange' );
|
||||
}
|
||||
|
||||
this.emit( 'change', transactions, selection );
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies an annotation to the current selection
|
||||
*
|
||||
* @method
|
||||
* @param {String} annotation action: toggle, clear, set
|
||||
* @param {Object} annotation object to apply.
|
||||
*/
|
||||
ve.dm.Surface.prototype.annotate = function ( method, annotation ) {
|
||||
var tx,
|
||||
selection = this.getSelection();
|
||||
if ( selection.getLength() ) {
|
||||
// Apply annotation immediately to selection
|
||||
selection = this.getDocument().trimOuterSpaceFromRange( selection );
|
||||
tx = ve.dm.Transaction.newFromAnnotation(
|
||||
this.getDocument(), selection, method, annotation
|
||||
);
|
||||
this.change( tx, selection );
|
||||
} else {
|
||||
// Apply annotation to stack
|
||||
if ( method === 'set' ) {
|
||||
this.documentModel.insertAnnotations.push( annotation );
|
||||
} else if ( method === 'clear' ) {
|
||||
this.documentModel.insertAnnotations.remove( annotation );
|
||||
}
|
||||
}
|
||||
this.emit( 'annotationChange' );
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets a history state breakpoint.
|
||||
*
|
||||
|
|
|
@ -310,7 +310,7 @@ ve.dm.SurfaceFragment.prototype.getAnnotations = function ( all ) {
|
|||
if ( this.range.getLength() ) {
|
||||
return this.document.getAnnotationsFromRange( this.range, all );
|
||||
} else {
|
||||
return this.document.getAnnotationsFromOffset( this.range.from );
|
||||
return this.surface.getInsertionAnnotations();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -427,8 +427,16 @@ ve.dm.SurfaceFragment.prototype.annotateContent = function ( method, name, data
|
|||
annotation.data = data;
|
||||
}
|
||||
if ( this.range.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 ) );
|
||||
} else {
|
||||
// Apply annotation to stack
|
||||
if ( method === 'set' ) {
|
||||
this.surface.addInsertionAnnotation( annotation );
|
||||
} else if ( method === 'clear' ) {
|
||||
this.surface.removeInsertionAnnotation( annotation );
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
|
|
@ -57,58 +57,3 @@ QUnit.test( 'change', 3, function ( assert ) {
|
|||
surface.change( tx, new ve.Range( 2, 2 ) );
|
||||
assert.deepEqual( events, { 'transact': 2, 'select': 2, 'change': 3 } );
|
||||
} );
|
||||
|
||||
QUnit.test( 'annotate', 1, function ( assert ) {
|
||||
var i,
|
||||
surface,
|
||||
cases = [
|
||||
{
|
||||
'msg': 'Set Bold',
|
||||
'data': [
|
||||
'b', 'o', 'l', 'd'
|
||||
],
|
||||
'expected':
|
||||
[
|
||||
[
|
||||
'b',
|
||||
[
|
||||
ve.dm.example.bold
|
||||
]
|
||||
],
|
||||
[
|
||||
'o',
|
||||
[
|
||||
ve.dm.example.bold
|
||||
]
|
||||
],
|
||||
[
|
||||
'l',
|
||||
[
|
||||
ve.dm.example.bold
|
||||
]
|
||||
],
|
||||
[
|
||||
'd',
|
||||
[
|
||||
ve.dm.example.bold
|
||||
]
|
||||
]
|
||||
],
|
||||
'annotate': {
|
||||
'method': 'set',
|
||||
'annotation': ve.dm.example.bold
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
QUnit.expect( cases.length );
|
||||
for ( i = 0; i < cases.length; i++ ) {
|
||||
ve.dm.example.preprocessAnnotations( cases[i].data );
|
||||
ve.dm.example.preprocessAnnotations( cases[i].expected );
|
||||
surface = new ve.dm.SurfaceStub( cases[i].data );
|
||||
surface.change( null, new ve.Range( 0, surface.getDocument().getData().length ) );
|
||||
surface.annotate( cases[i].annotate.method,
|
||||
ve.dm.example.createAnnotation( cases[i].annotate.annotation ) );
|
||||
assert.deepEqual( surface.getDocument().getData(), cases[i].expected, cases[i].msg );
|
||||
}
|
||||
} );
|
||||
|
|
Loading…
Reference in a new issue