diff --git a/build/license.txt b/build/license.txt index cb99f12465..312c5e4b17 100644 --- a/build/license.txt +++ b/build/license.txt @@ -90,3 +90,36 @@ * issues. Applications that use the code below will continue to work seamlessly * when that happens. */ + +/** + * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is + * Copyright (c) 2006, Yahoo! Inc. + * All rights reserved. + * + * Redistribution and use of this software in source and binary forms, with or + * without modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Yahoo! Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission of Yahoo! Inc. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ \ No newline at end of file diff --git a/lib/OpenLayers/Events.js b/lib/OpenLayers/Events.js index 588a10cbb7..82f4beb2a1 100644 --- a/lib/OpenLayers/Events.js +++ b/lib/OpenLayers/Events.js @@ -823,11 +823,10 @@ OpenLayers.Events = OpenLayers.Class({ } if (!this.element.scrolls) { + var viewportElement = OpenLayers.Util.getViewportElement(); this.element.scrolls = [ - (document.documentElement.scrollLeft - || document.body.scrollLeft), - (document.documentElement.scrollTop - || document.body.scrollTop) + viewportElement.scrollLeft, + viewportElement.scrollTop ]; } @@ -840,8 +839,6 @@ OpenLayers.Events = OpenLayers.Class({ if (!this.element.offsets) { this.element.offsets = OpenLayers.Util.pagePosition(this.element); - this.element.offsets[0] += this.element.scrolls[0]; - this.element.offsets[1] += this.element.scrolls[1]; } return new OpenLayers.Pixel( (evt.clientX + this.element.scrolls[0]) - this.element.offsets[0] diff --git a/lib/OpenLayers/Handler/Drag.js b/lib/OpenLayers/Handler/Drag.js index 562d2ae322..68a7e86d8e 100644 --- a/lib/OpenLayers/Handler/Drag.js +++ b/lib/OpenLayers/Handler/Drag.js @@ -397,7 +397,7 @@ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { * evt - {Object} */ adjustXY: function(evt) { - var pos = OpenLayers.Util.pagePosition(this.map.div); + var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv); evt.xy.x -= pos[0]; evt.xy.y -= pos[1]; }, diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index 12f1b9a1ca..57cc69640b 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -513,7 +513,7 @@ OpenLayers.Map = OpenLayers.Class({ this.viewPortDiv.appendChild(this.layerContainerDiv); this.events = new OpenLayers.Events(this, - this.div, + this.viewPortDiv, this.EVENT_TYPES, this.fallThrough, {includeXY: true}); @@ -610,7 +610,6 @@ OpenLayers.Map = OpenLayers.Class({ render: function(div) { this.div = OpenLayers.Util.getElement(div); OpenLayers.Element.addClass(this.div, 'olMap'); - this.events.attachToElement(this.div); this.viewPortDiv.parentNode.removeChild(this.viewPortDiv); this.div.appendChild(this.viewPortDiv); this.updateSize(); diff --git a/lib/OpenLayers/Util.js b/lib/OpenLayers/Util.js index 2862855cc2..f0e6725d45 100644 --- a/lib/OpenLayers/Util.js +++ b/lib/OpenLayers/Util.js @@ -1336,51 +1336,142 @@ OpenLayers.Util.safeStopPropagation = function(evt) { /** * Function: pagePositon - * Calculates the position of an element on the page. + * Calculates the position of an element on the page (see + * http://code.google.com/p/doctype/wiki/ArticlePageOffset) + * + * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is + * Copyright (c) 2006, Yahoo! Inc. + * All rights reserved. + * + * Redistribution and use of this software in source and binary forms, with or + * without modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Yahoo! Inc. nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission of Yahoo! Inc. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * * Parameters: * forElement - {DOMElement} * * Returns: - * {Array} two item array, L value then T value. + * {Array} two item array, Left value then Top value. */ -OpenLayers.Util.pagePosition = function(forElement) { - var valueT = 0, valueL = 0; +OpenLayers.Util.pagePosition = function(forElement) { + // NOTE: If element is hidden (display none or disconnected or any the + // ancestors are hidden) we get (0,0) by default but we still do the + // accumulation of scroll position. - var element = forElement; - var child = forElement; - while(element) { + var pos = [0, 0]; + var viewportElement = OpenLayers.Util.getViewportElement(); + if (!forElement || forElement == window || forElement == viewportElement) { + // viewport is always at 0,0 as that defined the coordinate system for + // this function - this avoids special case checks in the code below + return pos; + } - if(element == document.body) { - if(OpenLayers.Element.getStyle(child, 'position') == 'absolute') { - break; + // Gecko browsers normally use getBoxObjectFor to calculate the position. + // When invoked for an element with an implicit absolute position though it + // can be off by one. Therefore the recursive implementation is used in + // those (relatively rare) cases. + var BUGGY_GECKO_BOX_OBJECT = + OpenLayers.IS_GECKO && document.getBoxObjectFor && + OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' && + (forElement.style.top == '' || forElement.style.left == ''); + + var parent = null; + var box; + + if (forElement.getBoundingClientRect) { // IE + box = forElement.getBoundingClientRect(); + var scrollTop = viewportElement.scrollTop; + var scrollLeft = viewportElement.scrollLeft; + + pos[0] = box.left + scrollLeft; + pos[1] = box.top + scrollTop; + + } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) { // gecko + // Gecko ignores the scroll values for ancestors, up to 1.9. See: + // https://bugzilla.mozilla.org/show_bug.cgi?id=328881 and + // https://bugzilla.mozilla.org/show_bug.cgi?id=330619 + + box = document.getBoxObjectFor(forElement); + var vpBox = document.getBoxObjectFor(viewportElement); + pos[0] = box.screenX - vpBox.screenX; + pos[1] = box.screenY - vpBox.screenY; + + } else { // safari/opera + pos[0] = forElement.offsetLeft; + pos[1] = forElement.offsetTop; + parent = forElement.offsetParent; + if (parent != forElement) { + while (parent) { + pos[0] += parent.offsetLeft; + pos[1] += parent.offsetTop; + parent = parent.offsetParent; } } - - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - child = element; - try { - // wrapping this in a try/catch because IE chokes on the offsetParent - element = element.offsetParent; - } catch(e) { - OpenLayers.Console.error(OpenLayers.i18n( - "pagePositionFailed",{'elemId':element.id})); - break; + var browser = OpenLayers.BROWSER_NAME; + + // opera & (safari absolute) incorrectly account for body offsetTop + if (browser == "opera" || (browser == "safari" && + OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) { + pos[1] -= document.body.offsetTop; + } + + // accumulate the scroll positions for everything but the body element + parent = forElement.offsetParent; + while (parent && parent != document.body) { + pos[0] -= parent.scrollLeft; + // see https://bugs.opera.com/show_bug.cgi?id=249965 + if (browser != "opera" || parent.tagName != 'TR') { + pos[1] -= parent.scrollTop; + } + parent = parent.offsetParent; } } - - element = forElement; - while(element) { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - element = element.parentNode; - } - return [valueL, valueT]; + return pos; }; +/** + * Function: getViewportElement + * Returns die viewport element of the document. The viewport element is + * usually document.documentElement, except in IE,where it is either + * document.body or document.documentElement, depending on the document's + * compatibility mode (see + * http://code.google.com/p/doctype/wiki/ArticleClientViewportElement) + */ +OpenLayers.Util.getViewportElement = function() { + var viewportElement = arguments.callee.viewportElement; + if (viewportElement == undefined) { + viewportElement = (OpenLayers.BROWSER_NAME == "msie" && + document.compatMode != 'CSS1Compat') ? document.body : + document.documentElement; + arguments.callee.viewportElement = viewportElement; + } + return viewportElement; +}; /** * Function: isEquivalentUrl diff --git a/tests/manual/page-position.html b/tests/manual/page-position.html new file mode 100644 index 0000000000..a59dfdefb9 --- /dev/null +++ b/tests/manual/page-position.html @@ -0,0 +1,103 @@ + + +
++ Test if borders and scroll position cause unwanted offsets on the + mouse positions reported by map events. +
+This map's div has a border and absolute positioning, wrapped + by a container which also has a border. The page is also + scrollable. Neither the borders nor scrolling the page should + result in unwanted offsets on pixel positions reported by map + events.
+With the line drawing control active, click on the map to add a + point. The point should be drawn at the exact mouse location.
+With the navigation control active, shift-drag a zoom rectangle. + The rectangle's corner should align exactly with the mouse + cursor.
+Scroll the page and repeat the above tests.
+