Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81155 views
1
/**
2
* Copyright 2013 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
* @providesModule BeforeInputEventPlugin
10
* @typechecks static-only
11
*/
12
13
"use strict";
14
15
var EventConstants = require('EventConstants');
16
var EventPropagators = require('EventPropagators');
17
var ExecutionEnvironment = require('ExecutionEnvironment');
18
var SyntheticInputEvent = require('SyntheticInputEvent');
19
20
var keyOf = require('keyOf');
21
22
var canUseTextInputEvent = (
23
ExecutionEnvironment.canUseDOM &&
24
'TextEvent' in window &&
25
!('documentMode' in document || isPresto())
26
);
27
28
/**
29
* Opera <= 12 includes TextEvent in window, but does not fire
30
* text input events. Rely on keypress instead.
31
*/
32
function isPresto() {
33
var opera = window.opera;
34
return (
35
typeof opera === 'object' &&
36
typeof opera.version === 'function' &&
37
parseInt(opera.version(), 10) <= 12
38
);
39
}
40
41
var SPACEBAR_CODE = 32;
42
var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
43
44
var topLevelTypes = EventConstants.topLevelTypes;
45
46
// Events and their corresponding property names.
47
var eventTypes = {
48
beforeInput: {
49
phasedRegistrationNames: {
50
bubbled: keyOf({onBeforeInput: null}),
51
captured: keyOf({onBeforeInputCapture: null})
52
},
53
dependencies: [
54
topLevelTypes.topCompositionEnd,
55
topLevelTypes.topKeyPress,
56
topLevelTypes.topTextInput,
57
topLevelTypes.topPaste
58
]
59
}
60
};
61
62
// Track characters inserted via keypress and composition events.
63
var fallbackChars = null;
64
65
// Track whether we've ever handled a keypress on the space key.
66
var hasSpaceKeypress = false;
67
68
/**
69
* Return whether a native keypress event is assumed to be a command.
70
* This is required because Firefox fires `keypress` events for key commands
71
* (cut, copy, select-all, etc.) even though no character is inserted.
72
*/
73
function isKeypressCommand(nativeEvent) {
74
return (
75
(nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
76
// ctrlKey && altKey is equivalent to AltGr, and is not a command.
77
!(nativeEvent.ctrlKey && nativeEvent.altKey)
78
);
79
}
80
81
/**
82
* Create an `onBeforeInput` event to match
83
* http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
84
*
85
* This event plugin is based on the native `textInput` event
86
* available in Chrome, Safari, Opera, and IE. This event fires after
87
* `onKeyPress` and `onCompositionEnd`, but before `onInput`.
88
*
89
* `beforeInput` is spec'd but not implemented in any browsers, and
90
* the `input` event does not provide any useful information about what has
91
* actually been added, contrary to the spec. Thus, `textInput` is the best
92
* available event to identify the characters that have actually been inserted
93
* into the target node.
94
*/
95
var BeforeInputEventPlugin = {
96
97
eventTypes: eventTypes,
98
99
/**
100
* @param {string} topLevelType Record from `EventConstants`.
101
* @param {DOMEventTarget} topLevelTarget The listening component root node.
102
* @param {string} topLevelTargetID ID of `topLevelTarget`.
103
* @param {object} nativeEvent Native browser event.
104
* @return {*} An accumulation of synthetic events.
105
* @see {EventPluginHub.extractEvents}
106
*/
107
extractEvents: function(
108
topLevelType,
109
topLevelTarget,
110
topLevelTargetID,
111
nativeEvent) {
112
113
var chars;
114
115
if (canUseTextInputEvent) {
116
switch (topLevelType) {
117
case topLevelTypes.topKeyPress:
118
/**
119
* If native `textInput` events are available, our goal is to make
120
* use of them. However, there is a special case: the spacebar key.
121
* In Webkit, preventing default on a spacebar `textInput` event
122
* cancels character insertion, but it *also* causes the browser
123
* to fall back to its default spacebar behavior of scrolling the
124
* page.
125
*
126
* Tracking at:
127
* https://code.google.com/p/chromium/issues/detail?id=355103
128
*
129
* To avoid this issue, use the keypress event as if no `textInput`
130
* event is available.
131
*/
132
var which = nativeEvent.which;
133
if (which !== SPACEBAR_CODE) {
134
return;
135
}
136
137
hasSpaceKeypress = true;
138
chars = SPACEBAR_CHAR;
139
break;
140
141
case topLevelTypes.topTextInput:
142
// Record the characters to be added to the DOM.
143
chars = nativeEvent.data;
144
145
// If it's a spacebar character, assume that we have already handled
146
// it at the keypress level and bail immediately. Android Chrome
147
// doesn't give us keycodes, so we need to blacklist it.
148
if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
149
return;
150
}
151
152
// Otherwise, carry on.
153
break;
154
155
default:
156
// For other native event types, do nothing.
157
return;
158
}
159
} else {
160
switch (topLevelType) {
161
case topLevelTypes.topPaste:
162
// If a paste event occurs after a keypress, throw out the input
163
// chars. Paste events should not lead to BeforeInput events.
164
fallbackChars = null;
165
break;
166
case topLevelTypes.topKeyPress:
167
/**
168
* As of v27, Firefox may fire keypress events even when no character
169
* will be inserted. A few possibilities:
170
*
171
* - `which` is `0`. Arrow keys, Esc key, etc.
172
*
173
* - `which` is the pressed key code, but no char is available.
174
* Ex: 'AltGr + d` in Polish. There is no modified character for
175
* this key combination and no character is inserted into the
176
* document, but FF fires the keypress for char code `100` anyway.
177
* No `input` event will occur.
178
*
179
* - `which` is the pressed key code, but a command combination is
180
* being used. Ex: `Cmd+C`. No character is inserted, and no
181
* `input` event will occur.
182
*/
183
if (nativeEvent.which && !isKeypressCommand(nativeEvent)) {
184
fallbackChars = String.fromCharCode(nativeEvent.which);
185
}
186
break;
187
case topLevelTypes.topCompositionEnd:
188
fallbackChars = nativeEvent.data;
189
break;
190
}
191
192
// If no changes have occurred to the fallback string, no relevant
193
// event has fired and we're done.
194
if (fallbackChars === null) {
195
return;
196
}
197
198
chars = fallbackChars;
199
}
200
201
// If no characters are being inserted, no BeforeInput event should
202
// be fired.
203
if (!chars) {
204
return;
205
}
206
207
var event = SyntheticInputEvent.getPooled(
208
eventTypes.beforeInput,
209
topLevelTargetID,
210
nativeEvent
211
);
212
213
event.data = chars;
214
fallbackChars = null;
215
EventPropagators.accumulateTwoPhaseDispatches(event);
216
return event;
217
}
218
};
219
220
module.exports = BeforeInputEventPlugin;
221
222