several bug fixes and enhancements. legacy tile source issue discovered and corrected for images with width greater than height. adding basic support for sequenced tile sources including previous and next buttons. added mouse drag and scroll interactions for viewport navigator.

This commit is contained in:
thatcher 2012-04-03 03:08:27 -04:00
parent 94247b7225
commit 05f3c1d811
20 changed files with 1162 additions and 471 deletions

View File

@ -6,7 +6,7 @@
PROJECT: openseadragon
BUILD_MAJOR: 0
BUILD_MINOR: 9
BUILD_ID: 37
BUILD_ID: 40
BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}

BIN
images/next_grouphover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
images/next_hover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
images/next_pressed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
images/next_rest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
images/previous_hover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
images/previous_pressed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
images/previous_rest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because it is too large Load Diff

View File

@ -223,6 +223,18 @@ $.extend( $.Button.prototype, $.EventHandler.prototype, {
*/
notifyGroupExit: function() {
outTo( this, $.ButtonState.REST );
},
disable: function(){
this.notifyGroupExit();
this.element.disabled = true;
$.setElementOpacity( this.element, 0.2, true );
},
enable: function(){
this.element.disabled = false;
$.setElementOpacity( this.element, 1.0, true );
this.notifyGroupEnter();
}
});
@ -268,6 +280,11 @@ function stopFading( button ) {
};
function inTo( button, newState ) {
if( button.element.disabled ){
return;
}
if ( newState >= $.ButtonState.GROUP &&
button.currentState == $.ButtonState.REST ) {
stopFading( button );
@ -289,6 +306,11 @@ function inTo( button, newState ) {
function outTo( button, newState ) {
if( button.element.disabled ){
return;
}
if ( newState <= $.ButtonState.HOVER &&
button.currentState == $.ButtonState.DOWN ) {
button.imgDown.style.visibility = "hidden";

View File

@ -10,7 +10,8 @@ var TIMEOUT = 5000,
( BROWSER == $.BROWSERS.FIREFOX ) ||
( BROWSER == $.BROWSERS.OPERA ) ||
( BROWSER == $.BROWSERS.SAFARI && BROWSER_VERSION >= 4 ) ||
( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 )
( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 ) ||
( BROWSER == $.BROWSERS.IE && BROWSER_VERSION >= 9 )
) && ( !navigator.appVersion.match( 'Mobile' ) ),
USE_CANVAS = $.isFunction( document.createElement( "canvas" ).getContext ) &&

View File

@ -49,8 +49,8 @@ $.LegacyTileSource.prototype = {
var levelScale = NaN;
if ( level >= this.minLevel && level <= this.maxLevel ){
levelScale =
this.files[ level ].height /
this.files[ this.maxLevel ].height;
this.files[ level ].width /
this.files[ this.maxLevel ].width;
}
return levelScale;
},
@ -101,9 +101,9 @@ $.LegacyTileSource.prototype = {
py = ( y === 0 ) ? 0 : this.files[ level ].height,
sx = this.files[ level ].width,
sy = this.files[ level ].height,
scale = Math.max(
1.0 / dimensionsScaled.x,
1.0 / dimensionsScaled.y
scale = 1.0 / ( this.width >= this.height ?
dimensionsScaled.y :
dimensionsScaled.x
);
sx = Math.min( sx, dimensionsScaled.x - px );

View File

@ -466,7 +466,7 @@
function triggerOthers( tracker, handler, event ) {
var otherHash;
for ( otherHash in ACTIVE ) {
if ( trackers.hasOwnProperty( otherHash ) && tracker.hash != otherHash ) {
if ( ACTIVE.hasOwnProperty( otherHash ) && tracker.hash != otherHash ) {
handler( ACTIVE[ otherHash ], event );
}
}
@ -479,13 +479,16 @@
*/
function onFocus( tracker, event ){
//console.log( "focus %s", event );
var propagate;
if ( tracker.focusHandler ) {
try {
tracker.focusHandler(
propagate = tracker.focusHandler(
tracker,
event
);
$.cancelEvent( event );
if( propagate === false ){
$.cancelEvent( event );
}
} catch ( e ) {
$.console.error(
"%s while executing key handler: %s",
@ -504,13 +507,16 @@
*/
function onBlur( tracker, event ){
//console.log( "blur %s", event );
var propagate;
if ( tracker.blurHandler ) {
try {
tracker.blurHandler(
propagate = tracker.blurHandler(
tracker,
event
);
$.cancelEvent( event );
if( propagate === false ){
$.cancelEvent( event );
}
} catch ( e ) {
$.console.error(
"%s while executing key handler: %s",
@ -537,7 +543,7 @@
event.keyCode ? event.keyCode : event.charCode,
event.shiftKey
);
if( !propagate ){
if( propagate === false ){
$.cancelEvent( event );
}
} catch ( e ) {
@ -559,7 +565,8 @@
function onMouseOver( tracker, event ) {
var event = $.getEvent( event ),
delegate = THIS[ tracker.hash ];
delegate = THIS[ tracker.hash ],
propagate;
if ( $.Browser.vendor == $.BROWSERS.IE &&
delegate.capturing &&
@ -585,12 +592,15 @@
if ( tracker.enterHandler ) {
try {
tracker.enterHandler(
propagate = tracker.enterHandler(
tracker,
getMouseRelative( event, tracker.element ),
delegate.buttonDown,
IS_BUTTON_DOWN
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch ( e ) {
$.console.error(
"%s while executing enter handler: %s",
@ -609,7 +619,8 @@
*/
function onMouseOut( tracker, event ) {
var event = $.getEvent( event ),
delegate = THIS[ tracker.hash ];
delegate = THIS[ tracker.hash ],
propagate;
if ( $.Browser.vendor == $.BROWSERS.IE &&
delegate.capturing &&
@ -635,12 +646,16 @@
if ( tracker.exitHandler ) {
try {
tracker.exitHandler(
propagate = tracker.exitHandler(
tracker,
getMouseRelative( event, tracker.element ),
delegate.buttonDown,
IS_BUTTON_DOWN
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch ( e ) {
$.console.error(
"%s while executing exit handler: %s",
@ -659,7 +674,8 @@
*/
function onMouseDown( tracker, event ) {
var event = $.getEvent( event ),
delegate = THIS[ tracker.hash ];
delegate = THIS[ tracker.hash ],
propagate;
if ( event.button == 2 ) {
return;
@ -673,10 +689,13 @@
if ( tracker.pressHandler ) {
try {
tracker.pressHandler(
propagate = tracker.pressHandler(
tracker,
getMouseRelative( event, tracker.element )
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch (e) {
$.console.error(
"%s while executing press handler: %s",
@ -743,7 +762,8 @@
//were we inside the tracked element when we were pressed
insideElementPress = delegate.buttonDown,
//are we still inside the tracked element when we released
insideElementRelease = delegate.insideElement;
insideElementRelease = delegate.insideElement,
propagate;
if ( event.button == 2 ) {
return;
@ -753,12 +773,15 @@
if ( tracker.releaseHandler ) {
try {
tracker.releaseHandler(
propagate = tracker.releaseHandler(
tracker,
getMouseRelative( event, tracker.element ),
insideElementPress,
insideElementRelease
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch (e) {
$.console.error(
"%s while executing release handler: %s",
@ -867,7 +890,8 @@
* @inner
*/
function onMouseWheelSpin( tracker, event ) {
var nDelta = 0;
var nDelta = 0,
propagate;
if ( !event ) { // For IE, access the global (window) event object
event = window.event;
@ -888,12 +912,15 @@
if ( tracker.scrollHandler ) {
try {
tracker.scrollHandler(
propagate = tracker.scrollHandler(
tracker,
getMouseRelative( event, tracker.element ),
nDelta,
event.shiftKey
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch (e) {
$.console.error(
"%s while executing scroll handler: %s",
@ -902,8 +929,6 @@
e
);
}
$.cancelEvent( event );
}
};
@ -914,7 +939,8 @@
*/
function handleMouseClick( tracker, event ) {
var event = $.getEvent( event ),
delegate = THIS[ tracker.hash ];
delegate = THIS[ tracker.hash ],
propagate;
if ( event.button == 2 ) {
return;
@ -928,12 +954,15 @@
if ( tracker.clickHandler ) {
try {
tracker.clickHandler(
propagate = tracker.clickHandler(
tracker,
getMouseRelative( event, tracker.element ),
quick,
event.shiftKey
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch ( e ) {
$.console.error(
"%s while executing click handler: %s",
@ -954,18 +983,22 @@
var event = $.getEvent( event ),
delegate = THIS[ tracker.hash ],
point = getMouseAbsolute( event ),
delta = point.minus( delegate.lastPoint );
delta = point.minus( delegate.lastPoint ),
propagate;
delegate.lastPoint = point;
if ( tracker.dragHandler ) {
try {
tracker.dragHandler(
propagate = tracker.dragHandler(
tracker,
getMouseRelative( event, tracker.element ),
delta,
event.shiftKey
);
if( propagate === false ){
$.cancelEvent( event );
}
} catch (e) {
$.console.error(
"%s while executing drag handler: %s",
@ -975,7 +1008,6 @@
);
}
$.cancelEvent( event );
}
};

View File

@ -63,13 +63,31 @@ $.Navigator = function( options ){
style.cssFloat = 'left'; //Firefox
style.styleFloat = 'left'; //IE
style.zIndex = 999999999;
style.cursor = 'default';
}( this.displayRegion.style ));
this.element.innerTracker = new $.MouseTracker({
element: this.element,
scrollHandler: function(){
//dont scroll the page up and down if the user is scrolling
//in the navigator
return false;
}
}).setTracking( true );
this.displayRegion.innerTracker = new $.MouseTracker({
element: this.displayRegion,
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
clickHandler: $.delegate( this, onCanvasClick ),
dragHandler: $.delegate( this, onCanvasDrag ),
releaseHandler: $.delegate( this, onCanvasRelease ),
scrollHandler: $.delegate( this, onCanvasScroll ),
focusHandler: function(){
var point = $.getElementPosition( _this.viewer.element );
window.scrollTo( 0, point.y );
_this.viewer.setControlsEnabled( true );
(function( style ){
style.border = '2px solid #437AB2';
@ -89,12 +107,15 @@ $.Navigator = function( options ){
switch( keyCode ){
case 61://=|+
_this.viewer.viewport.zoomBy(1.1);
_this.viewer.viewport.applyConstraints();
return false;
case 45://-|_
_this.viewer.viewport.zoomBy(0.9);
_this.viewer.viewport.applyConstraints();
return false;
case 48://0|)
_this.viewer.viewport.goHome();
_this.viewer.viewport.applyConstraints();
return false;
case 119://w
case 87://W
@ -102,6 +123,7 @@ $.Navigator = function( options ){
shiftKey ?
_this.viewer.viewport.zoomBy(1.1):
_this.viewer.viewport.panBy(new $.Point(0, -0.05));
_this.viewer.viewport.applyConstraints();
return false;
case 115://s
case 83://S
@ -109,14 +131,17 @@ $.Navigator = function( options ){
shiftKey ?
_this.viewer.viewport.zoomBy(0.9):
_this.viewer.viewport.panBy(new $.Point(0, 0.05));
_this.viewer.viewport.applyConstraints();
return false;
case 97://a
case 37://left arrow
_this.viewer.viewport.panBy(new $.Point(-0.05, 0));
_this.viewer.viewport.applyConstraints();
return false;
case 100://d
case 39://right arrow
_this.viewer.viewport.panBy(new $.Point(0.05, 0));
_this.viewer.viewport.applyConstraints();
return false;
default:
//console.log( 'navigator keycode %s', keyCode );
@ -157,23 +182,73 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, {
update: function( viewport ){
var bounds = viewport.getBounds( true ),
topleft = this.viewport.pixelFromPoint( bounds.getTopLeft() )
var bounds,
topleft,
bottomright;
if( viewport && this.viewport ){
bounds = viewport.getBounds( true );
topleft = this.viewport.pixelFromPoint( bounds.getTopLeft() );
bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight() );
//update style for navigator-box
(function(style){
//update style for navigator-box
(function(style){
style.top = topleft.y + 'px';
style.left = topleft.x + 'px';
style.width = ( Math.abs( topleft.x - bottomright.x ) - 3 ) + 'px';
style.height = ( Math.abs( topleft.y - bottomright.y ) - 3 ) + 'px';
style.top = topleft.y + 'px';
style.left = topleft.x + 'px';
style.width = ( Math.abs( topleft.x - bottomright.x ) - 3 ) + 'px';
style.height = ( Math.abs( topleft.y - bottomright.y ) - 3 ) + 'px';
}( this.displayRegion.style ));
}( this.displayRegion.style ));
}
}
});
function onCanvasClick( tracker, position, quick, shift ) {
this.displayRegion.focus();
};
function onCanvasDrag( tracker, position, delta, shift ) {
if ( this.viewer.viewport ) {
if( !this.panHorizontal ){
delta.x = 0;
}
if( !this.panVertical ){
delta.y = 0;
}
this.viewer.viewport.panBy(
this.viewport.deltaPointsFromPixels(
delta
)
);
}
};
function onCanvasRelease( tracker, position, insideElementPress, insideElementRelease ) {
if ( insideElementPress && this.viewer.viewport ) {
this.viewer.viewport.applyConstraints();
}
};
function onCanvasScroll( tracker, position, scroll, shift ) {
var factor;
if ( this.viewer.viewport ) {
factor = Math.pow( this.zoomPerScroll, scroll );
this.viewer.viewport.zoomBy(
factor,
//this.viewport.pointFromPixel( position, true )
this.viewport.getCenter()
);
this.viewer.viewport.applyConstraints();
}
//cancels event
return false;
};
}( OpenSeadragon ));

View File

@ -188,6 +188,12 @@
* interactions include draging the image in a plane, and zooming in toward
* and away from the image.
*
* @param {Boolean} [options.preserveViewport=false]
* If the viewer has been configured with a sequence of tile sources, then
* normally navigating to through each image resets the viewport to 'home'
* position. If preserveViewport is set to true, then the viewport position
* is preserved when navigating between images in the sequence.
*
* @param {String} [options.prefixUrl='']
* Appends the prefixUrl to navImages paths, which is very useful
* since the default paths are rarely useful for production
@ -428,43 +434,50 @@ OpenSeadragon = window.OpenSeadragon || function( options ){
* @static
*/
DEFAULT_SETTINGS: {
xmlPath: null,
tileSources: null,
debugMode: true,
animationTime: 1.5,
blendTime: 0.5,
alwaysBlend: false,
autoHideControls: true,
immediateRender: false,
wrapHorizontal: false,
wrapVertical: false,
minZoomImageRatio: 0.8,
maxZoomPixelRatio: 2,
visibilityRatio: 0.5,
springStiffness: 5.0,
imageLoaderLimit: 0,
clickTimeThreshold: 200,
clickDistThreshold: 5,
zoomPerClick: 2.0,
zoomPerScroll: 1.2,
zoomPerSecond: 2.0,
showNavigationControl: true,
showNavigator: false,
navigatorElement: null,
navigatorHeight: null,
navigatorWidth: null,
navigatorPosition: null,
navigatorSizeRatio: 0.25,
//DATA SOURCE DETAILS
xmlPath: null,
tileSources: null,
tileHost: null,
//INTERFACE FEATURES
debugMode: true,
animationTime: 1.5,
blendTime: 0.5,
alwaysBlend: false,
autoHideControls: true,
immediateRender: false,
wrapHorizontal: false,
wrapVertical: false,
panHorizontal: true,
panVertical: true,
visibilityRatio: 0.5,
springStiffness: 5.0,
clickTimeThreshold: 200,
clickDistThreshold: 5,
zoomPerClick: 2.0,
zoomPerScroll: 1.2,
zoomPerSecond: 2.0,
showNavigationControl: true,
controlsFadeDelay: 2000,
controlsFadeLength: 1500,
mouseNavEnabled: true,
showNavigator: false,
navigatorElement: null,
navigatorHeight: null,
navigatorWidth: null,
navigatorPosition: null,
navigatorSizeRatio: 0.25,
preserveViewport: false,
//These two were referenced but never defined
controlsFadeDelay: 2000,
controlsFadeLength: 1500,
//PERFORMANCE SETTINGS
minPixelRatio: 0.5,
imageLoaderLimit: 0,
maxImageCacheCount: 200,
minZoomImageRatio: 0.9,
maxZoomPixelRatio: 2,
maxImageCacheCount: 200,
minPixelRatio: 0.5,
mouseNavEnabled: true,
prefixUrl: null,
//INTERFACE RESOURCE SETTINGS
prefixUrl: null,
navImages: {
zoomIn: {
REST: '/images/zoomin_rest.png',
@ -489,6 +502,18 @@ OpenSeadragon = window.OpenSeadragon || function( options ){
GROUP: '/images/fullpage_grouphover.png',
HOVER: '/images/fullpage_hover.png',
DOWN: '/images/fullpage_pressed.png'
},
previous: {
REST: '/images/previous_rest.png',
GROUP: '/images/previous_grouphover.png',
HOVER: '/images/previous_hover.png',
DOWN: '/images/previous_pressed.png'
},
next: {
REST: '/images/next_rest.png',
GROUP: '/images/next_grouphover.png',
HOVER: '/images/next_hover.png',
DOWN: '/images/next_pressed.png'
}
}
},
@ -1192,7 +1217,7 @@ OpenSeadragon = window.OpenSeadragon || function( options ){
* @param {String} xmlString
* @param {Function} callback
*/
createFromDZI: function( dzi, callback ) {
createFromDZI: function( dzi, callback, tileHost ) {
var async = typeof ( callback ) == "function",
xmlUrl = dzi.substring(0,1) != '<' ? dzi : null,
xmlString = xmlUrl ? null : dzi,
@ -1203,7 +1228,12 @@ OpenSeadragon = window.OpenSeadragon || function( options ){
tilesUrl;
if( xmlUrl ){
if( tileHost ){
tilesUrl = tileHost + "/_files/";
} else if( xmlUrl ) {
urlParts = xmlUrl.split( '/' );
filename = urlParts[ urlParts.length - 1 ];
lastDot = filename.lastIndexOf( '.' );
@ -1213,6 +1243,7 @@ OpenSeadragon = window.OpenSeadragon || function( options ){
}
tilesUrl = urlParts.join( '/' ) + "_files/";
}
function finish( func, obj ) {

View File

@ -6,28 +6,30 @@
// pythons gettext might be a reasonable approach.
var I18N = {
Errors: {
Failure: "Sorry, but Seadragon Ajax can't run on your browser!\n" +
"Please try using IE 7 or Firefox 3.\n",
Dzc: "Sorry, we don't support Deep Zoom Collections!",
Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Empty: "You asked us to open nothing, so we did just that.",
ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.",
Security: "It looks like a security restriction stopped us from " +
"loading this Deep Zoom Image.",
Status: "This space unintentionally left blank ({0} {1}).",
Unknown: "Whoops, something inexplicably went wrong. Sorry!"
Failure: "Sorry, but Seadragon Ajax can't run on your browser!\n" +
"Please try using IE 7 or Firefox 3.\n",
Dzc: "Sorry, we don't support Deep Zoom Collections!",
Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Empty: "You asked us to open nothing, so we did just that.",
ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.",
Security: "It looks like a security restriction stopped us from " +
"loading this Deep Zoom Image.",
Status: "This space unintentionally left blank ({0} {1}).",
Unknown: "Whoops, something inexplicably went wrong. Sorry!"
},
Messages: {
Loading: "Loading..."
Loading: "Loading..."
},
Tooltips: {
FullPage: "Toggle full page",
Home: "Go home",
ZoomIn: "Zoom in",
ZoomOut: "Zoom out"
FullPage: "Toggle full page",
Home: "Go home",
ZoomIn: "Zoom in",
ZoomOut: "Zoom out",
NextPage: "Next page",
PreviousPage: "Previous page"
}
};
@ -41,12 +43,13 @@ $.extend( $, {
getString: function( prop ) {
var props = prop.split('.'),
string = I18N,
string = null,
args = arguments,
i;
for ( i = 0; i < props.length; i++ ) {
string = string[ props[ i ] ] || {}; // in case not a subproperty
// in case not a subproperty
string = I18N[ props[ i ] ] || {};
}
if ( typeof( string ) != "string" ) {

View File

@ -45,7 +45,7 @@ $.Tile = function(level, x, y, bounds, exists, url) {
this.loading = false;
this.element = null;
this.image = null;
this.image = null;
this.style = null;
this.position = null;
@ -81,7 +81,7 @@ $.Tile.prototype = {
var position = this.position.apply( Math.floor ),
size = this.size.apply( Math.ceil );
if ( !this.loaded ) {
if ( !this.loaded || !this.image ) {
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
this.toString()
@ -90,9 +90,9 @@ $.Tile.prototype = {
}
if ( !this.element ) {
this.element = $.makeNeutralElement("img");
this.element.src = this.url;
this.style = this.element.style;
this.element = $.makeNeutralElement("img");
this.element.src = this.url;
this.style = this.element.style;
this.style.position = "absolute";
this.style.msInterpolationMode = "nearest-neighbor";
@ -122,14 +122,13 @@ $.Tile.prototype = {
var position = this.position,
size = this.size;
if ( !this.loaded ) {
if ( !this.loaded || !this.image ) {
$.console.warn(
"Attempting to draw tile %s when it's not yet loaded.",
this.toString()
);
return;
}
context.globalAlpha = this.opacity;
context.drawImage( this.image, position.x, position.y, size.x, size.y );
},

View File

@ -59,16 +59,25 @@ $.Viewer = function( options ) {
delete options.config;
}
//Public properties
//Allow the options object to override global defaults
$.extend( true, this, {
id: options.id,
hash: options.id,
overlays: [],
overlayControls: [],
//internal state and dom identifiers
id: options.id,
hash: options.id,
//dom nodes
element: null,
canvas: null,
container: null,
//TODO: not sure how to best describe these
overlays: [],
overlayControls:[],
//private state properties
previousBody: [],
previousBody: [],
//This was originally initialized in the constructor and so could never
//have anything in it. now it can because we allow it to be specified
@ -84,13 +93,76 @@ $.Viewer = function( options ) {
drawer: null,
viewport: null,
navigator: null,
//UI image resources
//TODO: rename navImages to uiImages
navImages: null,
//interface button controls
buttons: null,
//TODO: this is defunct so safely remove it
profiler: null
}, $.DEFAULT_SETTINGS, options );
//Private state properties
THIS[ this.hash ] = {
"fsBoundsDelta": new $.Point( 1, 1 ),
"prevContainerSize": null,
"lastOpenStartTime": 0,
"lastOpenEndTime": 0,
"animating": false,
"forceRedraw": false,
"mouseInside": false,
"group": null,
// whether we should be continuously zooming
"zooming": false,
// how much we should be continuously zooming by
"zoomFactor": null,
"lastZoomTime": null,
// did we decide this viewer has a sequence of tile sources
"sequenced": false,
"sequence": 0
};
//Inherit some behaviors and properties
$.EventHandler.call( this );
$.ControlDock.call( this, options );
//Deal with tile sources
var initialTileSource,
customTileSource;
if ( this.xmlPath ){
//Deprecated option. Now it is preferred to use the tileSources option
this.tileSources = [ this.xmlPath ];
}
if ( this.tileSources ){
//tileSources is a complex option...
//It can be a string, object, function, or an array of any of these.
// - A String implies a DZI
// - An Srray of Objects implies a simple image
// - A Function implies a custom tile source callback
// - An Array that is not an Array of simple Objects implies a sequence
// of tile sources which can be any of the above
if( $.isArray( this.tileSources ) ){
if( $.isPlainObject( this.tileSources[ 0 ] ) ){
//This is a non-sequenced legacy tile source
initialTileSource = this.tileSources;
} else {
//Sequenced tile source
initialTileSource = this.tileSources[ 0 ];
THIS[ this.hash ].sequenced = true;
}
} else {
initialTileSource = this.tileSources;
}
this.openTileSource( initialTileSource );
}
this.element = this.element || document.getElementById( this.id );
this.canvas = $.makeNeutralElement( "div" );
@ -112,7 +184,7 @@ $.Viewer = function( options ) {
container.textAlign = "left"; // needed to protect against
}( this.container.style ));
this.container.insertBefore( this.canvas, this.container.firstChild);
this.container.insertBefore( this.canvas, this.container.firstChild );
this.element.appendChild( this.container );
//Used for toggling between fullscreen and default container size
@ -123,16 +195,6 @@ $.Viewer = function( options ) {
this.bodyOverflow = document.body.style.overflow;
this.docOverflow = document.documentElement.style.overflow;
THIS[ this.hash ] = {
"fsBoundsDelta": new $.Point( 1, 1 ),
"prevContainerSize": null,
"lastOpenStartTime": 0,
"lastOpenEndTime": 0,
"animating": false,
"forceRedraw": false,
"mouseInside": false
};
this.innerTracker = new $.MouseTracker({
element: this.canvas,
clickTimeThreshold: this.clickTimeThreshold,
@ -153,16 +215,6 @@ $.Viewer = function( options ) {
}).setTracking( this.mouseNavEnabled ? true : false ); // always tracking
//private state properties
$.extend( THIS[ this.hash ], {
"group": null,
// whether we should be continuously zooming
"zooming": false,
// how much we should be continuously zooming by
"zoomFactor": null,
"lastZoomTime": null
});
//////////////////////////////////////////////////////////////////////////
// Navigation Controls
//////////////////////////////////////////////////////////////////////////
@ -175,16 +227,15 @@ $.Viewer = function( options ) {
onFullPageHandler = $.delegate( this, onFullPage ),
onFocusHandler = $.delegate( this, onFocus ),
onBlurHandler = $.delegate( this, onBlur ),
navImages = this.navImages;
onNextHandler = $.delegate( this, onNext ),
onPreviousHandler = $.delegate( this, onPrevious ),
navImages = this.navImages,
buttons = [];
this.zoomInButton = null;
this.zoomOutButton = null;
this.goHomeButton = null;
this.fullPageButton = null;
if( this.showNavigationControl ){
this.zoomInButton = new $.Button({
buttons.push( this.zoomInButton = new $.Button({
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
tooltip: $.getString( "Tooltips.ZoomIn" ),
@ -199,9 +250,9 @@ $.Viewer = function( options ) {
onExit: endZoomingHandler,
onFocus: onFocusHandler,
onBlur: onBlurHandler
});
}));
this.zoomOutButton = new $.Button({
buttons.push( this.zoomOutButton = new $.Button({
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
tooltip: $.getString( "Tooltips.ZoomOut" ),
@ -216,9 +267,9 @@ $.Viewer = function( options ) {
onExit: endZoomingHandler,
onFocus: onFocusHandler,
onBlur: onBlurHandler
});
}));
this.goHomeButton = new $.Button({
buttons.push( this.homeButton = new $.Button({
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
tooltip: $.getString( "Tooltips.Home" ),
@ -229,9 +280,9 @@ $.Viewer = function( options ) {
onRelease: onHomeHandler,
onFocus: onFocusHandler,
onBlur: onBlurHandler
});
}));
this.fullPageButton = new $.Button({
buttons.push( this.fullPageButton = new $.Button({
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
tooltip: $.getString( "Tooltips.FullPage" ),
@ -242,17 +293,44 @@ $.Viewer = function( options ) {
onRelease: onFullPageHandler,
onFocus: onFocusHandler,
onBlur: onBlurHandler
});
}));
this.buttons = new $.ButtonGroup({
if( THIS[ this.hash ].sequenced ){
buttons.push( this.previousButton = new $.Button({
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
tooltip: $.getString( "Tooltips.PreviousPage" ),
srcRest: resolveUrl( this.prefixUrl, navImages.previous.REST ),
srcGroup: resolveUrl( this.prefixUrl, navImages.previous.GROUP ),
srcHover: resolveUrl( this.prefixUrl, navImages.previous.HOVER ),
srcDown: resolveUrl( this.prefixUrl, navImages.previous.DOWN ),
onRelease: onPreviousHandler,
onFocus: onFocusHandler,
onBlur: onBlurHandler
}));
buttons.push( this.nextButton = new $.Button({
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
tooltip: $.getString( "Tooltips.NextPage" ),
srcRest: resolveUrl( this.prefixUrl, navImages.next.REST ),
srcGroup: resolveUrl( this.prefixUrl, navImages.next.GROUP ),
srcHover: resolveUrl( this.prefixUrl, navImages.next.HOVER ),
srcDown: resolveUrl( this.prefixUrl, navImages.next.DOWN ),
onRelease: onNextHandler,
onFocus: onFocusHandler,
onBlur: onBlurHandler
}));
this.previousButton.disable();
}
this.buttons = new $.ButtonGroup({
buttons: buttons,
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
buttons: [
this.zoomInButton,
this.zoomOutButton,
this.goHomeButton,
this.fullPageButton
]
clickDistThreshold: this.clickDistThreshold
});
this.navControl = this.buttons.element;
@ -274,73 +352,10 @@ $.Viewer = function( options ) {
);
}
//Instantiate a navigator if configured
if ( this.showNavigator ){
this.navigator = new $.Navigator({
id: this.navigatorElement,
position: this.navigatorPosition,
sizeRatio: this.navigatorSizeRatio,
height: this.navigatorHeight,
width: this.navigatorWidth,
tileSources: this.tileSources,
prefixUrl: this.prefixUrl,
overlays: this.overlays,
viewer: this
});
}
window.setTimeout( function(){
beginControlsAutoHide( _this );
}, 1 ); // initial fade out
var initialTileSource,
customTileSource;
if ( this.xmlPath ){
//Deprecated option. Now it is preferred to use the tileSources option
this.tileSources = [ this.xmlPath ];
}
if ( this.tileSources ){
//tileSource is a complex option...
//It can be a string, object, function, or an array of any of these.
//A string implies a DZI
//An object implies a simple image
//A function implies a custom tile source callback
//An array implies a sequence of tile sources which can be any of the
//above
if( $.isArray( this.tileSources ) ){
if( $.isPlainObject( this.tileSources[ 0 ] ) ){
//This is a non-sequenced legacy tile source
initialTileSource = this.tileSources;
} else {
//Sequenced tile source
initialTileSource = this.tileSources[ 0 ];
}
} else {
initialTileSource = this.tileSources
}
if ( $.type( initialTileSource ) == 'string') {
//Standard DZI format
this.openDzi( initialTileSource );
} else if ( $.isArray( initialTileSource ) ){
//Legacy image pyramid
this.open( new $.LegacyTileSource( initialTileSource ) );
} else if ( $.isPlainObject( initialTileSource ) && $.isFunction( initialTileSource.getTileUrl ) ){
//Custom tile source
customTileSource = new $.TileSource(
initialTileSource.width,
initialTileSource.height,
initialTileSource.tileSize,
initialTileSource.tileOverlap,
initialTileSource.minLevel,
initialTileSource.maxLevel
);
customTileSource.getTileUrl = initialTileSource.getTileUrl;
this.open( customTileSource );
}
}
};
$.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, {
@ -370,7 +385,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
dzi,
function( source ){
_this.open( source );
}
},
this.tileHost
);
return this;
},
@ -381,10 +397,34 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
* @return {OpenSeadragon.Viewer} Chainable.
*/
openTileSource: function ( tileSource ) {
var _this = this;
window.setTimeout( function () {
_this.open( tileSource );
}, 1 );
var _this = this,
customTileSource;
setTimeout(function(){
if ( $.type( tileSource ) == 'string') {
//Standard DZI format
_this.openDzi( tileSource );
} else if ( $.isArray( tileSource ) ){
//Legacy image pyramid
_this.open( new $.LegacyTileSource( tileSource ) );
} else if ( $.isPlainObject( tileSource ) && $.isFunction( tileSource.getTileUrl ) ){
//Custom tile source
customTileSource = new $.TileSource(
tileSource.width,
tileSource.height,
tileSource.tileSize,
tileSource.tileOverlap,
tileSource.minLevel,
tileSource.maxLevel
);
customTileSource.getTileUrl = tileSource.getTileUrl;
_this.open( customTileSource );
} else {
//can assume it's already a tile source implementation
_this.open( tileSource );
}
}, 1);
return this;
},
@ -399,7 +439,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
i;
if ( this.source ) {
this.close();
this.close( );
}
// to ignore earlier opens
@ -419,7 +459,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
this.source = source;
}
this.viewport = new $.Viewport({
this.viewport = this.viewport ? this.viewport : new $.Viewport({
containerSize: THIS[ this.hash ].prevContainerSize,
contentSize: this.source.dimensions,
springStiffness: this.springStiffness,
@ -430,6 +470,9 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
wrapHorizontal: this.wrapHorizontal,
wrapVertical: this.wrapVertical
});
if( this.preserveVewport ){
this.viewport.resetContentSize( this.source.dimensions );
}
this.drawer = new $.Drawer({
source: this.source,
@ -447,6 +490,22 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
minPixelRatio: this.minPixelRatio
});
//Instantiate a navigator if configured
if ( this.showNavigator && ! this.navigator ){
this.navigator = new $.Navigator({
id: this.navigatorElement,
position: this.navigatorPosition,
sizeRatio: this.navigatorSizeRatio,
height: this.navigatorHeight,
width: this.navigatorWidth,
tileSources: this.tileSources,
tileHost: this.tileHost,
prefixUrl: this.prefixUrl,
overlays: this.overlays,
viewer: this
});
}
//this.profiler = new $.Profiler();
THIS[ this.hash ].animating = false;
@ -487,6 +546,11 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
}
VIEWERS[ this.hash ] = this;
this.raiseEvent( "open" );
if( this.navigator ){
this.navigator.open( source );
}
return this;
},
@ -495,10 +559,10 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
* @name OpenSeadragon.Viewer.prototype.close
* @return {OpenSeadragon.Viewer} Chainable.
*/
close: function () {
close: function ( ) {
this.source = null;
this.viewport = null;
this.drawer = null;
this.viewport = this.preserveViewport ? this.viewport : null;
//this.profiler = null;
this.canvas.innerHTML = "";
@ -722,6 +786,9 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
viewer = VIEWERS[ hash ];
if( viewer !== this && viewer != this.navigator ){
viewer.open( viewer.source );
if( viewer.navigator ){
viewer.navigator.open( viewer.source );
}
}
}
}
@ -757,10 +824,11 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
});
///////////////////////////////////////////////////////////////////////////////
// Schedulers provide the general engine for animation
///////////////////////////////////////////////////////////////////////////////
function scheduleUpdate( viewer, updateFunc, prevUpdateTime ){
var currentTime,
targetTime,
@ -783,6 +851,7 @@ function scheduleUpdate( viewer, updateFunc, prevUpdateTime ){
}, deltaTime );
};
//provides a sequence in the fade animation
function scheduleControlsFade( viewer ) {
window.setTimeout( function(){
@ -790,6 +859,7 @@ function scheduleControlsFade( viewer ) {
}, 20);
};
//initiates an animation to hide the controls
function beginControlsAutoHide( viewer ) {
if ( !viewer.autoHideControls ) {
@ -831,6 +901,7 @@ function updateControlsFade( viewer ) {
}
};
//stop the fade animation on the controls and show them
function abortControlsAutoHide( viewer ) {
var i;
@ -841,6 +912,7 @@ function abortControlsAutoHide( viewer ) {
};
///////////////////////////////////////////////////////////////////////////////
// Default view event handlers.
///////////////////////////////////////////////////////////////////////////////
@ -869,6 +941,12 @@ function onCanvasClick( tracker, position, quick, shift ) {
function onCanvasDrag( tracker, position, delta, shift ) {
if ( this.viewport ) {
if( !this.panHorizontal ){
delta.x = 0;
}
if( !this.panVertical ){
delta.y = 0;
}
this.viewport.panBy(
this.viewport.deltaPointsFromPixels(
delta.negate()
@ -893,6 +971,8 @@ function onCanvasScroll( tracker, position, scroll, shift ) {
);
this.viewport.applyConstraints();
}
//cancels event
return false;
};
function onContainerExit( tracker, position, buttonDownElement, buttonDownAny ) {
@ -988,36 +1068,42 @@ function updateOnce( viewer ) {
//viewer.profiler.endUpdate();
};
///////////////////////////////////////////////////////////////////////////////
// Navigation Controls
///////////////////////////////////////////////////////////////////////////////
function resolveUrl( prefix, url ) {
return prefix ? prefix + url : url;
};
function beginZoomingIn() {
THIS[ this.hash ].lastZoomTime = +new Date();
THIS[ this.hash ].zoomFactor = this.zoomPerSecond;
THIS[ this.hash ].zooming = true;
scheduleZoom( this );
}
};
function beginZoomingOut() {
THIS[ this.hash ].lastZoomTime = +new Date();
THIS[ this.hash ].zoomFactor = 1.0 / this.zoomPerSecond;
THIS[ this.hash ].zooming = true;
scheduleZoom( this );
}
};
function endZooming() {
THIS[ this.hash ].zooming = false;
}
};
function scheduleZoom( viewer ) {
window.setTimeout( $.delegate( viewer, doZoom ), 10 );
}
};
function doZoom() {
var currentTime,
@ -1036,6 +1122,7 @@ function doZoom() {
}
};
function doSingleZoomIn() {
if ( this.viewport ) {
THIS[ this.hash ].zooming = false;
@ -1046,6 +1133,7 @@ function doSingleZoomIn() {
}
};
function doSingleZoomOut() {
if ( this.viewport ) {
THIS[ this.hash ].zooming = false;
@ -1056,17 +1144,20 @@ function doSingleZoomOut() {
}
};
function lightUp() {
this.buttons.emulateEnter();
this.buttons.emulateExit();
};
function onHome() {
if ( this.viewport ) {
this.viewport.goHome();
}
};
function onFullPage() {
this.setFullPage( !this.isFullPage() );
// correct for no mouseout event on change
@ -1077,4 +1168,47 @@ function onFullPage() {
}
};
function onPrevious(){
var previous = THIS[ this.hash ].sequence - 1,
preserveVewport = true;
if( previous >= 0 ){
THIS[ this.hash ].sequence = previous;
if( 0 === previous ){
//Disable previous button
this.previousButton.disable();
}
if( this.tileSources.length > 0 ){
//Enable next button
this.nextButton.enable();
}
this.openTileSource( this.tileSources[ previous ] );
}
};
function onNext(){
var next = THIS[ this.hash ].sequence + 1,
preserveVewport = true;
if( this.tileSources.length > next ){
THIS[ this.hash ].sequence = next;
if( ( this.tileSources.length - 1 ) === next ){
//Disable next button
this.nextButton.disable();
}
if( next > 0 ){
//Enable previous button
this.previousButton.enable();
}
this.openTileSource( this.tileSources[ next ] );
}
};
}( OpenSeadragon ));

View File

@ -46,9 +46,6 @@ $.Viewport = function( options ) {
}, options );
this.contentAspect = this.contentSize.x / this.contentSize.y;
this.contentHeight = this.contentSize.y / this.contentSize.x;
this.centerSpringX = new $.Spring({
initial: 0,
springStiffness: this.springStiffness,
@ -64,19 +61,39 @@ $.Viewport = function( options ) {
springStiffness: this.springStiffness,
animationTime: this.animationTime
});
this.homeBounds = new $.Rect( 0, 0, 1, this.contentHeight );
this.resetContentSize( this.contentSize );
this.goHome( true );
//this.fitHorizontally( true );
this.update();
};
$.Viewport.prototype = {
resetContentSize: function( contentSize ){
this.contentSize = contentSize;
this.contentAspectX = this.contentSize.x / this.contentSize.y;
this.contentAspectY = this.contentSize.y / this.contentSize.x;
this.homeBounds = new $.Rect(
0,
0,
1,
this.contentAspectY
);
this.fitWidthBounds = new $.Rect( 0, 0, 1, this.contentAspectX );
this.fitHeightBounds = new $.Rect( 0, 0, 1, this.contentAspectY );
},
/**
* @function
*/
getHomeZoom: function() {
var aspectFactor = this.contentAspect / this.getAspectRatio();
var aspectFactor = Math.min(
this.contentAspectX,
this.contentAspectY
) / this.getAspectRatio();
return ( aspectFactor >= 1 ) ?
1 :
aspectFactor;
@ -229,7 +246,7 @@ $.Viewport.prototype = {
left = bounds.x + bounds.width;
right = 1 - bounds.x;
top = bounds.y + bounds.height;
bottom = this.contentHeight - bounds.y;
bottom = this.contentAspectY - bounds.y;
if ( this.wrapHorizontal ) {
//do nothing
@ -311,15 +328,22 @@ $.Viewport.prototype = {
this.containerSize.x / newBounds.width
);
this.zoomTo( newZoom, referencePoint, immediately );
},
/**
* @function
* @param {Boolean} immediately
*/
goHome: function( immediately ) {
return this.fitVertically( immediately );
},
/**
* @function
* @param {Boolean} immediately
*/
goHome: function( immediately ) {
fitVertically: function( immediately ) {
var center = this.getCenter();
if ( this.wrapHorizontal ) {
@ -330,8 +354,8 @@ $.Viewport.prototype = {
if ( this.wrapVertical ) {
center.y = (
this.contentHeight + ( center.y % this.contentHeight )
) % this.contentHeight;
this.contentAspectY + ( center.y % this.contentAspectY )
) % this.contentAspectY;
this.centerSpringY.resetTo( center.y );
this.centerSpringY.update();
}
@ -339,6 +363,31 @@ $.Viewport.prototype = {
this.fitBounds( this.homeBounds, immediately );
},
/**
* @function
* @param {Boolean} immediately
*/
fitHorizontally: function( immediately ) {
var center = this.getCenter();
if ( this.wrapHorizontal ) {
center.x = (
this.contentAspectX + ( center.x % this.contentAspectX )
) % this.contentAspectX;
this.centerSpringX.resetTo( center.x );
this.centerSpringX.update();
}
if ( this.wrapVertical ) {
center.y = ( 1 + ( center.y % 1 ) ) % 1;
this.centerSpringY.resetTo( center.y );
this.centerSpringY.update();
}
this.fitBounds( this.fitWidthBounds, immediately );
},
/**
* @function
* @param {OpenSeadragon.Point} delta