UNPKG

7.97 kBJavaScriptView Raw
1/**
2 * Copyright 2013-present, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
8 *
9 *
10 */
11
12'use strict';
13
14var _prodInvariant = require('./reactProdInvariant');
15
16var EventListener = require('fbjs/lib/EventListener');
17var EventPropagators = require('./EventPropagators');
18var ReactDOMComponentTree = require('./ReactDOMComponentTree');
19var SyntheticAnimationEvent = require('./SyntheticAnimationEvent');
20var SyntheticClipboardEvent = require('./SyntheticClipboardEvent');
21var SyntheticEvent = require('./SyntheticEvent');
22var SyntheticFocusEvent = require('./SyntheticFocusEvent');
23var SyntheticKeyboardEvent = require('./SyntheticKeyboardEvent');
24var SyntheticMouseEvent = require('./SyntheticMouseEvent');
25var SyntheticDragEvent = require('./SyntheticDragEvent');
26var SyntheticTouchEvent = require('./SyntheticTouchEvent');
27var SyntheticTransitionEvent = require('./SyntheticTransitionEvent');
28var SyntheticUIEvent = require('./SyntheticUIEvent');
29var SyntheticWheelEvent = require('./SyntheticWheelEvent');
30
31var emptyFunction = require('fbjs/lib/emptyFunction');
32var getEventCharCode = require('./getEventCharCode');
33var invariant = require('fbjs/lib/invariant');
34
35/**
36 * Turns
37 * ['abort', ...]
38 * into
39 * eventTypes = {
40 * 'abort': {
41 * phasedRegistrationNames: {
42 * bubbled: 'onAbort',
43 * captured: 'onAbortCapture',
44 * },
45 * dependencies: ['topAbort'],
46 * },
47 * ...
48 * };
49 * topLevelEventsToDispatchConfig = {
50 * 'topAbort': { sameConfig }
51 * };
52 */
53var eventTypes = {};
54var topLevelEventsToDispatchConfig = {};
55['abort', 'animationEnd', 'animationIteration', 'animationStart', 'blur', 'canPlay', 'canPlayThrough', 'click', 'contextMenu', 'copy', 'cut', 'doubleClick', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'focus', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'progress', 'rateChange', 'reset', 'scroll', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'touchCancel', 'touchEnd', 'touchMove', 'touchStart', 'transitionEnd', 'volumeChange', 'waiting', 'wheel'].forEach(function (event) {
56 var capitalizedEvent = event[0].toUpperCase() + event.slice(1);
57 var onEvent = 'on' + capitalizedEvent;
58 var topEvent = 'top' + capitalizedEvent;
59
60 var type = {
61 phasedRegistrationNames: {
62 bubbled: onEvent,
63 captured: onEvent + 'Capture'
64 },
65 dependencies: [topEvent]
66 };
67 eventTypes[event] = type;
68 topLevelEventsToDispatchConfig[topEvent] = type;
69});
70
71var onClickListeners = {};
72
73function getDictionaryKey(inst) {
74 // Prevents V8 performance issue:
75 // https://github.com/facebook/react/pull/7232
76 return '.' + inst._rootNodeID;
77}
78
79function isInteractive(tag) {
80 return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';
81}
82
83var SimpleEventPlugin = {
84 eventTypes: eventTypes,
85
86 extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
87 var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
88 if (!dispatchConfig) {
89 return null;
90 }
91 var EventConstructor;
92 switch (topLevelType) {
93 case 'topAbort':
94 case 'topCanPlay':
95 case 'topCanPlayThrough':
96 case 'topDurationChange':
97 case 'topEmptied':
98 case 'topEncrypted':
99 case 'topEnded':
100 case 'topError':
101 case 'topInput':
102 case 'topInvalid':
103 case 'topLoad':
104 case 'topLoadedData':
105 case 'topLoadedMetadata':
106 case 'topLoadStart':
107 case 'topPause':
108 case 'topPlay':
109 case 'topPlaying':
110 case 'topProgress':
111 case 'topRateChange':
112 case 'topReset':
113 case 'topSeeked':
114 case 'topSeeking':
115 case 'topStalled':
116 case 'topSubmit':
117 case 'topSuspend':
118 case 'topTimeUpdate':
119 case 'topVolumeChange':
120 case 'topWaiting':
121 // HTML Events
122 // @see http://www.w3.org/TR/html5/index.html#events-0
123 EventConstructor = SyntheticEvent;
124 break;
125 case 'topKeyPress':
126 // Firefox creates a keypress event for function keys too. This removes
127 // the unwanted keypress events. Enter is however both printable and
128 // non-printable. One would expect Tab to be as well (but it isn't).
129 if (getEventCharCode(nativeEvent) === 0) {
130 return null;
131 }
132 /* falls through */
133 case 'topKeyDown':
134 case 'topKeyUp':
135 EventConstructor = SyntheticKeyboardEvent;
136 break;
137 case 'topBlur':
138 case 'topFocus':
139 EventConstructor = SyntheticFocusEvent;
140 break;
141 case 'topClick':
142 // Firefox creates a click event on right mouse clicks. This removes the
143 // unwanted click events.
144 if (nativeEvent.button === 2) {
145 return null;
146 }
147 /* falls through */
148 case 'topDoubleClick':
149 case 'topMouseDown':
150 case 'topMouseMove':
151 case 'topMouseUp':
152 // TODO: Disabled elements should not respond to mouse events
153 /* falls through */
154 case 'topMouseOut':
155 case 'topMouseOver':
156 case 'topContextMenu':
157 EventConstructor = SyntheticMouseEvent;
158 break;
159 case 'topDrag':
160 case 'topDragEnd':
161 case 'topDragEnter':
162 case 'topDragExit':
163 case 'topDragLeave':
164 case 'topDragOver':
165 case 'topDragStart':
166 case 'topDrop':
167 EventConstructor = SyntheticDragEvent;
168 break;
169 case 'topTouchCancel':
170 case 'topTouchEnd':
171 case 'topTouchMove':
172 case 'topTouchStart':
173 EventConstructor = SyntheticTouchEvent;
174 break;
175 case 'topAnimationEnd':
176 case 'topAnimationIteration':
177 case 'topAnimationStart':
178 EventConstructor = SyntheticAnimationEvent;
179 break;
180 case 'topTransitionEnd':
181 EventConstructor = SyntheticTransitionEvent;
182 break;
183 case 'topScroll':
184 EventConstructor = SyntheticUIEvent;
185 break;
186 case 'topWheel':
187 EventConstructor = SyntheticWheelEvent;
188 break;
189 case 'topCopy':
190 case 'topCut':
191 case 'topPaste':
192 EventConstructor = SyntheticClipboardEvent;
193 break;
194 }
195 !EventConstructor ? process.env.NODE_ENV !== 'production' ? invariant(false, 'SimpleEventPlugin: Unhandled event type, `%s`.', topLevelType) : _prodInvariant('86', topLevelType) : void 0;
196 var event = EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
197 EventPropagators.accumulateTwoPhaseDispatches(event);
198 return event;
199 },
200
201 didPutListener: function (inst, registrationName, listener) {
202 // Mobile Safari does not fire properly bubble click events on
203 // non-interactive elements, which means delegated click listeners do not
204 // fire. The workaround for this bug involves attaching an empty click
205 // listener on the target node.
206 // http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
207 if (registrationName === 'onClick' && !isInteractive(inst._tag)) {
208 var key = getDictionaryKey(inst);
209 var node = ReactDOMComponentTree.getNodeFromInstance(inst);
210 if (!onClickListeners[key]) {
211 onClickListeners[key] = EventListener.listen(node, 'click', emptyFunction);
212 }
213 }
214 },
215
216 willDeleteListener: function (inst, registrationName) {
217 if (registrationName === 'onClick' && !isInteractive(inst._tag)) {
218 var key = getDictionaryKey(inst);
219 onClickListeners[key].remove();
220 delete onClickListeners[key];
221 }
222 }
223};
224
225module.exports = SimpleEventPlugin;
\No newline at end of file