8
0
Просмотр исходного кода

Implemented support for fire()'s return values.

Piotrek Koszuliński 8 лет назад
Родитель
Сommit
ddc1eab138

+ 5 - 0
packages/ckeditor5-utils/src/emittermixin.js

@@ -135,6 +135,9 @@ const EmitterMixin = {
 	 * the priority value the sooner the callback will be fired. Events having the same priority are called in the
 	 * order they were added.
 	 * @param {Object} [options.context] The object that represents `this` in the callback. Defaults to the object firing the event.
+	 * @returns By default the method returns `undefined`. However, the return value can be changed by listeners
+	 * through modification of the {@link module:utils/emitterinfo~EmitterInfo#return}'s value (the event info
+	 * is the first param of every callback).
 	 */
 	listenTo( emitter, event, callback, options ) {
 		let emitterInfo, eventCallbacks;
@@ -296,6 +299,8 @@ const EmitterMixin = {
 				fireDelegatedEvents( passAllDestinations, eventInfo, args );
 			}
 		}
+
+		return eventInfo.return;
 	},
 
 	/**

+ 17 - 0
packages/ckeditor5-utils/src/eventinfo.js

@@ -58,5 +58,22 @@ export default class EventInfo {
 		 * @method #off
 		 */
 		this.off = spy();
+
+		/**
+		 * The value which will be returned by {@link module:utils/emittermixin~EmitterMixin#fire}.
+		 *
+		 * It's `undefined` by default and can be changed by an event listener:
+		 *
+		 *		editor.data.on( 'getSelectedContent', ( evt ) => {
+		 *			// This listener will make `editor.data.getSelectedContent()`
+		 *			// always return an empty DocumentFragment.
+		 *			evt.return = new DataFragment();
+		 *
+		 *			// Make sure no other listeners are executed.
+		 *			evt.stop();
+		 *		} );
+		 *
+		 * @member #return
+		 */
 	}
 }

+ 66 - 0
packages/ckeditor5-utils/tests/emittermixin.js

@@ -172,6 +172,72 @@ describe( 'EmitterMixin', () => {
 			sinon.assert.calledThrice( spyFoo );
 			sinon.assert.calledThrice( spyFoo2 );
 		} );
+
+		describe( 'return value', () => {
+			it( 'is undefined by default', () => {
+				expect( emitter.fire( 'foo' ) ).to.be.undefined;
+			} );
+
+			it( 'is undefined if none of the listeners modified EventInfo#return', () => {
+				emitter.on( 'foo', () => {} );
+
+				expect( emitter.fire( 'foo' ) ).to.be.undefined;
+			} );
+
+			it( 'equals EventInfo#return\'s value', () => {
+				emitter.on( 'foo', evt => {
+					evt.return = 1;
+				} );
+
+				expect( emitter.fire( 'foo' ) ).to.equal( 1 );
+			} );
+
+			it( 'equals EventInfo#return\'s value even if the event was stopped', () => {
+				emitter.on( 'foo', evt => {
+					evt.return = 1;
+				} );
+				emitter.on( 'foo', evt => {
+					evt.stop();
+				} );
+
+				expect( emitter.fire( 'foo' ) ).to.equal( 1 );
+			} );
+
+			it( 'equals EventInfo#return\'s value when it was set in a namespaced event', () => {
+				emitter.on( 'foo', evt => {
+					evt.return = 1;
+				} );
+
+				expect( emitter.fire( 'foo:bar' ) ).to.equal( 1 );
+			} );
+
+			// Rationale – delegation keeps the listeners of the two objects separate.
+			// E.g. the emitterB's listeners will always be executed before emitterA's ones.
+			// Hence, values should not be shared either.
+			it( 'is not affected by listeners executed on emitter to which the event was delegated', () => {
+				const emitterA = getEmitterInstance();
+				const emitterB = getEmitterInstance();
+
+				emitterB.delegate( 'foo' ).to( emitterA );
+
+				emitterA.on( 'foo', evt => {
+					evt.return = 1;
+				} );
+
+				expect( emitterB.fire( 'foo' ) ).to.be.undefined;
+			} );
+
+			it( 'equals the value set by the last callback', () => {
+				emitter.on( 'foo', evt => {
+					evt.return = 1;
+				} );
+				emitter.on( 'foo', evt => {
+					evt.return = 2;
+				}, { priority: 'high' } );
+
+				expect( emitter.fire( 'foo' ) ).to.equal( 1 );
+			} );
+		} );
 	} );
 
 	describe( 'on', () => {