UNPKG

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