8
0
Quellcode durchsuchen

Allowed delegation of all events in Emitter#delegate().

Aleksander Nowodzinski vor 9 Jahren
Ursprung
Commit
23132fe5d8

+ 39 - 13
packages/ckeditor5-utils/src/emittermixin.js

@@ -275,15 +275,14 @@ const EmitterMixin = {
 		// Delegate event to other emitters if needed.
 		if ( this._delegations ) {
 			const destinations = this._delegations.get( event );
+			const passThruDestinations = this._delegations.get( '*' );
 
 			if ( destinations ) {
-				for ( let [ emitter, name ] of destinations ) {
-					const passedEventInfo = new EventInfo( eventInfo.source, name || eventInfo.name );
-
-					passedEventInfo.path = [ ...eventInfo.path ];
+				fireDelegatedEvents( destinations, eventInfo, args );
+			}
 
-					emitter.fire( passedEventInfo, ...args );
-				}
+			if ( passThruDestinations ) {
+				fireDelegatedEvents( passThruDestinations, eventInfo, args );
 			}
 		}
 	},
@@ -313,20 +312,25 @@ const EmitterMixin = {
 			 *
 			 * @method utils.EmitterMixin.delegate#to
 			 * @param {utils.Emitter} emitter An `EmitterMixin` instance which is the destination for delegated events.
+			 * @param {String|Function} nameOrFunction A custom event name or function which converts the original name string.
 			 */
-			to: ( emitter, name ) => {
+			to: ( emitter, nameOrFunction ) => {
 				if ( !this._delegations ) {
 					this._delegations = new Map();
 				}
 
-				for ( let eventName of events ) {
-					let destinations = this._delegations.get( eventName );
+				if ( events.length ) {
+					for ( let eventName of events ) {
+						let destinations = this._delegations.get( eventName );
 
-					if ( !destinations ) {
-						this._delegations.set( eventName, new Map( [ [ emitter, name ] ] ) );
-					} else {
-						destinations.set( emitter, name );
+						if ( !destinations ) {
+							this._delegations.set( eventName, new Map( [ [ emitter, nameOrFunction ] ] ) );
+						} else {
+							destinations.set( emitter, nameOrFunction );
+						}
 					}
+				} else {
+					this._delegations.set( '*', new Map( [ [ emitter, nameOrFunction ] ] ) );
 				}
 			}
 		};
@@ -496,6 +500,28 @@ function getCallbacksForEvent( source, eventName ) {
 	return event.callbacks;
 }
 
+// Fires delegated events for given map of destinations.
+//
+// @private
+// * @param {Map.<utils.Emitter>} destinations A map containing {@link utils.Emitter}–event name pair destinations.
+// * @param {utils.EventInfo} eventInfo The original event info object.
+// * @param {Array.<*>} fireArgs Arguments the original event was fired with.
+function fireDelegatedEvents( destinations, eventInfo, fireArgs ) {
+	for ( let [ emitter, name ] of destinations ) {
+		if ( !name ) {
+			name = eventInfo.name;
+		} else if ( typeof name == 'function' ) {
+			name = name( eventInfo.name );
+		}
+
+		const delegatedInfo = new EventInfo( eventInfo.source, name );
+
+		delegatedInfo.path = [ ...eventInfo.path ];
+
+		emitter.fire( delegatedInfo, ...fireArgs );
+	}
+}
+
 /**
  * Interface representing classes which mix in {@link utils.EmitterMixin}.
  *

+ 114 - 2
packages/ckeditor5-utils/tests/emittermixin.js

@@ -723,12 +723,12 @@ describe( 'delegate', () => {
 			const spyDFoo = sinon.spy();
 
 			emitterB.delegate( 'foo' ).to( emitterA, 'bar' );
-			emitterB.delegate( 'foo' ).to( emitterC, 'baz' );
+			emitterB.delegate( 'foo' ).to( emitterC, name => name + '-baz' );
 			emitterB.delegate( 'foo' ).to( emitterD );
 
 			emitterA.on( 'foo', spyAFoo );
 			emitterA.on( 'bar', spyABar );
-			emitterC.on( 'baz', spyCBaz );
+			emitterC.on( 'foo-baz', spyCBaz );
 			emitterD.on( 'foo', spyDFoo );
 
 			emitterB.fire( 'foo' );
@@ -739,6 +739,67 @@ describe( 'delegate', () => {
 			sinon.assert.notCalled( spyAFoo );
 		} );
 
+		it( 'supports delegation under a different name with multiple events', () => {
+			const emitterA = getEmitterInstance();
+			const emitterB = getEmitterInstance();
+			const spyAFoo = sinon.spy();
+			const spyABar = sinon.spy();
+			const spyABaz = sinon.spy();
+			const spyAQux = sinon.spy();
+
+			emitterB.delegate( 'foo', 'bar', 'baz' ).to( emitterA, 'qux' );
+
+			emitterA.on( 'foo', spyAFoo );
+			emitterA.on( 'bar', spyABar );
+			emitterA.on( 'baz', spyABaz );
+			emitterA.on( 'qux', spyAQux );
+
+			emitterB.fire( 'foo' );
+			emitterB.fire( 'baz' );
+			emitterB.fire( 'bar' );
+
+			sinon.assert.notCalled( spyAFoo );
+			sinon.assert.notCalled( spyABar );
+			sinon.assert.notCalled( spyABaz );
+
+			sinon.assert.calledThrice( spyAQux );
+		} );
+
+		it( 'supports delegation with multiple events, each under a different name', () => {
+			const emitterA = getEmitterInstance();
+			const emitterB = getEmitterInstance();
+			const spyAFoo = sinon.spy();
+			const spyABar = sinon.spy();
+			const spyABaz = sinon.spy();
+			const spyAFooQux = sinon.spy();
+			const spyABarQux = sinon.spy();
+			const spyABazQux = sinon.spy();
+
+			emitterB.delegate( 'foo', 'bar', 'baz' ).to( emitterA, name => name + '-qux' );
+
+			emitterA.on( 'foo', spyAFoo );
+			emitterA.on( 'bar', spyABar );
+			emitterA.on( 'baz', spyABaz );
+
+			emitterA.on( 'foo-qux', spyAFooQux );
+			emitterA.on( 'bar-qux', spyABarQux );
+			emitterA.on( 'baz-qux', spyABazQux );
+
+			emitterB.fire( 'foo' );
+			emitterB.fire( 'baz' );
+			emitterB.fire( 'bar' );
+
+			sinon.assert.notCalled( spyAFoo );
+			sinon.assert.notCalled( spyABar );
+			sinon.assert.notCalled( spyABaz );
+
+			sinon.assert.calledOnce( spyAFooQux );
+			sinon.assert.calledOnce( spyABarQux );
+			sinon.assert.calledOnce( spyABazQux );
+
+			sinon.assert.callOrder( spyAFooQux, spyABazQux, spyABarQux );
+		} );
+
 		it( 'preserves path in delegation under a different name', ( done ) => {
 			const data = {};
 			const emitterA = getEmitterInstance();
@@ -763,6 +824,57 @@ describe( 'delegate', () => {
 
 			emitterB.fire( 'foo', data );
 		} );
+
+		it( 'supports delegation of all events', () => {
+			const emitterA = getEmitterInstance();
+			const emitterB = getEmitterInstance();
+			const spyAFoo = sinon.spy();
+			const spyABar = sinon.spy();
+			const spyABaz = sinon.spy();
+
+			emitterB.delegate().to( emitterA );
+
+			emitterA.on( 'foo', spyAFoo );
+			emitterA.on( 'bar', spyABar );
+			emitterA.on( 'baz', spyABaz );
+
+			emitterB.fire( 'foo' );
+			emitterB.fire( 'baz' );
+			emitterB.fire( 'bar' );
+
+			sinon.assert.callOrder( spyAFoo, spyABaz, spyABar );
+		} );
+
+		it( 'supports delegation of all events under different names', () => {
+			const emitterA = getEmitterInstance();
+			const emitterB = getEmitterInstance();
+			const spyAFoo = sinon.spy();
+			const spyABar = sinon.spy();
+			const spyABaz = sinon.spy();
+			const spyAFooDel = sinon.spy();
+			const spyABarDel = sinon.spy();
+			const spyABazDel = sinon.spy();
+
+			emitterB.delegate().to( emitterA, name => name + '-delegated' );
+
+			emitterA.on( 'foo', spyAFoo );
+			emitterA.on( 'bar', spyABar );
+			emitterA.on( 'baz', spyABaz );
+
+			emitterA.on( 'foo-delegated', spyAFooDel );
+			emitterA.on( 'bar-delegated', spyABarDel );
+			emitterA.on( 'baz-delegated', spyABazDel );
+
+			emitterB.fire( 'foo' );
+			emitterB.fire( 'baz' );
+			emitterB.fire( 'bar' );
+
+			sinon.assert.notCalled( spyAFoo );
+			sinon.assert.notCalled( spyABar );
+			sinon.assert.notCalled( spyABaz );
+
+			sinon.assert.callOrder( spyAFooDel, spyABazDel, spyABarDel );
+		} );
 	} );
 } );