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:
@@ -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/Feature/WFS.js",
|
||||||
"OpenLayers/Handler.js",
|
"OpenLayers/Handler.js",
|
||||||
"OpenLayers/Handler/Click.js",
|
"OpenLayers/Handler/Click.js",
|
||||||
|
"OpenLayers/Handler/Hover.js",
|
||||||
"OpenLayers/Handler/Point.js",
|
"OpenLayers/Handler/Point.js",
|
||||||
"OpenLayers/Handler/Path.js",
|
"OpenLayers/Handler/Path.js",
|
||||||
"OpenLayers/Handler/Polygon.js",
|
"OpenLayers/Handler/Polygon.js",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
});
|
||||||
@@ -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>Control/test_SelectFeature.html</li>
|
||||||
<li>test_Handler.html</li>
|
<li>test_Handler.html</li>
|
||||||
<li>Handler/test_Click.html</li>
|
<li>Handler/test_Click.html</li>
|
||||||
|
<li>Handler/test_Hover.html</li>
|
||||||
<li>Handler/test_Drag.html</li>
|
<li>Handler/test_Drag.html</li>
|
||||||
<li>Handler/test_Feature.html</li>
|
<li>Handler/test_Feature.html</li>
|
||||||
<li>Handler/test_Keyboard.html</li>
|
<li>Handler/test_Keyboard.html</li>
|
||||||
|
|||||||
Reference in New Issue
Block a user