react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / browser / ui / ReactEventListener.js
81155 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule ReactEventListener9* @typechecks static-only10*/1112"use strict";1314var EventListener = require('EventListener');15var ExecutionEnvironment = require('ExecutionEnvironment');16var PooledClass = require('PooledClass');17var ReactInstanceHandles = require('ReactInstanceHandles');18var ReactMount = require('ReactMount');19var ReactUpdates = require('ReactUpdates');2021var assign = require('Object.assign');22var getEventTarget = require('getEventTarget');23var getUnboundedScrollPosition = require('getUnboundedScrollPosition');2425/**26* Finds the parent React component of `node`.27*28* @param {*} node29* @return {?DOMEventTarget} Parent container, or `null` if the specified node30* is not nested.31*/32function findParent(node) {33// TODO: It may be a good idea to cache this to prevent unnecessary DOM34// traversal, but caching is difficult to do correctly without using a35// mutation observer to listen for all DOM changes.36var nodeID = ReactMount.getID(node);37var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);38var container = ReactMount.findReactContainerForID(rootID);39var parent = ReactMount.getFirstReactDOM(container);40return parent;41}4243// Used to store ancestor hierarchy in top level callback44function TopLevelCallbackBookKeeping(topLevelType, nativeEvent) {45this.topLevelType = topLevelType;46this.nativeEvent = nativeEvent;47this.ancestors = [];48}49assign(TopLevelCallbackBookKeeping.prototype, {50destructor: function() {51this.topLevelType = null;52this.nativeEvent = null;53this.ancestors.length = 0;54}55});56PooledClass.addPoolingTo(57TopLevelCallbackBookKeeping,58PooledClass.twoArgumentPooler59);6061function handleTopLevelImpl(bookKeeping) {62var topLevelTarget = ReactMount.getFirstReactDOM(63getEventTarget(bookKeeping.nativeEvent)64) || window;6566// Loop through the hierarchy, in case there's any nested components.67// It's important that we build the array of ancestors before calling any68// event handlers, because event handlers can modify the DOM, leading to69// inconsistencies with ReactMount's node cache. See #1105.70var ancestor = topLevelTarget;71while (ancestor) {72bookKeeping.ancestors.push(ancestor);73ancestor = findParent(ancestor);74}7576for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {77topLevelTarget = bookKeeping.ancestors[i];78var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';79ReactEventListener._handleTopLevel(80bookKeeping.topLevelType,81topLevelTarget,82topLevelTargetID,83bookKeeping.nativeEvent84);85}86}8788function scrollValueMonitor(cb) {89var scrollPosition = getUnboundedScrollPosition(window);90cb(scrollPosition);91}9293var ReactEventListener = {94_enabled: true,95_handleTopLevel: null,9697WINDOW_HANDLE: ExecutionEnvironment.canUseDOM ? window : null,9899setHandleTopLevel: function(handleTopLevel) {100ReactEventListener._handleTopLevel = handleTopLevel;101},102103setEnabled: function(enabled) {104ReactEventListener._enabled = !!enabled;105},106107isEnabled: function() {108return ReactEventListener._enabled;109},110111112/**113* Traps top-level events by using event bubbling.114*115* @param {string} topLevelType Record from `EventConstants`.116* @param {string} handlerBaseName Event name (e.g. "click").117* @param {object} handle Element on which to attach listener.118* @return {object} An object with a remove function which will forcefully119* remove the listener.120* @internal121*/122trapBubbledEvent: function(topLevelType, handlerBaseName, handle) {123var element = handle;124if (!element) {125return;126}127return EventListener.listen(128element,129handlerBaseName,130ReactEventListener.dispatchEvent.bind(null, topLevelType)131);132},133134/**135* Traps a top-level event by using event capturing.136*137* @param {string} topLevelType Record from `EventConstants`.138* @param {string} handlerBaseName Event name (e.g. "click").139* @param {object} handle Element on which to attach listener.140* @return {object} An object with a remove function which will forcefully141* remove the listener.142* @internal143*/144trapCapturedEvent: function(topLevelType, handlerBaseName, handle) {145var element = handle;146if (!element) {147return;148}149return EventListener.capture(150element,151handlerBaseName,152ReactEventListener.dispatchEvent.bind(null, topLevelType)153);154},155156monitorScrollValue: function(refresh) {157var callback = scrollValueMonitor.bind(null, refresh);158EventListener.listen(window, 'scroll', callback);159EventListener.listen(window, 'resize', callback);160},161162dispatchEvent: function(topLevelType, nativeEvent) {163if (!ReactEventListener._enabled) {164return;165}166167var bookKeeping = TopLevelCallbackBookKeeping.getPooled(168topLevelType,169nativeEvent170);171try {172// Event queue being processed in the same cycle allows173// `preventDefault`.174ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);175} finally {176TopLevelCallbackBookKeeping.release(bookKeeping);177}178}179};180181module.exports = ReactEventListener;182183184