Merge pull request #1872 from openseadragon/ms-mousetracker

MouseTracker Overhaul 2020
This commit is contained in:
Ian Gilman 2021-03-08 09:56:48 -08:00 committed by GitHub
commit 7ddc5c93e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1957 additions and 1386 deletions

View File

@ -202,7 +202,7 @@ module.exports = function(grunt) {
target: sources target: sources
}, },
"git-describe": { "git-describe": {
"options": { options: {
failOnError: false failOnError: false
}, },
build: {} build: {}

View File

@ -1,8 +1,9 @@
OPENSEADRAGON CHANGELOG OPENSEADRAGON CHANGELOG
======================= =======================
2.4.3: (In progress) 2.5.0: (In progress)
* DEPRECATION: MouseTracker exitHandler deprecated for name change to leaveHandler for consistency with DOM event names (#1872 @msalsbery)
* Now when "simple image" tile sources are removed from the viewer, they free the memory used by the pyramid they create (#1789 @TakumaKira) * Now when "simple image" tile sources are removed from the viewer, they free the memory used by the pyramid they create (#1789 @TakumaKira)
* Documentation fix (#1814 @kenanchristian) * Documentation fix (#1814 @kenanchristian)
* Better cleanup on destruction, to avoid memory leaks (#1832 @JoFrMueller) * Better cleanup on destruction, to avoid memory leaks (#1832 @JoFrMueller)
@ -11,6 +12,27 @@ OPENSEADRAGON CHANGELOG
* You can now specify tileSize for the Zoomify Tile Source (#1868 @abrlam) * You can now specify tileSize for the Zoomify Tile Source (#1868 @abrlam)
* Better use of IIIF "max" and "full" URL parameters (#1871 @MImranAsghar) * Better use of IIIF "max" and "full" URL parameters (#1871 @MImranAsghar)
* You can now specify the file format of the tiles in the Zoomify tile source (#1889 @abrlam) * You can now specify the file format of the tiles in the Zoomify tile source (#1889 @abrlam)
* Improved browser sniffing - detect EDGE and CHROMEEDGE browsers (#1872 @msalsbery)
* Improved DOM event model feature detection (#1872 @msalsbery)
* Added support for options parameter on addEvent()/removeEvent (to support passive option) (#1872 @msalsbery)
* Added OpenSeadragon.eventIsCanceled() function for defaultPrevented detection on DOM events (#1872 @msalsbery)
* MouseTracker: better PointerEvent model detection - removed use of deprecated window.navigator.pointerEnabled (#1872 @msalsbery)
* MouseTracker: added overHandler/outHandler options for handling corresponding pointerover/pointerout events (#1872 @msalsbery)
* MouseTracker: changed enterHandler/leaveHandler to use DOM pointerenter/pointerleave events instead of simulating using pointerover/pointerout (#1872 @msalsbery)
* All internal uses of MouseTracker use pointerenter/pointerleave events instead of pointerover/pointerout events for more consistent pointer tracking (#1872 @msalsbery)
* Fixed bug in Button class where two MouseTracker event handlers used an invalid "this" causing issues in some browsers (#1872 @msalsbery)
* Added pointerType property to Viewer container-enter, container-exit, canvas-drag, canvas-drag-end, canvas-pinch events (#1872 @msalsbery)
* MouseTracker: Fire dragEndHandler event even if release point same as initial contact point (#1872 @msalsbery)
* MouseTracker: Pointer capture implemented with capture APIs where available. Only fallback to emulated capture on extremely old browsers (#1872 @msalsbery)
* MouseTracker: Added preProcessEventHandler option to allow MouseTracker instances to control bubbling and default behavior of events on their associated element (#1872 @msalsbery)
* MouseTracker: Improved handling of canceled events (#1872 @msalsbery)
* 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)
* Added additional documentation for the zoomPerSecond viewer option (#1872 @msalsbery)
* MouseTracker: Per #1863, dropped support for Internet Explorer < 11 (#1872 @msalsbery)
2.4.2: 2.4.2:

View File

@ -77,6 +77,7 @@ $.ButtonState = {
* @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}. * @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.
* @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}. * @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.
* @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}. * @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.
* @param {Object} [options.userData=null] Arbitrary object to be passed unchanged to any attached handler methods.
*/ */
$.Button = function( options ) { $.Button = function( options ) {
@ -111,7 +112,8 @@ $.Button = function( options ) {
onEnter: null, onEnter: null,
onExit: null, onExit: null,
onFocus: null, onFocus: null,
onBlur: null onBlur: null,
userData: null
}, options ); }, options );
@ -136,6 +138,13 @@ $.Button = function( options ) {
this.imgDown.alt = this.imgDown.alt =
this.tooltip; this.tooltip;
// Allow pointer events to pass through the img elements so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.imgRest );
$.setElementPointerEventsNone( this.imgGroup );
$.setElementPointerEventsNone( this.imgHover );
$.setElementPointerEventsNone( this.imgDown );
this.element.style.position = "relative"; this.element.style.position = "relative";
$.setElementTouchActionNone( this.element ); $.setElementTouchActionNone( this.element );
@ -203,6 +212,7 @@ $.Button = function( options ) {
*/ */
this.tracker = new $.MouseTracker({ this.tracker = new $.MouseTracker({
userData: 'Button.tracker',
element: this.element, element: this.element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
@ -227,7 +237,7 @@ $.Button = function( options ) {
}, },
focusHandler: function ( event ) { focusHandler: function ( event ) {
this.enterHandler( event ); _this.tracker.enterHandler( event );
/** /**
* Raised when the Button element receives focus. * Raised when the Button element receives focus.
* *
@ -241,7 +251,7 @@ $.Button = function( options ) {
_this.raiseEvent( "focus", { originalEvent: event.originalEvent } ); _this.raiseEvent( "focus", { originalEvent: event.originalEvent } );
}, },
exitHandler: function( event ) { leaveHandler: function( event ) {
outTo( _this, $.ButtonState.GROUP ); outTo( _this, $.ButtonState.GROUP );
if ( event.insideElementPressed ) { if ( event.insideElementPressed ) {
/** /**
@ -259,7 +269,7 @@ $.Button = function( options ) {
}, },
blurHandler: function ( event ) { blurHandler: function ( event ) {
this.exitHandler( event ); _this.tracker.leaveHandler( event );
/** /**
* Raised when the Button element loses focus. * Raised when the Button element loses focus.
* *
@ -363,8 +373,8 @@ $.Button = function( options ) {
$.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{
/** /**
* TODO: Determine what this function is intended to do and if it's actually * Used by a button container element (e.g. a ButtonGroup) to transition the button state
* useful as an API point. * to ButtonState.GROUP.
* @function * @function
*/ */
notifyGroupEnter: function() { notifyGroupEnter: function() {
@ -372,8 +382,8 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
}, },
/** /**
* TODO: Determine what this function is intended to do and if it's actually * Used by a button container element (e.g. a ButtonGroup) to transition the button state
* useful as an API point. * to ButtonState.REST.
* @function * @function
*/ */
notifyGroupExit: function() { notifyGroupExit: function() {

View File

@ -88,6 +88,7 @@ $.ButtonGroup = function( options ) {
* @memberof OpenSeadragon.ButtonGroup# * @memberof OpenSeadragon.ButtonGroup#
*/ */
this.tracker = new $.MouseTracker({ this.tracker = new $.MouseTracker({
userData: 'ButtonGroup.tracker',
element: this.element, element: this.element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
@ -97,7 +98,7 @@ $.ButtonGroup = function( options ) {
_this.buttons[ i ].notifyGroupEnter(); _this.buttons[ i ].notifyGroupEnter();
} }
}, },
exitHandler: function ( event ) { leaveHandler: function ( event ) {
var i; var i;
if ( !event.insideElementPressed ) { if ( !event.insideElementPressed ) {
for ( i = 0; i < _this.buttons.length; i++ ) { for ( i = 0; i < _this.buttons.length; i++ ) {
@ -127,8 +128,8 @@ $.ButtonGroup.prototype = {
* @function * @function
* @private * @private
*/ */
emulateExit: function() { emulateLeave: function() {
this.tracker.exitHandler( { eventSource: this.tracker } ); this.tracker.leaveHandler( { eventSource: this.tracker } );
}, },
destroy: function() { destroy: function() {

View File

@ -126,6 +126,10 @@ $.Drawer = function( options ) {
this.canvas.style.height = "100%"; this.canvas.style.height = "100%";
this.canvas.style.position = "absolute"; this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true ); $.setElementOpacity( this.canvas, this.opacity, true );
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.canvas );
$.setElementTouchActionNone( this.canvas );
// explicit left-align // explicit left-align
this.container.style.textAlign = "left"; this.container.style.textAlign = "left";

File diff suppressed because it is too large Load Diff

View File

@ -167,12 +167,16 @@ $.Navigator = function( options ){
style.zIndex = 999999999; style.zIndex = 999999999;
style.cursor = 'default'; style.cursor = 'default';
}( this.displayRegion.style, this.borderWidth )); }( this.displayRegion.style, this.borderWidth ));
$.setElementPointerEventsNone( this.displayRegion );
$.setElementTouchActionNone( this.displayRegion );
this.displayRegionContainer = $.makeNeutralElement("div"); this.displayRegionContainer = $.makeNeutralElement("div");
this.displayRegionContainer.id = this.element.id + '-displayregioncontainer'; this.displayRegionContainer.id = this.element.id + '-displayregioncontainer';
this.displayRegionContainer.className = "displayregioncontainer"; this.displayRegionContainer.className = "displayregioncontainer";
this.displayRegionContainer.style.width = "100%"; this.displayRegionContainer.style.width = "100%";
this.displayRegionContainer.style.height = "100%"; this.displayRegionContainer.style.height = "100%";
$.setElementPointerEventsNone( this.displayRegionContainer );
$.setElementTouchActionNone( this.displayRegionContainer );
viewer.addControl( viewer.addControl(
this.element, this.element,
@ -221,12 +225,22 @@ $.Navigator = function( options ){
// Remove the base class' (Viewer's) innerTracker and replace it with our own // Remove the base class' (Viewer's) innerTracker and replace it with our own
this.innerTracker.destroy(); this.innerTracker.destroy();
this.innerTracker = new $.MouseTracker({ this.innerTracker = new $.MouseTracker({
element: this.element, userData: 'Navigator.innerTracker',
element: this.element, //this.canvas,
dragHandler: $.delegate( this, onCanvasDrag ), dragHandler: $.delegate( this, onCanvasDrag ),
clickHandler: $.delegate( this, onCanvasClick ), clickHandler: $.delegate( this, onCanvasClick ),
releaseHandler: $.delegate( this, onCanvasRelease ), releaseHandler: $.delegate( this, onCanvasRelease ),
scrollHandler: $.delegate( this, onCanvasScroll ) scrollHandler: $.delegate( this, onCanvasScroll )
}); });
this.outerTracker.userData = 'Navigator.outerTracker';
// this.innerTracker is attached to this.element...we need to allow pointer
// events to pass through this Viewer's canvas/container elements so implicit
// pointer capture works on touch devices
//TODO an alternative is to attach the new MouseTracker to this.canvas...not
// sure why it isn't already (see MouseTracker constructor call above)
$.setElementPointerEventsNone( this.canvas );
$.setElementPointerEventsNone( this.container );
this.addHandler("reset-size", function() { this.addHandler("reset-size", function() {
if (_this.viewport) { if (_this.viewport) {
@ -526,7 +540,7 @@ function onCanvasDrag( event ) {
* @property {Boolean} shift - True if the shift key was pressed during this event. * @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event. * @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
* @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false. * @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
*/ */
this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs); this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs);

View File

@ -381,7 +381,11 @@
* The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em> * The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
* *
* @property {Number} [zoomPerSecond=1.0] * @property {Number} [zoomPerSecond=1.0]
* The number of seconds to animate a single zoom event over. * Sets the zoom amount per second when zoomIn/zoomOut buttons are pressed and held.
* The value is a factor of the current zoom, so 1.0 (the default) disables zooming when the zoomIn/zoomOut buttons
* are held. Higher values will increase the rate of zoom when the zoomIn/zoomOut buttons are held. Note that values
* < 1.0 will reverse the operation of the zoomIn/zoomOut buttons (zoomIn button will decrease the zoom, zoomOut will
* increase the zoom).
* *
* @property {Boolean} [showNavigator=false] * @property {Boolean} [showNavigator=false]
* Set to true to make the navigator minimap appear. * Set to true to make the navigator minimap appear.
@ -917,6 +921,58 @@ function OpenSeadragon( options ){
return isTainted; return isTainted;
}; };
/**
* True if the browser supports the EventTarget.addEventListener() method
* @member {Boolean} supportsAddEventListener
* @memberof OpenSeadragon
*/
$.supportsAddEventListener = (function () {
return !!(document.documentElement.addEventListener && document.addEventListener);
}());
/**
* True if the browser supports the EventTarget.removeEventListener() method
* @member {Boolean} supportsRemoveEventListener
* @memberof OpenSeadragon
*/
$.supportsRemoveEventListener = (function () {
return !!(document.documentElement.removeEventListener && document.removeEventListener);
}());
/**
* True if the browser supports the newer EventTarget.addEventListener options argument
* @member {Boolean} supportsEventListenerOptions
* @memberof OpenSeadragon
*/
$.supportsEventListenerOptions = (function () {
var supported = 0;
if ( $.supportsAddEventListener ) {
try {
var options = {
get capture() {
supported++;
return false;
},
get once() {
supported++;
return false;
},
get passive() {
supported++;
return false;
}
};
window.addEventListener("test", null, options);
window.removeEventListener("test", null, options);
} catch ( e ) {
supported = 0;
}
}
return supported >= 3;
}());
/** /**
* A ratio comparing the device screen's pixel density to the canvas's backing store pixel density, * A ratio comparing the device screen's pixel density to the canvas's backing store pixel density,
* clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser. * clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.
@ -942,7 +998,7 @@ function OpenSeadragon( options ){
/** /**
* This closure defines all static methods available to the OpenSeadragon * This closure defines all static methods available to the OpenSeadragon
* namespace. Many, if not most, are taked directly from jQuery for use * namespace. Many, if not most, are taken directly from jQuery for use
* to simplify and reduce common programming patterns. More static methods * to simplify and reduce common programming patterns. More static methods
* from jQuery may eventually make their way into this though we are * from jQuery may eventually make their way into this though we are
* attempting to avoid an explicit dependency on jQuery only because * attempting to avoid an explicit dependency on jQuery only because
@ -1315,6 +1371,8 @@ function OpenSeadragon( options ){
* @property {Number} SAFARI * @property {Number} SAFARI
* @property {Number} CHROME * @property {Number} CHROME
* @property {Number} OPERA * @property {Number} OPERA
* @property {Number} EDGE
* @property {Number} CHROMEEDGE
*/ */
BROWSERS: { BROWSERS: {
UNKNOWN: 0, UNKNOWN: 0,
@ -1322,7 +1380,9 @@ function OpenSeadragon( options ){
FIREFOX: 2, FIREFOX: 2,
SAFARI: 3, SAFARI: 3,
CHROME: 4, CHROME: 4,
OPERA: 5 OPERA: 5,
EDGE: 6,
CHROMEEDGE: 7
}, },
@ -1893,6 +1953,19 @@ function OpenSeadragon( options ){
}, },
/**
* Sets the specified element's pointer-events style attribute to 'none'.
* @function
* @param {Element|String} element
*/
setElementPointerEventsNone: function( element ) {
element = $.getElement( element );
if ( typeof element.style.pointerEvents !== 'undefined' ) {
element.style.pointerEvents = 'none';
}
},
/** /**
* Add the specified CSS class to the element if not present. * Add the specified CSS class to the element if not present.
* @function * @function
@ -1978,6 +2051,34 @@ function OpenSeadragon( options ){
element.className = newClasses.join(' '); element.className = newClasses.join(' ');
}, },
/**
* Convert passed addEventListener() options to boolean or options object,
* depending on browser support.
* @function
* @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
* @param {Boolean} [options.capture]
* @param {Boolean} [options.passive]
* @param {Boolean} [options.once]
* @return {String} The protocol (http:, https:, file:, ftp: ...)
*/
normalizeEventListenerOptions: function (options) {
var opts;
if ( typeof options !== 'undefined' ) {
if ( typeof options === 'boolean' ) {
// Legacy Boolean useCapture
opts = $.supportsEventListenerOptions ? { capture: options } : options;
} else {
// Options object
opts = $.supportsEventListenerOptions ? options :
( ( typeof options.capture !== 'undefined' ) ? options.capture : false );
}
} else {
// No options specified - Legacy optional useCapture argument
// (for IE, first supported on version 9, so we'll pass a Boolean)
opts = $.supportsEventListenerOptions ? { capture: false } : false;
}
return opts;
},
/** /**
* Adds an event listener for the given element, eventName and handler. * Adds an event listener for the given element, eventName and handler.
@ -1985,16 +2086,20 @@ function OpenSeadragon( options ){
* @param {Element|String} element * @param {Element|String} element
* @param {String} eventName * @param {String} eventName
* @param {Function} handler * @param {Function} handler
* @param {Boolean} [useCapture] * @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
* @param {Boolean} [options.capture]
* @param {Boolean} [options.passive]
* @param {Boolean} [options.once]
*/ */
addEvent: (function () { addEvent: (function () {
if ( window.addEventListener ) { if ( $.supportsAddEventListener ) {
return function ( element, eventName, handler, useCapture ) { return function ( element, eventName, handler, options ) {
options = $.normalizeEventListenerOptions(options);
element = $.getElement( element ); element = $.getElement( element );
element.addEventListener( eventName, handler, useCapture ); element.addEventListener( eventName, handler, options );
}; };
} else if ( window.attachEvent ) { } else if ( document.documentElement.attachEvent && document.attachEvent ) {
return function ( element, eventName, handler, useCapture ) { return function ( element, eventName, handler ) {
element = $.getElement( element ); element = $.getElement( element );
element.attachEvent( 'on' + eventName, handler ); element.attachEvent( 'on' + eventName, handler );
}; };
@ -2011,16 +2116,18 @@ function OpenSeadragon( options ){
* @param {Element|String} element * @param {Element|String} element
* @param {String} eventName * @param {String} eventName
* @param {Function} handler * @param {Function} handler
* @param {Boolean} [useCapture] * @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
* @param {Boolean} [options.capture]
*/ */
removeEvent: (function () { removeEvent: (function () {
if ( window.removeEventListener ) { if ( $.supportsRemoveEventListener ) {
return function ( element, eventName, handler, useCapture ) { return function ( element, eventName, handler, options ) {
options = $.normalizeEventListenerOptions(options);
element = $.getElement( element ); element = $.getElement( element );
element.removeEventListener( eventName, handler, useCapture ); element.removeEventListener( eventName, handler, options );
}; };
} else if ( window.detachEvent ) { } else if ( document.documentElement.detachEvent && document.detachEvent ) {
return function( element, eventName, handler, useCapture ) { return function( element, eventName, handler ) {
element = $.getElement( element ); element = $.getElement( element );
element.detachEvent( 'on' + eventName, handler ); element.detachEvent( 'on' + eventName, handler );
}; };
@ -2049,7 +2156,7 @@ function OpenSeadragon( options ){
event = $.getEvent( event ); event = $.getEvent( event );
// legacy for preventing default // legacy for preventing default
event.cancel = true; event.cancel = true;
// IE for preventing default // IE < 9 for preventing default
event.returnValue = false; event.returnValue = false;
}; };
} }
@ -2058,7 +2165,39 @@ function OpenSeadragon( options ){
/** /**
* Stops the propagation of the event up the DOM. * Returns true if {@link OpenSeadragon.cancelEvent|cancelEvent} has been called on
* the event, otherwise returns false.
* @function
* @param {Event} [event]
*/
eventIsCanceled: function( event ) {
event = $.getEvent( event );
if ( event.preventDefault ) {
$.eventIsCanceled = function( event ){
// W3C
return event.defaultPrevented;
};
} else {
$.eventIsCanceled = function( event ){
event = $.getEvent( event );
if ( typeof event.returnValue !== 'undefined' ) {
// IE < 9
return !event.returnValue;
} else if ( typeof event.cancel !== 'undefined' ) {
// legacy
return event.cancel;
} else {
return false;
}
};
}
return $.eventIsCanceled( event );
},
/**
* Stops the propagation of the event through the DOM in the capturing and bubbling phases.
* @function * @function
* @param {Event} [event] * @param {Event} [event]
*/ */
@ -2071,7 +2210,7 @@ function OpenSeadragon( options ){
event.stopPropagation(); event.stopPropagation();
}; };
} else { } else {
// IE for stopping propagation // IE < 9 for stopping propagation
$.stopEvent = function( event ){ $.stopEvent = function( event ){
event = $.getEvent( event ); event = $.getEvent( event );
event.cancelBubble = true; event.cancelBubble = true;
@ -2569,7 +2708,17 @@ function OpenSeadragon( options ){
break; break;
case "Netscape": case "Netscape":
if (window.addEventListener) { if (window.addEventListener) {
if ( ua.indexOf( "Firefox" ) >= 0 ) { if ( ua.indexOf( "Edge" ) >= 0 ) {
$.Browser.vendor = $.BROWSERS.EDGE;
$.Browser.version = parseFloat(
ua.substring( ua.indexOf( "Edge" ) + 5 )
);
} else if ( ua.indexOf( "Edg" ) >= 0 ) {
$.Browser.vendor = $.BROWSERS.CHROMEEDGE;
$.Browser.version = parseFloat(
ua.substring( ua.indexOf( "Edg" ) + 4 )
);
} else if ( ua.indexOf( "Firefox" ) >= 0 ) {
$.Browser.vendor = $.BROWSERS.FIREFOX; $.Browser.vendor = $.BROWSERS.FIREFOX;
$.Browser.version = parseFloat( $.Browser.version = parseFloat(
ua.substring( ua.indexOf( "Firefox" ) + 8 ) ua.substring( ua.indexOf( "Firefox" ) + 8 )

View File

@ -85,14 +85,7 @@ $.ReferenceStrip = function ( options ) {
scroll: $.DEFAULT_SETTINGS.referenceStripScroll, scroll: $.DEFAULT_SETTINGS.referenceStripScroll,
clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold
}, options, { }, options, {
//required overrides element: this.element
element: this.element,
//These need to be overridden to prevent recursion since
//the navigator is a viewer and a viewer has a navigator
showNavigator: false,
mouseNavEnabled: false,
showNavigationControl: false,
showSequenceControl: false
} ); } );
$.extend( this, options ); $.extend( this, options );
@ -119,12 +112,14 @@ $.ReferenceStrip = function ( options ) {
$.setElementOpacity( this.element, 0.8 ); $.setElementOpacity( this.element, 0.8 );
this.viewer = viewer; this.viewer = viewer;
this.innerTracker = new $.MouseTracker( { this.tracker = new $.MouseTracker( {
userData: 'ReferenceStrip.tracker',
element: this.element, element: this.element,
clickHandler: $.delegate( this, onStripClick ),
dragHandler: $.delegate( this, onStripDrag ), dragHandler: $.delegate( this, onStripDrag ),
scrollHandler: $.delegate( this, onStripScroll ), scrollHandler: $.delegate( this, onStripScroll ),
enterHandler: $.delegate( this, onStripEnter ), enterHandler: $.delegate( this, onStripEnter ),
exitHandler: $.delegate( this, onStripExit ), leaveHandler: $.delegate( this, onStripLeave ),
keyDownHandler: $.delegate( this, onKeyDown ), keyDownHandler: $.delegate( this, onKeyDown ),
keyHandler: $.delegate( this, onKeyPress ) keyHandler: $.delegate( this, onKeyPress )
} ); } );
@ -189,34 +184,12 @@ $.ReferenceStrip = function ( options ) {
element.style.width = _this.panelWidth + 'px'; element.style.width = _this.panelWidth + 'px';
element.style.height = _this.panelHeight + 'px'; element.style.height = _this.panelHeight + 'px';
element.style.display = 'inline'; element.style.display = 'inline';
element.style.float = 'left'; //Webkit element.style['float'] = 'left'; //Webkit
element.style.cssFloat = 'left'; //Firefox element.style.cssFloat = 'left'; //Firefox
element.style.styleFloat = 'left'; //IE element.style.styleFloat = 'left'; //IE
element.style.padding = '2px'; element.style.padding = '2px';
$.setElementTouchActionNone( element ); $.setElementTouchActionNone( element );
$.setElementPointerEventsNone( element );
element.innerTracker = new $.MouseTracker( {
element: element,
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
pressHandler: function ( event ) {
event.eventSource.dragging = $.now();
},
releaseHandler: function ( event ) {
var tracker = event.eventSource,
id = tracker.element.id,
page = Number( id.split( '-' )[2] ),
now = $.now();
if ( event.insideElementPressed &&
event.insideElementReleased &&
tracker.dragging &&
( now - tracker.dragging ) < tracker.clickTimeThreshold ) {
tracker.dragging = null;
viewer.goToPage( page );
}
}
} );
this.element.appendChild( element ); this.element.appendChild( element );
@ -230,7 +203,8 @@ $.ReferenceStrip = function ( options ) {
}; };
$.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.ReferenceStrip.prototype */{ /** @lends OpenSeadragon.ReferenceStrip.prototype */
$.ReferenceStrip.prototype = {
/** /**
* @function * @function
@ -277,7 +251,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
} }
this.currentPage = page; this.currentPage = page;
onStripEnter.call( this, { eventSource: this.innerTracker } ); onStripEnter.call( this, { eventSource: this.tracker } );
} }
}, },
@ -292,7 +266,6 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
return false; return false;
}, },
// Overrides Viewer.destroy
destroy: function() { destroy: function() {
if (this.miniViewers) { if (this.miniViewers) {
for (var key in this.miniViewers) { for (var key in this.miniViewers) {
@ -300,14 +273,34 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
} }
} }
this.tracker.destroy();
if (this.element) { if (this.element) {
this.element.parentNode.removeChild(this.element); this.element.parentNode.removeChild(this.element);
} }
} }
} ); };
/**
* @private
* @inner
* @function
*/
function onStripClick( event ) {
if ( event.quick ) {
var page;
if ( 'horizontal' === this.scroll ) {
page = Math.floor(event.position.x / this.panelWidth);
} else {
page = Math.floor(event.position.y / this.panelHeight);
}
this.viewer.goToPage( page );
}
}
/** /**
@ -317,13 +310,14 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
*/ */
function onStripDrag( event ) { function onStripDrag( event ) {
this.dragging = true;
if ( this.element ) {
var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ), var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ), offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ), scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ), scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
viewerSize = $.getElementSize( this.viewer.canvas ); viewerSize = $.getElementSize( this.viewer.canvas );
this.dragging = true;
if ( this.element ) {
if ( 'horizontal' === this.scroll ) { if ( 'horizontal' === this.scroll ) {
if ( -event.delta.x > 0 ) { if ( -event.delta.x > 0 ) {
//forward //forward
@ -366,12 +360,13 @@ function onStripDrag( event ) {
* @function * @function
*/ */
function onStripScroll( event ) { function onStripScroll( event ) {
if ( this.element ) {
var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ), var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ), offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ), scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ), scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
viewerSize = $.getElementSize( this.viewer.canvas ); viewerSize = $.getElementSize( this.viewer.canvas );
if ( this.element ) {
if ( 'horizontal' === this.scroll ) { if ( 'horizontal' === this.scroll ) {
if ( event.scroll > 0 ) { if ( event.scroll > 0 ) {
//forward //forward
@ -412,7 +407,6 @@ function loadPanels( strip, viewerSize, scroll ) {
activePanelsStart, activePanelsStart,
activePanelsEnd, activePanelsEnd,
miniViewer, miniViewer,
style,
i, i,
element; element;
if ( 'horizontal' === strip.scroll ) { if ( 'horizontal' === strip.scroll ) {
@ -454,34 +448,14 @@ function loadPanels( strip, viewerSize, scroll ) {
ajaxHeaders: strip.viewer.ajaxHeaders, ajaxHeaders: strip.viewer.ajaxHeaders,
useCanvas: strip.useCanvas useCanvas: strip.useCanvas
} ); } );
// Allow pointer events to pass through miniViewer's canvas/container
miniViewer.displayRegion = $.makeNeutralElement( "div" ); // elements so implicit pointer capture works on touch devices
miniViewer.displayRegion.id = element.id + '-displayregion'; $.setElementPointerEventsNone( miniViewer.canvas );
miniViewer.displayRegion.className = 'displayregion'; $.setElementPointerEventsNone( miniViewer.container );
// We'll use event delegation from the reference strip element instead of
style = miniViewer.displayRegion.style; // handling events on every miniViewer
style.position = 'relative'; miniViewer.innerTracker.setTracking( false );
style.top = '0px'; miniViewer.outerTracker.setTracking( false );
style.left = '0px';
style.fontSize = '0px';
style.overflow = 'hidden';
style.float = 'left'; //Webkit
style.cssFloat = 'left'; //Firefox
style.styleFloat = 'left'; //IE
style.zIndex = 999999999;
style.cursor = 'default';
style.width = ( strip.panelWidth - 4 ) + 'px';
style.height = ( strip.panelHeight - 4 ) + 'px';
// TODO: What is this for? Future keyboard navigation support?
miniViewer.displayRegion.innerTracker = new $.MouseTracker( {
element: miniViewer.displayRegion,
startDisabled: true
} );
element.getElementsByTagName( 'div' )[0].appendChild(
miniViewer.displayRegion
);
strip.miniViewers[element.id] = miniViewer; strip.miniViewers[element.id] = miniViewer;
@ -524,7 +498,7 @@ function onStripEnter( event ) {
* @inner * @inner
* @function * @function
*/ */
function onStripExit( event ) { function onStripLeave( event ) {
var element = event.eventSource.element; var element = event.eventSource.element;
if ( 'horizontal' === this.scroll ) { if ( 'horizontal' === this.scroll ) {

View File

@ -267,6 +267,7 @@ $.Viewer = function( options ) {
style.top = "0px"; style.top = "0px";
style.textAlign = "left"; // needed to protect against style.textAlign = "left"; // needed to protect against
}( this.container.style )); }( this.container.style ));
$.setElementTouchActionNone( this.container );
this.container.insertBefore( this.canvas, this.container.firstChild ); this.container.insertBefore( this.canvas, this.container.firstChild );
this.element.appendChild( this.container ); this.element.appendChild( this.container );
@ -280,12 +281,14 @@ $.Viewer = function( options ) {
this.docOverflow = document.documentElement.style.overflow; this.docOverflow = document.documentElement.style.overflow;
this.innerTracker = new $.MouseTracker({ this.innerTracker = new $.MouseTracker({
userData: 'Viewer.innerTracker',
element: this.canvas, element: this.canvas,
startDisabled: !this.mouseNavEnabled, startDisabled: !this.mouseNavEnabled,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
dblClickTimeThreshold: this.dblClickTimeThreshold, dblClickTimeThreshold: this.dblClickTimeThreshold,
dblClickDistThreshold: this.dblClickDistThreshold, dblClickDistThreshold: this.dblClickDistThreshold,
contextMenuHandler: $.delegate( this, onCanvasContextMenu ),
keyDownHandler: $.delegate( this, onCanvasKeyDown ), keyDownHandler: $.delegate( this, onCanvasKeyDown ),
keyHandler: $.delegate( this, onCanvasKeyPress ), keyHandler: $.delegate( this, onCanvasKeyPress ),
clickHandler: $.delegate( this, onCanvasClick ), clickHandler: $.delegate( this, onCanvasClick ),
@ -293,7 +296,7 @@ $.Viewer = function( options ) {
dragHandler: $.delegate( this, onCanvasDrag ), dragHandler: $.delegate( this, onCanvasDrag ),
dragEndHandler: $.delegate( this, onCanvasDragEnd ), dragEndHandler: $.delegate( this, onCanvasDragEnd ),
enterHandler: $.delegate( this, onCanvasEnter ), enterHandler: $.delegate( this, onCanvasEnter ),
exitHandler: $.delegate( this, onCanvasExit ), leaveHandler: $.delegate( this, onCanvasLeave ),
pressHandler: $.delegate( this, onCanvasPress ), pressHandler: $.delegate( this, onCanvasPress ),
releaseHandler: $.delegate( this, onCanvasRelease ), releaseHandler: $.delegate( this, onCanvasRelease ),
nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ), nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ),
@ -303,6 +306,7 @@ $.Viewer = function( options ) {
}); });
this.outerTracker = new $.MouseTracker({ this.outerTracker = new $.MouseTracker({
userData: 'Viewer.outerTracker',
element: this.container, element: this.container,
startDisabled: !this.mouseNavEnabled, startDisabled: !this.mouseNavEnabled,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
@ -310,7 +314,7 @@ $.Viewer = function( options ) {
dblClickTimeThreshold: this.dblClickTimeThreshold, dblClickTimeThreshold: this.dblClickTimeThreshold,
dblClickDistThreshold: this.dblClickDistThreshold, dblClickDistThreshold: this.dblClickDistThreshold,
enterHandler: $.delegate( this, onContainerEnter ), enterHandler: $.delegate( this, onContainerEnter ),
exitHandler: $.delegate( this, onContainerExit ) leaveHandler: $.delegate( this, onContainerLeave )
}); });
if( this.toolbar ){ if( this.toolbar ){
@ -403,6 +407,8 @@ $.Viewer = function( options ) {
// Overlay container // Overlay container
this.overlaysContainer = $.makeNeutralElement( "div" ); this.overlaysContainer = $.makeNeutralElement( "div" );
$.setElementPointerEventsNone( this.overlaysContainer );
$.setElementTouchActionNone( this.overlaysContainer );
this.canvas.appendChild( this.overlaysContainer ); this.canvas.appendChild( this.overlaysContainer );
// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons // Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons
@ -1103,7 +1109,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
THIS[ this.hash ].fullPage = false; THIS[ this.hash ].fullPage = false;
// mouse will likely be outside now // mouse will likely be outside now
$.delegate( this, onContainerExit )( { } ); $.delegate( this, onContainerLeave )( { } );
} }
@ -2539,6 +2545,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 ) { function onCanvasKeyDown( event ) {
var canvasKeyDownEventArgs = { var canvasKeyDownEventArgs = {
originalEvent: event.originalEvent, originalEvent: event.originalEvent,
@ -2788,6 +2814,7 @@ function onCanvasDrag( event ) {
var canvasDragEventArgs = { var canvasDragEventArgs = {
tracker: event.eventSource, tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position, position: event.position,
delta: event.delta, delta: event.delta,
speed: event.speed, speed: event.speed,
@ -2805,13 +2832,14 @@ function onCanvasDrag( event ) {
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @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.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag. * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
* @property {Number} speed - Current computed speed, in pixels per second. * @property {Number} speed - Current computed speed, in pixels per second.
* @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0. * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
* @property {Boolean} shift - True if the shift key was pressed during this event. * @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event. * @property {Object} originalEvent - The original DOM event.
* @property {Boolean} preventDefaultAction - Set to true to prevent default drag behaviour. Default: false. * @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
this.raiseEvent( 'canvas-drag', canvasDragEventArgs); this.raiseEvent( 'canvas-drag', canvasDragEventArgs);
@ -2884,6 +2912,7 @@ function onCanvasDragEnd( event ) {
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @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.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} speed - Speed at the end of a drag gesture, in pixels per second. * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second.
* @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0. * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
@ -2893,6 +2922,7 @@ function onCanvasDragEnd( event ) {
*/ */
this.raiseEvent('canvas-drag-end', { this.raiseEvent('canvas-drag-end', {
tracker: event.eventSource, tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position, position: event.position,
speed: event.speed, speed: event.speed,
direction: event.direction, direction: event.direction,
@ -2931,12 +2961,7 @@ function onCanvasEnter( event ) {
}); });
} }
function onCanvasExit( event ) { function onCanvasLeave( event ) {
if (window.location !== window.parent.location){
$.MouseTracker.resetAllMouseTrackers();
}
/** /**
* Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element. * Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.
* *
@ -3115,6 +3140,7 @@ function onCanvasPinch( event ) {
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @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.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here. * @property {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here.
* @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element. * @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element.
* @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element. * @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element.
@ -3126,6 +3152,7 @@ function onCanvasPinch( event ) {
*/ */
this.raiseEvent('canvas-pinch', { this.raiseEvent('canvas-pinch', {
tracker: event.eventSource, tracker: event.eventSource,
pointerType: event.pointerType,
gesturePoints: event.gesturePoints, gesturePoints: event.gesturePoints,
lastCenter: event.lastCenter, lastCenter: event.lastCenter,
center: event.center, center: event.center,
@ -3214,6 +3241,7 @@ function onContainerEnter( event ) {
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @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.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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 {Number} buttons - Current buttons pressed. A 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 {Number} pointers - Number of pointers (all types) active in the tracked element. * @property {Number} pointers - Number of pointers (all types) active in the tracked element.
@ -3224,6 +3252,7 @@ function onContainerEnter( event ) {
*/ */
this.raiseEvent( 'container-enter', { this.raiseEvent( 'container-enter', {
tracker: event.eventSource, tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position, position: event.position,
buttons: event.buttons, buttons: event.buttons,
pointers: event.pointers, pointers: event.pointers,
@ -3233,7 +3262,7 @@ function onContainerEnter( event ) {
}); });
} }
function onContainerExit( event ) { function onContainerLeave( event ) {
if ( event.pointers < 1 ) { if ( event.pointers < 1 ) {
THIS[ this.hash ].mouseInside = false; THIS[ this.hash ].mouseInside = false;
if ( !THIS[ this.hash ].animating ) { if ( !THIS[ this.hash ].animating ) {
@ -3248,6 +3277,7 @@ function onContainerExit( event ) {
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @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.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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 {Number} buttons - Current buttons pressed. A 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 {Number} pointers - Number of pointers (all types) active in the tracked element. * @property {Number} pointers - Number of pointers (all types) active in the tracked element.
@ -3258,6 +3288,7 @@ function onContainerExit( event ) {
*/ */
this.raiseEvent( 'container-exit', { this.raiseEvent( 'container-exit', {
tracker: event.eventSource, tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position, position: event.position,
buttons: event.buttons, buttons: event.buttons,
pointers: event.pointers, pointers: event.pointers,
@ -3490,7 +3521,7 @@ function doSingleZoomOut() {
function lightUp() { function lightUp() {
if (this.buttonGroup) { if (this.buttonGroup) {
this.buttonGroup.emulateEnter(); this.buttonGroup.emulateEnter();
this.buttonGroup.emulateExit(); this.buttonGroup.emulateLeave();
} }
} }
@ -3511,7 +3542,7 @@ function onFullScreen() {
} }
// correct for no mouseout event on change // correct for no mouseout event on change
if ( this.buttonGroup ) { if ( this.buttonGroup ) {
this.buttonGroup.emulateExit(); this.buttonGroup.emulateLeave();
} }
this.fullPageButton.element.focus(); this.fullPageButton.element.focus();
if ( this.viewport ) { if ( this.viewport ) {

View File

@ -12,14 +12,24 @@
} }
$.MouseTracker.havePointerEvents = false; $.MouseTracker.havePointerEvents = false;
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { $.MouseTracker.unprefixedPointerEvents = true;
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
$.MouseTracker.haveMouseEnter = true; if ( $.Browser.vendor !== $.BROWSERS.IE || $.Browser.version > 8 ) {
} else {
$.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" ); $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
$.MouseTracker.haveMouseEnter = false; $.MouseTracker.havePointerOverOut = true;
} else {
$.MouseTracker.havePointerOverOut = false;
} }
$.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" ); $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
$.MouseTracker.mousePointerId = "legacy-mouse";
// Legacy mouse events capture support (IE/Firefox only?)
$.MouseTracker.havePointerCapture = (function () {
var divElement = document.createElement( 'div' );
return $.isFunction( divElement.setCapture ) && $.isFunction( divElement.releaseCapture );
}());
if ( $.MouseTracker.havePointerCapture ) {
$.MouseTracker.subscribeEvents.push( "losecapture" );
}
if ( 'ontouchstart' in window ) { if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations // iOS, Android, and other W3c Touch Event implementations
// (see http://www.w3.org/TR/touch-events/) // (see http://www.w3.org/TR/touch-events/)
@ -32,8 +42,5 @@
// Subscribe to these to prevent default gesture handling // Subscribe to these to prevent default gesture handling
$.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
} }
$.MouseTracker.mousePointerId = "legacy-mouse";
$.MouseTracker.maxTouchPoints = 10;
}(OpenSeadragon)); }(OpenSeadragon));

View File

@ -37,7 +37,7 @@
}; };
$canvas $canvas
.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event ) .simulate( 'mouseenter', event )
.simulate( 'mousedown', event ); .simulate( 'mousedown', event );
for ( var i = 0; i < args.dragCount; i++ ) { for ( var i = 0; i < args.dragCount; i++ ) {
event.clientX += args.dragDx; event.clientX += args.dragDx;
@ -47,7 +47,7 @@
} }
$canvas $canvas
.simulate( 'mouseup', event ) .simulate( 'mouseup', event )
.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', event ); .simulate( 'mouseleave', event );
}, },
// ---------- // ----------

View File

@ -32,7 +32,7 @@
offset = $canvas.offset(), offset = $canvas.offset(),
tracker = viewer.innerTracker, tracker = viewer.innerTracker,
origEnterHandler, origEnterHandler,
origExitHandler, origLeaveHandler,
origPressHandler, origPressHandler,
origReleaseHandler, origReleaseHandler,
origNonPrimaryPressHandler, origNonPrimaryPressHandler,
@ -43,7 +43,7 @@
origDragHandler, origDragHandler,
origDragEndHandler, origDragEndHandler,
enterCount, enterCount,
exitCount, leaveCount,
pressCount, pressCount,
releaseCount, releaseCount,
rightPressCount, rightPressCount,
@ -71,11 +71,11 @@
return true; return true;
} }
}; };
origExitHandler = tracker.exitHandler; origLeaveHandler = tracker.leaveHandler;
tracker.exitHandler = function ( event ) { tracker.leaveHandler = function ( event ) {
exitCount++; leaveCount++;
if (origExitHandler) { if (origLeaveHandler) {
return origExitHandler( event ); return origLeaveHandler( event );
} else { } else {
return true; return true;
} }
@ -182,7 +182,7 @@
var unhookViewerHandlers = function () { var unhookViewerHandlers = function () {
tracker.enterHandler = origEnterHandler; tracker.enterHandler = origEnterHandler;
tracker.exitHandler = origExitHandler; tracker.leaveHandler = origLeaveHandler;
tracker.pressHandler = origPressHandler; tracker.pressHandler = origPressHandler;
tracker.releaseHandler = origReleaseHandler; tracker.releaseHandler = origReleaseHandler;
tracker.moveHandler = origMoveHandler; tracker.moveHandler = origMoveHandler;
@ -195,21 +195,21 @@
var simulateEnter = function (x, y) { var simulateEnter = function (x, y) {
simEvent.clientX = offset.left + x; simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y; simEvent.clientY = offset.top + y;
$canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', simEvent ); $canvas.simulate( 'mouseenter', simEvent );
}; };
var simulateLeave = function (x, y) { var simulateLeave = function (x, y) {
simEvent.clientX = offset.left + x; simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y; simEvent.clientY = offset.top + y;
simEvent.relatedTarget = document.body; simEvent.relatedTarget = document.body;
$canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent ); $canvas.simulate( 'mouseleave', simEvent );
}; };
//var simulateLeaveFrame = function (x, y) { //var simulateLeaveFrame = function (x, y) {
// simEvent.clientX = offset.left + x; // simEvent.clientX = offset.left + x;
// simEvent.clientY = offset.top + y; // simEvent.clientY = offset.top + y;
// simEvent.relatedTarget = document.getElementsByTagName("html")[0]; // simEvent.relatedTarget = document.getElementsByTagName("html")[0];
// $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent ); // $canvas.simulate( 'mouseleave', simEvent );
//}; //};
var simulateDown = function (x, y) { var simulateDown = function (x, y) {
@ -256,7 +256,7 @@
clientY: offset.top clientY: offset.top
}; };
enterCount = 0; enterCount = 0;
exitCount = 0; leaveCount = 0;
pressCount = 0; pressCount = 0;
releaseCount = 0; releaseCount = 0;
rightPressCount = 0; rightPressCount = 0;
@ -280,8 +280,8 @@
if ('enterCount' in expected) { if ('enterCount' in expected) {
assert.equal( enterCount, expected.enterCount, expected.description + 'enterHandler event count matches expected (' + expected.enterCount + ')' ); assert.equal( enterCount, expected.enterCount, expected.description + 'enterHandler event count matches expected (' + expected.enterCount + ')' );
} }
if ('exitCount' in expected) { if ('leaveCount' in expected) {
assert.equal( exitCount, expected.exitCount, expected.description + 'exitHandler event count matches expected (' + expected.exitCount + ')' ); assert.equal( leaveCount, expected.leaveCount, expected.description + 'leaveHandler event count matches expected (' + expected.leaveCount + ')' );
} }
if ('pressCount' in expected) { if ('pressCount' in expected) {
assert.equal( pressCount, expected.pressCount, expected.description + 'pressHandler event count matches expected (' + expected.pressCount + ')' ); assert.equal( pressCount, expected.pressCount, expected.description + 'pressHandler event count matches expected (' + expected.pressCount + ')' );
@ -355,7 +355,7 @@
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-move-release (release in tracked element, press in unknown element): ', description: 'enter-move-release (release in tracked element, press in unknown element): ',
enterCount: 1, enterCount: 1,
exitCount: 0, leaveCount: 0,
pressCount: 0, pressCount: 0,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -375,16 +375,16 @@
}); });
simulateLeave(-1, -1); // flush tracked pointer simulateLeave(-1, -1); // flush tracked pointer
// enter-move-exit (fly-over) // enter-move-leave (fly-over)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateMove(1, 1, 10); simulateMove(1, 1, 10);
simulateMove(-1, -1, 10); simulateMove(-1, -1, 10);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-move-exit (fly-over): ', description: 'enter-move-leave (fly-over): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 0, rightPressCount: 0,
@ -403,15 +403,15 @@
//quickClick: false //quickClick: false
}); });
// move-exit (fly-over, no enter event) // move-leave (fly-over, no enter event)
resetForAssessment(); resetForAssessment();
simulateMove(1, 1, 10); simulateMove(1, 1, 10);
simulateMove(-1, -1, 10); simulateMove(-1, -1, 10);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'move-exit (fly-over, no enter event): ', description: 'move-leave (fly-over, no enter event): ',
enterCount: 0, enterCount: 0,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 0, rightPressCount: 0,
@ -430,7 +430,7 @@
//quickClick: false //quickClick: false
}); });
// enter-press-release-press-release-exit (primary/left double click) // enter-press-release-press-release-leave (primary/left double click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -439,9 +439,9 @@
simulateUp(0, 0); simulateUp(0, 0);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-release-press-release-exit (primary/left double click): ', description: 'enter-press-release-press-release-leave (primary/left double click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 2, pressCount: 2,
releaseCount: 2, releaseCount: 2,
rightPressCount: 0, rightPressCount: 0,
@ -452,7 +452,7 @@
clickCount: 2, clickCount: 2,
dblClickCount: 1, dblClickCount: 1,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 2, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
insideElementPressed: true, insideElementPressed: true,
insideElementReleased: true, insideElementReleased: true,
contacts: 0, contacts: 0,
@ -460,16 +460,16 @@
//quickClick: true //quickClick: true
}); });
// enter-press-release-exit (primary/left click) // enter-press-release-leave (primary/left click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
simulateUp(0, 0); simulateUp(0, 0);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-release-exit (primary/left click): ', description: 'enter-press-release-leave (primary/left click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -480,7 +480,7 @@
clickCount: 1, clickCount: 1,
dblClickCount: 0, dblClickCount: 0,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 1, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
insideElementPressed: true, insideElementPressed: true,
insideElementReleased: true, insideElementReleased: true,
contacts: 0, contacts: 0,
@ -488,16 +488,16 @@
quickClick: true quickClick: true
}); });
// enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click) // enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 2); simulateNonPrimaryDown(0, 0, 2);
simulateNonPrimaryUp(0, 0, 2); simulateNonPrimaryUp(0, 0, 2);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click): ', description: 'enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 1, rightPressCount: 1,
@ -516,16 +516,16 @@
//quickClick: true //quickClick: true
}); });
// enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click) // enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 1); simulateNonPrimaryDown(0, 0, 1);
simulateNonPrimaryUp(0, 0, 1); simulateNonPrimaryUp(0, 0, 1);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click): ', description: 'enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 0, rightPressCount: 0,
@ -544,7 +544,7 @@
//quickClick: true //quickClick: true
}); });
// enter-nonprimarypress-move-nonprimaryrelease-move-exit (secondary/right button drag, release in tracked element) // enter-nonprimarypress-move-nonprimaryrelease-move-leave (secondary/right button drag, release in tracked element)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 2); simulateNonPrimaryDown(0, 0, 2);
@ -553,9 +553,9 @@
simulateMove(-1, -1, 100); simulateMove(-1, -1, 100);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-nonprimarypress-move-nonprimaryrelease-move-exit (secondary/right button drag, release in tracked element): ', description: 'enter-nonprimarypress-move-nonprimaryrelease-move-leave (secondary/right button drag, release in tracked element): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 1, rightPressCount: 1,
@ -574,7 +574,7 @@
//quickClick: false //quickClick: false
}); });
// enter-press-move-release-move-exit (drag, release in tracked element) // enter-press-move-release-move-leave (drag, release in tracked element)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -583,9 +583,9 @@
simulateMove(-1, -1, 100); simulateMove(-1, -1, 100);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-move-release-move-exit (drag, release in tracked element): ', description: 'enter-press-move-release-move-leave (drag, release in tracked element): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -604,7 +604,7 @@
quickClick: false quickClick: false
}); });
// enter-press-move-exit-move-release (drag, release outside tracked element) // enter-press-move-leave-move-release (drag, release outside tracked element)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -614,9 +614,9 @@
simulateMove(-1, -1, 5); simulateMove(-1, -1, 5);
simulateUp(-5, -5); simulateUp(-5, -5);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-move-exit-move-release (drag, release outside tracked element): ', description: 'enter-press-move-leave-move-release (drag, release outside tracked element): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -635,7 +635,7 @@
quickClick: false quickClick: false
}); });
//// enter-press-move-exit-move-release-outside (drag, release outside iframe) //// enter-press-move-leave-move-release-outside (drag, release outside iframe)
//resetForAssessment(); //resetForAssessment();
//simulateEnter(0, 0); //simulateEnter(0, 0);
//simulateDown(0, 0); //simulateDown(0, 0);
@ -644,9 +644,9 @@
//simulateLeaveFrame(-1, -1); //simulateLeaveFrame(-1, -1);
//// you don't actually receive the mouseup if you mouseup outside of the document //// you don't actually receive the mouseup if you mouseup outside of the document
//assessGestureExpectations({ //assessGestureExpectations({
// description: 'enter-press-move-exit-move-release-outside (drag, release outside iframe): ', // description: 'enter-press-move-leave-move-release-outside (drag, release outside iframe): ',
// enterCount: 1, // enterCount: 1,
// exitCount: 1, // leaveCount: 1,
// pressCount: 1, // pressCount: 1,
// releaseCount: 1, // releaseCount: 1,
// rightPressCount: 0, // rightPressCount: 0,
@ -953,7 +953,7 @@
dragEndHandler: onMouseTrackerDragEnd, dragEndHandler: onMouseTrackerDragEnd,
releaseHandler: onMouseTrackerRelease, releaseHandler: onMouseTrackerRelease,
clickHandler: onMouseTrackerClick, clickHandler: onMouseTrackerClick,
exitHandler: onMouseTrackerExit leaveHandler: onMouseTrackerLeave
} ); } );
var event = { var event = {
@ -1050,7 +1050,7 @@
checkOriginalEventReceived( event ); checkOriginalEventReceived( event );
}; };
var onMouseTrackerExit = function ( event ) { var onMouseTrackerLeave = function ( event ) {
checkOriginalEventReceived( event ); checkOriginalEventReceived( event );
mouseTracker.destroy(); mouseTracker.destroy();

View File

@ -216,7 +216,7 @@
clientY: offset.top + locationY clientY: offset.top + locationY
}; };
$canvas $canvas
.simulate(OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event) .simulate('mouseenter', event)
.simulate('mousedown', event) .simulate('mousedown', event)
.simulate('mouseup', event); .simulate('mouseup', event);
}; };