import browser from "./browser.js"; import domObjects from "./domObjects.js"; import is from "./is.js"; import * as win from "./window.js"; export function nodeContains(parent, child) { if (parent.contains) { return parent.contains(child); } while (child) { if (child === parent) { return true; } child = child.parentNode; } return false; } export function closest(element, selector) { while (is.element(element)) { if (matchesSelector(element, selector)) { return element; } element = parentNode(element); } return null; } export 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; } export function matchesSelector(element, selector) { // remove /deep/ from selectors if shadowDOM polyfill is used if (win.window !== 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 export 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]]; 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(win.getWindow(higherNode).getComputedStyle(higherNode).zIndex, 10) || 0; const lowerIndex = parseInt(win.getWindow(lowerNode).getComputedStyle(lowerNode).zIndex, 10) || 0; return higherIndex >= lowerIndex; } export 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; } export function getActualElement(element) { return element.correspondingUseElement || element; } export function getScrollXY(relevantWindow) { relevantWindow = relevantWindow || win.window; return { x: relevantWindow.scrollX || relevantWindow.document.documentElement.scrollLeft, y: relevantWindow.scrollY || relevantWindow.document.documentElement.scrollTop }; } export 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 }; } export function getElementRect(element) { const clientRect = getElementClientRect(element); if (!browser.isIOS7 && clientRect) { const scroll = getScrollXY(win.getWindow(element)); clientRect.left += scroll.x; clientRect.right += scroll.x; clientRect.top += scroll.y; clientRect.bottom += scroll.y; } return clientRect; } export function getPath(node) { const path = []; while (node) { path.push(node); node = parentNode(node); } return path; } export function trySelector(value) { if (!is.string(value)) { return false; } // an exception will be raised if it is invalid domObjects.document.querySelector(value); return true; } //# sourceMappingURL=domUtils.js.map