From e76c9e65ab90433f04b295ae525062b820703d44 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Wed, 23 Oct 2013 12:58:36 -0700 Subject: [PATCH 1/3] Added 'wheel' Event Support Also improved OpenSeadragon.addEvent()/OpenSeadragon.removeEvent() --- changelog.txt | 3 +- src/mousetracker.js | 135 +++++++++++++++++++++++++++++++------------ src/openseadragon.js | 96 +++++++++++++++--------------- 3 files changed, 147 insertions(+), 87 deletions(-) diff --git a/changelog.txt b/changelog.txt index 86f1a0e7..6c1eef95 100644 --- a/changelog.txt +++ b/changelog.txt @@ -5,7 +5,7 @@ OPENSEADRAGON CHANGELOG * BREAKING CHANGE: All EventSource and MouseTracker event handler method signatures changed to 'handlerMethod(event)' where event == { eventSource, userData, ... } (#251) (Also fixes #23, #224, #239) * The new eventSource property in the event object replaces the old eventSource parameter that was passed to handler methods. - * Where the event object duplicated the eventSource value, those properties have been removed. This effects the following events: + * Where the event object duplicated the eventSource value, those properties have been removed. This affects the following events: * All Button events - 'button' property removed * All Viewer (Viewer, Drawer, Viewport) events - 'viewer' property removed * BREAKING CHANGE: Renamed EventHandler to EventSource (#225) @@ -34,6 +34,7 @@ OPENSEADRAGON CHANGELOG * Viewer: 'canvas-release', 'canvas-click', 'canvas-drag', 'canvas-scroll', 'container-enter', 'container-exit', 'container-release' * Button: 'enter', 'exit', 'press', 'release', 'focus', 'blur', 'click' * Fixed: IE 10 not reading DZI file correctly in certain circumstances (#218) +* Added support for the 'wheel' DOM mousewheel event (#261) 0.9.131: diff --git a/src/mousetracker.js b/src/mousetracker.js index e0b9e931..d274fe8b 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -157,8 +157,9 @@ mouseup: function ( event ) { onMouseUp( _this, event, false ); }, mousemove: function ( event ) { onMouseMove( _this, event ); }, click: function ( event ) { onMouseClick( _this, event ); }, - DOMMouseScroll: function ( event ) { onMouseWheelSpin( _this, event, false ); }, - mousewheel: function ( event ) { onMouseWheelSpin( _this, event, false ); }, + wheel: function ( event ) { onWheel( _this, event ); }, + mousewheel: function ( event ) { onMouseWheel( _this, event ); }, + DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); }, mouseupie: function ( event ) { onMouseUpIE( _this, event ); }, mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, @@ -436,6 +437,13 @@ blurHandler: function () { } }; + /** + * Detect available mouse wheel event. + */ + $.MouseTracker.wheelEventName = (($.Browser.vendor == $.BROWSERS.IE && $.Browser.version > 8) || ("onwheel" in document.createElement("div"))) ? "wheel" : // Modern browsers support "wheel" + document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" + "DOMMouseScroll"; // Assume old Firefox + /** * Starts tracking mouse events on this element. * @private @@ -445,7 +453,7 @@ var events = [ "mouseover", "mouseout", "mousedown", "mouseup", "mousemove", "click", - "DOMMouseScroll", "mousewheel", + $.MouseTracker.wheelEventName, "touchstart", "touchmove", "touchend", "keypress", "focus", "blur" @@ -478,7 +486,7 @@ var events = [ "mouseover", "mouseout", "mousedown", "mouseup", "mousemove", "click", - "DOMMouseScroll", "mousewheel", + $.MouseTracker.wheelEventName, "touchstart", "touchmove", "touchend", "keypress", "focus", "blur" @@ -1071,42 +1079,81 @@ * @private * @inner */ - function onMouseWheelSpin( tracker, event, isTouch ) { + function onWheel( tracker, event ) { + handleWheelEvent( tracker, event, event, false ); + } + + + /** + * @private + * @inner + */ + function onMouseWheel( tracker, originalEvent ) { + // For legacy IE, access the global (window) event object + originalEvent = originalEvent || window.event; + + // Simulate a 'wheel' event + var event = { + target: originalEvent.target || originalEvent.srcElement, + type: "wheel", + shiftKey: originalEvent.shiftKey || false, + clientX: originalEvent.clientX, + clientY: originalEvent.clientY, + pageX: originalEvent.pageX ? originalEvent.pageX : originalEvent.clientX, + pageY: originalEvent.pageY ? originalEvent.pageY : originalEvent.clientY, + deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1, // 0=pixel, 1=line, 2=page + deltaX: 0, + deltaZ: 0, + preventDefault: function() { + if ( originalEvent.preventDefault ) { + originalEvent.preventDefault(); + } else { + originalEvent.returnValue = false; + } + } + }; + + // Calculate event.deltaY + if ( $.MouseTracker.wheelEventName == "mousewheel" ) { + event.deltaY = - 1 / $.DEFAULT_SETTINGS.pixelsPerWheelLine * originalEvent.wheelDelta; + } else { + event.deltaY = originalEvent.detail; + } + + handleWheelEvent( tracker, event, originalEvent, false ); + } + + + /** + * Handles 'wheel' events. + * The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()) or onTouchMove(). + * + * @private + * @inner + */ + function handleWheelEvent( tracker, event, originalEvent, isTouch ) { var nDelta = 0, propagate; isTouch = isTouch || false; - if ( !event ) { // For IE, access the global (window) event object - event = window.event; - } - - if ( event.wheelDelta ) { // IE and Opera - nDelta = event.wheelDelta; - if ( window.opera ) { // Opera has the values reversed - nDelta = -nDelta; - } - } else if ( event.detail ) { // Mozilla FireFox - nDelta = -event.detail; - } - //The nDelta variable is gated to provide smooth z-index scrolling - //since the mouse wheel allows for substantial deltas meant for rapid - //y-index scrolling. - nDelta = nDelta > 0 ? 1 : -1; + // The nDelta variable is gated to provide smooth z-index scrolling + // since the mouse wheel allows for substantial deltas meant for rapid + // y-index scrolling. + // event.deltaMode: 0=pixel, 1=line, 2=page + // TODO: Deltas in pixel mode should be accumulated then a scroll value computed after $.DEFAULT_SETTINGS.pixelsPerWheelLine threshold reached + nDelta = event.deltaY < 0 ? 1 : -1; if ( tracker.scrollHandler ) { propagate = tracker.scrollHandler( { - eventSource: tracker, - // Note: Ok to call getMouseRelative on passed event for isTouch==true since - // event.pageX/event.pageY are added to the original touchmove event in - // onTouchMove(). - position: getMouseRelative( event, tracker.element ), - scroll: nDelta, - shift: event.shiftKey, - isTouchEvent: isTouch, - originalEvent: event, - userData: tracker.userData + eventSource: tracker, + position: getMouseRelative( event, tracker.element ), + scroll: nDelta, + shift: event.shiftKey, + isTouchEvent: isTouch, + originalEvent: originalEvent, + userData: tracker.userData } ); if ( propagate === false ) { @@ -1229,15 +1276,29 @@ if ( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ) { //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); - // Simulate a mouse wheel scroll event + // Simulate a 'wheel' event var simulatedEvent = { - shiftKey: event.shiftKey || false, - pageX: THIS[ tracker.hash ].pinchMidpoint.x, - pageY: THIS[ tracker.hash ].pinchMidpoint.y, - detail: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1 + target: event.target || event.srcElement, + type: "wheel", + shiftKey: event.shiftKey || false, + clientX: event.clientX, + clientY: event.clientY, + pageX: event.pageX ? event.pageX : event.clientX, + pageY: event.pageY ? event.pageY : event.clientY, + deltaMode: 1, // 0=pixel, 1=line, 2=page + deltaX: 0, + deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1, + deltaZ: 0, + preventDefault: function() { + if ( event.preventDefault ) { + event.preventDefault(); + } else { + event.returnValue = false; + } + } }; - onMouseWheelSpin( tracker, simulatedEvent, true ); + handleWheelEvent( tracker, simulatedEvent, event, true ); THIS[ tracker.hash ].lastPinchDelta = pinchDelta; } diff --git a/src/openseadragon.js b/src/openseadragon.js index 0c0a01ca..34d275ca 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -386,6 +386,50 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ }; + /** + * Detect event model and create appropriate _addEvent/_removeEvent methods + */ + if ( window.addEventListener ) { + //$._addEventListener = 'addEventListener'; + //$._eventNamePrefix = ''; + $._addEvent = function ( element, eventName, handler, useCapture ) { + element = $.getElement( element ); + element.addEventListener( eventName, handler, useCapture ); + }; + } else if ( window.attachEvent ) { + //$._addEventListener = 'attachEvent'; + //$._eventNamePrefix = 'on'; + $._addEvent = function ( element, eventName, handler, useCapture ) { + element = $.getElement( element ); + element.attachEvent( 'on' + eventName, handler ); + if ( useCapture && element.setCapture ) { + element.setCapture(); + } + }; + } else { + throw new Error( "No known event model." ); + } + + if ( window.removeEventListener ) { + //$._removeEventListener = 'removeEventListener'; + $._removeEvent = function ( element, eventName, handler, useCapture ) { + element = $.getElement( element ); + element.removeEventListener( eventName, handler, useCapture ); + }; + } else if ( window.detachEvent ) { + //$._removeEventListener = 'detachEvent'; + $._removeEvent = function( element, eventName, handler, useCapture ) { + element = $.getElement( element ); + element.detachEvent( 'on' + eventName, handler ); + if ( useCapture && element.releaseCapture ) { + element.releaseCapture(); + } + }; + } else { + throw new Error( "No known event model." ); + } + + }( OpenSeadragon )); /** @@ -517,6 +561,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ immediateRender: false, minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels + pixelsPerWheelLine: 40, //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE @@ -1142,33 +1187,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ * @param {String} eventName * @param {Function} handler * @param {Boolean} [useCapture] - * @throws {Error} */ addEvent: function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - - //TODO: Why do this if/else on every method call instead of just - // defining this function once based on the same logic - if ( element.addEventListener ) { - $.addEvent = function( element, eventName, handler, useCapture ){ - element = $.getElement( element ); - element.addEventListener( eventName, handler, useCapture ); - }; - } else if ( element.attachEvent ) { - $.addEvent = function( element, eventName, handler, useCapture ){ - element = $.getElement( element ); - element.attachEvent( "on" + eventName, handler ); - if ( useCapture && element.setCapture ) { - element.setCapture(); - } - }; - } else { - throw new Error( - "Unable to attach event handler, no known technique." - ); - } - - return $.addEvent( element, eventName, handler, useCapture ); + return $._addEvent( element, eventName, handler, useCapture ); }, @@ -1181,32 +1202,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ * @param {String} eventName * @param {Function} handler * @param {Boolean} [useCapture] - * @throws {Error} */ removeEvent: function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - - //TODO: Why do this if/else on every method call instead of just - // defining this function once based on the same logic - if ( element.removeEventListener ) { - $.removeEvent = function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - element.removeEventListener( eventName, handler, useCapture ); - }; - } else if ( element.detachEvent ) { - $.removeEvent = function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - element.detachEvent("on" + eventName, handler); - if ( useCapture && element.releaseCapture ) { - element.releaseCapture(); - } - }; - } else { - throw new Error( - "Unable to detach event handler, no known technique." - ); - } - return $.removeEvent( element, eventName, handler, useCapture ); + return $._removeEvent( element, eventName, handler, useCapture ); }, From 755c4752c099f4279abb338d086e5e20b2666e6e Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Wed, 23 Oct 2013 13:55:52 -0700 Subject: [PATCH 2/3] 'wheel' Event Support Fixes Whitespace, cancel handling --- src/mousetracker.js | 79 ++++++++++++++++++-------------------------- src/openseadragon.js | 6 ---- 2 files changed, 33 insertions(+), 52 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index d274fe8b..2f337711 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -440,9 +440,10 @@ /** * Detect available mouse wheel event. */ - $.MouseTracker.wheelEventName = (($.Browser.vendor == $.BROWSERS.IE && $.Browser.version > 8) || ("onwheel" in document.createElement("div"))) ? "wheel" : // Modern browsers support "wheel" - document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel" - "DOMMouseScroll"; // Assume old Firefox + $.MouseTracker.wheelEventName = ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version > 8 ) || + ( 'onwheel' in document.createElement( 'div' ) ) ? 'wheel' : // Modern browsers support 'wheel' + document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least 'mousewheel' + 'DOMMouseScroll'; // Assume old Firefox /** * Starts tracking mouse events on this element. @@ -1088,39 +1089,32 @@ * @private * @inner */ - function onMouseWheel( tracker, originalEvent ) { + function onMouseWheel( tracker, event ) { // For legacy IE, access the global (window) event object - originalEvent = originalEvent || window.event; + event = event || window.event; // Simulate a 'wheel' event - var event = { - target: originalEvent.target || originalEvent.srcElement, - type: "wheel", - shiftKey: originalEvent.shiftKey || false, - clientX: originalEvent.clientX, - clientY: originalEvent.clientY, - pageX: originalEvent.pageX ? originalEvent.pageX : originalEvent.clientX, - pageY: originalEvent.pageY ? originalEvent.pageY : originalEvent.clientY, - deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1, // 0=pixel, 1=line, 2=page - deltaX: 0, - deltaZ: 0, - preventDefault: function() { - if ( originalEvent.preventDefault ) { - originalEvent.preventDefault(); - } else { - originalEvent.returnValue = false; - } - } + var simulatedEvent = { + target: event.target || event.srcElement, + type: "wheel", + shiftKey: event.shiftKey || false, + clientX: event.clientX, + clientY: event.clientY, + pageX: event.pageX ? event.pageX : event.clientX, + pageY: event.pageY ? event.pageY : event.clientY, + deltaMode: event.type == "MozMousePixelScroll" ? 0 : 1, // 0=pixel, 1=line, 2=page + deltaX: 0, + deltaZ: 0 }; - // Calculate event.deltaY + // Calculate deltaY if ( $.MouseTracker.wheelEventName == "mousewheel" ) { - event.deltaY = - 1 / $.DEFAULT_SETTINGS.pixelsPerWheelLine * originalEvent.wheelDelta; + simulatedEvent.deltaY = - 1 / $.DEFAULT_SETTINGS.pixelsPerWheelLine * event.wheelDelta; } else { - event.deltaY = originalEvent.detail; + simulatedEvent.deltaY = event.detail; } - handleWheelEvent( tracker, event, originalEvent, false ); + handleWheelEvent( tracker, simulatedEvent, event, false ); } @@ -1157,7 +1151,7 @@ } ); if ( propagate === false ) { - $.cancelEvent( event ); + $.cancelEvent( originalEvent ); } } } @@ -1278,24 +1272,17 @@ // Simulate a 'wheel' event var simulatedEvent = { - target: event.target || event.srcElement, - type: "wheel", - shiftKey: event.shiftKey || false, - clientX: event.clientX, - clientY: event.clientY, - pageX: event.pageX ? event.pageX : event.clientX, - pageY: event.pageY ? event.pageY : event.clientY, - deltaMode: 1, // 0=pixel, 1=line, 2=page - deltaX: 0, - deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1, - deltaZ: 0, - preventDefault: function() { - if ( event.preventDefault ) { - event.preventDefault(); - } else { - event.returnValue = false; - } - } + target: event.target || event.srcElement, + type: "wheel", + shiftKey: event.shiftKey || false, + clientX: THIS[ tracker.hash ].pinchMidpoint.x, + clientY: THIS[ tracker.hash ].pinchMidpoint.y, + pageX: THIS[ tracker.hash ].pinchMidpoint.x, + pageY: THIS[ tracker.hash ].pinchMidpoint.y, + deltaMode: 1, // 0=pixel, 1=line, 2=page + deltaX: 0, + deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1, + deltaZ: 0 }; handleWheelEvent( tracker, simulatedEvent, event, true ); diff --git a/src/openseadragon.js b/src/openseadragon.js index 34d275ca..3d19f727 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -390,15 +390,11 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ * Detect event model and create appropriate _addEvent/_removeEvent methods */ if ( window.addEventListener ) { - //$._addEventListener = 'addEventListener'; - //$._eventNamePrefix = ''; $._addEvent = function ( element, eventName, handler, useCapture ) { element = $.getElement( element ); element.addEventListener( eventName, handler, useCapture ); }; } else if ( window.attachEvent ) { - //$._addEventListener = 'attachEvent'; - //$._eventNamePrefix = 'on'; $._addEvent = function ( element, eventName, handler, useCapture ) { element = $.getElement( element ); element.attachEvent( 'on' + eventName, handler ); @@ -411,13 +407,11 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ } if ( window.removeEventListener ) { - //$._removeEventListener = 'removeEventListener'; $._removeEvent = function ( element, eventName, handler, useCapture ) { element = $.getElement( element ); element.removeEventListener( eventName, handler, useCapture ); }; } else if ( window.detachEvent ) { - //$._removeEventListener = 'detachEvent'; $._removeEvent = function( element, eventName, handler, useCapture ) { element = $.getElement( element ); element.detachEvent( 'on' + eventName, handler ); From 0f8ac4cfab1e68adfcc56088ae1cd3663fe1c911 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Thu, 24 Oct 2013 13:39:00 -0700 Subject: [PATCH 3/3] Added 'MozMousePixelScroll' event handling For older Firefox versions --- src/mousetracker.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/mousetracker.js b/src/mousetracker.js index 2f337711..a12b3d50 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -160,6 +160,7 @@ wheel: function ( event ) { onWheel( _this, event ); }, mousewheel: function ( event ) { onMouseWheel( _this, event ); }, DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); }, + MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); }, mouseupie: function ( event ) { onMouseUpIE( _this, event ); }, mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, @@ -463,6 +464,11 @@ event, i; + // Add 'MozMousePixelScroll' event handler for older Firefox + if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { + events.push( "MozMousePixelScroll" ); + } + if ( !delegate.tracking ) { for ( i = 0; i < events.length; i++ ) { event = events[ i ]; @@ -496,6 +502,11 @@ event, i; + // Remove 'MozMousePixelScroll' event handler for older Firefox + if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { + events.push( "MozMousePixelScroll" ); + } + if ( delegate.tracking ) { for ( i = 0; i < events.length; i++ ) { event = events[ i ]; @@ -1077,6 +1088,8 @@ /** + * Handler for 'wheel' events + * * @private * @inner */ @@ -1086,6 +1099,8 @@ /** + * Handler for 'mousewheel', 'DOMMouseScroll', and 'MozMousePixelScroll' events + * * @private * @inner */