diff --git a/changelog.txt b/changelog.txt index 19434936..79025147 100644 --- a/changelog.txt +++ b/changelog.txt @@ -20,6 +20,8 @@ OPENSEADRAGON CHANGELOG * MouseTracker: IE 10 - MSPointerEnter/MSPointerLeave didn't exist then, simulated with MSPointerOver/MSPointerOut * MouseTracker: Simulate mouseover/mouseout on IE < 9 * Added pointerType property to Viewer container-enter, container-exit, canvas-drag, canvas-drag-end, canvas-pinch events +* MouseTracker: Fire dragEndHandler event even if release point same as initial contact point (#1459) +* MouseTracker: Pointer capture implemented with capture APIs where available. Only fallback to emulated capture on extremely old browsers 2.4.2: diff --git a/src/mousetracker.js b/src/mousetracker.js index af42e9e3..cc2c3cd5 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -216,6 +216,7 @@ DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); }, MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); }, + losecapture: function ( event ) { onLoseCapture( _this, event ); }, mouseenter: function ( event ) { onMouseEnter( _this, event ); }, mouseleave: function ( event ) { onMouseLeave( _this, event ); }, mouseover: function ( event ) { onMouseOver( _this, event ); }, // IE9+ only @@ -236,6 +237,10 @@ gesturestart: function ( event ) { onGestureStart( _this, event ); }, gesturechange: function ( event ) { onGestureChange( _this, event ); }, + gotpointercapture: function ( event ) { onGotPointerCapture( _this, event ); }, + MSGotPointerCapture: function ( event ) { onGotPointerCapture( _this, event ); }, + lostpointercapture: function ( event ) { onLostPointerCapture( _this, event ); }, + MSLostPointerCapture: function ( event ) { onLostPointerCapture( _this, event ); }, pointerenter: function ( event ) { onPointerEnter( _this, event ); }, pointerleave: function ( event ) { onPointerLeave( _this, event ); }, pointerover: function ( event ) { onPointerOver( _this, event ); }, @@ -989,20 +994,50 @@ }; /** - * Resets all active mousetrakers. (Added to patch issue #697 "Mouse up outside map will cause "canvas-drag" event to stick") - * + * True if inside an iframe, otherwise false. + * @member {Boolean} isInIframe * @private - * @member resetAllMouseTrackers - * @memberof OpenSeadragon.MouseTracker + * @inner */ - $.MouseTracker.resetAllMouseTrackers = function(){ - for(var i = 0; i < MOUSETRACKERS.length; i++){ - if (MOUSETRACKERS[i].isTracking()){ - MOUSETRACKERS[i].setTracking(false); - MOUSETRACKERS[i].setTracking(true); - } + var isInIframe = (function() { + try { + return window.self !== window.top; + } catch (e) { + return true; } - }; + })(); + + /** + * @function + * @private + * @inner + * @returns {Boolean} True if the target supports DOM Level 2 event subscription methods, otherwise false. + */ + function canAccessEvents (target) { + try { + return target.addEventListener && target.removeEventListener; + } catch (e) { + return false; + } + } + + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // /** + // * Resets all active mousetrakers. (Added to patch issue #697 "Mouse up outside map will cause "canvas-drag" event to stick") + // * + // * @private + // * @member resetAllMouseTrackers + // * @memberof OpenSeadragon.MouseTracker + // */ + // $.MouseTracker.resetAllMouseTrackers = function(){ + // for(var i = 0; i < MOUSETRACKERS.length; i++){ + // if (MOUSETRACKERS[i].isTracking()){ + // MOUSETRACKERS[i].setTracking(false); + // MOUSETRACKERS[i].setTracking(true); + // } + // } + // }; /** * Provides continuous computation of velocity (speed and direction) of active pointers. @@ -1130,6 +1165,9 @@ var divElement = document.createElement( 'div' ); return $.isFunction( divElement.setPointerCapture ) && $.isFunction( divElement.releasePointerCapture ); }()); + if ( $.MouseTracker.havePointerCapture ) { + $.MouseTracker.subscribeEvents.push( "gotpointercapture", "lostpointercapture" ); + } } else if ( window.MSPointerEvent && window.navigator.msPointerEnabled ) { // IE10 (MSPointerEnter/MSPointerLeave simulated with MSPointerOver/MSPointerOut) $.MouseTracker.havePointerEvents = true; @@ -1141,9 +1179,13 @@ var divElement = document.createElement( 'div' ); return $.isFunction( divElement.msSetPointerCapture ) && $.isFunction( divElement.msReleasePointerCapture ); }()); + if ( $.MouseTracker.havePointerCapture ) { + $.MouseTracker.subscribeEvents.push( "MSGotPointerCapture", "MSLostPointerCapture" ); + } } else { // Legacy W3C mouse events $.MouseTracker.havePointerEvents = false; + $.MouseTracker.unprefixedPointerEvents = true; $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); if ( $.Browser.vendor !== $.BROWSERS.IE || $.Browser.version > 8 ) { $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" ); @@ -1153,11 +1195,14 @@ } $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" ); $.MouseTracker.mousePointerId = "legacy-mouse"; - // Legacy mouse events capture support + // 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" ); + } // Legacy touch events if ( 'ontouchstart' in window ) { // iOS, Android, and other W3c Touch Event implementations @@ -1522,42 +1567,51 @@ * @private * @inner */ - function capturePointer( tracker, pointerType, pointerCount ) { - var pointsList = tracker.getActivePointersListByType( pointerType ), - eventParams; + function capturePointer( tracker, gPoint ) { + var eventParams; - pointsList.captureCount += (pointerCount || 1); - - if ( pointsList.captureCount === 1 ) { - if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { - tracker.element.setCapture( true ); - } else { - eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType ); - // We emulate mouse capture by hanging listeners on the document object. - // (Note we listen on the capture phase so the captured handlers will get called first) - // eslint-disable-next-line no-use-before-define - if (isInIframe && canAccessEvents(window.top)) { - $.addEvent( - window.top, - eventParams.upName, - eventParams.upHandler, - true - ); + if ( $.MouseTracker.havePointerCapture ) { + if ( $.MouseTracker.havePointerEvents ) { + if ( $.MouseTracker.unprefixedPointerEvents ) { + tracker.element.setPointerCapture( gPoint.id ); + $.console.log('element.setPointerCapture() called'); + } else { + tracker.element.msSetPointerCapture( gPoint.id ); + $.console.log('element.msSetPointerCapture() called'); } + } else { + tracker.element.setCapture( true ); + $.console.log('element.setCapture() called'); + } + } else { + // Emulate mouse capture by hanging listeners on the document object. + // (Note we listen on the capture phase so the captured handlers will get called first) + // eslint-disable-next-line no-use-before-define + $.console.log('Emulated mouse capture set'); + eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : gPoint.type ); + if (isInIframe && canAccessEvents(window.top)) { $.addEvent( - $.MouseTracker.captureElement, + window.top, eventParams.upName, eventParams.upHandler, true ); - $.addEvent( - $.MouseTracker.captureElement, - eventParams.moveName, - eventParams.moveHandler, - true - ); } + $.addEvent( + $.MouseTracker.captureElement, + eventParams.upName, + eventParams.upHandler, + true + ); + $.addEvent( + $.MouseTracker.captureElement, + eventParams.moveName, + eventParams.moveHandler, + true + ); } + + updatePointerCaptured( tracker, gPoint, true ); } @@ -1566,42 +1620,50 @@ * @private * @inner */ - function releasePointer( tracker, pointerType, pointerCount ) { - var pointsList = tracker.getActivePointersListByType( pointerType ), - eventParams; + function releasePointer( tracker, gPoint ) { + var eventParams; - pointsList.captureCount -= (pointerCount || 1); - - if ( pointsList.captureCount === 0 ) { - if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { - tracker.element.releaseCapture(); - } else { - eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType ); - // We emulate mouse capture by hanging listeners on the document object. - // (Note we listen on the capture phase so the captured handlers will get called first) - // eslint-disable-next-line no-use-before-define - if (isInIframe && canAccessEvents(window.top)) { - $.removeEvent( - window.top, - eventParams.upName, - eventParams.upHandler, - true - ); + if ( $.MouseTracker.havePointerCapture ) { + if ( $.MouseTracker.havePointerEvents ) { + if ( $.MouseTracker.unprefixedPointerEvents ) { + tracker.element.releasePointerCapture( gPoint.id ); + $.console.log('element.releasePointerCapture() called'); + } else { + tracker.element.msReleasePointerCapture( gPoint.id ); + $.console.log('element.msReleasePointerCapture() called'); } + } else { + tracker.element.releaseCapture(); + $.console.log('element.releaseCapture() called'); + } + } else { + // Emulate mouse capture by hanging listeners on the document object. + // (Note we listen on the capture phase so the captured handlers will get called first) + $.console.log('Emulated mouse capture release'); + eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : gPoint.type ); + if (isInIframe && canAccessEvents(window.top)) { $.removeEvent( - $.MouseTracker.captureElement, - eventParams.moveName, - eventParams.moveHandler, - true - ); - $.removeEvent( - $.MouseTracker.captureElement, + window.top, eventParams.upName, eventParams.upHandler, true ); } + $.removeEvent( + $.MouseTracker.captureElement, + eventParams.moveName, + eventParams.moveHandler, + true + ); + $.removeEvent( + $.MouseTracker.captureElement, + eventParams.upName, + eventParams.upHandler, + true + ); } + + updatePointerCaptured( tracker, gPoint, false ); } @@ -1612,9 +1674,12 @@ * @inner */ function getPointerType( event ) { + // Note: IE pointer events bug - sends invalid pointerType on lostpointercapture events + // and possibly other events. We rely on sane, valid property values in DOM events, so for + // IE, when the pointerType is missing, we'll default to 'mouse'...should be right most of the time var pointerTypeStr; if ( $.MouseTracker.unprefixedPointerEvents ) { - pointerTypeStr = event.pointerType; + pointerTypeStr = event.pointerType || (( $.Browser.vendor === $.BROWSERS.IE ) ? 'mouse' : ''); } else { // IE10 // MSPOINTER_TYPE_TOUCH: 0x00000002 @@ -1632,7 +1697,7 @@ pointerTypeStr = 'mouse'; break; default: - pointerTypeStr = ''; + pointerTypeStr = 'mouse'; } } return pointerTypeStr; @@ -1934,6 +1999,24 @@ } + /** + * TODO Never actually seen this event fired, documentation is tough to find + * @private + * @inner + */ + function onLoseCapture( tracker, event ) { + $.console.log('losecapture ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + event = $.getEvent( event ); + + var gPoint = { + id: $.MouseTracker.mousePointerId, + type: 'mouse' + }; + + updatePointerCaptured( tracker, gPoint, false ); + } + + /** * @private * @inner @@ -2072,6 +2155,8 @@ event = $.getEvent( event ); + //$.console.log('onMouseDown ' + (tracker.userData ? tracker.userData.toString() : '')); + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', @@ -2080,14 +2165,16 @@ currentTime: $.now() }; - if ( updatePointersDown( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) { - $.stopEvent( event ); - capturePointer( tracker, 'mouse' ); - } + $.cancelEvent( event ); + $.stopEvent( event ); + // if ( updatePointersDown( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) { + // $.stopEvent( event ); + capturePointer( tracker, gPoint ); + // } - if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) { - $.cancelEvent( event ); - } + // if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) { + // $.cancelEvent( event ); + // } } @@ -2121,6 +2208,8 @@ event = $.getEvent( event ); + //$.console.log('onMouseUp ' + (tracker.userData ? tracker.userData.toString() : '')); + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', @@ -2129,9 +2218,11 @@ currentTime: $.now() }; - if ( updatePointersUp( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) { - releasePointer( tracker, 'mouse' ); - } + $.cancelEvent( event ); + $.stopEvent( event ); + // if ( updatePointersUp( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) { + releasePointer( tracker, gPoint ); + // } } @@ -2140,7 +2231,9 @@ * @inner */ function onMouseMove( tracker, event ) { - handleMouseMove( tracker, event ); + // handleMouseMove( tracker, event ); + $.cancelEvent( event ); + $.stopEvent( event ); } @@ -2178,32 +2271,34 @@ } - /** - * @private - * @inner - */ - function abortContacts( tracker, event, pointsList ) { - var i, - gPointCount = pointsList.getLength(), - abortGPoints = []; + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // /** + // * @private + // * @inner + // */ + // function abortContacts( tracker, event, pointsList ) { + // var i, + // gPointCount = pointsList.getLength(), + // abortGPoints = []; - // Check contact count for hoverable pointer types before aborting - if (pointsList.type === 'touch' || pointsList.contacts > 0) { - for ( i = 0; i < gPointCount; i++ ) { - abortGPoints.push( pointsList.getByIndex( i ) ); - } + // // Check contact count for hoverable pointer types before aborting + // if (pointsList.type === 'touch' || pointsList.contacts > 0) { + // for ( i = 0; i < gPointCount; i++ ) { + // abortGPoints.push( pointsList.getByIndex( i ) ); + // } - if ( abortGPoints.length > 0 ) { - // simulate touchend/mouseup - updatePointersUp( tracker, event, abortGPoints, 0 ); // 0 means primary button press/release or touch contact - // release pointer capture - pointsList.captureCount = 1; - releasePointer( tracker, pointsList.type ); - // simulate touchleave/mouseout - updatePointersLeave( tracker, event, abortGPoints ); - } - } - } + // if ( abortGPoints.length > 0 ) { + // // simulate touchend/mouseup + // updatePointersUp( tracker, event, abortGPoints, 0 ); // 0 means primary button press/release or touch contact + // // release pointer capture + // pointsList.captureCount = 1; + // //releasePointer( tracker, pointsList.type ); + // // simulate touchleave/mouseout + // updatePointersLeave( tracker, event, abortGPoints ); + // } + // } + // } /** @@ -2221,9 +2316,16 @@ time = $.now(); + //$.console.log('onTouchStart ' + (tracker.userData ? tracker.userData.toString() : '')); + + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // if ( pointsList.getLength() > event.touches.length - touchCount ) { + // $.console.warn('Tracked touch contact count doesn\'t match event.touches.length. Removing all tracked touch pointers.'); + // abortContacts( tracker, event, pointsList ); + // } if ( pointsList.getLength() > event.touches.length - touchCount ) { - $.console.warn('Tracked touch contact count doesn\'t match event.touches.length. Removing all tracked touch pointers.'); - abortContacts( tracker, event, pointsList ); + $.console.warn('Tracked touch contact count doesn\'t match event.touches.length'); } for ( i = 0; i < touchCount; i++ ) { @@ -2256,12 +2358,17 @@ } } - if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact - $.stopEvent( event ); - capturePointer( tracker, 'touch', touchCount ); + $.cancelEvent( event ); + $.stopEvent( event ); + // if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact + // $.stopEvent( event ); + // } + + for ( i = 0; i < touchCount; i++ ) { + updatePointerCaptured( tracker, gPoints[ i ], true ); } - $.cancelEvent( event ); + // $.cancelEvent( event ); } @@ -2301,6 +2408,8 @@ time = $.now(); + //$.console.log('onTouchEnd ' + (tracker.userData ? tracker.userData.toString() : '')); + for ( i = 0; i < touchCount; i++ ) { gPoints.push( { id: event.changedTouches[ i ].identifier, @@ -2311,8 +2420,13 @@ } ); } - if ( updatePointersUp( tracker, event, gPoints, 0 ) ) { - releasePointer( tracker, 'touch', touchCount ); + $.cancelEvent( event ); + $.stopEvent( event ); + // //if ( updatePointersUp( tracker, event, gPoints, 0 ) ) {} + // updatePointersUp( tracker, event, gPoints, 0 ); + + for ( i = 0; i < touchCount; i++ ) { + updatePointerCaptured( tracker, gPoints[ i ], false ); } // simulate touchleave on our tracked element @@ -2335,7 +2449,7 @@ } } - $.cancelEvent( event ); + // $.cancelEvent( event ); } @@ -2344,7 +2458,9 @@ * @inner */ function onTouchMove( tracker, event ) { - handleTouchMove( tracker, event ); + // handleTouchMove( tracker, event ); + $.cancelEvent( event ); + $.stopEvent( event ); } @@ -2391,9 +2507,12 @@ * @inner */ function onTouchCancel( tracker, event ) { - var pointsList = tracker.getActivePointersListByType('touch'); + //var pointsList = tracker.getActivePointersListByType('touch'); - abortContacts( tracker, event, pointsList ); + $.console.log('onTouchCancel ' + (tracker.userData ? tracker.userData.toString() : '')); + + //TODO handle only the canceled touches + //abortContacts( tracker, event, pointsList ); } @@ -2419,6 +2538,44 @@ } + /** + * @private + * @inner + */ + function onGotPointerCapture( tracker, event ) { + $.console.log('gotpointercapture ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + + if ( event.target === tracker.element ) { + //$.console.log('gotpointercapture ' + (tracker.userData ? tracker.userData.toString() : '')); + ////$.cancelEvent( event ); not cancelable! + //$.stopEvent( event ); + updatePointerCaptured( tracker, { + id: event.pointerId, + type: getPointerType( event ) + }, true ); + } + } + + + /** + * @private + * @inner + */ + function onLostPointerCapture( tracker, event ) { + $.console.log('lostpointercapture ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + + if ( event.target === tracker.element ) { + //$.console.log('lostpointercapture ' + (tracker.userData ? tracker.userData.toString() : '')); + ////$.cancelEvent( event ); not cancelable! + //$.stopEvent( event ); + updatePointerCaptured( tracker, { + id: event.pointerId, + type: getPointerType( event ) + }, false ); + } + } + + /** * @private * @inner @@ -2541,6 +2698,18 @@ */ function onPointerDown( tracker, event ) { var gPoint; + // var shouldCapture; + + //$.console.log('onPointerDown ' + (tracker.userData ? tracker.userData.toString() : '')); + + // Most browsers implicitly capture touch pointer events...handle that appropriately + // Note IE10 with its prefixed PointerEvent model...no IE versions have + // element.hasPointerCapture() so no implicit pointer capture + var implicitlyCaptured = (tracker.element.hasPointerCapture && $.MouseTracker.unprefixedPointerEvents) ? + tracker.element.hasPointerCapture(event.pointerId) : false; + if (implicitlyCaptured) { + $.console.log('implicitlyCaptured ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.target === tracker.element ? 'tracker.element' : '')); + } gPoint = { id: event.pointerId, @@ -2550,14 +2719,31 @@ currentTime: $.now() }; - if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) { - $.stopEvent( event ); - capturePointer( tracker, gPoint.type ); + $.cancelEvent( event ); + $.stopEvent( event ); + if (!implicitlyCaptured) { + capturePointer( tracker, gPoint ); } - if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { - $.cancelEvent( event ); - } + // shouldCapture = updatePointersDown( tracker, event, [ gPoint ], event.button ); + + // if ( shouldCapture ) { + // $.cancelEvent( event ); + // } + + // if ( shouldCapture && !implicitlyCaptured ) { + // $.stopEvent( event ); + // capturePointer( tracker, gPoint ); + // } else if ( !shouldCapture && implicitlyCaptured ) { + // $.stopEvent( event ); + // releasePointer( tracker, gPoint ); //TODO should we do this? Investigate when implementing bubble handling + // } else if ( shouldCapture && implicitlyCaptured ) { + // $.stopEvent( event ); + // } + + // if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { + // $.cancelEvent( event ); + // } } @@ -2593,6 +2779,8 @@ function handlePointerUp( tracker, event ) { var gPoint; + //$.console.log('onPointerUp ' + (tracker.userData ? tracker.userData.toString() : '')); + gPoint = { id: event.pointerId, type: getPointerType( event ), @@ -2601,9 +2789,11 @@ currentTime: $.now() }; - if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) { - releasePointer( tracker, gPoint.type ); - } + $.cancelEvent( event ); + $.stopEvent( event ); + //if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) { + releasePointer( tracker, gPoint ); + //} } @@ -2612,7 +2802,9 @@ * @inner */ function onPointerMove( tracker, event ) { - handlePointerMove( tracker, event ); + // handlePointerMove( tracker, event ); + $.cancelEvent( event ); + $.stopEvent( event ); } @@ -2657,6 +2849,7 @@ * @inner */ function onPointerCancel( tracker, event ) { + $.console.log('pointercancel ' + (tracker.userData ? tracker.userData.toString() : '') + ' ' + (event.isPrimary ? 'isPrimary' : '')); var gPoint; gPoint = { @@ -2772,7 +2965,7 @@ curGPoint = updateGPoint; } else { // Initialize for tracking and add to the tracking list - curGPoint.captured = false; + curGPoint.captured = false; // Handled by updatePointerCaptured() curGPoint.insideElementPressed = false; curGPoint.insideElement = true; startTrackingPointer( pointsList, curGPoint ); @@ -2894,7 +3087,7 @@ if ( updateGPoint ) { curGPoint = updateGPoint; } else { - //curGPoint.captured = false; // Tracked by updatePointersEnter + //curGPoint.captured = false; // Handled by updatePointerCaptured() curGPoint.insideElementPressed = false; //curGPoint.insideElement = true; // Tracked by updatePointersEnter } @@ -3047,13 +3240,15 @@ } } - // Some pointers may steal control from another pointer without firing the appropriate release events - // e.g. Touching a screen while click-dragging with certain mice. - var otherPointsLists = tracker.getActivePointersListsExceptType(gPoints[ 0 ].type); - for (i = 0; i < otherPointsLists.length; i++) { - //If another pointer has contact, simulate the release - abortContacts(tracker, event, otherPointsLists[i]); // No-op if no active pointer - } + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // // Some pointers may steal control from another pointer without firing the appropriate release events + // // e.g. Touching a screen while click-dragging with certain mice. + // var otherPointsLists = tracker.getActivePointersListsExceptType(gPoints[ 0 ].type); + // for (i = 0; i < otherPointsLists.length; i++) { + // //If another pointer has contact, simulate the release + // abortContacts(tracker, event, otherPointsLists[i]); // No-op if no active pointer + // } // Only capture and track primary button, pen, and touch contacts if ( buttonChanged !== 0 ) { @@ -3086,7 +3281,7 @@ if ( updateGPoint ) { // Already tracking the pointer...update it - updateGPoint.captured = true; + //updateGPoint.captured = true; // Handled by updatePointerCaptured() updateGPoint.insideElementPressed = true; updateGPoint.insideElement = true; updateGPoint.contactPos = curGPoint.currentPos; @@ -3099,7 +3294,7 @@ curGPoint = updateGPoint; } else { // Initialize for tracking and add to the tracking list (no pointerover or pointermove event occurred before this) - curGPoint.captured = true; + curGPoint.captured = false; // Handled by updatePointerCaptured() curGPoint.insideElementPressed = true; curGPoint.insideElement = true; startTrackingPointer( pointsList, curGPoint ); @@ -3244,20 +3439,25 @@ } } - // A primary mouse button may have been released while the non-primary button was down - var otherPointsList = tracker.getActivePointersListByType("mouse"); - // Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223 - abortContacts(tracker, event, otherPointsList); // No-op if no active pointer + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // // A primary mouse button may have been released while the non-primary button was down + // var otherPointsList = tracker.getActivePointersListByType("mouse"); + // // Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223 + // abortContacts(tracker, event, otherPointsList); // No-op if no active pointer return false; } - // OS-specific gestures (e.g. swipe up with four fingers in iPadOS 13) - if (typeof gPoints[ 0 ].currentPos === "undefined") { - abortContacts(tracker, event, pointsList); + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // // OS-specific gestures (e.g. swipe up with four fingers in iPadOS 13) + // if (typeof gPoints[ 0 ].currentPos === "undefined") { + // $.console.log('typeof gPoints[ 0 ].currentPos === "undefined" ' + (tracker.userData ? tracker.userData.toString() : '')); + // abortContacts(tracker, event, pointsList); - return false; - } + // return false; + // } for ( i = 0; i < gPointCount; i++ ) { curGPoint = gPoints[ i ]; @@ -3265,7 +3465,7 @@ if ( updateGPoint ) { // Update the pointer, stop tracking it if not still in this element if ( updateGPoint.captured ) { - updateGPoint.captured = false; + //updateGPoint.captured = false; // Handled by updatePointerCaptured() releaseCapture = true; wasCaptured = true; } @@ -3314,7 +3514,7 @@ } // Drag End - if ( tracker.dragEndHandler && !updateGPoint.currentPos.equals( updateGPoint.contactPos ) ) { + if ( tracker.dragEndHandler ) { propagate = tracker.dragEndHandler( { eventSource: tracker, @@ -3432,6 +3632,41 @@ } + /** + * Sets or resets the captured property on the tracked pointer matching the passed gPoint's id/type + * + * @function + * @private + * @inner + * @param {OpenSeadragon.MouseTracker} tracker + * A reference to the MouseTracker instance. + * @param {Object} gPoint + * An object with id and type properties describing the pointer to update. + * @param {Boolean} isCaptured + * Value to set the captured property to. + */ + function updatePointerCaptured( tracker, gPoint, isCaptured ) { + var pointsList = tracker.getActivePointersListByType( gPoint.type ); + var updateGPoint = pointsList.getById( gPoint.id ); + + if ( updateGPoint ) { + if ( isCaptured && !updateGPoint.captured ) { + updateGPoint.captured = true; + pointsList.captureCount++; + } else if ( !isCaptured && updateGPoint.captured ) { + updateGPoint.captured = false; + pointsList.captureCount--; + if ( pointsList.captureCount < 0 ) { + pointsList.captureCount = 0; + $.console.warn('updatePointerCaptured() - pointsList.captureCount went negative'); + } + } + } else { + $.console.warn('updatePointerCaptured() called on untracked pointer'); + } + } + + /** * Call when pointer(s) change coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) * @@ -3475,7 +3710,7 @@ updateGPoint.currentTime = curGPoint.currentTime; } else { // Initialize for tracking and add to the tracking list (no pointerover or pointerdown event occurred before this) - curGPoint.captured = false; + curGPoint.captured = false; // Handled by updatePointerCaptured() curGPoint.insideElementPressed = false; curGPoint.insideElement = true; startTrackingPointer( pointsList, curGPoint ); @@ -3643,32 +3878,4 @@ } } - /** - * True if inside an iframe, otherwise false. - * @member {Boolean} isInIframe - * @private - * @inner - */ - var isInIframe = (function() { - try { - return window.self !== window.top; - } catch (e) { - return true; - } - })(); - - /** - * @function - * @private - * @inner - * @returns {Boolean} True if the target supports DOM Level 2 event subscription methods, otherwise false. - */ - function canAccessEvents (target) { - try { - return target.addEventListener && target.removeEventListener; - } catch (e) { - return false; - } - } - }(OpenSeadragon)); diff --git a/src/viewer.js b/src/viewer.js index 6b58bfa5..73a2f395 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2933,9 +2933,11 @@ function onCanvasEnter( event ) { function onCanvasLeave( event ) { - if (window.location !== window.parent.location){ - $.MouseTracker.resetAllMouseTrackers(); - } + //TODO Revisit this if there's still an issue. The PointerEvent model should have no problems + // like the issue this code attempts to fix. + // if (window.location !== window.parent.location){ + // $.MouseTracker.resetAllMouseTrackers(); + // } /** * Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.