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,6 +34,9 @@
(function ( $ ) { (function ( $ ) {
// All MouseTracker instances
var MOUSETRACKERS = [];
// dictionary from hash to private properties // 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;
releasePointer( tracker, 'touch' );
// simulate touchleave
updatePointersExit( tracker, event, abortGPoints );
} }
/**
* @private
* @inner
*/
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

@ -562,6 +562,10 @@
* 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,6 +559,7 @@ function onStripExit( event ) {
function onKeyPress( event ) { function onKeyPress( event ) {
//console.log( event.keyCode ); //console.log( event.keyCode );
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch ( event.keyCode ) { switch ( event.keyCode ) {
case 61: //=|+ case 61: //=|+
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
@ -537,26 +570,25 @@ function onKeyPress( event ) {
case 48: //0|) case 48: //0|)
case 119: //w case 119: //w
case 87: //W case 87: //W
case 38: //up 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 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
case 37: //left 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 100: //d case 100: //d
case 39: //right 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;
default: default:
//console.log( 'navigator keycode %s', event.keyCode ); //console.log( 'navigator keycode %s', event.keyCode );
return true; return true;
} }
} else {
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,18 +110,22 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
//source //source
$.extend( true, this, options ); $.extend( true, this, options );
if (!this.success) {
//Any functions that are passed as arguments are bound to the ready callback //Any functions that are passed as arguments are bound to the ready callback
/*jshint loopfunc:true*/
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 ) {
callback( event );
} );
//only one callback per constructor //only one callback per constructor
break; break;
} }
} }
}
if (this.success) {
this.addHandler( 'ready', function ( event ) {
_this.success( event );
} );
}
/** /**
* Ratio of width to height * Ratio of width to height
@ -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,10 +403,14 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
}); });
} else { } else {
// request info via xhr asynchronously. // request info via xhr asynchronously.
$.makeAjaxRequest( url, function( xhr ) { $.makeAjaxRequest( {
url: url,
withCredentials: this.ajaxWithCredentials,
success: function( xhr ) {
var data = processResponse( xhr ); var data = processResponse( xhr );
callback( data ); callback( data );
}, function ( xhr, exc ) { },
error: function ( xhr, exc ) {
var msg; var msg;
/* /*
@ -422,6 +446,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
message: msg, message: msg,
source: url 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({
url: tileSource,
ajaxWithCredentials: viewer.ajaxWithCredentials,
success: function( event ) {
successCallback( event.tileSource ); 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" );
} }