UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

316 lines (224 loc) 10.3 kB
/* Siesta 5.6.1 Copyright(c) 2009-2022 Bryntum AB https://bryntum.com/contact https://bryntum.com/products/siesta/license */ Role('Siesta.Util.Role.Dom', { does : [ Siesta.Util.Role.CanCalculatePageScroll ], has : { doesNotIncludeMarginInBodyOffset : false }, methods : { // Returns document or shadowRoot for a child element of a nested context (iframe / web component) which provides API for querySelector and elementFromPoint getQueryableContainer : function (el) { return el ? (el.getRootNode ? el.getRootNode() : el.ownerDocument) : this.global.document; }, // Returns the queryable root of an iframe or web component getQueryableContainerForNestedContext : function (el) { return el.shadowRoot || el.contentWindow.document; }, // Returns the root element of an element, the document (<HTML>) node or a shadowroot getRootElement : function (el) { if (el.getRootNode) { var rootNode = el.getRootNode(); if (rootNode.constructor.name === 'ShadowRoot' || rootNode.nodeType === 11) { return rootNode; } } return el.ownerDocument.documentElement; }, // Returns top most parent node of the visible DOM of an element, the body (<BODY>) node for non-web components getBodyElement : function (el) { var doc = this.getQueryableContainer(el); if (doc.constructor.name === 'ShadowRoot' || doc.nodeType === 11) { return doc; } return doc.body; }, isCrossOriginWindow : function (win) { try { var doc = win.document; } catch (e) { return true } // Safari doesn't throw exception when trying to access x-domain frames return !doc }, closest : function (elem, selector, maxLevels) { maxLevels = maxLevels || Number.MAX_VALUE; var rootEl = this.getRootElement(elem); // Get closest match for (var i = 0; i < maxLevels && elem && elem !== rootEl; elem = elem.parentNode) { if (Siesta.Sizzle.matchesSelector(elem, selector)) { return elem; } i++; } return false; }, contains : function (parentEl, childEl) { if (!parentEl) return false if (parentEl.contains) return parentEl.contains(childEl) // SVG elements in IE does not have "contains" method if (parentEl.compareDocumentPosition) return parentEl === childEl || Boolean(parentEl.compareDocumentPosition(childEl) & 16) throw new Error("Can't determine `contains` status") }, matches : function (node, selector) { return Siesta.Sizzle.matchesSelector(node, selector); }, // returns { left : Number, top : Number } object in page coordinates offset : function (elem) { if (!elem) return null var doc = elem.ownerDocument; if (!doc) return null if (elem === doc.body) return this.bodyOffset(elem); var box = this.getBoundingClientRect(elem) var win = doc.defaultView || doc.parentWindow return box ? { left : this.viewportXtoPageX(Math.floor(box.left), win), top : this.viewportYtoPageY(Math.floor(box.top), win) } : { left : 0, top : 0 } }, bodyOffset: function (body) { var top = body.offsetTop, left = body.offsetLeft; this.initializeOffset(); if (this.doesNotIncludeMarginInBodyOffset) { var style = getComputedStyle(body); top += parseFloat(style.marginTop) || 0; left += parseFloat(style.marginLeft) || 0; } return { top: top, left: left }; }, initializeOffset: function () { var body = document.body, container = document.createElement("div"), bodyMarginTop = parseFloat(getComputedStyle(body).marginTop) || 0, html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>"; var styles = { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" }; for (var o in styles) { container.style[ o ] = styles[ o ]; } container.innerHTML = html; body.insertBefore(container, body.firstChild); var innerDiv = container.firstChild; var checkDiv = innerDiv.firstChild; var td = innerDiv.nextSibling.firstChild.firstChild; checkDiv.style.position = "fixed"; checkDiv.style.top = "20px"; checkDiv.style.position = checkDiv.style.top = ""; innerDiv.style.overflow = "hidden"; innerDiv.style.position = "relative"; this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop); body.removeChild(container); this.initializeOffset = function () {}; }, getElementWidth : function (el) { return this.getBoundingClientRect(el).width; }, getElementHeight : function (el) { return this.getBoundingClientRect(el).height; }, getWindowSize : function (win) { var doc = win.document return { width : win.innerWidth || doc.documentElement.clientWidth || doc.body.clientWidth, height : win.innerHeight || doc.documentElement.clientHeight || doc.body.clientHeight } }, isPointWithinElement : function (x, y, el) { var rect = this.getBoundingClientRect(el); return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom; }, isElementReachableAt : function (el, pageX, pageY, allowChild) { allowChild = allowChild !== false var doc = el.ownerDocument var win = doc.defaultView || doc.parentWindow var foundEl = this.getQueryableContainer(el).elementFromPoint(this.pageXtoViewportX(pageX, win), this.pageYtoViewportY(pageY, win)) return foundEl && (foundEl === el || allowChild && this.contains(el, foundEl)) }, isElementReachableAtCenter : function (el, allowChild) { allowChild = allowChild !== false var offsets = this.offset(el); return this.isElementReachableAt( el, offsets.left + (this.getElementWidth(el) / 2), offsets.top + (this.getElementHeight(el) / 2), allowChild ); }, // patched version to support SVG in IE11/Edge getBoundingClientRect : function (el) { var svgEl = el.ownerSVGElement; if (svgEl && (bowser.msie || bowser.msedge || bowser.gecko)) { var elBox = el.getBBox(), svgRect = svgEl.getBoundingClientRect(), left = svgRect.left + elBox.x, top = svgRect.top + elBox.y, right = left + elBox.width, bottom = top + elBox.height; return { x : left, y : top, left : left, top : top, bottom : bottom, height : elBox.height, width : elBox.width }; } else { return el.getBoundingClientRect(); } }, nodeIsUnloaded : function (el) { try { // throws if accessed when element belonged to an iframe that's no longer in DOM el && el.tagName var doc = el.ownerDocument var win = doc && (doc.defaultView || doc.parentWindow) return !Boolean(win) } catch (e) { // exception here probably means the "lastOverEl" is from freed context (unloaded page) // access to such elements throws exceptions in IE el = null return true } }, nodeIsOrphan : function (el) { if (el.constructor.name === 'ShadowRoot' || el.nodeType === 11) { el = el.host; } var docOrHtmlFragment = this.getRootElement(el) // Detached doc fragment ? if (docOrHtmlFragment && docOrHtmlFragment.nodeType === 11) { return !docOrHtmlFragment.isConnected; } return !docOrHtmlFragment || !docOrHtmlFragment.contains(el); }, getNodeParents : function (node) { var nodes = []; for (; node && node.parentNode; node = node.parentNode) { nodes.unshift(node); } return nodes; }, getCommonAncestor : function (node1, node2) { var parents1 = this.getNodeParents(node1); var parents2 = this.getNodeParents(node2); // Make sure both nodes are part of same DOM tree if (parents1[ 0 ] != parents2[ 0 ]) return null; for (var i = 0; i < parents1.length; i++) { if (parents1[ i ] !== parents2[ i ]) { return parents1[ i - 1 ]; } } } } })