diff --git a/changelog.txt b/changelog.txt index 5e058c2c..7cd7b667 100644 --- a/changelog.txt +++ b/changelog.txt @@ -50,6 +50,7 @@ OPENSEADRAGON CHANGELOG * Viewport.setRotation now allows all rotation angles (#466) * Pinch rotate is now available (defaults to off) (#468) * Added option for home button to fill viewer (#474) +* Now handling iframe/frame mouseouts properly (#481) * Better handling of mid-update image loaded callbacks (#409) 1.1.1: diff --git a/src/mousetracker.js b/src/mousetracker.js index 7a4df604..0c865c74 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -193,6 +193,8 @@ mousemove: function ( event ) { onMouseMove( _this, event ); }, mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); }, + mouseoutdocument: function ( event ) { onMouseOutDocument( _this, event ); }, + touchenter: function ( event ) { onTouchEnter( _this, event ); }, touchleave: function ( event ) { onTouchLeave( _this, event ); }, touchstart: function ( event ) { onTouchStart( _this, event ); }, @@ -207,6 +209,10 @@ MSPointerOver: function ( event ) { onPointerOver( _this, event ); }, pointerout: function ( event ) { onPointerOut( _this, event ); }, MSPointerOut: function ( event ) { onPointerOut( _this, event ); }, + + pointeroutdocument: function ( event ) { onPointerOutDocument( _this, event ); }, + MSPointerOutdocument: function ( event ) { onPointerOutDocument( _this, event ); }, + pointerdown: function ( event ) { onPointerDown( _this, event ); }, MSPointerDown: function ( event ) { onPointerDown( _this, event ); }, pointerup: function ( event ) { onPointerUp( _this, event ); }, @@ -1049,6 +1055,16 @@ false ); } + + // handle pointer/mouse out of document body + if ( window.PointerEvent ) { + $.addEvent(document.body, "pointerout", delegate.pointeroutdocument); + } else if ( window.MSPointerEvent ) { + $.addEvent(document.body, "pointerout", delegate.MSPointerOutdocument); + } else { + $.addEvent(document.body, "mouseout", delegate.mouseoutdocument); + } + delegate.tracking = true; } } @@ -1074,6 +1090,15 @@ ); } + // handle pointer/mouse out of document body + if ( window.PointerEvent ) { + $.removeEvent(document.body, "pointerout", delegate.pointeroutdocument); + } else if ( window.MSPointerEvent ) { + $.removeEvent(document.body, "MSPointerOut", delegate.MSPointerOutdocument); + } else { + $.removeEvent(document.body, "mouseout", delegate.mouseoutdocument); + } + delegate.tracking = false; } } @@ -1456,6 +1481,35 @@ updatePointersExit( tracker, event, [ gPoint ] ); } + /** + * This handler is used to handle the case where the mouse is dragged out of the window, it should cause the drag to be properly released. + * + * @private + * @inner + */ + function onMouseOutDocument( tracker, event ) { + event = $.getEvent( event ); + + var html = document.getElementsByTagName("html")[0]; + var target = event.target || event.srcElement; + if ((event.relatedTarget!==html && event.relatedTarget!==null) || event.currentTarget !== document.body) { + return; // not a mouseout of the iframe + } + + var gPoint = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + isPrimary: true, + currentPos: getMouseAbsolute( event ), + currentTime: $.now() + }; + + event.buttons = undefined; + + if ( updatePointersUp( tracker, event, [ gPoint ], 0 ) ) { + releasePointer( tracker, true ); + } + } /** * @private @@ -1528,7 +1582,6 @@ } } - /** * @private * @inner @@ -1800,6 +1853,32 @@ updatePointersExit( tracker, event, [ gPoint ] ); } + /** + * This handler is used to handle the case where the pointer is dragged out of the window, it should cause the drag to be properly released. + * + * @private + * @inner + */ + function onPointerOutDocument( tracker, event ) { + event = $.getEvent( event ); + + var html = document.getElementsByTagName("html")[0]; + if ((event.relatedTarget!==html && event.relatedTarget!==null) || event.currentTarget !== document.body) { + return; // not a mouseout of the iframe + } + + var gPoint = { + id: event.pointerId, + type: getPointerType( event ), + isPrimary: event.isPrimary, + currentPos: getMouseAbsolute( event ), + currentTime: $.now() + }; + + if ( updatePointersUp( tracker, event, [ gPoint ], 0 ) ) { + releasePointer( tracker, false ); + } + } /** * @private diff --git a/test/events.js b/test/events.js index 36a49b17..ba1a36e2 100644 --- a/test/events.js +++ b/test/events.js @@ -165,6 +165,13 @@ 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 ); + }; + 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 ); }; @@ -446,6 +453,32 @@ quickClick: false }); + + // enter-press-move-exit-move-release-outside (drag, release outside iframe) + resetForAssessment(); + simulateEnter(0, 0); + simulateDown(0, 0); + simulateMove(1, 1, 5); + simulateMove(-1, -1, 5); + 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, + pressCount: 1, + releaseCount: 1, + moveCount: 10, + clickCount: 0, + dblClickCount: 0, + dragCount: 10, + dragEndCount: 1, + insideElementPressed: true, + insideElementReleased: false, + contacts: 0, + trackedPointers: 0, + quickClick: false + }); unhookViewerHandlers(); viewer.close(); @@ -590,7 +623,7 @@ var checkOriginalEventReceivedViewer = function ( event ) { eventsHandledViewer++; - //TODO Provide a better check for the original event...simulate doesn't currently extend the object + //TODO Provide a better check for the original event...simulate doesn't currently extend the object // with arbitrary user data. if ( event && event.originalEvent ) { originalEventsPassedViewer++; @@ -622,7 +655,7 @@ if ( event && event.eventSource === mouseTracker ) { eventSourcePassedMouseTracker++; } - //TODO Provide a better check for the original event...simulate doesn't currently extend the object + //TODO Provide a better check for the original event...simulate doesn't currently extend the object // with arbitrary user data. if ( event && event.originalEvent ) { originalEventsPassedMouseTracker++;