diff --git a/changelog.txt b/changelog.txt index 9b7ee4ff..d29ef6e1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -29,6 +29,8 @@ OPENSEADRAGON CHANGELOG * MouseTracker: Improved releasing of tracked pointers on destroy()/stopTracking() (#1872 @msalsbery) * Updated Viewer, Button, Drawer, Navigator, ReferenceStrip DOM for proper DOM event handling (#1872 @msalsbery) * Added OpenSeadragon.setElementPointerEventsNone() for setting pointer-events:'none' on DOM elements (#1872 @msalsbery) +* MouseTracker: added contextMenuHandler option for handling contextmenu events (#1872 @msalsbery) +* Viewer: added a canvas-contextmenu event (#1872 @msalsbery) 2.4.2: diff --git a/src/mousetracker.js b/src/mousetracker.js index dd24bfc7..01469a65 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -74,6 +74,8 @@ * event is fired. * @param {OpenSeadragon.EventHandler} [options.preProcessEventHandler=null] * An optional handler for controlling DOM event propagation and processing. + * @param {OpenSeadragon.EventHandler} [options.contextMenuHandler=null] + * An optional handler for contextmenu. * @param {OpenSeadragon.EventHandler} [options.enterHandler=null] * An optional handler for pointer enter. * @param {OpenSeadragon.EventHandler} [options.leaveHandler=null] @@ -173,6 +175,7 @@ this.stopDelay = options.stopDelay || 50; this.preProcessEventHandler = options.preProcessEventHandler || null; + this.contextMenuHandler = options.contextMenuHandler || null; this.enterHandler = options.enterHandler || null; this.leaveHandler = options.leaveHandler || null; this.exitHandler = options.exitHandler || null; // Deprecated v2.5.0 @@ -213,6 +216,7 @@ keypress: function ( event ) { onKeyPress( _this, event ); }, focus: function ( event ) { onFocus( _this, event ); }, blur: function ( event ) { onBlur( _this, event ); }, + contextmenu: function ( event ) { onContextMenu( _this, event ); }, wheel: function ( event ) { onWheel( _this, event ); }, mousewheel: function ( event ) { onMouseWheel( _this, event ); }, @@ -288,6 +292,7 @@ this.dragHandler || this.dragEndHandler || this.pinchHandler ); this.hasScrollHandler = !!this.scrollHandler; + this.hasContextMenuHandler = !!this.contextMenuHandler; if ( !options.startDisabled ) { this.setTracking( true ); @@ -415,6 +420,22 @@ */ preProcessEventHandler: function () { }, + /** + * Implement or assign implementation to these handlers during or after + * calling the constructor. + * @function + * @param {Object} event + * @param {OpenSeadragon.MouseTracker} event.eventSource + * A reference to the tracker instance. + * @param {OpenSeadragon.Point} event.position + * The position of the event relative to the tracked element. + * @param {Object} event.originalEvent + * The original event object. + * @param {Object} event.userData + * Arbitrary user-defined object. + */ + contextMenuHandler: function () { }, + /** * Implement or assign implementation to these handlers during or after * calling the constructor. @@ -1170,7 +1191,7 @@ /** * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to. */ - $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keydown", "keyup", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; + $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keydown", "keyup", "keypress", "focus", "blur", "contextmenu", $.MouseTracker.wheelEventName ]; if( $.MouseTracker.wheelEventName === "DOMMouseScroll" ) { // Older Firefox @@ -1261,7 +1282,7 @@ * @property {Number} eventPhase * 0 == NONE, 1 == CAPTURING_PHASE, 2 == AT_TARGET, 3 == BUBBLING_PHASE. * @property {String} eventType - * "gotpointercapture", "lostpointercapture", "pointerenter", "pointerleave", "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel", "wheel". + * "contextmenu", "gotpointercapture", "lostpointercapture", "pointerenter", "pointerleave", "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel", "wheel". * @property {String} pointerType * "mouse", "touch", "pen", etc. * @property {Boolean} isEmulated @@ -1688,7 +1709,7 @@ return; } // Can throw InvalidPointerId - // (should never happen so we'll log a warning) + // (should never happen, but it does on Firefox 79 touch so we won't log a warning) try { if ( $.MouseTracker.unprefixedPointerEvents ) { tracker.element.releasePointerCapture( gPoint.id ); @@ -1698,7 +1719,7 @@ //$.console.log('element.msReleasePointerCapture() called'); } } catch ( e ) { - $.console.warn('releasePointerCapture() called on invalid pointer ID'); + //$.console.warn('releasePointerCapture() called on invalid pointer ID'); } } else { tracker.element.releaseCapture(); @@ -1968,6 +1989,44 @@ } + /** + * @private + * @inner + */ + function onContextMenu( tracker, event ) { + //$.console.log('contextmenu ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + + event = $.getEvent( event ); + + var eventInfo = { + originalEvent: event, + eventType: 'contextmenu', + pointerType: 'mouse', + isEmulated: false + }; + preProcessEvent( tracker, eventInfo ); + + // ContextMenu + if ( tracker.contextMenuHandler ) { + tracker.contextMenuHandler( + { + eventSource: tracker, + position: getPointRelativeToAbsolute( getMouseAbsolute( event ), tracker.element ), + originalEvent: eventInfo.originalEvent, + userData: tracker.userData + } + ); + } + + if ( eventInfo.preventDefault && !eventInfo.defaultPrevented ) { + $.cancelEvent( event ); + } + if ( eventInfo.stopPropagation ) { + $.stopEvent( event ); + } + } + + /** * Handler for 'wheel' events * @@ -3253,6 +3312,13 @@ eventInfo.preventGesture = false; eventInfo.stopPropagation = false; break; + case 'contextmenu': + eventInfo.isStopable = true; + eventInfo.isCancelable = true; + eventInfo.preventDefault = tracker.hasContextMenuHandler; + eventInfo.preventGesture = !tracker.hasContextMenuHandler; + eventInfo.stopPropagation = false; + break; case 'pointerenter': case 'pointerleave': default: diff --git a/src/viewer.js b/src/viewer.js index 24b88109..1eb6f45e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -288,6 +288,7 @@ $.Viewer = function( options ) { clickDistThreshold: this.clickDistThreshold, dblClickTimeThreshold: this.dblClickTimeThreshold, dblClickDistThreshold: this.dblClickDistThreshold, + contextMenuHandler: $.delegate( this, onCanvasContextMenu ), keyDownHandler: $.delegate( this, onCanvasKeyDown ), keyHandler: $.delegate( this, onCanvasKeyPress ), clickHandler: $.delegate( this, onCanvasClick ), @@ -2538,6 +2539,26 @@ function onBlur(){ } +function onCanvasContextMenu( event ) { + /** + * Raised when a contextmenu event occurs in the {@link OpenSeadragon.Viewer#canvas} element. + * + * @event canvas-contextmenu + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. + * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. + * @property {Object} originalEvent - The original DOM event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'canvas-contextmenu', { + tracker: event.eventSource, + position: event.position, + originalEvent: event.originalEvent + }); +} + function onCanvasKeyDown( event ) { var canvasKeyDownEventArgs = { originalEvent: event.originalEvent,