8
0
فهرست منبع

Introduced `change:data` event.

Maciej Bukowski 7 سال پیش
والد
کامیت
6e8bcb00b9
2فایلهای تغییر یافته به همراه104 افزوده شده و 18 حذف شده
  1. 20 2
      packages/ckeditor5-engine/src/model/document.js
  2. 84 16
      packages/ckeditor5-engine/tests/model/document.js

+ 20 - 2
packages/ckeditor5-engine/src/model/document.js

@@ -151,7 +151,12 @@ export default class Document {
 			if ( !this.differ.isEmpty || hasSelectionChanged ) {
 				this._callPostFixers( writer );
 
-				this.fire( 'change', writer.batch );
+				// Fire `change:data` event when at least one operation changes the data model.
+				if ( !this.differ.isEmpty && isBatchAffectingData( writer.batch ) ) {
+					this.fire( 'change:data', writer.batch );
+				} else {
+					this.fire( 'change', writer.batch );
+				}
 
 				this.differ.reset();
 				hasSelectionChanged = false;
@@ -162,7 +167,7 @@ export default class Document {
 		// This is not covered in buffering operations because markers may change outside of them (when they
 		// are modified using `model.markers` collection, not through `MarkerOperation`).
 		this.listenTo( model.markers, 'update', ( evt, marker, oldRange, newRange ) => {
-			// Whenever marker is updated, buffer that change.
+			// Whenever marker is updated, buffer that chang7e33e.
 			this.differ.bufferMarkerChange( marker.name, oldRange, newRange );
 
 			if ( oldRange === null ) {
@@ -409,3 +414,16 @@ function validateTextNodePosition( rangeBoundary ) {
 
 	return true;
 }
+
+// Checks whether given batch affects data. Batch affects data if any of its operations affects data.
+function isBatchAffectingData( batch ) {
+	for ( const operation of batch.getOperations() ) {
+		if ( operation.affectsData === false ) {
+			continue;
+		}
+
+		return true;
+	}
+
+	return false;
+}

+ 84 - 16
packages/ckeditor5-engine/tests/model/document.js

@@ -47,7 +47,7 @@ describe( 'Document', () => {
 				baseVersion: 0,
 				isDocumentOperation: true,
 				_execute: sinon.stub().returns( data ),
-				_validate: () => {}
+				_validate: () => { }
 			};
 
 			delta = new Delta();
@@ -89,7 +89,7 @@ describe( 'Document', () => {
 			const operation = {
 				baseVersion: 1,
 				isDocumentOperation: true,
-				_execute: () => {}
+				_execute: () => { }
 			};
 
 			expect(
@@ -321,8 +321,22 @@ describe( 'Document', () => {
 			expect( spy.calledOnce ).to.be.true;
 		} );
 
-		it( 'should be fired if there was a change in a document tree in a change block and have a batch as param', () => {
-			doc.createRoot();
+		it( 'should be fired if there was a selection change in an (enqueue)change block', () => {
+			const root = doc.createRoot();
+			const spy = sinon.spy();
+
+			root._appendChild( new Text( 'foo' ) );
+
+			doc.on( 'change', spy );
+
+			model.change( writer => {
+				writer.setSelection( Range.createFromParentsAndOffsets( root, 2, root, 2 ) );
+			} );
+
+			expect( spy.calledOnce ).to.be.true;
+		} );
+
+		it( 'should not be fired if writer was used on non-document tree', () => {
 			const spy = sinon.spy();
 
 			doc.on( 'change', ( evt, batch ) => {
@@ -330,43 +344,97 @@ describe( 'Document', () => {
 				expect( batch ).to.be.instanceof( Batch );
 			} );
 
-			model.enqueueChange( writer => {
+			model.change( writer => {
+				const docFrag = writer.createDocumentFragment();
+				writer.insertText( 'foo', docFrag, 0 );
+			} );
+
+			expect( spy.calledOnce ).to.be.false;
+		} );
+	} );
+
+	describe( 'event change:data', () => {
+		it( 'should be fired if there was a change in a document tree in a change block and have a batch as a param', () => {
+			doc.createRoot();
+			const spy = sinon.spy();
+
+			doc.on( 'change:data', ( evt, batch ) => {
+				spy();
+				expect( batch ).to.be.instanceof( Batch );
+			} );
+
+			model.change( writer => {
 				writer.insertText( 'foo', doc.getRoot(), 0 );
 			} );
 
 			expect( spy.calledOnce ).to.be.true;
 		} );
 
-		it( 'should be fired if there was a selection change in an (enqueue)change block', () => {
+		it( 'should be fired before the change event', () => {
 			doc.createRoot();
+
+			const callOrder = [];
+
+			doc.on( 'change:data', () => {
+				callOrder.push( 1 );
+			} );
+
+			doc.on( 'change', () => {
+				callOrder.push( 2 );
+			} );
+
+			model.change( writer => {
+				writer.insertText( 'foo', doc.getRoot(), 0 );
+			} );
+
+			expect( callOrder ).to.deep.equal( [ 1, 2 ] );
+		} );
+
+		it( 'should not be fired if only selection changes', () => {
+			const root = doc.createRoot();
 			const spy = sinon.spy();
 
-			const root = doc.getRoot();
 			root._appendChild( new Text( 'foo' ) );
 
-			doc.on( 'change', spy );
+			doc.on( 'change:data', spy );
 
 			model.change( writer => {
 				writer.setSelection( Range.createFromParentsAndOffsets( root, 2, root, 2 ) );
 			} );
 
-			expect( spy.calledOnce ).to.be.true;
+			sinon.assert.notCalled( spy );
 		} );
 
-		it( 'should not be fired if writer was used on non-document tree', () => {
+		it( 'should fire if default marker operation is applied', () => {
+			const root = doc.createRoot();
 			const spy = sinon.spy();
 
-			doc.on( 'change', ( evt, batch ) => {
-				spy();
-				expect( batch ).to.be.instanceof( Batch );
+			root._appendChild( new Text( 'foo' ) );
+
+			doc.on( 'change:data', spy );
+
+			model.change( writer => {
+				const range = Range.createFromParentsAndOffsets( root, 2, root, 4 );
+				writer.addMarker( 'name', { range, usingOperation: true } );
 			} );
 
+			sinon.assert.calledOnce( spy );
+		} );
+
+		it( 'should not fire if the marker operation is applied and marker does not affect data', () => {
+			const root = doc.createRoot();
+			const spy = sinon.spy();
+
+			root._appendChild( new Text( 'foo' ) );
+
+			doc.on( 'change:data', spy );
+
 			model.change( writer => {
-				const docFrag = writer.createDocumentFragment();
-				writer.insertText( 'foo', docFrag, 0 );
+				const range = Range.createFromParentsAndOffsets( root, 2, root, 4 );
+				writer.addMarker( 'name', { range, usingOperation: true, affectsData: false } );
 			} );
 
-			expect( spy.calledOnce ).to.be.false;
+			sinon.assert.notCalled( spy );
 		} );
 	} );