diff --git a/source/dom/dom.js b/source/dom/dom.js index 8d091b129..6d4b55903 100644 --- a/source/dom/dom.js +++ b/source/dom/dom.js @@ -69,6 +69,20 @@ enyo.dom = { } return 320; }, + getWindowHeight: function() { + if (window.innerHeight) { + return window.innerHeight; + } + if (document.body && document.body.offsetHeight) { + return document.body.offsetHeight; + } + if (document.compatMode=='CSS1Compat' && + document.documentElement && + document.documentElement.offsetHeight ) { + return document.documentElement.offsetHeight; + } + return 480; + }, // moved from FittableLayout.js into common protected code _ieCssToPixelValue: function(inNode, inValue) { var v = inValue; @@ -124,5 +138,38 @@ enyo.dom = { //* Gets the calculated margin of a node. calcMarginExtents: function(inNode) { return this.calcBoxExtents(inNode, "margin"); + }, + //* Returns an object like `{top: 0, left: 0, bottom: 100, right: 100, height: 10, width: 10}` that represents the object's position within the viewport. Negative values mean part of the object is not visible. + calcViewportPositionForNode: function(inNode) { + // Parse upward and grab our positioning relative to the viewport + var left = top = 0, + node = inNode, + width = node.offsetWidth, + height = node.offsetHeight, + docHeight = (document.body.parentNode.offsetHeight > this.getWindowHeight() ? this.getWindowHeight() - document.body.parentNode.scrollTop : document.body.parentNode.offsetHeight), + docWidth = (document.body.parentNode.offsetWidth > this.getWindowWidth() ? this.getWindowWidth() - document.body.parentNode.scrollLeft : document.body.parentNode.offsetWidth), + transformProp = enyo.dom.getStyleTransformProp(), + xregex = /translateX\((-?\d+)px\)/i, + yregex = /translateY\((-?\d+)px\)/i; + if (node.offsetParent) { + do { + left += node.offsetLeft - (node.offsetParent ? node.offsetParent.scrollLeft : 0); + if (transformProp && xregex.test(node.style[transformProp])) { + left += parseInt(node.style[transformProp].replace(xregex, '$1')); + } + top += node.offsetTop - (node.offsetParent ? node.offsetParent.scrollTop : 0); + if (transformProp && yregex.test(node.style[transformProp])) { + top += parseInt(node.style[transformProp].replace(yregex, '$1')); + } + } while (node = node.offsetParent); + } + return { + 'top': top, + 'left': left, + 'bottom': docHeight - top - height, + 'right': docWidth - left - width, + 'height': height, + 'width': width + }; } }; diff --git a/tools/test/core/tests/ViewportPositioningTest.js b/tools/test/core/tests/ViewportPositioningTest.js new file mode 100644 index 000000000..195c0f1fb --- /dev/null +++ b/tools/test/core/tests/ViewportPositioningTest.js @@ -0,0 +1,53 @@ +enyo.kind({ + name: "ViewportPositioningTest", + kind: enyo.TestSuite, + + testMeasuringViewportCoordinates: function() { + var K = enyo.kind({ + kind: enyo.Control, + style: "position: absolute; top: 10px; left: 10px; width: 10px; height: 10px;" + }); + + // Similar to ControlTest, create a testing div to test DOM information with (delete at end) + var div = document.createElement("div"); + document.body.appendChild(div); + // Apply fixed positioning so that we know where things should be relative to the viewport + div.style.position = 'fixed'; + div.style.top = '0px'; + div.style.left = '0px'; + div.style.width = '100%'; + div.style.height = '100%'; + + var k = new K(); + k.renderInto(div); + + var p = enyo.dom.calcViewportPositionForNode(k.hasNode()), + // Bottom and right require calculating viewport size + fromBottom = (document.body.parentNode.offsetHeight > enyo.dom.getWindowHeight() ? enyo.dom.getWindowHeight() - document.body.parentNode.scrollTop : document.body.parentNode.offsetHeight) - 20, + fromRight = (document.body.parentNode.offsetWidth > enyo.dom.getWindowWidth() ? enyo.dom.getWindowWidth() - document.body.parentNode.scrollLeft : document.body.parentNode.offsetWidth) - 20; + if (p.top !== 10) { + this.log('top failed with value: ' + p.top + ' (should be 10)'); + } + if (p.left !== 10) { + this.log('left failed with value: ' + p.left + ' (should be 10)'); + } + if (p.bottom !== fromBottom) { + this.log('bottom measurement failed with value: ' + p.bottom + ' (should be ' + fromBottom + ')'); + } + if (p.right !== fromRight) { + this.log('right measurement failed with value: ' + p.right + ' (should be ' + fromRight + ')'); + } + if (p.width !== 10) { + this.log('width failed with value: ' + p.width + ' (should be 10)'); + } + if (p.height !== 10) { + this.log('height failed with value: ' + p.height + ' (should be 10)'); + } + + // Clean up + k.destroy(); + document.body.removeChild(div); + + this.finish(this.logMessages && this.logMessages.length ? 'Following measurements failed:' : ''); + } +}); \ No newline at end of file diff --git a/tools/test/core/tests/package.js b/tools/test/core/tests/package.js index c947c451d..62a7596db 100644 --- a/tools/test/core/tests/package.js +++ b/tools/test/core/tests/package.js @@ -10,5 +10,6 @@ enyo.depends( "ControlTest.js", "ControlPropsTest.js", "DecodePackagePathTest.js", - "PathResolverTest.js" + "PathResolverTest.js", + "ViewportPositioningTest.js" );