Merge branch 'collections' into m2

fixed Conflicts:
	changelog.txt
This commit is contained in:
Ian Gilman 2015-01-19 10:45:26 -08:00
commit 732cd87a10
8 changed files with 682 additions and 355 deletions

View File

@ -43,6 +43,7 @@ OPENSEADRAGON CHANGELOG
* You can now set a minZoomLevel that's greater than the home zoom level * You can now set a minZoomLevel that's greater than the home zoom level
* Added union() to OpenSeadragon.Rect * Added union() to OpenSeadragon.Rect
* Fixed an error in fitBounds if the new and old bounds were extremely close in size * Fixed an error in fitBounds if the new and old bounds were extremely close in size
* Added ajaxWithCredentials option (#543)
1.2.1: (in progress) 1.2.1: (in progress)
@ -50,6 +51,19 @@ OPENSEADRAGON CHANGELOG
* Fixed: DZI tilesource was broken on IE8/IE9 (#563) * Fixed: DZI tilesource was broken on IE8/IE9 (#563)
* Exposed secondary pointer button (middle, right, etc.) events from MouseTracker and through viewer (#479) * Exposed secondary pointer button (middle, right, etc.) events from MouseTracker and through viewer (#479)
* MouseTracker - Improved IE 8 compatibility (#562) * MouseTracker - Improved IE 8 compatibility (#562)
* MouseTracker - Improved IE 9+ compatibility (#564)
* MouseTracker - Simulated touchenter/touchleave events now bubble to parent element MouseTrackers (#566)
* MouseTracker - Improved multitouch support in enter/exit event handlers (#566)
* MouseTracker - orphaned tracked touch pointers removed (fix for #539)
* MouseTracker - removed touchenter/touchleave event support since the events don't exist on any known platform and have been removed from the W3C specification (#566)
* Removed Viewer onContainerPress/onContainerRelease handlers (and the associated 'container-release' event ) that were never fired due to the canvas (child) element capturing the DOM events (#566)
* Added 'canvas-enter', 'canvas-exit', and 'canvas-press' events to Viewer (#566)
* ButtonGroup - removed obsolete MouseTracker event handlers (#566)
* MouseTracker - added keydown and keyup handlers (#568)
* Modifier keys ignored in keyboard navigation handlers (#503)
* Requesting keyboard focus when viewer is clicked (#537)
* Arrow key navigation fixed across platforms (#565)
* Removed textarea element from viewer DOM. Viewer.canvas now handles keyboard navigation (#569)
1.2.0: 1.2.0:

View File

@ -105,22 +105,6 @@ $.ButtonGroup = function( options ) {
} }
} }
}, },
pressHandler: function ( event ) {
if ( event.pointerType === 'touch' && !$.MouseTracker.haveTouchEnter ) {
var i;
for ( i = 0; i < _this.buttons.length; i++ ) {
_this.buttons[ i ].notifyGroupEnter();
}
}
},
releaseHandler: function ( event ) {
var i;
if ( !event.insideElementReleased || ( event.pointerType === 'touch' && !$.MouseTracker.haveTouchEnter ) ) {
for ( i = 0; i < _this.buttons.length; i++ ) {
_this.buttons[ i ].notifyGroupExit();
}
}
}
}).setTracking( true ); }).setTracking( true );
}; };

View File

@ -34,7 +34,10 @@
(function ( $ ) { (function ( $ ) {
// dictionary from hash to private properties // All MouseTracker instances
var MOUSETRACKERS = [];
// dictionary from hash to private properties
var THIS = {}; var THIS = {};
@ -92,6 +95,10 @@
* An optional handler for after a drag gesture. * An optional handler for after a drag gesture.
* @param {OpenSeadragon.EventHandler} [options.pinchHandler=null] * @param {OpenSeadragon.EventHandler} [options.pinchHandler=null]
* An optional handler for the pinch gesture. * 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] * @param {OpenSeadragon.EventHandler} [options.keyHandler=null]
* An optional handler for keypress. * An optional handler for keypress.
* @param {OpenSeadragon.EventHandler} [options.focusHandler=null] * @param {OpenSeadragon.EventHandler} [options.focusHandler=null]
@ -103,6 +110,8 @@
*/ */
$.MouseTracker = function ( options ) { $.MouseTracker = function ( options ) {
MOUSETRACKERS.push( this );
var args = arguments; var args = arguments;
if ( !$.isPlainObject( options ) ) { if ( !$.isPlainObject( options ) ) {
@ -165,6 +174,8 @@
this.dragEndHandler = options.dragEndHandler || null; this.dragEndHandler = options.dragEndHandler || null;
this.pinchHandler = options.pinchHandler || null; this.pinchHandler = options.pinchHandler || null;
this.stopHandler = options.stopHandler || null; this.stopHandler = options.stopHandler || null;
this.keyDownHandler = options.keyDownHandler || null;
this.keyUpHandler = options.keyUpHandler || null;
this.keyHandler = options.keyHandler || null; this.keyHandler = options.keyHandler || null;
this.focusHandler = options.focusHandler || null; this.focusHandler = options.focusHandler || null;
this.blurHandler = options.blurHandler || null; this.blurHandler = options.blurHandler || null;
@ -180,6 +191,8 @@
THIS[ this.hash ] = { THIS[ this.hash ] = {
click: function ( event ) { onClick( _this, event ); }, click: function ( event ) { onClick( _this, event ); },
dblclick: function ( event ) { onDblClick( _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 ); }, keypress: function ( event ) { onKeyPress( _this, event ); },
focus: function ( event ) { onFocus( _this, event ); }, focus: function ( event ) { onFocus( _this, event ); },
blur: function ( event ) { onBlur( _this, event ); }, blur: function ( event ) { onBlur( _this, event ); },
@ -199,8 +212,6 @@
mousemove: function ( event ) { onMouseMove( _this, event ); }, mousemove: function ( event ) { onMouseMove( _this, event ); },
mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); }, mousemovecaptured: function ( event ) { onMouseMoveCaptured( _this, event ); },
touchenter: function ( event ) { onTouchEnter( _this, event ); },
touchleave: function ( event ) { onTouchLeave( _this, event ); },
touchstart: function ( event ) { onTouchStart( _this, event ); }, touchstart: function ( event ) { onTouchStart( _this, event ); },
touchend: function ( event ) { onTouchEnd( _this, event ); }, touchend: function ( event ) { onTouchEnd( _this, event ); },
touchendcaptured: function ( event ) { onTouchEndCaptured( _this, event ); }, touchendcaptured: function ( event ) { onTouchEndCaptured( _this, event ); },
@ -215,7 +226,6 @@
MSPointerOver: function ( event ) { onPointerOver( _this, event ); }, MSPointerOver: function ( event ) { onPointerOver( _this, event ); },
pointerout: function ( event ) { onPointerOut( _this, event ); }, pointerout: function ( event ) { onPointerOut( _this, event ); },
MSPointerOut: function ( event ) { onPointerOut( _this, event ); }, MSPointerOut: function ( event ) { onPointerOut( _this, event ); },
pointerdown: function ( event ) { onPointerDown( _this, event ); }, pointerdown: function ( event ) { onPointerDown( _this, event ); },
MSPointerDown: function ( event ) { onPointerDown( _this, event ); }, MSPointerDown: function ( event ) { onPointerDown( _this, event ); },
pointerup: function ( event ) { onPointerUp( _this, event ); }, pointerup: function ( event ) { onPointerUp( _this, event ); },
@ -256,9 +266,18 @@
* @function * @function
*/ */
destroy: function () { destroy: function () {
var i;
stopTracking( this ); stopTracking( this );
this.element = null; this.element = null;
for ( i = 0; i < MOUSETRACKERS.length; i++ ) {
if ( MOUSETRACKERS[ i ] === this ) {
MOUSETRACKERS.splice( i, 1 );
break;
}
}
THIS[ this.hash ] = null; THIS[ this.hash ] = null;
delete THIS[ this.hash ]; delete THIS[ this.hash ];
}, },
@ -313,6 +332,24 @@
return 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 * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
@ -327,6 +364,8 @@
* @param {Number} event.buttons * @param {Number} event.buttons
* Current buttons pressed. * 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. * 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 * @param {Boolean} event.insideElementPressed
* True if the left mouse button is currently being pressed and was * True if the left mouse button is currently being pressed and was
* initiated inside the tracked element, otherwise false. * initiated inside the tracked element, otherwise false.
@ -357,6 +396,8 @@
* @param {Number} event.buttons * @param {Number} event.buttons
* Current buttons pressed. * 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. * 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 * @param {Boolean} event.insideElementPressed
* True if the left mouse button is currently being pressed and was * True if the left mouse button is currently being pressed and was
* initiated inside the tracked element, otherwise false. * initiated inside the tracked element, otherwise false.
@ -710,8 +751,66 @@
* A reference to the tracker instance. * A reference to the tracker instance.
* @param {Number} event.keyCode * @param {Number} event.keyCode
* The key code that was pressed. * The key code that was pressed.
* @param {Boolean} event.ctrl
* True if the ctrl key was pressed during this event.
* @param {Boolean} event.shift * @param {Boolean} event.shift
* True if the shift key was pressed during this event. * 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 * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} event.preventDefaultAction * @param {Boolean} event.preventDefaultAction
@ -871,7 +970,7 @@
/** /**
* Detect browser pointer device event model(s) and build appropriate list of events to subscribe to. * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to.
*/ */
$.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keydown", "keyup", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
// Older Firefox // Older Firefox
@ -888,7 +987,6 @@
} else { } else {
$.MouseTracker.maxTouchPoints = 0; $.MouseTracker.maxTouchPoints = 0;
} }
$.MouseTracker.haveTouchEnter = false;
$.MouseTracker.haveMouseEnter = false; $.MouseTracker.haveMouseEnter = false;
} else if ( window.MSPointerEvent ) { } else if ( window.MSPointerEvent ) {
// IE10 // IE10
@ -900,7 +998,6 @@
} else { } else {
$.MouseTracker.maxTouchPoints = 0; $.MouseTracker.maxTouchPoints = 0;
} }
$.MouseTracker.haveTouchEnter = false;
$.MouseTracker.haveMouseEnter = false; $.MouseTracker.haveMouseEnter = false;
} else { } else {
// Legacy W3C mouse events // Legacy W3C mouse events
@ -914,19 +1011,14 @@
} }
$.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" ); $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'ontouchstart' in window ) { if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505) // 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" ); $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
if ( 'ontouchenter' in window ) {
$.MouseTracker.subscribeEvents.push( "touchenter", "touchleave" );
$.MouseTracker.haveTouchEnter = true;
} else {
$.MouseTracker.haveTouchEnter = false;
}
} else {
$.MouseTracker.haveTouchEnter = false;
} }
if ( 'ongesturestart' in window ) { if ( 'ongesturestart' in window ) {
// iOS (see https://developer.apple.com/library/safari/documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html) // iOS (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
// Subscribe to these to prevent default gesture handling // Subscribe to these to prevent default gesture handling
$.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
} }
@ -1414,12 +1506,72 @@
} }
/**
* @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,
position: getMouseRelative( event, tracker.element ),
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,
position: getMouseRelative( event, tracker.element ),
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 * @private
* @inner * @inner
*/ */
function onKeyPress( tracker, event ) { function onKeyPress( tracker, event ) {
//console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); //$.console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey );
var propagate; var propagate;
if ( tracker.keyHandler ) { if ( tracker.keyHandler ) {
event = $.getEvent( event ); event = $.getEvent( event );
@ -1428,7 +1580,10 @@
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( event, tracker.element ), position: getMouseRelative( event, tracker.element ),
keyCode: event.keyCode ? event.keyCode : event.charCode, keyCode: event.keyCode ? event.keyCode : event.charCode,
ctrl: event.ctrlKey,
shift: event.shiftKey, shift: event.shiftKey,
alt: event.altKey,
meta: event.metaKey,
originalEvent: event, originalEvent: event,
preventDefaultAction: false, preventDefaultAction: false,
userData: tracker.userData userData: tracker.userData
@ -1609,7 +1764,7 @@
function onMouseOver( tracker, event ) { function onMouseOver( tracker, event ) {
event = $.getEvent( event ); event = $.getEvent( event );
if ( this === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
return; return;
} }
@ -1654,7 +1809,7 @@
function onMouseOut( tracker, event ) { function onMouseOut( tracker, event ) {
event = $.getEvent( event ); event = $.getEvent( event );
if ( this === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
return; return;
} }
@ -1823,45 +1978,24 @@
* @private * @private
* @inner * @inner
*/ */
function onTouchEnter( tracker, event ) { function abortTouchContacts( tracker, event, pointsList ) {
var i, var i,
touchCount = event.changedTouches.length, gPointCount = pointsList.getLength(),
gPoints = []; abortGPoints = [];
for ( i = 0; i < touchCount; i++ ) { for ( i = 0; i < gPointCount; i++ ) {
gPoints.push( { abortGPoints.push( pointsList.getByIndex( i ) );
id: event.changedTouches[ i ].identifier,
type: 'touch',
// isPrimary not set - let the updatePointers functions determine it
currentPos: getMouseAbsolute( event.changedTouches[ i ] ),
currentTime: $.now()
} );
} }
updatePointersEnter( tracker, event, gPoints ); if ( abortGPoints.length > 0 ) {
} // simulate touchend
updatePointersUp( tracker, event, abortGPoints, 0 ); // 0 means primary button press/release or touch contact
// release pointer capture
/** pointsList.captureCount = 1;
* @private releasePointer( tracker, 'touch' );
* @inner // simulate touchleave
*/ updatePointersExit( tracker, event, abortGPoints );
function onTouchLeave( 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()
} );
} }
updatePointersExit( tracker, event, gPoints );
} }
@ -1872,11 +2006,19 @@
function onTouchStart( tracker, event ) { function onTouchStart( tracker, event ) {
var time, var time,
i, i,
j,
touchCount = event.changedTouches.length, touchCount = event.changedTouches.length,
gPoints = []; gPoints = [],
parentGPoints,
pointsList = tracker.getActivePointersListByType( 'touch' );
time = $.now(); 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.');
abortTouchContacts( tracker, event, pointsList );
}
for ( i = 0; i < touchCount; i++ ) { for ( i = 0; i < touchCount; i++ ) {
gPoints.push( { gPoints.push( {
id: event.changedTouches[ i ].identifier, id: event.changedTouches[ i ].identifier,
@ -1887,9 +2029,24 @@
} ); } );
} }
// simulate touchenter if not natively available // simulate touchenter on our tracked element
if ( !$.MouseTracker.haveTouchEnter ) { updatePointersEnter( tracker, event, gPoints );
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 if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
@ -1930,8 +2087,10 @@
function handleTouchEnd( tracker, event ) { function handleTouchEnd( tracker, event ) {
var time, var time,
i, i,
j,
touchCount = event.changedTouches.length, touchCount = event.changedTouches.length,
gPoints = []; gPoints = [],
parentGPoints;
time = $.now(); time = $.now();
@ -1949,9 +2108,24 @@
releasePointer( tracker, 'touch' ); releasePointer( tracker, 'touch' );
} }
// simulate touchleave if not natively available // simulate touchleave on our tracked element
if ( !$.MouseTracker.haveTouchEnter && touchCount > 0 ) { updatePointersExit( tracker, event, gPoints );
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 ); $.cancelEvent( event );
@ -2054,7 +2228,7 @@
function onPointerOver( tracker, event ) { function onPointerOver( tracker, event ) {
var gPoint; var gPoint;
if ( this === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
return; return;
} }
@ -2077,7 +2251,7 @@
function onPointerOut( tracker, event ) { function onPointerOut( tracker, event ) {
var gPoint; var gPoint;
if ( this === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) { if ( event.currentTarget === event.relatedTarget || isParentChild( event.currentTarget, event.relatedTarget ) ) {
return; return;
} }
@ -2092,6 +2266,7 @@
updatePointersExit( tracker, event, [ gPoint ] ); updatePointersExit( tracker, event, [ gPoint ] );
} }
/** /**
* @private * @private
* @inner * @inner
@ -2344,6 +2519,7 @@
pointerType: curGPoint.type, pointerType: curGPoint.type,
position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ),
buttons: pointsList.buttons, buttons: pointsList.buttons,
pointers: tracker.getActivePointerCount(),
insideElementPressed: curGPoint.insideElementPressed, insideElementPressed: curGPoint.insideElementPressed,
buttonDownAny: pointsList.buttons !== 0, buttonDownAny: pointsList.buttons !== 0,
isTouchEvent: curGPoint.type === 'touch', isTouchEvent: curGPoint.type === 'touch',
@ -2407,6 +2583,7 @@
pointerType: curGPoint.type, pointerType: curGPoint.type,
position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ), position: getPointRelativeToAbsolute( curGPoint.currentPos, tracker.element ),
buttons: pointsList.buttons, buttons: pointsList.buttons,
pointers: tracker.getActivePointerCount(),
insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false, insideElementPressed: updateGPoint ? updateGPoint.insideElementPressed : false,
buttonDownAny: pointsList.buttons !== 0, buttonDownAny: pointsList.buttons !== 0,
isTouchEvent: curGPoint.type === 'touch', isTouchEvent: curGPoint.type === 'touch',

View File

@ -559,8 +559,12 @@
* If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage. * If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage.
* *
* @property {String|Boolean} [crossOriginPolicy=false] * @property {String|Boolean} [crossOriginPolicy=false]
* Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will
* not use CORS, and the canvas will be tainted. * not use CORS, and the canvas will be tainted.
*
* @property {Boolean} [ajaxWithCredentials=false]
* Whether to set the withCredentials XHR flag for AJAX requests (when loading tile sources).
* Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
* *
*/ */
@ -919,6 +923,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
tileHost: null, tileHost: null,
initialPage: 0, initialPage: 0,
crossOriginPolicy: false, crossOriginPolicy: false,
ajaxWithCredentials: false,
//PAN AND ZOOM SETTINGS AND CONSTRAINTS //PAN AND ZOOM SETTINGS AND CONSTRAINTS
panHorizontal: true, panHorizontal: true,
@ -1925,13 +1930,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
/** /**
* Makes an AJAX request. * Makes an AJAX request.
* @function * @param {Object} options
* @param {String} url - the url to request * @param {String} options.url - the url to request
* @param {Function} onSuccess - a function to call on a successful response * @param {Function} options.success - a function to call on a successful response
* @param {Function} onError - a function to call on when an error occurs * @param {Function} options.error - a function to call on when an error occurs
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
* @throws {Error} * @throws {Error}
*/ */
makeAjaxRequest: function( url, onSuccess, onError ) { makeAjaxRequest: function( url, onSuccess, onError ) {
var withCredentials;
// Note that our preferred API is that you pass in a single object; the named
// arguments are for legacy support.
if( $.isPlainObject( url ) ){
onSuccess = url.success;
onError = url.error;
withCredentials = url.withCredentials;
url = url.url;
}
var protocol = $.getUrlProtocol( url ); var protocol = $.getUrlProtocol( url );
var request = $.createAjaxRequest( protocol === "file:" ); var request = $.createAjaxRequest( protocol === "file:" );
@ -1958,6 +1975,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
} }
}; };
if (withCredentials) {
request.withCredentials = true;
}
try { try {
request.open( "GET", url, true ); request.open( "GET", url, true );
request.send( null ); request.send( null );

View File

@ -125,6 +125,7 @@ $.ReferenceStrip = function ( options ) {
scrollHandler: $.delegate( this, onStripScroll ), scrollHandler: $.delegate( this, onStripScroll ),
enterHandler: $.delegate( this, onStripEnter ), enterHandler: $.delegate( this, onStripEnter ),
exitHandler: $.delegate( this, onStripExit ), exitHandler: $.delegate( this, onStripExit ),
keyDownHandler: $.delegate( this, onKeyDown ),
keyHandler: $.delegate( this, onKeyPress ) keyHandler: $.delegate( this, onKeyPress )
} ).setTracking( true ); } ).setTracking( true );
@ -518,6 +519,37 @@ function onStripExit( event ) {
} }
/**
* @private
* @inner
* @function
*/
function onKeyDown( event ) {
//console.log( event.keyCode );
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch ( event.keyCode ) {
case 38: //up arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false;
case 40: //down arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false;
case 37: //left arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false;
case 39: //right arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false;
default:
//console.log( 'navigator keycode %s', event.keyCode );
return true;
}
} else {
return true;
}
}
/** /**
* @private * @private
@ -527,35 +559,35 @@ function onStripExit( event ) {
function onKeyPress( event ) { function onKeyPress( event ) {
//console.log( event.keyCode ); //console.log( event.keyCode );
switch ( event.keyCode ) { if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
case 61: //=|+ switch ( event.keyCode ) {
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); case 61: //=|+
return false; onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
case 45: //-|_ return false;
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); case 45: //-|_
return false; onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
case 48: //0|) return false;
case 119: //w case 48: //0|)
case 87: //W case 119: //w
case 38: //up arrow case 87: //W
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; return false;
case 115: //s case 115: //s
case 83: //S case 83: //S
case 40: //down arrow onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); return false;
return false; case 97: //a
case 97: //a onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
case 37: //left arrow return false;
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); case 100: //d
return false; onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
case 100: //d return false;
case 39: //right arrow default:
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); //console.log( 'navigator keycode %s', event.keyCode );
return false; return true;
default: }
//console.log( 'navigator keycode %s', event.keyCode ); } else {
return true; return true;
} }
} }

View File

@ -38,44 +38,52 @@
/** /**
* @class TileSource * @class TileSource
* @classdesc The TileSource contains the most basic implementation required to create a * @classdesc The TileSource contains the most basic implementation required to create a
* smooth transition between layer in an image pyramid. It has only a single key * smooth transition between layers in an image pyramid. It has only a single key
* interface that must be implemented to complete it key functionality: * interface that must be implemented to complete its key functionality:
* 'getTileUrl'. It also has several optional interfaces that can be * 'getTileUrl'. It also has several optional interfaces that can be
* implemented if a new TileSource wishes to support configuration via a simple * implemented if a new TileSource wishes to support configuration via a simple
* object or array ('configure') and if the tile source supports or requires * object or array ('configure') and if the tile source supports or requires
* configuration via retreival of a document on the network ala AJAX or JSONP, * configuration via retrieval of a document on the network ala AJAX or JSONP,
* ('getImageInfo'). * ('getImageInfo').
* <br/> * <br/>
* By default the image pyramid is split into N layers where the images longest * By default the image pyramid is split into N layers where the image's longest
* side in M (in pixels), where N is the smallest integer which satisfies * side in M (in pixels), where N is the smallest integer which satisfies
* <strong>2^(N+1) >= M</strong>. * <strong>2^(N+1) >= M</strong>.
* *
* @memberof OpenSeadragon * @memberof OpenSeadragon
* @extends OpenSeadragon.EventSource * @extends OpenSeadragon.EventSource
* @param {Number|Object|Array|String} width * @param {Object} options
* If more than a single argument is supplied, the traditional use of * You can either specify a URL, or literally define the TileSource (by specifying
* positional parameters is supplied and width is expected to be the width * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,
* source image at its max resolution in pixels. If a single argument is supplied and * the extending class is expected to implement 'getImageInfo' and 'configure'.
* it is an Object or Array, the construction is assumed to occur through * For the latter, the construction is assumed to occur through
* the extending classes implementation of 'configure'. Finally if only a * the extending classes implementation of 'configure'.
* single argument is supplied and it is a String, the extending class is * @param {String} [options.url]
* expected to implement 'getImageInfo' and 'configure'. * The URL for the data necessary for this TileSource.
* @param {Number} height * @param {Function} [options.success]
* A function to be called upon successful creation.
* @param {Boolean} [options.ajaxWithCredentials]
* If this TileSource needs to make an AJAX call, this specifies whether to set
* the XHR's withCredentials (for accessing secure data).
* @param {Number} [options.width]
* Width of the source image at max resolution in pixels. * Width of the source image at max resolution in pixels.
* @param {Number} tileSize * @param {Number} [options.height]
* Height of the source image at max resolution in pixels.
* @param {Number} [options.tileSize]
* The size of the tiles to assumed to make up each pyramid layer in pixels. * The size of the tiles to assumed to make up each pyramid layer in pixels.
* Tile size determines the point at which the image pyramid must be * Tile size determines the point at which the image pyramid must be
* divided into a matrix of smaller images. * divided into a matrix of smaller images.
* @param {Number} tileOverlap * @param {Number} [options.tileOverlap]
* The number of pixels each tile is expected to overlap touching tiles. * The number of pixels each tile is expected to overlap touching tiles.
* @param {Number} minLevel * @param {Number} [options.minLevel]
* The minimum level to attempt to load. * The minimum level to attempt to load.
* @param {Number} maxLevel * @param {Number} [options.maxLevel]
* The maximum level to attempt to load. * The maximum level to attempt to load.
*/ */
$.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) { $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) {
var callback = null, var _this = this;
args = arguments,
var args = arguments,
options, options,
i; i;
@ -102,19 +110,23 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
//source //source
$.extend( true, this, options ); $.extend( true, this, options );
//Any functions that are passed as arguments are bound to the ready callback if (!this.success) {
/*jshint loopfunc:true*/ //Any functions that are passed as arguments are bound to the ready callback
for ( i = 0; i < arguments.length; i++ ) { for ( i = 0; i < arguments.length; i++ ) {
if ( $.isFunction( arguments[ i ] ) ) { if ( $.isFunction( arguments[ i ] ) ) {
callback = arguments[ i ]; this.success = arguments[ i ];
this.addHandler( 'ready', function ( event ) { //only one callback per constructor
callback( event ); break;
} ); }
//only one callback per constructor
break;
} }
} }
if (this.success) {
this.addHandler( 'ready', function ( event ) {
_this.success( event );
} );
}
/** /**
* Ratio of width to height * Ratio of width to height
* @member {Number} aspectRatio * @member {Number} aspectRatio
@ -154,6 +166,10 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
*/ */
if( 'string' == $.type( arguments[ 0 ] ) ){ if( 'string' == $.type( arguments[ 0 ] ) ){
this.url = arguments[0];
}
if (this.url) {
//in case the getImageInfo method is overriden and/or implies an //in case the getImageInfo method is overriden and/or implies an
//async mechanism set some safe defaults first //async mechanism set some safe defaults first
this.aspectRatio = 1; this.aspectRatio = 1;
@ -165,7 +181,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
this.ready = false; this.ready = false;
//configuration via url implies the extending class //configuration via url implies the extending class
//implements and 'configure' //implements and 'configure'
this.getImageInfo( arguments[ 0 ] ); this.getImageInfo( this.url );
} else { } else {
@ -185,8 +201,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
Math.log( 2 ) Math.log( 2 )
) : 0 ) : 0
); );
if( callback && $.isFunction( callback ) ){ if( this.success && $.isFunction( this.success ) ){
callback( this ); this.success( this );
} }
} }
@ -355,6 +371,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
} }
options = $TileSource.prototype.configure.apply( _this, [ data, url ]); options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
if (options.ajaxWithCredentials === undefined) {
options.ajaxWithCredentials = _this.ajaxWithCredentials;
}
readySource = new $TileSource( options ); readySource = new $TileSource( options );
_this.ready = true; _this.ready = true;
/** /**
@ -383,45 +403,50 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
}); });
} else { } else {
// request info via xhr asynchronously. // request info via xhr asynchronously.
$.makeAjaxRequest( url, function( xhr ) { $.makeAjaxRequest( {
var data = processResponse( xhr ); url: url,
callback( data ); withCredentials: this.ajaxWithCredentials,
}, function ( xhr, exc ) { success: function( xhr ) {
var msg; var data = processResponse( xhr );
callback( data );
},
error: function ( xhr, exc ) {
var msg;
/* /*
IE < 10 will block XHR requests to different origins. Any property access on the request IE < 10 will block XHR requests to different origins. Any property access on the request
object will raise an exception which we'll attempt to handle by formatting the original object will raise an exception which we'll attempt to handle by formatting the original
exception rather than the second one raised when we try to access xhr.status exception rather than the second one raised when we try to access xhr.status
*/ */
try { try {
msg = "HTTP " + xhr.status + " attempting to load TileSource"; msg = "HTTP " + xhr.status + " attempting to load TileSource";
} catch ( e ) { } catch ( e ) {
var formattedExc; var formattedExc;
if ( typeof( exc ) == "undefined" || !exc.toString ) { if ( typeof( exc ) == "undefined" || !exc.toString ) {
formattedExc = "Unknown error"; formattedExc = "Unknown error";
} else { } else {
formattedExc = exc.toString(); formattedExc = exc.toString();
}
msg = formattedExc + " attempting to load TileSource";
} }
msg = formattedExc + " attempting to load TileSource"; /***
* Raised when an error occurs loading a TileSource.
*
* @event open-failed
* @memberof OpenSeadragon.TileSource
* @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {String} message
* @property {String} source
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', {
message: msg,
source: url
});
} }
/***
* Raised when an error occurs loading a TileSource.
*
* @event open-failed
* @memberof OpenSeadragon.TileSource
* @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {String} message
* @property {String} source
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', {
message: msg,
source: url
});
}); });
} }

View File

@ -106,14 +106,6 @@ $.Viewer = function( options ) {
* @memberof OpenSeadragon.Viewer# * @memberof OpenSeadragon.Viewer#
*/ */
container: null, container: null,
/**
* A &lt;textarea&gt; element, the element where keyboard events are handled.<br><br>
* Child element of {@link OpenSeadragon.Viewer#container},
* positioned below {@link OpenSeadragon.Viewer#canvas}.
* @member {Element} keyboardCommandArea
* @memberof OpenSeadragon.Viewer#
*/
keyboardCommandArea: null,
/** /**
* A &lt;div&gt; element, the element where user-input events are handled for panning and zooming.<br><br> * A &lt;div&gt; element, the element where user-input events are handled for panning and zooming.<br><br>
* Child element of {@link OpenSeadragon.Viewer#container}, * Child element of {@link OpenSeadragon.Viewer#container},
@ -229,7 +221,6 @@ $.Viewer = function( options ) {
this.element = this.element || document.getElementById( this.id ); this.element = this.element || document.getElementById( this.id );
this.canvas = $.makeNeutralElement( "div" ); this.canvas = $.makeNeutralElement( "div" );
this.keyboardCommandArea = $.makeNeutralElement( "textarea" );
this.canvas.className = "openseadragon-canvas"; this.canvas.className = "openseadragon-canvas";
(function( style ){ (function( style ){
@ -241,6 +232,7 @@ $.Viewer = function( options ) {
style.left = "0px"; style.left = "0px";
}(this.canvas.style)); }(this.canvas.style));
$.setElementTouchActionNone( this.canvas ); $.setElementTouchActionNone( this.canvas );
this.canvas.tabIndex = 0;
//the container is created through applying the ControlDock constructor above //the container is created through applying the ControlDock constructor above
this.container.className = "openseadragon-container"; this.container.className = "openseadragon-container";
@ -254,19 +246,7 @@ $.Viewer = function( options ) {
style.textAlign = "left"; // needed to protect against style.textAlign = "left"; // needed to protect against
}( this.container.style )); }( this.container.style ));
this.keyboardCommandArea.className = "keyboard-command-area";
(function( style ){
style.width = "100%";
style.height = "100%";
style.overflow = "hidden";
style.position = "absolute";
style.top = "0px";
style.left = "0px";
style.resize = "none";
}( this.keyboardCommandArea.style ));
this.container.insertBefore( this.canvas, this.container.firstChild ); this.container.insertBefore( this.canvas, this.container.firstChild );
this.container.insertBefore( this.keyboardCommandArea, this.container.firstChild );
this.element.appendChild( this.container ); this.element.appendChild( this.container );
//Used for toggling between fullscreen and default container size //Used for toggling between fullscreen and default container size
@ -277,80 +257,22 @@ $.Viewer = function( options ) {
this.bodyOverflow = document.body.style.overflow; this.bodyOverflow = document.body.style.overflow;
this.docOverflow = document.documentElement.style.overflow; this.docOverflow = document.documentElement.style.overflow;
this.keyboardCommandArea.innerTracker = new $.MouseTracker({
_this : this,
element: this.keyboardCommandArea,
focusHandler: function( event ){
if ( !event.preventDefaultAction ) {
var point = $.getElementPosition( this.element );
window.scrollTo( 0, point.y );
}
},
keyHandler: function( event ){
if ( !event.preventDefaultAction ) {
switch( event.keyCode ){
case 61://=|+
_this.viewport.zoomBy(1.1);
_this.viewport.applyConstraints();
return false;
case 45://-|_
_this.viewport.zoomBy(0.9);
_this.viewport.applyConstraints();
return false;
case 48://0|)
_this.viewport.goHome();
_this.viewport.applyConstraints();
return false;
case 119://w
case 87://W
case 38://up arrow
if ( event.shift ) {
_this.viewport.zoomBy(1.1);
} else {
_this.viewport.panBy(new $.Point(0, -0.05));
}
_this.viewport.applyConstraints();
return false;
case 115://s
case 83://S
case 40://down arrow
if ( event.shift ) {
_this.viewport.zoomBy(0.9);
} else {
_this.viewport.panBy(new $.Point(0, 0.05));
}
_this.viewport.applyConstraints();
return false;
case 97://a
case 37://left arrow
_this.viewport.panBy(new $.Point(-0.05, 0));
_this.viewport.applyConstraints();
return false;
case 100://d
case 39://right arrow
_this.viewport.panBy(new $.Point(0.05, 0));
_this.viewport.applyConstraints();
return false;
default:
//console.log( 'navigator keycode %s', event.keyCode );
return true;
}
}
}
}).setTracking( true ); // default state
this.innerTracker = new $.MouseTracker({ this.innerTracker = new $.MouseTracker({
element: this.canvas, element: this.canvas,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
dblClickTimeThreshold: this.dblClickTimeThreshold, dblClickTimeThreshold: this.dblClickTimeThreshold,
dblClickDistThreshold: this.dblClickDistThreshold, dblClickDistThreshold: this.dblClickDistThreshold,
focusHandler: $.delegate( this, onCanvasFocus ),
keyDownHandler: $.delegate( this, onCanvasKeyDown ),
keyHandler: $.delegate( this, onCanvasKeyPress ),
clickHandler: $.delegate( this, onCanvasClick ), clickHandler: $.delegate( this, onCanvasClick ),
dblClickHandler: $.delegate( this, onCanvasDblClick ), dblClickHandler: $.delegate( this, onCanvasDblClick ),
dragHandler: $.delegate( this, onCanvasDrag ), dragHandler: $.delegate( this, onCanvasDrag ),
dragEndHandler: $.delegate( this, onCanvasDragEnd ), dragEndHandler: $.delegate( this, onCanvasDragEnd ),
enterHandler: $.delegate( this, onCanvasEnter ),
exitHandler: $.delegate( this, onCanvasExit ),
pressHandler: $.delegate( this, onCanvasPress ),
releaseHandler: $.delegate( this, onCanvasRelease ), releaseHandler: $.delegate( this, onCanvasRelease ),
nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ), nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ),
nonPrimaryReleaseHandler: $.delegate( this, onCanvasNonPrimaryRelease ), nonPrimaryReleaseHandler: $.delegate( this, onCanvasNonPrimaryRelease ),
@ -365,9 +287,7 @@ $.Viewer = function( options ) {
dblClickTimeThreshold: this.dblClickTimeThreshold, dblClickTimeThreshold: this.dblClickTimeThreshold,
dblClickDistThreshold: this.dblClickDistThreshold, dblClickDistThreshold: this.dblClickDistThreshold,
enterHandler: $.delegate( this, onContainerEnter ), enterHandler: $.delegate( this, onContainerEnter ),
exitHandler: $.delegate( this, onContainerExit ), exitHandler: $.delegate( this, onContainerExit )
pressHandler: $.delegate( this, onContainerPress ),
releaseHandler: $.delegate( this, onContainerRelease )
}).setTracking( this.mouseNavEnabled ? true : false ); // always tracking }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking
if( this.toolbar ){ if( this.toolbar ){
@ -814,9 +734,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
} }
// destroy the mouse trackers // destroy the mouse trackers
if (this.keyboardCommandArea){
this.keyboardCommandArea.innerTracker.destroy();
}
if (this.innerTracker){ if (this.innerTracker){
this.innerTracker.destroy(); this.innerTracker.destroy();
} }
@ -829,7 +746,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
// clear all our references to dom objects // clear all our references to dom objects
this.canvas = null; this.canvas = null;
this.keyboardCommandArea = null;
this.container = null; this.container = null;
// clear our reference to the main element - they will need to pass it in again, creating a new viewer // clear our reference to the main element - they will need to pass it in again, creating a new viewer
@ -2058,14 +1974,22 @@ function getTileSourceImplementation( viewer, tileSource, successCallback,
setTimeout( function() { setTimeout( function() {
if ( $.type( tileSource ) == 'string' ) { if ( $.type( tileSource ) == 'string' ) {
//If its still a string it means it must be a url at this point //If its still a string it means it must be a url at this point
tileSource = new $.TileSource( tileSource, function( event ) { tileSource = new $.TileSource({
successCallback( event.tileSource ); url: tileSource,
ajaxWithCredentials: viewer.ajaxWithCredentials,
success: function( event ) {
successCallback( event.tileSource );
}
}); });
tileSource.addHandler( 'open-failed', function( event ) { tileSource.addHandler( 'open-failed', function( event ) {
failCallback( event ); failCallback( event );
} ); } );
} else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ) { } else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ) {
if (tileSource.ajaxWithCredentials === undefined) {
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
}
if ( $.isFunction( tileSource.getTileUrl ) ) { if ( $.isFunction( tileSource.getTileUrl ) ) {
//Custom tile source //Custom tile source
var customTileSource = new $.TileSource( tileSource ); var customTileSource = new $.TileSource( tileSource );
@ -2261,9 +2185,109 @@ function onBlur(){
} }
function onCanvasFocus( event ) {
if ( !event.preventDefaultAction ) {
var point = $.getElementPosition( this.element );
window.scrollTo( 0, point.y );
}
}
function onCanvasKeyDown( event ) {
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch( event.keyCode ){
case 38://up arrow
if ( event.shift ) {
this.viewport.zoomBy(1.1);
} else {
this.viewport.panBy(new $.Point(0, -0.05));
}
this.viewport.applyConstraints();
return false;
case 40://down arrow
if ( event.shift ) {
this.viewport.zoomBy(0.9);
} else {
this.viewport.panBy(new $.Point(0, 0.05));
}
this.viewport.applyConstraints();
return false;
case 37://left arrow
this.viewport.panBy(new $.Point(-0.05, 0));
this.viewport.applyConstraints();
return false;
case 39://right arrow
this.viewport.panBy(new $.Point(0.05, 0));
this.viewport.applyConstraints();
return false;
default:
//console.log( 'navigator keycode %s', event.keyCode );
return true;
}
} else {
return true;
}
}
function onCanvasKeyPress( event ) {
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch( event.keyCode ){
case 61://=|+
this.viewport.zoomBy(1.1);
this.viewport.applyConstraints();
return false;
case 45://-|_
this.viewport.zoomBy(0.9);
this.viewport.applyConstraints();
return false;
case 48://0|)
this.viewport.goHome();
this.viewport.applyConstraints();
return false;
case 119://w
case 87://W
if ( event.shift ) {
this.viewport.zoomBy(1.1);
} else {
this.viewport.panBy(new $.Point(0, -0.05));
}
this.viewport.applyConstraints();
return false;
case 115://s
case 83://S
if ( event.shift ) {
this.viewport.zoomBy(0.9);
} else {
this.viewport.panBy(new $.Point(0, 0.05));
}
this.viewport.applyConstraints();
return false;
case 97://a
this.viewport.panBy(new $.Point(-0.05, 0));
this.viewport.applyConstraints();
return false;
case 100://d
this.viewport.panBy(new $.Point(0.05, 0));
this.viewport.applyConstraints();
return false;
default:
//console.log( 'navigator keycode %s', event.keyCode );
return true;
}
} else {
return true;
}
}
function onCanvasClick( event ) { function onCanvasClick( event ) {
var gestureSettings; var gestureSettings;
var haveKeyboardFocus = document.activeElement == this.canvas;
// If we don't have keyboard focus, request it.
if ( !haveKeyboardFocus ) {
this.canvas.focus();
}
if ( !event.preventDefaultAction && this.viewport && event.quick ) { if ( !event.preventDefaultAction && this.viewport && event.quick ) {
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType ); gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
if ( gestureSettings.clickToZoom ) { if ( gestureSettings.clickToZoom ) {
@ -2419,6 +2443,92 @@ function onCanvasDragEnd( event ) {
}); });
} }
function onCanvasEnter( event ) {
/**
* Raised when a pointer enters the {@link OpenSeadragon.Viewer#canvas} element.
*
* @event canvas-enter
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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.
* @property {Number} pointers - Number of pointers (all types) active in the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-enter', {
tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position,
buttons: event.buttons,
pointers: event.pointers,
insideElementPressed: event.insideElementPressed,
buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent
});
}
function onCanvasExit( event ) {
/**
* Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.
*
* @event canvas-exit
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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.
* @property {Number} pointers - Number of pointers (all types) active in the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-exit', {
tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position,
buttons: event.buttons,
pointers: event.pointers,
insideElementPressed: event.insideElementPressed,
buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent
});
}
function onCanvasPress( event ) {
/**
* Raised when the primary mouse button is pressed or touch starts on the {@link OpenSeadragon.Viewer#canvas} element.
*
* @event canvas-press
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-press', {
tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position,
insideElementPressed: event.insideElementPressed,
insideElementReleased: event.insideElementReleased,
originalEvent: event.originalEvent
});
}
function onCanvasRelease( event ) { function onCanvasRelease( event ) {
/** /**
* Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element. * Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element.
@ -2428,6 +2538,7 @@ function onCanvasRelease( event ) {
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false. * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released. * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.
@ -2436,6 +2547,7 @@ function onCanvasRelease( event ) {
*/ */
this.raiseEvent( 'canvas-release', { this.raiseEvent( 'canvas-release', {
tracker: event.eventSource, tracker: event.eventSource,
pointerType: event.pointerType,
position: event.position, position: event.position,
insideElementPressed: event.insideElementPressed, insideElementPressed: event.insideElementPressed,
insideElementReleased: event.insideElementReleased, insideElementReleased: event.insideElementReleased,
@ -2604,8 +2716,38 @@ function onCanvasScroll( event ) {
} }
} }
function onContainerEnter( event ) {
THIS[ this.hash ].mouseInside = true;
abortControlsAutoHide( this );
/**
* Raised when the cursor enters the {@link OpenSeadragon.Viewer#container} element.
*
* @event container-enter
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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.
* @property {Number} pointers - Number of pointers (all types) active in the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'container-enter', {
tracker: event.eventSource,
position: event.position,
buttons: event.buttons,
pointers: event.pointers,
insideElementPressed: event.insideElementPressed,
buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent
});
}
function onContainerExit( event ) { function onContainerExit( event ) {
if ( !event.insideElementPressed ) { if ( event.pointers < 1 ) {
THIS[ this.hash ].mouseInside = false; THIS[ this.hash ].mouseInside = false;
if ( !THIS[ this.hash ].animating ) { if ( !THIS[ this.hash ].animating ) {
beginControlsAutoHide( this ); beginControlsAutoHide( this );
@ -2621,6 +2763,7 @@ function onContainerExit( event ) {
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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. * @property {Number} buttons - Current buttons pressed. A 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.
* @property {Number} pointers - Number of pointers (all types) active in the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false. * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span> * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
* @property {Object} originalEvent - The original DOM event. * @property {Object} originalEvent - The original DOM event.
@ -2630,71 +2773,7 @@ function onContainerExit( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
buttons: event.buttons, buttons: event.buttons,
insideElementPressed: event.insideElementPressed, pointers: event.pointers,
buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent
});
}
function onContainerPress( event ) {
if ( event.pointerType === 'touch' && !$.MouseTracker.haveTouchEnter ) {
THIS[ this.hash ].mouseInside = true;
abortControlsAutoHide( this );
}
}
function onContainerRelease( event ) {
if ( !event.insideElementReleased || ( event.pointerType === 'touch' && !$.MouseTracker.haveTouchEnter ) ) {
THIS[ this.hash ].mouseInside = false;
if ( !THIS[ this.hash ].animating ) {
beginControlsAutoHide( this );
}
}
/**
* Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#container} element.
*
* @event container-release
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'container-release', {
tracker: event.eventSource,
position: event.position,
insideElementPressed: event.insideElementPressed,
insideElementReleased: event.insideElementReleased,
originalEvent: event.originalEvent
});
}
function onContainerEnter( event ) {
THIS[ this.hash ].mouseInside = true;
abortControlsAutoHide( this );
/**
* Raised when the cursor enters the {@link OpenSeadragon.Viewer#container} element.
*
* @event container-enter
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A 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.
* @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.
* @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. <span style="color:red;">Deprecated. Use buttons instead.</span>
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'container-enter', {
tracker: event.eventSource,
position: event.position,
buttons: event.buttons,
insideElementPressed: event.insideElementPressed, insideElementPressed: event.insideElementPressed,
buttonDownAny: event.buttonDownAny, buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent originalEvent: event.originalEvent

View File

@ -21,19 +21,14 @@
} }
$.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" ); $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'ontouchstart' in window ) { if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations (see http://www.w3.org/TR/2011/WD-touch-events-20110505) // 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" ); $.MouseTracker.subscribeEvents.push( "touchstart", "touchend", "touchmove", "touchcancel" );
if ( 'ontouchenter' in window ) {
$.MouseTracker.subscribeEvents.push( "touchenter", "touchleave" );
$.MouseTracker.haveTouchEnter = true;
} else {
$.MouseTracker.haveTouchEnter = false;
}
} else {
$.MouseTracker.haveTouchEnter = false;
} }
if ( 'ongesturestart' in window ) { if ( 'ongesturestart' in window ) {
// iOS (see https://developer.apple.com/library/safari/documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html) // iOS (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)
// Subscribe to these to prevent default gesture handling // Subscribe to these to prevent default gesture handling
$.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
} }