From d2bb48036317538671b1ecf0b1fc61b77649d9a4 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Fri, 24 Jul 2020 20:05:39 -0700 Subject: [PATCH] Browser sniffing and pointer events fixes and enhancements --- Gruntfile.js | 2 +- changelog.txt | 12 +- src/button.js | 73 +++- src/buttongroup.js | 7 +- src/mousetracker.js | 533 +++++++++++++++++++++++------- src/navigator.js | 1 + src/openseadragon.js | 171 +++++++++- src/referencestrip.js | 9 +- src/viewer.js | 16 +- test/helpers/legacy.mouse.shim.js | 10 +- test/helpers/test.js | 4 +- test/modules/events.js | 96 +++--- test/modules/navigator.js | 2 +- 13 files changed, 704 insertions(+), 232 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index e030188c..e3395308 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -202,7 +202,7 @@ module.exports = function(grunt) { target: sources }, "git-describe": { - "options": { + options: { failOnError: false }, build: {} diff --git a/changelog.txt b/changelog.txt index 11ca2b6d..bae5de01 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,16 @@ OPENSEADRAGON CHANGELOG * Documentation fix (#1814 @kenanchristian) * Better cleanup on destruction, to avoid memory leaks (#1832 @JoFrMueller) * Miscellaneous code cleanup (#1840 @msalsbery) +* Improved browser sniffing - detect EDGE and CHROMEEDGE browsers +* Improved DOM event model feature detection +* Added support for options parameter on addEvent()/removeEvent (to support passive option) +* Added OpenSeadragon.eventIsCanceled() function for defaultPrevented detection on DOM events +* MouseTracker: better PointerEvent model detection - removed use of deprecated window.navigator.pointerEnabled +* MouseTracker: added overHandler/outHandler options for handling corresponding pointerover/pointerout events +* MouseTracker: changed enterHandler/leaveHandler to use DOM pointerenter/pointerleave events instead of simulating using pointerover/pointerout +* DEPRECATION: MouseTracker exitHandler deprecated for name change to leaveHandler for consistency with DOM event names +* Added missing Button event object properties (were listed in documentation but not implemented) +* Fixed bug in Button class where two MouseTracker event handlers used an invalid "this" causing issues in some browsers 2.4.2: @@ -54,7 +64,7 @@ OPENSEADRAGON CHANGELOG * You can now prevent canvas-click events on the navigator (#1416) * The navigator can now be restricted to just horizontal or just vertical panning (#1416) * Fixed DziTileSource so it doesn't load levels above maxLevel or below minLevel, if set (#1492) - + 2.3.1: * Debug mode now uses different colors for different tiled images (customizable via debugGridColor) (#1271) diff --git a/src/button.js b/src/button.js index 7b00f9ee..9c3f2db0 100644 --- a/src/button.js +++ b/src/button.js @@ -77,6 +77,7 @@ $.ButtonState = { * @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.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 ) { @@ -111,7 +112,8 @@ $.Button = function( options ) { onEnter: null, onExit: null, onFocus: null, - onBlur: null + onBlur: null, + userData: null }, options ); @@ -203,6 +205,7 @@ $.Button = function( options ) { */ this.tracker = new $.MouseTracker({ + userData: 'Button.tracker', element: this.element, clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -220,14 +223,18 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "enter", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "enter", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); } else if ( !event.buttonDownAny ) { inTo( _this, $.ButtonState.HOVER ); } }, focusHandler: function ( event ) { - this.enterHandler( event ); + _this.tracker.enterHandler( event ); /** * Raised when the Button element receives focus. * @@ -238,10 +245,14 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "focus", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "focus", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); }, - exitHandler: function( event ) { + leaveHandler: function( event ) { outTo( _this, $.ButtonState.GROUP ); if ( event.insideElementPressed ) { /** @@ -254,12 +265,16 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "exit", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "exit", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); } }, blurHandler: function ( event ) { - this.exitHandler( event ); + _this.tracker.leaveHandler( event ); /** * Raised when the Button element loses focus. * @@ -270,7 +285,11 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "blur", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "blur", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); }, pressHandler: function ( event ) { @@ -285,7 +304,11 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "press", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "press", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); }, releaseHandler: function( event ) { @@ -301,7 +324,11 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "release", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "release", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); } else if ( event.insideElementPressed ) { outTo( _this, $.ButtonState.GROUP ); } else { @@ -321,7 +348,11 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent("click", { originalEvent: event.originalEvent }); + _this.raiseEvent("click", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + }); } }, @@ -338,7 +369,11 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "click", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "click", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); /*** * Raised when the mouse button is released or touch ends in the Button element. * @@ -349,7 +384,11 @@ $.Button = function( options ) { * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( "release", { originalEvent: event.originalEvent } ); + _this.raiseEvent( "release", { + eventSource: _this, + originalEvent: event.originalEvent, + userData: _this.userData + } ); return false; } return true; @@ -363,8 +402,8 @@ $.Button = function( options ) { $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{ /** - * TODO: Determine what this function is intended to do and if it's actually - * useful as an API point. + * Used by a button container element (e.g. a ButtonGroup) to transition the button state + * to ButtonState.GROUP. * @function */ notifyGroupEnter: function() { @@ -372,8 +411,8 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon. }, /** - * TODO: Determine what this function is intended to do and if it's actually - * useful as an API point. + * Used by a button container element (e.g. a ButtonGroup) to transition the button state + * to ButtonState.REST. * @function */ notifyGroupExit: function() { diff --git a/src/buttongroup.js b/src/buttongroup.js index 995f90d3..5af71fea 100644 --- a/src/buttongroup.js +++ b/src/buttongroup.js @@ -88,6 +88,7 @@ $.ButtonGroup = function( options ) { * @memberof OpenSeadragon.ButtonGroup# */ this.tracker = new $.MouseTracker({ + userData: 'ButtonGroup.tracker', element: this.element, clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -97,7 +98,7 @@ $.ButtonGroup = function( options ) { _this.buttons[ i ].notifyGroupEnter(); } }, - exitHandler: function ( event ) { + leaveHandler: function ( event ) { var i; if ( !event.insideElementPressed ) { for ( i = 0; i < _this.buttons.length; i++ ) { @@ -127,8 +128,8 @@ $.ButtonGroup.prototype = { * @function * @private */ - emulateExit: function() { - this.tracker.exitHandler( { eventSource: this.tracker } ); + emulateLeave: function() { + this.tracker.leaveHandler( { eventSource: this.tracker } ); }, destroy: function() { diff --git a/src/mousetracker.js b/src/mousetracker.js index d1b584ae..72658f2f 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -74,8 +74,14 @@ * event is fired. * @param {OpenSeadragon.EventHandler} [options.enterHandler=null] * An optional handler for pointer enter. + * @param {OpenSeadragon.EventHandler} [options.leaveHandler=null] + * An optional handler for pointer leave. * @param {OpenSeadragon.EventHandler} [options.exitHandler=null] - * An optional handler for pointer exit. + * An optional handler for pointer leave. Deprecated. Use leaveHandler instead. + * @param {OpenSeadragon.EventHandler} [options.overHandler=null] + * An optional handler for pointer over. + * @param {OpenSeadragon.EventHandler} [options.outHandler=null] + * An optional handler for pointer out. * @param {OpenSeadragon.EventHandler} [options.pressHandler=null] * An optional handler for pointer press. * @param {OpenSeadragon.EventHandler} [options.nonPrimaryPressHandler=null] @@ -165,7 +171,10 @@ this.stopDelay = options.stopDelay || 50; this.enterHandler = options.enterHandler || null; + this.leaveHandler = options.leaveHandler || null; this.exitHandler = options.exitHandler || null; + this.overHandler = options.overHandler || null; + this.outHandler = options.outHandler || null; this.pressHandler = options.pressHandler || null; this.nonPrimaryPressHandler = options.nonPrimaryPressHandler || null; this.releaseHandler = options.releaseHandler || null; @@ -207,10 +216,10 @@ 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 ); }, + mouseover: function ( event ) { onMouseOver( _this, event ); }, // IE9+ only + mouseout: function ( event ) { onMouseOut( _this, event ); }, // IE9+ only + mouseenter: function ( event ) { onMouseEnter( _this, event ); }, + mouseleave: function ( event ) { onMouseLeave( _this, event ); }, mousedown: function ( event ) { onMouseDown( _this, event ); }, mouseup: function ( event ) { onMouseUp( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, @@ -231,6 +240,10 @@ MSPointerOver: function ( event ) { onPointerOver( _this, event ); }, pointerout: function ( event ) { onPointerOut( _this, event ); }, MSPointerOut: function ( event ) { onPointerOut( _this, event ); }, + pointerenter: function ( event ) { onPointerEnter( _this, event ); }, + MSPointerEnter: function ( event ) { onPointerEnter( _this, event ); }, + pointerleave: function ( event ) { onPointerLeave( _this, event ); }, + MSPointerLeave: function ( event ) { onPointerLeave( _this, event ); }, pointerdown: function ( event ) { onPointerDown( _this, event ); }, MSPointerDown: function ( event ) { onPointerDown( _this, event ); }, pointerup: function ( event ) { onPointerUp( _this, event ); }, @@ -403,12 +416,74 @@ * 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 {Object} event.userData + * Arbitrary user-defined object. + */ + enterHandler: function () { }, + + /** + * Implement or assign implementation to these handlers during or after + * calling the constructor. + * @function + * @deprecated Use leaveHandler instead + * @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.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 {Number} event.pointers + * Number of pointers (all types) active in the tracked element. + * @param {Boolean} event.insideElementPressed + * True if the left mouse button is currently being pressed and was + * initiated inside the tracked element, otherwise false. + * @param {Boolean} event.buttonDownAny + * Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead. + * @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 {Object} event.userData + * Arbitrary user-defined object. + */ + leaveHandler: function () { }, + + /** + * Implement or assign implementation to these handlers during or after + * calling the constructor. + * @function + * @deprecated Use leaveHandler instead + * @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.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 {Number} event.pointers + * Number of pointers (all types) active in the tracked element. + * @param {Boolean} event.insideElementPressed + * True if the left mouse button is currently being pressed and was + * initiated inside the tracked element, otherwise false. + * @param {Boolean} event.buttonDownAny + * Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead. + * @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. */ - enterHandler: function () { }, + exitHandler: function () { }, /** * Implement or assign implementation to these handlers during or after @@ -440,7 +515,39 @@ * @param {Object} event.userData * Arbitrary user-defined object. */ - exitHandler: function () { }, + overHandler: 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.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 {Number} event.pointers + * Number of pointers (all types) active in the tracked element. + * @param {Boolean} event.insideElementPressed + * True if the left mouse button is currently being pressed and was + * initiated inside the tracked element, otherwise false. + * @param {Boolean} event.buttonDownAny + * Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead. + * @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. + */ + outHandler: function () { }, /** * Implement or assign implementation to these handlers during or after @@ -1020,38 +1127,37 @@ $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" ); } - // Note: window.navigator.pointerEnable is deprecated on IE 11 and not part of W3C spec. - if ( window.PointerEvent && ( window.navigator.pointerEnabled || $.Browser.vendor !== $.BROWSERS.IE ) ) { + 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.subscribeEvents.push( "pointerenter", "pointerleave", "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel" ); $.MouseTracker.unprefixedPointerEvents = true; if( navigator.maxTouchPoints ) { $.MouseTracker.maxTouchPoints = navigator.maxTouchPoints; } else { $.MouseTracker.maxTouchPoints = 0; } - $.MouseTracker.haveMouseEnter = false; + $.MouseTracker.haveMouseOver = true; } else if ( window.MSPointerEvent && window.navigator.msPointerEnabled ) { // IE10 $.MouseTracker.havePointerEvents = true; - $.MouseTracker.subscribeEvents.push( "MSPointerOver", "MSPointerOut", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" ); + $.MouseTracker.subscribeEvents.push( "MSPointerEnter", "MSPointerLeave", "MSPointerOver", "MSPointerOut", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" ); $.MouseTracker.unprefixedPointerEvents = false; if( navigator.msMaxTouchPoints ) { $.MouseTracker.maxTouchPoints = navigator.msMaxTouchPoints; } else { $.MouseTracker.maxTouchPoints = 0; } - $.MouseTracker.haveMouseEnter = false; + $.MouseTracker.haveMouseOver = true; } else { // Legacy W3C mouse events $.MouseTracker.havePointerEvents = false; - if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { - $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); - $.MouseTracker.haveMouseEnter = true; - } else { + $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); + if ( $.Browser.vendor !== $.BROWSERS.IE || $.Browser.version > 8 ) { $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" ); - $.MouseTracker.haveMouseEnter = false; + $.MouseTracker.haveMouseOver = true; + } else { + $.MouseTracker.haveMouseOver = false; } $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" ); if ( 'ontouchstart' in window ) { @@ -1832,38 +1938,12 @@ /** - * Only used on IE 8 - * * @private * @inner */ function onMouseEnter( tracker, event ) { event = $.getEvent( event ); - handleMouseEnter( tracker, event ); - } - - - /** - * @private - * @inner - */ - function onMouseOver( tracker, event ) { - event = $.getEvent( event ); - - if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { - return; - } - - handleMouseEnter( tracker, event ); - } - - - /** - * @private - * @inner - */ - function handleMouseEnter( tracker, event ) { var gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', @@ -1873,42 +1953,21 @@ }; updatePointersEnter( tracker, event, [ gPoint ] ); + + //TODO simulate mouseover on IE < 9? + // if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { + // handleMouseOver( tracker, event ); + // } } /** - * Only used on IE 8 - * * @private * @inner */ function onMouseLeave( tracker, event ) { event = $.getEvent( event ); - handleMouseExit( tracker, event ); - } - - - /** - * @private - * @inner - */ - function onMouseOut( tracker, event ) { - event = $.getEvent( event ); - - if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { - return; - } - - handleMouseExit( tracker, event ); - } - - - /** - * @private - * @inner - */ - function handleMouseExit( tracker, event ) { var gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', @@ -1917,7 +1976,76 @@ currentTime: $.now() }; - updatePointersExit( tracker, event, [ gPoint ] ); + updatePointersLeave( tracker, event, [ gPoint ] ); + + //TODO simulate mouseoout on IE < 9? + // if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { + // handleMouseOut( tracker, event ); + // } + } + + + /** + * IE9+ only + * + * @private + * @inner + */ + function onMouseOver( tracker, event ) { + // if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { + // return; + // } + + handleMouseOver( tracker, event ); + } + + + /** + * @private + * @inner + */ + function handleMouseOver( tracker, event ) { + var gPoint = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + isPrimary: true, + currentPos: getMouseAbsolute( event ), + currentTime: $.now() + }; + + updatePointersOver( tracker, event, [ gPoint ] ); + } + + + /** + * IE9+ only + * + * @private + * @inner + */ + function onMouseOut( tracker, event ) { + // if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { + // return; + // } + + handleMouseOut( tracker, event ); + } + + + /** + * @private + * @inner + */ + function handleMouseOut( tracker, event ) { + var gPoint = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + isPrimary: true, + currentPos: getMouseAbsolute( event ), + currentTime: $.now() + }; + + updatePointersOut( tracker, event, [ gPoint ] ); } @@ -2083,7 +2211,7 @@ pointsList.captureCount = 1; releasePointer( tracker, pointsList.type ); // simulate touchleave/mouseout - updatePointersExit( tracker, event, abortGPoints ); + updatePointersLeave( tracker, event, abortGPoints ); } } } @@ -2199,7 +2327,7 @@ } // simulate touchleave on our tracked element - updatePointersExit( tracker, event, gPoints ); + updatePointersLeave( tracker, event, gPoints ); // simulate touchleave on our tracked element's tracked ancestor elements for ( i = 0; i < MOUSETRACKERS.length; i++ ) { @@ -2214,7 +2342,7 @@ currentTime: time } ); } - updatePointersExit( MOUSETRACKERS[ i ], event, parentGPoints ); + updatePointersLeave( MOUSETRACKERS[ i ], event, parentGPoints ); } } @@ -2306,12 +2434,10 @@ * @private * @inner */ - function onPointerOver( tracker, event ) { + function onPointerEnter( tracker, event ) { var gPoint; - if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { - return; - } + //$.console.log('pointerenter ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); gPoint = { id: event.pointerId, @@ -2322,6 +2448,52 @@ }; updatePointersEnter( tracker, event, [ gPoint ] ); +} + + + /** + * @private + * @inner + */ + function onPointerLeave( tracker, event ) { + var gPoint; + + //$.console.log('pointerleave ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + isPrimary: event.isPrimary, + currentPos: getMouseAbsolute( event ), + currentTime: $.now() + }; + + updatePointersLeave( tracker, event, [ gPoint ] ); + } + + + /** + * @private + * @inner + */ + function onPointerOver( tracker, event ) { + var gPoint; + + $.console.log('pointerover ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + + //if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { + // return; + //} + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + isPrimary: event.isPrimary, + currentPos: getMouseAbsolute( event ), + currentTime: $.now() + }; + + updatePointersOver( tracker, event, [ gPoint ] ); } @@ -2332,9 +2504,11 @@ function onPointerOut( tracker, event ) { var gPoint; - if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { - return; - } + $.console.log('pointerout ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + + //if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { + // return; + //} gPoint = { id: event.pointerId, @@ -2344,7 +2518,7 @@ currentTime: $.now() }; - updatePointersExit( tracker, event, [ gPoint ] ); + updatePointersOut( tracker, event, [ gPoint ] ); } @@ -2568,8 +2742,7 @@ i, gPointCount = gPoints.length, curGPoint, - updateGPoint, - propagate; + updateGPoint; for ( i = 0; i < gPointCount; i++ ) { curGPoint = gPoints[ i ]; @@ -2592,9 +2765,129 @@ startTrackingPointer( pointsList, curGPoint ); } - // Enter + // Enter (doesn't bubble and not cancelable) if ( tracker.enterHandler ) { - propagate = tracker.enterHandler( + tracker.enterHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), + buttons: pointsList.buttons, + pointers: tracker.getActivePointerCount(), + insideElementPressed: curGPoint.insideElementPressed, + buttonDownAny: pointsList.buttons !== 0, + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + userData: tracker.userData + } + ); + } + } + } + + + /** + * @function + * @private + * @inner + * @param {OpenSeadragon.MouseTracker} tracker + * A reference to the MouseTracker instance. + * @param {Object} event + * A reference to the originating DOM event. + * @param {Array.} gPoints + * Gesture points associated with the event. + */ + function updatePointersLeave( tracker, event, gPoints ) { + var pointsList = tracker.getActivePointersListByType(gPoints[0].type), + i, + gPointCount = gPoints.length, + curGPoint, + updateGPoint, + dispatchEventObj; + + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = pointsList.getById( curGPoint.id ); + + if ( updateGPoint ) { + // Already tracking the pointer. If captured then update it, else stop tracking it + if ( updateGPoint.captured ) { + updateGPoint.insideElement = false; + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; + } else { + stopTrackingPointer( pointsList, updateGPoint ); + } + + curGPoint = updateGPoint; + } + + // Leave (doesn't bubble and not cancelable) + // Note: exitHandler is deprecated, replaced by leaveHandler + if ( tracker.leaveHandler || tracker.exitHandler ) { + dispatchEventObj = { + eventSource: tracker, + pointerType: curGPoint.type, + position: curGPoint.currentPos && getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), + buttons: pointsList.buttons, + pointers: tracker.getActivePointerCount(), + insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false, + buttonDownAny: pointsList.buttons !== 0, + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + userData: tracker.userData + }; + + if ( tracker.leaveHandler ) { + tracker.leaveHandler( dispatchEventObj ); + } + if ( tracker.exitHandler ) { + tracker.exitHandler( dispatchEventObj ); + } + } + } + } + + + /** + * @function + * @private + * @inner + * @param {OpenSeadragon.MouseTracker} tracker + * A reference to the MouseTracker instance. + * @param {Object} event + * A reference to the originating DOM event. + * @param {Array.} gPoints + * Gesture points associated with the event. + */ + function updatePointersOver( tracker, event, gPoints ) { + var pointsList, + i, + gPointCount, + curGPoint, + updateGPoint, + propagate; + + if ( tracker.overHandler ) { + pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ); + gPointCount = gPoints.length; + + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = pointsList.getById( curGPoint.id ); + + if ( updateGPoint ) { + curGPoint = updateGPoint; + } else { + //curGPoint.captured = false; // Tracked by updatePointersEnter + curGPoint.insideElementPressed = false; + //curGPoint.insideElement = true; // Tracked by updatePointersEnter + } + + // Over + propagate = tracker.overHandler( { eventSource: tracker, pointerType: curGPoint.type, @@ -2616,7 +2909,6 @@ } } - /** * @function * @private @@ -2628,51 +2920,40 @@ * @param {Array.} gPoints * Gesture points associated with the event. */ - function updatePointersExit( tracker, event, gPoints ) { - var pointsList = tracker.getActivePointersListByType(gPoints[0].type), + function updatePointersOut( tracker, event, gPoints ) { + var pointsList, i, - gPointCount = gPoints.length, + gPointCount, curGPoint, updateGPoint, propagate; - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - updateGPoint = pointsList.getById( curGPoint.id ); + if ( tracker.outHandler ) { + pointsList = tracker.getActivePointersListByType(gPoints[0].type); + gPointCount = gPoints.length; - if ( updateGPoint ) { - // Already tracking the pointer. If captured then update it, else stop tracking it - if ( updateGPoint.captured ) { - updateGPoint.insideElement = false; - updateGPoint.lastPos = updateGPoint.currentPos; - updateGPoint.lastTime = updateGPoint.currentTime; - updateGPoint.currentPos = curGPoint.currentPos; - updateGPoint.currentTime = curGPoint.currentTime; - } else { - stopTrackingPointer( pointsList, updateGPoint ); + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = pointsList.getById( curGPoint.id ); + + if ( updateGPoint ) { + curGPoint = updateGPoint; } - curGPoint = updateGPoint; - } - - // Exit - if ( tracker.exitHandler ) { - propagate = tracker.exitHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: curGPoint.currentPos && getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), - buttons: pointsList.buttons, - pointers: tracker.getActivePointerCount(), - insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false, - buttonDownAny: pointsList.buttons !== 0, - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - + // Out + propagate = tracker.outHandler( { + eventSource: tracker, + pointerType: curGPoint.type, + position: curGPoint.currentPos && getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), + buttons: pointsList.buttons, + pointers: tracker.getActivePointerCount(), + insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false, + buttonDownAny: pointsList.buttons !== 0, + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } ); if ( propagate === false ) { $.cancelEvent( event ); } @@ -3326,7 +3607,7 @@ */ function updatePointersCancel( tracker, event, gPoints ) { updatePointersUp( tracker, event, gPoints, 0 ); - updatePointersExit( tracker, event, gPoints ); + updatePointersLeave( tracker, event, gPoints ); } @@ -3367,7 +3648,7 @@ * @function * @private * @inner - * @returns {Boolean} True if the target has access rights to events, otherwise false. + * @returns {Boolean} True if the target supports DOM Level 2 event subscription methods, otherwise false. */ function canAccessEvents (target) { try { diff --git a/src/navigator.js b/src/navigator.js index f0bdf452..4a8db20a 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -221,6 +221,7 @@ $.Navigator = function( options ){ // Remove the base class' (Viewer's) innerTracker and replace it with our own this.innerTracker.destroy(); this.innerTracker = new $.MouseTracker({ + userData: 'Navigator.innerTracker', element: this.element, dragHandler: $.delegate( this, onCanvasDrag ), clickHandler: $.delegate( this, onCanvasClick ), diff --git a/src/openseadragon.js b/src/openseadragon.js index f035c436..d23291b8 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -917,6 +917,58 @@ function OpenSeadragon( options ){ return isTainted; }; + /** + * True if the browser supports the EventTarget.removeEventListener() 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, * clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser. @@ -942,7 +994,7 @@ function OpenSeadragon( options ){ /** * 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 * from jQuery may eventually make their way into this though we are * attempting to avoid an explicit dependency on jQuery only because @@ -1315,6 +1367,8 @@ function OpenSeadragon( options ){ * @property {Number} SAFARI * @property {Number} CHROME * @property {Number} OPERA + * @property {Number} EDGE + * @property {Number} CHROMEEDGE */ BROWSERS: { UNKNOWN: 0, @@ -1322,7 +1376,9 @@ function OpenSeadragon( options ){ FIREFOX: 2, SAFARI: 3, CHROME: 4, - OPERA: 5 + OPERA: 5, + EDGE: 6, + CHROMEEDGE: 7 }, @@ -1978,6 +2034,34 @@ function OpenSeadragon( options ){ 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. @@ -1985,16 +2069,23 @@ function OpenSeadragon( options ){ * @param {Element|String} element * @param {String} eventName * @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] */ + // undefined - false or {capture: false} + // bool - bool or (capture: bool} + // obje - obje.capture or obje addEvent: (function () { - if ( window.addEventListener ) { - return function ( element, eventName, handler, useCapture ) { + if ( $.supportsAddEventListener ) { + return function ( element, eventName, handler, options ) { + options = $.normalizeEventListenerOptions(options); element = $.getElement( element ); - element.addEventListener( eventName, handler, useCapture ); + element.addEventListener( eventName, handler, options ); }; - } else if ( window.attachEvent ) { - return function ( element, eventName, handler, useCapture ) { + } else if ( document.documentElement.attachEvent && document.attachEvent ) { + return function ( element, eventName, handler ) { element = $.getElement( element ); element.attachEvent( 'on' + eventName, handler ); }; @@ -2011,16 +2102,18 @@ function OpenSeadragon( options ){ * @param {Element|String} element * @param {String} eventName * @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 () { - if ( window.removeEventListener ) { - return function ( element, eventName, handler, useCapture ) { + if ( $.supportsRemoveEventListener ) { + return function ( element, eventName, handler, options ) { + options = $.normalizeEventListenerOptions(options); element = $.getElement( element ); - element.removeEventListener( eventName, handler, useCapture ); + element.removeEventListener( eventName, handler, options ); }; - } else if ( window.detachEvent ) { - return function( element, eventName, handler, useCapture ) { + } else if ( document.documentElement.detachEvent && document.detachEvent ) { + return function( element, eventName, handler ) { element = $.getElement( element ); element.detachEvent( 'on' + eventName, handler ); }; @@ -2049,7 +2142,7 @@ function OpenSeadragon( options ){ event = $.getEvent( event ); // legacy for preventing default event.cancel = true; - // IE for preventing default + // IE < 9 for preventing default event.returnValue = false; }; } @@ -2058,7 +2151,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 * @param {Event} [event] */ @@ -2071,7 +2196,7 @@ function OpenSeadragon( options ){ event.stopPropagation(); }; } else { - // IE for stopping propagation + // IE < 9 for stopping propagation $.stopEvent = function( event ){ event = $.getEvent( event ); event.cancelBubble = true; @@ -2569,7 +2694,17 @@ function OpenSeadragon( options ){ break; case "Netscape": 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.version = parseFloat( ua.substring( ua.indexOf( "Firefox" ) + 8 ) diff --git a/src/referencestrip.js b/src/referencestrip.js index 31340e43..ede83662 100644 --- a/src/referencestrip.js +++ b/src/referencestrip.js @@ -120,11 +120,12 @@ $.ReferenceStrip = function ( options ) { this.viewer = viewer; this.innerTracker = new $.MouseTracker( { + userData: 'ReferenceStrip.innerTracker', element: this.element, dragHandler: $.delegate( this, onStripDrag ), scrollHandler: $.delegate( this, onStripScroll ), enterHandler: $.delegate( this, onStripEnter ), - exitHandler: $.delegate( this, onStripExit ), + leaveHandler: $.delegate( this, onStripLeave ), keyDownHandler: $.delegate( this, onKeyDown ), keyHandler: $.delegate( this, onKeyPress ) } ); @@ -196,6 +197,7 @@ $.ReferenceStrip = function ( options ) { $.setElementTouchActionNone( element ); element.innerTracker = new $.MouseTracker( { + userData: 'ReferenceStrip.TileSource.innerTracker', element: element, clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -475,7 +477,8 @@ function loadPanels( strip, viewerSize, scroll ) { // TODO: What is this for? Future keyboard navigation support? miniViewer.displayRegion.innerTracker = new $.MouseTracker( { - element: miniViewer.displayRegion, + userData: 'ReferenceStrip.miniViewer.innerTracker', + element: miniViewer.displayRegion, startDisabled: true } ); @@ -524,7 +527,7 @@ function onStripEnter( event ) { * @inner * @function */ -function onStripExit( event ) { +function onStripLeave( event ) { var element = event.eventSource.element; if ( 'horizontal' === this.scroll ) { diff --git a/src/viewer.js b/src/viewer.js index b5b24318..7cb756a2 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -280,6 +280,7 @@ $.Viewer = function( options ) { this.docOverflow = document.documentElement.style.overflow; this.innerTracker = new $.MouseTracker({ + userData: 'Viewer.innerTracker', element: this.canvas, startDisabled: !this.mouseNavEnabled, clickTimeThreshold: this.clickTimeThreshold, @@ -293,7 +294,7 @@ $.Viewer = function( options ) { dragHandler: $.delegate( this, onCanvasDrag ), dragEndHandler: $.delegate( this, onCanvasDragEnd ), enterHandler: $.delegate( this, onCanvasEnter ), - exitHandler: $.delegate( this, onCanvasExit ), + leaveHandler: $.delegate( this, onCanvasLeave ), pressHandler: $.delegate( this, onCanvasPress ), releaseHandler: $.delegate( this, onCanvasRelease ), nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ), @@ -303,6 +304,7 @@ $.Viewer = function( options ) { }); this.outerTracker = new $.MouseTracker({ + userData: 'Viewer.outerTracker', element: this.container, startDisabled: !this.mouseNavEnabled, clickTimeThreshold: this.clickTimeThreshold, @@ -310,7 +312,7 @@ $.Viewer = function( options ) { dblClickTimeThreshold: this.dblClickTimeThreshold, dblClickDistThreshold: this.dblClickDistThreshold, enterHandler: $.delegate( this, onContainerEnter ), - exitHandler: $.delegate( this, onContainerExit ) + leaveHandler: $.delegate( this, onContainerLeave ) }); if( this.toolbar ){ @@ -1099,7 +1101,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, THIS[ this.hash ].fullPage = false; // mouse will likely be outside now - $.delegate( this, onContainerExit )( { } ); + $.delegate( this, onContainerLeave )( { } ); } @@ -2925,7 +2927,7 @@ function onCanvasEnter( event ) { }); } -function onCanvasExit( event ) { +function onCanvasLeave( event ) { if (window.location !== window.parent.location){ $.MouseTracker.resetAllMouseTrackers(); @@ -3227,7 +3229,7 @@ function onContainerEnter( event ) { }); } -function onContainerExit( event ) { +function onContainerLeave( event ) { if ( event.pointers < 1 ) { THIS[ this.hash ].mouseInside = false; if ( !THIS[ this.hash ].animating ) { @@ -3483,7 +3485,7 @@ function doSingleZoomOut() { function lightUp() { this.buttons.emulateEnter(); - this.buttons.emulateExit(); + this.buttons.emulateLeave(); } @@ -3503,7 +3505,7 @@ function onFullScreen() { } // correct for no mouseout event on change if ( this.buttons ) { - this.buttons.emulateExit(); + this.buttons.emulateLeave(); } this.fullPageButton.element.focus(); if ( this.viewport ) { diff --git a/test/helpers/legacy.mouse.shim.js b/test/helpers/legacy.mouse.shim.js index 3609ed85..e946c5f9 100644 --- a/test/helpers/legacy.mouse.shim.js +++ b/test/helpers/legacy.mouse.shim.js @@ -12,12 +12,12 @@ } $.MouseTracker.havePointerEvents = false; - if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { - $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); - $.MouseTracker.haveMouseEnter = true; - } else { + $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); + if ( $.Browser.vendor !== $.BROWSERS.IE || $.Browser.version > 8 ) { $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" ); - $.MouseTracker.haveMouseEnter = false; + $.MouseTracker.haveMouseOver = true; + } else { + $.MouseTracker.haveMouseOver = false; } $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" ); if ( 'ontouchstart' in window ) { diff --git a/test/helpers/test.js b/test/helpers/test.js index 8e7685d8..60931bba 100644 --- a/test/helpers/test.js +++ b/test/helpers/test.js @@ -37,7 +37,7 @@ }; $canvas - .simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event ) + .simulate( 'mouseenter', event ) .simulate( 'mousedown', event ); for ( var i = 0; i < args.dragCount; i++ ) { event.clientX += args.dragDx; @@ -47,7 +47,7 @@ } $canvas .simulate( 'mouseup', event ) - .simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', event ); + .simulate( 'mouseleave', event ); }, // ---------- diff --git a/test/modules/events.js b/test/modules/events.js index 849e86f9..68e6232d 100644 --- a/test/modules/events.js +++ b/test/modules/events.js @@ -32,7 +32,7 @@ offset = $canvas.offset(), tracker = viewer.innerTracker, origEnterHandler, - origExitHandler, + origLeaveHandler, origPressHandler, origReleaseHandler, origNonPrimaryPressHandler, @@ -43,7 +43,7 @@ origDragHandler, origDragEndHandler, enterCount, - exitCount, + leaveCount, pressCount, releaseCount, rightPressCount, @@ -71,11 +71,11 @@ return true; } }; - origExitHandler = tracker.exitHandler; - tracker.exitHandler = function ( event ) { - exitCount++; - if (origExitHandler) { - return origExitHandler( event ); + origLeaveHandler = tracker.leaveHandler; + tracker.leaveHandler = function ( event ) { + leaveCount++; + if (origLeaveHandler) { + return origLeaveHandler( event ); } else { return true; } @@ -182,7 +182,7 @@ var unhookViewerHandlers = function () { tracker.enterHandler = origEnterHandler; - tracker.exitHandler = origExitHandler; + tracker.leaveHandler = origLeaveHandler; tracker.pressHandler = origPressHandler; tracker.releaseHandler = origReleaseHandler; tracker.moveHandler = origMoveHandler; @@ -195,21 +195,21 @@ var simulateEnter = function (x, y) { simEvent.clientX = offset.left + x; simEvent.clientY = offset.top + y; - $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', simEvent ); + $canvas.simulate( 'mouseenter', simEvent ); }; 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 ); + $canvas.simulate( 'mouseleave', 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( 'mouseleave', simEvent ); //}; var simulateDown = function (x, y) { @@ -256,7 +256,7 @@ clientY: offset.top }; enterCount = 0; - exitCount = 0; + leaveCount = 0; pressCount = 0; releaseCount = 0; rightPressCount = 0; @@ -280,8 +280,8 @@ if ('enterCount' in expected) { assert.equal( enterCount, expected.enterCount, expected.description + 'enterHandler event count matches expected (' + expected.enterCount + ')' ); } - if ('exitCount' in expected) { - assert.equal( exitCount, expected.exitCount, expected.description + 'exitHandler event count matches expected (' + expected.exitCount + ')' ); + if ('leaveCount' in expected) { + assert.equal( leaveCount, expected.leaveCount, expected.description + 'leaveHandler event count matches expected (' + expected.leaveCount + ')' ); } if ('pressCount' in expected) { assert.equal( pressCount, expected.pressCount, expected.description + 'pressHandler event count matches expected (' + expected.pressCount + ')' ); @@ -355,7 +355,7 @@ assessGestureExpectations({ description: 'enter-move-release (release in tracked element, press in unknown element): ', enterCount: 1, - exitCount: 0, + leaveCount: 0, pressCount: 0, releaseCount: 1, rightPressCount: 0, @@ -375,16 +375,16 @@ }); simulateLeave(-1, -1); // flush tracked pointer - // enter-move-exit (fly-over) + // enter-move-leave (fly-over) resetForAssessment(); simulateEnter(0, 0); simulateMove(1, 1, 10); simulateMove(-1, -1, 10); simulateLeave(-1, -1); assessGestureExpectations({ - description: 'enter-move-exit (fly-over): ', + description: 'enter-move-leave (fly-over): ', enterCount: 1, - exitCount: 1, + leaveCount: 1, pressCount: 0, releaseCount: 0, rightPressCount: 0, @@ -403,15 +403,15 @@ //quickClick: false }); - // move-exit (fly-over, no enter event) + // move-leave (fly-over, no enter event) resetForAssessment(); simulateMove(1, 1, 10); simulateMove(-1, -1, 10); simulateLeave(-1, -1); assessGestureExpectations({ - description: 'move-exit (fly-over, no enter event): ', + description: 'move-leave (fly-over, no enter event): ', enterCount: 0, - exitCount: 1, + leaveCount: 1, pressCount: 0, releaseCount: 0, rightPressCount: 0, @@ -430,7 +430,7 @@ //quickClick: false }); - // enter-press-release-press-release-exit (primary/left double click) + // enter-press-release-press-release-leave (primary/left double click) resetForAssessment(); simulateEnter(0, 0); simulateDown(0, 0); @@ -439,9 +439,9 @@ simulateUp(0, 0); simulateLeave(-1, -1); 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, - exitCount: 1, + leaveCount: 1, pressCount: 2, releaseCount: 2, rightPressCount: 0, @@ -460,16 +460,16 @@ //quickClick: true }); - // enter-press-release-exit (primary/left click) + // enter-press-release-leave (primary/left click) resetForAssessment(); simulateEnter(0, 0); simulateDown(0, 0); simulateUp(0, 0); simulateLeave(-1, -1); assessGestureExpectations({ - description: 'enter-press-release-exit (primary/left click): ', + description: 'enter-press-release-leave (primary/left click): ', enterCount: 1, - exitCount: 1, + leaveCount: 1, pressCount: 1, releaseCount: 1, rightPressCount: 0, @@ -488,16 +488,16 @@ quickClick: true }); - // enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click) + // enter-nonprimarypress-nonprimaryrelease-leave (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): ', + description: 'enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click): ', enterCount: 1, - exitCount: 1, + leaveCount: 1, pressCount: 0, releaseCount: 0, rightPressCount: 1, @@ -516,16 +516,16 @@ //quickClick: true }); - // enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click) + // enter-nonprimarypress-nonprimaryrelease-leave (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): ', + description: 'enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click): ', enterCount: 1, - exitCount: 1, + leaveCount: 1, pressCount: 0, releaseCount: 0, rightPressCount: 0, @@ -544,7 +544,7 @@ //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(); simulateEnter(0, 0); simulateNonPrimaryDown(0, 0, 2); @@ -553,9 +553,9 @@ simulateMove(-1, -1, 100); simulateLeave(-1, -1); 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, - exitCount: 1, + leaveCount: 1, pressCount: 0, releaseCount: 0, rightPressCount: 1, @@ -574,7 +574,7 @@ //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(); simulateEnter(0, 0); simulateDown(0, 0); @@ -583,9 +583,9 @@ simulateMove(-1, -1, 100); simulateLeave(-1, -1); 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, - exitCount: 1, + leaveCount: 1, pressCount: 1, releaseCount: 1, rightPressCount: 0, @@ -604,7 +604,7 @@ 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(); simulateEnter(0, 0); simulateDown(0, 0); @@ -614,9 +614,9 @@ simulateMove(-1, -1, 5); simulateUp(-5, -5); 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, - exitCount: 1, + leaveCount: 1, pressCount: 1, releaseCount: 1, rightPressCount: 0, @@ -635,7 +635,7 @@ 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(); //simulateEnter(0, 0); //simulateDown(0, 0); @@ -644,9 +644,9 @@ //simulateLeaveFrame(-1, -1); //// you don't actually receive the mouseup if you mouseup outside of the document //assessGestureExpectations({ - // description: 'enter-press-move-exit-move-release-outside (drag, release outside iframe): ', - // enterCount: 1, - // exitCount: 1, + // description: 'enter-press-move-leave-move-release-outside (drag, release outside iframe): ', + // enterCount: 1, + // leaveCount: 1, // pressCount: 1, // releaseCount: 1, // rightPressCount: 0, @@ -953,7 +953,7 @@ dragEndHandler: onMouseTrackerDragEnd, releaseHandler: onMouseTrackerRelease, clickHandler: onMouseTrackerClick, - exitHandler: onMouseTrackerExit + leaveHandler: onMouseTrackerLeave } ); var event = { @@ -1050,7 +1050,7 @@ checkOriginalEventReceived( event ); }; - var onMouseTrackerExit = function ( event ) { + var onMouseTrackerLeave = function ( event ) { checkOriginalEventReceived( event ); mouseTracker.destroy(); diff --git a/test/modules/navigator.js b/test/modules/navigator.js index e7f5f327..d1f98dea 100644 --- a/test/modules/navigator.js +++ b/test/modules/navigator.js @@ -216,7 +216,7 @@ clientY: offset.top + locationY }; $canvas - .simulate(OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event) + .simulate('mouseenter', event) .simulate('mousedown', event) .simulate('mouseup', event); };