122 lines
3.3 KiB
JavaScript
122 lines
3.3 KiB
JavaScript
|
/**
|
||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under the BSD-style license found in the
|
||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||
|
*
|
||
|
* @providesModule EventHolder
|
||
|
* @flow
|
||
|
*/
|
||
|
'use strict';
|
||
|
|
||
|
const invariant = require('fbjs/lib/invariant');
|
||
|
|
||
|
class EventHolder {
|
||
|
|
||
|
_heldEvents: Object;
|
||
|
_currentEventKey: ?Object;
|
||
|
|
||
|
constructor() {
|
||
|
this._heldEvents = {};
|
||
|
this._currentEventKey = null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Holds a given event for processing later.
|
||
|
*
|
||
|
* TODO: Annotate return type better. The structural type of the return here
|
||
|
* is pretty obvious.
|
||
|
*
|
||
|
* @param {string} eventType - Name of the event to hold and later emit
|
||
|
* @param {...*} Arbitrary arguments to be passed to each registered listener
|
||
|
* @return {object} Token that can be used to release the held event
|
||
|
*
|
||
|
* @example
|
||
|
*
|
||
|
* holder.holdEvent({someEvent: 'abc'});
|
||
|
*
|
||
|
* holder.emitToHandler({
|
||
|
* someEvent: function(data, event) {
|
||
|
* console.log(data);
|
||
|
* }
|
||
|
* }); //logs 'abc'
|
||
|
*
|
||
|
*/
|
||
|
holdEvent(eventType: string, ...args: any) {
|
||
|
this._heldEvents[eventType] = this._heldEvents[eventType] || [];
|
||
|
const eventsOfType = this._heldEvents[eventType];
|
||
|
const key = {
|
||
|
eventType: eventType,
|
||
|
index: eventsOfType.length
|
||
|
};
|
||
|
eventsOfType.push(args);
|
||
|
return key;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Emits the held events of the specified type to the given listener.
|
||
|
*
|
||
|
* @param {?string} eventType - Optional name of the events to replay
|
||
|
* @param {function} listener - The listener to which to dispatch the event
|
||
|
* @param {?object} context - Optional context object to use when invoking
|
||
|
* the listener
|
||
|
*/
|
||
|
emitToListener(eventType: ?string , listener: Function, context: ?Object) {
|
||
|
const eventsOfType = this._heldEvents[eventType];
|
||
|
if (!eventsOfType) {
|
||
|
return;
|
||
|
}
|
||
|
const origEventKey = this._currentEventKey;
|
||
|
eventsOfType.forEach((/*?array*/ eventHeld, /*number*/ index) => {
|
||
|
if (!eventHeld) {
|
||
|
return;
|
||
|
}
|
||
|
this._currentEventKey = {
|
||
|
eventType: eventType,
|
||
|
index: index
|
||
|
};
|
||
|
listener.apply(context, eventHeld);
|
||
|
});
|
||
|
this._currentEventKey = origEventKey;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Provides an API that can be called during an eventing cycle to release
|
||
|
* the last event that was invoked, so that it is no longer "held".
|
||
|
*
|
||
|
* If it is called when not inside of an emitting cycle it will throw.
|
||
|
*
|
||
|
* @throws {Error} When called not during an eventing cycle
|
||
|
*/
|
||
|
releaseCurrentEvent() {
|
||
|
invariant(
|
||
|
this._currentEventKey !== null,
|
||
|
'Not in an emitting cycle; there is no current event'
|
||
|
);
|
||
|
this._currentEventKey && this.releaseEvent(this._currentEventKey);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Releases the event corresponding to the handle that was returned when the
|
||
|
* event was first held.
|
||
|
*
|
||
|
* @param {object} token - The token returned from holdEvent
|
||
|
*/
|
||
|
releaseEvent(token: Object) {
|
||
|
delete this._heldEvents[token.eventType][token.index];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Releases all events of a certain type.
|
||
|
*
|
||
|
* @param {string} type
|
||
|
*/
|
||
|
releaseEventType(type: string) {
|
||
|
this._heldEvents[type] = [];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = EventHolder;
|