Add hover handler. With this handler user-defined actions can be triggered as the mouse moves over the map and pauses. An example of use is send WMS/GetFeatureInfo requests. r=crschmidt,tschaub (closes #1255)
git-svn-id: http://svn.openlayers.org/trunk/openlayers@5746 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
209
examples/hover-handler.html
Normal file
209
examples/hover-handler.html
Normal file
@@ -0,0 +1,209 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>OpenLayers Hover Handler Example</title>
|
||||
|
||||
<style type="text/css">
|
||||
#map {
|
||||
width: 340px;
|
||||
height: 170px;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
#west {
|
||||
width: 350px;
|
||||
}
|
||||
#east {
|
||||
position: absolute;
|
||||
left: 370px;
|
||||
top: 3em;
|
||||
}
|
||||
|
||||
table td {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
textarea.output {
|
||||
text-align: left;
|
||||
font-size: 0.9em;
|
||||
width: 250px;
|
||||
height: 65px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
<script src="../lib/Firebug/firebug.js"></script>
|
||||
<script src="../lib/OpenLayers.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
OpenLayers.Control.Hover = OpenLayers.Class(OpenLayers.Control, {
|
||||
defaultHandlerOptions: {
|
||||
'delay': 500,
|
||||
'pixelTolerance': null,
|
||||
'stopMove': false
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
this.handlerOptions = OpenLayers.Util.extend(
|
||||
{}, this.defaultHandlerOptions
|
||||
);
|
||||
OpenLayers.Control.prototype.initialize.apply(
|
||||
this, arguments
|
||||
);
|
||||
this.handler = new OpenLayers.Handler.Hover(
|
||||
this,
|
||||
{'pause': this.onPause, 'move': this.onMove},
|
||||
this.handlerOptions
|
||||
);
|
||||
},
|
||||
|
||||
onPause: function(evt) {
|
||||
var output = document.getElementById(this.key + 'Output');
|
||||
var msg = 'pause ' + evt.xy;
|
||||
output.value = output.value + msg + "\r\n";
|
||||
},
|
||||
|
||||
onMove: function(evt) {
|
||||
// if this control sent an Ajax request (e.g. GetFeatureInfo) when
|
||||
// the mouse pauses the onMove callback could be used to abort that
|
||||
// request.
|
||||
}
|
||||
});
|
||||
|
||||
var map, controls;
|
||||
|
||||
function init(){
|
||||
|
||||
map = new OpenLayers.Map('map');
|
||||
var layer = new OpenLayers.Layer.WMS(
|
||||
'OpenLayers WMS',
|
||||
'http://labs.metacarta.com/wms/vmap0',
|
||||
{layers: 'basic'}
|
||||
);
|
||||
map.addLayers([layer]);
|
||||
|
||||
controls = {
|
||||
'long': new OpenLayers.Control.Hover({
|
||||
handlerOptions: {
|
||||
'delay': 2000
|
||||
}
|
||||
}),
|
||||
'short': new OpenLayers.Control.Hover({
|
||||
handlerOptions: {
|
||||
'delay': 100
|
||||
}
|
||||
}),
|
||||
'tolerant': new OpenLayers.Control.Hover({
|
||||
handlerOptions: {
|
||||
'delay': 1000,
|
||||
'pixelTolerance': 6
|
||||
}
|
||||
}),
|
||||
'untolerant': new OpenLayers.Control.Hover({
|
||||
handlerOptions: {
|
||||
'delay': 1000,
|
||||
'pixelTolerance': 1
|
||||
}
|
||||
}),
|
||||
'stoppropag': new OpenLayers.Control.Hover({
|
||||
handlerOptions: {
|
||||
'stopMove': true
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
var props = document.getElementById("props");
|
||||
var control;
|
||||
for(var key in controls) {
|
||||
control = controls[key];
|
||||
// only to route output here
|
||||
control.key = key;
|
||||
map.addControl(control);
|
||||
}
|
||||
|
||||
map.zoomToMaxExtent();
|
||||
}
|
||||
|
||||
function toggle(key) {
|
||||
var control = controls[key];
|
||||
if(control.active) {
|
||||
control.deactivate();
|
||||
} else {
|
||||
control.activate();
|
||||
}
|
||||
var status = document.getElementById(key + "Status");
|
||||
status.innerHTML = control.active ? "on" : "off";
|
||||
var output = document.getElementById(key + "Output");
|
||||
output.value = "";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init()">
|
||||
<h1 id="title">Hover Handler Example</h1>
|
||||
<div id="west">
|
||||
|
||||
<div id="tags">
|
||||
</div>
|
||||
|
||||
<p id="shortdesc">
|
||||
This example shows the use of the hover handler.
|
||||
</p>
|
||||
|
||||
<div id="map"></div>
|
||||
<p>
|
||||
The hover handler is to be used to emulate mouseovers on
|
||||
objects on the map that aren't DOM elements. For example
|
||||
one can use the hover hander to send WMS/GetFeatureInfo
|
||||
requests as the user moves the mouse over the map.
|
||||
</p>
|
||||
<p>
|
||||
The "delay" option specifies the number of milliseconds
|
||||
before the event is considered a hover. Default is 500
|
||||
milliseconds.
|
||||
</p>
|
||||
<p>
|
||||
The "pixelTolerance" option specifies the maximum number
|
||||
of pixels between mousemoves for an event to be
|
||||
considered a hover. Default is null, which means no
|
||||
pixel tolerance.
|
||||
</p>
|
||||
<p>
|
||||
The "stopMove" option specifies whether other mousemove
|
||||
listeners registered before the hover handler listener must
|
||||
be notified on mousemoves or not. Default is false (meaning
|
||||
that the other mousemove listeners will be notified on
|
||||
mousemove).
|
||||
</p>
|
||||
</div>
|
||||
<div id="east">
|
||||
<table>
|
||||
<caption>Controls with hover handlers (toggle on/off to clear output)</caption>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>long delay (2 sec)</td>
|
||||
<td><button id="longStatus" onclick="toggle('long')">off</button></td>
|
||||
<td><textarea class="output" id="longOutput"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>short delay (100 msec)</td>
|
||||
<td><button id="shortStatus" onclick="toggle('short')">off</button></td>
|
||||
<td><textarea class="output" id="shortOutput"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tolerant (6 pixels)</td>
|
||||
<td><button id="tolerantStatus" onclick="toggle('tolerant')">off</button></td>
|
||||
<td><textarea class="output" id="tolerantOutput"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>untolerant (1 pixel)</td>
|
||||
<td><button id="untolerantStatus" onclick="toggle('untolerant')">off</button></td>
|
||||
<td><textarea class="output" id="untolerantOutput"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>stop propagation</td>
|
||||
<td><button id="stoppropagStatus" onclick="toggle('stoppropag')">off</button></td>
|
||||
<td><textarea class="output" id="stoppropagOutput"></textarea></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -117,6 +117,7 @@
|
||||
"OpenLayers/Feature/WFS.js",
|
||||
"OpenLayers/Handler.js",
|
||||
"OpenLayers/Handler/Click.js",
|
||||
"OpenLayers/Handler/Hover.js",
|
||||
"OpenLayers/Handler/Point.js",
|
||||
"OpenLayers/Handler/Path.js",
|
||||
"OpenLayers/Handler/Polygon.js",
|
||||
|
||||
178
lib/OpenLayers/Handler/Hover.js
Normal file
178
lib/OpenLayers/Handler/Hover.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (c) 2006-2008 MetaCarta, Inc., published under the clear BSD license.
|
||||
* See http://svn.openlayers.org/trunk/openlayers/license.txt
|
||||
* for the full text of the license. */
|
||||
|
||||
/**
|
||||
* @requires OpenLayers/Handler.js
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Handler.Hover
|
||||
* The hover handler is to be used to emulate mouseovers on objects
|
||||
* on the map that aren't DOM elements. For example one can use
|
||||
* this handler to send WMS/GetFeatureInfo requests as the user
|
||||
* moves the mouve over the map.
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Handler>
|
||||
*/
|
||||
OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
|
||||
|
||||
/**
|
||||
* APIProperty: delay
|
||||
* {Integer} - Number of milliseconds between mousemoves before
|
||||
* the event is considered a hover. Default is 500.
|
||||
*/
|
||||
delay: 500,
|
||||
|
||||
/**
|
||||
* APIProperty: pixelTolerance
|
||||
* {Integer} - Maximum number of pixels between mousemoves for
|
||||
* an event to be considered a hover. Default is null.
|
||||
*/
|
||||
pixelTolerance: null,
|
||||
|
||||
/**
|
||||
* APIProperty: stopMove
|
||||
* {Boolean} Stop other listeners from being notified on mousemoves.
|
||||
* Default is false.
|
||||
*/
|
||||
stopMove: false,
|
||||
|
||||
/**
|
||||
* Property: px
|
||||
* {<OpenLayers.Pixel>} The location of the last mousemove, expressed
|
||||
* in pixels.
|
||||
*/
|
||||
px: null,
|
||||
|
||||
/**
|
||||
* Property: timerId
|
||||
* {Number} The id of the timer.
|
||||
*/
|
||||
timerId: null,
|
||||
|
||||
/**
|
||||
* Constructor: OpenLayers.Handler.Hover
|
||||
* Construct a hover handler.
|
||||
*
|
||||
* Parameters:
|
||||
* control - {<OpenLayers.Control>} The control that initialized this
|
||||
* handler. The control is assumed to have a valid map property; that
|
||||
* map is used in the handler's own setMap method.
|
||||
* callbacks - {Object} An object whose properties correspond to abstracted
|
||||
* events or sequences of browser events. The values for these
|
||||
* properties are functions defined by the control that get called by
|
||||
* the handler.
|
||||
* options - {Object} An optional object whose properties will be set on
|
||||
* the handler.
|
||||
*/
|
||||
initialize: function(control, callbacks, options) {
|
||||
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: mousemove
|
||||
* Called when the mouse moves on the map.
|
||||
*
|
||||
* Parameters:
|
||||
* evt - {<OpenLayers.Event>}
|
||||
*
|
||||
* Returns:
|
||||
* {Boolean} Continue propagating this event.
|
||||
*/
|
||||
mousemove: function(evt) {
|
||||
if(this.passesTolerance(evt.xy)) {
|
||||
this.clearTimer();
|
||||
this.callback('move', [evt]);
|
||||
this.px = evt.xy;
|
||||
this.timerId = window.setTimeout(
|
||||
OpenLayers.Function.bind(this.delayedCall, this, evt),
|
||||
this.delay
|
||||
);
|
||||
}
|
||||
return !this.stopMove;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: mouseout
|
||||
* Called when the mouse goes out of the map.
|
||||
*
|
||||
* Parameters:
|
||||
* evt - {<OpenLayers.Event>}
|
||||
*
|
||||
* Returns:
|
||||
* {Boolean} Continue propagating this event.
|
||||
*/
|
||||
mouseout: function(evt) {
|
||||
if (OpenLayers.Util.mouseLeft(evt, this.map.div)) {
|
||||
this.clearTimer();
|
||||
this.callback('move', [evt]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: passesTolerance
|
||||
* Determine whether the mouse move is within the optional pixel tolerance.
|
||||
*
|
||||
* Parameters:
|
||||
* px - {<OpenLayers.Pixel>}
|
||||
*
|
||||
* Returns:
|
||||
* {Boolean} The mouse move is within the pixel tolerance.
|
||||
*/
|
||||
passesTolerance: function(px) {
|
||||
var passes = true;
|
||||
if(this.pixelTolerance && this.px) {
|
||||
var dpx = Math.sqrt(
|
||||
Math.pow(this.px.x - px.x, 2) +
|
||||
Math.pow(this.px.y - px.y, 2)
|
||||
);
|
||||
if(dpx < this.pixelTolerance) {
|
||||
passes = false;
|
||||
}
|
||||
}
|
||||
return passes;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: clearTimer
|
||||
* Clear the timer and set <timerId> to null.
|
||||
*/
|
||||
clearTimer: function() {
|
||||
if(this.timerId != null) {
|
||||
window.clearTimeout(this.timerId);
|
||||
this.timerId = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: delayedCall
|
||||
* Triggers pause callback.
|
||||
*
|
||||
* Parameters:
|
||||
* evt - {<OpenLayers.Event>}
|
||||
*/
|
||||
delayedCall: function(evt) {
|
||||
this.callback('pause', [evt]);
|
||||
},
|
||||
|
||||
/**
|
||||
* APIMethod: deactivate
|
||||
* Deactivate the handler.
|
||||
*
|
||||
* Returns:
|
||||
* {Boolean} The handler was successfully deactivated.
|
||||
*/
|
||||
deactivate: function() {
|
||||
var deactivated = false;
|
||||
if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
|
||||
this.clearTimer();
|
||||
deactivated = true;
|
||||
}
|
||||
return deactivated;
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Handler.Hover"
|
||||
});
|
||||
136
tests/Handler/test_Hover.html
Normal file
136
tests/Handler/test_Hover.html
Normal file
@@ -0,0 +1,136 @@
|
||||
<html>
|
||||
<head>
|
||||
<script src="../../lib/OpenLayers.js"></script>
|
||||
<script type="text/javascript">
|
||||
function test_Handler_Hover_events(t) {
|
||||
t.plan(10);
|
||||
|
||||
var map = new OpenLayers.Map('map');
|
||||
var control = {
|
||||
map: map
|
||||
};
|
||||
map.events.registerPriority = function(type, obj, func) {
|
||||
var r = func();
|
||||
if(typeof r == "string") {
|
||||
// this is one of the mock handler methods
|
||||
t.eq(OpenLayers.Util.indexOf(nonevents, type), -1,
|
||||
"registered method is not one of the events " +
|
||||
"that should not be handled");
|
||||
t.ok(OpenLayers.Util.indexOf(events, type) > -1,
|
||||
"activate calls registerPriority with browser event: " + type);
|
||||
t.eq(typeof func, "function",
|
||||
"activate calls registerPriority with a function");
|
||||
t.eq(func(), type,
|
||||
"activate calls registerPriority with the correct method");
|
||||
t.eq(obj["CLASS_NAME"], "OpenLayers.Handler.Hover",
|
||||
"activate calls registerPriority with the handler");
|
||||
}
|
||||
}
|
||||
function setMethod(key) {
|
||||
handler[key] = function() {return key};
|
||||
}
|
||||
|
||||
// list below events that should be handled (events) and those
|
||||
// that should not be handled (nonevents) by the handler
|
||||
var events = ["mousemove", "mouseout"];
|
||||
var nonevents = ["mousedown", "mouseup", "click", "dblclick", "resize", "focus", "blur"];
|
||||
var handler = new OpenLayers.Handler.Hover(control);
|
||||
// set browser event like properties on the handler
|
||||
for(var i=0; i<events.length; ++i) {
|
||||
setMethod(events[i]);
|
||||
}
|
||||
handler.activate();
|
||||
}
|
||||
|
||||
function test_Handler_Hover_callbacks(t) {
|
||||
t.plan(8);
|
||||
|
||||
var map = new OpenLayers.Map('map', {controls: []});
|
||||
|
||||
var control = {
|
||||
map: map
|
||||
};
|
||||
|
||||
var timers = {};
|
||||
var sto = window.setTimeout;
|
||||
window.setTimeout = function(func, delay) {
|
||||
var key = Math.random();
|
||||
timers[key] = true;
|
||||
t.ok(typeof func == "function",
|
||||
"setTimeout called with a function");
|
||||
t.eq(delay, handler.delay,
|
||||
"setTimeout called with proper delay");
|
||||
// execute function that is supposed to be delayed
|
||||
func();
|
||||
return key;
|
||||
}
|
||||
var cto = window.clearTimeout;
|
||||
window.clearTimeout = function(key) {
|
||||
if(timers[key] === true) {
|
||||
delete timers[key];
|
||||
} else {
|
||||
t.fail("clearTimeout called with non-existent timerId");
|
||||
}
|
||||
}
|
||||
|
||||
var handler = new OpenLayers.Handler.Hover(control, {});
|
||||
handler.activate();
|
||||
var testEvt;
|
||||
|
||||
// test pause and move callbacks - four tests here (2 from setTimeout above)
|
||||
testEvt = Math.random();
|
||||
handler.callbacks = {
|
||||
"pause": function(evt) {
|
||||
t.eq(evt, testEvt,
|
||||
"pause callback called with correct evt");
|
||||
},
|
||||
"move": function(evt) {
|
||||
t.eq(evt, testEvt,
|
||||
"move callback called with correct evt");
|
||||
}
|
||||
};
|
||||
map.events.triggerEvent("mousemove", testEvt);
|
||||
handler.clearTimer();
|
||||
|
||||
// test pixelTolerance - four tests here (2 from setTimeout above)
|
||||
handler.pixelTolerance = 2;
|
||||
handler.px = new OpenLayers.Pixel(0, 0);
|
||||
testEvt = {
|
||||
xy: new OpenLayers.Pixel(0, 1)
|
||||
};
|
||||
// mouse moves one pixel, callbacks shouldn't be called
|
||||
handler.callbacks = {
|
||||
"pause": function(evt) {
|
||||
t.fail("(pixelTolerance met) pause callback shouldn't be called");
|
||||
},
|
||||
"move": function(evt) {
|
||||
t.fail("(pixelTolerance met) move callback shoudln't be called");
|
||||
}
|
||||
};
|
||||
map.events.triggerEvent("mousemove", testEvt);
|
||||
handler.clearTimer();
|
||||
handler.px = new OpenLayers.Pixel(0, 0);
|
||||
testEvt = {
|
||||
xy: new OpenLayers.Pixel(3, 3)
|
||||
};
|
||||
// mouse moves 3x3 pixels, callbacks should be called
|
||||
handler.callbacks = {
|
||||
"pause": function(evt) {
|
||||
t.ok(evt == testEvt, "(pixelTolerance unmet) pause callback called");
|
||||
},
|
||||
"move": function(evt) {
|
||||
t.ok(evt == testEvt, "(pixelTolerance unmet) move callback called");
|
||||
}
|
||||
};
|
||||
map.events.triggerEvent("mousemove", testEvt);
|
||||
handler.clearTimer();
|
||||
|
||||
window.setTimeout = sto;
|
||||
window.clearTimeout = cto;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map" style="width: 300px; height: 150px;"/>
|
||||
</body>
|
||||
</html>
|
||||
@@ -88,6 +88,7 @@
|
||||
<li>Control/test_SelectFeature.html</li>
|
||||
<li>test_Handler.html</li>
|
||||
<li>Handler/test_Click.html</li>
|
||||
<li>Handler/test_Hover.html</li>
|
||||
<li>Handler/test_Drag.html</li>
|
||||
<li>Handler/test_Feature.html</li>
|
||||
<li>Handler/test_Keyboard.html</li>
|
||||
|
||||
Reference in New Issue
Block a user