210 lines
6.5 KiB
JavaScript
210 lines
6.5 KiB
JavaScript
/**
|
|
* interact.js 1.10.27
|
|
*
|
|
* Copyright (c) 2012-present Taye Adeyemi <dev@taye.me>
|
|
* Released under the MIT License.
|
|
* https://raw.github.com/taye/interact.js/main/LICENSE
|
|
*/
|
|
|
|
import browser from './browser.js';
|
|
import domObjects from './domObjects.js';
|
|
import is from './is.js';
|
|
import { window as win, realWindow, getWindow } from './window.js';
|
|
import './isWindow.js';
|
|
function nodeContains(parent, child) {
|
|
if (parent.contains) {
|
|
return parent.contains(child);
|
|
}
|
|
while (child) {
|
|
if (child === parent) {
|
|
return true;
|
|
}
|
|
child = child.parentNode;
|
|
}
|
|
return false;
|
|
}
|
|
function closest(element, selector) {
|
|
while (is.element(element)) {
|
|
if (matchesSelector(element, selector)) {
|
|
return element;
|
|
}
|
|
element = parentNode(element);
|
|
}
|
|
return null;
|
|
}
|
|
function parentNode(node) {
|
|
let parent = node.parentNode;
|
|
if (is.docFrag(parent)) {
|
|
// skip past #shado-root fragments
|
|
// tslint:disable-next-line
|
|
while ((parent = parent.host) && is.docFrag(parent)) {
|
|
continue;
|
|
}
|
|
return parent;
|
|
}
|
|
return parent;
|
|
}
|
|
function matchesSelector(element, selector) {
|
|
// remove /deep/ from selectors if shadowDOM polyfill is used
|
|
if (win !== realWindow) {
|
|
selector = selector.replace(/\/deep\//g, ' ');
|
|
}
|
|
return element[browser.prefixedMatchesSelector](selector);
|
|
}
|
|
const getParent = el => el.parentNode || el.host;
|
|
|
|
// Test for the element that's "above" all other qualifiers
|
|
function indexOfDeepestElement(elements) {
|
|
let deepestNodeParents = [];
|
|
let deepestNodeIndex;
|
|
for (let i = 0; i < elements.length; i++) {
|
|
const currentNode = elements[i];
|
|
const deepestNode = elements[deepestNodeIndex];
|
|
|
|
// node may appear in elements array multiple times
|
|
if (!currentNode || i === deepestNodeIndex) {
|
|
continue;
|
|
}
|
|
if (!deepestNode) {
|
|
deepestNodeIndex = i;
|
|
continue;
|
|
}
|
|
const currentNodeParent = getParent(currentNode);
|
|
const deepestNodeParent = getParent(deepestNode);
|
|
|
|
// check if the deepest or current are document.documentElement/rootElement
|
|
// - if the current node is, do nothing and continue
|
|
if (currentNodeParent === currentNode.ownerDocument) {
|
|
continue;
|
|
}
|
|
// - if deepest is, update with the current node and continue to next
|
|
else if (deepestNodeParent === currentNode.ownerDocument) {
|
|
deepestNodeIndex = i;
|
|
continue;
|
|
}
|
|
|
|
// compare zIndex of siblings
|
|
if (currentNodeParent === deepestNodeParent) {
|
|
if (zIndexIsHigherThan(currentNode, deepestNode)) {
|
|
deepestNodeIndex = i;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// populate the ancestry array for the latest deepest node
|
|
deepestNodeParents = deepestNodeParents.length ? deepestNodeParents : getNodeParents(deepestNode);
|
|
let ancestryStart;
|
|
|
|
// if the deepest node is an HTMLElement and the current node is a non root svg element
|
|
if (deepestNode instanceof domObjects.HTMLElement && currentNode instanceof domObjects.SVGElement && !(currentNode instanceof domObjects.SVGSVGElement)) {
|
|
// TODO: is this check necessary? Was this for HTML elements embedded in SVG?
|
|
if (currentNode === deepestNodeParent) {
|
|
continue;
|
|
}
|
|
ancestryStart = currentNode.ownerSVGElement;
|
|
} else {
|
|
ancestryStart = currentNode;
|
|
}
|
|
const currentNodeParents = getNodeParents(ancestryStart, deepestNode.ownerDocument);
|
|
let commonIndex = 0;
|
|
|
|
// get (position of closest common ancestor) + 1
|
|
while (currentNodeParents[commonIndex] && currentNodeParents[commonIndex] === deepestNodeParents[commonIndex]) {
|
|
commonIndex++;
|
|
}
|
|
const parents = [currentNodeParents[commonIndex - 1], currentNodeParents[commonIndex], deepestNodeParents[commonIndex]];
|
|
if (parents[0]) {
|
|
let child = parents[0].lastChild;
|
|
while (child) {
|
|
if (child === parents[1]) {
|
|
deepestNodeIndex = i;
|
|
deepestNodeParents = currentNodeParents;
|
|
break;
|
|
} else if (child === parents[2]) {
|
|
break;
|
|
}
|
|
child = child.previousSibling;
|
|
}
|
|
}
|
|
}
|
|
return deepestNodeIndex;
|
|
}
|
|
function getNodeParents(node, limit) {
|
|
const parents = [];
|
|
let parent = node;
|
|
let parentParent;
|
|
while ((parentParent = getParent(parent)) && parent !== limit && parentParent !== parent.ownerDocument) {
|
|
parents.unshift(parent);
|
|
parent = parentParent;
|
|
}
|
|
return parents;
|
|
}
|
|
function zIndexIsHigherThan(higherNode, lowerNode) {
|
|
const higherIndex = parseInt(getWindow(higherNode).getComputedStyle(higherNode).zIndex, 10) || 0;
|
|
const lowerIndex = parseInt(getWindow(lowerNode).getComputedStyle(lowerNode).zIndex, 10) || 0;
|
|
return higherIndex >= lowerIndex;
|
|
}
|
|
function matchesUpTo(element, selector, limit) {
|
|
while (is.element(element)) {
|
|
if (matchesSelector(element, selector)) {
|
|
return true;
|
|
}
|
|
element = parentNode(element);
|
|
if (element === limit) {
|
|
return matchesSelector(element, selector);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function getActualElement(element) {
|
|
return element.correspondingUseElement || element;
|
|
}
|
|
function getScrollXY(relevantWindow) {
|
|
relevantWindow = relevantWindow || win;
|
|
return {
|
|
x: relevantWindow.scrollX || relevantWindow.document.documentElement.scrollLeft,
|
|
y: relevantWindow.scrollY || relevantWindow.document.documentElement.scrollTop
|
|
};
|
|
}
|
|
function getElementClientRect(element) {
|
|
const clientRect = element instanceof domObjects.SVGElement ? element.getBoundingClientRect() : element.getClientRects()[0];
|
|
return clientRect && {
|
|
left: clientRect.left,
|
|
right: clientRect.right,
|
|
top: clientRect.top,
|
|
bottom: clientRect.bottom,
|
|
width: clientRect.width || clientRect.right - clientRect.left,
|
|
height: clientRect.height || clientRect.bottom - clientRect.top
|
|
};
|
|
}
|
|
function getElementRect(element) {
|
|
const clientRect = getElementClientRect(element);
|
|
if (!browser.isIOS7 && clientRect) {
|
|
const scroll = getScrollXY(getWindow(element));
|
|
clientRect.left += scroll.x;
|
|
clientRect.right += scroll.x;
|
|
clientRect.top += scroll.y;
|
|
clientRect.bottom += scroll.y;
|
|
}
|
|
return clientRect;
|
|
}
|
|
function getPath(node) {
|
|
const path = [];
|
|
while (node) {
|
|
path.push(node);
|
|
node = parentNode(node);
|
|
}
|
|
return path;
|
|
}
|
|
function trySelector(value) {
|
|
if (!is.string(value)) {
|
|
return false;
|
|
}
|
|
|
|
// an exception will be raised if it is invalid
|
|
domObjects.document.querySelector(value);
|
|
return true;
|
|
}
|
|
export { closest, getActualElement, getElementClientRect, getElementRect, getPath, getScrollXY, indexOfDeepestElement, matchesSelector, matchesUpTo, nodeContains, parentNode, trySelector };
|
|
//# sourceMappingURL=domUtils.js.map
|