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.
193 lines
5.7 KiB
193 lines
5.7 KiB
/** |
|
* 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;
|
|
|