From b8ea87d3f8444f64211d1f44458a50a6455b7624 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Tue, 1 Apr 2014 18:27:54 -0700 Subject: [PATCH] MouseTracker Fixes Fixed mouse capture, should work on IE8 without IE8-specific code. --- src/mousetracker.js | 224 ++++++++++--------------------------------- src/openseadragon.js | 6 -- 2 files changed, 48 insertions(+), 182 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index c2aa48d8..8affd458 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -36,12 +36,6 @@ // is any button currently being pressed while pointer events occur var IS_BUTTON_DOWN = false, - // is any tracker currently capturing? - //IS_CAPTURING = false, - // dictionary from hash to MouseTracker - //ACTIVE = {}, - // list of trackers interested in capture - //CAPTURING = [], // dictionary from hash to private properties THIS = {}; @@ -159,6 +153,8 @@ * Are we curruently capturing mouse events (legacy mouse events only). */ THIS[ this.hash ] = { + setCaptureCapable: !!this.element.setCapture && !!this.element.releaseCapture, + click: function ( event ) { onClick( _this, event ); }, keypress: function ( event ) { onKeyPress( _this, event ); }, focus: function ( event ) { onFocus( _this, event ); }, @@ -670,7 +666,6 @@ len = trackerPoints.length; for ( i = 0; i < len; i++ ) { if ( trackerPoints[ i ].guid === guid ) { - //gPoint = trackerPoints[ i ].gPoint; trackerPoints.splice( i, 1 ); // Only run the interval timer if theres gesture points to track len--; @@ -730,7 +725,7 @@ } } else { - // Standard mouse events + // Legacy W3C mouse events $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout", "mousedown", "mouseup", "mousemove" ); if ( 'ontouchstart' in window ) { // iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505) @@ -901,7 +896,6 @@ ); } delegate.tracking = true; - //ACTIVE[ tracker.hash ] = tracker; } } @@ -928,18 +922,9 @@ releaseMouse( tracker ); delegate.tracking = false; - //delete ACTIVE[ tracker.hash ]; } } - /** - * @private - * @inner - */ - function hasMouse( tracker ) { - return THIS[ tracker.hash ].insideElement; - } - /** * Begin capturing mouse events on this element (legacy mouse events only). * @private @@ -947,28 +932,13 @@ */ function captureMouse( tracker ) { var delegate = THIS[ tracker.hash ]; - if ( !delegate.capturing ) { - //if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - // $.removeEvent( - // tracker.element, - // "mouseup", - // delegate.mouseup, - // false - // ); - // $.addEvent( - // tracker.element, - // "mouseup", - // delegate.mouseupcapturedie, - // true - // ); - // $.addEvent( - // tracker.element, - // "mousemove", - // delegate.mousemovecapturedie, - // true - // ); - //} else { + if ( !delegate.capturing ) { + if ( delegate.setCaptureCapable ) { + // IE<10, Firefox, other browsers with setCapture()/releaseCapture() + tracker.element.setCapture( true ); + } + else { $.addEvent( window, "mouseup", @@ -981,7 +951,7 @@ delegate.mousemovecaptured, true ); - //} + } delegate.capturing = true; } } @@ -994,28 +964,13 @@ */ function releaseMouse( tracker ) { var delegate = THIS[ tracker.hash ]; - if ( delegate.capturing ) { - //if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - // $.removeEvent( - // tracker.element, - // "mousemove", - // delegate.mousemovecapturedie, - // true - // ); - // $.removeEvent( - // tracker.element, - // "mouseup", - // delegate.mouseupcapturedie, - // true - // ); - // $.addEvent( - // tracker.element, - // "mouseup", - // delegate.mouseup, - // false - // ); - //} else { + if ( delegate.capturing ) { + if ( delegate.setCaptureCapable ) { + // IE<10, Firefox, other browsers with setCapture()/releaseCapture() + tracker.element.releaseCapture(); + } + else { $.removeEvent( window, "mousemove", @@ -1028,26 +983,12 @@ delegate.mouseupcaptured, true ); - //} + } delegate.capturing = false; } } - /** - * @private - * @inner - */ - //function triggerOthers( tracker, handler, event, isTouch ) { - // var otherHash; - // for ( otherHash in ACTIVE ) { - // if ( ACTIVE.hasOwnProperty( otherHash ) && tracker.hash != otherHash ) { - // handler( ACTIVE[ otherHash ], event, isTouch ); - // } - // } - //} - - /** * @private * @inner @@ -1326,6 +1267,10 @@ return; } + if ( tracker.pressHandler || tracker.clickHandler || tracker.dragHandler ) { + captureMouse( tracker ); + } + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', @@ -1336,20 +1281,9 @@ addPointers( tracker, event, [ gPoint ] ); - if ( tracker.pressHandler || tracker.dragHandler ) { + if ( tracker.pressHandler || tracker.clickHandler || tracker.dragHandler ) { $.cancelEvent( event ); } - - //if ( !( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) || - // !IS_CAPTURING ) { - captureMouse( tracker ); - //IS_CAPTURING = true; - //// reset to empty & add us - //CAPTURING = [ tracker ]; - //} else if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - // // add us to the list - // CAPTURING.push( tracker ); - //} } @@ -1358,82 +1292,53 @@ * @inner */ function onMouseUp( tracker, event ) { - handleMouseUp( tracker, event ); + var delegate = THIS[ tracker.hash ]; + + if ( !delegate.capturing || delegate.setCaptureCapable ) { + handleMouseUp( tracker, event ); + } + + if ( delegate.capturing ) { + releaseMouse( tracker ); + } } - /** - * Only triggered in W3C browsers by elements within which the mouse was - * initially pressed, since they are now listening to the window for - * mouseup during the capture phase. We shouldn't handle the mouseup - * here if the mouse is still inside this element, since the regular - * mouseup handler will still fire. + * This handler is attached to the window object to emulate mouse capture. + * Only triggered in older W3C browsers that don't have setCapture/releaseCapture + * methods or don't support the new pointer events model. + * onMouseUp is still called on the tracked element, so avoid processing twice. * @private * @inner */ function onMouseUpCaptured( tracker, event ) { - var delegate = THIS[ tracker.hash ], - gPoint = delegate.mousePoints.getById( $.MouseTracker.mousePointerId ); + var delegate = THIS[ tracker.hash ]; - if ( gPoint && !gPoint.insideElement ) { - handleMouseUp( tracker, event ); + handleMouseUp( tracker, event ); + + if ( delegate.capturing ) { + releaseMouse( tracker ); } - - releaseMouse( tracker ); } - /** - * Only triggered once by the deepest element that initially received - * the mouse down event. We want to make sure THIS event doesn't bubble. - * Instead, we want to trigger the elements that initially received the - * mouse down event (including this one) only if the mouse is no longer - * inside them. Then, we want to release capture, and emulate a regular - * mouseup on the event that this event was meant for. - * @private - * @inner - */ - //function onMouseUpCapturedIE( tracker, event ) { - // var othertracker, - // i; - - // event = $.getEvent( event ); - - // if ( event.button == 2 ) { - // return; - // } - - // for ( i = 0; i < CAPTURING.length; i++ ) { - // othertracker = CAPTURING[ i ]; - // if ( !hasMouse( othertracker ) ) { - // handleMouseUp( othertracker, event ); - // } - // } - - // releaseMouse( tracker ); - // IS_CAPTURING = false; - // event.srcElement.fireEvent( - // "on" + event.type, - // document.createEventObject( event ) - // ); - - // $.stopEvent( event ); - //} - - /** * @private * @inner */ function onMouseMove( tracker, event ) { var delegate = THIS[ tracker.hash ]; - if ( !delegate.capturing ) { + if ( !delegate.capturing || delegate.setCaptureCapable ) { handleMouseMove( tracker, event ); } } /** + * This handler is attached to the window object to emulate mouse capture. + * Only triggered in older W3C browsers that don't have setCapture/releaseCapture + * methods or don't support the new pointer events model. + * onMouseMove is still called on the tracked element, so avoid processing twice. * @private * @inner */ @@ -1442,25 +1347,6 @@ } - /** - * Only triggered once by the deepest element that initially received - * the mouse down event. Since no other element has captured the mouse, - * we want to trigger the elements that initially received the mouse - * down event (including this one). The the param tracker isn't used - * but for consistency with the other event handlers we include it. - * @private - * @inner - */ - //function onMouseMoveCapturedIE( tracker, event ) { - // var i; - // for ( i = 0; i < CAPTURING.length; i++ ) { - // handleMouseMove( CAPTURING[ i ], event ); - // } - // - // $.stopEvent( event ); - //} - - /** * @private * @inner @@ -1961,7 +1847,7 @@ for ( i = 0; i < gPointCount; i++ ) { curGPoint = gPoints[ i ]; - // Initialize for drag/swipe/pinch + // Initialize for gesture tracking curGPoint.insideElementPressed = true; curGPoint.insideElement = true; curGPoint.speed = 0; @@ -2408,22 +2294,8 @@ } - ///** - // * @private - // * @inner - // * Returns true if elementB is a child node of elementA, or if they're equal. - // */ - //function isChild( elementA, elementB ) { - // var body = document.body; - // while ( elementB && elementA != elementB && body != elementB ) { - // try { - // elementB = elementB.parentNode; - // } catch ( e ) { - // return false; - // } - // } - // return elementA == elementB; - //} + // TODO Do we really need these anymore (used as buttonDownAny in enterHandler/exitHandler callbacks)? + // Surely there's a more robust and elegant solution... /** * @private diff --git a/src/openseadragon.js b/src/openseadragon.js index a2bcbafa..d8c3d973 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -1515,9 +1515,6 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ return function ( element, eventName, handler, useCapture ) { element = $.getElement( element ); element.attachEvent( 'on' + eventName, handler ); - if ( useCapture && element.setCapture ) { - element.setCapture(); - } }; } else { throw new Error( "No known event model." ); @@ -1544,9 +1541,6 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ return function( element, eventName, handler, useCapture ) { element = $.getElement( element ); element.detachEvent( 'on' + eventName, handler ); - if ( useCapture && element.releaseCapture ) { - element.releaseCapture(); - } }; } else { throw new Error( "No known event model." );