mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 06:36:11 +03:00
3375 lines
136 KiB
JavaScript
3375 lines
136 KiB
JavaScript
/*
|
|
* OpenSeadragon - MouseTracker
|
|
*
|
|
* Copyright (C) 2009 CodePlex Foundation
|
|
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of CodePlex Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
(function ( $ ) {
|
|
|
|
// All MouseTracker instances
|
|
var MOUSETRACKERS = [];
|
|
|
|
// dictionary from hash to private properties
|
|
var THIS = {};
|
|
|
|
|
|
/**
|
|
* @class MouseTracker
|
|
* @classdesc Provides simplified handling of common pointer device (mouse, touch, pen, etc.) gestures
|
|
* and keyboard events on a specified element.
|
|
* @memberof OpenSeadragon
|
|
* @param {Object} options
|
|
* Allows configurable properties to be entirely specified by passing
|
|
* an options object to the constructor. The constructor also supports
|
|
* the original positional arguments 'element', 'clickTimeThreshold',
|
|
* and 'clickDistThreshold' in that order.
|
|
* @param {Element|String} options.element
|
|
* A reference to an element or an element id for which the pointer/key
|
|
* events will be monitored.
|
|
* @param {Boolean} [options.startDisabled=false]
|
|
* If true, event tracking on the element will not start until
|
|
* {@link OpenSeadragon.MouseTracker.setTracking|setTracking} is called.
|
|
* @param {Number} options.clickTimeThreshold
|
|
* The number of milliseconds within which a pointer down-up event combination
|
|
* will be treated as a click gesture.
|
|
* @param {Number} options.clickDistThreshold
|
|
* The maximum distance allowed between a pointer down event and a pointer up event
|
|
* to be treated as a click gesture.
|
|
* @param {Number} options.dblClickTimeThreshold
|
|
* The number of milliseconds within which two pointer down-up event combinations
|
|
* will be treated as a double-click gesture.
|
|
* @param {Number} options.dblClickDistThreshold
|
|
* The maximum distance allowed between two pointer click events
|
|
* to be treated as a click gesture.
|
|
* @param {Number} [options.stopDelay=50]
|
|
* The number of milliseconds without pointer move before the stop
|
|
* event is fired.
|
|
* @param {OpenSeadragon.EventHandler} [options.enterHandler=null]
|
|
* An optional handler for pointer enter.
|
|
* @param {OpenSeadragon.EventHandler} [options.exitHandler=null]
|
|
* An optional handler for pointer exit.
|
|
* @param {OpenSeadragon.EventHandler} [options.pressHandler=null]
|
|
* An optional handler for pointer press.
|
|
* @param {OpenSeadragon.EventHandler} [options.nonPrimaryPressHandler=null]
|
|
* An optional handler for pointer non-primary button press.
|
|
* @param {OpenSeadragon.EventHandler} [options.releaseHandler=null]
|
|
* An optional handler for pointer release.
|
|
* @param {OpenSeadragon.EventHandler} [options.nonPrimaryReleaseHandler=null]
|
|
* An optional handler for pointer non-primary button release.
|
|
* @param {OpenSeadragon.EventHandler} [options.moveHandler=null]
|
|
* An optional handler for pointer move.
|
|
* @param {OpenSeadragon.EventHandler} [options.scrollHandler=null]
|
|
* An optional handler for mouse wheel scroll.
|
|
* @param {OpenSeadragon.EventHandler} [options.clickHandler=null]
|
|
* An optional handler for pointer click.
|
|
* @param {OpenSeadragon.EventHandler} [options.dblClickHandler=null]
|
|
* An optional handler for pointer double-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.keyDownHandler=null]
|
|
* An optional handler for keydown.
|
|
* @param {OpenSeadragon.EventHandler} [options.keyUpHandler=null]
|
|
* An optional handler for keyup.
|
|
* @param {OpenSeadragon.EventHandler} [options.keyHandler=null]
|
|
* An optional handler for keypress.
|
|
* @param {OpenSeadragon.EventHandler} [options.focusHandler=null]
|
|
* An optional handler for focus.
|
|
* @param {OpenSeadragon.EventHandler} [options.blurHandler=null]
|
|
* An optional handler for blur.
|
|
* @param {Object} [options.userData=null]
|
|
* Arbitrary object to be passed unchanged to any attached handler methods.
|
|
*/
|
|
$.MouseTracker = function ( options ) {
|
|
|
|
MOUSETRACKERS.push( this );
|
|
|
|
var args = arguments;
|
|
|
|
if ( !$.isPlainObject( options ) ) {
|
|
options = {
|
|
element: args[ 0 ],
|
|
clickTimeThreshold: args[ 1 ],
|
|
clickDistThreshold: args[ 2 ]
|
|
};
|
|
}
|
|
|
|
this.hash = Math.random(); // An unique hash for this tracker.
|
|
/**
|
|
* The element for which pointer events are being monitored.
|
|
* @member {Element} element
|
|
* @memberof OpenSeadragon.MouseTracker#
|
|
*/
|
|
this.element = $.getElement( options.element );
|
|
/**
|
|
* The number of milliseconds within which a pointer down-up event combination
|
|
* will be treated as a click gesture.
|
|
* @member {Number} clickTimeThreshold
|
|
* @memberof OpenSeadragon.MouseTracker#
|
|
*/
|
|
this.clickTimeThreshold = options.clickTimeThreshold || $.DEFAULT_SETTINGS.clickTimeThreshold;
|
|
/**
|
|
* The maximum distance allowed between a pointer down event and a pointer up event
|
|
* to be treated as a click gesture.
|
|
* @member {Number} clickDistThreshold
|
|
* @memberof OpenSeadragon.MouseTracker#
|
|
*/
|
|
this.clickDistThreshold = options.clickDistThreshold || $.DEFAULT_SETTINGS.clickDistThreshold;
|
|
/**
|
|
* The number of milliseconds within which two pointer down-up event combinations
|
|
* will be treated as a double-click gesture.
|
|
* @member {Number} dblClickTimeThreshold
|
|
* @memberof OpenSeadragon.MouseTracker#
|
|
*/
|
|
this.dblClickTimeThreshold = options.dblClickTimeThreshold || $.DEFAULT_SETTINGS.dblClickTimeThreshold;
|
|
/**
|
|
* The maximum distance allowed between two pointer click events
|
|
* to be treated as a click gesture.
|
|
* @member {Number} clickDistThreshold
|
|
* @memberof OpenSeadragon.MouseTracker#
|
|
*/
|
|
this.dblClickDistThreshold = options.dblClickDistThreshold || $.DEFAULT_SETTINGS.dblClickDistThreshold;
|
|
/*eslint-disable no-multi-spaces*/
|
|
this.userData = options.userData || null;
|
|
this.stopDelay = options.stopDelay || 50;
|
|
|
|
this.enterHandler = options.enterHandler || null;
|
|
this.exitHandler = options.exitHandler || null;
|
|
this.pressHandler = options.pressHandler || null;
|
|
this.nonPrimaryPressHandler = options.nonPrimaryPressHandler || null;
|
|
this.releaseHandler = options.releaseHandler || null;
|
|
this.nonPrimaryReleaseHandler = options.nonPrimaryReleaseHandler || null;
|
|
this.moveHandler = options.moveHandler || null;
|
|
this.scrollHandler = options.scrollHandler || null;
|
|
this.clickHandler = options.clickHandler || null;
|
|
this.dblClickHandler = options.dblClickHandler || null;
|
|
this.dragHandler = options.dragHandler || null;
|
|
this.dragEndHandler = options.dragEndHandler || null;
|
|
this.pinchHandler = options.pinchHandler || null;
|
|
this.stopHandler = options.stopHandler || null;
|
|
this.keyDownHandler = options.keyDownHandler || null;
|
|
this.keyUpHandler = options.keyUpHandler || null;
|
|
this.keyHandler = options.keyHandler || null;
|
|
this.focusHandler = options.focusHandler || null;
|
|
this.blurHandler = options.blurHandler || null;
|
|
/*eslint-enable no-multi-spaces*/
|
|
|
|
//Store private properties in a scope sealed hash map
|
|
var _this = this;
|
|
|
|
/**
|
|
* @private
|
|
* @property {Boolean} tracking
|
|
* Are we currently tracking pointer events for this element.
|
|
*/
|
|
THIS[ this.hash ] = {
|
|
click: function ( event ) { onClick( _this, event ); },
|
|
dblclick: function ( event ) { onDblClick( _this, event ); },
|
|
keydown: function ( event ) { onKeyDown( _this, event ); },
|
|
keyup: function ( event ) { onKeyUp( _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 ); },
|
|
|
|
mouseenter: function ( event ) { onMouseEnter( _this, event ); }, // Used on IE8 only
|
|
mouseleave: function ( event ) { onMouseLeave( _this, event ); }, // Used on IE8 only
|
|
mouseover: function ( event ) { onMouseOver( _this, event ); },
|
|
mouseout: function ( event ) { onMouseOut( _this, event ); },
|
|
mousedown: function ( event ) { onMouseDown( _this, event ); },
|
|
mouseup: function ( event ) { onMouseUp( _this, event ); },
|
|
mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); },
|
|
mousemove: function ( event ) { onMouseMove( _this, event ); },
|
|
mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); },
|
|
|
|
touchstart: function ( event ) { onTouchStart( _this, event ); },
|
|
touchend: function ( event ) { onTouchEnd( _this, event ); },
|
|
touchendcaptured: function ( event ) { onTouchEndCaptured( _this, event ); },
|
|
touchmove: function ( event ) { onTouchMove( _this, event ); },
|
|
touchmovecaptured: function ( event ) { onTouchMoveCaptured( _this, event ); },
|
|
touchcancel: function ( event ) { onTouchCancel( _this, event ); },
|
|
|
|
gesturestart: function ( event ) { onGestureStart( _this, event ); },
|
|
gesturechange: function ( event ) { onGestureChange( _this, event ); },
|
|
|
|
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 ); },
|
|
pointerupcaptured: function ( event ) { onPointerUpCaptured( _this, event ); },
|
|
pointermovecaptured: function ( event ) { onPointerMoveCaptured( _this, event ); },
|
|
|
|
tracking: false,
|
|
|
|
// Active pointers lists. Array of GesturePointList objects, one for each pointer device type.
|
|
// GesturePointList objects are added each time a pointer is tracked by a new pointer device type (see getActivePointersListByType()).
|
|
// Active pointers are any pointer being tracked for this element which are in the hit-test area
|
|
// of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.
|
|
activePointersLists: [],
|
|
|
|
// Tracking for double-click gesture
|
|
lastClickPos: null,
|
|
dblClickTimeOut: null,
|
|
|
|
// Tracking for pinch gesture
|
|
pinchGPoints: [],
|
|
lastPinchDist: 0,
|
|
currentPinchDist: 0,
|
|
lastPinchCenter: null,
|
|
currentPinchCenter: null
|
|
};
|
|
|
|
if ( !options.startDisabled ) {
|
|
this.setTracking( true );
|
|
}
|
|
};
|
|
|
|
/** @lends OpenSeadragon.MouseTracker.prototype */
|
|
$.MouseTracker.prototype = {
|
|
|
|
/**
|
|
* Clean up any events or objects created by the tracker.
|
|
* @function
|
|
*/
|
|
destroy: function () {
|
|
var i;
|
|
|
|
stopTracking( this );
|
|
this.element = null;
|
|
|
|
for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
|
|
if ( MOUSETRACKERS[ i ] === this ) {
|
|
MOUSETRACKERS.splice( i, 1 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
THIS[ this.hash ] = null;
|
|
delete THIS[ this.hash ];
|
|
},
|
|
|
|
/**
|
|
* Are we currently tracking events on this element.
|
|
* @deprecated Just use this.tracking
|
|
* @function
|
|
* @returns {Boolean} Are we currently tracking events on this element.
|
|
*/
|
|
isTracking: function () {
|
|
return THIS[ this.hash ].tracking;
|
|
},
|
|
|
|
/**
|
|
* Enable or disable whether or not we are tracking events on this element.
|
|
* @function
|
|
* @param {Boolean} track True to start tracking, false to stop tracking.
|
|
* @returns {OpenSeadragon.MouseTracker} Chainable.
|
|
*/
|
|
setTracking: function ( track ) {
|
|
if ( track ) {
|
|
startTracking( this );
|
|
} else {
|
|
stopTracking( this );
|
|
}
|
|
//chain
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Returns the {@link OpenSeadragon.MouseTracker.GesturePointList|GesturePointList} for all but the given pointer device type.
|
|
* @function
|
|
* @param {String} type - The pointer device type: "mouse", "touch", "pen", etc.
|
|
* @returns {Array.<OpenSeadragon.MouseTracker.GesturePointList>}
|
|
*/
|
|
getActivePointersListsExceptType: function ( type ) {
|
|
var delegate = THIS[ this.hash ];
|
|
var listArray = [];
|
|
|
|
for (var i = 0; i < delegate.activePointersLists.length; ++i) {
|
|
if (delegate.activePointersLists[i].type !== type) {
|
|
listArray.push(delegate.activePointersLists[i]);
|
|
}
|
|
}
|
|
|
|
return listArray;
|
|
},
|
|
|
|
/**
|
|
* Returns the {@link OpenSeadragon.MouseTracker.GesturePointList|GesturePointList} for the given pointer device type,
|
|
* creating and caching a new {@link OpenSeadragon.MouseTracker.GesturePointList|GesturePointList} if one doesn't already exist for the type.
|
|
* @function
|
|
* @param {String} type - The pointer device type: "mouse", "touch", "pen", etc.
|
|
* @returns {OpenSeadragon.MouseTracker.GesturePointList}
|
|
*/
|
|
getActivePointersListByType: function ( type ) {
|
|
var delegate = THIS[ this.hash ],
|
|
i,
|
|
len = delegate.activePointersLists.length,
|
|
list;
|
|
|
|
for ( i = 0; i < len; i++ ) {
|
|
if ( delegate.activePointersLists[ i ].type === type ) {
|
|
return delegate.activePointersLists[ i ];
|
|
}
|
|
}
|
|
|
|
list = new $.MouseTracker.GesturePointList( type );
|
|
delegate.activePointersLists.push( list );
|
|
return list;
|
|
},
|
|
|
|
/**
|
|
* Returns the total number of pointers currently active on the tracked element.
|
|
* @function
|
|
* @returns {Number}
|
|
*/
|
|
getActivePointerCount: function () {
|
|
var delegate = THIS[ this.hash ],
|
|
i,
|
|
len = delegate.activePointersLists.length,
|
|
count = 0;
|
|
|
|
for ( i = 0; i < len; i++ ) {
|
|
count += delegate.activePointersLists[ i ].getLength();
|
|
}
|
|
|
|
return count;
|
|
},
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Number} event.pointers
|
|
* Number of pointers (all types) active in the tracked element.
|
|
* @param {Boolean} event.insideElementPressed
|
|
* True if the left mouse button is currently being pressed and was
|
|
* initiated inside the tracked element, otherwise false.
|
|
* @param {Boolean} event.buttonDownAny
|
|
* Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @param {Object} event.originalEvent
|
|
* The original event object.
|
|
* @param {Boolean} event.preventDefaultAction
|
|
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.
|
|
* @param {Object} event.userData
|
|
* Arbitrary user-defined object.
|
|
*/
|
|
enterHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Number} event.pointers
|
|
* Number of pointers (all types) active in the tracked element.
|
|
* @param {Boolean} event.insideElementPressed
|
|
* True if the left mouse button is currently being pressed and was
|
|
* initiated inside the tracked element, otherwise false.
|
|
* @param {Boolean} event.buttonDownAny
|
|
* Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
exitHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
pressHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.button
|
|
* Button which caused the event.
|
|
* -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
nonPrimaryPressHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Boolean} event.insideElementPressed
|
|
* True if the left mouse button is currently being pressed and was
|
|
* initiated inside the tracked element, otherwise false.
|
|
* @param {Boolean} event.insideElementReleased
|
|
* True if the cursor inside the tracked element when the button was released.
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
releaseHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.button
|
|
* Button which caused the event.
|
|
* -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
nonPrimaryReleaseHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
moveHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.scroll
|
|
* The scroll delta for the event.
|
|
* @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. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead. Touch devices no longer generate scroll event.</span>
|
|
* @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.
|
|
*/
|
|
scrollHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Boolean} event.quick
|
|
* True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for ignoring drag 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. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
clickHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {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. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
dblClickHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {OpenSeadragon.Point} event.delta
|
|
* 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} event.speed
|
|
* Current computed speed, in pixels per second.
|
|
* @param {Number} event.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
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
dragHandler: function () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.speed
|
|
* Speed at the end of a drag gesture, in pixels per second.
|
|
* @param {Number} event.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
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
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", etc.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} 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 () { },
|
|
|
|
/**
|
|
* Implement or assign implementation to these handlers during or after
|
|
* calling the constructor.
|
|
* @function
|
|
* @param {Object} event
|
|
* @param {OpenSeadragon.MouseTracker} event.eventSource
|
|
* A reference to the tracker instance.
|
|
* @param {String} event.pointerType
|
|
* "mouse", "touch", "pen", etc.
|
|
* @param {OpenSeadragon.Point} event.position
|
|
* The position of the event relative to the tracked element.
|
|
* @param {Number} event.buttons
|
|
* Current buttons pressed.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @param {Boolean} event.isTouchEvent
|
|
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
|
|
* @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.
|
|
*/
|
|
stopHandler: 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 {Number} event.keyCode
|
|
* The key code that was pressed.
|
|
* @param {Boolean} event.ctrl
|
|
* True if the ctrl key was pressed during this event.
|
|
* @param {Boolean} event.shift
|
|
* True if the shift key was pressed during this event.
|
|
* @param {Boolean} event.alt
|
|
* True if the alt key was pressed during this event.
|
|
* @param {Boolean} event.meta
|
|
* True if the meta 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.
|
|
*/
|
|
keyDownHandler: 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 {Number} event.keyCode
|
|
* The key code that was pressed.
|
|
* @param {Boolean} event.ctrl
|
|
* True if the ctrl key was pressed during this event.
|
|
* @param {Boolean} event.shift
|
|
* True if the shift key was pressed during this event.
|
|
* @param {Boolean} event.alt
|
|
* True if the alt key was pressed during this event.
|
|
* @param {Boolean} event.meta
|
|
* True if the meta 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.
|
|
*/
|
|
keyUpHandler: 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 {Number} event.keyCode
|
|
* The key code that was pressed.
|
|
* @param {Boolean} event.ctrl
|
|
* True if the ctrl key was pressed during this event.
|
|
* @param {Boolean} event.shift
|
|
* True if the shift key was pressed during this event.
|
|
* @param {Boolean} event.alt
|
|
* True if the alt key was pressed during this event.
|
|
* @param {Boolean} event.meta
|
|
* True if the meta 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.
|
|
*/
|
|
keyHandler: 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 {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.
|
|
*/
|
|
focusHandler: 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 {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.
|
|
*/
|
|
blurHandler: function () { }
|
|
};
|
|
|
|
/**
|
|
* Resets all active mousetrakers. (Added to patch issue #697 "Mouse up outside map will cause "canvas-drag" event to stick")
|
|
*
|
|
* @private
|
|
* @member resetAllMouseTrackers
|
|
* @memberof OpenSeadragon.MouseTracker
|
|
*/
|
|
$.MouseTracker.resetAllMouseTrackers = function(){
|
|
for(var i = 0; i < MOUSETRACKERS.length; i++){
|
|
if (MOUSETRACKERS[i].isTracking()){
|
|
MOUSETRACKERS[i].setTracking(false);
|
|
MOUSETRACKERS[i].setTracking(true);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Provides continuous computation of velocity (speed and direction) of active pointers.
|
|
* This is a singleton, used by all MouseTracker instances, as it is unlikely there will ever be more than
|
|
* two active gesture pointers at a time.
|
|
*
|
|
* @private
|
|
* @member gesturePointVelocityTracker
|
|
* @memberof OpenSeadragon.MouseTracker
|
|
*/
|
|
$.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 pointers 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 ) {
|
|
trackerPoints.splice( i, 1 );
|
|
// Only run the interval timer if theres gesture pointers to track
|
|
len--;
|
|
if ( len === 0 ) {
|
|
window.clearInterval( intervalId );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
return {
|
|
addPoint: addPoint,
|
|
removePoint: removePoint
|
|
};
|
|
} )();
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Pointer event model and feature detection
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
$.MouseTracker.captureElement = document;
|
|
|
|
/**
|
|
* 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 legacy mouse capture support.
|
|
*/
|
|
$.MouseTracker.supportsMouseCapture = (function () {
|
|
var divElement = document.createElement( 'div' );
|
|
return $.isFunction( divElement.setCapture ) && $.isFunction( divElement.releaseCapture );
|
|
}());
|
|
|
|
/**
|
|
* Detect browser pointer device event model(s) and build appropriate list of events to subscribe to.
|
|
*/
|
|
$.MouseTracker.subscribeEvents = [ "click", "dblclick", "keydown", "keyup", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
|
|
|
|
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
|
|
// Older Firefox
|
|
$.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" );
|
|
}
|
|
|
|
// Note: window.navigator.pointerEnable is deprecated on IE 11 and not part of W3C spec.
|
|
if ( window.PointerEvent && ( window.navigator.pointerEnabled || $.Browser.vendor !== $.BROWSERS.IE ) ) {
|
|
// IE11 and other W3C Pointer Event implementations (see http://www.w3.org/TR/pointerevents)
|
|
$.MouseTracker.havePointerEvents = true;
|
|
$.MouseTracker.subscribeEvents.push( "pointerover", "pointerout", "pointerdown", "pointerup", "pointermove", "pointercancel" );
|
|
$.MouseTracker.unprefixedPointerEvents = true;
|
|
if( navigator.maxTouchPoints ) {
|
|
$.MouseTracker.maxTouchPoints = navigator.maxTouchPoints;
|
|
} else {
|
|
$.MouseTracker.maxTouchPoints = 0;
|
|
}
|
|
$.MouseTracker.haveMouseEnter = false;
|
|
} else if ( window.MSPointerEvent && window.navigator.msPointerEnabled ) {
|
|
// IE10
|
|
$.MouseTracker.havePointerEvents = true;
|
|
$.MouseTracker.subscribeEvents.push( "MSPointerOver", "MSPointerOut", "MSPointerDown", "MSPointerUp", "MSPointerMove", "MSPointerCancel" );
|
|
$.MouseTracker.unprefixedPointerEvents = false;
|
|
if( navigator.msMaxTouchPoints ) {
|
|
$.MouseTracker.maxTouchPoints = navigator.msMaxTouchPoints;
|
|
} else {
|
|
$.MouseTracker.maxTouchPoints = 0;
|
|
}
|
|
$.MouseTracker.haveMouseEnter = false;
|
|
} else {
|
|
// Legacy W3C mouse events
|
|
$.MouseTracker.havePointerEvents = false;
|
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
|
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
|
|
$.MouseTracker.haveMouseEnter = true;
|
|
} else {
|
|
$.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
|
|
$.MouseTracker.haveMouseEnter = false;
|
|
}
|
|
$.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
|
|
if ( 'ontouchstart' in window ) {
|
|
// iOS, Android, and other W3c Touch Event implementations
|
|
// (see http://www.w3.org/TR/touch-events/)
|
|
// (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
|
|
// (see https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
|
|
$.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
|
|
}
|
|
if ( 'ongesturestart' in window ) {
|
|
// iOS (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
|
|
// Subscribe to these to prevent default gesture handling
|
|
$.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
|
|
}
|
|
$.MouseTracker.mousePointerId = "legacy-mouse";
|
|
$.MouseTracker.maxTouchPoints = 10;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Classes and typedefs
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointer 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
|
|
* The pointer device type: "mouse", "touch", "pen", etc.
|
|
* @property {Boolean} captured
|
|
* True if events for the gesture point are captured to the tracked element.
|
|
* @property {Boolean} isPrimary
|
|
* True if the gesture point is a master pointer amongst the set of active pointers for each pointer type. True for mouse and primary (first) touch/pen pointers.
|
|
* @property {Boolean} insideElementPressed
|
|
* True if button pressed or contact point initiated inside the screen area of the tracked element.
|
|
* @property {Boolean} insideElement
|
|
* True if pointer or contact point is currently inside the bounds of the tracked element.
|
|
* @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 {OpenSeadragon.Point} contactPos
|
|
* The initial pointer contact position, relative to the page including any scrolling. Only valid if the pointer has contact (pressed, touch contact, pen contact).
|
|
* @property {Number} contactTime
|
|
* The initial pointer contact time, in milliseconds. Only valid if the pointer has contact (pressed, touch contact, pen contact).
|
|
* @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 active {@link OpenSeadragon.MouseTracker.GesturePoint|GesturePoint} objects for a given pointer device type.
|
|
* Active pointers are any pointer being tracked for this element which are in the hit-test area
|
|
* of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.
|
|
* @memberof OpenSeadragon.MouseTracker
|
|
* @param {String} type - The pointer device type: "mouse", "touch", "pen", etc.
|
|
*/
|
|
$.MouseTracker.GesturePointList = function ( type ) {
|
|
this._gPoints = [];
|
|
/**
|
|
* The pointer device type: "mouse", "touch", "pen", etc.
|
|
* @member {String} type
|
|
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
|
|
*/
|
|
this.type = type;
|
|
/**
|
|
* Current buttons pressed for the device.
|
|
* Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
|
|
* @member {Number} buttons
|
|
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
|
|
*/
|
|
this.buttons = 0;
|
|
/**
|
|
* Current number of contact points (touch points, mouse down, etc.) for the device.
|
|
* @member {Number} contacts
|
|
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
|
|
*/
|
|
this.contacts = 0;
|
|
/**
|
|
* Current number of clicks for the device. Used for multiple click gesture tracking.
|
|
* @member {Number} clicks
|
|
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
|
|
*/
|
|
this.clicks = 0;
|
|
/**
|
|
* Current number of captured pointers for the device.
|
|
* @member {Number} captureCount
|
|
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
|
|
*/
|
|
this.captureCount = 0;
|
|
};
|
|
|
|
/** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */
|
|
$.MouseTracker.GesturePointList.prototype = {
|
|
/**
|
|
* @function
|
|
* @returns {Number} Number of gesture points in the list.
|
|
*/
|
|
getLength: function () {
|
|
return this._gPoints.length;
|
|
},
|
|
/**
|
|
* @function
|
|
* @returns {Array.<OpenSeadragon.MouseTracker.GesturePoint>} 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} index - The index of the gesture point to retrieve from the list.
|
|
* @returns {OpenSeadragon.MouseTracker.GesturePoint|null} The gesture point at the given index, or null if not found.
|
|
*/
|
|
getByIndex: function ( index ) {
|
|
if ( index < this._gPoints.length) {
|
|
return this._gPoints[ index ];
|
|
}
|
|
|
|
return null;
|
|
},
|
|
/**
|
|
* @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;
|
|
},
|
|
/**
|
|
* @function
|
|
* @returns {OpenSeadragon.MouseTracker.GesturePoint|null} The primary gesture point in the list, or null if not found.
|
|
*/
|
|
getPrimary: function ( id ) {
|
|
var i,
|
|
len = this._gPoints.length;
|
|
for ( i = 0; i < len; i++ ) {
|
|
if ( this._gPoints[ i ].isPrimary ) {
|
|
return this._gPoints[ i ];
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
/**
|
|
* Increment this pointer's contact count.
|
|
* It will evaluate whether this pointer type is allowed to have multiple contacts.
|
|
* @function
|
|
*/
|
|
addContact: function() {
|
|
++this.contacts;
|
|
|
|
if (this.contacts > 1 && (this.type === "mouse" || this.type === "pen")) {
|
|
this.contacts = 1;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Decrement this pointer's contact count.
|
|
* It will make sure the count does not go below 0.
|
|
* @function
|
|
*/
|
|
removeContact: function() {
|
|
--this.contacts;
|
|
|
|
if (this.contacts < 0) {
|
|
this.contacts = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Utility functions
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Removes all tracked pointers.
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function clearTrackedPointers( tracker ) {
|
|
var delegate = THIS[ tracker.hash ],
|
|
i,
|
|
pointerListCount = delegate.activePointersLists.length;
|
|
|
|
for ( i = 0; i < pointerListCount; i++ ) {
|
|
if ( delegate.activePointersLists[ i ].captureCount > 0 ) {
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
'mousemove',
|
|
delegate.mousemovecaptured,
|
|
true
|
|
);
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
'mouseup',
|
|
delegate.mouseupcaptured,
|
|
true
|
|
);
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
$.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
|
|
delegate.pointermovecaptured,
|
|
true
|
|
);
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
$.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
|
|
delegate.pointerupcaptured,
|
|
true
|
|
);
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
'touchmove',
|
|
delegate.touchmovecaptured,
|
|
true
|
|
);
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
'touchend',
|
|
delegate.touchendcaptured,
|
|
true
|
|
);
|
|
|
|
delegate.activePointersLists[ i ].captureCount = 0;
|
|
}
|
|
}
|
|
|
|
for ( i = 0; i < pointerListCount; i++ ) {
|
|
delegate.activePointersLists.pop();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts tracking pointer events on the tracked element.
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function startTracking( tracker ) {
|
|
var delegate = THIS[ tracker.hash ],
|
|
event,
|
|
i;
|
|
|
|
if ( !delegate.tracking ) {
|
|
for ( i = 0; i < $.MouseTracker.subscribeEvents.length; i++ ) {
|
|
event = $.MouseTracker.subscribeEvents[ i ];
|
|
$.addEvent(
|
|
tracker.element,
|
|
event,
|
|
delegate[ event ],
|
|
false
|
|
);
|
|
}
|
|
|
|
clearTrackedPointers( tracker );
|
|
|
|
delegate.tracking = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops tracking pointer events on the tracked element.
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function stopTracking( tracker ) {
|
|
var delegate = THIS[ tracker.hash ],
|
|
event,
|
|
i;
|
|
|
|
if ( delegate.tracking ) {
|
|
for ( i = 0; i < $.MouseTracker.subscribeEvents.length; i++ ) {
|
|
event = $.MouseTracker.subscribeEvents[ i ];
|
|
$.removeEvent(
|
|
tracker.element,
|
|
event,
|
|
delegate[ event ],
|
|
false
|
|
);
|
|
}
|
|
|
|
clearTrackedPointers( tracker );
|
|
|
|
delegate.tracking = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function getCaptureEventParams( tracker, pointerType ) {
|
|
var delegate = THIS[ tracker.hash ];
|
|
|
|
if ( pointerType === 'pointerevent' ) {
|
|
return {
|
|
upName: $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
|
|
upHandler: delegate.pointerupcaptured,
|
|
moveName: $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
|
|
moveHandler: delegate.pointermovecaptured
|
|
};
|
|
} else if ( pointerType === 'mouse' ) {
|
|
return {
|
|
upName: 'mouseup',
|
|
upHandler: delegate.mouseupcaptured,
|
|
moveName: 'mousemove',
|
|
moveHandler: delegate.mousemovecaptured
|
|
};
|
|
} else if ( pointerType === 'touch' ) {
|
|
return {
|
|
upName: 'touchend',
|
|
upHandler: delegate.touchendcaptured,
|
|
moveName: 'touchmove',
|
|
moveHandler: delegate.touchmovecaptured
|
|
};
|
|
} else {
|
|
throw new Error( "MouseTracker.getCaptureEventParams: Unknown pointer type." );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Begin capturing pointer events to the tracked element.
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function capturePointer( tracker, pointerType, pointerCount ) {
|
|
var pointsList = tracker.getActivePointersListByType( pointerType ),
|
|
eventParams;
|
|
|
|
pointsList.captureCount += (pointerCount || 1);
|
|
|
|
if ( pointsList.captureCount === 1 ) {
|
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
|
tracker.element.setCapture( true );
|
|
} else {
|
|
eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType );
|
|
// We emulate mouse capture by hanging listeners on the document object.
|
|
// (Note we listen on the capture phase so the captured handlers will get called first)
|
|
// eslint-disable-next-line no-use-before-define
|
|
if (isInIframe && canAccessEvents(window.top)) {
|
|
$.addEvent(
|
|
window.top,
|
|
eventParams.upName,
|
|
eventParams.upHandler,
|
|
true
|
|
);
|
|
}
|
|
$.addEvent(
|
|
$.MouseTracker.captureElement,
|
|
eventParams.upName,
|
|
eventParams.upHandler,
|
|
true
|
|
);
|
|
$.addEvent(
|
|
$.MouseTracker.captureElement,
|
|
eventParams.moveName,
|
|
eventParams.moveHandler,
|
|
true
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Stop capturing pointer events to the tracked element.
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function releasePointer( tracker, pointerType, pointerCount ) {
|
|
var pointsList = tracker.getActivePointersListByType( pointerType ),
|
|
eventParams;
|
|
|
|
pointsList.captureCount -= (pointerCount || 1);
|
|
|
|
if ( pointsList.captureCount === 0 ) {
|
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
|
tracker.element.releaseCapture();
|
|
} else {
|
|
eventParams = getCaptureEventParams( tracker, $.MouseTracker.havePointerEvents ? 'pointerevent' : pointerType );
|
|
// We emulate mouse capture by hanging listeners on the document object.
|
|
// (Note we listen on the capture phase so the captured handlers will get called first)
|
|
// eslint-disable-next-line no-use-before-define
|
|
if (isInIframe && canAccessEvents(window.top)) {
|
|
$.removeEvent(
|
|
window.top,
|
|
eventParams.upName,
|
|
eventParams.upHandler,
|
|
true
|
|
);
|
|
}
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
eventParams.moveName,
|
|
eventParams.moveHandler,
|
|
true
|
|
);
|
|
$.removeEvent(
|
|
$.MouseTracker.captureElement,
|
|
eventParams.upName,
|
|
eventParams.upHandler,
|
|
true
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets a W3C Pointer Events model compatible pointer type string from a DOM pointer event.
|
|
* IE10 used a long integer value, but the W3C specification (and IE11+) use a string "mouse", "touch", "pen", etc.
|
|
* @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 getPointRelativeToAbsolute( getMouseAbsolute( event ), element );
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function getPointRelativeToAbsolute( point, element ) {
|
|
var offset = $.getElementOffset( element );
|
|
return point.minus( offset );
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function getCenterPoint( point1, point2 ) {
|
|
return new $.Point( ( point1.x + point2.x ) / 2, ( point1.y + point2.y ) / 2 );
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Device-specific DOM event handlers
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onClick( tracker, event ) {
|
|
if ( tracker.clickHandler ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onDblClick( tracker, event ) {
|
|
if ( tracker.dblClickHandler ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onKeyDown( tracker, event ) {
|
|
//$.console.log( "keydown %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey );
|
|
var propagate;
|
|
if ( tracker.keyDownHandler ) {
|
|
event = $.getEvent( event );
|
|
propagate = tracker.keyDownHandler(
|
|
{
|
|
eventSource: tracker,
|
|
keyCode: event.keyCode ? event.keyCode : event.charCode,
|
|
ctrl: event.ctrlKey,
|
|
shift: event.shiftKey,
|
|
alt: event.altKey,
|
|
meta: event.metaKey,
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( !propagate ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onKeyUp( tracker, event ) {
|
|
//$.console.log( "keyup %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey );
|
|
var propagate;
|
|
if ( tracker.keyUpHandler ) {
|
|
event = $.getEvent( event );
|
|
propagate = tracker.keyUpHandler(
|
|
{
|
|
eventSource: tracker,
|
|
keyCode: event.keyCode ? event.keyCode : event.charCode,
|
|
ctrl: event.ctrlKey,
|
|
shift: event.shiftKey,
|
|
alt: event.altKey,
|
|
meta: event.metaKey,
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( !propagate ) {
|
|
$.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,
|
|
keyCode: event.keyCode ? event.keyCode : event.charCode,
|
|
ctrl: event.ctrlKey,
|
|
shift: event.shiftKey,
|
|
alt: event.altKey,
|
|
meta: event.metaKey,
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( !propagate ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onFocus( tracker, event ) {
|
|
//console.log( "focus %s", event );
|
|
var propagate;
|
|
if ( tracker.focusHandler ) {
|
|
event = $.getEvent( event );
|
|
propagate = tracker.focusHandler(
|
|
{
|
|
eventSource: tracker,
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onBlur( tracker, event ) {
|
|
//console.log( "blur %s", event );
|
|
var propagate;
|
|
if ( tracker.blurHandler ) {
|
|
event = $.getEvent( event );
|
|
propagate = tracker.blurHandler(
|
|
{
|
|
eventSource: tracker,
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Handler for 'wheel' events
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onWheel( tracker, event ) {
|
|
handleWheelEvent( tracker, event, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* Handler for 'mousewheel', 'DOMMouseScroll', and 'MozMousePixelScroll' events
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseWheel( tracker, event ) {
|
|
event = $.getEvent( event );
|
|
|
|
// Simulate a 'wheel' event
|
|
var simulatedEvent = {
|
|
target: event.target || event.srcElement,
|
|
type: "wheel",
|
|
shiftKey: event.shiftKey || false,
|
|
clientX: event.clientX,
|
|
clientY: event.clientY,
|
|
pageX: event.pageX ? event.pageX : event.clientX,
|
|
pageY: event.pageY ? event.pageY : event.clientY,
|
|
deltaMode: event.type == "MozMousePixelScroll" ? 0 : 1, // 0=pixel, 1=line, 2=page
|
|
deltaX: 0,
|
|
deltaZ: 0
|
|
};
|
|
|
|
// Calculate deltaY
|
|
if ( $.MouseTracker.wheelEventName == "mousewheel" ) {
|
|
simulatedEvent.deltaY = -event.wheelDelta / $.DEFAULT_SETTINGS.pixelsPerWheelLine;
|
|
} else {
|
|
simulatedEvent.deltaY = event.detail;
|
|
}
|
|
|
|
handleWheelEvent( tracker, simulatedEvent, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles 'wheel' events.
|
|
* The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()).
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleWheelEvent( tracker, event, originalEvent ) {
|
|
var nDelta = 0,
|
|
propagate;
|
|
|
|
// 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.
|
|
// event.deltaMode: 0=pixel, 1=line, 2=page
|
|
// TODO: Deltas in pixel mode should be accumulated then a scroll value computed after $.DEFAULT_SETTINGS.pixelsPerWheelLine threshold reached
|
|
nDelta = event.deltaY < 0 ? 1 : -1;
|
|
|
|
if ( tracker.scrollHandler ) {
|
|
propagate = tracker.scrollHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: 'mouse',
|
|
position: getMouseRelative( event, tracker.element ),
|
|
scroll: nDelta,
|
|
shift: event.shiftKey,
|
|
isTouchEvent: false,
|
|
originalEvent: originalEvent,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( originalEvent );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function isParentChild( parent, child )
|
|
{
|
|
if ( parent === child ) {
|
|
return false;
|
|
}
|
|
while ( child && child !== parent ) {
|
|
child = child.parentNode;
|
|
}
|
|
return child === parent;
|
|
}
|
|
|
|
|
|
/**
|
|
* Only used on IE 8
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseEnter( tracker, event ) {
|
|
event = $.getEvent( event );
|
|
|
|
handleMouseEnter( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseOver( tracker, event ) {
|
|
event = $.getEvent( event );
|
|
|
|
if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
|
|
return;
|
|
}
|
|
|
|
handleMouseEnter( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleMouseEnter( tracker, event ) {
|
|
var gPoint = {
|
|
id: $.MouseTracker.mousePointerId,
|
|
type: 'mouse',
|
|
isPrimary: true,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
updatePointersEnter( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* Only used on IE 8
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseLeave( tracker, event ) {
|
|
event = $.getEvent( event );
|
|
|
|
handleMouseExit( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseOut( tracker, event ) {
|
|
event = $.getEvent( event );
|
|
|
|
if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
|
|
return;
|
|
}
|
|
|
|
handleMouseExit( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleMouseExit( tracker, event ) {
|
|
var gPoint = {
|
|
id: $.MouseTracker.mousePointerId,
|
|
type: 'mouse',
|
|
isPrimary: true,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
updatePointersExit( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a W3C DOM level 3 standard button value given an event.button property:
|
|
* -1 == none, 0 == primary/left, 1 == middle, 2 == secondary/right, 3 == X1/back, 4 == X2/forward, 5 == eraser (pen)
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function getStandardizedButton( button ) {
|
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
|
// On IE 8, 0 == none, 1 == left, 2 == right, 3 == left and right, 4 == middle, 5 == left and middle, 6 == right and middle, 7 == all three
|
|
// TODO: Support chorded (multiple) button presses on IE 8?
|
|
if ( button === 1 ) {
|
|
return 0;
|
|
} else if ( button === 2 ) {
|
|
return 2;
|
|
} else if ( button === 4 ) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else {
|
|
return button;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseDown( tracker, event ) {
|
|
var gPoint;
|
|
|
|
event = $.getEvent( event );
|
|
|
|
gPoint = {
|
|
id: $.MouseTracker.mousePointerId,
|
|
type: 'mouse',
|
|
isPrimary: true,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
if ( updatePointersDown( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) {
|
|
$.stopEvent( event );
|
|
capturePointer( tracker, 'mouse' );
|
|
}
|
|
|
|
if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseUp( tracker, event ) {
|
|
handleMouseUp( tracker, event );
|
|
}
|
|
|
|
/**
|
|
* This handler is attached to the window object (on the capture phase) to emulate mouse capture.
|
|
* onMouseUp is still attached to the tracked element, so stop propagation to avoid processing twice.
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseUpCaptured( tracker, event ) {
|
|
handleMouseUp( tracker, event );
|
|
$.stopEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleMouseUp( tracker, event ) {
|
|
var gPoint;
|
|
|
|
event = $.getEvent( event );
|
|
|
|
gPoint = {
|
|
id: $.MouseTracker.mousePointerId,
|
|
type: 'mouse',
|
|
isPrimary: true,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
if ( updatePointersUp( tracker, event, [ gPoint ], getStandardizedButton( event.button ) ) ) {
|
|
releasePointer( tracker, 'mouse' );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseMove( tracker, event ) {
|
|
handleMouseMove( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* This handler is attached to the window object (on the capture phase) to emulate mouse capture.
|
|
* onMouseMove is still attached to the tracked element, so stop propagation to avoid processing twice.
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onMouseMoveCaptured( tracker, event ) {
|
|
handleMouseMove( tracker, event );
|
|
$.stopEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleMouseMove( tracker, event ) {
|
|
var gPoint;
|
|
|
|
event = $.getEvent( event );
|
|
|
|
gPoint = {
|
|
id: $.MouseTracker.mousePointerId,
|
|
type: 'mouse',
|
|
isPrimary: true,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
updatePointersMove( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function abortContacts( tracker, event, pointsList ) {
|
|
var i,
|
|
gPointCount = pointsList.getLength(),
|
|
abortGPoints = [];
|
|
|
|
// Check contact count for hoverable pointer types before aborting
|
|
if (pointsList.type === 'touch' || pointsList.contacts > 0) {
|
|
for ( i = 0; i < gPointCount; i++ ) {
|
|
abortGPoints.push( pointsList.getByIndex( i ) );
|
|
}
|
|
|
|
if ( abortGPoints.length > 0 ) {
|
|
// simulate touchend/mouseup
|
|
updatePointersUp( tracker, event, abortGPoints, 0 ); // 0 means primary button press/release or touch contact
|
|
// release pointer capture
|
|
pointsList.captureCount = 1;
|
|
releasePointer( tracker, pointsList.type );
|
|
// simulate touchleave/mouseout
|
|
updatePointersExit( tracker, event, abortGPoints );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onTouchStart( tracker, event ) {
|
|
var time,
|
|
i,
|
|
j,
|
|
touchCount = event.changedTouches.length,
|
|
gPoints = [],
|
|
parentGPoints,
|
|
pointsList = tracker.getActivePointersListByType( 'touch' );
|
|
|
|
time = $.now();
|
|
|
|
if ( pointsList.getLength() > event.touches.length - touchCount ) {
|
|
$.console.warn('Tracked touch contact count doesn\'t match event.touches.length. Removing all tracked touch pointers.');
|
|
abortContacts( tracker, event, pointsList );
|
|
}
|
|
|
|
for ( i = 0; i < touchCount; i++ ) {
|
|
gPoints.push( {
|
|
id: event.changedTouches[ i ].identifier,
|
|
type: 'touch',
|
|
// isPrimary not set - let the updatePointers functions determine it
|
|
currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
|
|
currentTime: time
|
|
} );
|
|
}
|
|
|
|
// simulate touchenter on our tracked element
|
|
updatePointersEnter( tracker, event, gPoints );
|
|
|
|
// simulate touchenter on our tracked element's tracked ancestor elements
|
|
for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
|
|
if ( MOUSETRACKERS[ i ] !== tracker && MOUSETRACKERS[ i ].isTracking() && isParentChild( MOUSETRACKERS[ i ].element, tracker.element ) ) {
|
|
parentGPoints = [];
|
|
for ( j = 0; j < touchCount; j++ ) {
|
|
parentGPoints.push( {
|
|
id: event.changedTouches[ j ].identifier,
|
|
type: 'touch',
|
|
// isPrimary not set - let the updatePointers functions determine it
|
|
currentPos: getMouseAbsolute( event.changedTouches[ j ] ),
|
|
currentTime: time
|
|
} );
|
|
}
|
|
updatePointersEnter( MOUSETRACKERS[ i ], event, parentGPoints );
|
|
}
|
|
}
|
|
|
|
if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
|
|
$.stopEvent( event );
|
|
capturePointer( tracker, 'touch', touchCount );
|
|
}
|
|
|
|
$.cancelEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onTouchEnd( tracker, event ) {
|
|
handleTouchEnd( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* This handler is attached to the window object (on the capture phase) to emulate pointer capture.
|
|
* onTouchEnd is still attached to the tracked element, so stop propagation to avoid processing twice.
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onTouchEndCaptured( tracker, event ) {
|
|
handleTouchEnd( tracker, event );
|
|
$.stopEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleTouchEnd( tracker, event ) {
|
|
var time,
|
|
i,
|
|
j,
|
|
touchCount = event.changedTouches.length,
|
|
gPoints = [],
|
|
parentGPoints;
|
|
|
|
time = $.now();
|
|
|
|
for ( i = 0; i < touchCount; i++ ) {
|
|
gPoints.push( {
|
|
id: event.changedTouches[ i ].identifier,
|
|
type: 'touch',
|
|
// isPrimary not set - let the updatePointers functions determine it
|
|
currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
|
|
currentTime: time
|
|
} );
|
|
}
|
|
|
|
if ( updatePointersUp( tracker, event, gPoints, 0 ) ) {
|
|
releasePointer( tracker, 'touch', touchCount );
|
|
}
|
|
|
|
// simulate touchleave on our tracked element
|
|
updatePointersExit( tracker, event, gPoints );
|
|
|
|
// simulate touchleave on our tracked element's tracked ancestor elements
|
|
for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
|
|
if ( MOUSETRACKERS[ i ] !== tracker && MOUSETRACKERS[ i ].isTracking() && isParentChild( MOUSETRACKERS[ i ].element, tracker.element ) ) {
|
|
parentGPoints = [];
|
|
for ( j = 0; j < touchCount; j++ ) {
|
|
parentGPoints.push( {
|
|
id: event.changedTouches[ j ].identifier,
|
|
type: 'touch',
|
|
// isPrimary not set - let the updatePointers functions determine it
|
|
currentPos: getMouseAbsolute( event.changedTouches[ j ] ),
|
|
currentTime: time
|
|
} );
|
|
}
|
|
updatePointersExit( MOUSETRACKERS[ i ], event, parentGPoints );
|
|
}
|
|
}
|
|
|
|
$.cancelEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onTouchMove( tracker, event ) {
|
|
handleTouchMove( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* This handler is attached to the window object (on the capture phase) to emulate pointer capture.
|
|
* onTouchMove is still attached to the tracked element, so stop propagation to avoid processing twice.
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onTouchMoveCaptured( tracker, event ) {
|
|
handleTouchMove( tracker, event );
|
|
$.stopEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handleTouchMove( tracker, event ) {
|
|
var i,
|
|
touchCount = event.changedTouches.length,
|
|
gPoints = [];
|
|
|
|
for ( i = 0; i < touchCount; i++ ) {
|
|
gPoints.push( {
|
|
id: event.changedTouches[ i ].identifier,
|
|
type: 'touch',
|
|
// isPrimary not set - let the updatePointers functions determine it
|
|
currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
|
|
currentTime: $.now()
|
|
} );
|
|
}
|
|
|
|
updatePointersMove( tracker, event, gPoints );
|
|
|
|
$.cancelEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onTouchCancel( tracker, event ) {
|
|
var pointsList = tracker.getActivePointersListByType('touch');
|
|
|
|
abortContacts( tracker, event, pointsList );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onGestureStart( tracker, event ) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onGestureChange( tracker, event ) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerOver( tracker, event ) {
|
|
var gPoint;
|
|
|
|
if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
|
|
return;
|
|
}
|
|
|
|
gPoint = {
|
|
id: event.pointerId,
|
|
type: getPointerType( event ),
|
|
isPrimary: event.isPrimary,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
updatePointersEnter( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerOut( tracker, event ) {
|
|
var gPoint;
|
|
|
|
if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
|
|
return;
|
|
}
|
|
|
|
gPoint = {
|
|
id: event.pointerId,
|
|
type: getPointerType( event ),
|
|
isPrimary: event.isPrimary,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
updatePointersExit( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerDown( tracker, event ) {
|
|
var gPoint;
|
|
|
|
gPoint = {
|
|
id: event.pointerId,
|
|
type: getPointerType( event ),
|
|
isPrimary: event.isPrimary,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) {
|
|
$.stopEvent( event );
|
|
capturePointer( tracker, gPoint.type );
|
|
}
|
|
|
|
if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerUp( tracker, event ) {
|
|
handlePointerUp( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* This handler is attached to the window object (on the capture phase) to emulate mouse capture.
|
|
* onPointerUp is still attached to the tracked element, so stop propagation to avoid processing twice.
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerUpCaptured( tracker, event ) {
|
|
var pointsList = tracker.getActivePointersListByType( getPointerType( event ) );
|
|
if ( pointsList.getById( event.pointerId ) ) {
|
|
handlePointerUp( tracker, event );
|
|
}
|
|
$.stopEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handlePointerUp( tracker, event ) {
|
|
var gPoint;
|
|
|
|
gPoint = {
|
|
id: event.pointerId,
|
|
type: getPointerType( event ),
|
|
isPrimary: event.isPrimary,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) {
|
|
releasePointer( tracker, gPoint.type );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerMove( tracker, event ) {
|
|
handlePointerMove( tracker, event );
|
|
}
|
|
|
|
|
|
/**
|
|
* This handler is attached to the window object (on the capture phase) to emulate mouse capture.
|
|
* onPointerMove is still attached to the tracked element, so stop propagation to avoid processing twice.
|
|
*
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerMoveCaptured( tracker, event ) {
|
|
var pointsList = tracker.getActivePointersListByType( getPointerType( event ) );
|
|
if ( pointsList.getById( event.pointerId ) ) {
|
|
handlePointerMove( tracker, event );
|
|
}
|
|
$.stopEvent( event );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handlePointerMove( tracker, event ) {
|
|
// Pointer changed coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height)
|
|
var gPoint;
|
|
|
|
gPoint = {
|
|
id: event.pointerId,
|
|
type: getPointerType( event ),
|
|
isPrimary: event.isPrimary,
|
|
currentPos: getMouseAbsolute( event ),
|
|
currentTime: $.now()
|
|
};
|
|
|
|
updatePointersMove( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function onPointerCancel( tracker, event ) {
|
|
var gPoint;
|
|
|
|
gPoint = {
|
|
id: event.pointerId,
|
|
type: getPointerType( event )
|
|
};
|
|
|
|
updatePointersCancel( tracker, event, [ gPoint ] );
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Device-agnostic DOM event handlers
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker.GesturePointList} pointsList
|
|
* The GesturePointList to track the pointer in.
|
|
* @param {OpenSeadragon.MouseTracker.GesturePoint} gPoint
|
|
* Gesture point to track.
|
|
* @returns {Number} Number of gesture points in pointsList.
|
|
*/
|
|
function startTrackingPointer( pointsList, gPoint ) {
|
|
|
|
// If isPrimary is not known for the pointer then set it according to our rules:
|
|
// true if the first pointer in the gesture, otherwise false
|
|
if ( !gPoint.hasOwnProperty( 'isPrimary' ) ) {
|
|
if ( pointsList.getLength() === 0 ) {
|
|
gPoint.isPrimary = true;
|
|
} else {
|
|
gPoint.isPrimary = false;
|
|
}
|
|
}
|
|
gPoint.speed = 0;
|
|
gPoint.direction = 0;
|
|
gPoint.contactPos = gPoint.currentPos;
|
|
gPoint.contactTime = gPoint.currentTime;
|
|
gPoint.lastPos = gPoint.currentPos;
|
|
gPoint.lastTime = gPoint.currentTime;
|
|
|
|
return pointsList.add( gPoint );
|
|
}
|
|
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker.GesturePointList} pointsList
|
|
* The GesturePointList to stop tracking the pointer on.
|
|
* @param {OpenSeadragon.MouseTracker.GesturePoint} gPoint
|
|
* Gesture point to stop tracking.
|
|
* @returns {Number} Number of gesture points in pointsList.
|
|
*/
|
|
function stopTrackingPointer( pointsList, gPoint ) {
|
|
var listLength,
|
|
primaryPoint;
|
|
|
|
if ( pointsList.getById( gPoint.id ) ) {
|
|
listLength = pointsList.removeById( gPoint.id );
|
|
|
|
// If isPrimary is not known for the pointer and we just removed the primary pointer from the list then we need to set another pointer as primary
|
|
if ( !gPoint.hasOwnProperty( 'isPrimary' ) ) {
|
|
primaryPoint = pointsList.getPrimary();
|
|
if ( !primaryPoint ) {
|
|
primaryPoint = pointsList.getByIndex( 0 );
|
|
if ( primaryPoint ) {
|
|
primaryPoint.isPrimary = true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
listLength = pointsList.getLength();
|
|
}
|
|
|
|
return listLength;
|
|
}
|
|
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker} tracker
|
|
* A reference to the MouseTracker instance.
|
|
* @param {Object} event
|
|
* A reference to the originating DOM event.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
|
|
* Gesture points associated with the event.
|
|
*/
|
|
function updatePointersEnter( tracker, event, gPoints ) {
|
|
var pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
|
|
i,
|
|
gPointCount = gPoints.length,
|
|
curGPoint,
|
|
updateGPoint,
|
|
propagate;
|
|
|
|
for ( i = 0; i < gPointCount; i++ ) {
|
|
curGPoint = gPoints[ i ];
|
|
updateGPoint = pointsList.getById( curGPoint.id );
|
|
|
|
if ( updateGPoint ) {
|
|
// Already tracking the pointer...update it
|
|
updateGPoint.insideElement = true;
|
|
updateGPoint.lastPos = updateGPoint.currentPos;
|
|
updateGPoint.lastTime = updateGPoint.currentTime;
|
|
updateGPoint.currentPos = curGPoint.currentPos;
|
|
updateGPoint.currentTime = curGPoint.currentTime;
|
|
|
|
curGPoint = updateGPoint;
|
|
} else {
|
|
// Initialize for tracking and add to the tracking list
|
|
curGPoint.captured = false;
|
|
curGPoint.insideElementPressed = false;
|
|
curGPoint.insideElement = true;
|
|
startTrackingPointer( pointsList, curGPoint );
|
|
}
|
|
|
|
// Enter
|
|
if ( tracker.enterHandler ) {
|
|
propagate = tracker.enterHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: curGPoint.type,
|
|
position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
pointers: tracker.getActivePointerCount(),
|
|
insideElementPressed: curGPoint.insideElementPressed,
|
|
buttonDownAny: pointsList.buttons !== 0,
|
|
isTouchEvent: curGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker} tracker
|
|
* A reference to the MouseTracker instance.
|
|
* @param {Object} event
|
|
* A reference to the originating DOM event.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
|
|
* Gesture points associated with the event.
|
|
*/
|
|
function updatePointersExit( tracker, event, gPoints ) {
|
|
var pointsList = tracker.getActivePointersListByType(gPoints[0].type),
|
|
i,
|
|
gPointCount = gPoints.length,
|
|
curGPoint,
|
|
updateGPoint,
|
|
propagate;
|
|
|
|
for ( i = 0; i < gPointCount; i++ ) {
|
|
curGPoint = gPoints[ i ];
|
|
updateGPoint = pointsList.getById( curGPoint.id );
|
|
|
|
if ( updateGPoint ) {
|
|
// Already tracking the pointer. If captured then update it, else stop tracking it
|
|
if ( updateGPoint.captured ) {
|
|
updateGPoint.insideElement = false;
|
|
updateGPoint.lastPos = updateGPoint.currentPos;
|
|
updateGPoint.lastTime = updateGPoint.currentTime;
|
|
updateGPoint.currentPos = curGPoint.currentPos;
|
|
updateGPoint.currentTime = curGPoint.currentTime;
|
|
} else {
|
|
stopTrackingPointer( pointsList, updateGPoint );
|
|
}
|
|
|
|
curGPoint = updateGPoint;
|
|
}
|
|
|
|
// Exit
|
|
if ( tracker.exitHandler ) {
|
|
propagate = tracker.exitHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: curGPoint.type,
|
|
position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
pointers: tracker.getActivePointerCount(),
|
|
insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false,
|
|
buttonDownAny: pointsList.buttons !== 0,
|
|
isTouchEvent: curGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker} tracker
|
|
* A reference to the MouseTracker instance.
|
|
* @param {Object} event
|
|
* A reference to the originating DOM event.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
|
|
* Gesture points associated with the event.
|
|
* @param {Number} buttonChanged
|
|
* The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
|
|
* Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,
|
|
* only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.
|
|
*
|
|
* @returns {Boolean} True if pointers should be captured to the tracked element, otherwise false.
|
|
*/
|
|
function updatePointersDown( tracker, event, gPoints, buttonChanged ) {
|
|
var delegate = THIS[ tracker.hash ],
|
|
propagate,
|
|
pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
|
|
i,
|
|
gPointCount = gPoints.length,
|
|
curGPoint,
|
|
updateGPoint;
|
|
|
|
if ( typeof event.buttons !== 'undefined' ) {
|
|
pointsList.buttons = event.buttons;
|
|
} else {
|
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
|
if ( buttonChanged === 0 ) {
|
|
// Primary
|
|
pointsList.buttons += 1;
|
|
} else if ( buttonChanged === 1 ) {
|
|
// Aux
|
|
pointsList.buttons += 4;
|
|
} else if ( buttonChanged === 2 ) {
|
|
// Secondary
|
|
pointsList.buttons += 2;
|
|
} else if ( buttonChanged === 3 ) {
|
|
// X1 (Back)
|
|
pointsList.buttons += 8;
|
|
} else if ( buttonChanged === 4 ) {
|
|
// X2 (Forward)
|
|
pointsList.buttons += 16;
|
|
} else if ( buttonChanged === 5 ) {
|
|
// Pen Eraser
|
|
pointsList.buttons += 32;
|
|
}
|
|
} else {
|
|
if ( buttonChanged === 0 ) {
|
|
// Primary
|
|
pointsList.buttons |= 1;
|
|
} else if ( buttonChanged === 1 ) {
|
|
// Aux
|
|
pointsList.buttons |= 4;
|
|
} else if ( buttonChanged === 2 ) {
|
|
// Secondary
|
|
pointsList.buttons |= 2;
|
|
} else if ( buttonChanged === 3 ) {
|
|
// X1 (Back)
|
|
pointsList.buttons |= 8;
|
|
} else if ( buttonChanged === 4 ) {
|
|
// X2 (Forward)
|
|
pointsList.buttons |= 16;
|
|
} else if ( buttonChanged === 5 ) {
|
|
// Pen Eraser
|
|
pointsList.buttons |= 32;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Some pointers may steal control from another pointer without firing the appropriate release events
|
|
// e.g. Touching a screen while click-dragging with certain mice.
|
|
var otherPointsLists = tracker.getActivePointersListsExceptType(gPoints[ 0 ].type);
|
|
for (i = 0; i < otherPointsLists.length; i++) {
|
|
//If another pointer has contact, simulate the release
|
|
abortContacts(tracker, event, otherPointsLists[i]); // No-op if no active pointer
|
|
}
|
|
|
|
// Only capture and track primary button, pen, and touch contacts
|
|
if ( buttonChanged !== 0 ) {
|
|
// Aux Press
|
|
if ( tracker.nonPrimaryPressHandler ) {
|
|
propagate = tracker.nonPrimaryPressHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: gPoints[ 0 ].type,
|
|
position: getPointRelativeToAbsolute( gPoints[ 0 ].currentPos, tracker.element ),
|
|
button: buttonChanged,
|
|
buttons: pointsList.buttons,
|
|
isTouchEvent: gPoints[ 0 ].type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; i < gPointCount; i++ ) {
|
|
curGPoint = gPoints[ i ];
|
|
updateGPoint = pointsList.getById( curGPoint.id );
|
|
|
|
if ( updateGPoint ) {
|
|
// Already tracking the pointer...update it
|
|
updateGPoint.captured = true;
|
|
updateGPoint.insideElementPressed = true;
|
|
updateGPoint.insideElement = true;
|
|
updateGPoint.contactPos = curGPoint.currentPos;
|
|
updateGPoint.contactTime = curGPoint.currentTime;
|
|
updateGPoint.lastPos = updateGPoint.currentPos;
|
|
updateGPoint.lastTime = updateGPoint.currentTime;
|
|
updateGPoint.currentPos = curGPoint.currentPos;
|
|
updateGPoint.currentTime = curGPoint.currentTime;
|
|
|
|
curGPoint = updateGPoint;
|
|
} else {
|
|
// Initialize for tracking and add to the tracking list (no pointerover or pointermove event occurred before this)
|
|
curGPoint.captured = true;
|
|
curGPoint.insideElementPressed = true;
|
|
curGPoint.insideElement = true;
|
|
startTrackingPointer( pointsList, curGPoint );
|
|
}
|
|
|
|
pointsList.addContact();
|
|
//$.console.log('contacts++ ', pointsList.contacts);
|
|
|
|
if ( tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
|
|
$.MouseTracker.gesturePointVelocityTracker.addPoint( tracker, curGPoint );
|
|
}
|
|
|
|
if ( pointsList.contacts === 1 ) {
|
|
// Press
|
|
if ( tracker.pressHandler ) {
|
|
propagate = tracker.pressHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: curGPoint.type,
|
|
position: getPointRelativeToAbsolute( curGPoint.contactPos, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
isTouchEvent: curGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
} else if ( pointsList.contacts === 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 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker} tracker
|
|
* A reference to the MouseTracker instance.
|
|
* @param {Object} event
|
|
* A reference to the originating DOM event.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
|
|
* Gesture points associated with the event.
|
|
* @param {Number} buttonChanged
|
|
* The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.
|
|
* Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,
|
|
* only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.
|
|
*
|
|
* @returns {Boolean} True if pointer capture should be released from the tracked element, otherwise false.
|
|
*/
|
|
function updatePointersUp( tracker, event, gPoints, buttonChanged ) {
|
|
var delegate = THIS[ tracker.hash ],
|
|
pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
|
|
propagate,
|
|
releasePoint,
|
|
releaseTime,
|
|
i,
|
|
gPointCount = gPoints.length,
|
|
curGPoint,
|
|
updateGPoint,
|
|
releaseCapture = false,
|
|
wasCaptured = false,
|
|
quick;
|
|
|
|
if ( typeof event.buttons !== 'undefined' ) {
|
|
pointsList.buttons = event.buttons;
|
|
} else {
|
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
|
if ( buttonChanged === 0 ) {
|
|
// Primary
|
|
pointsList.buttons -= 1;
|
|
} else if ( buttonChanged === 1 ) {
|
|
// Aux
|
|
pointsList.buttons -= 4;
|
|
} else if ( buttonChanged === 2 ) {
|
|
// Secondary
|
|
pointsList.buttons -= 2;
|
|
} else if ( buttonChanged === 3 ) {
|
|
// X1 (Back)
|
|
pointsList.buttons -= 8;
|
|
} else if ( buttonChanged === 4 ) {
|
|
// X2 (Forward)
|
|
pointsList.buttons -= 16;
|
|
} else if ( buttonChanged === 5 ) {
|
|
// Pen Eraser
|
|
pointsList.buttons -= 32;
|
|
}
|
|
} else {
|
|
if ( buttonChanged === 0 ) {
|
|
// Primary
|
|
pointsList.buttons ^= ~1;
|
|
} else if ( buttonChanged === 1 ) {
|
|
// Aux
|
|
pointsList.buttons ^= ~4;
|
|
} else if ( buttonChanged === 2 ) {
|
|
// Secondary
|
|
pointsList.buttons ^= ~2;
|
|
} else if ( buttonChanged === 3 ) {
|
|
// X1 (Back)
|
|
pointsList.buttons ^= ~8;
|
|
} else if ( buttonChanged === 4 ) {
|
|
// X2 (Forward)
|
|
pointsList.buttons ^= ~16;
|
|
} else if ( buttonChanged === 5 ) {
|
|
// Pen Eraser
|
|
pointsList.buttons ^= ~32;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only capture and track primary button, pen, and touch contacts
|
|
if ( buttonChanged !== 0 ) {
|
|
// Aux Release
|
|
if ( tracker.nonPrimaryReleaseHandler ) {
|
|
propagate = tracker.nonPrimaryReleaseHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: gPoints[ 0 ].type,
|
|
position: getPointRelativeToAbsolute(gPoints[0].currentPos, tracker.element),
|
|
button: buttonChanged,
|
|
buttons: pointsList.buttons,
|
|
isTouchEvent: gPoints[ 0 ].type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
// A primary mouse button may have been released while the non-primary button was down
|
|
var otherPointsList = tracker.getActivePointersListByType("mouse");
|
|
// Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223
|
|
abortContacts(tracker, event, otherPointsList); // No-op if no active pointer
|
|
|
|
return false;
|
|
}
|
|
|
|
for ( i = 0; i < gPointCount; i++ ) {
|
|
curGPoint = gPoints[ i ];
|
|
updateGPoint = pointsList.getById( curGPoint.id );
|
|
|
|
if ( updateGPoint ) {
|
|
// Update the pointer, stop tracking it if not still in this element
|
|
if ( updateGPoint.captured ) {
|
|
updateGPoint.captured = false;
|
|
releaseCapture = true;
|
|
wasCaptured = true;
|
|
}
|
|
updateGPoint.lastPos = updateGPoint.currentPos;
|
|
updateGPoint.lastTime = updateGPoint.currentTime;
|
|
updateGPoint.currentPos = curGPoint.currentPos;
|
|
updateGPoint.currentTime = curGPoint.currentTime;
|
|
if ( !updateGPoint.insideElement ) {
|
|
stopTrackingPointer( pointsList, updateGPoint );
|
|
}
|
|
|
|
releasePoint = updateGPoint.currentPos;
|
|
releaseTime = updateGPoint.currentTime;
|
|
|
|
if ( wasCaptured ) {
|
|
// Pointer was activated in our element but could have been removed in any element since events are captured to our element
|
|
|
|
pointsList.removeContact();
|
|
//$.console.log('contacts-- ', pointsList.contacts);
|
|
|
|
if ( tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
|
|
$.MouseTracker.gesturePointVelocityTracker.removePoint( tracker, updateGPoint );
|
|
}
|
|
|
|
if ( pointsList.contacts === 0 ) {
|
|
|
|
// Release (pressed in our element)
|
|
if ( tracker.releaseHandler ) {
|
|
propagate = tracker.releaseHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( releasePoint, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
insideElementPressed: updateGPoint.insideElementPressed,
|
|
insideElementReleased: updateGPoint.insideElement,
|
|
isTouchEvent: updateGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
// Drag End
|
|
if ( tracker.dragEndHandler && !updateGPoint.currentPos.equals( updateGPoint.contactPos ) ) {
|
|
propagate = tracker.dragEndHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
|
|
speed: updateGPoint.speed,
|
|
direction: updateGPoint.direction,
|
|
shift: event.shiftKey,
|
|
isTouchEvent: updateGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
// Click / Double-Click
|
|
if ( ( tracker.clickHandler || tracker.dblClickHandler ) && updateGPoint.insideElement ) {
|
|
quick = releaseTime - updateGPoint.contactTime <= tracker.clickTimeThreshold &&
|
|
updateGPoint.contactPos.distanceTo( releasePoint ) <= tracker.clickDistThreshold;
|
|
|
|
// Click
|
|
if ( tracker.clickHandler ) {
|
|
propagate = tracker.clickHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
|
|
quick: quick,
|
|
shift: event.shiftKey,
|
|
isTouchEvent: updateGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
// Double-Click
|
|
if ( tracker.dblClickHandler && quick ) {
|
|
pointsList.clicks++;
|
|
if ( pointsList.clicks === 1 ) {
|
|
delegate.lastClickPos = releasePoint;
|
|
/*jshint loopfunc:true*/
|
|
delegate.dblClickTimeOut = setTimeout( function() {
|
|
pointsList.clicks = 0;
|
|
}, tracker.dblClickTimeThreshold );
|
|
/*jshint loopfunc:false*/
|
|
} else if ( pointsList.clicks === 2 ) {
|
|
clearTimeout( delegate.dblClickTimeOut );
|
|
pointsList.clicks = 0;
|
|
if ( delegate.lastClickPos.distanceTo( releasePoint ) <= tracker.dblClickDistThreshold ) {
|
|
propagate = tracker.dblClickHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
|
|
shift: event.shiftKey,
|
|
isTouchEvent: updateGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
delegate.lastClickPos = null;
|
|
}
|
|
}
|
|
}
|
|
} else if ( pointsList.contacts === 2 ) {
|
|
if ( tracker.pinchHandler && updateGPoint.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 );
|
|
}
|
|
}
|
|
} else {
|
|
// Pointer was activated in another element but removed in our element
|
|
|
|
// Release (pressed in another element)
|
|
if ( tracker.releaseHandler ) {
|
|
propagate = tracker.releaseHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( releasePoint, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
insideElementPressed: updateGPoint.insideElementPressed,
|
|
insideElementReleased: updateGPoint.insideElement,
|
|
isTouchEvent: updateGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return releaseCapture;
|
|
}
|
|
|
|
|
|
/**
|
|
* Call when pointer(s) change coordinates, button state, pressure, tilt, or contact geometry (e.g. width and height)
|
|
*
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker} tracker
|
|
* A reference to the MouseTracker instance.
|
|
* @param {Object} event
|
|
* A reference to the originating DOM event.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
|
|
* Gesture points associated with the event.
|
|
*/
|
|
function updatePointersMove( tracker, event, gPoints ) {
|
|
var delegate = THIS[ tracker.hash ],
|
|
pointsList = tracker.getActivePointersListByType( gPoints[ 0 ].type ),
|
|
i,
|
|
gPointCount = gPoints.length,
|
|
curGPoint,
|
|
updateGPoint,
|
|
gPointArray,
|
|
delta,
|
|
propagate;
|
|
|
|
if ( typeof event.buttons !== 'undefined' ) {
|
|
pointsList.buttons = event.buttons;
|
|
}
|
|
|
|
for ( i = 0; i < gPointCount; i++ ) {
|
|
curGPoint = gPoints[ i ];
|
|
updateGPoint = pointsList.getById( curGPoint.id );
|
|
|
|
if ( updateGPoint ) {
|
|
// Already tracking the pointer...update it
|
|
if ( curGPoint.hasOwnProperty( 'isPrimary' ) ) {
|
|
updateGPoint.isPrimary = curGPoint.isPrimary;
|
|
}
|
|
updateGPoint.lastPos = updateGPoint.currentPos;
|
|
updateGPoint.lastTime = updateGPoint.currentTime;
|
|
updateGPoint.currentPos = curGPoint.currentPos;
|
|
updateGPoint.currentTime = curGPoint.currentTime;
|
|
} else {
|
|
// Initialize for tracking and add to the tracking list (no pointerover or pointerdown event occurred before this)
|
|
curGPoint.captured = false;
|
|
curGPoint.insideElementPressed = false;
|
|
curGPoint.insideElement = true;
|
|
startTrackingPointer( pointsList, curGPoint );
|
|
}
|
|
}
|
|
|
|
// Stop (mouse only)
|
|
if ( tracker.stopHandler && gPoints[ 0 ].type === 'mouse' ) {
|
|
clearTimeout( tracker.stopTimeOut );
|
|
tracker.stopTimeOut = setTimeout( function() {
|
|
handlePointerStop( tracker, event, gPoints[ 0 ].type );
|
|
}, tracker.stopDelay );
|
|
}
|
|
|
|
if ( pointsList.contacts === 0 ) {
|
|
// Move (no contacts: hovering mouse or other hover-capable device)
|
|
if ( tracker.moveHandler ) {
|
|
propagate = tracker.moveHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: gPoints[ 0 ].type,
|
|
position: getPointRelativeToAbsolute( gPoints[ 0 ].currentPos, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
isTouchEvent: gPoints[ 0 ].type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
} else if ( pointsList.contacts === 1 ) {
|
|
// Move (1 contact)
|
|
if ( tracker.moveHandler ) {
|
|
updateGPoint = pointsList.asArray()[ 0 ];
|
|
propagate = tracker.moveHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
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: updateGPoint.type,
|
|
position: getPointRelativeToAbsolute( updateGPoint.currentPos, tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
delta: delta,
|
|
speed: updateGPoint.speed,
|
|
direction: updateGPoint.direction,
|
|
shift: event.shiftKey,
|
|
isTouchEvent: updateGPoint.type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
} else if ( pointsList.contacts === 2 ) {
|
|
// Move (2 contacts, use center)
|
|
if ( tracker.moveHandler ) {
|
|
gPointArray = pointsList.asArray();
|
|
propagate = tracker.moveHandler(
|
|
{
|
|
eventSource: tracker,
|
|
pointerType: gPointArray[ 0 ].type,
|
|
position: getPointRelativeToAbsolute( getCenterPoint( gPointArray[ 0 ].currentPos, gPointArray[ 1 ].currentPos ), tracker.element ),
|
|
buttons: pointsList.buttons,
|
|
isTouchEvent: gPointArray[ 0 ].type === 'touch',
|
|
originalEvent: event,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
}
|
|
);
|
|
if ( propagate === false ) {
|
|
$.cancelEvent( event );
|
|
}
|
|
}
|
|
|
|
// 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: getPointRelativeToAbsolute( delegate.lastPinchCenter, tracker.element ),
|
|
center: getPointRelativeToAbsolute( 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 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @param {OpenSeadragon.MouseTracker} tracker
|
|
* A reference to the MouseTracker instance.
|
|
* @param {Object} event
|
|
* A reference to the originating DOM event.
|
|
* @param {Array.<OpenSeadragon.MouseTracker.GesturePoint>} gPoints
|
|
* Gesture points associated with the event.
|
|
*/
|
|
function updatePointersCancel( tracker, event, gPoints ) {
|
|
updatePointersUp( tracker, event, gPoints, 0 );
|
|
updatePointersExit( tracker, event, gPoints );
|
|
}
|
|
|
|
|
|
/**
|
|
* @private
|
|
* @inner
|
|
*/
|
|
function handlePointerStop( tracker, originalMoveEvent, pointerType ) {
|
|
if ( tracker.stopHandler ) {
|
|
tracker.stopHandler( {
|
|
eventSource: tracker,
|
|
pointerType: pointerType,
|
|
position: getMouseRelative( originalMoveEvent, tracker.element ),
|
|
buttons: tracker.getActivePointersListByType( pointerType ).buttons,
|
|
isTouchEvent: pointerType === 'touch',
|
|
originalEvent: originalMoveEvent,
|
|
preventDefaultAction: false,
|
|
userData: tracker.userData
|
|
} );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* True if inside an iframe, otherwise false.
|
|
* @member {Boolean} isInIframe
|
|
* @private
|
|
* @inner
|
|
*/
|
|
var isInIframe = (function() {
|
|
try {
|
|
return window.self !== window.top;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
})();
|
|
|
|
/**
|
|
* @function
|
|
* @private
|
|
* @inner
|
|
* @returns {Boolean} True if the target has access rights to events, otherwise false.
|
|
*/
|
|
function canAccessEvents (target) {
|
|
try {
|
|
return target.addEventListener && target.removeEventListener;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}(OpenSeadragon));
|