1
+/**
2
+ * Copyright 2013 Facebook, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ * @providesModule BeforeInputEventPlugin
17
+ * @typechecks static-only
18
+ */
19
+
20
+"use strict";
21
+
22
+var EventConstants = require('EventConstants');
23
+var EventPropagators = require('EventPropagators');
24
+var ExecutionEnvironment = require('ExecutionEnvironment');
25
+var SyntheticInputEvent = require('SyntheticInputEvent');
26
+
27
+var keyOf = require('keyOf');
28
+
29
+var useBeforeInputEvent = (
30
+ ExecutionEnvironment.canUseDOM &&
31
+ 'TextEvent' in window &&
32
+ !('documentMode' in document)
33
+);
34
+
35
+var topLevelTypes = EventConstants.topLevelTypes;
36
+
37
+// Events and their corresponding property names.
38
+var eventTypes = {
39
+ beforeInput: {
40
+ phasedRegistrationNames: {
41
+ bubbled: keyOf({onBeforeInput: null}),
42
+ captured: keyOf({onBeforeInputCapture: null})
43
+ },
44
+ dependencies: [
45
+ topLevelTypes.topCompositionEnd,
46
+ topLevelTypes.topKeyPress,
47
+ topLevelTypes.topTextInput,
48
+ topLevelTypes.topPaste
49
+ ]
50
+ }
51
+};
52
+
53
+// Track characters inserted via keypress and composition events.
54
+var fallbackChars = null;
55
+
56
+/**
57
+ * Return whether a native keypress event is assumed to be a command.
58
+ * This is required because Firefox fires `keypress` events for key commands
59
+ * (cut, copy, select-all, etc.) even though no character is inserted.
60
+ */
61
+function isKeypressCommand(nativeEvent) {
62
+ return (
63
+ (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
64
+ // ctrlKey && altKey is equivalent to AltGr, and is not a command.
65
+ !(nativeEvent.ctrlKey && nativeEvent.altKey)
66
+ );
67
+}
68
+
69
+/**
70
+ * Create an `onBeforeInput` event to match
71
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
72
+ *
73
+ * This event plugin is based on the native `textInput` event
74
+ * available in Chrome, Safari, Opera, and IE. This event fires after
75
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
76
+ *
77
+ * `beforeInput` is spec'd but not implemented in any browsers, and
78
+ * the `input` event does not provide any useful information about what has
79
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
80
+ * available event to identify the characters that have actually been inserted
81
+ * into the target node.
82
+ */
83
+var BeforeInputEventPlugin = {
84
+
85
+ eventTypes: eventTypes,
86
+
87
+ /**
88
+ * @param {string} topLevelType Record from `EventConstants`.
89
+ * @param {DOMEventTarget} topLevelTarget The listening component root node.
90
+ * @param {string} topLevelTargetID ID of `topLevelTarget`.
91
+ * @param {object} nativeEvent Native browser event.
92
+ * @return {*} An accumulation of synthetic events.
93
+ * @see {EventPluginHub.extractEvents}
94
+ */
95
+ extractEvents: function(
96
+ topLevelType,
97
+ topLevelTarget,
98
+ topLevelTargetID,
99
+ nativeEvent) {
100
+
101
+ var chars;
102
+
103
+ if (useBeforeInputEvent) {
104
+ // For browsers that support `textInput` events natively, don't do
105
+ // anything with keypress, composition, etc.
106
+ if (topLevelType !== topLevelTypes.topTextInput) {
107
+ return;
108
+ }
109
+ chars = nativeEvent.data;
110
+ } else {
111
+ switch (topLevelType) {
112
+ case topLevelTypes.topPaste:
113
+ // If a paste event occurs after a keypress, throw out the input
114
+ // chars. Paste events should not lead to BeforeInput events.
115
+ fallbackChars = null;
116
+ break;
117
+ case topLevelTypes.topKeyPress:
118
+ /**
119
+ * As of v27, Firefox may fire keypress events even when no character
120
+ * will be inserted. A few possibilities:
121
+ *
122
+ * - `which` is `0`. Arrow keys, Esc key, etc.
123
+ *
124
+ * - `which` is the pressed key code, but no char is available.
125
+ * Ex: 'AltGr + d` in Polish. There is no modified character for
126
+ * this key combination and no character is inserted into the
127
+ * document, but FF fires the keypress for char code `100` anyway.
128
+ * No `input` event will occur.
129
+ *
130
+ * - `which` is the pressed key code, but a command combination is
131
+ * being used. Ex: `Cmd+C`. No character is inserted, and no
132
+ * `input` event will occur.
133
+ */
134
+ if (nativeEvent.which && !isKeypressCommand(nativeEvent)) {
135
+ fallbackChars = String.fromCharCode(nativeEvent.which);
136
+ }
137
+ break;
138
+ case topLevelTypes.topCompositionEnd:
139
+ fallbackChars = nativeEvent.data;
140
+ break;
141
+ }
142
+
143
+ // If no changes have occurred to the fallback string, no relevant
144
+ // event has fired and we're done.
145
+ if (fallbackChars === null) {
146
+ return;
147
+ }
148
+
149
+ chars = fallbackChars;
150
+ }
151
+
152
+ // If no characters are being inserted, no BeforeInput event should
153
+ // be fired.
154
+ if (!chars) {
155
+ return;
156
+ }
157
+
158
+ var event = SyntheticInputEvent.getPooled(
159
+ eventTypes.beforeInput,
160
+ topLevelTargetID,
161
+ nativeEvent
162
+ );
163
+
164
+ event.data = chars;
165
+ fallbackChars = null;
166
+ EventPropagators.accumulateTwoPhaseDispatches(event);
167
+ return event;
168
+ }
169
+};
170
+
171
+module.exports = BeforeInputEventPlugin;