164 lines
5.4 KiB
JavaScript
164 lines
5.4 KiB
JavaScript
/**
|
|
* 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 EventPropagators
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var EventConstants = require('EventConstants');
|
|
var EventPluginHub = require('EventPluginHub');
|
|
|
|
var accumulateInto = require('accumulateInto');
|
|
var forEachAccumulated = require('forEachAccumulated');
|
|
|
|
var PropagationPhases = EventConstants.PropagationPhases;
|
|
var getListener = EventPluginHub.getListener;
|
|
|
|
/**
|
|
* Some event types have a notion of different registration names for different
|
|
* "phases" of propagation. This finds listeners by a given phase.
|
|
*/
|
|
function listenerAtPhase(id, event, propagationPhase) {
|
|
var registrationName =
|
|
event.dispatchConfig.phasedRegistrationNames[propagationPhase];
|
|
return getListener(id, registrationName);
|
|
}
|
|
|
|
/**
|
|
* Tags a `SyntheticEvent` with dispatched listeners. Creating this function
|
|
* here, allows us to not have to bind or create functions for each event.
|
|
* Mutating the event's members allows us to not have to create a wrapping
|
|
* "dispatch" object that pairs the event with the listener.
|
|
*/
|
|
function accumulateDirectionalDispatches(domID, upwards, event) {
|
|
if (__DEV__) {
|
|
if (!domID) {
|
|
throw new Error('Dispatching id must not be null');
|
|
}
|
|
}
|
|
var phase = upwards ? PropagationPhases.bubbled : PropagationPhases.captured;
|
|
var listener = listenerAtPhase(domID, event, phase);
|
|
if (listener) {
|
|
event._dispatchListeners =
|
|
accumulateInto(event._dispatchListeners, listener);
|
|
event._dispatchIDs = accumulateInto(event._dispatchIDs, domID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Collect dispatches (must be entirely collected before dispatching - see unit
|
|
* tests). Lazily allocate the array to conserve memory. We must loop through
|
|
* each event and perform the traversal for each one. We can not perform a
|
|
* single traversal for the entire collection of events because each event may
|
|
* have a different target.
|
|
*/
|
|
function accumulateTwoPhaseDispatchesSingle(event) {
|
|
if (event && event.dispatchConfig.phasedRegistrationNames) {
|
|
EventPluginHub.injection.getInstanceHandle().traverseTwoPhase(
|
|
event.dispatchMarker,
|
|
accumulateDirectionalDispatches,
|
|
event
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID.
|
|
*/
|
|
function accumulateTwoPhaseDispatchesSingleSkipTarget(event) {
|
|
if (event && event.dispatchConfig.phasedRegistrationNames) {
|
|
EventPluginHub.injection.getInstanceHandle().traverseTwoPhaseSkipTarget(
|
|
event.dispatchMarker,
|
|
accumulateDirectionalDispatches,
|
|
event
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Accumulates without regard to direction, does not look for phased
|
|
* registration names. Same as `accumulateDirectDispatchesSingle` but without
|
|
* requiring that the `dispatchMarker` be the same as the dispatched ID.
|
|
*/
|
|
function accumulateDispatches(id, ignoredDirection, event) {
|
|
if (event && event.dispatchConfig.registrationName) {
|
|
var registrationName = event.dispatchConfig.registrationName;
|
|
var listener = getListener(id, registrationName);
|
|
if (listener) {
|
|
event._dispatchListeners =
|
|
accumulateInto(event._dispatchListeners, listener);
|
|
event._dispatchIDs = accumulateInto(event._dispatchIDs, id);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accumulates dispatches on an `SyntheticEvent`, but only for the
|
|
* `dispatchMarker`.
|
|
* @param {SyntheticEvent} event
|
|
*/
|
|
function accumulateDirectDispatchesSingle(event) {
|
|
if (event && event.dispatchConfig.registrationName) {
|
|
accumulateDispatches(event.dispatchMarker, null, event);
|
|
}
|
|
}
|
|
|
|
function accumulateTwoPhaseDispatches(events) {
|
|
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
|
|
}
|
|
|
|
function accumulateTwoPhaseDispatchesSkipTarget(events) {
|
|
forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget);
|
|
}
|
|
|
|
function accumulateEnterLeaveDispatches(leave, enter, fromID, toID) {
|
|
EventPluginHub.injection.getInstanceHandle().traverseEnterLeave(
|
|
fromID,
|
|
toID,
|
|
accumulateDispatches,
|
|
leave,
|
|
enter
|
|
);
|
|
}
|
|
|
|
|
|
function accumulateDirectDispatches(events) {
|
|
forEachAccumulated(events, accumulateDirectDispatchesSingle);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* A small set of propagation patterns, each of which will accept a small amount
|
|
* of information, and generate a set of "dispatch ready event objects" - which
|
|
* are sets of events that have already been annotated with a set of dispatched
|
|
* listener functions/ids. The API is designed this way to discourage these
|
|
* propagation strategies from actually executing the dispatches, since we
|
|
* always want to collect the entire set of dispatches before executing event a
|
|
* single one.
|
|
*
|
|
* @constructor EventPropagators
|
|
*/
|
|
var EventPropagators = {
|
|
accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches,
|
|
accumulateTwoPhaseDispatchesSkipTarget: accumulateTwoPhaseDispatchesSkipTarget,
|
|
accumulateDirectDispatches: accumulateDirectDispatches,
|
|
accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches
|
|
};
|
|
|
|
module.exports = EventPropagators;
|