mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-29 04:35:36 +08:00
[react-packager][streamline oss] Move open sourced JS source to react-native-github
This commit is contained in:
599
Libraries/vendor/react/browser/eventPlugins/ResponderEventPlugin.js
vendored
Normal file
599
Libraries/vendor/react/browser/eventPlugins/ResponderEventPlugin.js
vendored
Normal file
@@ -0,0 +1,599 @@
|
||||
/**
|
||||
* Copyright 2013-2014 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ResponderEventPlugin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var EventConstants = require('EventConstants');
|
||||
var EventPluginUtils = require('EventPluginUtils');
|
||||
var EventPropagators = require('EventPropagators');
|
||||
var NodeHandle = require('NodeHandle');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
var ResponderSyntheticEvent = require('ResponderSyntheticEvent');
|
||||
var ResponderTouchHistoryStore = require('ResponderTouchHistoryStore');
|
||||
|
||||
var accumulate = require('accumulate');
|
||||
var invariant = require('invariant');
|
||||
var keyOf = require('keyOf');
|
||||
|
||||
var isStartish = EventPluginUtils.isStartish;
|
||||
var isMoveish = EventPluginUtils.isMoveish;
|
||||
var isEndish = EventPluginUtils.isEndish;
|
||||
var executeDirectDispatch = EventPluginUtils.executeDirectDispatch;
|
||||
var hasDispatches = EventPluginUtils.hasDispatches;
|
||||
var executeDispatchesInOrderStopAtTrue =
|
||||
EventPluginUtils.executeDispatchesInOrderStopAtTrue;
|
||||
|
||||
/**
|
||||
* ID of element that should respond to touch/move types of interactions, as
|
||||
* indicated explicitly by relevant callbacks.
|
||||
*/
|
||||
var responderID = null;
|
||||
|
||||
/**
|
||||
* Count of current touches. A textInput should become responder iff the
|
||||
* the selection changes while there is a touch on the screen.
|
||||
*/
|
||||
var trackedTouchCount = 0;
|
||||
|
||||
/**
|
||||
* Last reported number of active touches.
|
||||
*/
|
||||
var previousActiveTouches = 0;
|
||||
|
||||
var changeResponder = function(nextResponderID) {
|
||||
var oldResponderID = responderID;
|
||||
responderID = nextResponderID;
|
||||
if (ResponderEventPlugin.GlobalResponderHandler !== null) {
|
||||
ResponderEventPlugin.GlobalResponderHandler.onChange(
|
||||
oldResponderID,
|
||||
nextResponderID
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var eventTypes = {
|
||||
/**
|
||||
* On a `touchStart`/`mouseDown`, is it desired that this element become the
|
||||
* responder?
|
||||
*/
|
||||
startShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: keyOf({onStartShouldSetResponder: null}),
|
||||
captured: keyOf({onStartShouldSetResponderCapture: null})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On a `scroll`, is it desired that this element become the responder? This
|
||||
* is usually not needed, but should be used to retroactively infer that a
|
||||
* `touchStart` had occured during momentum scroll. During a momentum scroll,
|
||||
* a touch start will be immediately followed by a scroll event if the view is
|
||||
* currently scrolling.
|
||||
*
|
||||
* TODO: This shouldn't bubble.
|
||||
*/
|
||||
scrollShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: keyOf({onScrollShouldSetResponder: null}),
|
||||
captured: keyOf({onScrollShouldSetResponderCapture: null})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On text selection change, should this element become the responder? This
|
||||
* is needed for text inputs or other views with native selection, so the
|
||||
* JS view can claim the responder.
|
||||
*
|
||||
* TODO: This shouldn't bubble.
|
||||
*/
|
||||
selectionChangeShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: keyOf({onSelectionChangeShouldSetResponder: null}),
|
||||
captured: keyOf({onSelectionChangeShouldSetResponderCapture: null})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On a `touchMove`/`mouseMove`, is it desired that this element become the
|
||||
* responder?
|
||||
*/
|
||||
moveShouldSetResponder: {
|
||||
phasedRegistrationNames: {
|
||||
bubbled: keyOf({onMoveShouldSetResponder: null}),
|
||||
captured: keyOf({onMoveShouldSetResponderCapture: null})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Direct responder events dispatched directly to responder. Do not bubble.
|
||||
*/
|
||||
responderStart: {registrationName: keyOf({onResponderStart: null})},
|
||||
responderMove: {registrationName: keyOf({onResponderMove: null})},
|
||||
responderEnd: {registrationName: keyOf({onResponderEnd: null})},
|
||||
responderRelease: {registrationName: keyOf({onResponderRelease: null})},
|
||||
responderTerminationRequest: {
|
||||
registrationName: keyOf({onResponderTerminationRequest: null})
|
||||
},
|
||||
responderGrant: {registrationName: keyOf({onResponderGrant: null})},
|
||||
responderReject: {registrationName: keyOf({onResponderReject: null})},
|
||||
responderTerminate: {registrationName: keyOf({onResponderTerminate: null})}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Responder System:
|
||||
* ----------------
|
||||
*
|
||||
* - A global, solitary "interaction lock" on a view.
|
||||
* - If a `NodeHandle` becomes the responder, it should convey visual feedback
|
||||
* immediately to indicate so, either by highlighting or moving accordingly.
|
||||
* - To be the responder means, that touches are exclusively important to that
|
||||
* responder view, and no other view.
|
||||
* - While touches are still occuring, the responder lock can be transfered to
|
||||
* a new view, but only to increasingly "higher" views (meaning ancestors of
|
||||
* the current responder).
|
||||
*
|
||||
* Responder being granted:
|
||||
* ------------------------
|
||||
*
|
||||
* - Touch starts, moves, and scrolls can cause an ID to become the responder.
|
||||
* - We capture/bubble `startShouldSetResponder`/`moveShouldSetResponder` to
|
||||
* the "appropriate place".
|
||||
* - If nothing is currently the responder, the "appropriate place" is the
|
||||
* initiating event's `targetID`.
|
||||
* - If something *is* already the responder, the "appropriate place" is the
|
||||
* first common ancestor of the event target and the current `responderID`.
|
||||
* - Some negotiation happens: See the timing diagram below.
|
||||
* - Scrolled views automatically become responder. The reasoning is that a
|
||||
* platform scroll view that isn't built on top of the responder system has
|
||||
* began scrolling, and the active responder must now be notified that the
|
||||
* interaction is no longer locked to it - the system has taken over.
|
||||
*
|
||||
* - Responder being released:
|
||||
* As soon as no more touches that *started* inside of descendents of the
|
||||
* *current* responderID, an `onResponderRelease` event is dispatched to the
|
||||
* current responder, and the responder lock is released.
|
||||
*
|
||||
* TODO:
|
||||
* - on "end", a callback hook for `onResponderEndShouldRemainResponder` that
|
||||
* determines if the responder lock should remain.
|
||||
* - If a view shouldn't "remain" the responder, any active touches should by
|
||||
* default be considered "dead" and do not influence future negotiations or
|
||||
* bubble paths. It should be as if those touches do not exist.
|
||||
* -- For multitouch: Usually a translate-z will choose to "remain" responder
|
||||
* after one out of many touches ended. For translate-y, usually the view
|
||||
* doesn't wish to "remain" responder after one of many touches end.
|
||||
* - Consider building this on top of a `stopPropagation` model similar to
|
||||
* `W3C` events.
|
||||
* - Ensure that `onResponderTerminate` is called on touch cancels, whether or
|
||||
* not `onResponderTerminationRequest` returns `true` or `false`.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Negotiation Performed
|
||||
+-----------------------+
|
||||
/ \
|
||||
Process low level events to + Current Responder + wantsResponderID
|
||||
determine who to perform negot-| (if any exists at all) |
|
||||
iation/transition | Otherwise just pass through|
|
||||
-------------------------------+----------------------------+------------------+
|
||||
Bubble to find first ID | |
|
||||
to return true:wantsResponderID| |
|
||||
| |
|
||||
+-------------+ | |
|
||||
| onTouchStart| | |
|
||||
+------+------+ none | |
|
||||
| return| |
|
||||
+-----------v-------------+true| +------------------------+ |
|
||||
|onStartShouldSetResponder|----->|onResponderStart (cur) |<-----------+
|
||||
+-----------+-------------+ | +------------------------+ | |
|
||||
| | | +--------+-------+
|
||||
| returned true for| false:REJECT +-------->|onResponderReject
|
||||
| wantsResponderID | | | +----------------+
|
||||
| (now attempt | +------------------+-----+ |
|
||||
| handoff) | | onResponder | |
|
||||
+------------------->| TerminationRequest| |
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| true:GRANT +-------->|onResponderGrant|
|
||||
| | +--------+-------+
|
||||
| +------------------------+ | |
|
||||
| | onResponderTerminate |<-----------+
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| +-------->|onResponderStart|
|
||||
| | +----------------+
|
||||
Bubble to find first ID | |
|
||||
to return true:wantsResponderID| |
|
||||
| |
|
||||
+-------------+ | |
|
||||
| onTouchMove | | |
|
||||
+------+------+ none | |
|
||||
| return| |
|
||||
+-----------v-------------+true| +------------------------+ |
|
||||
|onMoveShouldSetResponder |----->|onResponderMove (cur) |<-----------+
|
||||
+-----------+-------------+ | +------------------------+ | |
|
||||
| | | +--------+-------+
|
||||
| returned true for| false:REJECT +-------->|onResponderRejec|
|
||||
| wantsResponderID | | | +----------------+
|
||||
| (now attempt | +------------------+-----+ |
|
||||
| handoff) | | onResponder | |
|
||||
+------------------->| TerminationRequest| |
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| true:GRANT +-------->|onResponderGrant|
|
||||
| | +--------+-------+
|
||||
| +------------------------+ | |
|
||||
| | onResponderTerminate |<-----------+
|
||||
| +------------------+-----+ |
|
||||
| | | +----------------+
|
||||
| +-------->|onResponderMove |
|
||||
| | +----------------+
|
||||
| |
|
||||
| |
|
||||
Some active touch started| |
|
||||
inside current responder | +------------------------+ |
|
||||
+------------------------->| onResponderEnd | |
|
||||
| | +------------------------+ |
|
||||
+---+---------+ | |
|
||||
| onTouchEnd | | |
|
||||
+---+---------+ | |
|
||||
| | +------------------------+ |
|
||||
+------------------------->| onResponderEnd | |
|
||||
No active touches started| +-----------+------------+ |
|
||||
inside current responder | | |
|
||||
| v |
|
||||
| +------------------------+ |
|
||||
| | onResponderRelease | |
|
||||
| +------------------------+ |
|
||||
| |
|
||||
+ + */
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A note about event ordering in the `EventPluginHub`.
|
||||
*
|
||||
* Suppose plugins are injected in the following order:
|
||||
*
|
||||
* `[R, S, C]`
|
||||
*
|
||||
* To help illustrate the example, assume `S` is `SimpleEventPlugin` (for
|
||||
* `onClick` etc) and `R` is `ResponderEventPlugin`.
|
||||
*
|
||||
* "Deferred-Dispatched Events":
|
||||
*
|
||||
* - The current event plugin system will traverse the list of injected plugins,
|
||||
* in order, and extract events by collecting the plugin's return value of
|
||||
* `extractEvents()`.
|
||||
* - These events that are returned from `extractEvents` are "deferred
|
||||
* dispatched events".
|
||||
* - When returned from `extractEvents`, deferred-dispatched events contain an
|
||||
* "accumulation" of deferred dispatches.
|
||||
* - These deferred dispatches are accumulated/collected before they are
|
||||
* returned, but processed at a later time by the `EventPluginHub` (hence the
|
||||
* name deferred).
|
||||
*
|
||||
* In the process of returning their deferred-dispatched events, event plugins
|
||||
* themselves can dispatch events on-demand without returning them from
|
||||
* `extractEvents`. Plugins might want to do this, so that they can use event
|
||||
* dispatching as a tool that helps them decide which events should be extracted
|
||||
* in the first place.
|
||||
*
|
||||
* "On-Demand-Dispatched Events":
|
||||
*
|
||||
* - On-demand-dispatched events are not returned from `extractEvents`.
|
||||
* - On-demand-dispatched events are dispatched during the process of returning
|
||||
* the deferred-dispatched events.
|
||||
* - They should not have side effects.
|
||||
* - They should be avoided, and/or eventually be replaced with another
|
||||
* abstraction that allows event plugins to perform multiple "rounds" of event
|
||||
* extraction.
|
||||
*
|
||||
* Therefore, the sequence of event dispatches becomes:
|
||||
*
|
||||
* - `R`s on-demand events (if any) (dispatched by `R` on-demand)
|
||||
* - `S`s on-demand events (if any) (dispatched by `S` on-demand)
|
||||
* - `C`s on-demand events (if any) (dispatched by `C` on-demand)
|
||||
* - `R`s extracted events (if any) (dispatched by `EventPluginHub`)
|
||||
* - `S`s extracted events (if any) (dispatched by `EventPluginHub`)
|
||||
* - `C`s extracted events (if any) (dispatched by `EventPluginHub`)
|
||||
*
|
||||
* In the case of `ResponderEventPlugin`: If the `startShouldSetResponder`
|
||||
* on-demand dispatch returns `true` (and some other details are satisfied) the
|
||||
* `onResponderGrant` deferred dispatched event is returned from
|
||||
* `extractEvents`. The sequence of dispatch executions in this case
|
||||
* will appear as follows:
|
||||
*
|
||||
* - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand)
|
||||
* - `touchStartCapture` (`EventPluginHub` dispatches as usual)
|
||||
* - `touchStart` (`EventPluginHub` dispatches as usual)
|
||||
* - `responderGrant/Reject` (`EventPluginHub` dispatches as usual)
|
||||
*
|
||||
* @param {string} topLevelType Record from `EventConstants`.
|
||||
* @param {string} topLevelTargetID ID of deepest React rendered element.
|
||||
* @param {object} nativeEvent Native browser event.
|
||||
* @return {*} An accumulation of synthetic events.
|
||||
*/
|
||||
function setResponderAndExtractTransfer(
|
||||
topLevelType,
|
||||
topLevelTargetID,
|
||||
nativeEvent) {
|
||||
var shouldSetEventType =
|
||||
isStartish(topLevelType) ? eventTypes.startShouldSetResponder :
|
||||
isMoveish(topLevelType) ? eventTypes.moveShouldSetResponder :
|
||||
topLevelType === EventConstants.topLevelTypes.topSelectionChange ?
|
||||
eventTypes.selectionChangeShouldSetResponder :
|
||||
eventTypes.scrollShouldSetResponder;
|
||||
|
||||
// TODO: stop one short of the the current responder.
|
||||
var bubbleShouldSetFrom = !responderID ?
|
||||
topLevelTargetID :
|
||||
ReactInstanceHandles._getFirstCommonAncestorID(responderID, topLevelTargetID);
|
||||
|
||||
// When capturing/bubbling the "shouldSet" event, we want to skip the target
|
||||
// (deepest ID) if it happens to be the current responder. The reasoning:
|
||||
// It's strange to get an `onMoveShouldSetResponder` when you're *already*
|
||||
// the responder.
|
||||
var skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderID;
|
||||
var shouldSetEvent = ResponderSyntheticEvent.getPooled(
|
||||
shouldSetEventType,
|
||||
bubbleShouldSetFrom,
|
||||
nativeEvent
|
||||
);
|
||||
shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
if (skipOverBubbleShouldSetFrom) {
|
||||
EventPropagators.accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent);
|
||||
} else {
|
||||
EventPropagators.accumulateTwoPhaseDispatches(shouldSetEvent);
|
||||
}
|
||||
var wantsResponderID = executeDispatchesInOrderStopAtTrue(shouldSetEvent);
|
||||
if (!shouldSetEvent.isPersistent()) {
|
||||
shouldSetEvent.constructor.release(shouldSetEvent);
|
||||
}
|
||||
|
||||
if (!wantsResponderID || wantsResponderID === responderID) {
|
||||
return null;
|
||||
}
|
||||
var extracted;
|
||||
var grantEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderGrant,
|
||||
wantsResponderID,
|
||||
nativeEvent
|
||||
);
|
||||
grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
|
||||
EventPropagators.accumulateDirectDispatches(grantEvent);
|
||||
if (responderID) {
|
||||
|
||||
var terminationRequestEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderTerminationRequest,
|
||||
responderID,
|
||||
nativeEvent
|
||||
);
|
||||
terminationRequestEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(terminationRequestEvent);
|
||||
var shouldSwitch = !hasDispatches(terminationRequestEvent) ||
|
||||
executeDirectDispatch(terminationRequestEvent);
|
||||
if (!terminationRequestEvent.isPersistent()) {
|
||||
terminationRequestEvent.constructor.release(terminationRequestEvent);
|
||||
}
|
||||
|
||||
if (shouldSwitch) {
|
||||
var terminateType = eventTypes.responderTerminate;
|
||||
var terminateEvent = ResponderSyntheticEvent.getPooled(
|
||||
terminateType,
|
||||
responderID,
|
||||
nativeEvent
|
||||
);
|
||||
terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(terminateEvent);
|
||||
extracted = accumulate(extracted, [grantEvent, terminateEvent]);
|
||||
changeResponder(wantsResponderID);
|
||||
} else {
|
||||
var rejectEvent = ResponderSyntheticEvent.getPooled(
|
||||
eventTypes.responderReject,
|
||||
wantsResponderID,
|
||||
nativeEvent
|
||||
);
|
||||
rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(rejectEvent);
|
||||
extracted = accumulate(extracted, rejectEvent);
|
||||
}
|
||||
} else {
|
||||
extracted = accumulate(extracted, grantEvent);
|
||||
changeResponder(wantsResponderID);
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
|
||||
/**
|
||||
* A transfer is a negotiation between a currently set responder and the next
|
||||
* element to claim responder status. Any start event could trigger a transfer
|
||||
* of responderID. Any move event could trigger a transfer.
|
||||
*
|
||||
* @param {string} topLevelType Record from `EventConstants`.
|
||||
* @return {boolean} True if a transfer of responder could possibly occur.
|
||||
*/
|
||||
function canTriggerTransfer(topLevelType, topLevelTargetID) {
|
||||
return topLevelTargetID && (
|
||||
topLevelType === EventConstants.topLevelTypes.topScroll ||
|
||||
(trackedTouchCount > 0 &&
|
||||
topLevelType === EventConstants.topLevelTypes.topSelectionChange) ||
|
||||
isStartish(topLevelType) ||
|
||||
isMoveish(topLevelType)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this touch end event makes it such that there are no
|
||||
* longer any touches that started inside of the current `responderID`.
|
||||
*
|
||||
* @param {NativeEvent} nativeEvent Native touch end event.
|
||||
* @return {bool} Whether or not this touch end event ends the responder.
|
||||
*/
|
||||
function noResponderTouches(nativeEvent) {
|
||||
var touches = nativeEvent.touches;
|
||||
if (!touches || touches.length === 0) {
|
||||
return true;
|
||||
}
|
||||
for (var i = 0; i < touches.length; i++) {
|
||||
var activeTouch = touches[i];
|
||||
var target = activeTouch.target;
|
||||
if (target !== null && target !== undefined && target !== 0) {
|
||||
// Is the original touch location inside of the current responder?
|
||||
var commonAncestor =
|
||||
ReactInstanceHandles._getFirstCommonAncestorID(
|
||||
responderID,
|
||||
NodeHandle.getRootNodeID(target)
|
||||
);
|
||||
if (commonAncestor === responderID) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
var ResponderEventPlugin = {
|
||||
|
||||
getResponderID: function() {
|
||||
return responderID;
|
||||
},
|
||||
|
||||
eventTypes: eventTypes,
|
||||
|
||||
/**
|
||||
* We must be resilient to `topLevelTargetID` being `undefined` on
|
||||
* `touchMove`, or `touchEnd`. On certain platforms, this means that a native
|
||||
* scroll has assumed control and the original touch targets are destroyed.
|
||||
*
|
||||
* @param {string} topLevelType Record from `EventConstants`.
|
||||
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
||||
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
||||
* @param {object} nativeEvent Native browser event.
|
||||
* @return {*} An accumulation of synthetic events.
|
||||
* @see {EventPluginHub.extractEvents}
|
||||
*/
|
||||
extractEvents: function(
|
||||
topLevelType,
|
||||
topLevelTarget,
|
||||
topLevelTargetID,
|
||||
nativeEvent) {
|
||||
|
||||
if (isStartish(topLevelType)) {
|
||||
trackedTouchCount += 1;
|
||||
} else if (isEndish(topLevelType)) {
|
||||
trackedTouchCount -= 1;
|
||||
invariant(
|
||||
trackedTouchCount >= 0,
|
||||
'Ended a touch event which was not counted in trackedTouchCount.'
|
||||
);
|
||||
}
|
||||
|
||||
ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent);
|
||||
|
||||
var extracted = canTriggerTransfer(topLevelType, topLevelTargetID) ?
|
||||
setResponderAndExtractTransfer(topLevelType, topLevelTargetID, nativeEvent) :
|
||||
null;
|
||||
// Responder may or may not have transfered on a new touch start/move.
|
||||
// Regardless, whoever is the responder after any potential transfer, we
|
||||
// direct all touch start/move/ends to them in the form of
|
||||
// `onResponderMove/Start/End`. These will be called for *every* additional
|
||||
// finger that move/start/end, dispatched directly to whoever is the
|
||||
// current responder at that moment, until the responder is "released".
|
||||
//
|
||||
// These multiple individual change touch events are are always bookended
|
||||
// by `onResponderGrant`, and one of
|
||||
// (`onResponderRelease/onResponderTerminate`).
|
||||
var isResponderTouchStart = responderID && isStartish(topLevelType);
|
||||
var isResponderTouchMove = responderID && isMoveish(topLevelType);
|
||||
var isResponderTouchEnd = responderID && isEndish(topLevelType);
|
||||
var incrementalTouch =
|
||||
isResponderTouchStart ? eventTypes.responderStart :
|
||||
isResponderTouchMove ? eventTypes.responderMove :
|
||||
isResponderTouchEnd ? eventTypes.responderEnd :
|
||||
null;
|
||||
|
||||
if (incrementalTouch) {
|
||||
var gesture =
|
||||
ResponderSyntheticEvent.getPooled(incrementalTouch, responderID, nativeEvent);
|
||||
gesture.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(gesture);
|
||||
extracted = accumulate(extracted, gesture);
|
||||
}
|
||||
|
||||
var isResponderTerminate =
|
||||
responderID &&
|
||||
topLevelType === EventConstants.topLevelTypes.topTouchCancel;
|
||||
var isResponderRelease =
|
||||
responderID &&
|
||||
!isResponderTerminate &&
|
||||
isEndish(topLevelType) &&
|
||||
noResponderTouches(nativeEvent);
|
||||
var finalTouch =
|
||||
isResponderTerminate ? eventTypes.responderTerminate :
|
||||
isResponderRelease ? eventTypes.responderRelease :
|
||||
null;
|
||||
if (finalTouch) {
|
||||
var finalEvent =
|
||||
ResponderSyntheticEvent.getPooled(finalTouch, responderID, nativeEvent);
|
||||
finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory;
|
||||
EventPropagators.accumulateDirectDispatches(finalEvent);
|
||||
extracted = accumulate(extracted, finalEvent);
|
||||
changeResponder(null);
|
||||
}
|
||||
|
||||
var numberActiveTouches =
|
||||
ResponderTouchHistoryStore.touchHistory.numberActiveTouches;
|
||||
if (ResponderEventPlugin.GlobalInteractionHandler &&
|
||||
numberActiveTouches !== previousActiveTouches) {
|
||||
ResponderEventPlugin.GlobalInteractionHandler.onChange(
|
||||
numberActiveTouches
|
||||
);
|
||||
}
|
||||
previousActiveTouches = numberActiveTouches;
|
||||
|
||||
return extracted;
|
||||
},
|
||||
|
||||
GlobalResponderHandler: null,
|
||||
GlobalInteractionHandler: null,
|
||||
|
||||
injection: {
|
||||
/**
|
||||
* @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler
|
||||
* Object that handles any change in responder. Use this to inject
|
||||
* integration with an existing touch handling system etc.
|
||||
*/
|
||||
injectGlobalResponderHandler: function(GlobalResponderHandler) {
|
||||
ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {{onChange: (numberActiveTouches) => void} GlobalInteractionHandler
|
||||
* Object that handles any change in the number of active touches.
|
||||
*/
|
||||
injectGlobalInteractionHandler: function(GlobalInteractionHandler) {
|
||||
ResponderEventPlugin.GlobalInteractionHandler = GlobalInteractionHandler;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ResponderEventPlugin;
|
||||
47
Libraries/vendor/react/browser/eventPlugins/ResponderSyntheticEvent.js
vendored
Normal file
47
Libraries/vendor/react/browser/eventPlugins/ResponderSyntheticEvent.js
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright 2013-2014 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ResponderSyntheticEvent
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var SyntheticEvent = require('SyntheticEvent');
|
||||
|
||||
/**
|
||||
* `touchHistory` isn't actually on the native event, but putting it in the
|
||||
* interface will ensure that it is cleaned up when pooled/destroyed. The
|
||||
* `ResponderEventPlugin` will populate it appropriately.
|
||||
*/
|
||||
var ResponderEventInterface = {
|
||||
touchHistory: function(nativeEvent) {
|
||||
return null; // Actually doesn't even look at the native event.
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {object} dispatchConfig Configuration used to dispatch this event.
|
||||
* @param {string} dispatchMarker Marker identifying the event target.
|
||||
* @param {object} nativeEvent Native event.
|
||||
* @extends {SyntheticEvent}
|
||||
*/
|
||||
function ResponderSyntheticEvent(dispatchConfig, dispatchMarker, nativeEvent) {
|
||||
SyntheticEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent);
|
||||
}
|
||||
|
||||
SyntheticEvent.augmentClass(ResponderSyntheticEvent, ResponderEventInterface);
|
||||
|
||||
module.exports = ResponderSyntheticEvent;
|
||||
171
Libraries/vendor/react/browser/eventPlugins/ResponderTouchHistoryStore.js
vendored
Normal file
171
Libraries/vendor/react/browser/eventPlugins/ResponderTouchHistoryStore.js
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @providesModule ResponderTouchHistoryStore
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var EventPluginUtils = require('EventPluginUtils');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var isMoveish = EventPluginUtils.isMoveish;
|
||||
var isStartish = EventPluginUtils.isStartish;
|
||||
var isEndish = EventPluginUtils.isEndish;
|
||||
|
||||
var MAX_TOUCH_BANK = 20;
|
||||
|
||||
/**
|
||||
* Touch position/time tracking information by touchID. Typically, we'll only
|
||||
* see IDs with a range of 1-20 (they are recycled when touches end and then
|
||||
* start again). This data is commonly needed by many different interaction
|
||||
* logic modules so precomputing it is very helpful to do once.
|
||||
* Each touch object in `touchBank` is of the following form:
|
||||
* { touchActive: boolean,
|
||||
* startTimeStamp: number,
|
||||
* startPageX: number,
|
||||
* startPageY: number,
|
||||
* currentPageX: number,
|
||||
* currentPageY: number,
|
||||
* currentTimeStamp: number
|
||||
* }
|
||||
*/
|
||||
var touchHistory = {
|
||||
touchBank: [ ],
|
||||
numberActiveTouches: 0,
|
||||
// If there is only one active touch, we remember its location. This prevents
|
||||
// us having to loop through all of the touches all the time in the most
|
||||
// common case.
|
||||
indexOfSingleActiveTouch: -1,
|
||||
mostRecentTimeStamp: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* TODO: Instead of making gestures recompute filtered velocity, we could
|
||||
* include a built in velocity computation that can be reused globally.
|
||||
* @param {Touch} touch Native touch object.
|
||||
*/
|
||||
var initializeTouchData = function(touch) {
|
||||
return {
|
||||
touchActive: true,
|
||||
startTimeStamp: touch.timeStamp,
|
||||
startPageX: touch.pageX,
|
||||
startPageY: touch.pageY,
|
||||
currentPageX: touch.pageX,
|
||||
currentPageY: touch.pageY,
|
||||
currentTimeStamp: touch.timeStamp,
|
||||
previousPageX: touch.pageX,
|
||||
previousPageY: touch.pageY,
|
||||
previousTimeStamp: touch.timeStamp,
|
||||
};
|
||||
};
|
||||
|
||||
var reinitializeTouchTrack = function(touchTrack, touch) {
|
||||
touchTrack.touchActive = true;
|
||||
touchTrack.startTimeStamp = touch.timeStamp;
|
||||
touchTrack.startPageX = touch.pageX;
|
||||
touchTrack.startPageY = touch.pageY;
|
||||
touchTrack.currentPageX = touch.pageX;
|
||||
touchTrack.currentPageY = touch.pageY;
|
||||
touchTrack.currentTimeStamp = touch.timeStamp;
|
||||
touchTrack.previousPageX = touch.pageX;
|
||||
touchTrack.previousPageY = touch.pageY;
|
||||
touchTrack.previousTimeStamp = touch.timeStamp;
|
||||
};
|
||||
|
||||
var validateTouch = function(touch) {
|
||||
var identifier = touch.identifier;
|
||||
invariant(identifier != null, 'Touch object is missing identifier');
|
||||
if (identifier > MAX_TOUCH_BANK) {
|
||||
console.warn(
|
||||
'Touch identifier ' + identifier + ' is greater than maximum ' +
|
||||
'supported ' + MAX_TOUCH_BANK + ' which causes performance issues ' +
|
||||
'backfilling array locations for all of the indices.'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var recordStartTouchData = function(touch) {
|
||||
var touchBank = touchHistory.touchBank;
|
||||
var identifier = touch.identifier;
|
||||
var touchTrack = touchBank[identifier];
|
||||
if (__DEV__) {
|
||||
validateTouch(touch);
|
||||
}
|
||||
if (!touchTrack) {
|
||||
touchBank[touch.identifier] = initializeTouchData(touch);
|
||||
} else {
|
||||
reinitializeTouchTrack(touchTrack, touch);
|
||||
}
|
||||
touchHistory.mostRecentTimeStamp = touch.timeStamp;
|
||||
};
|
||||
|
||||
var recordMoveTouchData = function(touch) {
|
||||
var touchBank = touchHistory.touchBank;
|
||||
var touchTrack = touchBank[touch.identifier];
|
||||
if (__DEV__) {
|
||||
validateTouch(touch);
|
||||
invariant(touchTrack, 'Touch data should have been recorded on start');
|
||||
}
|
||||
touchTrack.touchActive = true;
|
||||
touchTrack.previousPageX = touchTrack.currentPageX;
|
||||
touchTrack.previousPageY = touchTrack.currentPageY;
|
||||
touchTrack.previousTimeStamp = touchTrack.currentTimeStamp;
|
||||
touchTrack.currentPageX = touch.pageX;
|
||||
touchTrack.currentPageY = touch.pageY;
|
||||
touchTrack.currentTimeStamp = touch.timeStamp;
|
||||
touchHistory.mostRecentTimeStamp = touch.timeStamp;
|
||||
};
|
||||
|
||||
var recordEndTouchData = function(touch) {
|
||||
var touchBank = touchHistory.touchBank;
|
||||
var touchTrack = touchBank[touch.identifier];
|
||||
if (__DEV__) {
|
||||
validateTouch(touch);
|
||||
invariant(touchTrack, 'Touch data should have been recorded on start');
|
||||
}
|
||||
touchTrack.previousPageX = touchTrack.currentPageX;
|
||||
touchTrack.previousPageY = touchTrack.currentPageY;
|
||||
touchTrack.previousTimeStamp = touchTrack.currentTimeStamp;
|
||||
touchTrack.currentPageX = touch.pageX;
|
||||
touchTrack.currentPageY = touch.pageY;
|
||||
touchTrack.currentTimeStamp = touch.timeStamp;
|
||||
touchTrack.touchActive = false;
|
||||
touchHistory.mostRecentTimeStamp = touch.timeStamp;
|
||||
};
|
||||
|
||||
var ResponderTouchHistoryStore = {
|
||||
recordTouchTrack: function(topLevelType, nativeEvent) {
|
||||
var touchBank = touchHistory.touchBank;
|
||||
if (isMoveish(topLevelType)) {
|
||||
nativeEvent.changedTouches.forEach(recordMoveTouchData);
|
||||
} else if (isStartish(topLevelType)) {
|
||||
nativeEvent.changedTouches.forEach(recordStartTouchData);
|
||||
touchHistory.numberActiveTouches = nativeEvent.touches.length;
|
||||
if (touchHistory.numberActiveTouches === 1) {
|
||||
touchHistory.indexOfSingleActiveTouch = nativeEvent.touches[0].identifier;
|
||||
}
|
||||
} else if (isEndish(topLevelType)) {
|
||||
nativeEvent.changedTouches.forEach(recordEndTouchData);
|
||||
touchHistory.numberActiveTouches = nativeEvent.touches.length;
|
||||
if (touchHistory.numberActiveTouches === 1) {
|
||||
for (var i = 0; i < touchBank.length; i++) {
|
||||
var touchTrackToCheck = touchBank[i];
|
||||
if (touchTrackToCheck != null && touchTrackToCheck.touchActive) {
|
||||
touchHistory.indexOfSingleActiveTouch = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (__DEV__) {
|
||||
var activeTouchData = touchBank[touchHistory.indexOfSingleActiveTouch];
|
||||
var foundActive = activeTouchData != null && !!activeTouchData.touchActive;
|
||||
invariant(foundActive, 'Cannot find single active touch');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
touchHistory: touchHistory,
|
||||
};
|
||||
|
||||
|
||||
module.exports = ResponderTouchHistoryStore;
|
||||
Reference in New Issue
Block a user