| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). 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.Feature
- * Handler to respond to mouse events related to a drawn feature. Callbacks
- * with the following keys will be notified of the following events
- * associated with features: click, clickout, over, out, and dblclick.
- *
- * This handler stops event propagation for mousedown and mouseup if those
- * browser events target features that can be selected.
- */
- OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
- /**
- * Property: EVENTMAP
- * {Object} A object mapping the browser events to objects with callback
- * keys for in and out.
- */
- EVENTMAP: {
- 'click': {'in': 'click', 'out': 'clickout'},
- 'mousemove': {'in': 'over', 'out': 'out'},
- 'dblclick': {'in': 'dblclick', 'out': null},
- 'mousedown': {'in': null, 'out': null},
- 'mouseup': {'in': null, 'out': null},
- 'touchstart': {'in': 'click', 'out': 'clickout'}
- },
- /**
- * Property: feature
- * {<OpenLayers.Feature.Vector>} The last feature that was hovered.
- */
- feature: null,
- /**
- * Property: lastFeature
- * {<OpenLayers.Feature.Vector>} The last feature that was handled.
- */
- lastFeature: null,
- /**
- * Property: down
- * {<OpenLayers.Pixel>} The location of the last mousedown.
- */
- down: null,
- /**
- * Property: up
- * {<OpenLayers.Pixel>} The location of the last mouseup.
- */
- up: null,
- /**
- * Property: touch
- * {Boolean} When a touchstart event is fired, touch will be true and all
- * mouse related listeners will do nothing.
- */
- touch: false,
-
- /**
- * Property: clickTolerance
- * {Number} The number of pixels the mouse can move between mousedown
- * and mouseup for the event to still be considered a click.
- * Dragging the map should not trigger the click and clickout callbacks
- * unless the map is moved by less than this tolerance. Defaults to 4.
- */
- clickTolerance: 4,
- /**
- * Property: geometryTypes
- * To restrict dragging to a limited set of geometry types, send a list
- * of strings corresponding to the geometry class names.
- *
- * @type Array(String)
- */
- geometryTypes: null,
- /**
- * Property: stopClick
- * {Boolean} If stopClick is set to true, handled clicks do not
- * propagate to other click listeners. Otherwise, handled clicks
- * do propagate. Unhandled clicks always propagate, whatever the
- * value of stopClick. Defaults to true.
- */
- stopClick: true,
- /**
- * Property: stopDown
- * {Boolean} If stopDown is set to true, handled mousedowns do not
- * propagate to other mousedown listeners. Otherwise, handled
- * mousedowns do propagate. Unhandled mousedowns always propagate,
- * whatever the value of stopDown. Defaults to true.
- */
- stopDown: true,
- /**
- * Property: stopUp
- * {Boolean} If stopUp is set to true, handled mouseups do not
- * propagate to other mouseup listeners. Otherwise, handled mouseups
- * do propagate. Unhandled mouseups always propagate, whatever the
- * value of stopUp. Defaults to false.
- */
- stopUp: false,
-
- /**
- * Constructor: OpenLayers.Handler.Feature
- *
- * Parameters:
- * control - {<OpenLayers.Control>}
- * layer - {<OpenLayers.Layer.Vector>}
- * callbacks - {Object} An object with a 'over' property whos value is
- * a function to be called when the mouse is over a feature. The
- * callback should expect to recieve a single argument, the feature.
- * options - {Object}
- */
- initialize: function(control, layer, callbacks, options) {
- OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
- this.layer = layer;
- },
- /**
- * Method: touchstart
- * Handle touchstart events
- *
- * Parameters:
- * evt - {Event}
- *
- * Returns:
- * {Boolean} Let the event propagate.
- */
- touchstart: function(evt) {
- if(!this.touch) {
- this.touch = true;
- this.map.events.un({
- mousedown: this.mousedown,
- mouseup: this.mouseup,
- mousemove: this.mousemove,
- click: this.click,
- dblclick: this.dblclick,
- scope: this
- });
- }
- return OpenLayers.Event.isMultiTouch(evt) ?
- true : this.mousedown(evt);
- },
- /**
- * Method: touchmove
- * Handle touchmove events. We just prevent the browser default behavior,
- * for Android Webkit not to select text when moving the finger after
- * selecting a feature.
- *
- * Parameters:
- * evt - {Event}
- */
- touchmove: function(evt) {
- OpenLayers.Event.stop(evt);
- },
- /**
- * Method: mousedown
- * Handle mouse down. Stop propagation if a feature is targeted by this
- * event (stops map dragging during feature selection).
- *
- * Parameters:
- * evt - {Event}
- */
- mousedown: function(evt) {
- this.down = evt.xy;
- return this.handle(evt) ? !this.stopDown : true;
- },
-
- /**
- * Method: mouseup
- * Handle mouse up. Stop propagation if a feature is targeted by this
- * event.
- *
- * Parameters:
- * evt - {Event}
- */
- mouseup: function(evt) {
- this.up = evt.xy;
- return this.handle(evt) ? !this.stopUp : true;
- },
- /**
- * Method: click
- * Handle click. Call the "click" callback if click on a feature,
- * or the "clickout" callback if click outside any feature.
- *
- * Parameters:
- * evt - {Event}
- *
- * Returns:
- * {Boolean}
- */
- click: function(evt) {
- return this.handle(evt) ? !this.stopClick : true;
- },
-
- /**
- * Method: mousemove
- * Handle mouse moves. Call the "over" callback if moving in to a feature,
- * or the "out" callback if moving out of a feature.
- *
- * Parameters:
- * evt - {Event}
- *
- * Returns:
- * {Boolean}
- */
- mousemove: function(evt) {
- if (!this.callbacks['over'] && !this.callbacks['out']) {
- return true;
- }
- this.handle(evt);
- return true;
- },
-
- /**
- * Method: dblclick
- * Handle dblclick. Call the "dblclick" callback if dblclick on a feature.
- *
- * Parameters:
- * evt - {Event}
- *
- * Returns:
- * {Boolean}
- */
- dblclick: function(evt) {
- return !this.handle(evt);
- },
- /**
- * Method: geometryTypeMatches
- * Return true if the geometry type of the passed feature matches
- * one of the geometry types in the geometryTypes array.
- *
- * Parameters:
- * feature - {<OpenLayers.Vector.Feature>}
- *
- * Returns:
- * {Boolean}
- */
- geometryTypeMatches: function(feature) {
- return this.geometryTypes == null ||
- OpenLayers.Util.indexOf(this.geometryTypes,
- feature.geometry.CLASS_NAME) > -1;
- },
- /**
- * Method: handle
- *
- * Parameters:
- * evt - {Event}
- *
- * Returns:
- * {Boolean} The event occurred over a relevant feature.
- */
- handle: function(evt) {
- if(this.feature && !this.feature.layer) {
- // feature has been destroyed
- this.feature = null;
- }
- var type = evt.type;
- var handled = false;
- var previouslyIn = !!(this.feature); // previously in a feature
- var click = (type == "click" || type == "dblclick" || type == "touchstart");
- this.feature = this.layer.getFeatureFromEvent(evt);
- if(this.feature && !this.feature.layer) {
- // feature has been destroyed
- this.feature = null;
- }
- if(this.lastFeature && !this.lastFeature.layer) {
- // last feature has been destroyed
- this.lastFeature = null;
- }
- if(this.feature) {
- if(type === "touchstart") {
- // stop the event to prevent Android Webkit from
- // "flashing" the map div
- OpenLayers.Event.stop(evt);
- }
- var inNew = (this.feature != this.lastFeature);
- if(this.geometryTypeMatches(this.feature)) {
- // in to a feature
- if(previouslyIn && inNew) {
- // out of last feature and in to another
- if(this.lastFeature) {
- this.triggerCallback(type, 'out', [this.lastFeature]);
- }
- this.triggerCallback(type, 'in', [this.feature]);
- } else if(!previouslyIn || click) {
- // in feature for the first time
- this.triggerCallback(type, 'in', [this.feature]);
- }
- this.lastFeature = this.feature;
- handled = true;
- } else {
- // not in to a feature
- if(this.lastFeature && (previouslyIn && inNew || click)) {
- // out of last feature for the first time
- this.triggerCallback(type, 'out', [this.lastFeature]);
- }
- // next time the mouse goes in a feature whose geometry type
- // doesn't match we don't want to call the 'out' callback
- // again, so let's set this.feature to null so that
- // previouslyIn will evaluate to false the next time
- // we enter handle. Yes, a bit hackish...
- this.feature = null;
- }
- } else {
- if(this.lastFeature && (previouslyIn || click)) {
- this.triggerCallback(type, 'out', [this.lastFeature]);
- }
- }
- return handled;
- },
-
- /**
- * Method: triggerCallback
- * Call the callback keyed in the event map with the supplied arguments.
- * For click and clickout, the <clickTolerance> is checked first.
- *
- * Parameters:
- * type - {String}
- */
- triggerCallback: function(type, mode, args) {
- var key = this.EVENTMAP[type][mode];
- if(key) {
- if(type == 'click' && this.up && this.down) {
- // for click/clickout, only trigger callback if tolerance is met
- var dpx = Math.sqrt(
- Math.pow(this.up.x - this.down.x, 2) +
- Math.pow(this.up.y - this.down.y, 2)
- );
- if(dpx <= this.clickTolerance) {
- this.callback(key, args);
- }
- } else {
- this.callback(key, args);
- }
- }
- },
- /**
- * Method: activate
- * Turn on the handler. Returns false if the handler was already active.
- *
- * Returns:
- * {Boolean}
- */
- activate: function() {
- var activated = false;
- if(OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
- this.moveLayerToTop();
- this.map.events.on({
- "removelayer": this.handleMapEvents,
- "changelayer": this.handleMapEvents,
- scope: this
- });
- activated = true;
- }
- return activated;
- },
-
- /**
- * Method: deactivate
- * Turn off the handler. Returns false if the handler was already active.
- *
- * Returns:
- * {Boolean}
- */
- deactivate: function() {
- var deactivated = false;
- if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
- this.moveLayerBack();
- this.feature = null;
- this.lastFeature = null;
- this.down = null;
- this.up = null;
- this.touch = false;
- this.map.events.un({
- "removelayer": this.handleMapEvents,
- "changelayer": this.handleMapEvents,
- scope: this
- });
- deactivated = true;
- }
- return deactivated;
- },
-
- /**
- * Method handleMapEvents
- *
- * Parameters:
- * evt - {Object}
- */
- handleMapEvents: function(evt) {
- if (evt.type == "removelayer" || evt.property == "order") {
- this.moveLayerToTop();
- }
- },
-
- /**
- * Method: moveLayerToTop
- * Moves the layer for this handler to the top, so mouse events can reach
- * it.
- */
- moveLayerToTop: function() {
- var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1,
- this.layer.getZIndex()) + 1;
- this.layer.setZIndex(index);
-
- },
-
- /**
- * Method: moveLayerBack
- * Moves the layer back to the position determined by the map's layers
- * array.
- */
- moveLayerBack: function() {
- var index = this.layer.getZIndex() - 1;
- if (index >= this.map.Z_INDEX_BASE['Feature']) {
- this.layer.setZIndex(index);
- } else {
- this.map.setLayerZIndex(this.layer,
- this.map.getLayerIndex(this.layer));
- }
- },
- CLASS_NAME: "OpenLayers.Handler.Feature"
- });
|