2015-01-30 01:10:49 +00:00
|
|
|
/**
|
2015-03-23 22:07:33 +00:00
|
|
|
* 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.
|
2015-01-30 01:10:49 +00:00
|
|
|
*
|
2015-05-08 16:45:43 +00:00
|
|
|
* @providesModule ReactNativeEventEmitter
|
2015-03-25 22:36:50 +00:00
|
|
|
* @flow
|
2015-01-30 01:10:49 +00:00
|
|
|
*/
|
2015-05-19 20:43:46 +00:00
|
|
|
'use strict';
|
2015-01-30 01:10:49 +00:00
|
|
|
|
|
|
|
var EventPluginHub = require('EventPluginHub');
|
|
|
|
var ReactEventEmitterMixin = require('ReactEventEmitterMixin');
|
2015-05-08 16:45:43 +00:00
|
|
|
var ReactNativeTagHandles = require('ReactNativeTagHandles');
|
2015-01-30 01:10:49 +00:00
|
|
|
var NodeHandle = require('NodeHandle');
|
|
|
|
var EventConstants = require('EventConstants');
|
|
|
|
|
|
|
|
var merge = require('merge');
|
|
|
|
var warning = require('warning');
|
|
|
|
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Version of `ReactBrowserEventEmitter` that works on the receiving side of a
|
|
|
|
* serialized worker boundary.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Shared default empty native event - conserve memory.
|
|
|
|
var EMPTY_NATIVE_EVENT = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Selects a subsequence of `Touch`es, without destroying `touches`.
|
|
|
|
*
|
|
|
|
* @param {Array<Touch>} touches Deserialized touch objects.
|
|
|
|
* @param {Array<number>} indices Indices by which to pull subsequence.
|
|
|
|
* @return {Array<Touch>} Subsequence of touch objects.
|
|
|
|
*/
|
|
|
|
var touchSubsequence = function(touches, indices) {
|
|
|
|
var ret = [];
|
|
|
|
for (var i = 0; i < indices.length; i++) {
|
|
|
|
ret.push(touches[indices[i]]);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: Pool all of this.
|
|
|
|
*
|
|
|
|
* Destroys `touches` by removing touch objects at indices `indices`. This is
|
|
|
|
* to maintain compatibility with W3C touch "end" events, where the active
|
|
|
|
* touches don't include the set that has just been "ended".
|
|
|
|
*
|
|
|
|
* @param {Array<Touch>} touches Deserialized touch objects.
|
|
|
|
* @param {Array<number>} indices Indices to remove from `touches`.
|
|
|
|
* @return {Array<Touch>} Subsequence of removed touch objects.
|
|
|
|
*/
|
2015-03-25 22:36:50 +00:00
|
|
|
var removeTouchesAtIndices = function(
|
|
|
|
touches: Array<Object>,
|
|
|
|
indices: Array<number>
|
|
|
|
): Array<Object> {
|
2015-01-30 01:10:49 +00:00
|
|
|
var rippedOut = [];
|
2015-03-25 22:36:50 +00:00
|
|
|
// use an unsafe downcast to alias to nullable elements,
|
|
|
|
// so we can delete and then compact.
|
|
|
|
var temp: Array<?Object> = (touches: Array<any>);
|
2015-01-30 01:10:49 +00:00
|
|
|
for (var i = 0; i < indices.length; i++) {
|
|
|
|
var index = indices[i];
|
|
|
|
rippedOut.push(touches[index]);
|
2015-03-25 22:36:50 +00:00
|
|
|
temp[index] = null;
|
2015-01-30 01:10:49 +00:00
|
|
|
}
|
|
|
|
var fillAt = 0;
|
2015-03-25 22:36:50 +00:00
|
|
|
for (var j = 0; j < temp.length; j++) {
|
|
|
|
var cur = temp[j];
|
2015-01-30 01:10:49 +00:00
|
|
|
if (cur !== null) {
|
2015-03-25 22:36:50 +00:00
|
|
|
temp[fillAt++] = cur;
|
2015-01-30 01:10:49 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-25 22:36:50 +00:00
|
|
|
temp.length = fillAt;
|
2015-01-30 01:10:49 +00:00
|
|
|
return rippedOut;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2015-05-08 16:45:43 +00:00
|
|
|
* `ReactNativeEventEmitter` is used to attach top-level event listeners. For example:
|
2015-01-30 01:10:49 +00:00
|
|
|
*
|
2015-05-08 16:45:43 +00:00
|
|
|
* ReactNativeEventEmitter.putListener('myID', 'onClick', myFunction);
|
2015-01-30 01:10:49 +00:00
|
|
|
*
|
|
|
|
* This would allocate a "registration" of `('onClick', myFunction)` on 'myID'.
|
|
|
|
*
|
|
|
|
* @internal
|
|
|
|
*/
|
2015-05-08 16:45:43 +00:00
|
|
|
var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
|
2015-01-30 01:10:49 +00:00
|
|
|
|
|
|
|
registrationNames: EventPluginHub.registrationNameModules,
|
|
|
|
|
|
|
|
putListener: EventPluginHub.putListener,
|
|
|
|
|
|
|
|
getListener: EventPluginHub.getListener,
|
|
|
|
|
|
|
|
deleteListener: EventPluginHub.deleteListener,
|
|
|
|
|
|
|
|
deleteAllListeners: EventPluginHub.deleteAllListeners,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal version of `receiveEvent` in terms of normalized (non-tag)
|
|
|
|
* `rootNodeID`.
|
|
|
|
*
|
|
|
|
* @see receiveEvent.
|
|
|
|
*
|
|
|
|
* @param {rootNodeID} rootNodeID React root node ID that event occured on.
|
|
|
|
* @param {TopLevelType} topLevelType Top level type of event.
|
|
|
|
* @param {object} nativeEventParam Object passed from native.
|
|
|
|
*/
|
2015-03-25 22:36:50 +00:00
|
|
|
_receiveRootNodeIDEvent: function(
|
|
|
|
rootNodeID: ?string,
|
|
|
|
topLevelType: string,
|
|
|
|
nativeEventParam: Object
|
|
|
|
) {
|
2015-01-30 01:10:49 +00:00
|
|
|
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
|
2015-05-08 16:45:43 +00:00
|
|
|
ReactNativeEventEmitter.handleTopLevel(
|
2015-01-30 01:10:49 +00:00
|
|
|
topLevelType,
|
|
|
|
rootNodeID,
|
|
|
|
rootNodeID,
|
2015-07-29 09:30:26 +00:00
|
|
|
nativeEvent,
|
|
|
|
nativeEvent.target
|
2015-01-30 01:10:49 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Publically exposed method on module for native objc to invoke when a top
|
|
|
|
* level event is extracted.
|
|
|
|
* @param {rootNodeID} rootNodeID React root node ID that event occured on.
|
|
|
|
* @param {TopLevelType} topLevelType Top level type of event.
|
|
|
|
* @param {object} nativeEventParam Object passed from native.
|
|
|
|
*/
|
2015-03-25 22:36:50 +00:00
|
|
|
receiveEvent: function(
|
|
|
|
tag: number,
|
|
|
|
topLevelType: string,
|
|
|
|
nativeEventParam: Object
|
|
|
|
) {
|
2015-05-08 16:45:43 +00:00
|
|
|
var rootNodeID = ReactNativeTagHandles.tagToRootNodeID[tag];
|
|
|
|
ReactNativeEventEmitter._receiveRootNodeIDEvent(
|
2015-01-30 01:10:49 +00:00
|
|
|
rootNodeID,
|
|
|
|
topLevelType,
|
|
|
|
nativeEventParam
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simple multi-wrapper around `receiveEvent` that is intended to receive an
|
|
|
|
* efficient representation of `Touch` objects, and other information that
|
|
|
|
* can be used to construct W3C compliant `Event` and `Touch` lists.
|
|
|
|
*
|
|
|
|
* This may create dispatch behavior that differs than web touch handling. We
|
|
|
|
* loop through each of the changed touches and receive it as a single event.
|
|
|
|
* So two `touchStart`/`touchMove`s that occur simultaneously are received as
|
|
|
|
* two separate touch event dispatches - when they arguably should be one.
|
|
|
|
*
|
|
|
|
* This implementation reuses the `Touch` objects themselves as the `Event`s
|
|
|
|
* since we dispatch an event for each touch (though that might not be spec
|
|
|
|
* compliant). The main purpose of reusing them is to save allocations.
|
|
|
|
*
|
|
|
|
* TODO: Dispatch multiple changed touches in one event. The bubble path
|
|
|
|
* could be the first common ancestor of all the `changedTouches`.
|
|
|
|
*
|
|
|
|
* One difference between this behavior and W3C spec: cancelled touches will
|
|
|
|
* not appear in `.touches`, or in any future `.touches`, though they may
|
|
|
|
* still be "actively touching the surface".
|
|
|
|
*
|
|
|
|
* Web desktop polyfills only need to construct a fake touch event with
|
|
|
|
* identifier 0, also abandoning traditional click handlers.
|
|
|
|
*/
|
2015-03-25 22:36:50 +00:00
|
|
|
receiveTouches: function(
|
|
|
|
eventTopLevelType: string,
|
|
|
|
touches: Array<Object>,
|
|
|
|
changedIndices: Array<number>
|
|
|
|
) {
|
2015-01-30 01:10:49 +00:00
|
|
|
var changedTouches =
|
|
|
|
eventTopLevelType === topLevelTypes.topTouchEnd ||
|
|
|
|
eventTopLevelType === topLevelTypes.topTouchCancel ?
|
|
|
|
removeTouchesAtIndices(touches, changedIndices) :
|
|
|
|
touchSubsequence(touches, changedIndices);
|
|
|
|
|
|
|
|
for (var jj = 0; jj < changedTouches.length; jj++) {
|
|
|
|
var touch = changedTouches[jj];
|
|
|
|
// Touch objects can fullfill the role of `DOM` `Event` objects if we set
|
|
|
|
// the `changedTouches`/`touches`. This saves allocations.
|
|
|
|
touch.changedTouches = changedTouches;
|
|
|
|
touch.touches = touches;
|
|
|
|
var nativeEvent = touch;
|
|
|
|
var rootNodeID = null;
|
|
|
|
var target = nativeEvent.target;
|
|
|
|
if (target !== null && target !== undefined) {
|
2015-05-08 16:45:43 +00:00
|
|
|
if (target < ReactNativeTagHandles.tagsStartAt) {
|
2015-01-30 01:10:49 +00:00
|
|
|
if (__DEV__) {
|
2015-02-19 01:39:09 +00:00
|
|
|
warning(
|
|
|
|
false,
|
|
|
|
'A view is reporting that a touch occured on tag zero.'
|
|
|
|
);
|
2015-01-30 01:10:49 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rootNodeID = NodeHandle.getRootNodeID(target);
|
|
|
|
}
|
|
|
|
}
|
2015-05-08 16:45:43 +00:00
|
|
|
ReactNativeEventEmitter._receiveRootNodeIDEvent(
|
2015-01-30 01:10:49 +00:00
|
|
|
rootNodeID,
|
|
|
|
eventTopLevelType,
|
|
|
|
nativeEvent
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-05-08 16:45:43 +00:00
|
|
|
module.exports = ReactNativeEventEmitter;
|