Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81155 views
1
/**
2
* Copyright 2013-2014, 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 SelectEventPlugin
10
*/
11
12
"use strict";
13
14
var EventConstants = require('EventConstants');
15
var EventPropagators = require('EventPropagators');
16
var ReactInputSelection = require('ReactInputSelection');
17
var SyntheticEvent = require('SyntheticEvent');
18
19
var getActiveElement = require('getActiveElement');
20
var isTextInputElement = require('isTextInputElement');
21
var keyOf = require('keyOf');
22
var shallowEqual = require('shallowEqual');
23
24
var topLevelTypes = EventConstants.topLevelTypes;
25
26
var eventTypes = {
27
select: {
28
phasedRegistrationNames: {
29
bubbled: keyOf({onSelect: null}),
30
captured: keyOf({onSelectCapture: null})
31
},
32
dependencies: [
33
topLevelTypes.topBlur,
34
topLevelTypes.topContextMenu,
35
topLevelTypes.topFocus,
36
topLevelTypes.topKeyDown,
37
topLevelTypes.topMouseDown,
38
topLevelTypes.topMouseUp,
39
topLevelTypes.topSelectionChange
40
]
41
}
42
};
43
44
var activeElement = null;
45
var activeElementID = null;
46
var lastSelection = null;
47
var mouseDown = false;
48
49
/**
50
* Get an object which is a unique representation of the current selection.
51
*
52
* The return value will not be consistent across nodes or browsers, but
53
* two identical selections on the same node will return identical objects.
54
*
55
* @param {DOMElement} node
56
* @param {object}
57
*/
58
function getSelection(node) {
59
if ('selectionStart' in node &&
60
ReactInputSelection.hasSelectionCapabilities(node)) {
61
return {
62
start: node.selectionStart,
63
end: node.selectionEnd
64
};
65
} else if (window.getSelection) {
66
var selection = window.getSelection();
67
return {
68
anchorNode: selection.anchorNode,
69
anchorOffset: selection.anchorOffset,
70
focusNode: selection.focusNode,
71
focusOffset: selection.focusOffset
72
};
73
} else if (document.selection) {
74
var range = document.selection.createRange();
75
return {
76
parentElement: range.parentElement(),
77
text: range.text,
78
top: range.boundingTop,
79
left: range.boundingLeft
80
};
81
}
82
}
83
84
/**
85
* Poll selection to see whether it's changed.
86
*
87
* @param {object} nativeEvent
88
* @return {?SyntheticEvent}
89
*/
90
function constructSelectEvent(nativeEvent) {
91
// Ensure we have the right element, and that the user is not dragging a
92
// selection (this matches native `select` event behavior). In HTML5, select
93
// fires only on input and textarea thus if there's no focused element we
94
// won't dispatch.
95
if (mouseDown ||
96
activeElement == null ||
97
activeElement != getActiveElement()) {
98
return;
99
}
100
101
// Only fire when selection has actually changed.
102
var currentSelection = getSelection(activeElement);
103
if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
104
lastSelection = currentSelection;
105
106
var syntheticEvent = SyntheticEvent.getPooled(
107
eventTypes.select,
108
activeElementID,
109
nativeEvent
110
);
111
112
syntheticEvent.type = 'select';
113
syntheticEvent.target = activeElement;
114
115
EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);
116
117
return syntheticEvent;
118
}
119
}
120
121
/**
122
* This plugin creates an `onSelect` event that normalizes select events
123
* across form elements.
124
*
125
* Supported elements are:
126
* - input (see `isTextInputElement`)
127
* - textarea
128
* - contentEditable
129
*
130
* This differs from native browser implementations in the following ways:
131
* - Fires on contentEditable fields as well as inputs.
132
* - Fires for collapsed selection.
133
* - Fires after user input.
134
*/
135
var SelectEventPlugin = {
136
137
eventTypes: eventTypes,
138
139
/**
140
* @param {string} topLevelType Record from `EventConstants`.
141
* @param {DOMEventTarget} topLevelTarget The listening component root node.
142
* @param {string} topLevelTargetID ID of `topLevelTarget`.
143
* @param {object} nativeEvent Native browser event.
144
* @return {*} An accumulation of synthetic events.
145
* @see {EventPluginHub.extractEvents}
146
*/
147
extractEvents: function(
148
topLevelType,
149
topLevelTarget,
150
topLevelTargetID,
151
nativeEvent) {
152
153
switch (topLevelType) {
154
// Track the input node that has focus.
155
case topLevelTypes.topFocus:
156
if (isTextInputElement(topLevelTarget) ||
157
topLevelTarget.contentEditable === 'true') {
158
activeElement = topLevelTarget;
159
activeElementID = topLevelTargetID;
160
lastSelection = null;
161
}
162
break;
163
case topLevelTypes.topBlur:
164
activeElement = null;
165
activeElementID = null;
166
lastSelection = null;
167
break;
168
169
// Don't fire the event while the user is dragging. This matches the
170
// semantics of the native select event.
171
case topLevelTypes.topMouseDown:
172
mouseDown = true;
173
break;
174
case topLevelTypes.topContextMenu:
175
case topLevelTypes.topMouseUp:
176
mouseDown = false;
177
return constructSelectEvent(nativeEvent);
178
179
// Chrome and IE fire non-standard event when selection is changed (and
180
// sometimes when it hasn't).
181
// Firefox doesn't support selectionchange, so check selection status
182
// after each key entry. The selection changes after keydown and before
183
// keyup, but we check on keydown as well in the case of holding down a
184
// key, when multiple keydown events are fired but only one keyup is.
185
case topLevelTypes.topSelectionChange:
186
case topLevelTypes.topKeyDown:
187
case topLevelTypes.topKeyUp:
188
return constructSelectEvent(nativeEvent);
189
}
190
}
191
};
192
193
module.exports = SelectEventPlugin;
194
195