From c7ea247baa9fa889edf175779a3562ffa4c651ae Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Mon, 10 Mar 2014 11:49:51 -0700 Subject: [PATCH 01/17] Initial commit - Multi-touch, pointer event MouseTracker --- src/mousetracker.js | 2328 +++++++++++++++++++++++++++++++----------- src/openseadragon.js | 15 + src/viewer.js | 50 +- 3 files changed, 1766 insertions(+), 627 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index 86238d8c..f70bef3f 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -84,7 +84,11 @@ * @param {OpenSeadragon.EventHandler} [options.clickHandler=null] * An optional handler for mouse click. * @param {OpenSeadragon.EventHandler} [options.dragHandler=null] - * An optional handler for mouse drag. + * An optional handler for the drag gesture. + * @param {OpenSeadragon.EventHandler} [options.pinchHandler=null] + * An optional handler for the pinch gesture. + * @param {OpenSeadragon.EventHandler} [options.swipeHandler=null] + * An optional handler for the swipe gesture. * @param {OpenSeadragon.EventHandler} [options.keyHandler=null] * An optional handler for keypress. * @param {OpenSeadragon.EventHandler} [options.focusHandler=null] @@ -136,6 +140,8 @@ this.scrollHandler = options.scrollHandler || null; this.clickHandler = options.clickHandler || null; this.dragHandler = options.dragHandler || null; + this.pinchHandler = options.pinchHandler || null; + this.swipeHandler = options.swipeHandler || null; this.stopHandler = options.stopHandler || null; this.keyHandler = options.keyHandler || null; this.focusHandler = options.focusHandler || null; @@ -163,34 +169,57 @@ * Position of last mouse down */ THIS[ this.hash ] = { - mouseover: function ( event ) { onMouseOver( _this, event, false ); }, - mouseout: function ( event ) { onMouseOut( _this, event, false ); }, - mousedown: function ( event ) { onMouseDown( _this, event ); }, - mouseup: function ( event ) { onMouseUp( _this, event, false ); }, - mousemove: function ( event ) { onMouseMove( _this, event ); }, - click: function ( event ) { onMouseClick( _this, event ); }, + click: function ( event ) { onClick( _this, event ); }, + keypress: function ( event ) { onKeyPress( _this, event ); }, + focus: function ( event ) { onFocus( _this, event ); }, + blur: function ( event ) { onBlur( _this, event ); }, wheel: function ( event ) { onWheel( _this, event ); }, mousewheel: function ( event ) { onMouseWheel( _this, event ); }, DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); }, MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); }, - mouseupie: function ( event ) { onMouseUpIE( _this, event ); }, + pointerover: function ( event ) { onPointerOver( _this, event ); }, + MSPointerOver: function ( event ) { onPointerOver( _this, event ); }, + mouseover: function ( event ) { onMouseOver( _this, event ); }, + pointerout: function ( event ) { onPointerOut( _this, event ); }, + MSPointerOut: function ( event ) { onPointerOut( _this, event ); }, + mouseout: function ( event ) { onMouseOut( _this, event ); }, + pointerdown: function ( event ) { onPointerDown( _this, event ); }, + MSPointerDown: function ( event ) { onPointerDown( _this, event ); }, + mousedown: function ( event ) { onMouseDown( _this, event ); }, + pointerup: function ( event ) { onPointerUp( _this, event ); }, + MSPointerUp: function ( event ) { onPointerUp( _this, event ); }, + mouseup: function ( event ) { onMouseUp( _this, event ); }, + pointermove: function ( event ) { onPointerMove( _this, event ); }, + MSPointerMove: function ( event ) { onPointerMove( _this, event ); }, + mousemove: function ( event ) { onMouseMove( _this, event ); }, + pointercancel: function ( event ) { onPointerCancel( _this, event ); }, + MSPointerCancel: function ( event ) { onPointerCancel( _this, event ); }, + mouseupcapturedie: function ( event ) { onMouseUpCapturedIE( _this, event ); }, mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, - mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event, false ); }, + mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); }, touchstart: function ( event ) { onTouchStart( _this, event ); }, touchmove: function ( event ) { onTouchMove( _this, event ); }, touchend: function ( event ) { onTouchEnd( _this, event ); }, - keypress: function ( event ) { onKeyPress( _this, event ); }, - focus: function ( event ) { onFocus( _this, event ); }, - blur: function ( event ) { onBlur( _this, event ); }, tracking: false, capturing: false, - insideElementPressed: false, - insideElement: false, - lastPoint: null, - lastMouseDownTime: null, - lastMouseDownPoint: null, - lastPinchDelta: 0 + // Contact Points + mousePoints: {}, + mousePointCount: 0, + touchPoints: {}, + touchPointCount: 0, + penPoints: {}, + penPointCount: 0, + // Tracking for pinch gesture + lastPinchDist: 0, + currentPinchDist: 0, + + //insideElementPressed: false, + //insideElement: false, + //lastPoint: null, + //lastMouseDownTime: null, + //lastMouseDownPoint: null, + //lastPinchDelta: 0, }; }; @@ -247,7 +276,7 @@ * @param {Boolean} event.buttonDownAny * Was the button down anywhere in the screen during the event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -272,7 +301,7 @@ * @param {Boolean} event.buttonDownAny * Was the button down anywhere in the screen during the event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -292,7 +321,7 @@ * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -317,7 +346,7 @@ * @param {Boolean} event.insideElementReleased * True if the cursor still inside the tracked element when the button was released. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -337,7 +366,7 @@ * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -361,7 +390,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -385,7 +414,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -409,7 +438,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -428,8 +457,56 @@ * A reference to the tracker instance. * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. + * @param {OpenSeadragon.Point} event.delta + * The x,y components of the difference between start drag and end drag. Usefule for ignoring or weighting the events. + * @param {Boolean} event.shift + * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. + * True if the original event is a touch event, otherwise false. Deprecated. Examine 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. + */ + pinchHandler: 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 {OpenSeadragon.Point} event.position + * The position of the event relative to the tracked element. + * @param {OpenSeadragon.Point} event.delta + * The x,y components of the difference between start drag and end drag. Usefule for ignoring or weighting the events. + * @param {Boolean} event.shift + * True if the shift key was pressed during this event. + * @param {Boolean} event.isTouchEvent + * True if the original event is a touch event, otherwise false. Deprecated. Examine 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. + */ + swipeHandler: 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 {OpenSeadragon.Point} event.position + * The position of the event relative to the tracked element. + * @param {Boolean} event.isTouchEvent + * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -492,40 +569,75 @@ blurHandler: function () { } }; + /** - * Detect available mouse wheel event. + * Detect available mouse wheel event name. */ $.MouseTracker.wheelEventName = ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version > 8 ) || ( 'onwheel' in document.createElement( 'div' ) ) ? 'wheel' : // Modern browsers support 'wheel' document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least 'mousewheel' 'DOMMouseScroll'; // Assume old Firefox + /** + * Detect browser pointer/touch event model and build appropriate list of events to subscribe to. + */ + $.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; + + if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { + // Older Firefox + $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" ); + } + + if ( window.PointerEvent ) { + // IE11 and other W3C Pointer Event implementations (see http://www.w3.org/TR/pointerevents) + $.MouseTracker.subscribeEvents.push( "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel" ); + $.MouseTracker.unprefixedPointerEvents = true; + if( navigator.maxTouchPoints ) { + $.MouseTracker.maxTouchPoints = navigator.maxTouchPoints; + } + else { + $.MouseTracker.maxTouchPoints = 0; + } + } + else if ( window.MSPointerEvent ) { + // IE10 + $.MouseTracker.subscribeEvents.push( "MSPointerOver", "MSPointerOut", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" ); + $.MouseTracker.unprefixedPointerEvents = false; + if( navigator.msMaxTouchPoints ) { + $.MouseTracker.maxTouchPoints = navigator.msMaxTouchPoints; + } + else { + $.MouseTracker.maxTouchPoints = 0; + } + } + else { + $.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) + $.MouseTracker.subscribeEvents.push( "touchstart", "touchmove", "touchend" ); + } + $.MouseTracker.mousePointerId = "legacy-mouse"; + $.MouseTracker.maxTouchPoints = 10; + } + + +//******************************************************************************************************************************************* +//** Utility Functions + + /** * Starts tracking mouse events on this element. * @private * @inner */ function startTracking( tracker ) { - var events = [ - "mouseover", "mouseout", "mousedown", "mouseup", "mousemove", - "click", - $.MouseTracker.wheelEventName, - "touchstart", "touchmove", "touchend", - "keypress", - "focus", "blur" - ], - delegate = THIS[ tracker.hash ], + var delegate = THIS[ tracker.hash ], event, i; - // Add 'MozMousePixelScroll' event handler for older Firefox - if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { - events.push( "MozMousePixelScroll" ); - } - if ( !delegate.tracking ) { - for ( i = 0; i < events.length; i++ ) { - event = events[ i ]; + for ( i = 0; i < $.MouseTracker.subscribeEvents.length; i++ ) { + event = $.MouseTracker.subscribeEvents[ i ]; $.addEvent( tracker.element, event, @@ -544,26 +656,13 @@ * @inner */ function stopTracking( tracker ) { - var events = [ - "mouseover", "mouseout", "mousedown", "mouseup", "mousemove", - "click", - $.MouseTracker.wheelEventName, - "touchstart", "touchmove", "touchend", - "keypress", - "focus", "blur" - ], - delegate = THIS[ tracker.hash ], + var delegate = THIS[ tracker.hash ], event, i; - // Remove 'MozMousePixelScroll' event handler for older Firefox - if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { - events.push( "MozMousePixelScroll" ); - } - if ( delegate.tracking ) { - for ( i = 0; i < events.length; i++ ) { - event = events[ i ]; + for ( i = 0; i < $.MouseTracker.subscribeEvents.length; i++ ) { + event = $.MouseTracker.subscribeEvents[ i ]; $.removeEvent( tracker.element, event, @@ -605,7 +704,7 @@ $.addEvent( tracker.element, "mouseup", - delegate.mouseupie, + delegate.mouseupcapturedie, true ); $.addEvent( @@ -652,7 +751,7 @@ $.removeEvent( tracker.element, "mouseup", - delegate.mouseupie, + delegate.mouseupcapturedie, true ); $.addEvent( @@ -684,11 +783,112 @@ * @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 ); + //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 + */ + function getPointerType( event ) { + var pointerTypeStr; + if ( $.MouseTracker.unprefixedPointerEvents ) { + pointerTypeStr = event.pointerType; + } + else { + // IE10 + // MSPOINTER_TYPE_TOUCH: 0x00000002 + // MSPOINTER_TYPE_PEN: 0x00000003 + // MSPOINTER_TYPE_MOUSE: 0x00000004 + switch( event.pointerType ) + { + case 0x00000002: + pointerTypeStr = 'touch'; + break; + case 0x00000003: + pointerTypeStr = 'pen'; + break; + case 0x00000004: + pointerTypeStr = 'mouse'; + break; + default: + pointerTypeStr = ''; + } + } + return pointerTypeStr; + } + + + /** + * @private + * @inner + */ + function getMouseAbsolute( event ) { + return $.getMousePosition( event ); + } + + /** + * @private + * @inner + */ + function getMouseRelative( event, element ) { + return getPointRelative( getMouseAbsolute( event ), element ); + } + + /** + * @private + * @inner + */ + function getPointRelative( point, element ) { + var offset = $.getElementOffset( element ); + return point.minus( offset ); + } + + +//******************************************************************************************************************************************* +//** DOM EVent Handlers + + + /** + * @private + * @inner + */ + function onClick( tracker, event ) { + if ( tracker.clickHandler ) { + $.cancelEvent( event ); + } + } + + + /** + * @private + * @inner + */ + function onKeyPress( tracker, event ) { + //console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); + var propagate; + if ( tracker.keyHandler ) { + event = $.getEvent( event ); + propagate = tracker.keyHandler( + { + eventSource: tracker, + position: getMouseRelative( event, tracker.element ), + keyCode: event.keyCode ? event.keyCode : event.charCode, + shift: event.shiftKey, + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( !propagate ) { + $.cancelEvent( event ); } } } @@ -702,6 +902,7 @@ //console.log( "focus %s", event ); var propagate; if ( tracker.focusHandler ) { + event = $.getEvent( event ); propagate = tracker.focusHandler( { eventSource: tracker, @@ -725,6 +926,7 @@ //console.log( "blur %s", event ); var propagate; if ( tracker.blurHandler ) { + event = $.getEvent( event ); propagate = tracker.blurHandler( { eventSource: tracker, @@ -740,437 +942,6 @@ } - /** - * @private - * @inner - */ - function onKeyPress( tracker, event ) { - //console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); - var propagate; - if ( tracker.keyHandler ) { - propagate = tracker.keyHandler( - { - eventSource: tracker, - position: getMouseRelative( event, tracker.element ), - keyCode: event.keyCode ? event.keyCode : event.charCode, - shift: event.shiftKey, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - if ( !propagate ) { - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseOver( tracker, event, isTouch ) { - - var delegate = THIS[ tracker.hash ], - propagate; - - isTouch = isTouch || false; - - event = $.getEvent( event ); - - if ( !isTouch ) { - if ( $.Browser.vendor == $.BROWSERS.IE && - $.Browser.version < 9 && - delegate.capturing && - !isChild( event.srcElement, tracker.element ) ) { - - triggerOthers( tracker, onMouseOver, event, isTouch ); - } - - var to = event.target ? - event.target : - event.srcElement, - from = event.relatedTarget ? - event.relatedTarget : - event.fromElement; - - if ( !isChild( tracker.element, to ) || - isChild( tracker.element, from ) ) { - return; - } - } - - delegate.insideElement = true; - - if ( tracker.enterHandler ) { - propagate = tracker.enterHandler( - { - eventSource: tracker, - position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ), - insideElementPressed: delegate.insideElementPressed, - buttonDownAny: IS_BUTTON_DOWN, - isTouchEvent: isTouch, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - if ( propagate === false ) { - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseOut( tracker, event, isTouch ) { - var delegate = THIS[ tracker.hash ], - propagate; - - isTouch = isTouch || false; - - event = $.getEvent( event ); - - if ( !isTouch ) { - if ( $.Browser.vendor == $.BROWSERS.IE && - $.Browser.version < 9 && - delegate.capturing && - !isChild( event.srcElement, tracker.element ) ) { - - triggerOthers( tracker, onMouseOut, event, isTouch ); - - } - - var from = event.target ? - event.target : - event.srcElement, - to = event.relatedTarget ? - event.relatedTarget : - event.toElement; - - if ( !isChild( tracker.element, from ) || - isChild( tracker.element, to ) ) { - return; - } - } - - delegate.insideElement = false; - - if ( tracker.exitHandler ) { - propagate = tracker.exitHandler( - { - eventSource: tracker, - position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ), - insideElementPressed: delegate.insideElementPressed, - buttonDownAny: IS_BUTTON_DOWN, - isTouchEvent: isTouch, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - - if ( propagate === false ) { - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseDown( tracker, event, noCapture, isTouch ) { - var delegate = THIS[ tracker.hash ], - propagate; - - isTouch = isTouch || false; - - event = $.getEvent(event); - - var eventOrTouchPoint = isTouch ? event.touches[ 0 ] : event; - - if ( event.button == 2 ) { - return; - } - - delegate.insideElementPressed = true; - - delegate.lastPoint = getMouseAbsolute( eventOrTouchPoint ); - delegate.lastMouseDownPoint = delegate.lastPoint; - delegate.lastMouseDownTime = $.now(); - - if ( tracker.pressHandler ) { - propagate = tracker.pressHandler( - { - eventSource: tracker, - position: getMouseRelative( eventOrTouchPoint, tracker.element ), - isTouchEvent: isTouch, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - if ( propagate === false ) { - $.cancelEvent( event ); - } - } - - if ( tracker.pressHandler || tracker.dragHandler ) { - $.cancelEvent( event ); - } - - if ( noCapture ) { - return; - } - - if ( isTouch || - !( $.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 ); - } - } - - /** - * @private - * @inner - */ - function onTouchStart( tracker, event ) { - var touchA, - touchB; - - if ( event.touches.length == 1 && - event.targetTouches.length == 1 && - event.changedTouches.length == 1 ) { - - THIS[ tracker.hash ].lastTouch = event.touches[ 0 ]; - onMouseOver( tracker, event, true ); - // call with no capture as the onMouseMoveCaptured will - // be triggered by onTouchMove - onMouseDown( tracker, event, true, true ); - } - - if ( event.touches.length == 2 ) { - - touchA = getMouseAbsolute( event.touches[ 0 ] ); - touchB = getMouseAbsolute( event.touches[ 1 ] ); - THIS[ tracker.hash ].lastPinchDelta = - Math.abs( touchA.x - touchB.x ) + - Math.abs( touchA.y - touchB.y ); - THIS[ tracker.hash ].pinchMidpoint = new $.Point( - ( touchA.x + touchB.x ) / 2, - ( touchA.y + touchB.y ) / 2 - ); - //$.console.debug("pinch start : "+THIS[ tracker.hash ].lastPinchDelta); - } - - event.preventDefault(); - } - - - /** - * @private - * @inner - */ - function onMouseUp( tracker, event, isTouch ) { - var delegate = THIS[ tracker.hash ], - //were we inside the tracked element when we were pressed - insideElementPressed = delegate.insideElementPressed, - //are we still inside the tracked element when we released - insideElementReleased = delegate.insideElement, - propagate; - - isTouch = isTouch || false; - - event = $.getEvent(event); - - if ( event.button == 2 ) { - return; - } - - delegate.insideElementPressed = false; - - if ( tracker.releaseHandler ) { - propagate = tracker.releaseHandler( - { - eventSource: tracker, - position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ), - insideElementPressed: insideElementPressed, - insideElementReleased: insideElementReleased, - isTouchEvent: isTouch, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - if ( propagate === false ) { - $.cancelEvent( event ); - } - } - - if ( insideElementPressed && insideElementReleased ) { - handleMouseClick( tracker, event, isTouch ); - } - } - - - /** - * @private - * @inner - */ - function onTouchEnd( tracker, event ) { - - if ( event.touches.length === 0 && - event.targetTouches.length === 0 && - event.changedTouches.length == 1 ) { - - THIS[ tracker.hash ].lastTouch = null; - - // call with no release, as the mouse events are - // not registered in onTouchStart - onMouseUpCaptured( tracker, event, true, true ); - onMouseOut( tracker, event, true ); - } - if ( event.touches.length + event.changedTouches.length == 2 ) { - THIS[ tracker.hash ].lastPinchDelta = null; - THIS[ tracker.hash ].pinchMidpoint = null; - //$.console.debug("pinch end"); - } - event.preventDefault(); - } - - - /** - * 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 onMouseUpIE( 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 ) ) { - onMouseUp( othertracker, event, false ); - } - } - - releaseMouse( tracker ); - IS_CAPTURING = false; - event.srcElement.fireEvent( - "on" + event.type, - document.createEventObject( event ) - ); - - $.stopEvent( event ); - } - - - /** - * 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. - * @private - * @inner - */ - function onMouseUpCaptured( tracker, event, noRelease, isTouch ) { - isTouch = isTouch || false; - - if ( !THIS[ tracker.hash ].insideElement || isTouch ) { - onMouseUp( tracker, event, isTouch ); - } - - if ( noRelease ) { - return; - } - - releaseMouse( tracker ); - } - - - /** - * @private - * @inner - */ - function onMouseMove( tracker, event ) { - if ( tracker.moveHandler ) { - event = $.getEvent( event ); - - var propagate = tracker.moveHandler( - { - eventSource: tracker, - position: getMouseRelative( event, tracker.element ), - isTouchEvent: false, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData - } - ); - if ( propagate === false ) { - $.cancelEvent( event ); - } - } - if ( tracker.stopHandler ) { - clearTimeout( tracker.stopTimeOut ); - tracker.stopTimeOut = setTimeout( function() { - onMouseStop( tracker, event ); - }, tracker.stopDelay ); - } - } - - /** - * @private - * @inner - */ - function onMouseStop( tracker, originalMoveEvent ) { - if ( tracker.stopHandler ) { - tracker.stopHandler( { - eventSource: tracker, - position: getMouseRelative( originalMoveEvent, tracker.element ), - isTouchEvent: false, - originalEvent: originalMoveEvent, - preventDefaultAction: false, - userData: tracker.userData - } ); - } - } - - /** - * @private - * @inner - */ - function onMouseClick( tracker, event ) { - if ( tracker.clickHandler ) { - $.cancelEvent( event ); - } - } - - /** * Handler for 'wheel' events * @@ -1178,7 +949,7 @@ * @inner */ function onWheel( tracker, event ) { - handleWheelEvent( tracker, event, event, false ); + handleWheelEvent( tracker, event, event ); } @@ -1189,8 +960,7 @@ * @inner */ function onMouseWheel( tracker, event ) { - // For legacy IE, access the global (window) event object - event = event || window.event; + event = $.getEvent( event ); // Simulate a 'wheel' event var simulatedEvent = { @@ -1213,23 +983,743 @@ simulatedEvent.deltaY = event.detail; } - handleWheelEvent( tracker, simulatedEvent, event, false ); + handleWheelEvent( tracker, simulatedEvent, event ); } + /** + * @private + * @inner + */ + function onPointerOver( tracker, event ) { + var time, + point, + pointer; + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + updatePointersOver( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function onMouseOver( tracker, event ) { + var time, + point, + pointer; + + event = $.getEvent(event); + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + //isCaptured: true, + //insideElementPressed: true, + insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + updatePointersOver( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function onPointerOut( tracker, event ) { + var time, + point, + pointer; + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + insideElement: false, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + updatePointersOut( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function onMouseOut( tracker, event ) { + var time, + point, + pointer; + + event = $.getEvent(event); + + var eventOrTouchPoint = event;//isTouch ? event.touches[ 0 ] : event; + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + //isCaptured: true, + //insideElementPressed: true, + insideElement: false, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + updatePointersOut( tracker, event, [pointer] ); + } + + +//$.MouseTracker.mousePointerId = "legacy-mouse"; +//$.MouseTracker.unprefixedPointerEvents = false; +//$.MouseTracker.maxTouchPoints = 10; +// function addPointers( tracker, event, pointers ) { +// } +// function updatePointersOver( tracker, event, pointers ) { +// } +// function updatePointersOut( tracker, event, pointers ) { +// } +// function updatePointers( tracker, event, pointers ) { +// } +// function removePointers( tracker, event, pointers ) { +// } +// function cancelPointers( tracker, event, pointers ) { +// } +//pointer = { +// id: x, // getPointerType( event ) +// type: '', // 'mouse', 'touch', 'pen', '' +// isCaptured: false, +// insideElementPressed: true, +// insideElement: true, +// startPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// startTime: 0xFFFFFFFF, +// lastPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// lastTime: 0xFFFFFFFF, +// currentPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// currentTime: 0xFFFFFFFF, +//} +//var delegate = THIS[ tracker.hash ] +//delegate.mousePoints: {}, +//delegate.mousePointCount: 0, +//delegate.touchPoints: {}, +//delegate.touchPointCount: 0, +//delegate.penPoints: {}, +//delegate.penPointCount: 0 +// +//var touchPoints = {}; +//touchPoints[event.pointerId] = "test"; +//touchPoints[12345] = "test12345"; +//delete touchPoints[event.pointerId]; + /** + * @private + * @inner + */ + function onPointerDown( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + point, + pointer; + + if ( event.button == 2 ) { + return; + } + + if ( $.MouseTracker.unprefixedPointerEvents ) { + event.target.setPointerCapture(event.pointerId); + } + else { + event.target.msSetPointerCapture(event.pointerId); + } + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: event.pointerId, + type: getPointerType( event ), + isCaptured: true, + insideElementPressed: true, + insideElement: true, + startPos: point, + startTime: time, + lastPos: point, + lastTime: time, + currentPos: point, + currentTime: time + }; + + addPointers( tracker, event, [pointer] ); + + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.cancelEvent( event ); + } + } + + + /** + * @private + * @inner + */ + function onMouseDown( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + point, + pointer; + + event = $.getEvent(event); + + if ( event.button == 2 ) { + return; + } + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + isCaptured: true, + insideElementPressed: true, + insideElement: true, + startPos: point, + startTime: time, + lastPos: point, + lastTime: time, + currentPos: point, + currentTime: time + }; + + addPointers( tracker, event, [pointer] ); + + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.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 ); + } + } + + + /** + * @private + * @inner + */ + function onPointerUp( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + point, + pointer; + + if ( event.button == 2 ) { + return; + } + + if ( $.MouseTracker.unprefixedPointerEvents ) { + event.target.releasePointerCapture(event.pointerId); + } + else { + event.target.msReleasePointerCapture(event.pointerId); + } + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: event.pointerId, + type: getPointerType( event ), + isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + removePointers( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function onMouseUp( tracker, event ) { + handleMouseUp( tracker, event ); + } + + + /** + * 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. + * @private + * @inner + */ + function onMouseUpCaptured( tracker, event ) { + var delegate = THIS[ tracker.hash ], + pointer = delegate.mousePoints[ $.MouseTracker.mousePointerId ] || null; + + if ( !pointer.insideElement ) { + handleMouseUp( tracker, event ); + } + + 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 onPointerMove( tracker, event ) { + // Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) + var time, + point, + pointer; + + time = $.now(); + point = getMouseAbsolute( event ); + + pointer = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + updatePointers( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function onMouseMove( tracker, event ) { + handleMouseMove( tracker, event ); + } + + + /** + * @private + * @inner + */ + function onMouseMoveCaptured( tracker, event ) { + handleMouseMove( tracker, event ); + } + + + /** + * 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 + */ + function onPointerCancel( tracker, event ) { + var //time, + //point, + pointer; + + //time = $.now(); + //point = getMouseAbsolute( event ); + + pointer = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + //currentPos: point, + //currentTime: time + }; + + cancelPointers( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function onTouchStart( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + point, + pointer, + i, + touchCount = event.changedTouches.length, + pointers = []; + + time = $.now(); + + if ( touchCount > 0 && delegate.touchPointCount === 0 ) { + pointer = { + id: event.changedTouches[ 0 ].identifier, + type: 'touch', + //isCaptured: false, + //insideElementPressed: true, + insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), + currentTime: time + }; + updatePointersOver( tracker, event, [pointer] ); + } + + for ( i = 0; i < touchCount; i++ ) { + point = getMouseAbsolute( event.changedTouches[ i ] ); + + pointers.push( { + id: event.changedTouches[ i ].identifier, + type: 'touch', + isCaptured: false, + insideElementPressed: true, + insideElement: true, + startPos: point, + startTime: time, + lastPos: point, + lastTime: time, + currentPos: point, + currentTime: time + } ); + } + + addPointers( tracker, event, pointers ); + + event.preventDefault(); +////**************************************************************** +// var touchA, +// touchB; + +// if ( event.touches.length == 1 && +// event.targetTouches.length == 1 && +// event.changedTouches.length == 1 ) { + +// THIS[ tracker.hash ].lastTouch = event.touches[ 0 ]; +// handlePointerOver( tracker, event, getMouseRelative( event.changedTouches[ 0 ], tracker.element ) ); +// // call with no capture as the onMouseMoveCaptured will +// // be triggered by onTouchMove +// onMouseDown( tracker, event, true, true ); +// } + +// if ( event.touches.length == 2 ) { + +// touchA = getMouseAbsolute( event.touches[ 0 ] ); +// touchB = getMouseAbsolute( event.touches[ 1 ] ); +// THIS[ tracker.hash ].lastPinchDelta = +// Math.abs( touchA.x - touchB.x ) + +// Math.abs( touchA.y - touchB.y ); +// THIS[ tracker.hash ].pinchMidpoint = new $.Point( +// ( touchA.x + touchB.x ) / 2, +// ( touchA.y + touchB.y ) / 2 +// ); +// //$.console.debug("pinch start : "+THIS[ tracker.hash ].lastPinchDelta); +// } + +// event.preventDefault(); + } + + + /** + * @private + * @inner + */ + function onTouchEnd( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + point, + pointer, + i, + touchCount = event.changedTouches.length, + pointers = []; + + time = $.now(); + + for ( i = 0; i < touchCount; i++ ) { + point = getMouseAbsolute( event.changedTouches[ i ] ); + + pointers.push( { + id: event.changedTouches[ i ].identifier, + type: 'touch', + isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + } ); + } + + removePointers( tracker, event, pointers ); + + if ( touchCount > 0 && delegate.touchPointCount === 0 ) { + pointer = { + id: event.changedTouches[ 0 ].identifier, + type: 'touch', + //isCaptured: false, + //insideElementPressed: true, + insideElement: false, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), + currentTime: time + }; + updatePointersOut( tracker, event, [pointer] ); + } + + event.preventDefault(); +//**************************************************************************************** +// if ( event.touches.length === 0 && +// event.targetTouches.length === 0 && +// event.changedTouches.length == 1 ) { + +// THIS[ tracker.hash ].lastTouch = null; + +// // call with no release, as the mouse events are +// // not registered in onTouchStart +// onMouseUpCaptured( tracker, event, true, true ); +// handlePointerOut( tracker, event, getMouseRelative( event.changedTouches[ 0 ], tracker.element ) ); +// } +// if ( event.touches.length + event.changedTouches.length == 2 ) { +// THIS[ tracker.hash ].lastPinchDelta = null; +// THIS[ tracker.hash ].pinchMidpoint = null; +// //$.console.debug("pinch end"); +// } +// event.preventDefault(); + } + + + /** + * @private + * @inner + */ + function onTouchMove( tracker, event ) { + var time, + point, + i, + touchCount = event.changedTouches.length, + pointers = []; + + time = $.now(); + + for ( i = 0; i < touchCount; i++ ) { + point = getMouseAbsolute( event.changedTouches[ i ] ); + + pointers.push( { + id: event.changedTouches[ i ].identifier, + type: 'touch', + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + } ); + } + + updatePointers( tracker, event, pointers ); + + event.preventDefault(); +//******************************************************************************* +// var touchA, +// touchB, +// pinchDelta; + +// if ( !THIS[ tracker.hash ].lastTouch ) { +// return; +// } + +// if ( event.touches.length === 1 && +// event.targetTouches.length === 1 && +// event.changedTouches.length === 1 && +// THIS[ tracker.hash ].lastTouch.identifier === event.touches[ 0 ].identifier ) { + +// onMouseMoveCaptured( tracker, event, true ); + +// } else if ( event.touches.length === 2 ) { + +// touchA = getMouseAbsolute( event.touches[ 0 ] ); +// touchB = getMouseAbsolute( event.touches[ 1 ] ); +// pinchDelta = +// Math.abs( touchA.x - touchB.x ) + +// Math.abs( touchA.y - touchB.y ); + +// ////TODO: make the 75px pinch threshold configurable +// //if ( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ) { +// // //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); + +// // // Simulate a 'wheel' event +// // var simulatedEvent = { +// // target: event.target || event.srcElement, +// // type: "wheel", +// // shiftKey: event.shiftKey || false, +// // clientX: THIS[ tracker.hash ].pinchMidpoint.x, +// // clientY: THIS[ tracker.hash ].pinchMidpoint.y, +// // pageX: THIS[ tracker.hash ].pinchMidpoint.x, +// // pageY: THIS[ tracker.hash ].pinchMidpoint.y, +// // deltaMode: 1, // 0=pixel, 1=line, 2=page +// // deltaX: 0, +// // deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1, +// // deltaZ: 0 +// // }; + +// // handleWheelEvent( tracker, simulatedEvent, event, true ); + +// // THIS[ tracker.hash ].lastPinchDelta = pinchDelta; +// //} +// } +// event.preventDefault(); + } + + +//******************************************************************************************************************************************* +//** Event Processing Functions + + /** * Handles 'wheel' events. - * The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()) or onTouchMove(). + * The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()). * * @private * @inner */ - function handleWheelEvent( tracker, event, originalEvent, isTouch ) { + function handleWheelEvent( tracker, event, originalEvent ) { var nDelta = 0, propagate; - isTouch = isTouch || false; - // The nDelta variable is gated to provide smooth z-index scrolling // since the mouse wheel allows for substantial deltas meant for rapid // y-index scrolling. @@ -1244,7 +1734,7 @@ position: getMouseRelative( event, tracker.element ), scroll: nDelta, shift: event.shiftKey, - isTouchEvent: isTouch, + isTouchEvent: false, originalEvent: originalEvent, preventDefaultAction: false, userData: tracker.userData @@ -1261,41 +1751,190 @@ * @private * @inner */ - function handleMouseClick( tracker, event, isTouch ) { - var delegate = THIS[ tracker.hash ], - propagate; + function handleMouseMove( tracker, event ) { + var time, + point, + pointer; - isTouch = isTouch || false; + event = $.getEvent(event); - event = $.getEvent( event ); + time = $.now(); + point = getMouseAbsolute( event ); - var eventOrTouchPoint = isTouch ? event.changedTouches[ 0 ] : event; + pointer = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + updatePointers( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function handleMouseUp( tracker, event ) { + var time, + point, + pointer; + + event = $.getEvent(event); if ( event.button == 2 ) { return; } - var time = $.now() - delegate.lastMouseDownTime, - point = getMouseAbsolute( eventOrTouchPoint ), - distance = delegate.lastMouseDownPoint.distanceTo( point ), - quick = time <= tracker.clickTimeThreshold && - distance <= tracker.clickDistThreshold; + time = $.now(); + point = getMouseAbsolute( event ); - if ( tracker.clickHandler ) { - propagate = tracker.clickHandler( - { - eventSource: tracker, - position: getMouseRelative( eventOrTouchPoint, tracker.element ), - quick: quick, - shift: event.shiftKey, - isTouchEvent: isTouch, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + pointer = { + id: $.MouseTracker.mousePointerId, + type: 'mouse', + isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: point, + //startTime: time, + //lastPos: point, + //lastTime: time, + currentPos: point, + currentTime: time + }; + + removePointers( tracker, event, [pointer] ); + } + + + /** + * @private + * @inner + */ + function handlePointerStop( tracker, originalMoveEvent ) { + if ( tracker.stopHandler ) { + tracker.stopHandler( { + eventSource: tracker, + position: getMouseRelative( originalMoveEvent, tracker.element ), + isTouchEvent: false, + originalEvent: originalMoveEvent, + preventDefaultAction: false, + userData: tracker.userData + } ); + } + } + + +//$.MouseTracker.mousePointerId = "legacy-mouse"; +//$.MouseTracker.unprefixedPointerEvents = false; +//$.MouseTracker.maxTouchPoints = 10; +// function addPointers( tracker, event, pointers ) { +// } +// function updatePointersOver( tracker, event, pointers ) { +// } +// function updatePointersOut( tracker, event, pointers ) { +// } +// function updatePointers( tracker, event, pointers ) { +// } +// function removePointers( tracker, event, pointers ) { +// } +// function cancelPointers( tracker, event, pointers ) { +// } +//pointer = { +// id: x, // getPointerType( event ) +// type: '', // 'mouse', 'touch', 'pen', '' +// isCaptured: false, +// insideElementPressed: true, +// insideElement: true, +// startPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// startTime: 0xFFFFFFFF, +// lastPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// lastTime: 0xFFFFFFFF, +// currentPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// currentTime: 0xFFFFFFFF, +//} +//var delegate = THIS[ tracker.hash ] +//delegate.mousePoints: {}, +//delegate.mousePointCount: 0, +//delegate.touchPoints: {}, +//delegate.touchPointCount: 0, +//delegate.penPoints: {}, +//delegate.penPointCount: 0 +// +//var touchPoints = {}; +//touchPoints[event.pointerId] = "test"; +//touchPoints[12345] = "test12345"; +//delete touchPoints[event.pointerId]; + + /** + * @private + * @inner + */ + function addPointers( tracker, event, pointers ) { + var delegate = THIS[ tracker.hash ], + propagate, + dispatchPress = false, + i, + pointerCount = pointers.length, + curPointer, + gesturePoints; + + for ( i = 0; i < pointerCount; i++ ) { + curPointer = pointers[ i ]; + if ( curPointer.type === 'mouse' ) { + if ( !delegate.mousePointCount ) { + delegate.mousePoints[ curPointer.id ] = curPointer; + delegate.mousePointCount++; + dispatchPress = true; + } + } + else if ( curPointer.type === 'touch' ) { + if ( !delegate.touchPointCount ) { + dispatchPress = true; + } + if ( !delegate.touchPoints[ curPointer.id ] ) { + delegate.touchPoints[ curPointer.id ] = curPointer; + delegate.touchPointCount++; + if ( delegate.touchPointCount == 2 && tracker.pinchHandler ) { + // Initialize for pinch gesture tracking + gesturePoints = []; + for ( var p in delegate.touchPoints ) { + gesturePoints.push( delegate.touchPoints[ p ] ); + } + delegate.lastPinchDist = delegate.currentPinchDist = gesturePoints[0].currentPos.distanceTo(gesturePoints[1].currentPos); + } + } + } + else if ( curPointer.type === 'pen' ) { + if ( !delegate.penPointCount ) { + delegate.penPoints[ curPointer.id ] = curPointer; + delegate.penPointCount++; + dispatchPress = true; + } + } + + if ( dispatchPress && tracker.pressHandler ) { + propagate = tracker.pressHandler( + { + eventSource: tracker, + position: getPointRelative( curPointer.startPos, tracker.element ), + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -1305,36 +1944,239 @@ * @private * @inner */ - function onMouseMoveCaptured( tracker, event, isTouch ) { + function updatePointersOver( tracker, event, pointers ) { var delegate = THIS[ tracker.hash ], + i, + pointerCount = pointers.length, + curPointer, + updatePointer, + insideElementPressed, + propagate; + + for ( i = 0; i < pointerCount; i++ ) { + curPointer = pointers[ i ]; + if ( curPointer.type === 'mouse' ) { + updatePointer = delegate.mousePoints[ curPointer.id ] || null; + } + else if ( curPointer.type === 'touch' ) { + updatePointer = delegate.touchPoints[ curPointer.id ] || null; + } + else if ( curPointer.type === 'pen' ) { + updatePointer = delegate.penPoints[ curPointer.id ] || null; + } + else { + updatePointer = null; + } + + if ( updatePointer ) { + updatePointer.insideElement = true; + updatePointer.lastPos = updatePointer.currentPos; + updatePointer.lastTime = updatePointer.currentTime; + updatePointer.currentPos = curPointer.currentPos; + updatePointer.currentTime = curPointer.currentTime; + insideElementPressed = updatePointer.insideElementPressed; + } + else { + insideElementPressed = false; + } + + if ( tracker.enterHandler ) { + propagate = tracker.enterHandler( + { + eventSource: tracker, + position: getPointRelative( curPointer.currentPos, tracker.element ), + insideElementPressed: insideElementPressed, + buttonDownAny: IS_BUTTON_DOWN, + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + } + + + /** + * @private + * @inner + */ + function updatePointersOut( tracker, event, pointers ) { + var delegate = THIS[ tracker.hash ], + i, + pointerCount = pointers.length, + curPointer, + updatePointer, + insideElementPressed, + propagate; + + for ( i = 0; i < pointerCount; i++ ) { + curPointer = pointers[ i ]; + if ( curPointer.type === 'mouse' ) { + updatePointer = delegate.mousePoints[ curPointer.id ] || null; + } + else if ( curPointer.type === 'touch' ) { + updatePointer = delegate.touchPoints[ curPointer.id ] || null; + } + else if ( curPointer.type === 'pen' ) { + updatePointer = delegate.penPoints[ curPointer.id ] || null; + } + else { + updatePointer = null; + } + + if ( updatePointer ) { + updatePointer.insideElement = false; + updatePointer.lastPos = updatePointer.currentPos; + updatePointer.lastTime = updatePointer.currentTime; + updatePointer.currentPos = curPointer.currentPos; + updatePointer.currentTime = curPointer.currentTime; + insideElementPressed = updatePointer.insideElementPressed; + } + else { + insideElementPressed = false; + } + + if ( tracker.exitHandler ) { + propagate = tracker.exitHandler( + { + eventSource: tracker, + position: getPointRelative( curPointer.currentPos, tracker.element ), + insideElementPressed: insideElementPressed, + buttonDownAny: IS_BUTTON_DOWN, + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + } + + + /** + * @private + * @inner + */ + function updatePointers( tracker, event, pointers ) { + // Pointer(s) changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) + var delegate = THIS[ tracker.hash ], + i, + pointerCount = pointers.length, + curPointer, + updatePointer, + points, + pointCount, delta, propagate, - point; + gesturePoints; - isTouch = isTouch || false; + if ( pointers[ 0 ].type === 'mouse' ) { + points = delegate.mousePoints; + pointCount = delegate.mousePointCount; + } + else if ( pointers[ 0 ].type === 'touch' ) { + points = delegate.touchPoints; + pointCount = delegate.touchPointCount; + } + else if ( pointers[ 0 ].type === 'pen' ) { + points = delegate.penPoints; + pointCount = delegate.penPointCount; + } + else { + points = null; + } - event = $.getEvent(event); - var eventOrTouchPoint = isTouch ? event.touches[ 0 ] : event; - point = getMouseAbsolute( eventOrTouchPoint ); - delta = point.minus( delegate.lastPoint ); + for ( i = 0; i < pointerCount; i++ ) { + curPointer = pointers[ i ]; + updatePointer = points ? ( points[ curPointer.id ] || null ) : null; - delegate.lastPoint = point; + if ( updatePointer ) { + updatePointer.lastPos = updatePointer.currentPos; + updatePointer.lastTime = updatePointer.currentTime; + updatePointer.currentPos = curPointer.currentPos; + updatePointer.currentTime = curPointer.currentTime; - if ( tracker.dragHandler ) { - propagate = tracker.dragHandler( - { - eventSource: tracker, - position: getMouseRelative( eventOrTouchPoint, tracker.element ), - delta: delta, - shift: event.shiftKey, - isTouchEvent: isTouch, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + // Drag Gesture + if ( pointCount == 1 && tracker.dragHandler && !updatePointer.currentPos.equals( updatePointer.lastPos ) ) { + delta = updatePointer.currentPos.minus( updatePointer.lastPos ); + propagate = tracker.dragHandler( + { + eventSource: tracker, + position: getPointRelative( updatePointer.currentPos, tracker.element ), + delta: delta, + shift: event.shiftKey, + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + + if ( pointCount == 1 && tracker.moveHandler ) { + propagate = tracker.moveHandler( + { + eventSource: tracker, + position: getPointRelative( curPointer.currentPos, tracker.element ), + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + //if ( tracker.stopHandler ) { + // clearTimeout( tracker.stopTimeOut ); + // tracker.stopTimeOut = setTimeout( function() { + // handlePointerStop( tracker, event ); + // }, tracker.stopDelay ); + //} + } + + // Pinch Gesture + if ( pointers[ 0 ].type === 'touch' && delegate.touchPointCount == 2 && tracker.pinchHandler ) { + gesturePoints = []; + for ( var p in delegate.touchPoints ) { + gesturePoints.push( delegate.touchPoints[ p ] ); + } + delta = gesturePoints[0].currentPos.distanceTo( gesturePoints[1].currentPos ); + //if ( delta != delegate.currentPinchDist ) { + if (delta != delegate.currentPinchDist && Math.abs(delta - delegate.lastPinchDist) > 75) { + delegate.lastPinchDist = delegate.currentPinchDist; + delegate.currentPinchDist = delta; + + propagate = tracker.pinchHandler( + { + eventSource: tracker, + position: getPointRelative( updatePointer.currentPos, tracker.element ), + delta: delegate.currentPinchDist - delegate.lastPinchDist, + shift: event.shiftKey, + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -1344,93 +2186,333 @@ * @private * @inner */ - function onTouchMove( tracker, event ) { - var touchA, - touchB, - pinchDelta; + function removePointers( tracker, event, pointers ) { + var delegate = THIS[ tracker.hash ], + propagate, + //were we inside the tracked element when we were pressed + insideElementPressed, + //are we still inside the tracked element when we released + insideElementReleased, + dispatchRelease, + pressPoint, + pressTime, + releasePoint, + i, + pointerCount = pointers.length, + curPointer, + updatePointer; - if ( !THIS[ tracker.hash ].lastTouch ) { - return; - } + for ( i = 0; i < pointerCount; i++ ) { + curPointer = pointers[ i ]; + dispatchRelease = false; + if ( curPointer.type === 'mouse' ) { + updatePointer = delegate.mousePoints[ curPointer.id ] || null; + if ( updatePointer ) { + pressPoint = updatePointer.startPos; + pressTime = updatePointer.startTime; + delete delegate.mousePoints[ curPointer.id ]; + delegate.mousePointCount--; + if ( !delegate.mousePointCount ) { + dispatchRelease = true; + releasePoint = curPointer.currentPos; + } + } + } + else if ( curPointer.type === 'touch' ) { + updatePointer = delegate.touchPoints[ curPointer.id ] || null; + if ( updatePointer ) { + pressPoint = updatePointer.startPos; + pressTime = updatePointer.startTime; + delete delegate.touchPoints[ curPointer.id ]; + delegate.touchPointCount--; + if ( !delegate.touchPointCount ) { + dispatchRelease = true; + releasePoint = curPointer.currentPos; + } + } + } + else if ( curPointer.type === 'pen' ) { + updatePointer = delegate.penPoints[ curPointer.id ] || null; + if ( updatePointer ) { + pressPoint = updatePointer.startPos; + pressTime = updatePointer.startTime; + delete delegate.penPoints[ curPointer.id ]; + delegate.penPointCount--; + if ( !delegate.penPointCount ) { + dispatchRelease = true; + releasePoint = curPointer.currentPos; + } + } + } + else { + updatePointer = null; + } - if ( event.touches.length === 1 && - event.targetTouches.length === 1 && - event.changedTouches.length === 1 && - THIS[ tracker.hash ].lastTouch.identifier === event.touches[ 0 ].identifier ) { + if ( dispatchRelease ) { + if ( updatePointer ) { + insideElementPressed = updatePointer.insideElementPressed; + insideElementReleased = $.pointInElement( tracker.element, releasePoint ); + } + else { + insideElementPressed = false; + insideElementReleased = false; + } - onMouseMoveCaptured( tracker, event, true ); + if ( tracker.releaseHandler ) { + propagate = tracker.releaseHandler( + { + eventSource: tracker, + position: getPointRelative( releasePoint, tracker.element ), + insideElementPressed: insideElementPressed, + insideElementReleased: insideElementReleased, + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } - } else if ( event.touches.length === 2 ) { + // Click Gesture + if ( insideElementPressed && insideElementReleased && tracker.clickHandler ) { + var time = curPointer.currentTime - pressTime, + distance = pressPoint.distanceTo( curPointer.currentPos ), + quick = time <= tracker.clickTimeThreshold && + distance <= tracker.clickDistThreshold; - touchA = getMouseAbsolute( event.touches[ 0 ] ); - touchB = getMouseAbsolute( event.touches[ 1 ] ); - pinchDelta = - Math.abs( touchA.x - touchB.x ) + - Math.abs( touchA.y - touchB.y ); - - //TODO: make the 75px pinch threshold configurable - if ( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ) { - //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); - - // Simulate a 'wheel' event - var simulatedEvent = { - target: event.target || event.srcElement, - type: "wheel", - shiftKey: event.shiftKey || false, - clientX: THIS[ tracker.hash ].pinchMidpoint.x, - clientY: THIS[ tracker.hash ].pinchMidpoint.y, - pageX: THIS[ tracker.hash ].pinchMidpoint.x, - pageY: THIS[ tracker.hash ].pinchMidpoint.y, - deltaMode: 1, // 0=pixel, 1=line, 2=page - deltaX: 0, - deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1, - deltaZ: 0 - }; - - handleWheelEvent( tracker, simulatedEvent, event, true ); - - THIS[ tracker.hash ].lastPinchDelta = pinchDelta; + propagate = tracker.clickHandler( + { + eventSource: tracker, + position: getPointRelative( curPointer.currentPos, tracker.element ), + quick: quick, + shift: event.shiftKey, + isTouchEvent: curPointer.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } } } - event.preventDefault(); } + /** - * 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++ ) { - onMouseMoveCaptured( CAPTURING[ i ], event, false ); + function cancelPointers( tracker, event, pointers ) { + var delegate = THIS[ tracker.hash ], + i, + pointerCount = pointers.length, + curPointer; + + for ( i = 0; i < pointerCount; i++ ) { + curPointer = pointers[ i ]; + if ( curPointer.type === 'mouse' ) { + if ( delegate.mousePoints[ curPointer.id ] ) { + delete delegate.mousePoints[ curPointer.id ]; + delegate.mousePointCount--; + } + } + else if ( curPointer.type === 'touch' ) { + if ( delegate.touchPoints[ curPointer.id ] ) { + delete delegate.touchPoints[ curPointer.id ]; + delegate.touchPointCount--; + } + } + else if ( curPointer.type === 'pen' ) { + if ( delegate.penPoints[ curPointer.id ] ) { + delete delegate.penPoints[ curPointer.id ]; + delegate.penPointCount--; + } + } } - - $.stopEvent( event ); } - /** - * @private - * @inner - */ - function getMouseAbsolute( event ) { - return $.getMousePosition( event ); - } - /** - * @private - * @inner - */ - function getMouseRelative( event, element ) { - var mouse = $.getMousePosition( event ), - offset = $.getElementOffset( element ); +//******************************************************************************************************************************************* +//******************************************************************************************************************************************* + + + //function handlePointerOver( tracker, event, position ) { + // var delegate = THIS[ tracker.hash ], + // propagate; + + // //if ( !isTouch ) { + // // if ( $.Browser.vendor == $.BROWSERS.IE && + // // $.Browser.version < 9 && + // // delegate.capturing && + // // !isChild( event.srcElement, tracker.element ) ) { + + // // triggerOthers( tracker, onMouseOver, event, isTouch ); + // // } + + // // var to = event.target ? + // // event.target : + // // event.srcElement, + // // from = event.relatedTarget ? + // // event.relatedTarget : + // // event.fromElement; + + // // if ( !isChild( tracker.element, to ) || + // // isChild( tracker.element, from ) ) { + // // return; + // // } + // //} + + // //delegate.insideElement = true; + + // if ( tracker.enterHandler ) { + // propagate = tracker.enterHandler( + // { + // eventSource: tracker, + // position: position, + // insideElementPressed: delegate.insideElementPressed, + // buttonDownAny: IS_BUTTON_DOWN, + // isTouchEvent: false,//isTouch, + // originalEvent: event, + // preventDefaultAction: false, + // userData: tracker.userData + // } + // ); + // if ( propagate === false ) { + // $.cancelEvent( event ); + // } + // } + //} + + + //function handlePointerOut( tracker, event, position ) { + // var delegate = THIS[ tracker.hash ], + // propagate; + + // //if ( !isTouch ) { + // // if ( $.Browser.vendor == $.BROWSERS.IE && + // // $.Browser.version < 9 && + // // delegate.capturing && + // // !isChild( event.srcElement, tracker.element ) ) { + + // // triggerOthers( tracker, onMouseOut, event, isTouch ); + + // // } + + // // var from = event.target ? + // // event.target : + // // event.srcElement, + // // to = event.relatedTarget ? + // // event.relatedTarget : + // // event.toElement; + + // // if ( !isChild( tracker.element, from ) || + // // isChild( tracker.element, to ) ) { + // // return; + // // } + // //} + + // //delegate.insideElement = false; + + // if ( tracker.exitHandler ) { + // propagate = tracker.exitHandler( + // { + // eventSource: tracker, + // position: position, + // insideElementPressed: delegate.insideElementPressed, + // buttonDownAny: IS_BUTTON_DOWN, + // isTouchEvent: false,//isTouch, + // originalEvent: event, + // preventDefaultAction: false, + // userData: tracker.userData + // } + // ); + + // if ( propagate === false ) { + // $.cancelEvent( event ); + // } + // } + //} + + + ///** + // * @private + // * @inner + // */ + //function handlePointerMove( tracker, event ) { + // var propagate; + // if ( tracker.moveHandler ) { + // propagate = tracker.moveHandler( + // { + // eventSource: tracker, + // position: getMouseRelative( event, tracker.element ), + // isTouchEvent: false, + // originalEvent: event, + // preventDefaultAction: false, + // userData: tracker.userData + // } + // ); + // if ( propagate === false ) { + // $.cancelEvent( event ); + // } + // } + // if ( tracker.stopHandler ) { + // clearTimeout( tracker.stopTimeOut ); + // tracker.stopTimeOut = setTimeout( function() { + // handlePointerStop( tracker, event ); + // }, tracker.stopDelay ); + // } + //} + + // + ///** + // * @private + // * @inner + // */ + //function handleMouseClick( tracker, event, isTouch ) { + // var delegate = THIS[ tracker.hash ], + // propagate; + + // isTouch = isTouch || false; + + // event = $.getEvent( event ); + + // var eventOrTouchPoint = isTouch ? event.changedTouches[ 0 ] : event; + + // if ( event.button == 2 ) { + // return; + // } + + // var time = $.now() - delegate.lastMouseDownTime, + // point = getMouseAbsolute( eventOrTouchPoint ), + // distance = delegate.lastMouseDownPoint.distanceTo( point ), + // quick = time <= tracker.clickTimeThreshold && + // distance <= tracker.clickDistThreshold; + + // if ( tracker.clickHandler ) { + // propagate = tracker.clickHandler( + // { + // eventSource: tracker, + // position: getMouseRelative( eventOrTouchPoint, tracker.element ), + // quick: quick, + // shift: event.shiftKey, + // isTouchEvent: isTouch, + // originalEvent: event, + // preventDefaultAction: false, + // userData: tracker.userData + // } + // ); + // if ( propagate === false ) { + // $.cancelEvent( event ); + // } + // } + //} - return mouse.minus( offset ); - } /** * @private diff --git a/src/openseadragon.js b/src/openseadragon.js index 611d1cd4..b56af941 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -998,6 +998,21 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ }, + /** + * Determines if a point is within the bounding rectangle of the given element (hit-test). + * @function + * @param {Element|String} element + * @param {OpenSeadragon.Point} point + * @returns {Boolean} + */ + pointInElement: function( element, point ) { + element = $.getElement( element ); + var offset = $.getElementOffset( element ), + size = $.getElementSize( element ); + return point.x >= offset.x && point.x < offset.x + size.x && point.y < offset.y + size.y && point.y >= offset.y; + }, + + /** * Gets the latest event, really only useful internally since its * specific to IE behavior. diff --git a/src/viewer.js b/src/viewer.js index cd4b5614..1d53fa2c 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -270,7 +270,14 @@ $.Viewer = function( options ) { style.position = "absolute"; style.top = "0px"; style.left = "0px"; - }( this.canvas.style )); + // Disable browser default touch handling + if (style["touch-action"] !== undefined) { + style["touch-action"] = "none"; + } + else if (style["-ms-touch-action"] !== undefined) { + style["-ms-touch-action"] = "none"; + } + }(this.canvas.style)); //the container is created through applying the ControlDock constructor above this.container.className = "openseadragon-container"; @@ -378,7 +385,8 @@ $.Viewer = function( options ) { clickHandler: $.delegate( this, onCanvasClick ), dragHandler: $.delegate( this, onCanvasDrag ), releaseHandler: $.delegate( this, onCanvasRelease ), - scrollHandler: $.delegate( this, onCanvasScroll ) + scrollHandler: $.delegate( this, onCanvasScroll ), + pinchHandler: $.delegate( this, onCanvasPinch ) }).setTracking( this.mouseNavEnabled ? true : false ); // default state this.outerTracker = new $.MouseTracker({ @@ -1817,7 +1825,7 @@ function onCanvasScroll( event ) { this.viewport.applyConstraints(); } /** - * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#canvas} element (mouse wheel, touch pinch, etc.). + * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#canvas} element (mouse wheel). * * @event canvas-scroll * @memberof OpenSeadragon.Viewer @@ -1841,7 +1849,41 @@ function onCanvasScroll( event ) { return false; } -function onContainerExit( event ) { +function onCanvasPinch(event) { + if (!event.preventDefaultAction && this.viewport) { + //TODO This is temporary for testing. Zoom should track pinch gesture one-to-one, around center point! + this.viewport.zoomBy( + ( event.delta > 0 ) ? 1.2 : 0.8, + this.viewport.pointFromPixel(event.position, true) + ); + this.viewport.applyConstraints(); + } + /** + * Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element. + * + * @event canvas-pinch + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. + * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. + * @property {Number} delta - The pinch delta for the event. + * @property {Boolean} shift - True if the shift key was pressed during this event. + * @property {Object} originalEvent - The original DOM event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent('canvas-pinch', { + tracker: event.eventSource, + position: event.position, + delta: event.delta, + shift: event.shift, + originalEvent: event.originalEvent + }); + //cancels event + return false; +} + +function onContainerExit(event) { if ( !event.insideElementPressed ) { THIS[ this.hash ].mouseInside = false; if ( !THIS[ this.hash ].animating ) { From 38cae86659387af15da459831ddc33703dbdc834 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Fri, 14 Mar 2014 10:42:22 -0700 Subject: [PATCH 02/17] Multi-Touch MouseTracker Update --- src/mousetracker.js | 82 +++++++++++++++++++++++++++++++++++---------- src/viewer.js | 21 ++++++++---- 2 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index f70bef3f..a52d761b 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -201,7 +201,9 @@ touchstart: function ( event ) { onTouchStart( _this, event ); }, touchmove: function ( event ) { onTouchMove( _this, event ); }, touchend: function ( event ) { onTouchEnd( _this, event ); }, - tracking: false, + gesturestart: function ( event ) { onGestureStart( _this, event ); }, + gesturechange: function ( event ) { onGestureChange( _this, event ); }, + tracking: false, capturing: false, // Contact Points mousePoints: {}, @@ -614,7 +616,7 @@ $.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) - $.MouseTracker.subscribeEvents.push( "touchstart", "touchmove", "touchend" ); + $.MouseTracker.subscribeEvents.push( "touchstart", "touchmove", "touchend", "gesturestart", "gesturechange" ); } $.MouseTracker.mousePointerId = "legacy-mouse"; $.MouseTracker.maxTouchPoints = 10; @@ -1168,10 +1170,10 @@ } if ( $.MouseTracker.unprefixedPointerEvents ) { - event.target.setPointerCapture(event.pointerId); + event.currentTarget.setPointerCapture(event.pointerId); } else { - event.target.msSetPointerCapture(event.pointerId); + event.currentTarget.msSetPointerCapture(event.pointerId); } time = $.now(); @@ -1193,9 +1195,12 @@ addPointers( tracker, event, [pointer] ); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.cancelEvent( event ); - } + //if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + // $.cancelEvent(event); + event.stopPropagation(); + event.preventDefault(); + return false; + //} } @@ -1266,10 +1271,10 @@ } if ( $.MouseTracker.unprefixedPointerEvents ) { - event.target.releasePointerCapture(event.pointerId); + event.currentTarget.releasePointerCapture(event.pointerId); } else { - event.target.msReleasePointerCapture(event.pointerId); + event.currentTarget.msReleasePointerCapture(event.pointerId); } time = $.now(); @@ -1289,7 +1294,14 @@ currentTime: time }; - removePointers( tracker, event, [pointer] ); + removePointers(tracker, event, [pointer]); + + //if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + // $.cancelEvent(event); + event.stopPropagation(); + event.preventDefault(); + return false; + //} } @@ -1388,7 +1400,14 @@ currentTime: time }; - updatePointers( tracker, event, [pointer] ); + updatePointers(tracker, event, [pointer]); + + //if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + // $.cancelEvent(event); + event.stopPropagation(); + event.preventDefault(); + return false; + //} } @@ -1511,7 +1530,9 @@ addPointers( tracker, event, pointers ); + event.stopPropagation(); event.preventDefault(); + return false; ////**************************************************************** // var touchA, // touchB; @@ -1597,7 +1618,9 @@ updatePointersOut( tracker, event, [pointer] ); } + event.stopPropagation(); event.preventDefault(); + return false; //**************************************************************************************** // if ( event.touches.length === 0 && // event.targetTouches.length === 0 && @@ -1652,7 +1675,9 @@ updatePointers( tracker, event, pointers ); + event.stopPropagation(); event.preventDefault(); + return false; //******************************************************************************* // var touchA, // touchB, @@ -1705,6 +1730,28 @@ } + /** + * @private + * @inner + */ + function onGestureStart( tracker, event ) { + event.stopPropagation(); + event.preventDefault(); + return false; + } + + + /** + * @private + * @inner + */ + function onGestureChange( tracker, event ) { + event.stopPropagation(); + event.preventDefault(); + return false; + } + + //******************************************************************************************************************************************* //** Event Processing Functions @@ -2157,18 +2204,17 @@ gesturePoints.push( delegate.touchPoints[ p ] ); } delta = gesturePoints[0].currentPos.distanceTo( gesturePoints[1].currentPos ); - //if ( delta != delegate.currentPinchDist ) { - if (delta != delegate.currentPinchDist && Math.abs(delta - delegate.lastPinchDist) > 75) { + if ( delta != delegate.currentPinchDist ) { delegate.lastPinchDist = delegate.currentPinchDist; delegate.currentPinchDist = delta; - propagate = tracker.pinchHandler( { eventSource: tracker, - position: getPointRelative( updatePointer.currentPos, tracker.element ), - delta: delegate.currentPinchDist - delegate.lastPinchDist, - shift: event.shiftKey, - isTouchEvent: curPointer.type === 'touch', + gesturePoints: gesturePoints, + center: getPointRelative( new $.Point( ( gesturePoints[0].currentPos.x + gesturePoints[1].currentPos.x ) / 2, + ( gesturePoints[0].currentPos.y + gesturePoints[1].currentPos.y ) / 2 ) ), + lastDistance: delegate.lastPinchDist, + currentDistance: delegate.currentPinchDist, originalEvent: event, preventDefaultAction: false, userData: tracker.userData diff --git a/src/viewer.js b/src/viewer.js index 1d53fa2c..d8564294 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1850,12 +1850,21 @@ function onCanvasScroll( event ) { } function onCanvasPinch(event) { +//{ +// eventSource: tracker, +// gesturePoints: gesturePoints, +// center: getPointRelative( new $.Point( ( gesturePoints[0].currentPos.x + gesturePoints[1].currentPos.x ) / 2, +// ( gesturePoints[0].currentPos.y + gesturePoints[1].currentPos.y ) / 2 ) ), +// lastDistance: delegate.lastPinchDist, +// currentDistance: delegate.currentPinchDist, +// originalEvent: event, +// preventDefaultAction: false, +// userData: tracker.userData +//} if (!event.preventDefaultAction && this.viewport) { - //TODO This is temporary for testing. Zoom should track pinch gesture one-to-one, around center point! - this.viewport.zoomBy( - ( event.delta > 0 ) ? 1.2 : 0.8, - this.viewport.pointFromPixel(event.position, true) - ); + //TODO This is temporary for testing. Zoom should track pinch gesture one-to-one! + this.viewport.zoomBy( event.currentDistance / event.lastDistance, + this.viewport.pointFromPixel( event.center, true ) ); this.viewport.applyConstraints(); } /** @@ -1883,7 +1892,7 @@ function onCanvasPinch(event) { return false; } -function onContainerExit(event) { +function onContainerExit( event ) { if ( !event.insideElementPressed ) { THIS[ this.hash ].mouseInside = false; if ( !THIS[ this.hash ].animating ) { From 7aa0df1b66b5c9b7cc4ad0d9f3892dfc25eb487f Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Fri, 14 Mar 2014 14:15:09 -0700 Subject: [PATCH 03/17] Multi-Touch MouseTracker Update Pinch zoom update --- src/mousetracker.js | 43 ++++++++++++++++++++++++++++--------------- src/viewer.js | 20 +++++++++++--------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index a52d761b..81c7d246 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -213,8 +213,11 @@ penPoints: {}, penPointCount: 0, // Tracking for pinch gesture + pinchGesturePoints: [], lastPinchDist: 0, currentPinchDist: 0, + lastPinchCenter: null, + currentPinchCenter: null, //insideElementPressed: false, //insideElement: false, @@ -853,6 +856,14 @@ return point.minus( offset ); } + /** + * @private + * @inner + */ + function getCenterPoint( point1, point2 ) { + return new $.Point( ( point1.x + point2.x ) / 2, ( point1.y + point2.y ) / 2 ); + } + //******************************************************************************************************************************************* //** DOM EVent Handlers @@ -1931,8 +1942,7 @@ dispatchPress = false, i, pointerCount = pointers.length, - curPointer, - gesturePoints; + curPointer; for ( i = 0; i < pointerCount; i++ ) { curPointer = pointers[ i ]; @@ -1952,11 +1962,12 @@ delegate.touchPointCount++; if ( delegate.touchPointCount == 2 && tracker.pinchHandler ) { // Initialize for pinch gesture tracking - gesturePoints = []; + delegate.pinchGesturePoints = []; for ( var p in delegate.touchPoints ) { - gesturePoints.push( delegate.touchPoints[ p ] ); + delegate.pinchGesturePoints.push( delegate.touchPoints[ p ] ); } - delegate.lastPinchDist = delegate.currentPinchDist = gesturePoints[0].currentPos.distanceTo(gesturePoints[1].currentPos); + delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGesturePoints[0].currentPos.distanceTo( delegate.pinchGesturePoints[1].currentPos ); + delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGesturePoints[0].currentPos, delegate.pinchGesturePoints[1].currentPos ); } } } @@ -2124,8 +2135,7 @@ points, pointCount, delta, - propagate, - gesturePoints; + propagate; if ( pointers[ 0 ].type === 'mouse' ) { points = delegate.mousePoints; @@ -2199,20 +2209,23 @@ // Pinch Gesture if ( pointers[ 0 ].type === 'touch' && delegate.touchPointCount == 2 && tracker.pinchHandler ) { - gesturePoints = []; - for ( var p in delegate.touchPoints ) { - gesturePoints.push( delegate.touchPoints[ p ] ); - } - delta = gesturePoints[0].currentPos.distanceTo( gesturePoints[1].currentPos ); + //gesturePoints = []; + //for ( var p in delegate.touchPoints ) { + // gesturePoints.push( delegate.touchPoints[ p ] ); + //} + delta = delegate.pinchGesturePoints[0].currentPos.distanceTo( delegate.pinchGesturePoints[1].currentPos ); if ( delta != delegate.currentPinchDist ) { + //window.alert(delegate.pinchGesturePoints[0].currentPos.x + ',' + delegate.pinchGesturePoints[0].currentPos.y + '\n' + delegate.pinchGesturePoints[1].currentPos.x + ',' + delegate.pinchGesturePoints[1].currentPos.y); delegate.lastPinchDist = delegate.currentPinchDist; delegate.currentPinchDist = delta; + delegate.lastPinchCenter = delegate.currentPinchCenter; + delegate.currentPinchCenter = getCenterPoint( delegate.pinchGesturePoints[0].currentPos, delegate.pinchGesturePoints[1].currentPos ); propagate = tracker.pinchHandler( { eventSource: tracker, - gesturePoints: gesturePoints, - center: getPointRelative( new $.Point( ( gesturePoints[0].currentPos.x + gesturePoints[1].currentPos.x ) / 2, - ( gesturePoints[0].currentPos.y + gesturePoints[1].currentPos.y ) / 2 ) ), + gesturePoints: delegate.pinchGesturePoints, + lastCenter: getPointRelative( delegate.lastPinchCenter ), + center: getPointRelative( delegate.currentPinchCenter ), lastDistance: delegate.lastPinchDist, currentDistance: delegate.currentPinchDist, originalEvent: event, diff --git a/src/viewer.js b/src/viewer.js index d8564294..38be0472 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1851,20 +1851,22 @@ function onCanvasScroll( event ) { function onCanvasPinch(event) { //{ -// eventSource: tracker, -// gesturePoints: gesturePoints, -// center: getPointRelative( new $.Point( ( gesturePoints[0].currentPos.x + gesturePoints[1].currentPos.x ) / 2, -// ( gesturePoints[0].currentPos.y + gesturePoints[1].currentPos.y ) / 2 ) ), -// lastDistance: delegate.lastPinchDist, -// currentDistance: delegate.currentPinchDist, -// originalEvent: event, -// preventDefaultAction: false, -// userData: tracker.userData +// eventSource: +// gesturePoints: +// lastCenter: +// center: +// lastDistance: +// currentDistance: +// originalEvent: +// preventDefaultAction: +// userData: //} if (!event.preventDefaultAction && this.viewport) { + //window.alert(event.lastCenter.x + ',' + event.lastCenter.y + '\n' + event.center.x + ',' + event.center.y); //TODO This is temporary for testing. Zoom should track pinch gesture one-to-one! this.viewport.zoomBy( event.currentDistance / event.lastDistance, this.viewport.pointFromPixel( event.center, true ) ); + this.viewport.panBy( this.viewport.pointFromPixel( event.lastCenter, true ).minus( this.viewport.pointFromPixel( event.center, true ) ), false ); this.viewport.applyConstraints(); } /** From d52df4a9bf63706b27e2076dd8dfa813f877eafc Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Sat, 15 Mar 2014 17:12:13 -0700 Subject: [PATCH 04/17] Multi-Touch MouseTracker Update Pinch gesture fixups --- src/mousetracker.js | 5 ++--- src/viewer.js | 9 ++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index 81c7d246..27610fc8 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -2215,7 +2215,6 @@ //} delta = delegate.pinchGesturePoints[0].currentPos.distanceTo( delegate.pinchGesturePoints[1].currentPos ); if ( delta != delegate.currentPinchDist ) { - //window.alert(delegate.pinchGesturePoints[0].currentPos.x + ',' + delegate.pinchGesturePoints[0].currentPos.y + '\n' + delegate.pinchGesturePoints[1].currentPos.x + ',' + delegate.pinchGesturePoints[1].currentPos.y); delegate.lastPinchDist = delegate.currentPinchDist; delegate.currentPinchDist = delta; delegate.lastPinchCenter = delegate.currentPinchCenter; @@ -2224,8 +2223,8 @@ { eventSource: tracker, gesturePoints: delegate.pinchGesturePoints, - lastCenter: getPointRelative( delegate.lastPinchCenter ), - center: getPointRelative( delegate.currentPinchCenter ), + lastCenter: getPointRelative( delegate.lastPinchCenter, tracker.element ), + center: getPointRelative( delegate.currentPinchCenter, tracker.element ), lastDistance: delegate.lastPinchDist, currentDistance: delegate.currentPinchDist, originalEvent: event, diff --git a/src/viewer.js b/src/viewer.js index 38be0472..0620866f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1862,11 +1862,10 @@ function onCanvasPinch(event) { // userData: //} if (!event.preventDefaultAction && this.viewport) { - //window.alert(event.lastCenter.x + ',' + event.lastCenter.y + '\n' + event.center.x + ',' + event.center.y); - //TODO This is temporary for testing. Zoom should track pinch gesture one-to-one! - this.viewport.zoomBy( event.currentDistance / event.lastDistance, - this.viewport.pointFromPixel( event.center, true ) ); - this.viewport.panBy( this.viewport.pointFromPixel( event.lastCenter, true ).minus( this.viewport.pointFromPixel( event.center, true ) ), false ); + var centerPt = this.viewport.pointFromPixel( event.center, true ), + lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true ); + this.viewport.zoomBy( event.currentDistance / event.lastDistance, centerPt, true ); + this.viewport.panBy( lastCenterPt.minus( centerPt ), false ); this.viewport.applyConstraints(); } /** From f6dfe9b07958c94585f2de59d75c6dac4a916613 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Fri, 21 Mar 2014 11:45:20 -0700 Subject: [PATCH 05/17] Multi-Touch MouseTracker Update --- src/mousetracker.js | 1214 +++++++++++++++++++++++++------------------ src/viewer.js | 15 +- 2 files changed, 704 insertions(+), 525 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index 27610fc8..879e1a0a 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -45,6 +45,37 @@ // dictionary from hash to private properties THIS = {}; + + /** + * Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointing device. + * + * @typedef {Object} GesturePoint + * @memberof OpenSeadragon.MouseTracker + * + * @property {Number} id + * Identifier unique from all other active GesturePoints for a given pointer device. + * @property {String} type + * "mouse", "touch", "pen", or "". + * @property {Boolean} isCaptured + * True if input for the pointer is captured to the tracked element. + * @property {Boolean} insideElementPressed + * True if mouse button pressed or contact point initiated inside the screen area of the tracked element. + * @property {Boolean} insideElement + * True if mouse cursor or contact point is currently inside the screen area of the tracked element. + * @property {OpenSeadragon.Point} startPos + * The initial pointer position, relative to the page including any scrolling. + * @property {Number} startTime + * The initial pointer contact time, in milliseconds. + * @property {OpenSeadragon.Point} lastPos + * The last pointer position, relative to the page including any scrolling. + * @property {Number} lastTime + * The last pointer contact time, in milliseconds. + * @property {OpenSeadragon.Point} currentPos + * The current pointer position, relative to the page including any scrolling. + * @property {Number} currentTime + * The current pointer contact time, in milliseconds. + */ + /** * @class MouseTracker * @classdesc Provides simplified handling of common mouse, touch, and keyboard @@ -173,37 +204,46 @@ keypress: function ( event ) { onKeyPress( _this, event ); }, focus: function ( event ) { onFocus( _this, event ); }, blur: function ( event ) { onBlur( _this, event ); }, + wheel: function ( event ) { onWheel( _this, event ); }, mousewheel: function ( event ) { onMouseWheel( _this, event ); }, DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); }, MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); }, - pointerover: function ( event ) { onPointerOver( _this, event ); }, - MSPointerOver: function ( event ) { onPointerOver( _this, event ); }, + mouseover: function ( event ) { onMouseOver( _this, event ); }, - pointerout: function ( event ) { onPointerOut( _this, event ); }, - MSPointerOut: function ( event ) { onPointerOut( _this, event ); }, mouseout: function ( event ) { onMouseOut( _this, event ); }, - pointerdown: function ( event ) { onPointerDown( _this, event ); }, - MSPointerDown: function ( event ) { onPointerDown( _this, event ); }, mousedown: function ( event ) { onMouseDown( _this, event ); }, - pointerup: function ( event ) { onPointerUp( _this, event ); }, - MSPointerUp: function ( event ) { onPointerUp( _this, event ); }, mouseup: function ( event ) { onMouseUp( _this, event ); }, - pointermove: function ( event ) { onPointerMove( _this, event ); }, - MSPointerMove: function ( event ) { onPointerMove( _this, event ); }, - mousemove: function ( event ) { onMouseMove( _this, event ); }, - pointercancel: function ( event ) { onPointerCancel( _this, event ); }, - MSPointerCancel: function ( event ) { onPointerCancel( _this, event ); }, - mouseupcapturedie: function ( event ) { onMouseUpCapturedIE( _this, event ); }, - mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, + mouseupcapturedie: function ( event ) { onMouseUpCapturedIE( _this, event ); }, + mousemove: function ( event ) { onMouseMove( _this, event ); }, mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); }, + mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, + + touchenter: function ( event ) { onTouchEnter( _this, event ); }, + touchleave: function ( event ) { onTouchLeave( _this, event ); }, touchstart: function ( event ) { onTouchStart( _this, event ); }, - touchmove: function ( event ) { onTouchMove( _this, event ); }, touchend: function ( event ) { onTouchEnd( _this, event ); }, + touchmove: function ( event ) { onTouchMove( _this, event ); }, + touchcancel: function ( event ) { onTouchCancel( _this, event ); }, + gesturestart: function ( event ) { onGestureStart( _this, event ); }, gesturechange: function ( event ) { onGestureChange( _this, event ); }, - tracking: false, + + pointerover: function ( event ) { onPointerOver( _this, event ); }, + MSPointerOver: function ( event ) { onPointerOver( _this, event ); }, + pointerout: function ( event ) { onPointerOut( _this, event ); }, + MSPointerOut: function ( event ) { onPointerOut( _this, event ); }, + pointerdown: function ( event ) { onPointerDown( _this, event ); }, + MSPointerDown: function ( event ) { onPointerDown( _this, event ); }, + pointerup: function ( event ) { onPointerUp( _this, event ); }, + MSPointerUp: function ( event ) { onPointerUp( _this, event ); }, + pointermove: function ( event ) { onPointerMove( _this, event ); }, + MSPointerMove: function ( event ) { onPointerMove( _this, event ); }, + pointercancel: function ( event ) { onPointerCancel( _this, event ); }, + MSPointerCancel: function ( event ) { onPointerCancel( _this, event ); }, + + tracking: false, capturing: false, // Contact Points mousePoints: {}, @@ -213,7 +253,7 @@ penPoints: {}, penPointCount: 0, // Tracking for pinch gesture - pinchGesturePoints: [], + pinchGPoints: [], lastPinchDist: 0, currentPinchDist: 0, lastPinchCenter: null, @@ -273,6 +313,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.insideElementPressed @@ -281,7 +323,7 @@ * @param {Boolean} event.buttonDownAny * Was the button down anywhere in the screen during the event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -298,6 +340,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.insideElementPressed @@ -306,7 +350,7 @@ * @param {Boolean} event.buttonDownAny * Was the button down anywhere in the screen during the event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -323,10 +367,12 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -343,6 +389,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.insideElementPressed @@ -351,7 +399,7 @@ * @param {Boolean} event.insideElementReleased * True if the cursor still inside the tracked element when the button was released. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -368,10 +416,12 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -388,6 +438,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Number} event.scroll @@ -395,7 +447,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead. Touch devices no longer generate scroll event. * @param {Object} event.originalEvent * The original event object. * @param {Boolean} event.preventDefaultAction @@ -412,6 +464,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Number} event.quick @@ -419,7 +473,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -436,6 +490,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {OpenSeadragon.Point} event.delta @@ -443,7 +499,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -460,6 +516,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {OpenSeadragon.Point} event.delta @@ -467,7 +525,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -484,6 +542,8 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {OpenSeadragon.Point} event.delta @@ -491,7 +551,7 @@ * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -508,10 +568,12 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. + * @param {String} pointerType + * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {Boolean} event.isTouchEvent - * True if the original event is a touch event, otherwise false. Deprecated. Examine originalEvent instead. + * 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 @@ -584,7 +646,7 @@ 'DOMMouseScroll'; // Assume old Firefox /** - * Detect browser pointer/touch event model and build appropriate list of events to subscribe to. + * Detect browser pointer device event model and build appropriate list of events to subscribe to. */ $.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; @@ -619,7 +681,12 @@ $.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) - $.MouseTracker.subscribeEvents.push( "touchstart", "touchmove", "touchend", "gesturestart", "gesturechange" ); + $.MouseTracker.subscribeEvents.push( "touchenter", "touchleave", "touchstart", "touchend", "touchmove", "touchcancel" ); + } + if ( 'ongesturestart' in window ) { + // iOS (see https://developer.apple.com/library/safari/documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html) + // Subscribe to these to prevent default gesture handling + $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); } $.MouseTracker.mousePointerId = "legacy-mouse"; $.MouseTracker.maxTouchPoints = 10; @@ -1000,95 +1067,35 @@ } - /** - * @private - * @inner - */ - function onPointerOver( tracker, event ) { - var time, - point, - pointer; - - time = $.now(); - point = getMouseAbsolute( event ); - - pointer = { - id: event.pointerId, - type: getPointerType( event ), - //isCaptured: false, - //insideElementPressed: true, - insideElement: true, - //startPos: point, - //startTime: time, - //lastPos: point, - //lastTime: time, - currentPos: point, - currentTime: time - }; - - updatePointersOver( tracker, event, [pointer] ); - } - - /** * @private * @inner */ function onMouseOver( tracker, event ) { var time, - point, - pointer; + position, + gPoint; event = $.getEvent(event); time = $.now(); - point = getMouseAbsolute( event ); + position = getMouseAbsolute( event ); - pointer = { + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', //isCaptured: true, //insideElementPressed: true, insideElement: true, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, - currentPos: point, + currentPos: position, currentTime: time }; - updatePointersOver( tracker, event, [pointer] ); - } - - - /** - * @private - * @inner - */ - function onPointerOut( tracker, event ) { - var time, - point, - pointer; - - time = $.now(); - point = getMouseAbsolute( event ); - - pointer = { - id: event.pointerId, - type: getPointerType( event ), - //isCaptured: false, - //insideElementPressed: true, - insideElement: false, - //startPos: point, - //startTime: time, - //lastPos: point, - //lastTime: time, - currentPos: point, - currentTime: time - }; - - updatePointersOut( tracker, event, [pointer] ); + updatePointersOver( tracker, event, [gPoint] ); } @@ -1098,120 +1105,31 @@ */ function onMouseOut( tracker, event ) { var time, - point, - pointer; + position, + gPoint; event = $.getEvent(event); var eventOrTouchPoint = event;//isTouch ? event.touches[ 0 ] : event; time = $.now(); - point = getMouseAbsolute( event ); + position = getMouseAbsolute( event ); - pointer = { + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', //isCaptured: true, //insideElementPressed: true, insideElement: false, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, - currentPos: point, + currentPos: position, currentTime: time }; - updatePointersOut( tracker, event, [pointer] ); - } - - -//$.MouseTracker.mousePointerId = "legacy-mouse"; -//$.MouseTracker.unprefixedPointerEvents = false; -//$.MouseTracker.maxTouchPoints = 10; -// function addPointers( tracker, event, pointers ) { -// } -// function updatePointersOver( tracker, event, pointers ) { -// } -// function updatePointersOut( tracker, event, pointers ) { -// } -// function updatePointers( tracker, event, pointers ) { -// } -// function removePointers( tracker, event, pointers ) { -// } -// function cancelPointers( tracker, event, pointers ) { -// } -//pointer = { -// id: x, // getPointerType( event ) -// type: '', // 'mouse', 'touch', 'pen', '' -// isCaptured: false, -// insideElementPressed: true, -// insideElement: true, -// startPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// startTime: 0xFFFFFFFF, -// lastPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// lastTime: 0xFFFFFFFF, -// currentPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// currentTime: 0xFFFFFFFF, -//} -//var delegate = THIS[ tracker.hash ] -//delegate.mousePoints: {}, -//delegate.mousePointCount: 0, -//delegate.touchPoints: {}, -//delegate.touchPointCount: 0, -//delegate.penPoints: {}, -//delegate.penPointCount: 0 -// -//var touchPoints = {}; -//touchPoints[event.pointerId] = "test"; -//touchPoints[12345] = "test12345"; -//delete touchPoints[event.pointerId]; - /** - * @private - * @inner - */ - function onPointerDown( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - point, - pointer; - - if ( event.button == 2 ) { - return; - } - - if ( $.MouseTracker.unprefixedPointerEvents ) { - event.currentTarget.setPointerCapture(event.pointerId); - } - else { - event.currentTarget.msSetPointerCapture(event.pointerId); - } - - time = $.now(); - point = getMouseAbsolute( event ); - - pointer = { - id: event.pointerId, - type: getPointerType( event ), - isCaptured: true, - insideElementPressed: true, - insideElement: true, - startPos: point, - startTime: time, - lastPos: point, - lastTime: time, - currentPos: point, - currentTime: time - }; - - addPointers( tracker, event, [pointer] ); - - //if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - // $.cancelEvent(event); - event.stopPropagation(); - event.preventDefault(); - return false; - //} + updatePointersOut( tracker, event, [gPoint] ); } @@ -1222,8 +1140,8 @@ function onMouseDown( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - point, - pointer; + position, + gPoint; event = $.getEvent(event); @@ -1232,23 +1150,23 @@ } time = $.now(); - point = getMouseAbsolute( event ); + position = getMouseAbsolute( event ); - pointer = { + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', isCaptured: true, insideElementPressed: true, insideElement: true, - startPos: point, + startPos: position, startTime: time, - lastPos: point, + lastPos: position, lastTime: time, - currentPos: point, + currentPos: position, currentTime: time }; - addPointers( tracker, event, [pointer] ); + addPointers( tracker, event, [gPoint] ); if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { $.cancelEvent( event ); @@ -1267,55 +1185,6 @@ } - /** - * @private - * @inner - */ - function onPointerUp( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - point, - pointer; - - if ( event.button == 2 ) { - return; - } - - if ( $.MouseTracker.unprefixedPointerEvents ) { - event.currentTarget.releasePointerCapture(event.pointerId); - } - else { - event.currentTarget.msReleasePointerCapture(event.pointerId); - } - - time = $.now(); - point = getMouseAbsolute( event ); - - pointer = { - id: event.pointerId, - type: getPointerType( event ), - isCaptured: false, - //insideElementPressed: true, - //insideElement: true, - //startPos: point, - //startTime: time, - //lastPos: point, - //lastTime: time, - currentPos: point, - currentTime: time - }; - - removePointers(tracker, event, [pointer]); - - //if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - // $.cancelEvent(event); - event.stopPropagation(); - event.preventDefault(); - return false; - //} - } - - /** * @private * @inner @@ -1336,9 +1205,9 @@ */ function onMouseUpCaptured( tracker, event ) { var delegate = THIS[ tracker.hash ], - pointer = delegate.mousePoints[ $.MouseTracker.mousePointerId ] || null; + gPoint = delegate.mousePoints[ $.MouseTracker.mousePointerId ] || null; - if ( !pointer.insideElement ) { + if ( !gPoint.insideElement ) { handleMouseUp( tracker, event ); } @@ -1384,44 +1253,6 @@ } - /** - * @private - * @inner - */ - function onPointerMove( tracker, event ) { - // Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) - var time, - point, - pointer; - - time = $.now(); - point = getMouseAbsolute( event ); - - pointer = { - id: event.pointerId, - type: getPointerType( event ), - //isCaptured: false, - //insideElementPressed: true, - //insideElement: true, - //startPos: point, - //startTime: time, - //lastPos: point, - //lastTime: time, - currentPos: point, - currentTime: time - }; - - updatePointers(tracker, event, [pointer]); - - //if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - // $.cancelEvent(event); - event.stopPropagation(); - event.preventDefault(); - return false; - //} - } - - /** * @private * @inner @@ -1463,29 +1294,69 @@ * @private * @inner */ - function onPointerCancel( tracker, event ) { - var //time, - //point, - pointer; + function onTouchEnter( tracker, event ) { + var time, + position, + i, + touchCount = event.changedTouches.length, + gPoints = []; - //time = $.now(); - //point = getMouseAbsolute( event ); + time = $.now(); - pointer = { - id: event.pointerId, - type: getPointerType( event ), - //isCaptured: false, - //insideElementPressed: true, - //insideElement: true, - //startPos: point, - //startTime: time, - //lastPos: point, - //lastTime: time, - //currentPos: point, - //currentTime: time - }; + for ( i = 0; i < touchCount; i++ ) { + position = getMouseAbsolute( event.changedTouches[ i ] ); - cancelPointers( tracker, event, [pointer] ); + gPoints.push( { + id: event.changedTouches[ i ].identifier, + type: 'touch', + //isCaptured: false, + //insideElementPressed: true, + insideElement: true, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + currentPos: position, + currentTime: time + } ); + } + + updatePointersOver( tracker, event, gPoints ); + } + + + /** + * @private + * @inner + */ + function onTouchLeave( tracker, event ) { + var time, + position, + i, + touchCount = event.changedTouches.length, + gPoints = []; + + time = $.now(); + + for ( i = 0; i < touchCount; i++ ) { + position = getMouseAbsolute( event.changedTouches[ i ] ); + + gPoints.push( { + id: event.changedTouches[ i ].identifier, + type: 'touch', + //isCaptured: false, + //insideElementPressed: true, + insideElement: false, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + currentPos: position, + currentTime: time + } ); + } + + updatePointersOut( tracker, event, gPoints ); } @@ -1496,54 +1367,56 @@ function onTouchStart( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - point, - pointer, + position, + gPoint, i, touchCount = event.changedTouches.length, - pointers = []; + gPoints = []; time = $.now(); if ( touchCount > 0 && delegate.touchPointCount === 0 ) { - pointer = { + gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', //isCaptured: false, //insideElementPressed: true, insideElement: true, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; - updatePointersOver( tracker, event, [pointer] ); + updatePointersOver( tracker, event, [gPoint] ); } for ( i = 0; i < touchCount; i++ ) { - point = getMouseAbsolute( event.changedTouches[ i ] ); + position = getMouseAbsolute( event.changedTouches[ i ] ); - pointers.push( { + gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', isCaptured: false, insideElementPressed: true, insideElement: true, - startPos: point, + startPos: position, startTime: time, - lastPos: point, + lastPos: position, lastTime: time, - currentPos: point, + currentPos: position, currentTime: time } ); } - addPointers( tracker, event, pointers ); + addPointers( tracker, event, gPoints ); - event.stopPropagation(); - event.preventDefault(); - return false; + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.stopEvent(event); + $.cancelEvent(event); + return false; + } ////**************************************************************** // var touchA, // touchB; @@ -1584,54 +1457,56 @@ function onTouchEnd( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - point, - pointer, + position, + gPoint, i, touchCount = event.changedTouches.length, - pointers = []; + gPoints = []; time = $.now(); for ( i = 0; i < touchCount; i++ ) { - point = getMouseAbsolute( event.changedTouches[ i ] ); + position = getMouseAbsolute( event.changedTouches[ i ] ); - pointers.push( { + gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', isCaptured: false, //insideElementPressed: true, //insideElement: true, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, - currentPos: point, + currentPos: position, currentTime: time } ); } - removePointers( tracker, event, pointers ); + removePointers( tracker, event, gPoints ); if ( touchCount > 0 && delegate.touchPointCount === 0 ) { - pointer = { + gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', //isCaptured: false, //insideElementPressed: true, insideElement: false, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; - updatePointersOut( tracker, event, [pointer] ); + updatePointersOut( tracker, event, [gPoint] ); } - event.stopPropagation(); - event.preventDefault(); - return false; + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.stopEvent(event); + $.cancelEvent(event); + return false; + } //**************************************************************************************** // if ( event.touches.length === 0 && // event.targetTouches.length === 0 && @@ -1659,36 +1534,38 @@ */ function onTouchMove( tracker, event ) { var time, - point, + position, i, touchCount = event.changedTouches.length, - pointers = []; + gPoints = []; time = $.now(); for ( i = 0; i < touchCount; i++ ) { - point = getMouseAbsolute( event.changedTouches[ i ] ); + position = getMouseAbsolute( event.changedTouches[ i ] ); - pointers.push( { + gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', //isCaptured: false, //insideElementPressed: true, //insideElement: true, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, - currentPos: point, + currentPos: position, currentTime: time } ); } - updatePointers( tracker, event, pointers ); + updatePointers( tracker, event, gPoints ); - event.stopPropagation(); - event.preventDefault(); - return false; + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.stopEvent(event); + $.cancelEvent(event); + return false; + } //******************************************************************************* // var touchA, // touchB, @@ -1741,6 +1618,41 @@ } + /** + * @private + * @inner + */ + function onTouchCancel( tracker, event ) { + var //time, + //position, + i, + touchCount = event.changedTouches.length, + gPoints = []; + + //time = $.now(); + + for ( i = 0; i < touchCount; i++ ) { + //position = getMouseAbsolute( event.changedTouches[ i ] ); + + gPoints.push( { + id: event.changedTouches[ i ].identifier, + type: 'touch', + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + //currentPos: position, + //currentTime: time + } ); + } + + cancelPointers( tracker, event, gPoints ); + } + + /** * @private * @inner @@ -1763,6 +1675,269 @@ } + /** + * @private + * @inner + */ + function onPointerOver( tracker, event ) { + var time, + position, + gPoint; + + time = $.now(); + position = getMouseAbsolute( event ); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + insideElement: true, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + currentPos: position, + currentTime: time + }; + + updatePointersOver( tracker, event, [gPoint] ); + } + + + /** + * @private + * @inner + */ + function onPointerOut( tracker, event ) { + var time, + position, + gPoint; + + time = $.now(); + position = getMouseAbsolute( event ); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + insideElement: false, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + currentPos: position, + currentTime: time + }; + + updatePointersOut( tracker, event, [gPoint] ); + } + + +//$.MouseTracker.mousePointerId = "legacy-mouse"; +//$.MouseTracker.unprefixedPointerEvents = false; +//$.MouseTracker.maxTouchPoints = 10; +// function addPointers( tracker, event, gPoints ) { +// } +// function updatePointersOver( tracker, event, gPoints ) { +// } +// function updatePointersOut( tracker, event, gPoints ) { +// } +// function updatePointers( tracker, event, gPoints ) { +// } +// function removePointers( tracker, event, gPoints ) { +// } +// function cancelPointers( tracker, event, gPoints ) { +// } +//pointer = { +// id: x, // getPointerType( event ) +// type: '', // 'mouse', 'touch', 'pen', '' +// isCaptured: false, +// insideElementPressed: true, +// insideElement: true, +// startPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// startTime: 0xFFFFFFFF, +// lastPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// lastTime: 0xFFFFFFFF, +// currentPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) +// currentTime: 0xFFFFFFFF, +//} +//var delegate = THIS[ tracker.hash ] +//delegate.mousePoints: {}, +//delegate.mousePointCount: 0, +//delegate.touchPoints: {}, +//delegate.touchPointCount: 0, +//delegate.penPoints: {}, +//delegate.penPointCount: 0 +// +//var touchPoints = {}; +//touchPoints[event.pointerId] = "test"; +//touchPoints[12345] = "test12345"; +//delete touchPoints[event.pointerId]; + /** + * @private + * @inner + */ + function onPointerDown( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + position, + gPoint; + + if ( event.button == 2 ) { + return; + } + + if ( $.MouseTracker.unprefixedPointerEvents ) { + event.currentTarget.setPointerCapture(event.pointerId); + } + else { + event.currentTarget.msSetPointerCapture(event.pointerId); + } + + time = $.now(); + position = getMouseAbsolute( event ); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + isCaptured: true, + insideElementPressed: true, + insideElement: true, + startPos: position, + startTime: time, + lastPos: position, + lastTime: time, + currentPos: position, + currentTime: time + }; + + addPointers( tracker, event, [gPoint] ); + + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.stopEvent(event); + $.cancelEvent(event); + return false; + } + } + + + /** + * @private + * @inner + */ + function onPointerUp( tracker, event ) { + var delegate = THIS[ tracker.hash ], + time, + position, + gPoint; + + if ( event.button == 2 ) { + return; + } + + if ( $.MouseTracker.unprefixedPointerEvents ) { + event.currentTarget.releasePointerCapture(event.pointerId); + } + else { + event.currentTarget.msReleasePointerCapture(event.pointerId); + } + + time = $.now(); + position = getMouseAbsolute( event ); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + currentPos: position, + currentTime: time + }; + + removePointers(tracker, event, [gPoint]); + + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.stopEvent(event); + $.cancelEvent(event); + return false; + } + } + + + /** + * @private + * @inner + */ + function onPointerMove( tracker, event ) { + // Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) + var time, + position, + gPoint; + + time = $.now(); + position = getMouseAbsolute( event ); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + currentPos: position, + currentTime: time + }; + + updatePointers(tracker, event, [gPoint]); + + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + $.stopEvent(event); + $.cancelEvent(event); + return false; + } + } + + + /** + * @private + * @inner + */ + function onPointerCancel( tracker, event ) { + var //time, + //position, + gPoint; + + //time = $.now(); + //position = getMouseAbsolute( event ); + + gPoint = { + id: event.pointerId, + type: getPointerType( event ), + //isCaptured: false, + //insideElementPressed: true, + //insideElement: true, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, + //currentPos: position, + //currentTime: time + }; + + cancelPointers( tracker, event, [gPoint] ); + } + + //******************************************************************************************************************************************* //** Event Processing Functions @@ -1789,6 +1964,7 @@ propagate = tracker.scrollHandler( { eventSource: tracker, + pointerType: 'mouse', position: getMouseRelative( event, tracker.element ), scroll: nDelta, shift: event.shiftKey, @@ -1811,29 +1987,29 @@ */ function handleMouseMove( tracker, event ) { var time, - point, - pointer; + position, + gPoint; event = $.getEvent(event); time = $.now(); - point = getMouseAbsolute( event ); + position = getMouseAbsolute( event ); - pointer = { + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', //isCaptured: false, //insideElementPressed: true, //insideElement: true, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, - currentPos: point, + currentPos: position, currentTime: time }; - updatePointers( tracker, event, [pointer] ); + updatePointers( tracker, event, [gPoint] ); } @@ -1843,8 +2019,8 @@ */ function handleMouseUp( tracker, event ) { var time, - point, - pointer; + position, + gPoint; event = $.getEvent(event); @@ -1853,23 +2029,23 @@ } time = $.now(); - point = getMouseAbsolute( event ); + position = getMouseAbsolute( event ); - pointer = { + gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', isCaptured: false, //insideElementPressed: true, //insideElement: true, - //startPos: point, + //startPos: position, //startTime: time, - //lastPos: point, + //lastPos: position, //lastTime: time, - currentPos: point, + currentPos: position, currentTime: time }; - removePointers( tracker, event, [pointer] ); + removePointers( tracker, event, [gPoint] ); } @@ -1881,6 +2057,7 @@ if ( tracker.stopHandler ) { tracker.stopHandler( { eventSource: tracker, + pointerType: '', position: getMouseRelative( originalMoveEvent, tracker.element ), isTouchEvent: false, originalEvent: originalMoveEvent, @@ -1894,17 +2071,17 @@ //$.MouseTracker.mousePointerId = "legacy-mouse"; //$.MouseTracker.unprefixedPointerEvents = false; //$.MouseTracker.maxTouchPoints = 10; -// function addPointers( tracker, event, pointers ) { +// function addPointers( tracker, event, gPoints ) { // } -// function updatePointersOver( tracker, event, pointers ) { +// function updatePointersOver( tracker, event, gPoints ) { // } -// function updatePointersOut( tracker, event, pointers ) { +// function updatePointersOut( tracker, event, gPoints ) { // } -// function updatePointers( tracker, event, pointers ) { +// function updatePointers( tracker, event, gPoints ) { // } -// function removePointers( tracker, event, pointers ) { +// function removePointers( tracker, event, gPoints ) { // } -// function cancelPointers( tracker, event, pointers ) { +// function cancelPointers( tracker, event, gPoints ) { // } //pointer = { // id: x, // getPointerType( event ) @@ -1936,44 +2113,44 @@ * @private * @inner */ - function addPointers( tracker, event, pointers ) { + function addPointers( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], propagate, dispatchPress = false, i, - pointerCount = pointers.length, - curPointer; + gPointCount = gPoints.length, + curGPoint; - for ( i = 0; i < pointerCount; i++ ) { - curPointer = pointers[ i ]; - if ( curPointer.type === 'mouse' ) { + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + if ( curGPoint.type === 'mouse' ) { if ( !delegate.mousePointCount ) { - delegate.mousePoints[ curPointer.id ] = curPointer; + delegate.mousePoints[ curGPoint.id ] = curGPoint; delegate.mousePointCount++; dispatchPress = true; } } - else if ( curPointer.type === 'touch' ) { + else if ( curGPoint.type === 'touch' ) { if ( !delegate.touchPointCount ) { dispatchPress = true; } - if ( !delegate.touchPoints[ curPointer.id ] ) { - delegate.touchPoints[ curPointer.id ] = curPointer; + if ( !delegate.touchPoints[ curGPoint.id ] ) { + delegate.touchPoints[ curGPoint.id ] = curGPoint; delegate.touchPointCount++; if ( delegate.touchPointCount == 2 && tracker.pinchHandler ) { // Initialize for pinch gesture tracking - delegate.pinchGesturePoints = []; + delegate.pinchGPoints = []; for ( var p in delegate.touchPoints ) { - delegate.pinchGesturePoints.push( delegate.touchPoints[ p ] ); + delegate.pinchGPoints.push( delegate.touchPoints[ p ] ); } - delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGesturePoints[0].currentPos.distanceTo( delegate.pinchGesturePoints[1].currentPos ); - delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGesturePoints[0].currentPos, delegate.pinchGesturePoints[1].currentPos ); + delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGPoints[0].currentPos.distanceTo( delegate.pinchGPoints[1].currentPos ); + delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[0].currentPos, delegate.pinchGPoints[1].currentPos ); } } } - else if ( curPointer.type === 'pen' ) { + else if ( curGPoint.type === 'pen' ) { if ( !delegate.penPointCount ) { - delegate.penPoints[ curPointer.id ] = curPointer; + delegate.penPoints[ curGPoint.id ] = curGPoint; delegate.penPointCount++; dispatchPress = true; } @@ -1983,8 +2160,9 @@ propagate = tracker.pressHandler( { eventSource: tracker, - position: getPointRelative( curPointer.startPos, tracker.element ), - isTouchEvent: curPointer.type === 'touch', + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.startPos, tracker.element ), + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2002,37 +2180,37 @@ * @private * @inner */ - function updatePointersOver( tracker, event, pointers ) { + function updatePointersOver( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], i, - pointerCount = pointers.length, - curPointer, - updatePointer, + gPointCount = gPoints.length, + curGPoint, + updateGPoint, insideElementPressed, propagate; - for ( i = 0; i < pointerCount; i++ ) { - curPointer = pointers[ i ]; - if ( curPointer.type === 'mouse' ) { - updatePointer = delegate.mousePoints[ curPointer.id ] || null; + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + if ( curGPoint.type === 'mouse' ) { + updateGPoint = delegate.mousePoints[ curGPoint.id ] || null; } - else if ( curPointer.type === 'touch' ) { - updatePointer = delegate.touchPoints[ curPointer.id ] || null; + else if ( curGPoint.type === 'touch' ) { + updateGPoint = delegate.touchPoints[ curGPoint.id ] || null; } - else if ( curPointer.type === 'pen' ) { - updatePointer = delegate.penPoints[ curPointer.id ] || null; + else if ( curGPoint.type === 'pen' ) { + updateGPoint = delegate.penPoints[ curGPoint.id ] || null; } else { - updatePointer = null; + updateGPoint = null; } - if ( updatePointer ) { - updatePointer.insideElement = true; - updatePointer.lastPos = updatePointer.currentPos; - updatePointer.lastTime = updatePointer.currentTime; - updatePointer.currentPos = curPointer.currentPos; - updatePointer.currentTime = curPointer.currentTime; - insideElementPressed = updatePointer.insideElementPressed; + if ( updateGPoint ) { + updateGPoint.insideElement = true; + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; + insideElementPressed = updateGPoint.insideElementPressed; } else { insideElementPressed = false; @@ -2042,10 +2220,11 @@ propagate = tracker.enterHandler( { eventSource: tracker, - position: getPointRelative( curPointer.currentPos, tracker.element ), + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), insideElementPressed: insideElementPressed, buttonDownAny: IS_BUTTON_DOWN, - isTouchEvent: curPointer.type === 'touch', + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2063,37 +2242,37 @@ * @private * @inner */ - function updatePointersOut( tracker, event, pointers ) { + function updatePointersOut( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], i, - pointerCount = pointers.length, - curPointer, - updatePointer, + gPointCount = gPoints.length, + curGPoint, + updateGPoint, insideElementPressed, propagate; - for ( i = 0; i < pointerCount; i++ ) { - curPointer = pointers[ i ]; - if ( curPointer.type === 'mouse' ) { - updatePointer = delegate.mousePoints[ curPointer.id ] || null; + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + if ( curGPoint.type === 'mouse' ) { + updateGPoint = delegate.mousePoints[ curGPoint.id ] || null; } - else if ( curPointer.type === 'touch' ) { - updatePointer = delegate.touchPoints[ curPointer.id ] || null; + else if ( curGPoint.type === 'touch' ) { + updateGPoint = delegate.touchPoints[ curGPoint.id ] || null; } - else if ( curPointer.type === 'pen' ) { - updatePointer = delegate.penPoints[ curPointer.id ] || null; + else if ( curGPoint.type === 'pen' ) { + updateGPoint = delegate.penPoints[ curGPoint.id ] || null; } else { - updatePointer = null; + updateGPoint = null; } - if ( updatePointer ) { - updatePointer.insideElement = false; - updatePointer.lastPos = updatePointer.currentPos; - updatePointer.lastTime = updatePointer.currentTime; - updatePointer.currentPos = curPointer.currentPos; - updatePointer.currentTime = curPointer.currentTime; - insideElementPressed = updatePointer.insideElementPressed; + if ( updateGPoint ) { + updateGPoint.insideElement = false; + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; + insideElementPressed = updateGPoint.insideElementPressed; } else { insideElementPressed = false; @@ -2103,10 +2282,11 @@ propagate = tracker.exitHandler( { eventSource: tracker, - position: getPointRelative( curPointer.currentPos, tracker.element ), + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), insideElementPressed: insideElementPressed, buttonDownAny: IS_BUTTON_DOWN, - isTouchEvent: curPointer.type === 'touch', + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2125,27 +2305,27 @@ * @private * @inner */ - function updatePointers( tracker, event, pointers ) { + function updatePointers( tracker, event, gPoints ) { // Pointer(s) changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) var delegate = THIS[ tracker.hash ], i, - pointerCount = pointers.length, - curPointer, - updatePointer, + gPointCount = gPoints.length, + curGPoint, + updateGPoint, points, pointCount, delta, propagate; - if ( pointers[ 0 ].type === 'mouse' ) { + if ( gPoints[ 0 ].type === 'mouse' ) { points = delegate.mousePoints; pointCount = delegate.mousePointCount; } - else if ( pointers[ 0 ].type === 'touch' ) { + else if ( gPoints[ 0 ].type === 'touch' ) { points = delegate.touchPoints; pointCount = delegate.touchPointCount; } - else if ( pointers[ 0 ].type === 'pen' ) { + else if ( gPoints[ 0 ].type === 'pen' ) { points = delegate.penPoints; pointCount = delegate.penPointCount; } @@ -2153,26 +2333,27 @@ points = null; } - for ( i = 0; i < pointerCount; i++ ) { - curPointer = pointers[ i ]; - updatePointer = points ? ( points[ curPointer.id ] || null ) : null; + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = points ? ( points[ curGPoint.id ] || null ) : null; - if ( updatePointer ) { - updatePointer.lastPos = updatePointer.currentPos; - updatePointer.lastTime = updatePointer.currentTime; - updatePointer.currentPos = curPointer.currentPos; - updatePointer.currentTime = curPointer.currentTime; + if ( updateGPoint ) { + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; // Drag Gesture - if ( pointCount == 1 && tracker.dragHandler && !updatePointer.currentPos.equals( updatePointer.lastPos ) ) { - delta = updatePointer.currentPos.minus( updatePointer.lastPos ); + if ( pointCount == 1 && tracker.dragHandler && !updateGPoint.currentPos.equals( updateGPoint.lastPos ) ) { + delta = updateGPoint.currentPos.minus( updateGPoint.lastPos ); propagate = tracker.dragHandler( { eventSource: tracker, - position: getPointRelative( updatePointer.currentPos, tracker.element ), + pointerType: curGPoint.type, + position: getPointRelative( updateGPoint.currentPos, tracker.element ), delta: delta, shift: event.shiftKey, - isTouchEvent: curPointer.type === 'touch', + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2188,8 +2369,9 @@ propagate = tracker.moveHandler( { eventSource: tracker, - position: getPointRelative( curPointer.currentPos, tracker.element ), - isTouchEvent: curPointer.type === 'touch', + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2208,25 +2390,25 @@ } // Pinch Gesture - if ( pointers[ 0 ].type === 'touch' && delegate.touchPointCount == 2 && tracker.pinchHandler ) { + if ( gPoints[ 0 ].type === 'touch' && delegate.touchPointCount == 2 && tracker.pinchHandler ) { //gesturePoints = []; //for ( var p in delegate.touchPoints ) { // gesturePoints.push( delegate.touchPoints[ p ] ); //} - delta = delegate.pinchGesturePoints[0].currentPos.distanceTo( delegate.pinchGesturePoints[1].currentPos ); + delta = delegate.pinchGPoints[0].currentPos.distanceTo( delegate.pinchGPoints[1].currentPos ); if ( delta != delegate.currentPinchDist ) { delegate.lastPinchDist = delegate.currentPinchDist; delegate.currentPinchDist = delta; delegate.lastPinchCenter = delegate.currentPinchCenter; - delegate.currentPinchCenter = getCenterPoint( delegate.pinchGesturePoints[0].currentPos, delegate.pinchGesturePoints[1].currentPos ); + delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[0].currentPos, delegate.pinchGPoints[1].currentPos ); propagate = tracker.pinchHandler( { eventSource: tracker, - gesturePoints: delegate.pinchGesturePoints, + gesturePoints: delegate.pinchGPoints, lastCenter: getPointRelative( delegate.lastPinchCenter, tracker.element ), center: getPointRelative( delegate.currentPinchCenter, tracker.element ), lastDistance: delegate.lastPinchDist, - currentDistance: delegate.currentPinchDist, + distance: delegate.currentPinchDist, originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2244,7 +2426,7 @@ * @private * @inner */ - function removePointers( tracker, event, pointers ) { + function removePointers( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], propagate, //were we inside the tracked element when we were pressed @@ -2256,59 +2438,59 @@ pressTime, releasePoint, i, - pointerCount = pointers.length, - curPointer, - updatePointer; + gPointCount = gPoints.length, + curGPoint, + updateGPoint; - for ( i = 0; i < pointerCount; i++ ) { - curPointer = pointers[ i ]; + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; dispatchRelease = false; - if ( curPointer.type === 'mouse' ) { - updatePointer = delegate.mousePoints[ curPointer.id ] || null; - if ( updatePointer ) { - pressPoint = updatePointer.startPos; - pressTime = updatePointer.startTime; - delete delegate.mousePoints[ curPointer.id ]; + if ( curGPoint.type === 'mouse' ) { + updateGPoint = delegate.mousePoints[ curGPoint.id ] || null; + if ( updateGPoint ) { + pressPoint = updateGPoint.startPos; + pressTime = updateGPoint.startTime; + delete delegate.mousePoints[ curGPoint.id ]; delegate.mousePointCount--; if ( !delegate.mousePointCount ) { dispatchRelease = true; - releasePoint = curPointer.currentPos; + releasePoint = curGPoint.currentPos; } } } - else if ( curPointer.type === 'touch' ) { - updatePointer = delegate.touchPoints[ curPointer.id ] || null; - if ( updatePointer ) { - pressPoint = updatePointer.startPos; - pressTime = updatePointer.startTime; - delete delegate.touchPoints[ curPointer.id ]; + else if ( curGPoint.type === 'touch' ) { + updateGPoint = delegate.touchPoints[ curGPoint.id ] || null; + if ( updateGPoint ) { + pressPoint = updateGPoint.startPos; + pressTime = updateGPoint.startTime; + delete delegate.touchPoints[ curGPoint.id ]; delegate.touchPointCount--; if ( !delegate.touchPointCount ) { dispatchRelease = true; - releasePoint = curPointer.currentPos; + releasePoint = curGPoint.currentPos; } } } - else if ( curPointer.type === 'pen' ) { - updatePointer = delegate.penPoints[ curPointer.id ] || null; - if ( updatePointer ) { - pressPoint = updatePointer.startPos; - pressTime = updatePointer.startTime; - delete delegate.penPoints[ curPointer.id ]; + else if ( curGPoint.type === 'pen' ) { + updateGPoint = delegate.penPoints[ curGPoint.id ] || null; + if ( updateGPoint ) { + pressPoint = updateGPoint.startPos; + pressTime = updateGPoint.startTime; + delete delegate.penPoints[ curGPoint.id ]; delegate.penPointCount--; if ( !delegate.penPointCount ) { dispatchRelease = true; - releasePoint = curPointer.currentPos; + releasePoint = curGPoint.currentPos; } } } else { - updatePointer = null; + updateGPoint = null; } if ( dispatchRelease ) { - if ( updatePointer ) { - insideElementPressed = updatePointer.insideElementPressed; + if ( updateGPoint ) { + insideElementPressed = updateGPoint.insideElementPressed; insideElementReleased = $.pointInElement( tracker.element, releasePoint ); } else { @@ -2320,10 +2502,11 @@ propagate = tracker.releaseHandler( { eventSource: tracker, + pointerType: curGPoint.type, position: getPointRelative( releasePoint, tracker.element ), insideElementPressed: insideElementPressed, insideElementReleased: insideElementReleased, - isTouchEvent: curPointer.type === 'touch', + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2336,18 +2519,19 @@ // Click Gesture if ( insideElementPressed && insideElementReleased && tracker.clickHandler ) { - var time = curPointer.currentTime - pressTime, - distance = pressPoint.distanceTo( curPointer.currentPos ), + var time = curGPoint.currentTime - pressTime, + distance = pressPoint.distanceTo( curGPoint.currentPos ), quick = time <= tracker.clickTimeThreshold && distance <= tracker.clickDistThreshold; propagate = tracker.clickHandler( { eventSource: tracker, - position: getPointRelative( curPointer.currentPos, tracker.element ), + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), quick: quick, shift: event.shiftKey, - isTouchEvent: curPointer.type === 'touch', + isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2366,29 +2550,29 @@ * @private * @inner */ - function cancelPointers( tracker, event, pointers ) { + function cancelPointers( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], i, - pointerCount = pointers.length, - curPointer; + gPointCount = gPoints.length, + curGPoint; - for ( i = 0; i < pointerCount; i++ ) { - curPointer = pointers[ i ]; - if ( curPointer.type === 'mouse' ) { - if ( delegate.mousePoints[ curPointer.id ] ) { - delete delegate.mousePoints[ curPointer.id ]; + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + if ( curGPoint.type === 'mouse' ) { + if ( delegate.mousePoints[ curGPoint.id ] ) { + delete delegate.mousePoints[ curGPoint.id ]; delegate.mousePointCount--; } } - else if ( curPointer.type === 'touch' ) { - if ( delegate.touchPoints[ curPointer.id ] ) { - delete delegate.touchPoints[ curPointer.id ]; + else if ( curGPoint.type === 'touch' ) { + if ( delegate.touchPoints[ curGPoint.id ] ) { + delete delegate.touchPoints[ curGPoint.id ]; delegate.touchPointCount--; } } - else if ( curPointer.type === 'pen' ) { - if ( delegate.penPoints[ curPointer.id ] ) { - delete delegate.penPoints[ curPointer.id ]; + else if ( curGPoint.type === 'pen' ) { + if ( delegate.penPoints[ curGPoint.id ] ) { + delete delegate.penPoints[ curGPoint.id ]; delegate.penPointCount--; } } diff --git a/src/viewer.js b/src/viewer.js index 0620866f..790942d0 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1755,11 +1755,7 @@ function onCanvasDrag( event ) { if( !this.panVertical ){ event.delta.y = 0; } - this.viewport.panBy( - this.viewport.deltaPointsFromPixels( - event.delta.negate() - ) - ); + this.viewport.panBy( this.viewport.deltaPointsFromPixels( event.delta.negate() ), true ); if( this.constrainDuringPan ){ this.viewport.applyConstraints(); } @@ -1856,7 +1852,7 @@ function onCanvasPinch(event) { // lastCenter: // center: // lastDistance: -// currentDistance: +// distance: // originalEvent: // preventDefaultAction: // userData: @@ -1864,8 +1860,8 @@ function onCanvasPinch(event) { if (!event.preventDefaultAction && this.viewport) { var centerPt = this.viewport.pointFromPixel( event.center, true ), lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true ); - this.viewport.zoomBy( event.currentDistance / event.lastDistance, centerPt, true ); - this.viewport.panBy( lastCenterPt.minus( centerPt ), false ); + this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true ); + this.viewport.panBy( lastCenterPt.minus( centerPt ), true ); this.viewport.applyConstraints(); } /** @@ -1879,13 +1875,12 @@ function onCanvasPinch(event) { * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {Number} delta - The pinch delta for the event. * @property {Boolean} shift - True if the shift key was pressed during this event. + * @property {OpenSeadragon.GesturePoint[]} gesturePoints - * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. */ this.raiseEvent('canvas-pinch', { tracker: event.eventSource, - position: event.position, - delta: event.delta, shift: event.shift, originalEvent: event.originalEvent }); From 1475f5865ddb230928af613676ff52ad935a4aa4 Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Sat, 29 Mar 2014 17:02:24 -0700 Subject: [PATCH 06/17] Multi-Touch MouseTracker Update Ready to pull request, merging upstream changes and running tests. --- src/mousetracker.js | 1509 +++++++++++++++++++++--------------------- src/openseadragon.js | 2 +- src/viewer.js | 126 ++-- 3 files changed, 831 insertions(+), 806 deletions(-) diff --git a/src/mousetracker.js b/src/mousetracker.js index 879e1a0a..cc588a14 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -37,45 +37,15 @@ // is any button currently being pressed while mouse events occur var IS_BUTTON_DOWN = false, // is any tracker currently capturing? - IS_CAPTURING = false, + //IS_CAPTURING = false, // dictionary from hash to MouseTracker - ACTIVE = {}, + //ACTIVE = {}, // list of trackers interested in capture - CAPTURING = [], + //CAPTURING = [], // dictionary from hash to private properties THIS = {}; - /** - * Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointing device. - * - * @typedef {Object} GesturePoint - * @memberof OpenSeadragon.MouseTracker - * - * @property {Number} id - * Identifier unique from all other active GesturePoints for a given pointer device. - * @property {String} type - * "mouse", "touch", "pen", or "". - * @property {Boolean} isCaptured - * True if input for the pointer is captured to the tracked element. - * @property {Boolean} insideElementPressed - * True if mouse button pressed or contact point initiated inside the screen area of the tracked element. - * @property {Boolean} insideElement - * True if mouse cursor or contact point is currently inside the screen area of the tracked element. - * @property {OpenSeadragon.Point} startPos - * The initial pointer position, relative to the page including any scrolling. - * @property {Number} startTime - * The initial pointer contact time, in milliseconds. - * @property {OpenSeadragon.Point} lastPos - * The last pointer position, relative to the page including any scrolling. - * @property {Number} lastTime - * The last pointer contact time, in milliseconds. - * @property {OpenSeadragon.Point} currentPos - * The current pointer position, relative to the page including any scrolling. - * @property {Number} currentTime - * The current pointer contact time, in milliseconds. - */ - /** * @class MouseTracker * @classdesc Provides simplified handling of common mouse, touch, and keyboard @@ -116,10 +86,10 @@ * An optional handler for mouse click. * @param {OpenSeadragon.EventHandler} [options.dragHandler=null] * An optional handler for the drag gesture. + * @param {OpenSeadragon.EventHandler} [options.dragEndHandler=null] + * An optional handler for after a drag gesture. * @param {OpenSeadragon.EventHandler} [options.pinchHandler=null] * An optional handler for the pinch gesture. - * @param {OpenSeadragon.EventHandler} [options.swipeHandler=null] - * An optional handler for the swipe gesture. * @param {OpenSeadragon.EventHandler} [options.keyHandler=null] * An optional handler for keypress. * @param {OpenSeadragon.EventHandler} [options.focusHandler=null] @@ -171,8 +141,8 @@ this.scrollHandler = options.scrollHandler || null; this.clickHandler = options.clickHandler || null; this.dragHandler = options.dragHandler || null; + this.dragEndHandler = options.dragEndHandler || null; this.pinchHandler = options.pinchHandler || null; - this.swipeHandler = options.swipeHandler || null; this.stopHandler = options.stopHandler || null; this.keyHandler = options.keyHandler || null; this.focusHandler = options.focusHandler || null; @@ -215,10 +185,10 @@ mousedown: function ( event ) { onMouseDown( _this, event ); }, mouseup: function ( event ) { onMouseUp( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, - mouseupcapturedie: function ( event ) { onMouseUpCapturedIE( _this, event ); }, + //mouseupcapturedie: function ( event ) { onMouseUpCapturedIE( _this, event ); }, mousemove: function ( event ) { onMouseMove( _this, event ); }, mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); }, - mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, + //mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, touchenter: function ( event ) { onTouchEnter( _this, event ); }, touchleave: function ( event ) { onTouchLeave( _this, event ); }, @@ -245,26 +215,16 @@ tracking: false, capturing: false, - // Contact Points - mousePoints: {}, - mousePointCount: 0, - touchPoints: {}, - touchPointCount: 0, - penPoints: {}, - penPointCount: 0, + // Active Contact Points + mousePoints: new $.MouseTracker.GesturePointList(), + touchPoints: new $.MouseTracker.GesturePointList(), + penPoints: new $.MouseTracker.GesturePointList(), // Tracking for pinch gesture pinchGPoints: [], lastPinchDist: 0, currentPinchDist: 0, lastPinchCenter: null, - currentPinchCenter: null, - - //insideElementPressed: false, - //insideElement: false, - //lastPoint: null, - //lastMouseDownTime: null, - //lastMouseDownPoint: null, - //lastPinchDelta: 0, + currentPinchCenter: null }; }; @@ -313,7 +273,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -340,7 +300,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -367,7 +327,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -389,7 +349,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -416,7 +376,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -438,7 +398,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -464,7 +424,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -490,12 +450,16 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. * @param {OpenSeadragon.Point} event.delta - * The x,y components of the difference between start drag and end drag. Usefule for ignoring or weighting the events. + * The x,y components of the difference between the current position and the last drag event position. Useful for ignoring or weighting the events. + * @param {Number} speed + * Current computed speed, in pixels per second. + * @property {Number} direction + * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0. * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent @@ -516,12 +480,14 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. - * @param {OpenSeadragon.Point} event.delta - * The x,y components of the difference between start drag and end drag. Usefule for ignoring or weighting the events. + * @param {Number} speed + * Speed at the end of a drag gesture, in pixels per second. + * @property {Number} direction + * Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0. * @param {Boolean} event.shift * True if the shift key was pressed during this event. * @param {Boolean} event.isTouchEvent @@ -533,6 +499,36 @@ * @param {Object} event.userData * Arbitrary user-defined object. */ + dragEndHandler: 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", or "". + * @param {Array.} event.gesturePoints + * Gesture points associated with the gesture. Velocity data can be found here. + * @param {OpenSeadragon.Point} event.lastCenter + * The previous center point of the two pinch contact points relative to the tracked element. + * @param {OpenSeadragon.Point} event.center + * The center point of the two pinch contact points relative to the tracked element. + * @param {Number} event.lastDistance + * The previous distance between the two pinch contact points in CSS pixels. + * @param {Number} event.distance + * The distance between the two pinch contact points in CSS pixels. + * @param {Boolean} event.shift + * True if the shift key was pressed during this event. + * @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. + */ pinchHandler: function () { }, /** @@ -542,33 +538,7 @@ * @param {Object} event * @param {OpenSeadragon.MouseTracker} event.eventSource * A reference to the tracker instance. - * @param {String} pointerType - * "mouse", "touch", "pen", or "". - * @param {OpenSeadragon.Point} event.position - * The position of the event relative to the tracked element. - * @param {OpenSeadragon.Point} event.delta - * The x,y components of the difference between start drag and end drag. Usefule for ignoring or weighting the events. - * @param {Boolean} event.shift - * True if the shift key was pressed during this event. - * @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. - */ - swipeHandler: 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} pointerType + * @param {String} event.pointerType * "mouse", "touch", "pen", or "". * @param {OpenSeadragon.Point} event.position * The position of the event relative to the tracked element. @@ -637,6 +607,99 @@ }; + /** + * @member gesturePointVelocityTracker + * @memberof OpenSeadragon.MouseTracker + * @private + * + * Provides continuous computation of velocity (speed and direction) of active pointers. + * + * This is a singleton, used by all MouseTracker instances. Currently it is extremely unlikely there will ever be more than + * two active gesture pointers at a time. + */ + $.MouseTracker.gesturePointVelocityTracker = (function () { + var trackerPoints = [], + intervalId = 0, + lastTime = 0; + + // Generates a unique identifier for a tracked gesture point + var _generateGuid = function ( tracker, gPoint ) { + return tracker.hash.toString() + gPoint.type + gPoint.id.toString(); + }; + + // Interval timer callback. Computes velocity for all tracked gesture points. + var _doTracking = function () { + var i, + len = trackerPoints.length, + trackPoint, + gPoint, + now = $.now(), + elapsedTime, + distance, + speed; + + elapsedTime = now - lastTime; + lastTime = now; + + for ( i = 0; i < len; i++ ) { + trackPoint = trackerPoints[ i ]; + gPoint = trackPoint.gPoint; + // Math.atan2 gives us just what we need for a velocity vector, as we can simply + // use cos()/sin() to extract the x/y velocity components. + gPoint.direction = Math.atan2( gPoint.currentPos.y - trackPoint.lastPos.y, gPoint.currentPos.x - trackPoint.lastPos.x ); + // speed = distance / elapsed time + distance = trackPoint.lastPos.distanceTo( gPoint.currentPos ); + trackPoint.lastPos = gPoint.currentPos; + speed = 1000 * distance / ( elapsedTime + 1 ); + // Simple biased average, favors the most recent speed computation. Smooths out erratic gestures a bit. + gPoint.speed = 0.75 * speed + 0.25 * gPoint.speed; + } + }; + + // Public. Add a gesture point to be tracked + var addPoint = function ( tracker, gPoint ) { + var guid = _generateGuid( tracker, gPoint ); + + trackerPoints.push( + { + guid: guid, + gPoint: gPoint, + lastPos: gPoint.currentPos + } ); + + // Only fire up the interval timer when there's gesture points to track + if ( trackerPoints.length === 1 ) { + lastTime = $.now(); + intervalId = window.setInterval( _doTracking, 50 ); + } + }; + + // Public. Stop tracking a gesture point + var removePoint = function ( tracker, gPoint ) { + var guid = _generateGuid( tracker, gPoint ), + i, + 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--; + if ( len === 0 ) { + window.clearInterval( intervalId ); + } + break; + } + } + }; + + return { + addPoint: addPoint, + removePoint: removePoint + }; + } )(); + + /** * Detect available mouse wheel event name. */ @@ -646,7 +709,7 @@ 'DOMMouseScroll'; // Assume old Firefox /** - * Detect browser pointer device event model and build appropriate list of events to subscribe to. + * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to. */ $.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; @@ -678,10 +741,18 @@ } } else { + // Standard 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) - $.MouseTracker.subscribeEvents.push( "touchenter", "touchleave", "touchstart", "touchend", "touchmove", "touchcancel" ); + $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" ); + if ( 'ontouchenter' in window ) { + $.MouseTracker.subscribeEvents.push( "touchenter", "touchleave" ); + $.MouseTracker.haveTouchEnter = true; + } + else { + $.MouseTracker.haveTouchEnter = false; + } } if ( 'ongesturestart' in window ) { // iOS (see https://developer.apple.com/library/safari/documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html) @@ -693,6 +764,108 @@ } +//******************************************************************************************************************************************* +//** Utility Types/Classes + + + /** + * Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointing device. + * + * @typedef {Object} GesturePoint + * @memberof OpenSeadragon.MouseTracker + * + * @property {Number} id + * Identifier unique from all other active GesturePoints for a given pointer device. + * @property {String} type + * "mouse", "touch", "pen", or "". + * @property {Boolean} insideElementPressed + * True if mouse button pressed or contact point initiated inside the screen area of the tracked element. + * @property {Boolean} insideElement + * True if mouse cursor or contact point is currently inside the screen area of the tracked element. + * @property {Number} speed + * Continuously computed speed, in pixels per second. + * @property {Number} direction + * Continuously computed direction, in radians. Only valid if speed > 0. + * @property {OpenSeadragon.Point} startPos + * The initial pointer position, relative to the page including any scrolling. + * @property {Number} startTime + * The initial pointer contact time, in milliseconds. + * @property {OpenSeadragon.Point} lastPos + * The last pointer position, relative to the page including any scrolling. + * @property {Number} lastTime + * The last pointer contact time, in milliseconds. + * @property {OpenSeadragon.Point} currentPos + * The current pointer position, relative to the page including any scrolling. + * @property {Number} currentTime + * The current pointer contact time, in milliseconds. + */ + + + /** + * @class GesturePointList + * @classdesc Provides an abstraction for a set of {@link OpenSeadragon.MouseTracker.GesturePoint} objects. + * @memberof OpenSeadragon.MouseTracker + */ + $.MouseTracker.GesturePointList = function () { + this._gPoints = []; + }; + $.MouseTracker.GesturePointList.prototype = /** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */{ + /** + * @function + * @returns {Number} Number of gesture points in the list. + */ + getLength: function () { + return this._gPoints.length; + }, + /** + * @function + * @returns {Array.} The list of gesture points in the list as an array (read-only). + */ + asArray: function () { + return this._gPoints; + }, + /** + * @function + * @param {OpenSeadragon.MouseTracker.GesturePoint} gesturePoint - A gesture point to add to the list. + * @returns {Number} Number of gesture points in the list. + */ + add: function ( gp ) { + return this._gPoints.push( gp ); + }, + /** + * @function + * @param {Number} id - The id of the gesture point to remove from the list. + * @returns {Number} Number of gesture points in the list. + */ + removeById: function ( id ) { + var i, + len = this._gPoints.length; + for ( i = 0; i < len; i++ ) { + if ( this._gPoints[ i ].id === id ) { + this._gPoints.splice( i, 1 ); + break; + } + } + return this._gPoints.length; + }, + /** + * @function + * @param {Number} id - The id of the gesture point to retrieve from the list. + * @returns {OpenSeadragon.MouseTracker.GesturePoint|null} The gesture point with the given id, or null if not found. + */ + getById: function ( id ) { + var i, + len = this._gPoints.length; + for ( i = 0; i < len; i++ ) { + if ( this._gPoints[ i ].id === id ) { + return this._gPoints[ i ]; + } + } + return null; + } + }; + + //******************************************************************************************************************************************* //** Utility Functions @@ -718,7 +891,7 @@ ); } delegate.tracking = true; - ACTIVE[ tracker.hash ] = tracker; + //ACTIVE[ tracker.hash ] = tracker; } } @@ -745,7 +918,7 @@ releaseMouse( tracker ); delegate.tracking = false; - delete ACTIVE[ tracker.hash ]; + //delete ACTIVE[ tracker.hash ]; } } @@ -766,26 +939,26 @@ 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 ( $.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 { $.addEvent( window, "mouseup", @@ -798,7 +971,7 @@ delegate.mousemovecaptured, true ); - } + //} delegate.capturing = true; } } @@ -813,26 +986,26 @@ 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 ( $.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 { $.removeEvent( window, "mousemove", @@ -845,7 +1018,7 @@ delegate.mouseupcaptured, true ); - } + //} delegate.capturing = false; } } @@ -865,6 +1038,29 @@ //} + /** + * @private + * @inner + */ + function getGPointsListByType( tracker, type ) { + var delegate = THIS[ tracker.hash ], + list; + if ( type === 'mouse' ) { + list = delegate.mousePoints; + } + else if ( type === 'touch' ) { + list = delegate.touchPoints; + } + else if ( type === 'pen' ) { + list = delegate.penPoints; + } + else { + list = null; + } + return list; + } + + /** * @private * @inner @@ -1076,7 +1272,7 @@ position, gPoint; - event = $.getEvent(event); + event = $.getEvent( event ); time = $.now(); position = getMouseAbsolute( event ); @@ -1084,9 +1280,9 @@ gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //isCaptured: true, //insideElementPressed: true, insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1095,7 +1291,7 @@ currentTime: time }; - updatePointersOver( tracker, event, [gPoint] ); + updatePointersOver( tracker, event, [ gPoint ] ); } @@ -1108,7 +1304,7 @@ position, gPoint; - event = $.getEvent(event); + event = $.getEvent( event ); var eventOrTouchPoint = event;//isTouch ? event.touches[ 0 ] : event; @@ -1118,9 +1314,9 @@ gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //isCaptured: true, //insideElementPressed: true, insideElement: false, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1129,7 +1325,7 @@ currentTime: time }; - updatePointersOut( tracker, event, [gPoint] ); + updatePointersOut( tracker, event, [ gPoint ] ); } @@ -1143,7 +1339,7 @@ position, gPoint; - event = $.getEvent(event); + event = $.getEvent( event ); if ( event.button == 2 ) { return; @@ -1155,33 +1351,33 @@ gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - isCaptured: true, insideElementPressed: true, insideElement: true, - startPos: position, - startTime: time, - lastPos: position, - lastTime: time, + //speed: 0, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, currentPos: position, currentTime: time }; - addPointers( tracker, event, [gPoint] ); + addPointers( tracker, event, [ gPoint ] ); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { + if ( tracker.pressHandler || tracker.dragHandler ) { $.cancelEvent( event ); } - if ( !( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) || - !IS_CAPTURING ) { + //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 ); - } + //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 ); + //} } @@ -1205,9 +1401,9 @@ */ function onMouseUpCaptured( tracker, event ) { var delegate = THIS[ tracker.hash ], - gPoint = delegate.mousePoints[ $.MouseTracker.mousePointerId ] || null; + gPoint = delegate.mousePoints.getById( $.MouseTracker.mousePointerId ); - if ( !gPoint.insideElement ) { + if ( gPoint && !gPoint.insideElement ) { handleMouseUp( tracker, event ); } @@ -1225,32 +1421,32 @@ * @private * @inner */ - function onMouseUpCapturedIE( tracker, event ) { - var othertracker, - i; + //function onMouseUpCapturedIE( tracker, event ) { + // var othertracker, + // i; - event = $.getEvent( event ); + // event = $.getEvent( event ); - if ( event.button == 2 ) { - return; - } + // if ( event.button == 2 ) { + // return; + // } - for ( i = 0; i < CAPTURING.length; i++ ) { - othertracker = CAPTURING[ i ]; - if ( !hasMouse( othertracker ) ) { - handleMouseUp( othertracker, event ); - } - } + // 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 ) - ); + // releaseMouse( tracker ); + // IS_CAPTURING = false; + // event.srcElement.fireEvent( + // "on" + event.type, + // document.createEventObject( event ) + // ); - $.stopEvent( event ); - } + // $.stopEvent( event ); + //} /** @@ -1280,14 +1476,14 @@ * @private * @inner */ - function onMouseMoveCapturedIE( tracker, event ) { - var i; - for ( i = 0; i < CAPTURING.length; i++ ) { - handleMouseMove( CAPTURING[ i ], event ); - } - - $.stopEvent( event ); - } + //function onMouseMoveCapturedIE( tracker, event ) { + // var i; + // for ( i = 0; i < CAPTURING.length; i++ ) { + // handleMouseMove( CAPTURING[ i ], event ); + // } + // + // $.stopEvent( event ); + //} /** @@ -1309,9 +1505,9 @@ gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //isCaptured: false, //insideElementPressed: true, insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1344,9 +1540,9 @@ gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //isCaptured: false, //insideElementPressed: true, insideElement: false, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1375,13 +1571,13 @@ time = $.now(); - if ( touchCount > 0 && delegate.touchPointCount === 0 ) { + if ( !$.MouseTracker.haveTouchEnter && touchCount > 0 && delegate.touchPoints.getLength() === 0 ) { gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', - //isCaptured: false, //insideElementPressed: true, insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1389,7 +1585,7 @@ currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; - updatePointersOver( tracker, event, [gPoint] ); + updatePointersOver( tracker, event, [ gPoint ] ); } for ( i = 0; i < touchCount; i++ ) { @@ -1398,13 +1594,13 @@ gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - isCaptured: false, insideElementPressed: true, insideElement: true, - startPos: position, - startTime: time, - lastPos: position, - lastTime: time, + //speed: 0, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, currentPos: position, currentTime: time } ); @@ -1412,41 +1608,11 @@ addPointers( tracker, event, gPoints ); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.stopEvent(event); - $.cancelEvent(event); + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler ) { + $.stopEvent( event ); + $.cancelEvent( event ); return false; } -////**************************************************************** -// var touchA, -// touchB; - -// if ( event.touches.length == 1 && -// event.targetTouches.length == 1 && -// event.changedTouches.length == 1 ) { - -// THIS[ tracker.hash ].lastTouch = event.touches[ 0 ]; -// handlePointerOver( tracker, event, getMouseRelative( event.changedTouches[ 0 ], tracker.element ) ); -// // call with no capture as the onMouseMoveCaptured will -// // be triggered by onTouchMove -// onMouseDown( tracker, event, true, true ); -// } - -// if ( event.touches.length == 2 ) { - -// touchA = getMouseAbsolute( event.touches[ 0 ] ); -// touchB = getMouseAbsolute( event.touches[ 1 ] ); -// THIS[ tracker.hash ].lastPinchDelta = -// Math.abs( touchA.x - touchB.x ) + -// Math.abs( touchA.y - touchB.y ); -// THIS[ tracker.hash ].pinchMidpoint = new $.Point( -// ( touchA.x + touchB.x ) / 2, -// ( touchA.y + touchB.y ) / 2 -// ); -// //$.console.debug("pinch start : "+THIS[ tracker.hash ].lastPinchDelta); -// } - -// event.preventDefault(); } @@ -1471,9 +1637,9 @@ gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1485,13 +1651,13 @@ removePointers( tracker, event, gPoints ); - if ( touchCount > 0 && delegate.touchPointCount === 0 ) { + if ( !$.MouseTracker.haveTouchEnter && touchCount > 0 && delegate.touchPoints.getLength() === 0 ) { gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', - //isCaptured: false, //insideElementPressed: true, insideElement: false, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1499,32 +1665,14 @@ currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; - updatePointersOut( tracker, event, [gPoint] ); + updatePointersOut( tracker, event, [ gPoint ] ); } - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.stopEvent(event); - $.cancelEvent(event); + if ( tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { + $.stopEvent( event ); + $.cancelEvent( event ); return false; } -//**************************************************************************************** -// if ( event.touches.length === 0 && -// event.targetTouches.length === 0 && -// event.changedTouches.length == 1 ) { - -// THIS[ tracker.hash ].lastTouch = null; - -// // call with no release, as the mouse events are -// // not registered in onTouchStart -// onMouseUpCaptured( tracker, event, true, true ); -// handlePointerOut( tracker, event, getMouseRelative( event.changedTouches[ 0 ], tracker.element ) ); -// } -// if ( event.touches.length + event.changedTouches.length == 2 ) { -// THIS[ tracker.hash ].lastPinchDelta = null; -// THIS[ tracker.hash ].pinchMidpoint = null; -// //$.console.debug("pinch end"); -// } -// event.preventDefault(); } @@ -1547,9 +1695,9 @@ gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1561,60 +1709,11 @@ updatePointers( tracker, event, gPoints ); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.stopEvent(event); - $.cancelEvent(event); + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler ) { + $.stopEvent( event ); + $.cancelEvent( event ); return false; } -//******************************************************************************* -// var touchA, -// touchB, -// pinchDelta; - -// if ( !THIS[ tracker.hash ].lastTouch ) { -// return; -// } - -// if ( event.touches.length === 1 && -// event.targetTouches.length === 1 && -// event.changedTouches.length === 1 && -// THIS[ tracker.hash ].lastTouch.identifier === event.touches[ 0 ].identifier ) { - -// onMouseMoveCaptured( tracker, event, true ); - -// } else if ( event.touches.length === 2 ) { - -// touchA = getMouseAbsolute( event.touches[ 0 ] ); -// touchB = getMouseAbsolute( event.touches[ 1 ] ); -// pinchDelta = -// Math.abs( touchA.x - touchB.x ) + -// Math.abs( touchA.y - touchB.y ); - -// ////TODO: make the 75px pinch threshold configurable -// //if ( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ) { -// // //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); - -// // // Simulate a 'wheel' event -// // var simulatedEvent = { -// // target: event.target || event.srcElement, -// // type: "wheel", -// // shiftKey: event.shiftKey || false, -// // clientX: THIS[ tracker.hash ].pinchMidpoint.x, -// // clientY: THIS[ tracker.hash ].pinchMidpoint.y, -// // pageX: THIS[ tracker.hash ].pinchMidpoint.x, -// // pageY: THIS[ tracker.hash ].pinchMidpoint.y, -// // deltaMode: 1, // 0=pixel, 1=line, 2=page -// // deltaX: 0, -// // deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1, -// // deltaZ: 0 -// // }; - -// // handleWheelEvent( tracker, simulatedEvent, event, true ); - -// // THIS[ tracker.hash ].lastPinchDelta = pinchDelta; -// //} -// } -// event.preventDefault(); } @@ -1637,9 +1736,9 @@ gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1690,9 +1789,9 @@ gPoint = { id: event.pointerId, type: getPointerType( event ), - //isCaptured: false, //insideElementPressed: true, insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1701,7 +1800,7 @@ currentTime: time }; - updatePointersOver( tracker, event, [gPoint] ); + updatePointersOver( tracker, event, [ gPoint ] ); } @@ -1720,9 +1819,9 @@ gPoint = { id: event.pointerId, type: getPointerType( event ), - //isCaptured: false, //insideElementPressed: true, insideElement: false, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1731,50 +1830,10 @@ currentTime: time }; - updatePointersOut( tracker, event, [gPoint] ); + updatePointersOut( tracker, event, [ gPoint ] ); } -//$.MouseTracker.mousePointerId = "legacy-mouse"; -//$.MouseTracker.unprefixedPointerEvents = false; -//$.MouseTracker.maxTouchPoints = 10; -// function addPointers( tracker, event, gPoints ) { -// } -// function updatePointersOver( tracker, event, gPoints ) { -// } -// function updatePointersOut( tracker, event, gPoints ) { -// } -// function updatePointers( tracker, event, gPoints ) { -// } -// function removePointers( tracker, event, gPoints ) { -// } -// function cancelPointers( tracker, event, gPoints ) { -// } -//pointer = { -// id: x, // getPointerType( event ) -// type: '', // 'mouse', 'touch', 'pen', '' -// isCaptured: false, -// insideElementPressed: true, -// insideElement: true, -// startPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// startTime: 0xFFFFFFFF, -// lastPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// lastTime: 0xFFFFFFFF, -// currentPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// currentTime: 0xFFFFFFFF, -//} -//var delegate = THIS[ tracker.hash ] -//delegate.mousePoints: {}, -//delegate.mousePointCount: 0, -//delegate.touchPoints: {}, -//delegate.touchPointCount: 0, -//delegate.penPoints: {}, -//delegate.penPointCount: 0 -// -//var touchPoints = {}; -//touchPoints[event.pointerId] = "test"; -//touchPoints[12345] = "test12345"; -//delete touchPoints[event.pointerId]; /** * @private * @inner @@ -1790,10 +1849,10 @@ } if ( $.MouseTracker.unprefixedPointerEvents ) { - event.currentTarget.setPointerCapture(event.pointerId); + event.currentTarget.setPointerCapture( event.pointerId ); } else { - event.currentTarget.msSetPointerCapture(event.pointerId); + event.currentTarget.msSetPointerCapture( event.pointerId ); } time = $.now(); @@ -1802,22 +1861,22 @@ gPoint = { id: event.pointerId, type: getPointerType( event ), - isCaptured: true, insideElementPressed: true, insideElement: true, - startPos: position, - startTime: time, - lastPos: position, - lastTime: time, + //speed: 0, + //startPos: position, + //startTime: time, + //lastPos: position, + //lastTime: time, currentPos: position, currentTime: time }; - addPointers( tracker, event, [gPoint] ); + addPointers( tracker, event, [ gPoint ] ); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.stopEvent(event); - $.cancelEvent(event); + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler ) { + $.stopEvent( event ); + $.cancelEvent( event ); return false; } } @@ -1838,10 +1897,10 @@ } if ( $.MouseTracker.unprefixedPointerEvents ) { - event.currentTarget.releasePointerCapture(event.pointerId); + event.currentTarget.releasePointerCapture( event.pointerId ); } else { - event.currentTarget.msReleasePointerCapture(event.pointerId); + event.currentTarget.msReleasePointerCapture( event.pointerId ); } time = $.now(); @@ -1850,9 +1909,9 @@ gPoint = { id: event.pointerId, type: getPointerType( event ), - isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1861,11 +1920,11 @@ currentTime: time }; - removePointers(tracker, event, [gPoint]); + removePointers(tracker, event, [ gPoint ]); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.stopEvent(event); - $.cancelEvent(event); + if ( tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { + $.stopEvent( event ); + $.cancelEvent( event ); return false; } } @@ -1887,9 +1946,9 @@ gPoint = { id: event.pointerId, type: getPointerType( event ), - //isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1898,11 +1957,11 @@ currentTime: time }; - updatePointers(tracker, event, [gPoint]); + updatePointers(tracker, event, [ gPoint ]); - if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler || tracker.swipeHandler ) { - $.stopEvent(event); - $.cancelEvent(event); + if ( tracker.pressHandler || tracker.dragHandler || tracker.pinchHandler ) { + $.stopEvent( event ); + $.cancelEvent( event ); return false; } } @@ -1923,9 +1982,9 @@ gPoint = { id: event.pointerId, type: getPointerType( event ), - //isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -1934,7 +1993,7 @@ //currentTime: time }; - cancelPointers( tracker, event, [gPoint] ); + cancelPointers( tracker, event, [ gPoint ] ); } @@ -1990,7 +2049,7 @@ position, gPoint; - event = $.getEvent(event); + event = $.getEvent( event ); time = $.now(); position = getMouseAbsolute( event ); @@ -1998,9 +2057,9 @@ gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -2009,7 +2068,7 @@ currentTime: time }; - updatePointers( tracker, event, [gPoint] ); + updatePointers( tracker, event, [ gPoint ] ); } @@ -2022,7 +2081,7 @@ position, gPoint; - event = $.getEvent(event); + event = $.getEvent( event ); if ( event.button == 2 ) { return; @@ -2034,9 +2093,9 @@ gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - isCaptured: false, //insideElementPressed: true, //insideElement: true, + //speed: 0, //startPos: position, //startTime: time, //lastPos: position, @@ -2045,7 +2104,7 @@ currentTime: time }; - removePointers( tracker, event, [gPoint] ); + removePointers( tracker, event, [ gPoint ] ); } @@ -2068,47 +2127,6 @@ } -//$.MouseTracker.mousePointerId = "legacy-mouse"; -//$.MouseTracker.unprefixedPointerEvents = false; -//$.MouseTracker.maxTouchPoints = 10; -// function addPointers( tracker, event, gPoints ) { -// } -// function updatePointersOver( tracker, event, gPoints ) { -// } -// function updatePointersOut( tracker, event, gPoints ) { -// } -// function updatePointers( tracker, event, gPoints ) { -// } -// function removePointers( tracker, event, gPoints ) { -// } -// function cancelPointers( tracker, event, gPoints ) { -// } -//pointer = { -// id: x, // getPointerType( event ) -// type: '', // 'mouse', 'touch', 'pen', '' -// isCaptured: false, -// insideElementPressed: true, -// insideElement: true, -// startPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// startTime: 0xFFFFFFFF, -// lastPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// lastTime: 0xFFFFFFFF, -// currentPos: null, // $.Point getMouseAbsolute( eventOrTouchPoint ); getPointRelative( point, tracker.element ) -// currentTime: 0xFFFFFFFF, -//} -//var delegate = THIS[ tracker.hash ] -//delegate.mousePoints: {}, -//delegate.mousePointCount: 0, -//delegate.touchPoints: {}, -//delegate.touchPointCount: 0, -//delegate.penPoints: {}, -//delegate.penPointCount: 0 -// -//var touchPoints = {}; -//touchPoints[event.pointerId] = "test"; -//touchPoints[12345] = "test12345"; -//delete touchPoints[event.pointerId]; - /** * @private * @inner @@ -2116,60 +2134,56 @@ function addPointers( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], propagate, - dispatchPress = false, + pointsList = getGPointsListByType( tracker, gPoints[ 0 ].type ), + pointsListLength, i, gPointCount = gPoints.length, curGPoint; - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - if ( curGPoint.type === 'mouse' ) { - if ( !delegate.mousePointCount ) { - delegate.mousePoints[ curGPoint.id ] = curGPoint; - delegate.mousePointCount++; - dispatchPress = true; - } - } - else if ( curGPoint.type === 'touch' ) { - if ( !delegate.touchPointCount ) { - dispatchPress = true; - } - if ( !delegate.touchPoints[ curGPoint.id ] ) { - delegate.touchPoints[ curGPoint.id ] = curGPoint; - delegate.touchPointCount++; - if ( delegate.touchPointCount == 2 && tracker.pinchHandler ) { - // Initialize for pinch gesture tracking - delegate.pinchGPoints = []; - for ( var p in delegate.touchPoints ) { - delegate.pinchGPoints.push( delegate.touchPoints[ p ] ); - } - delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGPoints[0].currentPos.distanceTo( delegate.pinchGPoints[1].currentPos ); - delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[0].currentPos, delegate.pinchGPoints[1].currentPos ); - } - } - } - else if ( curGPoint.type === 'pen' ) { - if ( !delegate.penPointCount ) { - delegate.penPoints[ curGPoint.id ] = curGPoint; - delegate.penPointCount++; - dispatchPress = true; - } - } + if ( pointsList ) { + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; - if ( dispatchPress && tracker.pressHandler ) { - propagate = tracker.pressHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: getPointRelative( curGPoint.startPos, tracker.element ), - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + // Initialize for drag/swipe/pinch + curGPoint.speed = 0; + curGPoint.direction = 0; + curGPoint.startPos = curGPoint.currentPos; + curGPoint.startTime = curGPoint.currentTime; + curGPoint.lastPos = curGPoint.currentPos; + curGPoint.lastTime = curGPoint.currentTime; + + if ( tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { + $.MouseTracker.gesturePointVelocityTracker.addPoint( tracker, curGPoint ); + } + + pointsListLength = pointsList.add( curGPoint ); + + if ( pointsListLength == 1 ) { + // Press + if ( tracker.pressHandler ) { + propagate = tracker.pressHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.startPos, tracker.element ), + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + else if ( pointsListLength == 2 ) { + if ( tracker.pinchHandler && curGPoint.type === 'touch' ) { + // Initialize for pinch + delegate.pinchGPoints = pointsList.asArray(); + delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGPoints[ 0 ].currentPos.distanceTo( delegate.pinchGPoints[ 1 ].currentPos ); + delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[ 0 ].currentPos, delegate.pinchGPoints[ 1 ].currentPos ); } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -2182,6 +2196,7 @@ */ function updatePointersOver( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], + pointsList = getGPointsListByType( tracker, gPoints[ 0 ].type ), i, gPointCount = gPoints.length, curGPoint, @@ -2189,49 +2204,41 @@ insideElementPressed, propagate; - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - if ( curGPoint.type === 'mouse' ) { - updateGPoint = delegate.mousePoints[ curGPoint.id ] || null; - } - else if ( curGPoint.type === 'touch' ) { - updateGPoint = delegate.touchPoints[ curGPoint.id ] || null; - } - else if ( curGPoint.type === 'pen' ) { - updateGPoint = delegate.penPoints[ curGPoint.id ] || null; - } - else { - updateGPoint = null; - } + if ( pointsList ) { + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = pointsList.getById( curGPoint.id ); - if ( updateGPoint ) { - updateGPoint.insideElement = true; - updateGPoint.lastPos = updateGPoint.currentPos; - updateGPoint.lastTime = updateGPoint.currentTime; - updateGPoint.currentPos = curGPoint.currentPos; - updateGPoint.currentTime = curGPoint.currentTime; - insideElementPressed = updateGPoint.insideElementPressed; - } - else { - insideElementPressed = false; - } + if ( updateGPoint ) { + updateGPoint.insideElement = true; + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; + insideElementPressed = updateGPoint.insideElementPressed; + } + else { + insideElementPressed = false; + } - if ( tracker.enterHandler ) { - propagate = tracker.enterHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), - insideElementPressed: insideElementPressed, - buttonDownAny: IS_BUTTON_DOWN, - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + // Enter + if ( tracker.enterHandler ) { + propagate = tracker.enterHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), + insideElementPressed: insideElementPressed, + buttonDownAny: IS_BUTTON_DOWN, + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -2244,6 +2251,7 @@ */ function updatePointersOut( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], + pointsList = getGPointsListByType( tracker, gPoints[ 0 ].type ), i, gPointCount = gPoints.length, curGPoint, @@ -2251,50 +2259,42 @@ insideElementPressed, propagate; - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - if ( curGPoint.type === 'mouse' ) { - updateGPoint = delegate.mousePoints[ curGPoint.id ] || null; - } - else if ( curGPoint.type === 'touch' ) { - updateGPoint = delegate.touchPoints[ curGPoint.id ] || null; - } - else if ( curGPoint.type === 'pen' ) { - updateGPoint = delegate.penPoints[ curGPoint.id ] || null; - } - else { - updateGPoint = null; - } + if ( pointsList ) { + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = pointsList.getById( curGPoint.id ); - if ( updateGPoint ) { - updateGPoint.insideElement = false; - updateGPoint.lastPos = updateGPoint.currentPos; - updateGPoint.lastTime = updateGPoint.currentTime; - updateGPoint.currentPos = curGPoint.currentPos; - updateGPoint.currentTime = curGPoint.currentTime; - insideElementPressed = updateGPoint.insideElementPressed; - } - else { - insideElementPressed = false; - } + if ( updateGPoint ) { + updateGPoint.insideElement = false; + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; + insideElementPressed = updateGPoint.insideElementPressed; + } + else { + insideElementPressed = false; + } - if ( tracker.exitHandler ) { - propagate = tracker.exitHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), - insideElementPressed: insideElementPressed, - buttonDownAny: IS_BUTTON_DOWN, - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + // Exit + if ( tracker.exitHandler ) { + propagate = tracker.exitHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), + insideElementPressed: insideElementPressed, + buttonDownAny: IS_BUTTON_DOWN, + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + + if ( propagate === false ) { + $.cancelEvent( event ); } - ); - - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -2308,52 +2308,72 @@ function updatePointers( tracker, event, gPoints ) { // Pointer(s) changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) var delegate = THIS[ tracker.hash ], + pointsList = getGPointsListByType( tracker, gPoints[ 0 ].type ), + pointsListLength, i, gPointCount = gPoints.length, curGPoint, updateGPoint, - points, - pointCount, + gPointArray, delta, propagate; - if ( gPoints[ 0 ].type === 'mouse' ) { - points = delegate.mousePoints; - pointCount = delegate.mousePointCount; - } - else if ( gPoints[ 0 ].type === 'touch' ) { - points = delegate.touchPoints; - pointCount = delegate.touchPointCount; - } - else if ( gPoints[ 0 ].type === 'pen' ) { - points = delegate.penPoints; - pointCount = delegate.penPointCount; - } - else { - points = null; - } + if ( pointsList ) { + pointsListLength = pointsList.getLength(); - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - updateGPoint = points ? ( points[ curGPoint.id ] || null ) : null; + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; + updateGPoint = pointsList.getById( curGPoint.id ); - if ( updateGPoint ) { - updateGPoint.lastPos = updateGPoint.currentPos; - updateGPoint.lastTime = updateGPoint.currentTime; - updateGPoint.currentPos = curGPoint.currentPos; - updateGPoint.currentTime = curGPoint.currentTime; + if ( updateGPoint ) { + updateGPoint.lastPos = updateGPoint.currentPos; + updateGPoint.lastTime = updateGPoint.currentTime; + updateGPoint.currentPos = curGPoint.currentPos; + updateGPoint.currentTime = curGPoint.currentTime; + } - // Drag Gesture - if ( pointCount == 1 && tracker.dragHandler && !updateGPoint.currentPos.equals( updateGPoint.lastPos ) ) { + //if ( tracker.stopHandler ) { + // clearTimeout( tracker.stopTimeOut ); + // tracker.stopTimeOut = setTimeout( function() { + // handlePointerStop( tracker, event ); + // }, tracker.stopDelay ); + //} + } + + if ( pointsListLength == 1 ) { + // Move (1 contact) + if ( tracker.moveHandler ) { + updateGPoint = pointsList.asArray()[ 0 ]; + propagate = tracker.moveHandler( + { + eventSource: tracker, + pointerType: updateGPoint.type, + position: getPointRelative( updateGPoint.currentPos, tracker.element ), + isTouchEvent: updateGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + + // Drag + if ( tracker.dragHandler ) { + updateGPoint = pointsList.asArray()[ 0 ]; delta = updateGPoint.currentPos.minus( updateGPoint.lastPos ); propagate = tracker.dragHandler( { eventSource: tracker, - pointerType: curGPoint.type, + pointerType: updateGPoint.type, position: getPointRelative( updateGPoint.currentPos, tracker.element ), delta: delta, + speed: updateGPoint.speed, + direction: updateGPoint.direction, shift: event.shiftKey, - isTouchEvent: curGPoint.type === 'touch', + isTouchEvent: updateGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, userData: tracker.userData @@ -2364,58 +2384,53 @@ } } } - - if ( pointCount == 1 && tracker.moveHandler ) { - propagate = tracker.moveHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + else if ( pointsListLength == 2 ) { + // Move (2 contacts, use center) + if ( tracker.moveHandler ) { + gPointArray = pointsList.asArray(); + propagate = tracker.moveHandler( + { + eventSource: tracker, + pointerType: gPointArray[ 0 ].type, + position: getPointRelative( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ), + isTouchEvent: gPointArray[ 0 ].type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } - } - //if ( tracker.stopHandler ) { - // clearTimeout( tracker.stopTimeOut ); - // tracker.stopTimeOut = setTimeout( function() { - // handlePointerStop( tracker, event ); - // }, tracker.stopDelay ); - //} - } - // Pinch Gesture - if ( gPoints[ 0 ].type === 'touch' && delegate.touchPointCount == 2 && tracker.pinchHandler ) { - //gesturePoints = []; - //for ( var p in delegate.touchPoints ) { - // gesturePoints.push( delegate.touchPoints[ p ] ); - //} - delta = delegate.pinchGPoints[0].currentPos.distanceTo( delegate.pinchGPoints[1].currentPos ); - if ( delta != delegate.currentPinchDist ) { - delegate.lastPinchDist = delegate.currentPinchDist; - delegate.currentPinchDist = delta; - delegate.lastPinchCenter = delegate.currentPinchCenter; - delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[0].currentPos, delegate.pinchGPoints[1].currentPos ); - propagate = tracker.pinchHandler( - { - eventSource: tracker, - gesturePoints: delegate.pinchGPoints, - lastCenter: getPointRelative( delegate.lastPinchCenter, tracker.element ), - center: getPointRelative( delegate.currentPinchCenter, tracker.element ), - lastDistance: delegate.lastPinchDist, - distance: delegate.currentPinchDist, - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + // Pinch + if ( tracker.pinchHandler && gPoints[ 0 ].type === 'touch' ) { + delta = delegate.pinchGPoints[ 0 ].currentPos.distanceTo( delegate.pinchGPoints[ 1 ].currentPos ); + if ( delta != delegate.currentPinchDist ) { + delegate.lastPinchDist = delegate.currentPinchDist; + delegate.currentPinchDist = delta; + delegate.lastPinchCenter = delegate.currentPinchCenter; + delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[ 0 ].currentPos, delegate.pinchGPoints[ 1 ].currentPos ); + propagate = tracker.pinchHandler( + { + eventSource: tracker, + pointerType: 'touch', + gesturePoints: delegate.pinchGPoints, + lastCenter: getPointRelative( delegate.lastPinchCenter, tracker.element ), + center: getPointRelative( delegate.currentPinchCenter, tracker.element ), + lastDistance: delegate.lastPinchDist, + distance: delegate.currentPinchDist, + shift: event.shiftKey, + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -2428,117 +2443,116 @@ */ function removePointers( tracker, event, gPoints ) { var delegate = THIS[ tracker.hash ], + pointsList = getGPointsListByType( tracker, gPoints[ 0 ].type ), + pointsListLength, propagate, - //were we inside the tracked element when we were pressed insideElementPressed, - //are we still inside the tracked element when we released insideElementReleased, - dispatchRelease, - pressPoint, - pressTime, releasePoint, + releaseTime, i, gPointCount = gPoints.length, curGPoint, - updateGPoint; + updateGPoint, + removedGPoint; - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - dispatchRelease = false; - if ( curGPoint.type === 'mouse' ) { - updateGPoint = delegate.mousePoints[ curGPoint.id ] || null; - if ( updateGPoint ) { - pressPoint = updateGPoint.startPos; - pressTime = updateGPoint.startTime; - delete delegate.mousePoints[ curGPoint.id ]; - delegate.mousePointCount--; - if ( !delegate.mousePointCount ) { - dispatchRelease = true; - releasePoint = curGPoint.currentPos; - } - } - } - else if ( curGPoint.type === 'touch' ) { - updateGPoint = delegate.touchPoints[ curGPoint.id ] || null; - if ( updateGPoint ) { - pressPoint = updateGPoint.startPos; - pressTime = updateGPoint.startTime; - delete delegate.touchPoints[ curGPoint.id ]; - delegate.touchPointCount--; - if ( !delegate.touchPointCount ) { - dispatchRelease = true; - releasePoint = curGPoint.currentPos; - } - } - } - else if ( curGPoint.type === 'pen' ) { - updateGPoint = delegate.penPoints[ curGPoint.id ] || null; - if ( updateGPoint ) { - pressPoint = updateGPoint.startPos; - pressTime = updateGPoint.startTime; - delete delegate.penPoints[ curGPoint.id ]; - delegate.penPointCount--; - if ( !delegate.penPointCount ) { - dispatchRelease = true; - releasePoint = curGPoint.currentPos; - } - } - } - else { - updateGPoint = null; - } + if ( pointsList ) { + for ( i = 0; i < gPointCount; i++ ) { + curGPoint = gPoints[ i ]; - if ( dispatchRelease ) { - if ( updateGPoint ) { - insideElementPressed = updateGPoint.insideElementPressed; - insideElementReleased = $.pointInElement( tracker.element, releasePoint ); - } - else { - insideElementPressed = false; - insideElementReleased = false; - } + removedGPoint = pointsList.getById( curGPoint.id ); - if ( tracker.releaseHandler ) { - propagate = tracker.releaseHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: getPointRelative( releasePoint, tracker.element ), - insideElementPressed: insideElementPressed, - insideElementReleased: insideElementReleased, - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + if ( removedGPoint ) { + + releasePoint = removedGPoint.currentPos; + releaseTime = removedGPoint.currentTime; + + if ( tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) { + $.MouseTracker.gesturePointVelocityTracker.removePoint( tracker, removedGPoint ); + } + + pointsListLength = pointsList.removeById( curGPoint.id ); + + if ( pointsListLength === 0 ) { + + insideElementPressed = removedGPoint.insideElementPressed; + insideElementReleased = $.pointInElement( tracker.element, releasePoint ); + + // Release + if ( tracker.releaseHandler ) { + propagate = tracker.releaseHandler( + { + eventSource: tracker, + pointerType: removedGPoint.type, + position: getPointRelative( releasePoint, tracker.element ), + insideElementPressed: insideElementPressed, + insideElementReleased: insideElementReleased, + isTouchEvent: removedGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } } - ); - if ( propagate === false ) { - $.cancelEvent( event ); - } - } - // Click Gesture - if ( insideElementPressed && insideElementReleased && tracker.clickHandler ) { - var time = curGPoint.currentTime - pressTime, - distance = pressPoint.distanceTo( curGPoint.currentPos ), - quick = time <= tracker.clickTimeThreshold && - distance <= tracker.clickDistThreshold; - - propagate = tracker.clickHandler( - { - eventSource: tracker, - pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), - quick: quick, - shift: event.shiftKey, - isTouchEvent: curGPoint.type === 'touch', - originalEvent: event, - preventDefaultAction: false, - userData: tracker.userData + // Drag End + if ( tracker.dragEndHandler ) { + propagate = tracker.dragEndHandler( + { + eventSource: tracker, + pointerType: removedGPoint.type, + position: getPointRelative( removedGPoint.currentPos, tracker.element ), + speed: removedGPoint.speed, + direction: removedGPoint.direction, + shift: event.shiftKey, + isTouchEvent: removedGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + + // Click + if ( tracker.clickHandler && insideElementPressed && insideElementReleased ) { + var time = releaseTime - removedGPoint.startTime, + distance = removedGPoint.startPos.distanceTo( releasePoint ), + quick = time <= tracker.clickTimeThreshold && + distance <= tracker.clickDistThreshold; + + propagate = tracker.clickHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelative( curGPoint.currentPos, tracker.element ), + quick: quick, + shift: event.shiftKey, + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + //else if ( pointsListLength === 1 ) { + //} + else if ( pointsListLength === 2 ) { + if ( tracker.pinchHandler && curGPoint.type === 'touch' ) { + // Reset for pinch + delegate.pinchGPoints = pointsList.asArray(); + delegate.lastPinchDist = delegate.currentPinchDist = delegate.pinchGPoints[ 0 ].currentPos.distanceTo( delegate.pinchGPoints[ 1 ].currentPos ); + delegate.lastPinchCenter = delegate.currentPinchCenter = getCenterPoint( delegate.pinchGPoints[ 0 ].currentPos, delegate.pinchGPoints[ 1 ].currentPos ); } - ); - if ( propagate === false ) { - $.cancelEvent( event ); } } } @@ -2551,32 +2565,7 @@ * @inner */ function cancelPointers( tracker, event, gPoints ) { - var delegate = THIS[ tracker.hash ], - i, - gPointCount = gPoints.length, - curGPoint; - - for ( i = 0; i < gPointCount; i++ ) { - curGPoint = gPoints[ i ]; - if ( curGPoint.type === 'mouse' ) { - if ( delegate.mousePoints[ curGPoint.id ] ) { - delete delegate.mousePoints[ curGPoint.id ]; - delegate.mousePointCount--; - } - } - else if ( curGPoint.type === 'touch' ) { - if ( delegate.touchPoints[ curGPoint.id ] ) { - delete delegate.touchPoints[ curGPoint.id ]; - delegate.touchPointCount--; - } - } - else if ( curGPoint.type === 'pen' ) { - if ( delegate.penPoints[ curGPoint.id ] ) { - delete delegate.penPoints[ curGPoint.id ]; - delegate.penPointCount--; - } - } - } + removePointers( tracker, event, gPoints ); } diff --git a/src/openseadragon.js b/src/openseadragon.js index 41065ea8..a2bcbafa 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -742,7 +742,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ maxZoomLevel: null, //UI RESPONSIVENESS AND FEEL - springStiffness: 7.0, + springStiffness: 5.0, clickTimeThreshold: 300, clickDistThreshold: 5, zoomPerClick: 2, diff --git a/src/viewer.js b/src/viewer.js index d66bd3f3..5b4d231e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1,3 +1,4 @@ +/// /* * OpenSeadragon - Viewer * @@ -391,6 +392,7 @@ $.Viewer = function( options ) { clickDistThreshold: this.clickDistThreshold, clickHandler: $.delegate( this, onCanvasClick ), dragHandler: $.delegate( this, onCanvasDrag ), + dragEndHandler: $.delegate( this, onCanvasDragEnd ), releaseHandler: $.delegate( this, onCanvasRelease ), scrollHandler: $.delegate( this, onCanvasScroll ), pinchHandler: $.delegate( this, onCanvasPinch ) @@ -2246,7 +2248,7 @@ function onCanvasDrag( event ) { if( !this.panVertical ){ event.delta.y = 0; } - this.viewport.panBy( this.viewport.deltaPointsFromPixels( event.delta.negate() ), true ); + this.viewport.panBy( this.viewport.deltaPointsFromPixels( event.delta.negate() ), ( event.pointerType !== 'mouse' ) ? true : false ); if( this.constrainDuringPan ){ this.viewport.applyConstraints(); } @@ -2261,6 +2263,8 @@ function onCanvasDrag( event ) { * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag. + * @property {Number} speed - Current computed speed, in pixels per second. + * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0. * @property {Boolean} shift - True if the shift key was pressed during this event. * @property {Object} originalEvent - The original DOM event. * @property {?Object} userData - Arbitrary subscriber-defined object. @@ -2269,13 +2273,49 @@ function onCanvasDrag( event ) { tracker: event.eventSource, position: event.position, delta: event.delta, + speed: event.speed, + direction: event.direction, + shift: event.shift, + originalEvent: event.originalEvent + }); +} + +function onCanvasDragEnd( event ) { + if ( event.pointerType !== 'mouse' && !event.preventDefaultAction && this.viewport && event.speed > 20 ) { + var amplitudeX = 0.35 * ( event.speed * Math.cos( event.direction ) ), + amplitudeY = 0.35 * ( event.speed * Math.sin( event.direction ) ), + center = this.viewport.pixelFromPoint( this.viewport.getCenter( true ) ), + target = this.viewport.pointFromPixel( new $.Point( center.x - amplitudeX, center.y - amplitudeY ) ); + this.viewport.panTo( target, false ); + this.viewport.applyConstraints(); + } + /** + * Raised when a mouse or touch drag operation ends on the {@link OpenSeadragon.Viewer#canvas} element. + * + * @event canvas-drag-end + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. + * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. + * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second. + * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0. + * @property {Boolean} shift - True if the shift key was pressed during this event. + * @property {Object} originalEvent - The original DOM event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'canvas-drag-end', { + tracker: event.eventSource, + position: event.position, + speed: event.speed, + direction: event.direction, shift: event.shift, originalEvent: event.originalEvent }); } function onCanvasRelease( event ) { - if ( event.insideElementPressed && this.viewport ) { + if ( event.pointerType === 'mouse' && event.insideElementPressed && this.viewport ) { this.viewport.applyConstraints(); } /** @@ -2301,6 +2341,45 @@ function onCanvasRelease( event ) { }); } +function onCanvasPinch( event ) { + if ( !event.preventDefaultAction && this.viewport ) { + var centerPt = this.viewport.pointFromPixel( event.center, true ), + lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true ); + this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true ); + this.viewport.panBy( lastCenterPt.minus( centerPt ), true ); + this.viewport.applyConstraints(); + } + /** + * Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element. + * + * @event canvas-pinch + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. + * @property {Array.} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here. + * @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element. + * @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element. + * @property {Number} lastDistance - The previous distance between the two pinch contact points in CSS pixels. + * @property {Number} distance - The distance between the two pinch contact points in CSS pixels. + * @property {Boolean} shift - True if the shift key was pressed during this event. + * @property {Object} originalEvent - The original DOM event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent('canvas-pinch', { + tracker: event.eventSource, + gesturePoints: event.gesturePoints, + lastCenter: event.lastCenter, + center: event.center, + lastDistance: event.lastDistance, + distance: event.distance, + shift: event.shift, + originalEvent: event.originalEvent + }); + //cancels event + return false; +} + function onCanvasScroll( event ) { var factor; if ( !event.preventDefaultAction && this.viewport ) { @@ -2336,49 +2415,6 @@ function onCanvasScroll( event ) { return false; } -function onCanvasPinch(event) { -//{ -// eventSource: -// gesturePoints: -// lastCenter: -// center: -// lastDistance: -// distance: -// originalEvent: -// preventDefaultAction: -// userData: -//} - if (!event.preventDefaultAction && this.viewport) { - var centerPt = this.viewport.pointFromPixel( event.center, true ), - lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true ); - this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true ); - this.viewport.panBy( lastCenterPt.minus( centerPt ), true ); - this.viewport.applyConstraints(); - } - /** - * Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element. - * - * @event canvas-pinch - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. - * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. - * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. - * @property {Number} delta - The pinch delta for the event. - * @property {Boolean} shift - True if the shift key was pressed during this event. - * @property {OpenSeadragon.GesturePoint[]} gesturePoints - - * @property {Object} originalEvent - The original DOM event. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.raiseEvent('canvas-pinch', { - tracker: event.eventSource, - shift: event.shift, - originalEvent: event.originalEvent - }); - //cancels event - return false; -} - function onContainerExit( event ) { if ( !event.insideElementPressed ) { THIS[ this.hash ].mouseInside = false; From d324aea76132b3dc9cd7b199b2cc738b0f30254b Mon Sep 17 00:00:00 2001 From: Mark Salsbery Date: Mon, 31 Mar 2014 12:54:37 -0700 Subject: [PATCH 07/17] Multi-Touch MouseTracker Update Refactored, fixed a couple bugs, unit tests working. Issuing a pull request. --- src/mousetracker.js | 381 +++++++++----------------------------- src/viewer.js | 1 + test/legacy.mouse.shim.js | 34 ++++ test/test.html | 3 +- 4 files changed, 125 insertions(+), 294 deletions(-) create mode 100644 test/legacy.mouse.shim.js diff --git a/src/mousetracker.js b/src/mousetracker.js index cc588a14..4134a1da 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -157,17 +157,6 @@ * Are we currently tracking mouse events. * @property {Boolean} capturing * Are we curruently capturing mouse events. - * @property {Boolean} insideElementPressed - * True if the left mouse button is currently being pressed and was - * initiated inside the tracked element, otherwise false. - * @property {Boolean} insideElement - * Are we currently inside the screen area of the tracked element. - * @property {OpenSeadragon.Point} lastPoint - * Position of last mouse down/move - * @property {Number} lastMouseDownTime - * Time of last mouse down. - * @property {OpenSeadragon.Point} lastMouseDownPoint - * Position of last mouse down */ THIS[ this.hash ] = { click: function ( event ) { onClick( _this, event ); }, @@ -215,6 +204,7 @@ tracking: false, capturing: false, + // Active Contact Points mousePoints: new $.MouseTracker.GesturePointList(), touchPoints: new $.MouseTracker.GesturePointList(), @@ -777,11 +767,11 @@ * @property {Number} id * Identifier unique from all other active GesturePoints for a given pointer device. * @property {String} type - * "mouse", "touch", "pen", or "". + * The pointer device type: "mouse", "touch", "pen", or "". * @property {Boolean} insideElementPressed * True if mouse button pressed or contact point initiated inside the screen area of the tracked element. * @property {Boolean} insideElement - * True if mouse cursor or contact point is currently inside the screen area of the tracked element. + * True if mouse cursor or contact point is currently inside the bounds of the tracked element. * @property {Number} speed * Continuously computed speed, in pixels per second. * @property {Number} direction @@ -1107,14 +1097,14 @@ * @inner */ function getMouseRelative( event, element ) { - return getPointRelative( getMouseAbsolute( event ), element ); + return getPointRelativeToAbsolute( getMouseAbsolute( event ), element ); } /** * @private * @inner */ - function getPointRelative( point, element ) { + function getPointRelativeToAbsolute( point, element ) { var offset = $.getElementOffset( element ); return point.minus( offset ); } @@ -1268,27 +1258,16 @@ * @inner */ function onMouseOver( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOver( tracker, event, [ gPoint ] ); @@ -1300,29 +1279,16 @@ * @inner */ function onMouseOut( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); - var eventOrTouchPoint = event;//isTouch ? event.touches[ 0 ] : event; - - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOut( tracker, event, [ gPoint ] ); @@ -1334,10 +1300,7 @@ * @inner */ function onMouseDown( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); @@ -1345,21 +1308,11 @@ return; } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - insideElementPressed: true, - insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; addPointers( tracker, event, [ gPoint ] ); @@ -1454,8 +1407,11 @@ * @inner */ function onMouseMove( tracker, event ) { - handleMouseMove( tracker, event ); - } + var delegate = THIS[ tracker.hash ]; + if ( !delegate.capturing ) { + handleMouseMove( tracker, event ); + } + } /** @@ -1491,29 +1447,17 @@ * @inner */ function onTouchEnter( tracker, event ) { - var time, - position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), + currentTime: $.now() } ); } @@ -1526,29 +1470,17 @@ * @inner */ function onTouchLeave( tracker, event ) { - var time, - position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), + currentTime: $.now() } ); } @@ -1563,7 +1495,6 @@ function onTouchStart( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - position, gPoint, i, touchCount = event.changedTouches.length, @@ -1575,13 +1506,7 @@ gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; @@ -1589,19 +1514,10 @@ } for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - insideElementPressed: true, - insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), currentTime: time } ); } @@ -1623,7 +1539,6 @@ function onTouchEnd( tracker, event ) { var delegate = THIS[ tracker.hash ], time, - position, gPoint, i, touchCount = event.changedTouches.length, @@ -1632,19 +1547,10 @@ time = $.now(); for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), currentTime: time } ); } @@ -1655,13 +1561,7 @@ gPoint = { id: event.changedTouches[ 0 ].identifier, type: 'touch', - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, currentPos: getMouseAbsolute( event.changedTouches[ 0 ] ), currentTime: time }; @@ -1681,29 +1581,16 @@ * @inner */ function onTouchMove( tracker, event ) { - var time, - position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event.changedTouches[ i ] ), + currentTime: $.now() } ); } @@ -1722,29 +1609,14 @@ * @inner */ function onTouchCancel( tracker, event ) { - var //time, - //position, - i, + var i, touchCount = event.changedTouches.length, gPoints = []; - //time = $.now(); - for ( i = 0; i < touchCount; i++ ) { - //position = getMouseAbsolute( event.changedTouches[ i ] ); - gPoints.push( { id: event.changedTouches[ i ].identifier, type: 'touch', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - //currentPos: position, - //currentTime: time } ); } @@ -1779,25 +1651,14 @@ * @inner */ function onPointerOver( tracker, event ) { - var time, - position, - gPoint; - - time = $.now(); - position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOver( tracker, event, [ gPoint ] ); @@ -1809,25 +1670,14 @@ * @inner */ function onPointerOut( tracker, event ) { - var time, - position, - gPoint; - - time = $.now(); - position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, insideElement: false, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointersOut( tracker, event, [ gPoint ] ); @@ -1839,10 +1689,7 @@ * @inner */ function onPointerDown( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - position, - gPoint; + var gPoint; if ( event.button == 2 ) { return; @@ -1855,21 +1702,11 @@ event.currentTarget.msSetPointerCapture( event.pointerId ); } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: event.pointerId, type: getPointerType( event ), - insideElementPressed: true, - insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; addPointers( tracker, event, [ gPoint ] ); @@ -1887,10 +1724,7 @@ * @inner */ function onPointerUp( tracker, event ) { - var delegate = THIS[ tracker.hash ], - time, - position, - gPoint; + var gPoint; if ( event.button == 2 ) { return; @@ -1903,21 +1737,11 @@ event.currentTarget.msReleasePointerCapture( event.pointerId ); } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; removePointers(tracker, event, [ gPoint ]); @@ -1936,25 +1760,13 @@ */ function onPointerMove( tracker, event ) { // Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height) - var time, - position, - gPoint; - - time = $.now(); - position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointers(tracker, event, [ gPoint ]); @@ -1972,25 +1784,11 @@ * @inner */ function onPointerCancel( tracker, event ) { - var //time, - //position, - gPoint; - - //time = $.now(); - //position = getMouseAbsolute( event ); + var gPoint; gPoint = { id: event.pointerId, type: getPointerType( event ), - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - //currentPos: position, - //currentTime: time }; cancelPointers( tracker, event, [ gPoint ] ); @@ -2045,27 +1843,15 @@ * @inner */ function handleMouseMove( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; updatePointers( tracker, event, [ gPoint ] ); @@ -2077,9 +1863,7 @@ * @inner */ function handleMouseUp( tracker, event ) { - var time, - position, - gPoint; + var gPoint; event = $.getEvent( event ); @@ -2087,21 +1871,11 @@ return; } - time = $.now(); - position = getMouseAbsolute( event ); - gPoint = { id: $.MouseTracker.mousePointerId, type: 'mouse', - //insideElementPressed: true, - //insideElement: true, - //speed: 0, - //startPos: position, - //startTime: time, - //lastPos: position, - //lastTime: time, - currentPos: position, - currentTime: time + currentPos: getMouseAbsolute( event ), + currentTime: $.now() }; removePointers( tracker, event, [ gPoint ] ); @@ -2145,6 +1919,8 @@ curGPoint = gPoints[ i ]; // Initialize for drag/swipe/pinch + curGPoint.insideElementPressed = true; + curGPoint.insideElement = true; curGPoint.speed = 0; curGPoint.direction = 0; curGPoint.startPos = curGPoint.currentPos; @@ -2165,7 +1941,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.startPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.startPos, tracker.element ), isTouchEvent: curGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, @@ -2227,7 +2003,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), insideElementPressed: insideElementPressed, buttonDownAny: IS_BUTTON_DOWN, isTouchEvent: curGPoint.type === 'touch', @@ -2282,7 +2058,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), insideElementPressed: insideElementPressed, buttonDownAny: IS_BUTTON_DOWN, isTouchEvent: curGPoint.type === 'touch', @@ -2340,7 +2116,26 @@ //} } - if ( pointsListLength == 1 ) { + if ( pointsListLength === 0 ) { + // Move (no contacts) + if ( tracker.moveHandler ) { + propagate = tracker.moveHandler( + { + eventSource: tracker, + pointerType: curGPoint.type, + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), + isTouchEvent: curGPoint.type === 'touch', + originalEvent: event, + preventDefaultAction: false, + userData: tracker.userData + } + ); + if ( propagate === false ) { + $.cancelEvent( event ); + } + } + } + else if ( pointsListLength === 1 ) { // Move (1 contact) if ( tracker.moveHandler ) { updateGPoint = pointsList.asArray()[ 0 ]; @@ -2348,7 +2143,7 @@ { eventSource: tracker, pointerType: updateGPoint.type, - position: getPointRelative( updateGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ), isTouchEvent: updateGPoint.type === 'touch', originalEvent: event, preventDefaultAction: false, @@ -2368,7 +2163,7 @@ { eventSource: tracker, pointerType: updateGPoint.type, - position: getPointRelative( updateGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ), delta: delta, speed: updateGPoint.speed, direction: updateGPoint.direction, @@ -2384,7 +2179,7 @@ } } } - else if ( pointsListLength == 2 ) { + else if ( pointsListLength === 2 ) { // Move (2 contacts, use center) if ( tracker.moveHandler ) { gPointArray = pointsList.asArray(); @@ -2392,7 +2187,7 @@ { eventSource: tracker, pointerType: gPointArray[ 0 ].type, - position: getPointRelative( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ), + position: getPointRelativeToAbsolute( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ), isTouchEvent: gPointArray[ 0 ].type === 'touch', originalEvent: event, preventDefaultAction: false, @@ -2417,8 +2212,8 @@ eventSource: tracker, pointerType: 'touch', gesturePoints: delegate.pinchGPoints, - lastCenter: getPointRelative( delegate.lastPinchCenter, tracker.element ), - center: getPointRelative( delegate.currentPinchCenter, tracker.element ), + lastCenter: getPointRelativeToAbsolute( delegate.lastPinchCenter, tracker.element ), + center: getPointRelativeToAbsolute( delegate.currentPinchCenter, tracker.element ), lastDistance: delegate.lastPinchDist, distance: delegate.currentPinchDist, shift: event.shiftKey, @@ -2476,7 +2271,7 @@ if ( pointsListLength === 0 ) { insideElementPressed = removedGPoint.insideElementPressed; - insideElementReleased = $.pointInElement( tracker.element, releasePoint ); + insideElementReleased = removedGPoint.insideElement || $.pointInElement( tracker.element, releasePoint ); // Release if ( tracker.releaseHandler ) { @@ -2484,7 +2279,7 @@ { eventSource: tracker, pointerType: removedGPoint.type, - position: getPointRelative( releasePoint, tracker.element ), + position: getPointRelativeToAbsolute( releasePoint, tracker.element ), insideElementPressed: insideElementPressed, insideElementReleased: insideElementReleased, isTouchEvent: removedGPoint.type === 'touch', @@ -2504,7 +2299,7 @@ { eventSource: tracker, pointerType: removedGPoint.type, - position: getPointRelative( removedGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( removedGPoint.currentPos, tracker.element ), speed: removedGPoint.speed, direction: removedGPoint.direction, shift: event.shiftKey, @@ -2530,7 +2325,7 @@ { eventSource: tracker, pointerType: curGPoint.type, - position: getPointRelative( curGPoint.currentPos, tracker.element ), + position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), quick: quick, shift: event.shiftKey, isTouchEvent: curGPoint.type === 'touch', diff --git a/src/viewer.js b/src/viewer.js index 7092046e..79ad821f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2281,6 +2281,7 @@ function onCanvasDrag( event ) { } function onCanvasDragEnd( event ) { + // TODO: Make the magic numbers configurable (20 pixels-per-second speed threshold and 0.35 momentum dampener). if ( event.pointerType !== 'mouse' && !event.preventDefaultAction && this.viewport && event.speed > 20 ) { var amplitudeX = 0.35 * ( event.speed * Math.cos( event.direction ) ), amplitudeY = 0.35 * ( event.speed * Math.sin( event.direction ) ), diff --git a/test/legacy.mouse.shim.js b/test/legacy.mouse.shim.js new file mode 100644 index 00000000..8a503759 --- /dev/null +++ b/test/legacy.mouse.shim.js @@ -0,0 +1,34 @@ +(function($, undefined) { + + /** + * Plugin to force OpenSeadragon to use the legacy mouse pointer event model + */ + + $.MouseTracker.subscribeEvents = [ "click", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; + + if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { + // Older Firefox + $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" ); + } + + $.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) + $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" ); + if ( 'ontouchenter' in window ) { + $.MouseTracker.subscribeEvents.push( "touchenter", "touchleave" ); + $.MouseTracker.haveTouchEnter = true; + } + else { + $.MouseTracker.haveTouchEnter = false; + } + } + if ( 'ongesturestart' in window ) { + // iOS (see https://developer.apple.com/library/safari/documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html) + // Subscribe to these to prevent default gesture handling + $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); + } + $.MouseTracker.mousePointerId = "legacy-mouse"; + $.MouseTracker.maxTouchPoints = 10; + +}(OpenSeadragon)); diff --git a/test/test.html b/test/test.html index 94602d88..c6159545 100644 --- a/test/test.html +++ b/test/test.html @@ -1,4 +1,4 @@ - + @@ -15,6 +15,7 @@ +