Skip to content

Commit

Permalink
Make it so getMousePosition does not report incorrect position when b…
Browse files Browse the repository at this point in the history
…orders are used in containing elements, by replacing the pagePosition method with a new one and attaching map events to the internal viewport div instead of the user provided map div. r=erilem,tschaub (closes #2247)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@10871 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
  • Loading branch information
ahocevar committed Nov 5, 2010
1 parent e40bb1f commit a592663
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 40 deletions.
33 changes: 33 additions & 0 deletions build/license.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
9 changes: 3 additions & 6 deletions lib/OpenLayers/Events.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
];
}

Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion lib/OpenLayers/Handler/Drag.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
},
Expand Down
3 changes: 1 addition & 2 deletions lib/OpenLayers/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -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});
Expand Down Expand Up @@ -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();
Expand Down
153 changes: 122 additions & 31 deletions lib/OpenLayers/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

var element = forElement;
var child = forElement;
while(element) {

if(element == document.body) {
if(OpenLayers.Element.getStyle(child, 'position') == 'absolute') {
break;
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 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;
}

// 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;
}
}

element = forElement;
while(element) {
valueT -= element.scrollTop || 0;
valueL -= element.scrollLeft || 0;
element = element.parentNode;
// 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;
}
}

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
Expand Down
103 changes: 103 additions & 0 deletions tests/manual/page-position.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Page Position Test</title>

<link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
<link rel="stylesheet" href="../../examples/style.css" type="text/css" />
<style type="text/css">
#mapwrap {
border: 10px solid red;
width: 532px;
height: 276px;
}
#map {
position: absolute;
border: 10px solid #ccc;
width: 512px;
height: 256px;
}
#controlToggle li {
list-style: none;
}
p {
width: 512px;
}
#scrollspace {
height: 500px;
}
</style>
<script src="../../lib/OpenLayers.js"></script>
<script type="text/javascript">
var map, drawControls;
function init(){
map = new OpenLayers.Map('map');

var wmsLayer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0?", {layers: 'basic'});

var lineLayer = new OpenLayers.Layer.Vector("Line Layer");

map.addLayers([wmsLayer, lineLayer]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
map.addControl(new OpenLayers.Control.MousePosition());

drawControl = new OpenLayers.Control.DrawFeature(lineLayer,
OpenLayers.Handler.Path);

map.addControl(drawControl);

map.setCenter(new OpenLayers.LonLat(0, 0), 3);

document.getElementById('noneToggle').checked = true;
}

function toggleControl(element) {
var control = drawControl;
if(element.value == "draw" && element.checked) {
control.activate();
} else {
control.deactivate();
}
}
</script>
</head>
<body onload="init()">
<h1 id="title">OpenLayers Page Position Test</h1>

<p id="shortdesc">
Test if borders and scroll position cause unwanted offsets on the
mouse positions reported by map events.
</p>
<div id="mapwrap">
<div id="map"></div>
</div>

<ul id="controlToggle">
<li>
<input type="radio" name="type" value="none" id="noneToggle"
onclick="toggleControl(this);" checked="checked" />
<label for="noneToggle">navigate</label>
</li>
<li>
<input type="radio" name="type" value="draw" id="lineToggle" onclick="toggleControl(this);" />
<label for="lineToggle">draw line</label>
</li>
</ul>

<div id="docs">
<p>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.</p>
<p>With the line drawing control active, click on the map to add a
point. The point should be drawn at the exact mouse location.</p>
<p>With the navigation control active, shift-drag a zoom rectangle.
The rectangle's corner should align exactly with the mouse
cursor.</p>
<p>Scroll the page and repeat the above tests.</p>
<div id="scrollspace"><div>
</div>
</body>
</html>

0 comments on commit a592663

Please sign in to comment.