200 lines
4.7 KiB
JavaScript
200 lines
4.7 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
exports.__esModule = true;
|
||
|
exports.setResponderId = setResponderId;
|
||
|
exports.getResponderPaths = getResponderPaths;
|
||
|
exports.getLowestCommonAncestor = getLowestCommonAncestor;
|
||
|
exports.hasTargetTouches = hasTargetTouches;
|
||
|
exports.hasValidSelection = hasValidSelection;
|
||
|
exports.isPrimaryPointerDown = isPrimaryPointerDown;
|
||
|
|
||
|
var _isSelectionValid = _interopRequireDefault(require("../../modules/isSelectionValid"));
|
||
|
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
|
||
|
/**
|
||
|
* Copyright (c) Nicolas Gallagher
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
var keyName = '__reactResponderId';
|
||
|
|
||
|
function getEventPath(domEvent) {
|
||
|
// The 'selectionchange' event always has the 'document' as the target.
|
||
|
// Use the anchor node as the initial target to reconstruct a path.
|
||
|
// (We actually only need the first "responder" node in practice.)
|
||
|
if (domEvent.type === 'selectionchange') {
|
||
|
var target = window.getSelection().anchorNode;
|
||
|
return composedPathFallback(target);
|
||
|
} else {
|
||
|
var path = domEvent.composedPath != null ? domEvent.composedPath() : composedPathFallback(domEvent.target);
|
||
|
return path;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function composedPathFallback(target) {
|
||
|
var path = [];
|
||
|
|
||
|
while (target != null && target !== document.body) {
|
||
|
path.push(target);
|
||
|
target = target.parentNode;
|
||
|
}
|
||
|
|
||
|
return path;
|
||
|
}
|
||
|
/**
|
||
|
* Retrieve the responderId from a host node
|
||
|
*/
|
||
|
|
||
|
|
||
|
function getResponderId(node) {
|
||
|
if (node != null) {
|
||
|
return node[keyName];
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
/**
|
||
|
* Store the responderId on a host node
|
||
|
*/
|
||
|
|
||
|
|
||
|
function setResponderId(node, id) {
|
||
|
if (node != null) {
|
||
|
node[keyName] = id;
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Filter the event path to contain only the nodes attached to the responder system
|
||
|
*/
|
||
|
|
||
|
|
||
|
function getResponderPaths(domEvent) {
|
||
|
var idPath = [];
|
||
|
var nodePath = [];
|
||
|
var eventPath = getEventPath(domEvent);
|
||
|
|
||
|
for (var i = 0; i < eventPath.length; i++) {
|
||
|
var node = eventPath[i];
|
||
|
var id = getResponderId(node);
|
||
|
|
||
|
if (id != null) {
|
||
|
idPath.push(id);
|
||
|
nodePath.push(node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
idPath: idPath,
|
||
|
nodePath: nodePath
|
||
|
};
|
||
|
}
|
||
|
/**
|
||
|
* Walk the paths and find the first common ancestor
|
||
|
*/
|
||
|
|
||
|
|
||
|
function getLowestCommonAncestor(pathA, pathB) {
|
||
|
var pathALength = pathA.length;
|
||
|
var pathBLength = pathB.length;
|
||
|
|
||
|
if ( // If either path is empty
|
||
|
pathALength === 0 || pathBLength === 0 || // If the last elements aren't the same there can't be a common ancestor
|
||
|
// that is connected to the responder system
|
||
|
pathA[pathALength - 1] !== pathB[pathBLength - 1]) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
var itemA = pathA[0];
|
||
|
var indexA = 0;
|
||
|
var itemB = pathB[0];
|
||
|
var indexB = 0; // If A is deeper, skip indices that can't match.
|
||
|
|
||
|
if (pathALength - pathBLength > 0) {
|
||
|
indexA = pathALength - pathBLength;
|
||
|
itemA = pathA[indexA];
|
||
|
pathALength = pathBLength;
|
||
|
} // If B is deeper, skip indices that can't match
|
||
|
|
||
|
|
||
|
if (pathBLength - pathALength > 0) {
|
||
|
indexB = pathBLength - pathALength;
|
||
|
itemB = pathB[indexB];
|
||
|
pathBLength = pathALength;
|
||
|
} // Walk in lockstep until a match is found
|
||
|
|
||
|
|
||
|
var depth = pathALength;
|
||
|
|
||
|
while (depth--) {
|
||
|
if (itemA === itemB) {
|
||
|
return itemA;
|
||
|
}
|
||
|
|
||
|
itemA = pathA[indexA++];
|
||
|
itemB = pathB[indexB++];
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
/**
|
||
|
* Determine whether any of the active touches are within the current responder.
|
||
|
* This cannot rely on W3C `targetTouches`, as neither IE11 nor Safari implement it.
|
||
|
*/
|
||
|
|
||
|
|
||
|
function hasTargetTouches(target, touches) {
|
||
|
if (!touches || touches.length === 0) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < touches.length; i++) {
|
||
|
var node = touches[i].target;
|
||
|
|
||
|
if (node != null) {
|
||
|
if (target.contains(node)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
/**
|
||
|
* Ignore 'selectionchange' events that don't correspond with a person's intent to
|
||
|
* select text.
|
||
|
*/
|
||
|
|
||
|
|
||
|
function hasValidSelection(domEvent) {
|
||
|
if (domEvent.type === 'selectionchange') {
|
||
|
return (0, _isSelectionValid.default)();
|
||
|
}
|
||
|
|
||
|
return domEvent.type === 'select';
|
||
|
}
|
||
|
/**
|
||
|
* Events are only valid if the primary button was used without specific modifier keys.
|
||
|
*/
|
||
|
|
||
|
|
||
|
function isPrimaryPointerDown(domEvent) {
|
||
|
var altKey = domEvent.altKey,
|
||
|
button = domEvent.button,
|
||
|
buttons = domEvent.buttons,
|
||
|
ctrlKey = domEvent.ctrlKey,
|
||
|
type = domEvent.type;
|
||
|
var isTouch = type === 'touchstart' || type === 'touchmove';
|
||
|
var isPrimaryMouseDown = type === 'mousedown' && (button === 0 || buttons === 1);
|
||
|
var isPrimaryMouseMove = type === 'mousemove' && buttons === 1;
|
||
|
var noModifiers = altKey === false && ctrlKey === false;
|
||
|
|
||
|
if (isTouch || isPrimaryMouseDown && noModifiers || isPrimaryMouseMove && noModifiers) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|