Merge branch 'master' of https://github.com/openseadragon/openseadragon into fullscreen

This commit is contained in:
Antoine Vandecreme 2013-11-08 13:40:04 -05:00
commit fafc985ada
11 changed files with 576 additions and 267 deletions

View File

@ -4,23 +4,24 @@ OPENSEADRAGON CHANGELOG
1.0.0: (in progress) 1.0.0: (in progress)
* BREAKING CHANGE: All EventSource and MouseTracker event handler method signatures changed to 'handlerMethod(event)' where event == { eventSource, userData, ... } (#251) (Also fixes #23, #224, #239) * BREAKING CHANGE: All EventSource and MouseTracker event handler method signatures changed to 'handlerMethod(event)' where event == { eventSource, userData, ... } (#251) (Also fixes #23, #224, #239)
* The new eventSource property in the event object replaces the old eventSource parameter that was passed to handler methods. * The new eventSource property in the event object replaces the old eventSource parameter that was passed to handler methods.
* Where the event object duplicated the eventSource value, those properties have been removed. This effects the following events: * Where the event object duplicated the eventSource value, those properties have been removed. This affects the following events:
* All Button events - 'button' property removed * All Button events - 'button' property removed
* All Viewer (Viewer, Drawer, Viewport) events - 'viewer' property removed * All Viewer (Viewer, Drawer, Viewport) events - 'viewer' property removed
* BREAKING CHANGE: Renamed EventHandler to EventSource (#225) * BREAKING CHANGE: Renamed EventHandler to EventSource (#225)
* BREAKING CHANGE: Event names changed for consistency: changed to lower case, compound names hyphenated, and "on" prefixes removed (#226): * BREAKING CHANGE: Event names changed for consistency: changed to lower case, compound names hyphenated, and "on" prefixes removed (#226):
* Viewer "animationstart" changed to "animation-start" * Viewer "animationstart" changed to "animation-start"
* Viewer "animationfinish" changed to "animation-finish" * Viewer "animationfinish" changed to "animation-finish"
* Button "onPress" changed to "press" * Button "onPress" changed to "press"
* Button "onRelease" changed to "release" * Button "onRelease" changed to "release"
* Button "onClick" changed to "click" * Button "onClick" changed to "click"
* Button "onEnter" changed to "enter" * Button "onEnter" changed to "enter"
* Button "onExit" changed to "exit" * Button "onExit" changed to "exit"
* Button "onFocus" changed to "focus" * Button "onFocus" changed to "focus"
* Button "onBlur" changed to "blur" * Button "onBlur" changed to "blur"
* MouseTracker now passes the original event objects to its handler methods (#23) * MouseTracker now passes the original event objects to its handler methods (#23)
* MouseTracker now supports an optional 'moveHandler' method for tracking mousemove events (#215) * MouseTracker now supports an optional 'moveHandler' method for tracking mousemove events (#215)
* Added stopHandler to MouseTracker. (#262)
* Fixed: Element-relative mouse coordinates now correct if the element and/or page is scrolled (using new OpenSeadragon.getElementOffset() method) (#131) * Fixed: Element-relative mouse coordinates now correct if the element and/or page is scrolled (using new OpenSeadragon.getElementOffset() method) (#131)
* Fixed: Pinch zoom event issue, regressive issue from previous event system changes (#244) * Fixed: Pinch zoom event issue, regressive issue from previous event system changes (#244)
* Added IIIF Image API 1.1 Tile Source (#230) * Added IIIF Image API 1.1 Tile Source (#230)
@ -29,6 +30,20 @@ OPENSEADRAGON CHANGELOG
* Check that zoom reference point is valid before using it in zoomTo and zoomBy (#247) * Check that zoom reference point is valid before using it in zoomTo and zoomBy (#247)
* Added a number of easier coordinate conversion methods to viewport (#243) * Added a number of easier coordinate conversion methods to viewport (#243)
* Added the ability to create a viewer and start at a specified page (#252) * Added the ability to create a viewer and start at a specified page (#252)
* Fixed image resolve issue with collection mode (#255)
* DOM events are now passed through as 'event.originalEvent' in viewer and button events where appropriate. (#257) Affects the following events:
* Viewer: 'canvas-release', 'canvas-click', 'canvas-drag', 'canvas-scroll', 'container-enter', 'container-exit', 'container-release'
* Button: 'enter', 'exit', 'press', 'release', 'focus', 'blur', 'click'
* Fixed: IE 10 not reading DZI file correctly in certain circumstances (#218)
* Added support for the 'wheel' DOM mousewheel event (#261)
* Fix for non-canvas tile rendering at large size (#264)
* Drawer now uses an HTML5 canvas element whenever it's available. Can be overridden with the Viewer.useCanvas option (#191)
* Added a boolean preventDefaultAction property (default false) to the event object passed to MouseTracker handler methods. (#270) Implemented in the following MouseTracker subscribers:
* Viewer.keyboardCommandArea.innerTracker.focusHandler: preventDefaultAction == true prevents scrolling viewer into view
* Viewer.keyboardCommandArea.innerTracker.keyHandler: preventDefaultAction == true prevents viewer keyboard navigation
* Viewer.innerTracker.clickHandler: preventDefaultAction == true prevents viewer zoom on click
* Viewer.innerTracker.dragHandler: preventDefaultAction == true prevents viewer panning with mouse/touch
* Viewer.innerTracker.scrollHandler: preventDefaultAction == true prevents viewer zooming on mousewheel/pinch
0.9.131: 0.9.131:

View File

@ -178,7 +178,7 @@ $.Button = function( options ) {
enterHandler: function( event ) { enterHandler: function( event ) {
if ( event.insideElementPressed ) { if ( event.insideElementPressed ) {
inTo( _this, $.ButtonState.DOWN ); inTo( _this, $.ButtonState.DOWN );
_this.raiseEvent( "enter", {} ); _this.raiseEvent( "enter", { originalEvent: event.originalEvent } );
} else if ( !event.buttonDownAny ) { } else if ( !event.buttonDownAny ) {
inTo( _this, $.ButtonState.HOVER ); inTo( _this, $.ButtonState.HOVER );
} }
@ -186,30 +186,30 @@ $.Button = function( options ) {
focusHandler: function ( event ) { focusHandler: function ( event ) {
this.enterHandler( event ); this.enterHandler( event );
_this.raiseEvent( "focus", {} ); _this.raiseEvent( "focus", { originalEvent: event.originalEvent } );
}, },
exitHandler: function( event ) { exitHandler: function( event ) {
outTo( _this, $.ButtonState.GROUP ); outTo( _this, $.ButtonState.GROUP );
if ( event.insideElementPressed ) { if ( event.insideElementPressed ) {
_this.raiseEvent( "exit", {} ); _this.raiseEvent( "exit", { originalEvent: event.originalEvent } );
} }
}, },
blurHandler: function ( event ) { blurHandler: function ( event ) {
this.exitHandler( event ); this.exitHandler( event );
_this.raiseEvent( "blur", {} ); _this.raiseEvent( "blur", { originalEvent: event.originalEvent } );
}, },
pressHandler: function ( event ) { pressHandler: function ( event ) {
inTo( _this, $.ButtonState.DOWN ); inTo( _this, $.ButtonState.DOWN );
_this.raiseEvent( "press", {} ); _this.raiseEvent( "press", { originalEvent: event.originalEvent } );
}, },
releaseHandler: function( event ) { releaseHandler: function( event ) {
if ( event.insideElementPressed && event.insideElementReleased ) { if ( event.insideElementPressed && event.insideElementReleased ) {
outTo( _this, $.ButtonState.HOVER ); outTo( _this, $.ButtonState.HOVER );
_this.raiseEvent( "release", {} ); _this.raiseEvent( "release", { originalEvent: event.originalEvent } );
} else if ( event.insideElementPressed ) { } else if ( event.insideElementPressed ) {
outTo( _this, $.ButtonState.GROUP ); outTo( _this, $.ButtonState.GROUP );
} else { } else {
@ -219,15 +219,15 @@ $.Button = function( options ) {
clickHandler: function( event ) { clickHandler: function( event ) {
if ( event.quick ) { if ( event.quick ) {
_this.raiseEvent("click", {}); _this.raiseEvent("click", { originalEvent: event.originalEvent });
} }
}, },
keyHandler: function( event ){ keyHandler: function( event ){
//console.log( "%s : handling key %s!", _this.tooltip, event.keyCode); //console.log( "%s : handling key %s!", _this.tooltip, event.keyCode);
if( 13 === event.keyCode ){ if( 13 === event.keyCode ){
_this.raiseEvent( "click", {} ); _this.raiseEvent( "click", { originalEvent: event.originalEvent } );
_this.raiseEvent( "release", {} ); _this.raiseEvent( "release", { originalEvent: event.originalEvent } );
return false; return false;
} }
return true; return true;

View File

@ -44,14 +44,8 @@ var DEVICE_SCREEN = $.getWindowSize(),
( BROWSER == $.BROWSERS.SAFARI && BROWSER_VERSION >= 4 ) || ( BROWSER == $.BROWSERS.SAFARI && BROWSER_VERSION >= 4 ) ||
( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 ) || ( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 ) ||
( BROWSER == $.BROWSERS.IE && BROWSER_VERSION >= 9 ) ( BROWSER == $.BROWSERS.IE && BROWSER_VERSION >= 9 )
), );
USE_CANVAS = SUBPIXEL_RENDERING &&
!( DEVICE_SCREEN.x <= 400 || DEVICE_SCREEN.y <= 400 ) &&
!( navigator.appVersion.match( 'Mobile' ) ) &&
$.isFunction( document.createElement( "canvas" ).getContext );
//console.error( 'USE_CANVAS ' + USE_CANVAS );
/** /**
* @class * @class
@ -124,9 +118,10 @@ $.Drawer = function( options ) {
}, options ); }, options );
this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true );
this.container = $.getElement( this.element ); this.container = $.getElement( this.element );
this.canvas = $.makeNeutralElement( USE_CANVAS ? "canvas" : "div" ); this.canvas = $.makeNeutralElement( this.useCanvas ? "canvas" : "div" );
this.context = USE_CANVAS ? this.canvas.getContext( "2d" ) : null; this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null;
this.normHeight = this.source.dimensions.y / this.source.dimensions.x; this.normHeight = this.source.dimensions.y / this.source.dimensions.x;
this.element = this.container; this.element = this.container;
@ -405,7 +400,7 @@ $.Drawer.prototype = {
}, },
canRotate: function() { canRotate: function() {
return USE_CANVAS; return this.useCanvas;
} }
}; };
@ -522,7 +517,7 @@ function updateViewport( drawer ) {
//TODO //TODO
drawer.canvas.innerHTML = ""; drawer.canvas.innerHTML = "";
if ( USE_CANVAS ) { if ( drawer.useCanvas ) {
if( drawer.canvas.width != viewportSize.x || if( drawer.canvas.width != viewportSize.x ||
drawer.canvas.height != viewportSize.y ){ drawer.canvas.height != viewportSize.y ){
drawer.canvas.width = viewportSize.x; drawer.canvas.width = viewportSize.x;
@ -1160,6 +1155,7 @@ function drawTiles( drawer, lastDrawn ){
//$.console.log("Rendering collection tile %s | %s | %s", tile.y, tile.y, position); //$.console.log("Rendering collection tile %s | %s | %s", tile.y, tile.y, position);
if( tileSource ){ if( tileSource ){
drawer.collectionOverlays[ tileKey ] = viewer = new $.Viewer({ drawer.collectionOverlays[ tileKey ] = viewer = new $.Viewer({
hash: viewport.viewer.hash + "-" + tileKey,
element: $.makeNeutralElement( "div" ), element: $.makeNeutralElement( "div" ),
mouseNavEnabled: false, mouseNavEnabled: false,
showNavigator: false, showNavigator: false,
@ -1199,7 +1195,7 @@ function drawTiles( drawer, lastDrawn ){
} else { } else {
if ( USE_CANVAS ) { if ( drawer.useCanvas ) {
// TODO do this in a more performant way // TODO do this in a more performant way
// specifically, don't save,rotate,restore every time we draw a tile // specifically, don't save,rotate,restore every time we draw a tile
if( drawer.viewport.degrees !== 0 ) { if( drawer.viewport.degrees !== 0 ) {
@ -1262,7 +1258,7 @@ function restoreRotationChanges( tile, canvas, context ){
function drawDebugInfo( drawer, tile, count, i ){ function drawDebugInfo( drawer, tile, count, i ){
if ( USE_CANVAS ) { if ( drawer.useCanvas ) {
drawer.context.save(); drawer.context.save();
drawer.context.lineWidth = 2; drawer.context.lineWidth = 2;
drawer.context.font = 'small-caps bold 13px ariel'; drawer.context.font = 'small-caps bold 13px ariel';

View File

@ -59,11 +59,14 @@
* A reference to an element or an element id for which the mouse * A reference to an element or an element id for which the mouse
* events will be monitored. * events will be monitored.
* @param {Number} options.clickTimeThreshold * @param {Number} options.clickTimeThreshold
* The number of milliseconds within which mutliple mouse clicks * The number of milliseconds within which multiple mouse clicks
* will be treated as a single event. * will be treated as a single event.
* @param {Number} options.clickDistThreshold * @param {Number} options.clickDistThreshold
* The distance between mouse click within multiple mouse clicks * The distance between mouse click within multiple mouse clicks
* will be treated as a single event. * will be treated as a single event.
* @param {Number} options.stopDelay
* The number of milliseconds without mouse move before the mouse stop
* event is fired.
* @param {Function} options.enterHandler * @param {Function} options.enterHandler
* An optional handler for mouse enter. * An optional handler for mouse enter.
* @param {Function} options.exitHandler * @param {Function} options.exitHandler
@ -116,6 +119,7 @@
this.clickTimeThreshold = options.clickTimeThreshold; this.clickTimeThreshold = options.clickTimeThreshold;
this.clickDistThreshold = options.clickDistThreshold; this.clickDistThreshold = options.clickDistThreshold;
this.userData = options.userData || null; this.userData = options.userData || null;
this.stopDelay = options.stopDelay || 50;
this.enterHandler = options.enterHandler || null; this.enterHandler = options.enterHandler || null;
this.exitHandler = options.exitHandler || null; this.exitHandler = options.exitHandler || null;
@ -125,6 +129,7 @@
this.scrollHandler = options.scrollHandler || null; this.scrollHandler = options.scrollHandler || null;
this.clickHandler = options.clickHandler || null; this.clickHandler = options.clickHandler || null;
this.dragHandler = options.dragHandler || null; this.dragHandler = options.dragHandler || null;
this.stopHandler = options.stopHandler || 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;
@ -157,8 +162,10 @@
mouseup: function ( event ) { onMouseUp( _this, event, false ); }, mouseup: function ( event ) { onMouseUp( _this, event, false ); },
mousemove: function ( event ) { onMouseMove( _this, event ); }, mousemove: function ( event ) { onMouseMove( _this, event ); },
click: function ( event ) { onMouseClick( _this, event ); }, click: function ( event ) { onMouseClick( _this, event ); },
DOMMouseScroll: function ( event ) { onMouseWheelSpin( _this, event, false ); }, wheel: function ( event ) { onWheel( _this, event ); },
mousewheel: function ( event ) { onMouseWheelSpin( _this, event, false ); }, mousewheel: function ( event ) { onMouseWheel( _this, event ); },
DOMMouseScroll: function ( event ) { onMouseWheel( _this, event ); },
MozMousePixelScroll: function ( event ) { onMouseWheel( _this, event ); },
mouseupie: function ( event ) { onMouseUpIE( _this, event ); }, mouseupie: function ( event ) { onMouseUpIE( _this, event ); },
mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); }, mousemovecapturedie: function ( event ) { onMouseMoveCapturedIE( _this, event ); },
mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); }, mouseupcaptured: function ( event ) { onMouseUpCaptured( _this, event ); },
@ -219,7 +226,7 @@
}, },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -236,13 +243,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
enterHandler: function () { }, enterHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -259,13 +268,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
exitHandler: function () { }, exitHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -277,13 +288,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
pressHandler: function () { }, pressHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -300,13 +313,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
releaseHandler: function () { }, releaseHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -318,13 +333,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
moveHandler: function () { }, moveHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -340,13 +357,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
scrollHandler: function () { }, scrollHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -362,13 +381,15 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
clickHandler: function () { }, clickHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -384,13 +405,35 @@
* True if the original event is a touch event, otherwise false. * True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
dragHandler: function () { }, dragHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor.
* @function
* @param {Object} event
* @param {OpenSeadragon.MouseTracker} event.eventSource
* A reference to the tracker instance.
* @param {OpenSeadragon.Point} event.position
* The position of the event relative to the tracked element.
* @param {Boolean} event.isTouchEvent
* True if the original event is a touch event, otherwise false.
* @param {Object} event.originalEvent
* The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData
* Arbitrary user-defined object.
*/
stopHandler: function () { },
/**
* Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -402,13 +445,15 @@
* True if the shift key was pressed during this event. * True if the shift 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=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
keyHandler: function () { }, keyHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -416,13 +461,15 @@
* A reference to the tracker instance. * A reference to the tracker instance.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
focusHandler: function () { }, focusHandler: function () { },
/** /**
* Implement or assign implmentation to these handlers during or after * Implement or assign implementation to these handlers during or after
* calling the constructor. * calling the constructor.
* @function * @function
* @param {Object} event * @param {Object} event
@ -430,12 +477,22 @@
* A reference to the tracker instance. * A reference to the tracker instance.
* @param {Object} event.originalEvent * @param {Object} event.originalEvent
* The original event object. * The original event object.
* @param {Boolean} [event.preventDefaultAction=false]
* Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent).
* @param {Object} event.userData * @param {Object} event.userData
* Arbitrary user-defined object. * Arbitrary user-defined object.
*/ */
blurHandler: function () { } blurHandler: function () { }
}; };
/**
* Detect available mouse wheel event.
*/
$.MouseTracker.wheelEventName = ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version > 8 ) ||
( 'onwheel' in document.createElement( 'div' ) ) ? 'wheel' : // Modern browsers support 'wheel'
document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least 'mousewheel'
'DOMMouseScroll'; // Assume old Firefox
/** /**
* Starts tracking mouse events on this element. * Starts tracking mouse events on this element.
* @private * @private
@ -445,7 +502,7 @@
var events = [ var events = [
"mouseover", "mouseout", "mousedown", "mouseup", "mousemove", "mouseover", "mouseout", "mousedown", "mouseup", "mousemove",
"click", "click",
"DOMMouseScroll", "mousewheel", $.MouseTracker.wheelEventName,
"touchstart", "touchmove", "touchend", "touchstart", "touchmove", "touchend",
"keypress", "keypress",
"focus", "blur" "focus", "blur"
@ -454,6 +511,11 @@
event, event,
i; i;
// Add 'MozMousePixelScroll' event handler for older Firefox
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
events.push( "MozMousePixelScroll" );
}
if ( !delegate.tracking ) { if ( !delegate.tracking ) {
for ( i = 0; i < events.length; i++ ) { for ( i = 0; i < events.length; i++ ) {
event = events[ i ]; event = events[ i ];
@ -478,7 +540,7 @@
var events = [ var events = [
"mouseover", "mouseout", "mousedown", "mouseup", "mousemove", "mouseover", "mouseout", "mousedown", "mouseup", "mousemove",
"click", "click",
"DOMMouseScroll", "mousewheel", $.MouseTracker.wheelEventName,
"touchstart", "touchmove", "touchend", "touchstart", "touchmove", "touchend",
"keypress", "keypress",
"focus", "blur" "focus", "blur"
@ -487,6 +549,11 @@
event, event,
i; i;
// Remove 'MozMousePixelScroll' event handler for older Firefox
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
events.push( "MozMousePixelScroll" );
}
if ( delegate.tracking ) { if ( delegate.tracking ) {
for ( i = 0; i < events.length; i++ ) { for ( i = 0; i < events.length; i++ ) {
event = events[ i ]; event = events[ i ];
@ -630,9 +697,10 @@
if ( tracker.focusHandler ) { if ( tracker.focusHandler ) {
propagate = tracker.focusHandler( propagate = tracker.focusHandler(
{ {
eventSource: tracker, eventSource: tracker,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -652,9 +720,10 @@
if ( tracker.blurHandler ) { if ( tracker.blurHandler ) {
propagate = tracker.blurHandler( propagate = tracker.blurHandler(
{ {
eventSource: tracker, eventSource: tracker,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -674,12 +743,13 @@
if ( tracker.keyHandler ) { if ( tracker.keyHandler ) {
propagate = tracker.keyHandler( propagate = tracker.keyHandler(
{ {
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,
shift: event.shiftKey, shift: event.shiftKey,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( !propagate ) { if ( !propagate ) {
@ -729,13 +799,14 @@
if ( tracker.enterHandler ) { if ( tracker.enterHandler ) {
propagate = tracker.enterHandler( propagate = tracker.enterHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ), position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ),
insideElementPressed: delegate.insideElementPressed, insideElementPressed: delegate.insideElementPressed,
buttonDownAny: IS_BUTTON_DOWN, buttonDownAny: IS_BUTTON_DOWN,
isTouchEvent: isTouch, isTouchEvent: isTouch,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -785,13 +856,14 @@
if ( tracker.exitHandler ) { if ( tracker.exitHandler ) {
propagate = tracker.exitHandler( propagate = tracker.exitHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ), position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ),
insideElementPressed: delegate.insideElementPressed, insideElementPressed: delegate.insideElementPressed,
buttonDownAny: IS_BUTTON_DOWN, buttonDownAny: IS_BUTTON_DOWN,
isTouchEvent: isTouch, isTouchEvent: isTouch,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
@ -829,11 +901,12 @@
if ( tracker.pressHandler ) { if ( tracker.pressHandler ) {
propagate = tracker.pressHandler( propagate = tracker.pressHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( eventOrTouchPoint, tracker.element ), position: getMouseRelative( eventOrTouchPoint, tracker.element ),
isTouchEvent: isTouch, isTouchEvent: isTouch,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -924,13 +997,14 @@
if ( tracker.releaseHandler ) { if ( tracker.releaseHandler ) {
propagate = tracker.releaseHandler( propagate = tracker.releaseHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ), position: getMouseRelative( isTouch ? event.changedTouches[ 0 ] : event, tracker.element ),
insideElementPressed: insideElementPressed, insideElementPressed: insideElementPressed,
insideElementReleased: insideElementReleased, insideElementReleased: insideElementReleased,
isTouchEvent: isTouch, isTouchEvent: isTouch,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -1042,19 +1116,42 @@
var propagate = tracker.moveHandler( var propagate = tracker.moveHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( event, tracker.element ), position: getMouseRelative( event, tracker.element ),
isTouchEvent: false, isTouchEvent: false,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
$.cancelEvent( event ); $.cancelEvent( event );
} }
} }
if ( tracker.stopHandler ) {
clearTimeout( tracker.stopTimeOut );
tracker.stopTimeOut = setTimeout( function() {
onMouseStop( tracker, event );
}, tracker.stopDelay );
}
}
/**
* @private
* @inner
*/
function onMouseStop( tracker, originalMoveEvent ) {
if ( tracker.stopHandler ) {
tracker.stopHandler( {
eventSource: tracker,
position: getMouseRelative( originalMoveEvent, tracker.element ),
isTouchEvent: false,
originalEvent: originalMoveEvent,
preventDefaultAction: false,
userData: tracker.userData
} );
}
} }
/** /**
* @private * @private
@ -1068,49 +1165,86 @@
/** /**
* Handler for 'wheel' events
*
* @private * @private
* @inner * @inner
*/ */
function onMouseWheelSpin( tracker, event, isTouch ) { function onWheel( tracker, event ) {
handleWheelEvent( tracker, event, event, false );
}
/**
* Handler for 'mousewheel', 'DOMMouseScroll', and 'MozMousePixelScroll' events
*
* @private
* @inner
*/
function onMouseWheel( tracker, event ) {
// For legacy IE, access the global (window) event object
event = event || window.event;
// Simulate a 'wheel' event
var simulatedEvent = {
target: event.target || event.srcElement,
type: "wheel",
shiftKey: event.shiftKey || false,
clientX: event.clientX,
clientY: event.clientY,
pageX: event.pageX ? event.pageX : event.clientX,
pageY: event.pageY ? event.pageY : event.clientY,
deltaMode: event.type == "MozMousePixelScroll" ? 0 : 1, // 0=pixel, 1=line, 2=page
deltaX: 0,
deltaZ: 0
};
// Calculate deltaY
if ( $.MouseTracker.wheelEventName == "mousewheel" ) {
simulatedEvent.deltaY = - 1 / $.DEFAULT_SETTINGS.pixelsPerWheelLine * event.wheelDelta;
} else {
simulatedEvent.deltaY = event.detail;
}
handleWheelEvent( tracker, simulatedEvent, event, false );
}
/**
* Handles 'wheel' events.
* The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()) or onTouchMove().
*
* @private
* @inner
*/
function handleWheelEvent( tracker, event, originalEvent, isTouch ) {
var nDelta = 0, var nDelta = 0,
propagate; propagate;
isTouch = isTouch || false; isTouch = isTouch || false;
if ( !event ) { // For IE, access the global (window) event object // The nDelta variable is gated to provide smooth z-index scrolling
event = window.event; // since the mouse wheel allows for substantial deltas meant for rapid
} // y-index scrolling.
// event.deltaMode: 0=pixel, 1=line, 2=page
if ( event.wheelDelta ) { // IE and Opera // TODO: Deltas in pixel mode should be accumulated then a scroll value computed after $.DEFAULT_SETTINGS.pixelsPerWheelLine threshold reached
nDelta = event.wheelDelta; nDelta = event.deltaY < 0 ? 1 : -1;
if ( window.opera ) { // Opera has the values reversed
nDelta = -nDelta;
}
} else if ( event.detail ) { // Mozilla FireFox
nDelta = -event.detail;
}
//The nDelta variable is gated to provide smooth z-index scrolling
//since the mouse wheel allows for substantial deltas meant for rapid
//y-index scrolling.
nDelta = nDelta > 0 ? 1 : -1;
if ( tracker.scrollHandler ) { if ( tracker.scrollHandler ) {
propagate = tracker.scrollHandler( propagate = tracker.scrollHandler(
{ {
eventSource: tracker, eventSource: tracker,
// Note: Ok to call getMouseRelative on passed event for isTouch==true since position: getMouseRelative( event, tracker.element ),
// event.pageX/event.pageY are added to the original touchmove event in scroll: nDelta,
// onTouchMove(). shift: event.shiftKey,
position: getMouseRelative( event, tracker.element ), isTouchEvent: isTouch,
scroll: nDelta, originalEvent: originalEvent,
shift: event.shiftKey, preventDefaultAction: false,
isTouchEvent: isTouch, userData: tracker.userData
originalEvent: event,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
$.cancelEvent( event ); $.cancelEvent( originalEvent );
} }
} }
} }
@ -1143,13 +1277,14 @@
if ( tracker.clickHandler ) { if ( tracker.clickHandler ) {
propagate = tracker.clickHandler( propagate = tracker.clickHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( eventOrTouchPoint, tracker.element ), position: getMouseRelative( eventOrTouchPoint, tracker.element ),
quick: quick, quick: quick,
shift: event.shiftKey, shift: event.shiftKey,
isTouchEvent: isTouch, isTouchEvent: isTouch,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -1181,13 +1316,14 @@
if ( tracker.dragHandler ) { if ( tracker.dragHandler ) {
propagate = tracker.dragHandler( propagate = tracker.dragHandler(
{ {
eventSource: tracker, eventSource: tracker,
position: getMouseRelative( eventOrTouchPoint, tracker.element ), position: getMouseRelative( eventOrTouchPoint, tracker.element ),
delta: delta, delta: delta,
shift: event.shiftKey, shift: event.shiftKey,
isTouchEvent: isTouch, isTouchEvent: isTouch,
originalEvent: event, originalEvent: event,
userData: tracker.userData preventDefaultAction: false,
userData: tracker.userData
} }
); );
if ( propagate === false ) { if ( propagate === false ) {
@ -1229,15 +1365,22 @@
if ( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ) { if ( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ) {
//$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta);
// Simulate a mouse wheel scroll event // Simulate a 'wheel' event
var simulatedEvent = { var simulatedEvent = {
shiftKey: event.shiftKey || false, target: event.target || event.srcElement,
pageX: THIS[ tracker.hash ].pinchMidpoint.x, type: "wheel",
pageY: THIS[ tracker.hash ].pinchMidpoint.y, shiftKey: event.shiftKey || false,
detail: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1 clientX: THIS[ tracker.hash ].pinchMidpoint.x,
clientY: THIS[ tracker.hash ].pinchMidpoint.y,
pageX: THIS[ tracker.hash ].pinchMidpoint.x,
pageY: THIS[ tracker.hash ].pinchMidpoint.y,
deltaMode: 1, // 0=pixel, 1=line, 2=page
deltaX: 0,
deltaY: ( THIS[ tracker.hash ].lastPinchDelta > pinchDelta ) ? 1 : -1,
deltaZ: 0
}; };
onMouseWheelSpin( tracker, simulatedEvent, true ); handleWheelEvent( tracker, simulatedEvent, event, true );
THIS[ tracker.hash ].lastPinchDelta = pinchDelta; THIS[ tracker.hash ].lastPinchDelta = pinchDelta;
} }

View File

@ -226,6 +226,9 @@
* @param {Number} [options.maxImageCacheCount=100] * @param {Number} [options.maxImageCacheCount=100]
* The max number of images we should keep in memory (per drawer). * The max number of images we should keep in memory (per drawer).
* *
* @param {Boolean} [options.useCanvas=true]
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
*
* @param {Number} [options.minPixelRatio=0.5] * @param {Number} [options.minPixelRatio=0.5]
* The higher the minPixelRatio, the lower the quality of the image that * The higher the minPixelRatio, the lower the quality of the image that
* is considered sufficient to stop rendering a given zoom level. For * is considered sufficient to stop rendering a given zoom level. For
@ -293,6 +296,12 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
toString = Object.prototype.toString, toString = Object.prototype.toString,
hasOwn = Object.prototype.hasOwnProperty; hasOwn = Object.prototype.hasOwnProperty;
// Detects canvas support
function isCanvasSupported() {
var canvasElement = document.createElement( 'canvas' );
return !!( $.isFunction( canvasElement.getContext ) &&
canvasElement.getContext( '2d' ) );
}
/** /**
* Taken from jQuery 1.6.1 * Taken from jQuery 1.6.1
@ -386,6 +395,52 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
}; };
/**
* True if the browser supports the HTML5 canvas element
* @name $.supportsCanvas
* @property
*/
$.supportsCanvas = isCanvasSupported();
/**
* Detect event model and create appropriate _addEvent/_removeEvent methods
*/
if ( window.addEventListener ) {
$._addEvent = function ( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.addEventListener( eventName, handler, useCapture );
};
} else if ( window.attachEvent ) {
$._addEvent = function ( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.attachEvent( 'on' + eventName, handler );
if ( useCapture && element.setCapture ) {
element.setCapture();
}
};
} else {
throw new Error( "No known event model." );
}
if ( window.removeEventListener ) {
$._removeEvent = function ( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.removeEventListener( eventName, handler, useCapture );
};
} else if ( window.detachEvent ) {
$._removeEvent = function( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.detachEvent( 'on' + eventName, handler );
if ( useCapture && element.releaseCapture ) {
element.releaseCapture();
}
};
} else {
throw new Error( "No known event model." );
}
}( OpenSeadragon )); }( OpenSeadragon ));
/** /**
@ -517,6 +572,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
immediateRender: false, immediateRender: false,
minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
pixelsPerWheelLine: 40,
//DEFAULT CONTROL SETTINGS //DEFAULT CONTROL SETTINGS
showSequenceControl: true, //SEQUENCE showSequenceControl: true, //SEQUENCE
@ -559,6 +615,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
imageLoaderLimit: 0, imageLoaderLimit: 0,
maxImageCacheCount: 200, maxImageCacheCount: 200,
timeout: 30000, timeout: 30000,
useCanvas: true, // Use canvas element for drawing if available
//INTERFACE RESOURCE SETTINGS //INTERFACE RESOURCE SETTINGS
prefixUrl: "/images/", prefixUrl: "/images/",
@ -1192,33 +1249,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
* @param {String} eventName * @param {String} eventName
* @param {Function} handler * @param {Function} handler
* @param {Boolean} [useCapture] * @param {Boolean} [useCapture]
* @throws {Error}
*/ */
addEvent: function( element, eventName, handler, useCapture ) { addEvent: function( element, eventName, handler, useCapture ) {
element = $.getElement( element ); return $._addEvent( element, eventName, handler, useCapture );
//TODO: Why do this if/else on every method call instead of just
// defining this function once based on the same logic
if ( element.addEventListener ) {
$.addEvent = function( element, eventName, handler, useCapture ){
element = $.getElement( element );
element.addEventListener( eventName, handler, useCapture );
};
} else if ( element.attachEvent ) {
$.addEvent = function( element, eventName, handler, useCapture ){
element = $.getElement( element );
element.attachEvent( "on" + eventName, handler );
if ( useCapture && element.setCapture ) {
element.setCapture();
}
};
} else {
throw new Error(
"Unable to attach event handler, no known technique."
);
}
return $.addEvent( element, eventName, handler, useCapture );
}, },
@ -1231,32 +1264,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
* @param {String} eventName * @param {String} eventName
* @param {Function} handler * @param {Function} handler
* @param {Boolean} [useCapture] * @param {Boolean} [useCapture]
* @throws {Error}
*/ */
removeEvent: function( element, eventName, handler, useCapture ) { removeEvent: function( element, eventName, handler, useCapture ) {
element = $.getElement( element ); return $._removeEvent( element, eventName, handler, useCapture );
//TODO: Why do this if/else on every method call instead of just
// defining this function once based on the same logic
if ( element.removeEventListener ) {
$.removeEvent = function( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.removeEventListener( eventName, handler, useCapture );
};
} else if ( element.detachEvent ) {
$.removeEvent = function( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.detachEvent("on" + eventName, handler);
if ( useCapture && element.releaseCapture ) {
element.releaseCapture();
}
};
} else {
throw new Error(
"Unable to detach event handler, no known technique."
);
}
return $.removeEvent( element, eventName, handler, useCapture );
}, },

View File

@ -54,8 +54,9 @@
* this tile failed to load? * this tile failed to load?
* @property {String} url The URL of this tile's image. * @property {String} url The URL of this tile's image.
* @property {Boolean} loaded Is this tile loaded? * @property {Boolean} loaded Is this tile loaded?
* @property {Boolean} loading Is this tile loading * @property {Boolean} loading Is this tile loading?
* @property {Element} element The HTML element for this tile * @property {Element} element The HTML div element for this tile
* @property {Element} imgElement The HTML img element for this tile
* @property {Image} image The Image object for this tile * @property {Image} image The Image object for this tile
* @property {String} style The alias of this.element.style. * @property {String} style The alias of this.element.style.
* @property {String} position This tile's position on screen, in pixels. * @property {String} position This tile's position on screen, in pixels.
@ -78,6 +79,7 @@ $.Tile = function(level, x, y, bounds, exists, url) {
this.loading = false; this.loading = false;
this.element = null; this.element = null;
this.imgElement = null;
this.image = null; this.image = null;
this.style = null; this.style = null;
@ -122,9 +124,12 @@ $.Tile.prototype = {
// content during animation of the container size. // content during animation of the container size.
if ( !this.element ) { if ( !this.element ) {
this.element = $.makeNeutralElement("img"); this.element = $.makeNeutralElement( "div" );
this.element.src = this.url; this.imgElement = $.makeNeutralElement( "img" );
this.element.style.msInterpolationMode = "nearest-neighbor"; this.imgElement.src = this.url;
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
this.imgElement.style.width = "100%";
this.imgElement.style.height = "100%";
this.style = this.element.style; this.style = this.element.style;
this.style.position = "absolute"; this.style.position = "absolute";
@ -132,6 +137,9 @@ $.Tile.prototype = {
if ( this.element.parentNode != container ) { if ( this.element.parentNode != container ) {
container.appendChild( this.element ); container.appendChild( this.element );
} }
if ( this.imgElement.parentNode != this.element ) {
this.element.appendChild( this.imgElement );
}
this.style.top = this.position.y + "px"; this.style.top = this.position.y + "px";
this.style.left = this.position.x + "px"; this.style.left = this.position.x + "px";
@ -216,6 +224,9 @@ $.Tile.prototype = {
* @function * @function
*/ */
unload: function() { unload: function() {
if ( this.imgElement && this.imgElement.parentNode ) {
this.imgElement.parentNode.removeChild( this.imgElement );
}
if ( this.element && this.element.parentNode ) { if ( this.element && this.element.parentNode ) {
this.element.parentNode.removeChild( this.element ); this.element.parentNode.removeChild( this.element );
} }
@ -223,10 +234,11 @@ $.Tile.prototype = {
delete TILE_CACHE[ this.url ]; delete TILE_CACHE[ this.url ];
} }
this.element = null; this.element = null;
this.image = null; this.imgElement = null;
this.loaded = false; this.image = null;
this.loading = false; this.loaded = false;
this.loading = false;
} }
}; };

View File

@ -292,6 +292,9 @@ $.TileSource.prototype = {
} }
callback = function( data ){ callback = function( data ){
if( typeof(data) === "string" ) {
data = $.parseXml( data );
}
var $TileSource = $.TileSource.determineType( _this, data, url ); var $TileSource = $.TileSource.determineType( _this, data, url );
if ( !$TileSource ) { if ( !$TileSource ) {
_this.raiseEvent( 'open-failed', { message: "Unable to load TileSource", source: url } ); _this.raiseEvent( 'open-failed', { message: "Unable to load TileSource", source: url } );

View File

@ -100,7 +100,7 @@ $.Viewer = function( options ) {
//internal state and dom identifiers //internal state and dom identifiers
id: options.id, id: options.id,
hash: options.id, hash: options.hash || options.id,
//dom nodes //dom nodes
element: null, element: null,
@ -147,6 +147,15 @@ $.Viewer = function( options ) {
}, $.DEFAULT_SETTINGS, options ); }, $.DEFAULT_SETTINGS, options );
if ( typeof( this.hash) === "undefined" ) {
throw new Error("A hash must be defined, either by specifying options.id or options.hash.");
}
if ( typeof( THIS[ this.hash ] ) !== "undefined" ) {
// We don't want to throw an error here, as the user might have discarded
// the previous viewer with the same hash and now want to recreate it.
$.console.warn("Hash " + this.hash + " has already been used.");
}
//Private state properties //Private state properties
THIS[ this.hash ] = { THIS[ this.hash ] = {
"fsBoundsDelta": new $.Point( 1, 1 ), "fsBoundsDelta": new $.Point( 1, 1 ),
@ -267,58 +276,62 @@ $.Viewer = function( options ) {
this.keyboardCommandArea.innerTracker = new $.MouseTracker({ this.keyboardCommandArea.innerTracker = new $.MouseTracker({
_this : this, _this : this,
element: this.keyboardCommandArea, element: this.keyboardCommandArea,
focusHandler: function(){ focusHandler: function( event ){
var point = $.getElementPosition( this.element ); if ( !event.preventDefaultAction ) {
window.scrollTo( 0, point.y ); var point = $.getElementPosition( this.element );
window.scrollTo( 0, point.y );
}
}, },
keyHandler: function( event ){ keyHandler: function( event ){
switch( event.keyCode ){ if ( !event.preventDefaultAction ) {
case 61://=|+ switch( event.keyCode ){
_this.viewport.zoomBy(1.1); case 61://=|+
_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); _this.viewport.zoomBy(1.1);
} else { _this.viewport.applyConstraints();
_this.viewport.panBy(new $.Point(0, -0.05)); return false;
} case 45://-|_
_this.viewport.applyConstraints();
return false;
case 115://s
case 83://S
case 40://down arrow
if ( event.shift ) {
_this.viewport.zoomBy(0.9); _this.viewport.zoomBy(0.9);
} else { _this.viewport.applyConstraints();
_this.viewport.panBy(new $.Point(0, 0.05)); return false;
} case 48://0|)
_this.viewport.applyConstraints(); _this.viewport.goHome();
return false; _this.viewport.applyConstraints();
case 97://a return false;
case 37://left arrow case 119://w
_this.viewport.panBy(new $.Point(-0.05, 0)); case 87://W
_this.viewport.applyConstraints(); case 38://up arrow
return false; if ( event.shift ) {
case 100://d _this.viewport.zoomBy(1.1);
case 39://right arrow } else {
_this.viewport.panBy(new $.Point(0.05, 0)); _this.viewport.panBy(new $.Point(0, -0.05));
_this.viewport.applyConstraints(); }
return false; _this.viewport.applyConstraints();
default: return false;
//console.log( 'navigator keycode %s', event.keyCode ); case 115://s
return true; 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 }).setTracking( true ); // default state
@ -521,7 +534,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
VIEWERS[ this.hash ] = null; VIEWERS[ this.hash ] = null;
delete VIEWERS[ this.hash ]; delete VIEWERS[ this.hash ];
this.raiseEvent( 'close', {} ); this.raiseEvent( 'close' );
return this; return this;
}, },
@ -1452,7 +1465,7 @@ function onBlur(){
function onCanvasClick( event ) { function onCanvasClick( event ) {
var zoomPerClick, var zoomPerClick,
factor; factor;
if ( this.viewport && event.quick ) { // ignore clicks where mouse moved if ( !event.preventDefaultAction && this.viewport && event.quick ) { // ignore clicks where mouse moved
zoomPerClick = this.zoomPerClick; zoomPerClick = this.zoomPerClick;
factor = event.shift ? 1.0 / zoomPerClick : zoomPerClick; factor = event.shift ? 1.0 / zoomPerClick : zoomPerClick;
this.viewport.zoomBy( this.viewport.zoomBy(
@ -1465,12 +1478,13 @@ function onCanvasClick( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
quick: event.quick, quick: event.quick,
shift: event.shift shift: event.shift,
originalEvent: event.originalEvent
}); });
} }
function onCanvasDrag( event ) { function onCanvasDrag( event ) {
if ( this.viewport ) { if ( !event.preventDefaultAction && this.viewport ) {
if( !this.panHorizontal ){ if( !this.panHorizontal ){
event.delta.x = 0; event.delta.x = 0;
} }
@ -1490,7 +1504,8 @@ function onCanvasDrag( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
delta: event.delta, delta: event.delta,
shift: event.shift shift: event.shift,
originalEvent: event.originalEvent
}); });
} }
@ -1502,13 +1517,14 @@ function onCanvasRelease( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
insideElementPressed: event.insideElementPressed, insideElementPressed: event.insideElementPressed,
insideElementReleased: event.insideElementReleased insideElementReleased: event.insideElementReleased,
originalEvent: event.originalEvent
}); });
} }
function onCanvasScroll( event ) { function onCanvasScroll( event ) {
var factor; var factor;
if ( this.viewport ) { if ( !event.preventDefaultAction && this.viewport ) {
factor = Math.pow( this.zoomPerScroll, event.scroll ); factor = Math.pow( this.zoomPerScroll, event.scroll );
this.viewport.zoomBy( this.viewport.zoomBy(
factor, factor,
@ -1520,7 +1536,8 @@ function onCanvasScroll( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
scroll: event.scroll, scroll: event.scroll,
shift: event.shift shift: event.shift,
originalEvent: event.originalEvent
}); });
//cancels event //cancels event
return false; return false;
@ -1537,7 +1554,8 @@ function onContainerExit( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
insideElementPressed: event.insideElementPressed, insideElementPressed: event.insideElementPressed,
buttonDownAny: event.buttonDownAny buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent
}); });
} }
@ -1552,7 +1570,8 @@ function onContainerRelease( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
insideElementPressed: event.insideElementPressed, insideElementPressed: event.insideElementPressed,
insideElementReleased: event.insideElementReleased insideElementReleased: event.insideElementReleased,
originalEvent: event.originalEvent
}); });
} }
@ -1563,7 +1582,8 @@ function onContainerEnter( event ) {
tracker: event.eventSource, tracker: event.eventSource,
position: event.position, position: event.position,
insideElementPressed: event.insideElementPressed, insideElementPressed: event.insideElementPressed,
buttonDownAny: event.buttonDownAny buttonDownAny: event.buttonDownAny,
originalEvent: event.originalEvent
}); });
} }

View File

@ -916,19 +916,41 @@ $.Viewport.prototype = {
return viewerCoordinates.plus( return viewerCoordinates.plus(
OpenSeadragon.getElementPosition( this.viewer.element )); OpenSeadragon.getElementPosition( this.viewer.element ));
}, },
/** /**
* Get the zoom ratio of the image. 1 means original image size, 0.5 half size... * Convert a viewport zoom to an image zoom.
* Image zoom: ratio of the original image size to displayed image size.
* 1 means original image size, 0.5 half size...
* Viewport zoom: ratio of the displayed image's width to viewport's width.
* 1 means identical width, 2 means image's width is twice the viewport's width...
* @function * @function
* @param {Boolean} current If true gives the current zoom otherwise gives the * @param {Number} viewportZoom The viewport zoom
* target zoom. * target zoom.
* @returns {Number} * @returns {Number} imageZoom The image zoom
*/ */
getImageZoomRatio: function( current ) { viewportToImageZoom: function( viewportZoom ) {
var imageWidth = this.viewer.source.dimensions.x; var imageWidth = this.viewer.source.dimensions.x;
var containerWidth = this.getContainerSize().x; var containerWidth = this.getContainerSize().x;
var zoomToZoomLevelRatio = containerWidth / imageWidth; var viewportToImageZoomRatio = containerWidth / imageWidth;
return this.getZoom( current ) * zoomToZoomLevelRatio; return viewportZoom * viewportToImageZoomRatio;
},
/**
* Convert an image zoom to a viewport zoom.
* Image zoom: ratio of the original image size to displayed image size.
* 1 means original image size, 0.5 half size...
* Viewport zoom: ratio of the displayed image's width to viewport's width.
* 1 means identical width, 2 means image's width is twice the viewport's width...
* @function
* @param {Number} imageZoom The image zoom
* target zoom.
* @returns {Number} viewportZoom The viewport zoom
*/
imageToViewportZoom: function( imageZoom ) {
var imageWidth = this.viewer.source.dimensions.x;
var containerWidth = this.getContainerSize().x;
var viewportToImageZoomRatio = imageWidth / containerWidth;
return imageZoom * viewportToImageZoomRatio;
} }
}; };

View File

@ -74,6 +74,8 @@
eventsHandledMouseTracker = 0, eventsHandledMouseTracker = 0,
eventSourcePassedMouseTracker = 0, eventSourcePassedMouseTracker = 0,
originalEventsPassedMouseTracker = 0, originalEventsPassedMouseTracker = 0,
eventsHandledViewer = 0,
originalEventsPassedViewer = 0,
releasesExpected = 1, releasesExpected = 1,
clicksExpected = 1; clicksExpected = 1;
@ -117,15 +119,27 @@
$canvas.simulate( 'blur', event ); $canvas.simulate( 'blur', event );
}; };
var checkOriginalEventReceivedViewer = function ( event ) {
eventsHandledViewer++;
//TODO Provide a better check for the original event...simulate doesn't currently extend the object
// with arbitrary user data.
if ( event && event.originalEvent ) {
originalEventsPassedViewer++;
}
};
var onEventSourceDrag = function ( event ) { var onEventSourceDrag = function ( event ) {
checkOriginalEventReceivedViewer( event );
dragsHandledEventSource++; dragsHandledEventSource++;
}; };
var onEventSourceRelease = function ( event ) { var onEventSourceRelease = function ( event ) {
checkOriginalEventReceivedViewer( event );
releasesHandledEventSource++; releasesHandledEventSource++;
}; };
var onEventSourceClick = function ( event ) { var onEventSourceClick = function ( event ) {
checkOriginalEventReceivedViewer( event );
clicksHandledEventSource++; clicksHandledEventSource++;
}; };
@ -184,6 +198,7 @@
equal( dragsHandledEventSource, dragCount, "'canvas-drag' event count matches 'mousemove' event count (" + dragCount + ")" ); equal( dragsHandledEventSource, dragCount, "'canvas-drag' event count matches 'mousemove' event count (" + dragCount + ")" );
equal( releasesHandledEventSource, releasesExpected, "'canvas-release' event count matches expected (" + releasesExpected + ")" ); equal( releasesHandledEventSource, releasesExpected, "'canvas-release' event count matches expected (" + releasesExpected + ")" );
equal( clicksHandledEventSource, releasesExpected, "'canvas-click' event count matches expected (" + releasesExpected + ")" ); equal( clicksHandledEventSource, releasesExpected, "'canvas-click' event count matches expected (" + releasesExpected + ")" );
equal( originalEventsPassedViewer, eventsHandledViewer, "Original event received count matches expected (" + eventsHandledViewer + ")" );
equal( eventSourcePassedMouseTracker, eventsHandledMouseTracker, "Event source received count matches expected (" + eventsHandledMouseTracker + ")" ); equal( eventSourcePassedMouseTracker, eventsHandledMouseTracker, "Event source received count matches expected (" + eventsHandledMouseTracker + ")" );
equal( originalEventsPassedMouseTracker, eventsHandledMouseTracker, "Original event received count matches expected (" + eventsHandledMouseTracker + ")" ); equal( originalEventsPassedMouseTracker, eventsHandledMouseTracker, "Original event received count matches expected (" + eventsHandledMouseTracker + ")" );
@ -197,4 +212,72 @@
viewer.open( '/test/data/testpattern.dzi' ); viewer.open( '/test/data/testpattern.dzi' );
} ); } );
// ----------
asyncTest( 'MouseTracker preventDefaultAction', function () {
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
tracker = viewer.innerTracker,
origClickHandler,
origDragHandler,
dragCount = 10,
originalZoom = 0,
originalBounds = null;
var onOpen = function ( event ) {
viewer.removeHandler( 'open', onOpen );
// Hook viewer events to set preventDefaultAction
origClickHandler = tracker.clickHandler;
tracker.clickHandler = function ( event ) {
event.preventDefaultAction = true;
return origClickHandler( event );
};
origDragHandler = tracker.dragHandler;
tracker.dragHandler = function ( event ) {
event.preventDefaultAction = true;
return origDragHandler( event );
};
originalZoom = viewer.viewport.getZoom();
originalBounds = viewer.viewport.getBounds();
var event = {
clientX:1,
clientY:1
};
$canvas.simulate( 'focus', event );
// Drag to pan
Util.simulateViewerClickWithDrag( {
viewer: viewer,
widthFactor: 0.25,
heightFactor: 0.25,
dragCount: dragCount,
dragDx: 1,
dragDy: 1
} );
// Click to zoom
Util.simulateViewerClickWithDrag( {
viewer: viewer,
widthFactor: 0.25,
heightFactor: 0.25,
dragCount: 0,
dragDx: 0,
dragDy: 0
} );
$canvas.simulate( 'blur', event );
var zoom = viewer.viewport.getZoom(),
bounds = viewer.viewport.getBounds();
equal( zoom, originalZoom, "Zoom prevented" );
ok( bounds.x == originalBounds.x && bounds.y == originalBounds.y, 'Pan prevented' );
viewer.close();
start();
};
viewer.addHandler( 'open', onOpen );
viewer.open( '/test/data/testpattern.dzi' );
} );
} )(); } )();

View File

@ -91,9 +91,14 @@
function checkZoom() { function checkZoom() {
var currentImageWidth = getCurrentImageWidth(); var currentImageWidth = getCurrentImageWidth();
var expected = currentImageWidth / imageWidth; var expectedImageZoom = currentImageWidth / imageWidth;
var actual = viewport.getImageZoomRatio(true); var expectedViewportZoom = viewport.getZoom(true);
equal(actual, expected); var actualImageZoom = viewport.viewportToImageZoom(
expectedViewportZoom);
equal(actualImageZoom, expectedImageZoom);
var actualViewportZoom = viewport.imageToViewportZoom(actualImageZoom);
equal(actualViewportZoom, expectedViewportZoom);
} }
checkZoom(); checkZoom();