diff --git a/Libraries/BugReporting/BugReporting.js b/Libraries/BugReporting/BugReporting.js index dc6967d97e5a3c..7ac4a121b125f3 100644 --- a/Libraries/BugReporting/BugReporting.js +++ b/Libraries/BugReporting/BugReporting.js @@ -14,7 +14,7 @@ const BugReportingNativeModule = require('NativeModules').BugReporting; const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); -import type {EmitterSubscription} from 'EventEmitter'; +import type EmitterSubscription from 'EmitterSubscription'; type ExtraData = { [key: string]: string }; type SourceCallback = () => string; @@ -28,7 +28,7 @@ type SourceCallback = () => string; class BugReporting { static _sources: Map = new Map(); - static _subscription: EmitterSubscription = null; + static _subscription: ?EmitterSubscription = null; /** * `init` is called in `AppRegistry.runApplication`, so you shouldn't have to worry about it. diff --git a/Libraries/EventEmitter/EmitterSubscription.js b/Libraries/EventEmitter/EmitterSubscription.js new file mode 100644 index 00000000000000..5188c8692f2718 --- /dev/null +++ b/Libraries/EventEmitter/EmitterSubscription.js @@ -0,0 +1,62 @@ +/** + * 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 EmitterSubscription + * @noflow + * @typechecks + */ +'use strict'; + +const EventSubscription = require('EventSubscription'); + +import type EventEmitter from 'EventEmitter'; +import type EventSubscriptionVendor from 'EventSubscriptionVendor'; + +/** + * EmitterSubscription represents a subscription with listener and context data. + */ +class EmitterSubscription extends EventSubscription { + + emitter: EventEmitter; + listener: Function; + context: ?Object; + + /** + * @param {EventEmitter} emitter - The event emitter that registered this + * subscription + * @param {EventSubscriptionVendor} subscriber - The subscriber that controls + * this subscription + * @param {function} listener - Function to invoke when the specified event is + * emitted + * @param {*} context - Optional context object to use when invoking the + * listener + */ + constructor( + emitter: EventEmitter, + subscriber: EventSubscriptionVendor, + listener: Function, + context: ?Object + ) { + super(subscriber); + this.emitter = emitter; + this.listener = listener; + this.context = context; + } + + /** + * Removes this subscription from the emitter that registered it. + * Note: we're overriding the `remove()` method of EventSubscription here + * but deliberately not calling `super.remove()` as the responsibility + * for removing the subscription lies with the EventEmitter. + */ + remove() { + this.emitter.removeSubscription(this); + } +} + +module.exports = EmitterSubscription; diff --git a/Libraries/vendor/emitter/EventEmitter.js b/Libraries/EventEmitter/EventEmitter.js similarity index 70% rename from Libraries/vendor/emitter/EventEmitter.js rename to Libraries/EventEmitter/EventEmitter.js index ba6781a57e3f98..2745cdda02be47 100644 --- a/Libraries/vendor/emitter/EventEmitter.js +++ b/Libraries/EventEmitter/EventEmitter.js @@ -8,14 +8,13 @@ * * @providesModule EventEmitter * @noflow - * @typechecks + * @typecheck */ -var EmitterSubscription = require('EmitterSubscription'); -var ErrorUtils = require('ErrorUtils'); -var EventSubscriptionVendor = require('EventSubscriptionVendor'); -var emptyFunction = require('fbjs/lib/emptyFunction'); -var invariant = require('fbjs/lib/invariant'); +const EmitterSubscription = require('EmitterSubscription'); +const EventSubscriptionVendor = require('EventSubscriptionVendor'); +const emptyFunction = require('fbjs/lib/emptyFunction'); +const invariant = require('fbjs/lib/invariant'); /** * @class EventEmitter @@ -31,11 +30,18 @@ var invariant = require('fbjs/lib/invariant'); * more advanced emitter may use an EventHolder and EventFactory. */ class EventEmitter { + + _subscriber: EventSubscriptionVendor; + _currentSubscription: ?EmitterSubscription; + /** * @constructor + * + * @param {EventSubscriptionVendor} subscriber - Optional subscriber instance + * to use. If omitted, a new subscriber will be created for the emitter. */ - constructor() { - this._subscriber = new EventSubscriptionVendor(); + constructor(subscriber: ?EventSubscriptionVendor) { + this._subscriber = subscriber || new EventSubscriptionVendor(); } /** @@ -53,10 +59,12 @@ class EventEmitter { * listener */ addListener( - eventType: String, listener, context: ?Object): EmitterSubscription { - return this._subscriber.addSubscription( + eventType: string, listener: Function, context: ?Object): EmitterSubscription { + + return (this._subscriber.addSubscription( eventType, - new EmitterSubscription(this._subscriber, listener, context)); + new EmitterSubscription(this, this._subscriber, listener, context) + ) : any); } /** @@ -69,10 +77,9 @@ class EventEmitter { * @param {*} context - Optional context object to use when invoking the * listener */ - once(eventType: String, listener, context: ?Object): EmitterSubscription { - var emitter = this; - return this.addListener(eventType, function() { - emitter.removeCurrentListener(); + once(eventType: string, listener: Function, context: ?Object): EmitterSubscription { + return this.addListener(eventType, () => { + this.removeCurrentListener(); listener.apply(context, arguments); }); } @@ -84,7 +91,7 @@ class EventEmitter { * @param {?string} eventType - Optional name of the event whose registered * listeners to remove */ - removeAllListeners(eventType: ?String) { + removeAllListeners(eventType: ?string) { this._subscriber.removeAllSubscriptions(eventType); } @@ -114,7 +121,19 @@ class EventEmitter { !!this._currentSubscription, 'Not in an emitting cycle; there is no current subscription' ); - this._subscriber.removeSubscription(this._currentSubscription); + this.removeSubscription(this._currentSubscription); + } + + /** + * Removes a specific subscription. Called by the `remove()` method of the + * subscription itself to ensure any necessary cleanup is performed. + */ + removeSubscription(subscription: EmitterSubscription) { + invariant( + subscription.emitter === this, + 'Subscription does not belong to this emitter.' + ); + this._subscriber.removeSubscription(subscription); } /** @@ -124,8 +143,8 @@ class EventEmitter { * @param {string} eventType - Name of the event to query * @returns {array} */ - listeners(eventType: String): Array /* TODO: Array */ { - var subscriptions = this._subscriber.getSubscriptionsForType(eventType); + listeners(eventType: string): [EmitterSubscription] { + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any); return subscriptions ? subscriptions.filter(emptyFunction.thatReturnsTrue).map( function(subscription) { @@ -148,13 +167,11 @@ class EventEmitter { * * emitter.emit('someEvent', 'abc'); // logs 'abc' */ - emit(eventType: String) { - var subscriptions = this._subscriber.getSubscriptionsForType(eventType); + emit(eventType: string) { + const subscriptions: ?[EmitterSubscription] = (this._subscriber.getSubscriptionsForType(eventType): any); if (subscriptions) { - var keys = Object.keys(subscriptions); - for (var ii = 0; ii < keys.length; ii++) { - var key = keys[ii]; - var subscription = subscriptions[key]; + for (let i = 0, l = subscriptions.length; i < l; i++) { + const subscription = subscriptions[i]; // The subscription may have been removed during this event loop. if (subscription) { diff --git a/Libraries/vendor/emitter/EventEmitterWithHolding.js b/Libraries/EventEmitter/EventEmitterWithHolding.js similarity index 69% rename from Libraries/vendor/emitter/EventEmitterWithHolding.js rename to Libraries/EventEmitter/EventEmitterWithHolding.js index 89ab4759fa0778..a74d37c01681b0 100644 --- a/Libraries/vendor/emitter/EventEmitterWithHolding.js +++ b/Libraries/EventEmitter/EventEmitterWithHolding.js @@ -1,22 +1,20 @@ /** - * @generated SignedSource<> + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * 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 EventEmitterWithHolding - * @typechecks + * @flow */ 'use strict'; +import type EmitterSubscription from 'EmitterSubscription'; +import type EventEmitter from 'EventEmitter'; +import type EventHolder from 'EventHolder'; + /** * @class EventEmitterWithHolding * @description @@ -30,6 +28,12 @@ * that uses an emitter. */ class EventEmitterWithHolding { + + _emitter: EventEmitter; + _eventHolder: EventHolder; + _currentEventToken: ?Object; + _emittingHeldEvents: boolean; + /** * @constructor * @param {object} emitter - The object responsible for emitting the actual @@ -37,7 +41,7 @@ class EventEmitterWithHolding { * @param {object} holder - The event holder that is responsible for holding * and then emitting held events. */ - constructor(emitter, holder) { + constructor(emitter: EventEmitter, holder: EventHolder) { this._emitter = emitter; this._eventHolder = holder; this._currentEventToken = null; @@ -47,14 +51,14 @@ class EventEmitterWithHolding { /** * @see EventEmitter#addListener */ - addListener(eventType: String, listener, context: ?Object) { + addListener(eventType: string, listener: Function, context: ?Object) { return this._emitter.addListener(eventType, listener, context); } /** * @see EventEmitter#once */ - once(eventType: String, listener, context: ?Object) { + once(eventType: string, listener: Function, context: ?Object) { return this._emitter.once(eventType, listener, context); } @@ -79,8 +83,8 @@ class EventEmitterWithHolding { * }); // logs 'abc' */ addRetroactiveListener( - eventType: String, listener, context: ?Object): EmitterSubscription { - var subscription = this._emitter.addListener(eventType, listener, context); + eventType: string, listener: Function, context: ?Object): EmitterSubscription { + const subscription = this._emitter.addListener(eventType, listener, context); this._emittingHeldEvents = true; this._eventHolder.emitToListener(eventType, listener, context); @@ -92,7 +96,7 @@ class EventEmitterWithHolding { /** * @see EventEmitter#removeAllListeners */ - removeAllListeners(eventType: String) { + removeAllListeners(eventType: string) { this._emitter.removeAllListeners(eventType); } @@ -106,15 +110,15 @@ class EventEmitterWithHolding { /** * @see EventEmitter#listeners */ - listeners(eventType: String) /* TODO: Annotate return type here */ { + listeners(eventType: string) /* TODO: Annotate return type here */ { return this._emitter.listeners(eventType); } /** * @see EventEmitter#emit */ - emit(eventType: String, a, b, c, d, e, _) { - this._emitter.emit(eventType, a, b, c, d, e, _); + emit(eventType: string, ...args: any) { + this._emitter.emit(eventType, ...args); } /** @@ -132,12 +136,9 @@ class EventEmitterWithHolding { * console.log(message); * }); // logs 'abc' */ - emitAndHold(eventType: String, a, b, c, d, e, _) { - this._currentEventToken = this._eventHolder.holdEvent( - eventType, - a, b, c, d, e, _ - ); - this._emitter.emit(eventType, a, b, c, d, e, _); + emitAndHold(eventType: string, ...args: any) { + this._currentEventToken = this._eventHolder.holdEvent(eventType, ...args); + this._emitter.emit(eventType, ...args); this._currentEventToken = null; } @@ -145,7 +146,7 @@ class EventEmitterWithHolding { * @see EventHolder#releaseCurrentEvent */ releaseCurrentEvent() { - if (this._currentEventToken !== null) { + if (this._currentEventToken) { this._eventHolder.releaseEvent(this._currentEventToken); } else if (this._emittingHeldEvents) { this._eventHolder.releaseCurrentEvent(); @@ -156,7 +157,7 @@ class EventEmitterWithHolding { * @see EventHolder#releaseEventType * @param {string} eventType */ - releaseHeldEventType(eventType: String) { + releaseHeldEventType(eventType: string) { this._eventHolder.releaseEventType(eventType); } } diff --git a/Libraries/vendor/emitter/EventHolder.js b/Libraries/EventEmitter/EventHolder.js similarity index 67% rename from Libraries/vendor/emitter/EventHolder.js rename to Libraries/EventEmitter/EventHolder.js index 76cfd33141e821..8957ad5cce4204 100644 --- a/Libraries/vendor/emitter/EventHolder.js +++ b/Libraries/EventEmitter/EventHolder.js @@ -1,25 +1,23 @@ /** - * @generated SignedSource<<0591836c443c735d24e61782320d3d16>> + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * 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 - * @typechecks + * @flow */ 'use strict'; -var invariant = require('fbjs/lib/invariant'); +const invariant = require('fbjs/lib/invariant'); class EventHolder { + + _heldEvents: Object; + _currentEventKey: ?Object; + constructor() { this._heldEvents = {}; this._currentEventKey = null; @@ -46,14 +44,14 @@ class EventHolder { * }); //logs 'abc' * */ - holdEvent(eventType: String, a, b, c, d, e, _) { + holdEvent(eventType: string, ...args: any) { this._heldEvents[eventType] = this._heldEvents[eventType] || []; - var eventsOfType = this._heldEvents[eventType]; - var key = { + const eventsOfType = this._heldEvents[eventType]; + const key = { eventType: eventType, index: eventsOfType.length }; - eventsOfType.push([a, b, c, d, e, _]); + eventsOfType.push(args); return key; } @@ -65,12 +63,12 @@ class EventHolder { * @param {?object} context - Optional context object to use when invoking * the listener */ - emitToListener(eventType: ?String , listener, context: ?Object) { - var eventsOfType = this._heldEvents[eventType]; + emitToListener(eventType: ?string , listener: Function, context: ?Object) { + const eventsOfType = this._heldEvents[eventType]; if (!eventsOfType) { return; } - var origEventKey = this._currentEventKey; + const origEventKey = this._currentEventKey; eventsOfType.forEach((/*?array*/ eventHeld, /*number*/ index) => { if (!eventHeld) { return; @@ -97,7 +95,7 @@ class EventHolder { this._currentEventKey !== null, 'Not in an emitting cycle; there is no current event' ); - this.releaseEvent(this._currentEventKey); + this._currentEventKey && this.releaseEvent(this._currentEventKey); } /** @@ -115,7 +113,7 @@ class EventHolder { * * @param {string} type */ - releaseEventType(type: String) { + releaseEventType(type: string) { this._heldEvents[type] = []; } } diff --git a/Libraries/EventEmitter/EventSubscription.js b/Libraries/EventEmitter/EventSubscription.js new file mode 100644 index 00000000000000..955a234ecbf976 --- /dev/null +++ b/Libraries/EventEmitter/EventSubscription.js @@ -0,0 +1,42 @@ +/** + * 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 EventSubscription + * @flow + */ +'use strict'; + +import type EventSubscriptionVendor from 'EventSubscriptionVendor'; + +/** + * EventSubscription represents a subscription to a particular event. It can + * remove its own subscription. + */ +class EventSubscription { + + eventType: string; + key: number; + subscriber: EventSubscriptionVendor; + + /** + * @param {EventSubscriptionVendor} subscriber the subscriber that controls + * this subscription. + */ + constructor(subscriber: EventSubscriptionVendor) { + this.subscriber = subscriber; + } + + /** + * Removes this subscription from the subscriber that controls it. + */ + remove() { + this.subscriber.removeSubscription(this); + } +} + +module.exports = EventSubscription; diff --git a/Libraries/vendor/emitter/EventSubscriptionVendor.js b/Libraries/EventEmitter/EventSubscriptionVendor.js similarity index 62% rename from Libraries/vendor/emitter/EventSubscriptionVendor.js rename to Libraries/EventEmitter/EventSubscriptionVendor.js index d992a69a553696..7078766a01b4c9 100644 --- a/Libraries/vendor/emitter/EventSubscriptionVendor.js +++ b/Libraries/EventEmitter/EventSubscriptionVendor.js @@ -1,23 +1,19 @@ /** - * @generated SignedSource<<24d5cc1cdd24704296686faf89dd36cf>> + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * 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 EventSubscriptionVendor - * @typechecks + * @flow */ 'use strict'; -var invariant = require('fbjs/lib/invariant'); +const invariant = require('fbjs/lib/invariant'); + +import type EventSubscription from 'EventSubscription'; /** * EventSubscriptionVendor stores a set of EventSubscriptions that are @@ -25,6 +21,9 @@ var invariant = require('fbjs/lib/invariant'); */ class EventSubscriptionVendor { + _subscriptionsForType: Object; + _currentSubscription: ?EventSubscription; + constructor() { this._subscriptionsForType = {}; this._currentSubscription = null; @@ -37,14 +36,14 @@ class EventSubscriptionVendor { * @param {EventSubscription} subscription */ addSubscription( - eventType: String, subscription: EventSubscription): EventSubscription { + eventType: string, subscription: EventSubscription): EventSubscription { invariant( subscription.subscriber === this, 'The subscriber of the subscription is incorrectly set.'); if (!this._subscriptionsForType[eventType]) { this._subscriptionsForType[eventType] = []; } - var key = this._subscriptionsForType[eventType].length; + const key = this._subscriptionsForType[eventType].length; this._subscriptionsForType[eventType].push(subscription); subscription.eventType = eventType; subscription.key = key; @@ -57,7 +56,7 @@ class EventSubscriptionVendor { * @param {?string} eventType - Optional name of the event type whose * registered supscriptions to remove, if null remove all subscriptions. */ - removeAllSubscriptions(eventType: ?String) { + removeAllSubscriptions(eventType: ?string) { if (eventType === undefined) { this._subscriptionsForType = {}; } else { @@ -72,10 +71,10 @@ class EventSubscriptionVendor { * @param {object} subscription */ removeSubscription(subscription: Object) { - var eventType = subscription.eventType; - var key = subscription.key; + const eventType = subscription.eventType; + const key = subscription.key; - var subscriptionsForType = this._subscriptionsForType[eventType]; + const subscriptionsForType = this._subscriptionsForType[eventType]; if (subscriptionsForType) { delete subscriptionsForType[key]; } @@ -93,7 +92,7 @@ class EventSubscriptionVendor { * @param {string} eventType * @returns {?array} */ - getSubscriptionsForType(eventType: String): ?Array { + getSubscriptionsForType(eventType: string): ?[EventSubscription] { return this._subscriptionsForType[eventType]; } } diff --git a/Libraries/vendor/emitter/EventValidator.js b/Libraries/EventEmitter/EventValidator.js similarity index 69% rename from Libraries/vendor/emitter/EventValidator.js rename to Libraries/EventEmitter/EventValidator.js index edece9ebeef758..26fda76dfab7f5 100644 --- a/Libraries/vendor/emitter/EventValidator.js +++ b/Libraries/EventEmitter/EventValidator.js @@ -1,22 +1,17 @@ /** - * @generated SignedSource<<7149bdac6fb48595f245ad6e76938e44>> + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * 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 EventValidator + * @flow */ 'use strict'; -var copyProperties = require('copyProperties'); +const copyProperties = require('copyProperties'); /** * EventValidator is designed to validate event types to make it easier to catch @@ -27,7 +22,7 @@ var copyProperties = require('copyProperties'); * mistyped the event name it will suggest what you might have meant to type in * the error message. */ -var EventValidator = { +const EventValidator = { /** * @param {Object} emitter - The object responsible for emitting the actual * events @@ -35,12 +30,12 @@ var EventValidator = { * check for errors * @return {Object} A new emitter with event type validation * @example - * var types = {someEvent: true, anotherEvent: true}; - * var emitter = EventValidator.addValidation(emitter, types); + * const types = {someEvent: true, anotherEvent: true}; + * const emitter = EventValidator.addValidation(emitter, types); */ addValidation: function(emitter: Object, types: Object) { - var eventTypes = Object.keys(types); - var emitterWithValidation = Object.create(emitter); + const eventTypes = Object.keys(types); + const emitterWithValidation = Object.create(emitter); copyProperties(emitterWithValidation, { emit: function emit(type, a, b, c, d, e, _) { @@ -60,7 +55,7 @@ function assertAllowsEventType(type, allowedTypes) { } function errorMessageFor(type, allowedTypes) { - var message = 'Unknown event type "' + type + '". '; + let message = 'Unknown event type "' + type + '". '; if (__DEV__) { message += recommendationFor(type, allowedTypes); } @@ -71,7 +66,7 @@ function errorMessageFor(type, allowedTypes) { // Allow for good error messages if (__DEV__) { var recommendationFor = function (type, allowedTypes) { - var closestTypeRecommendation = closestTypeFor(type, allowedTypes); + const closestTypeRecommendation = closestTypeFor(type, allowedTypes); if (isCloseEnough(closestTypeRecommendation, type)) { return 'Did you mean "' + closestTypeRecommendation.type + '"? '; } else { @@ -80,7 +75,7 @@ if (__DEV__) { }; var closestTypeFor = function (type, allowedTypes) { - var typeRecommendations = allowedTypes.map( + const typeRecommendations = allowedTypes.map( typeRecommendationFor.bind(this, type) ); return typeRecommendations.sort(recommendationSort)[0]; @@ -108,8 +103,8 @@ if (__DEV__) { }; var damerauLevenshteinDistance = function (a, b) { - var i, j; - var d = []; + let i, j; + const d = []; for (i = 0; i <= a.length; i++) { d[i] = [i]; @@ -121,7 +116,7 @@ if (__DEV__) { for (i = 1; i <= a.length; i++) { for (j = 1; j <= b.length; j++) { - var cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1; + const cost = a.charAt(i - 1) === b.charAt(j - 1) ? 0 : 1; d[i][j] = Math.min( d[i - 1][j] + 1, @@ -130,8 +125,8 @@ if (__DEV__) { ); if (i > 1 && j > 1 && - a.charAt(i - 1) == b.charAt(j - 2) && - a.charAt(i - 2) == b.charAt(j - 1)) { + a.charAt(i - 1) === b.charAt(j - 2) && + a.charAt(i - 2) === b.charAt(j - 1)) { d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + cost); } } diff --git a/Libraries/Device/NativeEventEmitter.js b/Libraries/EventEmitter/NativeEventEmitter.js similarity index 68% rename from Libraries/Device/NativeEventEmitter.js rename to Libraries/EventEmitter/NativeEventEmitter.js index 5b7224f3b30e3b..67774a1655f45a 100644 --- a/Libraries/Device/NativeEventEmitter.js +++ b/Libraries/EventEmitter/NativeEventEmitter.js @@ -11,6 +11,7 @@ */ 'use strict'; +const EventEmitter = require('EventEmitter'); const Platform = require('Platform'); const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); const invariant = require('fbjs/lib/invariant'); @@ -21,45 +22,39 @@ import type EmitterSubscription from 'EmitterSubscription'; * Abstract base class for implementing event-emitting modules. This implements * a subset of the standard EventEmitter node module API. */ -class NativeEventEmitter { +class NativeEventEmitter extends EventEmitter { _nativeModule: Object; constructor(nativeModule: Object) { + super(RCTDeviceEventEmitter.sharedSubscriber); if (Platform.OS === 'ios') { invariant(nativeModule, 'Native module cannot be null.'); this._nativeModule = nativeModule; } } - addListener(eventType: string, listener: any, context: ?Object): EmitterSubscription { + addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription { if (Platform.OS === 'ios') { this._nativeModule.addListener(eventType); } - return RCTDeviceEventEmitter.nativeAddListener(eventType, listener, context); - } - - once(eventType: string, listener: any, context: ?Object): EmitterSubscription { - return this.addListener(eventType, () => { - this.removeCurrentListener(); - listener.apply(context, arguments); - }); + return super.addListener(eventType, listener, context); } removeAllListeners(eventType: string) { invariant(eventType, 'eventType argument is required.'); if (Platform.OS === 'ios') { - const count = RCTDeviceEventEmitter.listeners(eventType).length; + const count = this.listeners(eventType).length; this._nativeModule.removeListeners(count); } - RCTDeviceEventEmitter.removeAllListeners(eventType); + super.removeAllListeners(eventType); } - removeCurrentListener() { + removeSubscription(subscription: EmitterSubscription) { if (Platform.OS === 'ios') { this._nativeModule.removeListeners(1); } - RCTDeviceEventEmitter.removeCurrentListener(); + super.removeSubscription(subscription); } } diff --git a/Libraries/Device/RCTDeviceEventEmitter.js b/Libraries/EventEmitter/RCTDeviceEventEmitter.js similarity index 55% rename from Libraries/Device/RCTDeviceEventEmitter.js rename to Libraries/EventEmitter/RCTDeviceEventEmitter.js index 96d9183b297d5a..8a6bbea4724757 100644 --- a/Libraries/Device/RCTDeviceEventEmitter.js +++ b/Libraries/EventEmitter/RCTDeviceEventEmitter.js @@ -12,6 +12,7 @@ 'use strict'; const EventEmitter = require('EventEmitter'); +const EventSubscriptionVendor = require('EventSubscriptionVendor'); const BatchedBridge = require('BatchedBridge'); import type EmitterSubscription from 'EmitterSubscription'; @@ -22,7 +23,15 @@ import type EmitterSubscription from 'EmitterSubscription'; */ class RCTDeviceEventEmitter extends EventEmitter { - addListener(eventType: string, listener: any, context: ?Object): EmitterSubscription { + sharedSubscriber: EventSubscriptionVendor; + + constructor() { + const sharedSubscriber = new EventSubscriptionVendor(); + super(sharedSubscriber); + this.sharedSubscriber = sharedSubscriber; + } + + addListener(eventType: string, listener: Function, context: ?Object): EmitterSubscription { if (eventType.lastIndexOf('statusBar', 0) === 0) { console.warn('`%s` event should be registered via the StatusBarIOS module', eventType); return require('StatusBarIOS').addListener(eventType, listener, context); @@ -34,8 +43,26 @@ class RCTDeviceEventEmitter extends EventEmitter { return super.addListener(eventType, listener, context); } - nativeAddListener(eventType: string, listener: any, context: ?Object): EmitterSubscription { - return super.addListener(eventType, listener, context); + removeAllListeners(eventType: ?string) { + if (eventType) { + if (eventType.lastIndexOf('statusBar', 0) === 0) { + console.warn('statusBar events should be unregistered via the StatusBarIOS module'); + return require('StatusBarIOS').removeAllListeners(eventType); + } + if (eventType.lastIndexOf('keyboard', 0) === 0) { + console.warn('keyboard events should be unregistered via the Keyboard module'); + return require('Keyboard').removeAllListeners(eventType); + } + } + super.removeAllListeners(eventType); + } + + removeSubscription(subscription: EmitterSubscription) { + if (subscription.emitter !== this) { + subscription.emitter.removeSubscription(subscription); + } else { + super.removeSubscription(subscription); + } } } diff --git a/Libraries/NativeApp/RCTNativeAppEventEmitter.js b/Libraries/EventEmitter/RCTNativeAppEventEmitter.js similarity index 61% rename from Libraries/NativeApp/RCTNativeAppEventEmitter.js rename to Libraries/EventEmitter/RCTNativeAppEventEmitter.js index 9d4de85f39e875..b59c54fc36d854 100644 --- a/Libraries/NativeApp/RCTNativeAppEventEmitter.js +++ b/Libraries/EventEmitter/RCTNativeAppEventEmitter.js @@ -11,10 +11,14 @@ */ 'use strict'; -var BatchedBridge = require('BatchedBridge'); -var EventEmitter = require('EventEmitter'); +const BatchedBridge = require('BatchedBridge'); +const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); -var RCTNativeAppEventEmitter = new EventEmitter(); +/** + * Deprecated - subclass NativeEventEmitter to create granular event modules instead of + * adding all event listeners directly to RCTNativeAppEventEmitter. + */ +const RCTNativeAppEventEmitter = RCTDeviceEventEmitter; BatchedBridge.registerCallableModule( 'RCTNativeAppEventEmitter', diff --git a/Libraries/vendor/emitter/mixInEventEmitter.js b/Libraries/EventEmitter/mixInEventEmitter.js similarity index 69% rename from Libraries/vendor/emitter/mixInEventEmitter.js rename to Libraries/EventEmitter/mixInEventEmitter.js index fc39f7b649e13f..c2f43a0441ba7b 100644 --- a/Libraries/vendor/emitter/mixInEventEmitter.js +++ b/Libraries/EventEmitter/mixInEventEmitter.js @@ -1,30 +1,25 @@ /** - * @generated SignedSource<> + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * 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 mixInEventEmitter + * @flow */ -var EventEmitter = require('EventEmitter'); -var EventEmitterWithHolding = require('EventEmitterWithHolding'); -var EventHolder = require('EventHolder'); -var EventValidator = require('EventValidator'); +const EventEmitter = require('EventEmitter'); +const EventEmitterWithHolding = require('EventEmitterWithHolding'); +const EventHolder = require('EventHolder'); +const EventValidator = require('EventValidator'); -var copyProperties = require('copyProperties'); -var invariant = require('fbjs/lib/invariant'); -var keyOf = require('fbjs/lib/keyOf'); +const copyProperties = require('copyProperties'); +const invariant = require('fbjs/lib/invariant'); +const keyOf = require('fbjs/lib/keyOf'); -var TYPES_KEY = keyOf({__types: true}); +const TYPES_KEY = keyOf({__types: true}); /** * API to setup an object or constructor to be able to emit data events. @@ -47,16 +42,16 @@ var TYPES_KEY = keyOf({__types: true}); * mixInEventEmitter(Singleton, {lonely: true}); * Singleton.emit('lonely', true); */ -function mixInEventEmitter(klass, types) { +function mixInEventEmitter(cls: Function | Object, types: Object) { invariant(types, 'Must supply set of valid event types'); // If this is a constructor, write to the prototype, otherwise write to the // singleton object. - var target = klass.prototype || klass; + const target = cls.prototype || cls; invariant(!target.__eventEmitter, 'An active emitter is already mixed in'); - var ctor = klass.constructor; + const ctor = cls.constructor; if (ctor) { invariant( ctor === Object || ctor === Function, @@ -76,7 +71,7 @@ function mixInEventEmitter(klass, types) { copyProperties(target, EventEmitterMixin); } -var EventEmitterMixin = { +const EventEmitterMixin = { emit: function(eventType, a, b, c, d, e, _) { return this.__getEventEmitter().emit(eventType, a, b, c, d, e, _); }, @@ -123,10 +118,10 @@ var EventEmitterMixin = { __getEventEmitter: function() { if (!this.__eventEmitter) { - var emitter = new EventEmitter(); + let emitter = new EventEmitter(); emitter = EventValidator.addValidation(emitter, this.__types); - var holder = new EventHolder(); + const holder = new EventHolder(); this.__eventEmitter = new EventEmitterWithHolding(emitter, holder); } return this.__eventEmitter; diff --git a/Libraries/vendor/emitter/EmitterSubscription.js b/Libraries/vendor/emitter/EmitterSubscription.js deleted file mode 100644 index bbd1d414c431cc..00000000000000 --- a/Libraries/vendor/emitter/EmitterSubscription.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @generated SignedSource<> - * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * - * @providesModule EmitterSubscription - * @noflow - * @typechecks - */ -'use strict'; - -var EventSubscription = require('EventSubscription'); - -/** - * EmitterSubscription represents a subscription with listener and context data. - */ -class EmitterSubscription extends EventSubscription { - - /** - * @param {EventSubscriptionVendor} subscriber - The subscriber that controls - * this subscription - * @param {function} listener - Function to invoke when the specified event is - * emitted - * @param {*} context - Optional context object to use when invoking the - * listener - */ - constructor(subscriber: EventSubscriptionVendor, listener, context: ?Object) { - super(subscriber); - this.listener = listener; - this.context = context; - } -} - -module.exports = EmitterSubscription; diff --git a/Libraries/vendor/emitter/EventSubscription.js b/Libraries/vendor/emitter/EventSubscription.js deleted file mode 100644 index e6dd36700644af..00000000000000 --- a/Libraries/vendor/emitter/EventSubscription.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @generated SignedSource<<92108a17b1f3eee4b7e3dd7d484aa17a>> - * - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !! This file is a check-in of a static_upstream project! !! - * !! !! - * !! You should not modify this file directly. Instead: !! - * !! 1) Use `fjs use-upstream` to temporarily replace this with !! - * !! the latest version from upstream. !! - * !! 2) Make your changes, test them, etc. !! - * !! 3) Use `fjs push-upstream` to copy your changes back to !! - * !! static_upstream. !! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * - * @providesModule EventSubscription - * @typechecks - */ -'use strict'; - -/** - * EventSubscription represents a subscription to a particular event. It can - * remove its own subscription. - */ -class EventSubscription { - - /** - * @param {EventSubscriptionVendor} subscriber the subscriber that controls - * this subscription. - */ - constructor(subscriber: EventSubscriptionVendor) { - this.subscriber = subscriber; - } - - /** - * Removes this subscription from the subscriber that controls it. - */ - remove() { - this.subscriber.removeSubscription(this); - } -} - -module.exports = EventSubscription; diff --git a/React/Modules/RCTEventEmitter.m b/React/Modules/RCTEventEmitter.m index d53e1f8c2f035c..a3b22fa82e9840 100644 --- a/React/Modules/RCTEventEmitter.m +++ b/React/Modules/RCTEventEmitter.m @@ -72,7 +72,9 @@ - (void)dealloc RCT_EXPORT_METHOD(removeListeners:(NSInteger)count) { - RCTAssert(count <= _listenerCount, @"Attempted to remove more listeners than added"); + if (RCT_DEBUG && count > _listenerCount) { + RCTLogError(@"Attempted to remove more %@ listeners than added", [self class]); + } if (count == _listenerCount) { [self stopObserving]; }