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:
Christian Williams 2012-11-16 14:57:20 -08:00 committed by Trevor Parscal
parent 96fcbbd695
commit 9787166bab
5 changed files with 133 additions and 134 deletions

View file

@ -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 );
};
/**

View file

@ -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++ ) {

View file

@ -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.
*

View file

@ -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;
};

View file

@ -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 );
}
} );