wallet-core/node_modules/react-dom/lib/SelectEventPlugin.js
2017-08-14 05:02:09 +02:00

187 lines
5.9 KiB
JavaScript

/**
* Copyright 2013-present, 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.
*
*/
'use strict';
var EventPropagators = require('./EventPropagators');
var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactInputSelection = require('./ReactInputSelection');
var SyntheticEvent = require('./SyntheticEvent');
var getActiveElement = require('fbjs/lib/getActiveElement');
var isTextInputElement = require('./isTextInputElement');
var shallowEqual = require('fbjs/lib/shallowEqual');
var skipSelectionChangeEvent = ExecutionEnvironment.canUseDOM && 'documentMode' in document && document.documentMode <= 11;
var eventTypes = {
select: {
phasedRegistrationNames: {
bubbled: 'onSelect',
captured: 'onSelectCapture'
},
dependencies: ['topBlur', 'topContextMenu', 'topFocus', 'topKeyDown', 'topKeyUp', 'topMouseDown', 'topMouseUp', 'topSelectionChange']
}
};
var activeElement = null;
var activeElementInst = null;
var lastSelection = null;
var mouseDown = false;
// Track whether a listener exists for this plugin. If none exist, we do
// not extract events. See #3639.
var hasListener = 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
* @return {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, nativeEventTarget) {
// 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, activeElementInst, nativeEvent, nativeEventTarget);
syntheticEvent.type = 'select';
syntheticEvent.target = activeElement;
EventPropagators.accumulateTwoPhaseDispatches(syntheticEvent);
return syntheticEvent;
}
return null;
}
/**
* 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,
extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
if (!hasListener) {
return null;
}
var targetNode = targetInst ? ReactDOMComponentTree.getNodeFromInstance(targetInst) : window;
switch (topLevelType) {
// Track the input node that has focus.
case 'topFocus':
if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') {
activeElement = targetNode;
activeElementInst = targetInst;
lastSelection = null;
}
break;
case 'topBlur':
activeElement = null;
activeElementInst = null;
lastSelection = null;
break;
// Don't fire the event while the user is dragging. This matches the
// semantics of the native select event.
case 'topMouseDown':
mouseDown = true;
break;
case 'topContextMenu':
case 'topMouseUp':
mouseDown = false;
return constructSelectEvent(nativeEvent, nativeEventTarget);
// Chrome and IE fire non-standard event when selection is changed (and
// sometimes when it hasn't). IE's event fires out of order with respect
// to key and input events on deletion, so we discard it.
//
// 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.
// This is also our approach for IE handling, for the reason above.
case 'topSelectionChange':
if (skipSelectionChangeEvent) {
break;
}
// falls through
case 'topKeyDown':
case 'topKeyUp':
return constructSelectEvent(nativeEvent, nativeEventTarget);
}
return null;
},
didPutListener: function (inst, registrationName, listener) {
if (registrationName === 'onSelect') {
hasListener = true;
}
}
};
module.exports = SelectEventPlugin;