/* Copyright (c) 2006-2010 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.MouseWheel
* Handler for wheel up/down events.
*
* Inherits from:
* - <OpenLayers.Handler>
*/
OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
/**
* Property: wheelListener
* {function}
*/
wheelListener: null,
/**
* Property: mousePosition
* {<OpenLayers.Pixel>} mousePosition is necessary because
* evt.clientX/Y is buggy in Moz on wheel events, so we cache and use the
* value from the last mousemove.
*/
mousePosition: null,
/**
* Property: interval
* {Integer} In order to increase server performance, an interval (in
* milliseconds) can be set to reduce the number of up/down events
* called. If set, a new up/down event will not be set until the
* interval has passed.
* Defaults to 0, meaning no interval.
*/
interval: 0,
/**
* Property: delta
* {Integer} When interval is set, delta collects the mousewheel z-deltas
* of the events that occur within the interval.
* See also the cumulative option
*/
delta: 0,
/**
* Property: cumulative
* {Boolean} When interval is set: true to collect all the mousewheel
* z-deltas, false to only record the delta direction (positive or
* negative)
*/
cumulative: true,
/**
* Constructor: OpenLayers.Handler.MouseWheel
*
* Parameters:
* control - {<OpenLayers.Control>}
* callbacks - {Object} An object containing a single function to be
* called when the drag operation is finished.
* The callback should expect to recieve a single
* argument, the point geometry.
* options - {Object}
*/
initialize: function(control, callbacks, options) {
OpenLayers.Handler.prototype.initialize.apply(this, arguments);
this.wheelListener = OpenLayers.Function.bindAsEventListener(
this.onWheelEvent, this
);
},
/**
* Method: destroy
*/
destroy: function() {
OpenLayers.Handler.prototype.destroy.apply(this, arguments);
this.wheelListener = null;
},
/**
* Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
*/
/**
* Method: onWheelEvent
* Catch the wheel event and handle it xbrowserly
*
* Parameters:
* e - {Event}
*/
onWheelEvent: function(e){
// make sure we have a map and check keyboard modifiers
if (!this.map || !this.checkModifiers(e)) {
return;
}
// Ride up the element's DOM hierarchy to determine if it or any of
// its ancestors was:
// * specifically marked as scrollable
// * one of our layer divs
// * the map div
//
var overScrollableDiv = false;
var overLayerDiv = false;
var overMapDiv = false;
var elem = OpenLayers.Event.element(e);
while((elem != null) && !overMapDiv && !overScrollableDiv) {
if (!overScrollableDiv) {
try {
if (elem.currentStyle) {
overflow = elem.currentStyle["overflow"];
} else {
var style =
document.defaultView.getComputedStyle(elem, null);
var overflow = style.getPropertyValue("overflow");
}
overScrollableDiv = ( overflow &&
(overflow == "auto") || (overflow == "scroll") );
} catch(err) {
//sometimes when scrolling in a popup, this causes
// obscure browser error
}
}
if (!overLayerDiv) {
for(var i=0, len=this.map.layers.length; i<len; i++) {
// Are we in the layer div? Note that we have two cases
// here: one is to catch EventPane layers, which have a
// pane above the layer (layer.pane)
if (elem == this.map.layers[i].div
|| elem == this.map.layers[i].pane) {
overLayerDiv = true;
break;
}
}
}
overMapDiv = (elem == this.map.div);
elem = elem.parentNode;
}
// Logic below is the following:
//
// If we are over a scrollable div or not over the map div:
// * do nothing (let the browser handle scrolling)
//
// otherwise
//
// If we are over the layer div:
// * zoom/in out
// then
// * kill event (so as not to also scroll the page after zooming)
//
// otherwise
//
// Kill the event (dont scroll the page if we wheel over the
// layerswitcher or the pan/zoom control)
//
if (!overScrollableDiv && overMapDiv) {
if (overLayerDiv) {
var delta = 0;
if (!e) {
e = window.event;
}
if (e.wheelDelta) {
delta = e.wheelDelta/120;
if (window.opera && window.opera.version() < 9.2) {
delta = -delta;
}
} else if (e.detail) {
delta = -e.detail / 3;
}
this.delta = this.delta + delta;
if(this.interval) {
window.clearTimeout(this._timeoutId);
this._timeoutId = window.setTimeout(
OpenLayers.Function.bind(function(){
this.wheelZoom(e);
}, this),
this.interval
);
} else {
this.wheelZoom(e);
}
}
OpenLayers.Event.stop(e);
}
},
/**
* Method: wheelZoom
* Given the wheel event, we carry out the appropriate zooming in or out,
* based on the 'wheelDelta' or 'detail' property of the event.
*
* Parameters:
* e - {Event}
*/
wheelZoom: function(e) {
var delta = this.delta;
this.delta = 0;
if (delta) {
// add the mouse position to the event because mozilla has
// a bug with clientX and clientY (see
// https://bugzilla.mozilla.org/show_bug.cgi?id=352179)
// getLonLatFromViewPortPx(e) returns wrong values
if (this.mousePosition) {
e.xy = this.mousePosition;
}
if (!e.xy) {
// If the mouse hasn't moved over the map yet, then
// we don't have a mouse position (in FF), so we just
// act as if the mouse was at the center of the map.
// Note that we can tell we are in the map -- and
// this.map is ensured to be true above.
e.xy = this.map.getPixelFromLonLat(
this.map.getCenter()
);
}
if (delta < 0) {
this.callback("down", [e, this.cumulative ? delta : -1]);
} else {
this.callback("up", [e, this.cumulative ? delta : 1]);
}
}
},
/**
* Method: mousemove
* Update the stored mousePosition on every move.
*
* Parameters:
* evt - {Event} The browser event
*
* Returns:
* {Boolean} Allow event propagation
*/
mousemove: function (evt) {
this.mousePosition = evt.xy;
},
/**
* Method: activate
*/
activate: function (evt) {
if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
//register mousewheel events specifically on the window and document
var wheelListener = this.wheelListener;
OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
OpenLayers.Event.observe(window, "mousewheel", wheelListener);
OpenLayers.Event.observe(document, "mousewheel", wheelListener);
return true;
} else {
return false;
}
},
/**
* Method: deactivate
*/
deactivate: function (evt) {
if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
// unregister mousewheel events specifically on the window and document
var wheelListener = this.wheelListener;
OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
return true;
} else {
return false;
}
},
CLASS_NAME: "OpenLayers.Handler.MouseWheel"
});