You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
5.7 KiB
194 lines
5.7 KiB
10 years ago
|
/**
|
||
|
* Copyright 2013-2015, Facebook, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under the BSD-style license found in the
|
||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||
|
*
|
||
|
* @providesModule SelectEventPlugin
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var EventConstants = require("./EventConstants");
|
||
|
var EventPropagators = require("./EventPropagators");
|
||
|
var ReactInputSelection = require("./ReactInputSelection");
|
||
|
var SyntheticEvent = require("./SyntheticEvent");
|
||
|
|
||
|
var getActiveElement = require("./getActiveElement");
|
||
|
var isTextInputElement = require("./isTextInputElement");
|
||
|
var keyOf = require("./keyOf");
|
||
|
var shallowEqual = require("./shallowEqual");
|
||
|
|
||
|
var topLevelTypes = EventConstants.topLevelTypes;
|
||
|
|
||
|
var eventTypes = {
|
||
|
select: {
|
||
|
phasedRegistrationNames: {
|
||
|
bubbled: keyOf({onSelect: null}),
|
||
|
captured: keyOf({onSelectCapture: null})
|
||
|
},
|
||
|
dependencies: [
|
||
|
topLevelTypes.topBlur,
|
||
|
topLevelTypes.topContextMenu,
|
||
|
topLevelTypes.topFocus,
|
||
|
topLevelTypes.topKeyDown,
|
||
|
topLevelTypes.topMouseDown,
|
||
|
topLevelTypes.topMouseUp,
|
||
|
topLevelTypes.topSelectionChange
|
||
|
]
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var activeElement = null;
|
||
|
var activeElementID = null;
|
||
|
var lastSelection = null;
|
||
|
var mouseDown = false;
|
||
|
|
||
|
/**
|
||
|
* Get an object which is a unique representation of the current selection.
|
||
|
*
|
||
|
* The return value will not be consistent across nodes or browsers, but
|
||
|
* two identical selections on the same node will return identical objects.
|
||
|
*
|
||
|
* @param {DOMElement} node
|
||
|
* @param {object}
|
||
|
*/
|
||
|
function getSelection(node) {
|
||
|
if ('selectionStart' in node &&
|
||
|
ReactInputSelection.hasSelectionCapabilities(node)) {
|
||
|
return {
|
||
|
start: node.selectionStart,
|
||
|
end: node.selectionEnd
|
||
|
};
|
||
|
} else if (window.getSelection) {
|
||
|
var selection = window.getSelection();
|
||
|
return {
|
||
|
anchorNode: selection.anchorNode,
|
||
|
anchorOffset: selection.anchorOffset,
|
||
|
focusNode: selection.focusNode,
|
||
|
focusOffset: selection.focusOffset
|
||
|
};
|
||
|
} else if (document.selection) {
|
||
|
var range = document.selection.createRange();
|
||
|
return {
|
||
|
parentElement: range.parentElement(),
|
||
|
text: range.text,
|
||
|
top: range.boundingTop,
|
||
|
left: range.boundingLeft
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Poll selection to see whether it's changed.
|
||
|
*
|
||
|
* @param {object} nativeEvent
|
||
|
* @return {?SyntheticEvent}
|
||
|
*/
|
||
|
function constructSelectEvent(nativeEvent) {
|
||
|
// Ensure we have the right element, and that the user is not dragging a
|
||
|
// selection (this matches native `select` event behavior). In HTML5, select
|
||
|
// fires only on input and textarea thus if there's no focused element we
|
||
|
// won't dispatch.
|
||
|
if (mouseDown ||
|
||
|
activeElement == null ||
|
||
|
activeElement !== getActiveElement()) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// Only fire when selection has actually changed.
|
||
|
var currentSelection = getSelection(activeElement);
|
||
|
if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
|
||
|
lastSelection = currentSelection;
|
||
|
|
||
|
var syntheticEvent = SyntheticEvent.getPooled(
|
||
|
eventTypes.select,
|
||
|
activeElementID,
|
||
|
nativeEvent
|
||
|
);
|
||
|
|
||
|
syntheticEvent.type = 'select';
|
||
|
syntheticEvent.target = activeElement;
|
||
|
|
||
|
EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);
|
||
|
|
||
|
return syntheticEvent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This plugin creates an `onSelect` event that normalizes select events
|
||
|
* across form elements.
|
||
|
*
|
||
|
* Supported elements are:
|
||
|
* - input (see `isTextInputElement`)
|
||
|
* - textarea
|
||
|
* - contentEditable
|
||
|
*
|
||
|
* This differs from native browser implementations in the following ways:
|
||
|
* - Fires on contentEditable fields as well as inputs.
|
||
|
* - Fires for collapsed selection.
|
||
|
* - Fires after user input.
|
||
|
*/
|
||
|
var SelectEventPlugin = {
|
||
|
|
||
|
eventTypes: eventTypes,
|
||
|
|
||
|
/**
|
||
|
* @param {string} topLevelType Record from `EventConstants`.
|
||
|
* @param {DOMEventTarget} topLevelTarget The listening component root node.
|
||
|
* @param {string} topLevelTargetID ID of `topLevelTarget`.
|
||
|
* @param {object} nativeEvent Native browser event.
|
||
|
* @return {*} An accumulation of synthetic events.
|
||
|
* @see {EventPluginHub.extractEvents}
|
||
|
*/
|
||
|
extractEvents: function(
|
||
|
topLevelType,
|
||
|
topLevelTarget,
|
||
|
topLevelTargetID,
|
||
|
nativeEvent) {
|
||
|
|
||
|
switch (topLevelType) {
|
||
|
// Track the input node that has focus.
|
||
|
case topLevelTypes.topFocus:
|
||
|
if (isTextInputElement(topLevelTarget) ||
|
||
|
topLevelTarget.contentEditable === 'true') {
|
||
|
activeElement = topLevelTarget;
|
||
|
activeElementID = topLevelTargetID;
|
||
|
lastSelection = null;
|
||
|
}
|
||
|
break;
|
||
|
case topLevelTypes.topBlur:
|
||
|
activeElement = null;
|
||
|
activeElementID = null;
|
||
|
lastSelection = null;
|
||
|
break;
|
||
|
|
||
|
// Don't fire the event while the user is dragging. This matches the
|
||
|
// semantics of the native select event.
|
||
|
case topLevelTypes.topMouseDown:
|
||
|
mouseDown = true;
|
||
|
break;
|
||
|
case topLevelTypes.topContextMenu:
|
||
|
case topLevelTypes.topMouseUp:
|
||
|
mouseDown = false;
|
||
|
return constructSelectEvent(nativeEvent);
|
||
|
|
||
|
// Chrome and IE fire non-standard event when selection is changed (and
|
||
|
// sometimes when it hasn't).
|
||
|
// Firefox doesn't support selectionchange, so check selection status
|
||
|
// after each key entry. The selection changes after keydown and before
|
||
|
// keyup, but we check on keydown as well in the case of holding down a
|
||
|
// key, when multiple keydown events are fired but only one keyup is.
|
||
|
case topLevelTypes.topSelectionChange:
|
||
|
case topLevelTypes.topKeyDown:
|
||
|
case topLevelTypes.topKeyUp:
|
||
|
return constructSelectEvent(nativeEvent);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
module.exports = SelectEventPlugin;
|