1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2 * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3 * full text of the license. */
7 * @requires OpenLayers/Handler.js
11 * Class: OpenLayers.Handler.Feature
12 * Handler to respond to mouse events related to a drawn feature. Callbacks
13 * with the following keys will be notified of the following events
14 * associated with features: click, clickout, over, out, and dblclick.
16 * This handler stops event propagation for mousedown and mouseup if those
17 * browser events target features that can be selected.
19 OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
23 * {Object} A object mapping the browser events to objects with callback
24 * keys for in and out.
27 'click': {'in': 'click', 'out': 'clickout'},
28 'mousemove': {'in': 'over', 'out': 'out'},
29 'dblclick': {'in': 'dblclick', 'out': null},
30 'mousedown': {'in': null, 'out': null},
31 'mouseup': {'in': null, 'out': null}
36 * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
41 * Property: lastFeature
42 * {<OpenLayers.Feature.Vector>} The last feature that was handled.
48 * {<OpenLayers.Pixel>} The location of the last mousedown.
54 * {<OpenLayers.Pixel>} The location of the last mouseup.
59 * Property: clickTolerance
60 * {Number} The number of pixels the mouse can move between mousedown
61 * and mouseup for the event to still be considered a click.
62 * Dragging the map should not trigger the click and clickout callbacks
63 * unless the map is moved by less than this tolerance. Defaults to 4.
68 * Property: geometryTypes
69 * To restrict dragging to a limited set of geometry types, send a list
70 * of strings corresponding to the geometry class names.
78 * {Boolean} If stopClick is set to true, handled clicks do not
79 * propagate to other click listeners. Otherwise, handled clicks
80 * do propagate. Unhandled clicks always propagate, whatever the
81 * value of stopClick. Defaults to true.
87 * {Boolean} If stopDown is set to true, handled mousedowns do not
88 * propagate to other mousedown listeners. Otherwise, handled
89 * mousedowns do propagate. Unhandled mousedowns always propagate,
90 * whatever the value of stopDown. Defaults to true.
96 * {Boolean} If stopUp is set to true, handled mouseups do not
97 * propagate to other mouseup listeners. Otherwise, handled mouseups
98 * do propagate. Unhandled mouseups always propagate, whatever the
99 * value of stopUp. Defaults to false.
104 * Constructor: OpenLayers.Handler.Feature
107 * control - {<OpenLayers.Control>}
108 * layer - {<OpenLayers.Layer.Vector>}
109 * callbacks - {Object} An object with a 'over' property whos value is
110 * a function to be called when the mouse is over a feature. The
111 * callback should expect to recieve a single argument, the feature.
114 initialize: function(control, layer, callbacks, options) {
115 OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
122 * Handle mouse down. Stop propagation if a feature is targeted by this
123 * event (stops map dragging during feature selection).
128 mousedown: function(evt) {
130 return this.handle(evt) ? !this.stopDown : true;
135 * Handle mouse up. Stop propagation if a feature is targeted by this
141 mouseup: function(evt) {
143 return this.handle(evt) ? !this.stopUp : true;
148 * Handle click. Call the "click" callback if click on a feature,
149 * or the "clickout" callback if click outside any feature.
157 click: function(evt) {
158 return this.handle(evt) ? !this.stopClick : true;
163 * Handle mouse moves. Call the "over" callback if moving in to a feature,
164 * or the "out" callback if moving out of a feature.
172 mousemove: function(evt) {
173 if (!this.callbacks['over'] && !this.callbacks['out']) {
182 * Handle dblclick. Call the "dblclick" callback if dblclick on a feature.
190 dblclick: function(evt) {
191 return !this.handle(evt);
195 * Method: geometryTypeMatches
196 * Return true if the geometry type of the passed feature matches
197 * one of the geometry types in the geometryTypes array.
200 * feature - {<OpenLayers.Vector.Feature>}
205 geometryTypeMatches: function(feature) {
206 return this.geometryTypes == null ||
207 OpenLayers.Util.indexOf(this.geometryTypes,
208 feature.geometry.CLASS_NAME) > -1;
218 * {Boolean} The event occurred over a relevant feature.
220 handle: function(evt) {
221 if(this.feature && !this.feature.layer) {
222 // feature has been destroyed
227 var previouslyIn = !!(this.feature); // previously in a feature
228 var click = (type == "click" || type == "dblclick");
229 this.feature = this.layer.getFeatureFromEvent(evt);
230 if(this.feature && !this.feature.layer) {
231 // feature has been destroyed
234 if(this.lastFeature && !this.lastFeature.layer) {
235 // last feature has been destroyed
236 this.lastFeature = null;
239 var inNew = (this.feature != this.lastFeature);
240 if(this.geometryTypeMatches(this.feature)) {
242 if(previouslyIn && inNew) {
243 // out of last feature and in to another
244 if(this.lastFeature) {
245 this.triggerCallback(type, 'out', [this.lastFeature]);
247 this.triggerCallback(type, 'in', [this.feature]);
248 } else if(!previouslyIn || click) {
249 // in feature for the first time
250 this.triggerCallback(type, 'in', [this.feature]);
252 this.lastFeature = this.feature;
255 // not in to a feature
256 if(this.lastFeature && (previouslyIn && inNew || click)) {
257 // out of last feature for the first time
258 this.triggerCallback(type, 'out', [this.lastFeature]);
260 // next time the mouse goes in a feature whose geometry type
261 // doesn't match we don't want to call the 'out' callback
262 // again, so let's set this.feature to null so that
263 // previouslyIn will evaluate to false the next time
264 // we enter handle. Yes, a bit hackish...
268 if(this.lastFeature && (previouslyIn || click)) {
269 this.triggerCallback(type, 'out', [this.lastFeature]);
276 * Method: triggerCallback
277 * Call the callback keyed in the event map with the supplied arguments.
278 * For click and clickout, the <clickTolerance> is checked first.
283 triggerCallback: function(type, mode, args) {
284 var key = this.EVENTMAP[type][mode];
286 if(type == 'click' && this.up && this.down) {
287 // for click/clickout, only trigger callback if tolerance is met
289 Math.pow(this.up.x - this.down.x, 2) +
290 Math.pow(this.up.y - this.down.y, 2)
292 if(dpx <= this.clickTolerance) {
293 this.callback(key, args);
296 this.callback(key, args);
303 * Turn on the handler. Returns false if the handler was already active.
308 activate: function() {
309 var activated = false;
310 if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
311 this.moveLayerToTop();
313 "removelayer": this.handleMapEvents,
314 "changelayer": this.handleMapEvents,
324 * Turn off the handler. Returns false if the handler was already active.
329 deactivate: function() {
330 var deactivated = false;
331 if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
332 this.moveLayerBack();
334 this.lastFeature = null;
338 "removelayer": this.handleMapEvents,
339 "changelayer": this.handleMapEvents,
348 * Method handleMapEvents
353 handleMapEvents: function(evt) {
354 if (!evt.property || evt.property == "order") {
355 this.moveLayerToTop();
360 * Method: moveLayerToTop
361 * Moves the layer for this handler to the top, so mouse events can reach
364 moveLayerToTop: function() {
365 var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
366 this.layer.getZIndex()) + 1;
367 this.layer.setZIndex(index);
372 * Method: moveLayerBack
373 * Moves the layer back to the position determined by the map's layers
376 moveLayerBack: function() {
377 var index = this.layer.getZIndex() - 1;
378 if (index >= this.map.Z_INDEX_BASE['Feature']) {
379 this.layer.setZIndex(index);
381 this.map.setLayerZIndex(this.layer,
382 this.map.getLayerIndex(this.layer));
386 CLASS_NAME: "OpenLayers.Handler.Feature"