mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
2d069fc5e6
Resets currentTarget on the pooled event instead of adding an expando.
262 lines
7.7 KiB
JavaScript
262 lines
7.7 KiB
JavaScript
/**
|
|
* Copyright 2013-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 EventPluginUtils
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
var EventConstants = require('EventConstants');
|
|
var ReactErrorUtils = require('ReactErrorUtils');
|
|
|
|
var invariant = require('invariant');
|
|
var warning = require('warning');
|
|
|
|
/**
|
|
* Injected dependencies:
|
|
*/
|
|
|
|
/**
|
|
* - `ComponentTree`: [required] Module that can convert between React instances
|
|
* and actual node references.
|
|
*/
|
|
var ComponentTree;
|
|
var TreeTraversal;
|
|
var injection = {
|
|
injectComponentTree: function(Injected) {
|
|
ComponentTree = Injected;
|
|
if (__DEV__) {
|
|
warning(
|
|
Injected &&
|
|
Injected.getNodeFromInstance &&
|
|
Injected.getInstanceFromNode,
|
|
'EventPluginUtils.injection.injectComponentTree(...): Injected ' +
|
|
'module is missing getNodeFromInstance or getInstanceFromNode.'
|
|
);
|
|
}
|
|
},
|
|
injectTreeTraversal: function(Injected) {
|
|
TreeTraversal = Injected;
|
|
if (__DEV__) {
|
|
warning(
|
|
Injected && Injected.isAncestor && Injected.getLowestCommonAncestor,
|
|
'EventPluginUtils.injection.injectTreeTraversal(...): Injected ' +
|
|
'module is missing isAncestor or getLowestCommonAncestor.'
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
var topLevelTypes = EventConstants.topLevelTypes;
|
|
|
|
function isEndish(topLevelType) {
|
|
return topLevelType === topLevelTypes.topMouseUp ||
|
|
topLevelType === topLevelTypes.topTouchEnd ||
|
|
topLevelType === topLevelTypes.topTouchCancel;
|
|
}
|
|
|
|
function isMoveish(topLevelType) {
|
|
return topLevelType === topLevelTypes.topMouseMove ||
|
|
topLevelType === topLevelTypes.topTouchMove;
|
|
}
|
|
function isStartish(topLevelType) {
|
|
return topLevelType === topLevelTypes.topMouseDown ||
|
|
topLevelType === topLevelTypes.topTouchStart;
|
|
}
|
|
|
|
|
|
var validateEventDispatches;
|
|
if (__DEV__) {
|
|
validateEventDispatches = function(event) {
|
|
var dispatchListeners = event._dispatchListeners;
|
|
var dispatchInstances = event._dispatchInstances;
|
|
|
|
var listenersIsArr = Array.isArray(dispatchListeners);
|
|
var listenersLen = listenersIsArr ?
|
|
dispatchListeners.length :
|
|
dispatchListeners ? 1 : 0;
|
|
|
|
var instancesIsArr = Array.isArray(dispatchInstances);
|
|
var instancesLen = instancesIsArr ?
|
|
dispatchInstances.length :
|
|
dispatchInstances ? 1 : 0;
|
|
|
|
warning(
|
|
instancesIsArr === listenersIsArr && instancesLen === listenersLen,
|
|
'EventPluginUtils: Invalid `event`.'
|
|
);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Dispatch the event to the listener.
|
|
* @param {SyntheticEvent} event SyntheticEvent to handle
|
|
* @param {boolean} simulated If the event is simulated (changes exn behavior)
|
|
* @param {function} listener Application-level callback
|
|
* @param {*} inst Internal component instance
|
|
*/
|
|
function executeDispatch(event, simulated, listener, inst) {
|
|
var type = event.type || 'unknown-event';
|
|
event.currentTarget = EventPluginUtils.getNodeFromInstance(inst);
|
|
if (simulated) {
|
|
ReactErrorUtils.invokeGuardedCallbackWithCatch(
|
|
type,
|
|
listener,
|
|
event
|
|
);
|
|
} else {
|
|
ReactErrorUtils.invokeGuardedCallback(type, listener, event);
|
|
}
|
|
event.currentTarget = null;
|
|
}
|
|
|
|
/**
|
|
* Standard/simple iteration through an event's collected dispatches.
|
|
*/
|
|
function executeDispatchesInOrder(event, simulated) {
|
|
var dispatchListeners = event._dispatchListeners;
|
|
var dispatchInstances = event._dispatchInstances;
|
|
if (__DEV__) {
|
|
validateEventDispatches(event);
|
|
}
|
|
if (Array.isArray(dispatchListeners)) {
|
|
for (var i = 0; i < dispatchListeners.length; i++) {
|
|
if (event.isPropagationStopped()) {
|
|
break;
|
|
}
|
|
// Listeners and Instances are two parallel arrays that are always in sync.
|
|
executeDispatch(
|
|
event,
|
|
simulated,
|
|
dispatchListeners[i],
|
|
dispatchInstances[i]
|
|
);
|
|
}
|
|
} else if (dispatchListeners) {
|
|
executeDispatch(event, simulated, dispatchListeners, dispatchInstances);
|
|
}
|
|
event._dispatchListeners = null;
|
|
event._dispatchInstances = null;
|
|
}
|
|
|
|
/**
|
|
* Standard/simple iteration through an event's collected dispatches, but stops
|
|
* at the first dispatch execution returning true, and returns that id.
|
|
*
|
|
* @return {?string} id of the first dispatch execution who's listener returns
|
|
* true, or null if no listener returned true.
|
|
*/
|
|
function executeDispatchesInOrderStopAtTrueImpl(event) {
|
|
var dispatchListeners = event._dispatchListeners;
|
|
var dispatchInstances = event._dispatchInstances;
|
|
if (__DEV__) {
|
|
validateEventDispatches(event);
|
|
}
|
|
if (Array.isArray(dispatchListeners)) {
|
|
for (var i = 0; i < dispatchListeners.length; i++) {
|
|
if (event.isPropagationStopped()) {
|
|
break;
|
|
}
|
|
// Listeners and Instances are two parallel arrays that are always in sync.
|
|
if (dispatchListeners[i](event, dispatchInstances[i])) {
|
|
return dispatchInstances[i];
|
|
}
|
|
}
|
|
} else if (dispatchListeners) {
|
|
if (dispatchListeners(event, dispatchInstances)) {
|
|
return dispatchInstances;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @see executeDispatchesInOrderStopAtTrueImpl
|
|
*/
|
|
function executeDispatchesInOrderStopAtTrue(event) {
|
|
var ret = executeDispatchesInOrderStopAtTrueImpl(event);
|
|
event._dispatchInstances = null;
|
|
event._dispatchListeners = null;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Execution of a "direct" dispatch - there must be at most one dispatch
|
|
* accumulated on the event or it is considered an error. It doesn't really make
|
|
* sense for an event with multiple dispatches (bubbled) to keep track of the
|
|
* return values at each dispatch execution, but it does tend to make sense when
|
|
* dealing with "direct" dispatches.
|
|
*
|
|
* @return {*} The return value of executing the single dispatch.
|
|
*/
|
|
function executeDirectDispatch(event) {
|
|
if (__DEV__) {
|
|
validateEventDispatches(event);
|
|
}
|
|
var dispatchListener = event._dispatchListeners;
|
|
var dispatchInstance = event._dispatchInstances;
|
|
invariant(
|
|
!Array.isArray(dispatchListener),
|
|
'executeDirectDispatch(...): Invalid `event`.'
|
|
);
|
|
event.currentTarget = EventPluginUtils.getNodeFromInstance(dispatchInstance);
|
|
var res = dispatchListener ? dispatchListener(event) : null;
|
|
event.currentTarget = null;
|
|
event._dispatchListeners = null;
|
|
event._dispatchInstances = null;
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* @param {SyntheticEvent} event
|
|
* @return {boolean} True iff number of dispatches accumulated is greater than 0.
|
|
*/
|
|
function hasDispatches(event) {
|
|
return !!event._dispatchListeners;
|
|
}
|
|
|
|
/**
|
|
* General utilities that are useful in creating custom Event Plugins.
|
|
*/
|
|
var EventPluginUtils = {
|
|
isEndish: isEndish,
|
|
isMoveish: isMoveish,
|
|
isStartish: isStartish,
|
|
|
|
executeDirectDispatch: executeDirectDispatch,
|
|
executeDispatchesInOrder: executeDispatchesInOrder,
|
|
executeDispatchesInOrderStopAtTrue: executeDispatchesInOrderStopAtTrue,
|
|
hasDispatches: hasDispatches,
|
|
|
|
getInstanceFromNode: function(node) {
|
|
return ComponentTree.getInstanceFromNode(node);
|
|
},
|
|
getNodeFromInstance: function(node) {
|
|
return ComponentTree.getNodeFromInstance(node);
|
|
},
|
|
isAncestor: function(a, b) {
|
|
return TreeTraversal.isAncestor(a, b);
|
|
},
|
|
getLowestCommonAncestor: function(a, b) {
|
|
return TreeTraversal.getLowestCommonAncestor(a, b);
|
|
},
|
|
getParentInstance: function(inst) {
|
|
return TreeTraversal.getParentInstance(inst);
|
|
},
|
|
traverseTwoPhase: function(target, fn, arg) {
|
|
return TreeTraversal.traverseTwoPhase(target, fn, arg);
|
|
},
|
|
traverseEnterLeave: function(from, to, fn, argFrom, argTo) {
|
|
return TreeTraversal.traverseEnterLeave(from, to, fn, argFrom, argTo);
|
|
},
|
|
|
|
injection: injection,
|
|
};
|
|
|
|
module.exports = EventPluginUtils;
|