diff --git a/changelog.txt b/changelog.txt
index 063ca6fa..abeea2c2 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -43,7 +43,9 @@ OPENSEADRAGON CHANGELOG
1.2.1: (in progress)
* Added preserveOverlays option (#561)
-* Fixed: DZI tilesource was broken (#563)
+* Fixed: DZI tilesource was broken on IE8/IE9 (#563)
+* Exposed secondary pointer button (middle, right, etc.) events from MouseTracker and through viewer (#479)
+* MouseTracker - Improved IE 8 compatibility (#562)
1.2.0:
diff --git a/src/mousetracker.js b/src/mousetracker.js
index 8e89e43a..09b3c751 100644
--- a/src/mousetracker.js
+++ b/src/mousetracker.js
@@ -72,8 +72,12 @@
* An optional handler for pointer exit.
* @param {OpenSeadragon.EventHandler} [options.pressHandler=null]
* An optional handler for pointer press.
+ * @param {OpenSeadragon.EventHandler} [options.nonPrimaryPressHandler=null]
+ * An optional handler for pointer non-primary button press.
* @param {OpenSeadragon.EventHandler} [options.releaseHandler=null]
* An optional handler for pointer release.
+ * @param {OpenSeadragon.EventHandler} [options.nonPrimaryReleaseHandler=null]
+ * An optional handler for pointer non-primary button release.
* @param {OpenSeadragon.EventHandler} [options.moveHandler=null]
* An optional handler for pointer move.
* @param {OpenSeadragon.EventHandler} [options.scrollHandler=null]
@@ -144,24 +148,26 @@
* @memberof OpenSeadragon.MouseTracker#
*/
this.dblClickDistThreshold = options.dblClickDistThreshold || $.DEFAULT_SETTINGS.dblClickDistThreshold;
- this.userData = options.userData || null;
- this.stopDelay = options.stopDelay || 50;
+ this.userData = options.userData || null;
+ this.stopDelay = options.stopDelay || 50;
- this.enterHandler = options.enterHandler || null;
- this.exitHandler = options.exitHandler || null;
- this.pressHandler = options.pressHandler || null;
- this.releaseHandler = options.releaseHandler || null;
- this.moveHandler = options.moveHandler || null;
- this.scrollHandler = options.scrollHandler || null;
- this.clickHandler = options.clickHandler || null;
- this.dblClickHandler = options.dblClickHandler || null;
- this.dragHandler = options.dragHandler || null;
- this.dragEndHandler = options.dragEndHandler || null;
- this.pinchHandler = options.pinchHandler || null;
- this.stopHandler = options.stopHandler || null;
- this.keyHandler = options.keyHandler || null;
- this.focusHandler = options.focusHandler || null;
- this.blurHandler = options.blurHandler || null;
+ this.enterHandler = options.enterHandler || null;
+ this.exitHandler = options.exitHandler || null;
+ this.pressHandler = options.pressHandler || null;
+ this.nonPrimaryPressHandler = options.nonPrimaryPressHandler || null;
+ this.releaseHandler = options.releaseHandler || null;
+ this.nonPrimaryReleaseHandler = options.nonPrimaryReleaseHandler || null;
+ this.moveHandler = options.moveHandler || null;
+ this.scrollHandler = options.scrollHandler || null;
+ this.clickHandler = options.clickHandler || null;
+ this.dblClickHandler = options.dblClickHandler || null;
+ this.dragHandler = options.dragHandler || null;
+ this.dragEndHandler = options.dragEndHandler || null;
+ this.pinchHandler = options.pinchHandler || null;
+ this.stopHandler = options.stopHandler || null;
+ this.keyHandler = options.keyHandler || null;
+ this.focusHandler = options.focusHandler || null;
+ this.blurHandler = options.blurHandler || null;
//Store private properties in a scope sealed hash map
var _this = this;
@@ -183,6 +189,8 @@
DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); },
MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); },
+ mouseenter: function ( event ) { onMouseEnter( _this, event ); }, // Used on IE8 only
+ mouseleave: function ( event ) { onMouseLeave( _this, event ); }, // Used on IE8 only
mouseover: function ( event ) { onMouseOver( _this, event ); },
mouseout: function ( event ) { onMouseOut( _this, event ); },
mousedown: function ( event ) { onMouseDown( _this, event ); },
@@ -390,6 +398,34 @@
*/
pressHandler: 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 {String} event.pointerType
+ * "mouse", "touch", "pen", etc.
+ * @param {OpenSeadragon.Point} event.position
+ * The position of the event relative to the tracked element.
+ * @param {Number} event.button
+ * Button which caused the event.
+ * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
+ * @param {Number} event.buttons
+ * Current buttons pressed.
+ * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
+ * @param {Boolean} event.isTouchEvent
+ * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.
+ * @param {Object} event.originalEvent
+ * The original event object.
+ * @param {Boolean} event.preventDefaultAction
+ * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
+ * @param {Object} event.userData
+ * Arbitrary user-defined object.
+ */
+ nonPrimaryPressHandler: function () { },
+
/**
* Implement or assign implementation to these handlers during or after
* calling the constructor.
@@ -420,6 +456,34 @@
*/
releaseHandler: 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 {String} event.pointerType
+ * "mouse", "touch", "pen", etc.
+ * @param {OpenSeadragon.Point} event.position
+ * The position of the event relative to the tracked element.
+ * @param {Number} event.button
+ * Button which caused the event.
+ * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
+ * @param {Number} event.buttons
+ * Current buttons pressed.
+ * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
+ * @param {Boolean} event.isTouchEvent
+ * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.
+ * @param {Object} event.originalEvent
+ * The original event object.
+ * @param {Boolean} event.preventDefaultAction
+ * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
+ * @param {Object} event.userData
+ * Arbitrary user-defined object.
+ */
+ nonPrimaryReleaseHandler: function () { },
+
/**
* Implement or assign implementation to these handlers during or after
* calling the constructor.
@@ -816,6 +880,7 @@
if ( window.PointerEvent ) {
// IE11 and other W3C Pointer Event implementations (see http://www.w3.org/TR/pointerevents)
+ $.MouseTracker.havePointerEvents = true;
$.MouseTracker.subscribeEvents.push( "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel" );
$.MouseTracker.unprefixedPointerEvents = true;
if( navigator.maxTouchPoints ) {
@@ -827,6 +892,7 @@
$.MouseTracker.haveMouseEnter = false;
} else if ( window.MSPointerEvent ) {
// IE10
+ $.MouseTracker.havePointerEvents = true;
$.MouseTracker.subscribeEvents.push( "MSPointerOver", "MSPointerOut", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" );
$.MouseTracker.unprefixedPointerEvents = false;
if( navigator.msMaxTouchPoints ) {
@@ -838,8 +904,15 @@
$.MouseTracker.haveMouseEnter = false;
} else {
// Legacy W3C mouse events
- $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout", "mousedown", "mouseup", "mousemove" );
- $.MouseTracker.haveMouseEnter = false;
+ $.MouseTracker.havePointerEvents = false;
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
+ $.MouseTracker.haveMouseEnter = true;
+ } else {
+ $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
+ $.MouseTracker.haveMouseEnter = false;
+ }
+ $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505)
$.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
@@ -1151,7 +1224,14 @@
function getCaptureEventParams( tracker, pointerType ) {
var delegate = THIS[ tracker.hash ];
- if ( pointerType === 'mouse' ) {
+ if ( pointerType === 'pointerevent' ) {
+ return {
+ upName: $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
+ upHandler: delegate.pointerupcaptured,
+ moveName: $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
+ moveHandler: delegate.pointermovecaptured
+ };
+ } else if ( pointerType === 'mouse' ) {
return {
upName: 'mouseup',
upHandler: delegate.mouseupcaptured,
@@ -1166,12 +1246,7 @@
moveHandler: delegate.touchmovecaptured
};
} else {
- return {
- upName: $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
- upHandler: delegate.pointerupcaptured,
- moveName: $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
- moveHandler: delegate.pointermovecaptured
- };
+ throw new Error( "MouseTracker.getCaptureEventParams: Unknown pointer type." );
}
}
@@ -1181,27 +1256,31 @@
* @inner
*/
function capturePointer( tracker, pointerType ) {
- var delegate = THIS[ tracker.hash ],
- pointsList = tracker.getActivePointersListByType( pointerType ),
- eventParams = getCaptureEventParams( tracker, pointerType );
+ var pointsList = tracker.getActivePointersListByType( pointerType ),
+ eventParams;
pointsList.captureCount++;
if ( pointsList.captureCount === 1 ) {
- // We emulate mouse capture by hanging listeners on the window object.
- // (Note we listen on the capture phase so the captured handlers will get called first)
- $.addEvent(
- $.MouseTracker.captureElement,
- eventParams.upName,
- eventParams.upHandler,
- true
- );
- $.addEvent(
- $.MouseTracker.captureElement,
- eventParams.moveName,
- eventParams.moveHandler,
- true
- );
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ tracker.element.setCapture( true );
+ } else {
+ eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType );
+ // We emulate mouse capture by hanging listeners on the document object.
+ // (Note we listen on the capture phase so the captured handlers will get called first)
+ $.addEvent(
+ $.MouseTracker.captureElement,
+ eventParams.upName,
+ eventParams.upHandler,
+ true
+ );
+ $.addEvent(
+ $.MouseTracker.captureElement,
+ eventParams.moveName,
+ eventParams.moveHandler,
+ true
+ );
+ }
}
}
@@ -1212,27 +1291,31 @@
* @inner
*/
function releasePointer( tracker, pointerType ) {
- var delegate = THIS[ tracker.hash ],
- pointsList = tracker.getActivePointersListByType( pointerType ),
- eventParams = getCaptureEventParams( tracker, pointerType );
+ var pointsList = tracker.getActivePointersListByType( pointerType ),
+ eventParams;
pointsList.captureCount--;
if ( pointsList.captureCount === 0 ) {
- // We emulate mouse capture by hanging listeners on the window object.
- // (Note we listen on the capture phase so the captured handlers will get called first)
- $.removeEvent(
- $.MouseTracker.captureElement,
- eventParams.moveName,
- eventParams.moveHandler,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- eventParams.upName,
- eventParams.upHandler,
- true
- );
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ tracker.element.releaseCapture();
+ } else {
+ eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType );
+ // We emulate mouse capture by hanging listeners on the document object.
+ // (Note we listen on the capture phase so the captured handlers will get called first)
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ eventParams.moveName,
+ eventParams.moveHandler,
+ true
+ );
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ eventParams.upName,
+ eventParams.upHandler,
+ true
+ );
+ }
}
}
@@ -1506,20 +1589,40 @@
}
+ /**
+ * Only used on IE 8
+ *
+ * @private
+ * @inner
+ */
+ function onMouseEnter( tracker, event ) {
+ event = $.getEvent( event );
+
+ handleMouseEnter( tracker, event );
+ }
+
+
/**
* @private
* @inner
*/
function onMouseOver( tracker, event ) {
- var gPoint;
-
event = $.getEvent( event );
if ( this === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
return;
}
- gPoint = {
+ handleMouseEnter( tracker, event );
+ }
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function handleMouseEnter( tracker, event ) {
+ var gPoint = {
id: $.MouseTracker.mousePointerId,
type: 'mouse',
isPrimary: true,
@@ -1531,20 +1634,40 @@
}
+ /**
+ * Only used on IE 8
+ *
+ * @private
+ * @inner
+ */
+ function onMouseLeave( tracker, event ) {
+ event = $.getEvent( event );
+
+ handleMouseExit( tracker, event );
+ }
+
+
/**
* @private
* @inner
*/
function onMouseOut( tracker, event ) {
- var gPoint;
-
event = $.getEvent( event );
if ( this === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
return;
}
- gPoint = {
+ handleMouseExit( tracker, event );
+ }
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function handleMouseExit( tracker, event ) {
+ var gPoint = {
id: $.MouseTracker.mousePointerId,
type: 'mouse',
isPrimary: true,
@@ -1556,6 +1679,31 @@
}
+ /**
+ * Returns a W3C DOM level 3 standard button value given an event.button property:
+ * -1 == none, 0 == primary/left, 1 == middle, 2 == secondary/right, 3 == X1/back, 4 == X2/forward, 5 == eraser (pen)
+ * @private
+ * @inner
+ */
+ function getStandardizedButton( button ) {
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ // On IE 8, 0 == none, 1 == left, 2 == right, 3 == left and right, 4 == middle, 5 == left and middle, 6 == right and middle, 7 == all three
+ // TODO: Support chorded (multiple) button presses on IE 8?
+ if ( button === 1 ) {
+ return 0;
+ } else if ( button === 2 ) {
+ return 2;
+ } else if ( button === 4 ) {
+ return 1;
+ } else {
+ return -1;
+ }
+ } else {
+ return button;
+ }
+ }
+
+
/**
* @private
* @inner
@@ -1573,7 +1721,7 @@
currentTime: $.now()
};
- if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) {
+ if ( updatePointersDown( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) {
$.stopEvent( event );
capturePointer( tracker, 'mouse' );
}
@@ -1622,11 +1770,12 @@
currentTime: $.now()
};
- if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) {
+ if ( updatePointersUp( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) {
releasePointer( tracker, 'mouse' );
}
}
+
/**
* @private
* @inner
@@ -1960,7 +2109,7 @@
if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) {
$.stopEvent( event );
- capturePointer( tracker, 'pointer' );
+ capturePointer( tracker, gPoint.type );
}
if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
@@ -2010,7 +2159,7 @@
};
if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) {
- releasePointer( tracker, 'pointer' );
+ releasePointer( tracker, gPoint.type );
}
}
@@ -2286,7 +2435,7 @@
* @param {Array.} gPoints
* Gesture points associated with the event.
* @param {Number} buttonChanged
- * The button involved in the event: -1: none, 0: primary, 1: aux, 2: secondary, 3: X1, 4: X2, 5: pen eraser.
+ * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
* Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,
* only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.
*
@@ -2304,30 +2453,71 @@
if ( typeof event.buttons !== 'undefined' ) {
pointsList.buttons = event.buttons;
} else {
- if ( buttonChanged === 0 ) {
- // Primary
- pointsList.buttons |= 1;
- } else if ( buttonChanged === 1 ) {
- // Aux
- pointsList.buttons |= 4;
- } else if ( buttonChanged === 2 ) {
- // Secondary
- pointsList.buttons |= 2;
- } else if ( buttonChanged === 3 ) {
- // X1 (Back)
- pointsList.buttons |= 8;
- } else if ( buttonChanged === 4 ) {
- // X2 (Forward)
- pointsList.buttons |= 16;
- } else if ( buttonChanged === 5 ) {
- // Pen Eraser
- pointsList.buttons |= 32;
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ if ( buttonChanged === 0 ) {
+ // Primary
+ pointsList.buttons += 1;
+ } else if ( buttonChanged === 1 ) {
+ // Aux
+ pointsList.buttons += 4;
+ } else if ( buttonChanged === 2 ) {
+ // Secondary
+ pointsList.buttons += 2;
+ } else if ( buttonChanged === 3 ) {
+ // X1 (Back)
+ pointsList.buttons += 8;
+ } else if ( buttonChanged === 4 ) {
+ // X2 (Forward)
+ pointsList.buttons += 16;
+ } else if ( buttonChanged === 5 ) {
+ // Pen Eraser
+ pointsList.buttons += 32;
+ }
+ } else {
+ if ( buttonChanged === 0 ) {
+ // Primary
+ pointsList.buttons |= 1;
+ } else if ( buttonChanged === 1 ) {
+ // Aux
+ pointsList.buttons |= 4;
+ } else if ( buttonChanged === 2 ) {
+ // Secondary
+ pointsList.buttons |= 2;
+ } else if ( buttonChanged === 3 ) {
+ // X1 (Back)
+ pointsList.buttons |= 8;
+ } else if ( buttonChanged === 4 ) {
+ // X2 (Forward)
+ pointsList.buttons |= 16;
+ } else if ( buttonChanged === 5 ) {
+ // Pen Eraser
+ pointsList.buttons |= 32;
+ }
}
}
// Only capture and track primary button, pen, and touch contacts
- //if ( buttonChanged !== 0 ) {
- if ( buttonChanged !== 0 && buttonChanged !== 1 ) { //TODO Remove this IE8 compatibility and use the commented line above
+ if ( buttonChanged !== 0 ) {
+ // Aux Press
+ if ( tracker.nonPrimaryPressHandler ) {
+ propagate = tracker.nonPrimaryPressHandler(
+ {
+ eventSource: tracker,
+ pointerType: gPoints[ 0 ].type,
+ position: getPointRelativeToAbsolute( gPoints[ 0 ].currentPos, tracker.element ),
+ button: buttonChanged,
+ buttons: pointsList.buttons,
+ isTouchEvent: gPoints[ 0 ].type === 'touch',
+ originalEvent: event,
+ preventDefaultAction: false,
+ userData: tracker.userData
+ }
+ );
+ if ( propagate === false ) {
+ $.cancelEvent( event );
+ }
+ }
+
return false;
}
@@ -2407,7 +2597,7 @@
* @param {Array.} gPoints
* Gesture points associated with the event.
* @param {Number} buttonChanged
- * The button involved in the event: -1: none, 0: primary, 1: aux, 2: secondary, 3: X1, 4: X2, 5: pen eraser.
+ * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
* Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,
* only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.
*
@@ -2431,30 +2621,71 @@
if ( typeof event.buttons !== 'undefined' ) {
pointsList.buttons = event.buttons;
} else {
- if ( buttonChanged === 0 ) {
- // Primary
- pointsList.buttons ^= ~1;
- } else if ( buttonChanged === 1 ) {
- // Aux
- pointsList.buttons ^= ~4;
- } else if ( buttonChanged === 2 ) {
- // Secondary
- pointsList.buttons ^= ~2;
- } else if ( buttonChanged === 3 ) {
- // X1 (Back)
- pointsList.buttons ^= ~8;
- } else if ( buttonChanged === 4 ) {
- // X2 (Forward)
- pointsList.buttons ^= ~16;
- } else if ( buttonChanged === 5 ) {
- // Pen Eraser
- pointsList.buttons ^= ~32;
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ if ( buttonChanged === 0 ) {
+ // Primary
+ pointsList.buttons -= 1;
+ } else if ( buttonChanged === 1 ) {
+ // Aux
+ pointsList.buttons -= 4;
+ } else if ( buttonChanged === 2 ) {
+ // Secondary
+ pointsList.buttons -= 2;
+ } else if ( buttonChanged === 3 ) {
+ // X1 (Back)
+ pointsList.buttons -= 8;
+ } else if ( buttonChanged === 4 ) {
+ // X2 (Forward)
+ pointsList.buttons -= 16;
+ } else if ( buttonChanged === 5 ) {
+ // Pen Eraser
+ pointsList.buttons -= 32;
+ }
+ } else {
+ if ( buttonChanged === 0 ) {
+ // Primary
+ pointsList.buttons ^= ~1;
+ } else if ( buttonChanged === 1 ) {
+ // Aux
+ pointsList.buttons ^= ~4;
+ } else if ( buttonChanged === 2 ) {
+ // Secondary
+ pointsList.buttons ^= ~2;
+ } else if ( buttonChanged === 3 ) {
+ // X1 (Back)
+ pointsList.buttons ^= ~8;
+ } else if ( buttonChanged === 4 ) {
+ // X2 (Forward)
+ pointsList.buttons ^= ~16;
+ } else if ( buttonChanged === 5 ) {
+ // Pen Eraser
+ pointsList.buttons ^= ~32;
+ }
}
}
// Only capture and track primary button, pen, and touch contacts
- //if ( buttonChanged !== 0 ) {
- if ( buttonChanged !== 0 && buttonChanged !== 1 ) { //TODO Remove this IE8 compatibility and use the commented line above
+ if ( buttonChanged !== 0 ) {
+ // Aux Release
+ if ( tracker.nonPrimaryReleaseHandler ) {
+ propagate = tracker.nonPrimaryReleaseHandler(
+ {
+ eventSource: tracker,
+ pointerType: gPoints[ 0 ].type,
+ position: getPointRelativeToAbsolute( gPoints[ 0 ].currentPos, tracker.element ),
+ button: buttonChanged,
+ buttons: pointsList.buttons,
+ isTouchEvent: gPoints[ 0 ].type === 'touch',
+ originalEvent: event,
+ preventDefaultAction: false,
+ userData: tracker.userData
+ }
+ );
+ if ( propagate === false ) {
+ $.cancelEvent( event );
+ }
+ }
+
return false;
}
diff --git a/src/viewer.js b/src/viewer.js
index 0f19fc8f..e41f9c04 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -342,18 +342,20 @@ $.Viewer = function( options ) {
this.innerTracker = new $.MouseTracker({
- element: this.canvas,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- dblClickTimeThreshold: this.dblClickTimeThreshold,
- dblClickDistThreshold: this.dblClickDistThreshold,
- clickHandler: $.delegate( this, onCanvasClick ),
- dblClickHandler: $.delegate( this, onCanvasDblClick ),
- dragHandler: $.delegate( this, onCanvasDrag ),
- dragEndHandler: $.delegate( this, onCanvasDragEnd ),
- releaseHandler: $.delegate( this, onCanvasRelease ),
- scrollHandler: $.delegate( this, onCanvasScroll ),
- pinchHandler: $.delegate( this, onCanvasPinch )
+ element: this.canvas,
+ clickTimeThreshold: this.clickTimeThreshold,
+ clickDistThreshold: this.clickDistThreshold,
+ dblClickTimeThreshold: this.dblClickTimeThreshold,
+ dblClickDistThreshold: this.dblClickDistThreshold,
+ clickHandler: $.delegate( this, onCanvasClick ),
+ dblClickHandler: $.delegate( this, onCanvasDblClick ),
+ dragHandler: $.delegate( this, onCanvasDrag ),
+ dragEndHandler: $.delegate( this, onCanvasDragEnd ),
+ releaseHandler: $.delegate( this, onCanvasRelease ),
+ nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ),
+ nonPrimaryReleaseHandler: $.delegate( this, onCanvasNonPrimaryRelease ),
+ scrollHandler: $.delegate( this, onCanvasScroll ),
+ pinchHandler: $.delegate( this, onCanvasPinch )
}).setTracking( this.mouseNavEnabled ? true : false ); // default state
this.outerTracker = new $.MouseTracker({
@@ -2419,7 +2421,7 @@ function onCanvasDragEnd( event ) {
function onCanvasRelease( event ) {
/**
- * Raised when the mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element.
+ * Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element.
*
* @event canvas-release
* @memberof OpenSeadragon.Viewer
@@ -2441,6 +2443,62 @@ function onCanvasRelease( event ) {
});
}
+function onCanvasNonPrimaryPress( event ) {
+ /**
+ * Raised when any non-primary pointer button is pressed on the {@link OpenSeadragon.Viewer#canvas} element.
+ *
+ * @event canvas-nonprimary-press
+ * @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 {String} pointerType - "mouse", "touch", "pen", etc.
+ * @property {Number} button - Button which caused the event.
+ * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
+ * @property {Number} buttons - Current buttons pressed.
+ * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.raiseEvent( 'canvas-nonprimary-press', {
+ tracker: event.eventSource,
+ position: event.position,
+ pointerType: event.pointerType,
+ button: event.button,
+ buttons: event.buttons,
+ originalEvent: event.originalEvent
+ });
+}
+
+function onCanvasNonPrimaryRelease( event ) {
+ /**
+ * Raised when any non-primary pointer button is released on the {@link OpenSeadragon.Viewer#canvas} element.
+ *
+ * @event canvas-nonprimary-release
+ * @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 {String} pointerType - "mouse", "touch", "pen", etc.
+ * @property {Number} button - Button which caused the event.
+ * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
+ * @property {Number} buttons - Current buttons pressed.
+ * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.raiseEvent( 'canvas-nonprimary-release', {
+ tracker: event.eventSource,
+ position: event.position,
+ pointerType: event.pointerType,
+ button: event.button,
+ buttons: event.buttons,
+ originalEvent: event.originalEvent
+ });
+}
+
function onCanvasPinch( event ) {
var gestureSettings,
centerPt,
@@ -2590,7 +2648,7 @@ function onContainerRelease( event ) {
}
}
/**
- * Raised when the mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#container} element.
+ * Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#container} element.
*
* @event container-release
* @memberof OpenSeadragon.Viewer
diff --git a/test/helpers/legacy.mouse.shim.js b/test/helpers/legacy.mouse.shim.js
index 7a8ff7cc..535f8815 100644
--- a/test/helpers/legacy.mouse.shim.js
+++ b/test/helpers/legacy.mouse.shim.js
@@ -11,8 +11,15 @@
$.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" );
}
- $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout", "mousedown", "mouseup", "mousemove" );
- $.MouseTracker.haveMouseEnter = false;
+ $.MouseTracker.havePointerEvents = false;
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
+ $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
+ $.MouseTracker.haveMouseEnter = true;
+ } else {
+ $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
+ $.MouseTracker.haveMouseEnter = false;
+ }
+ $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505)
$.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
@@ -33,4 +40,5 @@
$.MouseTracker.mousePointerId = "legacy-mouse";
$.MouseTracker.maxTouchPoints = 10;
+
}(OpenSeadragon));
diff --git a/test/modules/events.js b/test/modules/events.js
index 07c14ea6..5f850082 100644
--- a/test/modules/events.js
+++ b/test/modules/events.js
@@ -35,6 +35,8 @@
origExitHandler,
origPressHandler,
origReleaseHandler,
+ origNonPrimaryPressHandler,
+ origNonPrimaryReleaseHandler,
origMoveHandler,
origClickHandler,
origDblClickHandler,
@@ -44,6 +46,10 @@
exitCount,
pressCount,
releaseCount,
+ rightPressCount,
+ rightReleaseCount,
+ middlePressCount,
+ middleReleaseCount,
moveCount,
clickCount,
dblClickCount,
@@ -94,6 +100,36 @@
return true;
}
};
+ origNonPrimaryPressHandler = tracker.nonPrimaryPressHandler;
+ tracker.nonPrimaryPressHandler = function ( event ) {
+ if (event.button === 0) {
+ pressCount++;
+ } else if (event.button === 1) {
+ middlePressCount++;
+ } else if (event.button === 2) {
+ rightPressCount++;
+ }
+ if (origNonPrimaryPressHandler) {
+ return origNonPrimaryPressHandler( event );
+ } else {
+ return true;
+ }
+ };
+ origNonPrimaryReleaseHandler = tracker.nonPrimaryReleaseHandler;
+ tracker.nonPrimaryReleaseHandler = function ( event ) {
+ if (event.button === 0) {
+ releaseCount++;
+ } else if (event.button === 1) {
+ middleReleaseCount++;
+ } else if (event.button === 2) {
+ rightReleaseCount++;
+ }
+ if (origNonPrimaryReleaseHandler) {
+ return origNonPrimaryReleaseHandler( event );
+ } else {
+ return true;
+ }
+ };
origMoveHandler = tracker.moveHandler;
tracker.moveHandler = function ( event ) {
moveCount++;
@@ -165,23 +201,40 @@
var simulateLeave = function (x, y) {
simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y;
- // simEvent.relatedTarget = document.body;
- // $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent );
- //};
+ simEvent.relatedTarget = document.body;
+ $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent );
+ };
+
//var simulateLeaveFrame = function (x, y) {
// simEvent.clientX = offset.left + x;
// simEvent.clientY = offset.top + y;
// simEvent.relatedTarget = document.getElementsByTagName("html")[0];
- $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent );
- };
+ // $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent );
+ //};
var simulateDown = function (x, y) {
+ simEvent.button = 0;
simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y;
$canvas.simulate( 'mousedown', simEvent );
};
var simulateUp = function (x, y) {
+ simEvent.button = 0;
+ simEvent.clientX = offset.left + x;
+ simEvent.clientY = offset.top + y;
+ $canvas.simulate( 'mouseup', simEvent );
+ };
+
+ var simulateNonPrimaryDown = function (x, y, button) {
+ simEvent.button = button;
+ simEvent.clientX = offset.left + x;
+ simEvent.clientY = offset.top + y;
+ $canvas.simulate( 'mousedown', simEvent );
+ };
+
+ var simulateNonPrimaryUp = function (x, y, button) {
+ simEvent.button = button;
simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y;
$canvas.simulate( 'mouseup', simEvent );
@@ -198,6 +251,7 @@
var resetForAssessment = function () {
simEvent = {
+ button: 0,
clientX: offset.left,
clientY: offset.top
};
@@ -205,6 +259,10 @@
exitCount = 0;
pressCount = 0;
releaseCount = 0;
+ rightPressCount = 0;
+ rightReleaseCount = 0;
+ middlePressCount = 0;
+ middleReleaseCount = 0;
moveCount = 0;
clickCount = 0;
dblClickCount = 0;
@@ -231,6 +289,18 @@
if ('releaseCount' in expected) {
equal( releaseCount, expected.releaseCount, expected.description + 'releaseHandler event count matches expected (' + expected.releaseCount + ')' );
}
+ if ('rightPressCount' in expected) {
+ equal( rightPressCount, expected.rightPressCount, expected.description + 'nonPrimaryPressHandler event count (secondary/right button) matches expected (' + expected.rightPressCount + ')' );
+ }
+ if ('rightReleaseCount' in expected) {
+ equal( rightReleaseCount, expected.rightReleaseCount, expected.description + 'nonPrimaryReleaseHandler event count (secondary/right button) matches expected (' + expected.rightReleaseCount + ')' );
+ }
+ if ('middlePressCount' in expected) {
+ equal( middlePressCount, expected.middlePressCount, expected.description + 'nonPrimaryPressHandler event count (aux/middle button) matches expected (' + expected.middlePressCount + ')' );
+ }
+ if ('middleReleaseCount' in expected) {
+ equal( middleReleaseCount, expected.middleReleaseCount, expected.description + 'nonPrimaryReleaseHandler event count (aux/middle button) matches expected (' + expected.middleReleaseCount + ')' );
+ }
if ('moveCount' in expected) {
equal( moveCount, expected.moveCount, expected.description + 'moveHandler event count matches expected (' + expected.moveCount + ')' );
}
@@ -290,6 +360,10 @@
exitCount: 0,
pressCount: 0,
releaseCount: 1,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 20,
clickCount: 0,
dblClickCount: 0,
@@ -315,6 +389,10 @@
exitCount: 1,
pressCount: 0,
releaseCount: 0,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 20,
clickCount: 0,
dblClickCount: 0,
@@ -338,6 +416,10 @@
exitCount: 1,
pressCount: 0,
releaseCount: 0,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 20,
clickCount: 0,
dblClickCount: 0,
@@ -350,7 +432,7 @@
//quickClick: false
});
- // enter-press-release-press-release-exit (double click)
+ // enter-press-release-press-release-exit (primary/left double click)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
@@ -359,11 +441,15 @@
simulateUp(0, 0);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-press-release-press-release-exit (double click): ',
+ description: 'enter-press-release-press-release-exit (primary/left double click): ',
enterCount: 1,
exitCount: 1,
pressCount: 2,
releaseCount: 2,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 0,
clickCount: 2,
dblClickCount: 1,
@@ -376,18 +462,22 @@
//quickClick: true
});
- // enter-press-release-exit (click)
+ // enter-press-release-exit (primary/left click)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
simulateUp(0, 0);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-press-release-exit (click): ',
+ description: 'enter-press-release-exit (primary/left click): ',
enterCount: 1,
exitCount: 1,
pressCount: 1,
releaseCount: 1,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 0,
clickCount: 1,
dblClickCount: 0,
@@ -400,6 +490,92 @@
quickClick: true
});
+ // enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click)
+ resetForAssessment();
+ simulateEnter(0, 0);
+ simulateNonPrimaryDown(0, 0, 2);
+ simulateNonPrimaryUp(0, 0, 2);
+ simulateLeave(-1, -1);
+ assessGestureExpectations({
+ description: 'enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click): ',
+ enterCount: 1,
+ exitCount: 1,
+ pressCount: 0,
+ releaseCount: 0,
+ rightPressCount: 1,
+ rightReleaseCount: 1,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
+ moveCount: 0,
+ clickCount: 0,
+ dblClickCount: 0,
+ dragCount: 0,
+ dragEndCount: 0,
+ //insideElementPressed: true,
+ //insideElementReleased: true,
+ contacts: 0,
+ trackedPointers: 0,
+ //quickClick: true
+ });
+
+ // enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click)
+ resetForAssessment();
+ simulateEnter(0, 0);
+ simulateNonPrimaryDown(0, 0, 1);
+ simulateNonPrimaryUp(0, 0, 1);
+ simulateLeave(-1, -1);
+ assessGestureExpectations({
+ description: 'enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click): ',
+ enterCount: 1,
+ exitCount: 1,
+ pressCount: 0,
+ releaseCount: 0,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 1,
+ middleReleaseCount: 1,
+ moveCount: 0,
+ clickCount: 0,
+ dblClickCount: 0,
+ dragCount: 0,
+ dragEndCount: 0,
+ //insideElementPressed: true,
+ //insideElementReleased: true,
+ contacts: 0,
+ trackedPointers: 0,
+ //quickClick: true
+ });
+
+ // enter-nonprimarypress-move-nonprimaryrelease-move-exit (secondary/right button drag, release in tracked element)
+ resetForAssessment();
+ simulateEnter(0, 0);
+ simulateNonPrimaryDown(0, 0, 2);
+ simulateMove(1, 1, 100);
+ simulateNonPrimaryUp(10, 10, 2);
+ simulateMove(-1, -1, 100);
+ simulateLeave(-1, -1);
+ assessGestureExpectations({
+ description: 'enter-nonprimarypress-move-nonprimaryrelease-move-exit (secondary/right button drag, release in tracked element): ',
+ enterCount: 1,
+ exitCount: 1,
+ pressCount: 0,
+ releaseCount: 0,
+ rightPressCount: 1,
+ rightReleaseCount: 1,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
+ moveCount: 200,
+ clickCount: 0,
+ dblClickCount: 0,
+ dragCount: 0,
+ dragEndCount: 0,
+ //insideElementPressed: true,
+ //insideElementReleased: true,
+ contacts: 0,
+ trackedPointers: 0,
+ //quickClick: false
+ });
+
// enter-press-move-release-move-exit (drag, release in tracked element)
resetForAssessment();
simulateEnter(0, 0);
@@ -414,6 +590,10 @@
exitCount: 1,
pressCount: 1,
releaseCount: 1,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 200,
clickCount: 1,
dblClickCount: 0,
@@ -441,6 +621,10 @@
exitCount: 1,
pressCount: 1,
releaseCount: 1,
+ rightPressCount: 0,
+ rightReleaseCount: 0,
+ middlePressCount: 0,
+ middleReleaseCount: 0,
moveCount: 15,
clickCount: 0,
dblClickCount: 0,
@@ -453,7 +637,6 @@
quickClick: false
});
-
//// enter-press-move-exit-move-release-outside (drag, release outside iframe)
//resetForAssessment();
//simulateEnter(0, 0);
@@ -468,6 +651,10 @@
// exitCount: 1,
// pressCount: 1,
// releaseCount: 1,
+ // rightPressCount: 0,
+ // rightReleaseCount: 0,
+ // middlePressCount: 0,
+ // middleReleaseCount: 0,
// moveCount: 10,
// clickCount: 0,
// dblClickCount: 0,
@@ -479,6 +666,7 @@
// trackedPointers: 0,
// quickClick: false
//});
+
unhookViewerHandlers();
viewer.close();