Merge branch 'master' into pr/2258

This commit is contained in:
Mark Salsbery 2023-07-31 15:25:34 -07:00
commit 98fa5ff5c6
54 changed files with 2292 additions and 6974 deletions

1
.browserslistrc Normal file
View File

@ -0,0 +1 @@
defaults

View File

@ -1,14 +1,16 @@
{ {
"root": true, "root": true,
"plugins": ["compat"],
"extends": [ "extends": [
"eslint:recommended" "eslint:recommended",
"plugin:compat/recommended"
], ],
"env": { "env": {
"es6": false, "es6": true,
"browser": true "browser": true
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 5, "ecmaVersion": 6,
"sourceType": "script", "sourceType": "script",
"ecmaFeatures": { "ecmaFeatures": {
"globalReturn": false, "globalReturn": false,

View File

@ -1,6 +1,10 @@
# Specify dist until Travis CI default Runner OS updates to one
# with glibc version required by newer Node versions
# See: https://github.com/nodejs/node/issues/42351#issuecomment-1068424442
dist: jammy
language: node_js language: node_js
sudo: false sudo: false
node_js: node_js:
- "16.14.2" - lts/*
before_install: before_install:
- npm install -g grunt-cli - npm install -g grunt-cli

View File

@ -161,8 +161,11 @@ module.exports = function(grunt) {
normal: { normal: {
options: { options: {
urls: [ "http://localhost:8000/test/test.html" ], urls: [ "http://localhost:8000/test/test.html" ],
timeout: 10000 timeout: 10000,
} puppeteer: {
headless: 'new'
}
},
}, },
coverage: { coverage: {
options: { options: {

View File

@ -1,5 +1,5 @@
Copyright (C) 2009 CodePlex Foundation Copyright (C) 2009 CodePlex Foundation
Copyright (C) 2010-2022 OpenSeadragon contributors Copyright (C) 2010-2023 OpenSeadragon contributors
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:

View File

@ -1,9 +1,42 @@
OPENSEADRAGON CHANGELOG OPENSEADRAGON CHANGELOG
======================= =======================
3.2.0: (in progress...) 5.0.0: (in progress...)
* NEW BEHAVIOR: Setting the viewport rotation now animates by default (pass false for the new immediately parameter to disable) (#2136 @jonasengelmann) * BREAKING CHANGE: Dropped support for IE11 (#2300, #2361 @AndrewADev)
* DEPRECATION: The OpenSeadragon.createCallback function is no longer recommended (#2367 @akansjain)
* Fixed: Sometimes if the viewport was flipped and the user zoomed in far enough, it would flip back (#2364 @SebDelile)
* Test improvements (#2382 @AndrewADev)
4.1.0:
* NEW BEHAVIOR: When `navigatorRotate` is false, while the navigator image doesn't rotate, the red outline now does (#2356 @lcl45)
* The viewer no longer emits `canvas-key` events for both keydown and keypress events; canvas-key is now just for keydown, and the new `canvas-key-press` is for keypress (#2270 @hrghauri)
* You can now specify a priority when calling addHandler, to control when your event handler gets called relative to others (#2273 @Aiosa)
* Added tileRetryMax and tileRetryDelay options, so the viewer can retry loading failed tiles (#2238 @Ughuuu @paaddyy, #2334 @Ughuuu @Titan21)
* All of the viewers keyboard handling is now in response to keydown events (it used to be split between keydown and keypress) (#2291 @MohitBansal321)
* Added `canvas-focus` and `canvas-blur` events to Viewer (#2301 @MohitBansal321)
* You can now more easily add custom buttons to the viewer (#2306 @MohitBansal321)
* The fitBounds function now takes zoom constraints into account (#2293 @pearcetm)
* The viewer now has an `after-resize` event what happens after the viewport bounds have been updated, to complement the `resize` event which happens before (#2317 @pearcetm)
* IIIFTileSource now uses resolution level dimensions provided in the info.json "sizes" field for more accurate tile requests (#2337 @ruven)
* Added setAjaxHeaders method to Viewer and TiledImage (#2346 @uschmidt83)
* Improved documentation (#2297 @KevinBritten)
* Fixed: The `tile-loaded` event's completionCallback could be called more than once in some circumstances (#2282 @Aiosa, @pearcetm)
* Fixed: Navigator display rectangle was off if the page had `box-sizing: border-box` (#2276 @ambujsahu81)
* Fixed: Code that required identifying functions would fail for async functions (#2273 @Aiosa)
* Fixed: Reference strip click detection was not accurate for long reference strips (#2280 @damonsson)
* Fixed: Translation problems in some circumstances with cropping polygons enabled (#2316 @pearcetm)
* Fixed: The navigator area rectangle would grow larger when you zoom in very far (#2318 @donotloveshampo)
* Fixed: JSON with embedded XML was being incorrectly identified as XML (#2328 @craigberry)
* Fixed: Touch/pinch rotate was not working properly on some platforms (#2324 @rsimon, @pearcetm)
* Fixed: Navigator rotation didn't honor `immediately` parameter (#2333 @robertjcolley)
* Fixed: The navigator didn't update for its new size in certain circumstances (#2347 @pearcetm)
4.0.0:
* NEW BEHAVIOR: Setting the viewport rotation now animates by default (pass true for the new `immediately` parameter to disable) (#2136 @jonasengelmann)
* NEW BEHAVIOR: The auto resize now takes both width and height into account when scaling the contents proportionally to the viewer (#2256 @pearcetm)
* DEPRECATION: Don't access the viewport's degrees property directly anymore; instead use setRotation and getRotation (#2136 @jonasengelmann) * DEPRECATION: Don't access the viewport's degrees property directly anymore; instead use setRotation and getRotation (#2136 @jonasengelmann)
* New gesture: Double-click and drag to zoom (on by default for touch) (#2225 @HamzaTatheer) * New gesture: Double-click and drag to zoom (on by default for touch) (#2225 @HamzaTatheer)
* You can now provide a pivot point when rotating the viewport (#2233 #2253 @pearcetm) * You can now provide a pivot point when rotating the viewport (#2233 #2253 @pearcetm)
@ -13,8 +46,10 @@ OPENSEADRAGON CHANGELOG
* We now delegate tile fetching and caching to the TileSource, to allow for custom tile formats (#2148 @Aiosa) * We now delegate tile fetching and caching to the TileSource, to allow for custom tile formats (#2148 @Aiosa)
* Added support for dynamic URLs from tile sources (#2247 @JohnReagan) * Added support for dynamic URLs from tile sources (#2247 @JohnReagan)
* The viewer now emits before-destroy and destroy events (#2239 @pearcetm) * The viewer now emits before-destroy and destroy events (#2239 @pearcetm)
* Auto resize detection is now more efficient (#2256 @pearcetm)
* Improved documentation (#2211 @shyamkumaryadav) * Improved documentation (#2211 @shyamkumaryadav)
* Fixed: Cropping tiled images with polygons was broken (#2183 @altert) * Fixed: Cropping tiled images with polygons was broken (#2183 @altert)
* Fixed: Boundary constraints were wrong when the viewport was rotated (#2249 @pearcetm)
* Fixed: IIIF tile sizes would be calculated wrong on rare occasions (#2206 @filak) * Fixed: IIIF tile sizes would be calculated wrong on rare occasions (#2206 @filak)
* Fixed: Disabling buttons only changed their appearance, but they were still clickable (#2187 @pearcetm) * Fixed: Disabling buttons only changed their appearance, but they were still clickable (#2187 @pearcetm)
* Fixed: ImageTileSource produced an error having to do with getTileHashKey (#2190 @Aiosa) * Fixed: ImageTileSource produced an error having to do with getTileHashKey (#2190 @Aiosa)

7468
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "openseadragon", "name": "openseadragon",
"version": "3.1.0", "version": "4.1.0",
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
"keywords": [ "keywords": [
"image", "image",
@ -29,19 +29,20 @@
"url": "https://github.com/openseadragon/openseadragon.git" "url": "https://github.com/openseadragon/openseadragon.git"
}, },
"devDependencies": { "devDependencies": {
"grunt": "^1.4.1", "eslint-plugin-compat": "^4.1.2",
"grunt-contrib-clean": "^2.0.0", "grunt": "^1.6.1",
"grunt-contrib-clean": "^2.0.1",
"grunt-contrib-compress": "^2.0.0", "grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0", "grunt-contrib-concat": "^2.1.0",
"grunt-contrib-connect": "^3.0.0", "grunt-contrib-connect": "^3.0.0",
"grunt-contrib-qunit": "^6.2.0", "grunt-contrib-qunit": "^7.0.1",
"grunt-contrib-uglify": "^5.0.1", "grunt-contrib-uglify": "^5.0.1",
"grunt-contrib-watch": "^1.1.0", "grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^24.0.0", "grunt-eslint": "^24.0.1",
"grunt-git-describe": "^2.4.4", "grunt-git-describe": "^2.4.4",
"grunt-istanbul": "^0.8.0", "grunt-istanbul": "^0.8.0",
"grunt-text-replace": "^0.4.0", "grunt-text-replace": "^0.4.0",
"qunitjs": "2.4.1" "qunit": "^2.19.4"
}, },
"scripts": { "scripts": {
"test": "grunt test", "test": "grunt test",

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Button * OpenSeadragon - Button
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ButtonGroup * OpenSeadragon - ButtonGroup
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -112,6 +112,17 @@ $.ButtonGroup = function( options ) {
/** @lends OpenSeadragon.ButtonGroup.prototype */ /** @lends OpenSeadragon.ButtonGroup.prototype */
$.ButtonGroup.prototype = { $.ButtonGroup.prototype = {
/**
* Adds the given button to this button group.
*
* @function
* @param {OpenSeadragon.Button} button
*/
addButton: function( button ){
this.buttons.push(button);
this.element.appendChild(button.element);
},
/** /**
* TODO: Figure out why this is used on the public API and if a more useful * TODO: Figure out why this is used on the public API and if a more useful
* api can be created. * api can be created.

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Control * OpenSeadragon - Control
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ControlDock * OpenSeadragon - ControlDock
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - DisplayRect * OpenSeadragon - DisplayRect
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Drawer * OpenSeadragon - Drawer
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - DziTileSource * OpenSeadragon - DziTileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - EventSource * OpenSeadragon - EventSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -58,7 +58,7 @@ $.EventSource.prototype = {
/** /**
* Add an event handler to be triggered only once (or a given number of times) * Add an event handler to be triggered only once (or a given number of times)
* for a given event. * for a given event. It is not removable with removeHandler().
* @function * @function
* @param {String} eventName - Name of event to register. * @param {String} eventName - Name of event to register.
* @param {OpenSeadragon.EventHandler} handler - Function to call when event * @param {OpenSeadragon.EventHandler} handler - Function to call when event
@ -67,8 +67,9 @@ $.EventSource.prototype = {
* to the handler. * to the handler.
* @param {Number} [times=1] - The number of times to handle the event * @param {Number} [times=1] - The number of times to handle the event
* before removing it. * before removing it.
* @param {Number} [priority=0] - Handler priority. By default, all priorities are 0. Higher number = priority.
*/ */
addOnceHandler: function(eventName, handler, userData, times) { addOnceHandler: function(eventName, handler, userData, times, priority) {
var self = this; var self = this;
times = times || 1; times = times || 1;
var count = 0; var count = 0;
@ -77,9 +78,9 @@ $.EventSource.prototype = {
if (count === times) { if (count === times) {
self.removeHandler(eventName, onceHandler); self.removeHandler(eventName, onceHandler);
} }
handler(event); return handler(event);
}; };
this.addHandler(eventName, onceHandler, userData); this.addHandler(eventName, onceHandler, userData, priority);
}, },
/** /**
@ -88,14 +89,22 @@ $.EventSource.prototype = {
* @param {String} eventName - Name of event to register. * @param {String} eventName - Name of event to register.
* @param {OpenSeadragon.EventHandler} handler - Function to call when event is triggered. * @param {OpenSeadragon.EventHandler} handler - Function to call when event is triggered.
* @param {Object} [userData=null] - Arbitrary object to be passed unchanged to the handler. * @param {Object} [userData=null] - Arbitrary object to be passed unchanged to the handler.
* @param {Number} [priority=0] - Handler priority. By default, all priorities are 0. Higher number = priority.
*/ */
addHandler: function ( eventName, handler, userData ) { addHandler: function ( eventName, handler, userData, priority ) {
var events = this.events[ eventName ]; var events = this.events[ eventName ];
if ( !events ) { if ( !events ) {
this.events[ eventName ] = events = []; this.events[ eventName ] = events = [];
} }
if ( handler && $.isFunction( handler ) ) { if ( handler && $.isFunction( handler ) ) {
events[ events.length ] = { handler: handler, userData: userData || null }; var index = events.length,
event = { handler: handler, userData: userData || null, priority: priority || 0 };
events[ index ] = event;
while ( index > 0 && events[ index - 1 ].priority < events[ index ].priority ) {
events[ index ] = events[ index - 1 ];
events[ index - 1 ] = event;
index--;
}
} }
}, },
@ -156,7 +165,7 @@ $.EventSource.prototype = {
* @function * @function
* @param {String} eventName - Name of event to get handlers for. * @param {String} eventName - Name of event to get handlers for.
*/ */
getHandler: function ( eventName ) { getHandler: function ( eventName) {
var events = this.events[ eventName ]; var events = this.events[ eventName ];
if ( !events || !events.length ) { if ( !events || !events.length ) {
return null; return null;
@ -186,15 +195,12 @@ $.EventSource.prototype = {
raiseEvent: function( eventName, eventArgs ) { raiseEvent: function( eventName, eventArgs ) {
//uncomment if you want to get a log of all events //uncomment if you want to get a log of all events
//$.console.log( eventName ); //$.console.log( eventName );
var handler = this.getHandler( eventName ); var handler = this.getHandler( eventName );
if ( handler ) { if ( handler ) {
if ( !eventArgs ) { return handler( this, eventArgs || {} );
eventArgs = {};
}
handler( this, eventArgs );
} }
return undefined;
} }
}; };

View File

@ -2,7 +2,7 @@
* OpenSeadragon - full-screen support functions * OpenSeadragon - full-screen support functions
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -67,10 +67,14 @@
return document.fullscreenElement; return document.fullscreenElement;
}; };
fullScreenApi.requestFullScreen = function( element ) { fullScreenApi.requestFullScreen = function( element ) {
return element.requestFullscreen(); return element.requestFullscreen().catch(function (msg) {
$.console.error('Fullscreen request failed: ', msg);
});
}; };
fullScreenApi.exitFullScreen = function() { fullScreenApi.exitFullScreen = function() {
document.exitFullscreen(); document.exitFullscreen().catch(function (msg) {
$.console.error('Error while exiting fullscreen: ', msg);
});
}; };
fullScreenApi.fullScreenEventName = "fullscreenchange"; fullScreenApi.fullScreenEventName = "fullscreenchange";
fullScreenApi.fullScreenErrorEventName = "fullscreenerror"; fullScreenApi.fullScreenErrorEventName = "fullscreenerror";

View File

@ -2,7 +2,7 @@
* OpenSeadragon - IIIFTileSource * OpenSeadragon - IIIFTileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -141,6 +141,18 @@ $.IIIFTileSource = function( options ){
} }
} }
// Create an array with our exact resolution sizes if these have been supplied
if( this.sizes ) {
var sizeLength = this.sizes.length;
if ( (sizeLength === options.maxLevel) || (sizeLength === options.maxLevel + 1) ) {
this.levelSizes = this.sizes;
// Need to take into account that the list may or may not include the full resolution size
if( sizeLength === options.maxLevel ) {
this.levelSizes.push( {width: this.width, height: this.height} );
}
}
}
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
}; };
@ -333,7 +345,17 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
} }
} }
return $.TileSource.prototype.getNumTiles.call(this, level); // Use supplied list of scaled resolution sizes if these exist
if( this.levelSizes ) {
var levelSize = this.levelSizes[level];
var x = Math.ceil( levelSize.width / this.getTileWidth(level) ),
y = Math.ceil( levelSize.height / this.getTileHeight(level) );
return new $.Point( x, y );
}
// Otherwise call default TileSource->getNumTiles() function
else {
return $.TileSource.prototype.getNumTiles.call(this, level);
}
}, },
@ -348,6 +370,34 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
return new $.Point(0, 0); return new $.Point(0, 0);
} }
// Use supplied list of scaled resolution sizes if these exist
if( this.levelSizes ) {
var validPoint = point.x >= 0 && point.x <= 1 &&
point.y >= 0 && point.y <= 1 / this.aspectRatio;
$.console.assert(validPoint, "[TileSource.getTileAtPoint] must be called with a valid point.");
var widthScaled = this.levelSizes[level].width;
var pixelX = point.x * widthScaled;
var pixelY = point.y * widthScaled;
var x = Math.floor(pixelX / this.getTileWidth(level));
var y = Math.floor(pixelY / this.getTileHeight(level));
// When point.x == 1 or point.y == 1 / this.aspectRatio we want to
// return the last tile of the row/column
if (point.x >= 1) {
x = this.getNumTiles(level).x - 1;
}
var EPSILON = 1e-15;
if (point.y >= 1 / this.aspectRatio - EPSILON) {
y = this.getNumTiles(level).y - 1;
}
return new $.Point(x, y);
}
// Otherwise call default TileSource->getTileAtPoint() function
return $.TileSource.prototype.getTileAtPoint.call(this, level, point); return $.TileSource.prototype.getTileAtPoint.call(this, level, point);
}, },
@ -375,10 +425,9 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
var IIIF_ROTATION = '0', var IIIF_ROTATION = '0',
//## get the scale (level as a decimal) //## get the scale (level as a decimal)
scale = Math.pow( 0.5, this.maxLevel - level ), scale = Math.pow( 0.5, this.maxLevel - level ),
//# image dimensions at this level //# image dimensions at this level
levelWidth = Math.round( this.width * scale ), levelWidth,
levelHeight = Math.round( this.height * scale ), levelHeight,
//## iiif region //## iiif region
tileWidth, tileWidth,
@ -396,6 +445,17 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
iiifQuality, iiifQuality,
uri; uri;
// Use supplied list of scaled resolution sizes if these exist
if( this.levelSizes ) {
levelWidth = this.levelSizes[level].width;
levelHeight = this.levelSizes[level].height;
}
// Otherwise calculate the sizes ourselves
else {
levelWidth = Math.ceil( this.width * scale );
levelHeight = Math.ceil( this.height * scale );
}
tileWidth = this.getTileWidth(level); tileWidth = this.getTileWidth(level);
tileHeight = this.getTileHeight(level); tileHeight = this.getTileHeight(level);
iiifTileSizeWidth = Math.round( tileWidth / scale ); iiifTileSizeWidth = Math.round( tileWidth / scale );
@ -426,8 +486,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
} else { } else {
iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' ); iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
} }
iiifSizeW = Math.round( iiifTileW * scale ); iiifSizeW = Math.min( tileWidth, levelWidth - (x * tileWidth) );
iiifSizeH = Math.round( iiifTileH * scale ); iiifSizeH = Math.min( tileHeight, levelHeight - (y * tileHeight) );
if ( this.version === 2 && iiifSizeW === this.width ) { if ( this.version === 2 && iiifSizeW === this.width ) {
iiifSize = "full"; iiifSize = "full";
} else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) { } else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) {

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ImageLoader * OpenSeadragon - ImageLoader
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -50,17 +50,19 @@
* @param {Function} [options.callback] - Called once image has been downloaded. * @param {Function} [options.callback] - Called once image has been downloaded.
* @param {Function} [options.abort] - Called when this image job is aborted. * @param {Function} [options.abort] - Called when this image job is aborted.
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete. * @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
* @param {Number} [options.tries] - Actual number of the current try.
*/ */
$.ImageJob = function(options) { $.ImageJob = function(options) {
$.extend(true, this, { $.extend(true, this, {
timeout: $.DEFAULT_SETTINGS.timeout, timeout: $.DEFAULT_SETTINGS.timeout,
jobId: null jobId: null,
tries: 0
}, options); }, options);
/** /**
* Data object which will contain downloaded image data. * Data object which will contain downloaded image data.
* @member {Image|*} image data object, by default an Image object (depends on TileSource) * @member {Image|*} data data object, by default an Image object (depends on TileSource)
* @memberof OpenSeadragon.ImageJob# * @memberof OpenSeadragon.ImageJob#
*/ */
this.data = null; this.data = null;
@ -87,6 +89,8 @@ $.ImageJob.prototype = {
* @method * @method
*/ */
start: function() { start: function() {
this.tries++;
var self = this; var self = this;
var selfAbort = this.abort; var selfAbort = this.abort;
@ -138,6 +142,7 @@ $.ImageLoader = function(options) {
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit, jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
timeout: $.DEFAULT_SETTINGS.timeout, timeout: $.DEFAULT_SETTINGS.timeout,
jobQueue: [], jobQueue: [],
failedTiles: [],
jobsInProgress: 0 jobsInProgress: 0
}, options); }, options);
@ -220,7 +225,8 @@ $.ImageLoader.prototype = {
}; };
/** /**
* Cleans up ImageJob once completed. * Cleans up ImageJob once completed. Restarts job after tileRetryDelay seconds if failed
* but max tileRetryMax times
* @method * @method
* @private * @private
* @param loader - ImageLoader used to start job. * @param loader - ImageLoader used to start job.
@ -228,6 +234,9 @@ $.ImageLoader.prototype = {
* @param callback - Called once cleanup is finished. * @param callback - Called once cleanup is finished.
*/ */
function completeJob(loader, job, callback) { function completeJob(loader, job, callback) {
if (job.errorMsg !== '' && (job.data === null || job.data === undefined) && job.tries < 1 + loader.tileRetryMax) {
loader.failedTiles.push(job);
}
var nextJob; var nextJob;
loader.jobsInProgress--; loader.jobsInProgress--;
@ -238,6 +247,16 @@ function completeJob(loader, job, callback) {
loader.jobsInProgress++; loader.jobsInProgress++;
} }
if (loader.tileRetryMax > 0 && loader.jobQueue.length === 0) {
if ((!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.failedTiles.length > 0) {
nextJob = loader.failedTiles.shift();
setTimeout(function () {
nextJob.start();
}, loader.tileRetryDelay);
loader.jobsInProgress++;
}
}
callback(job.data, job.errorMsg, job.request); callback(job.data, job.errorMsg, job.request);
} }

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ImageTileSource * OpenSeadragon - ImageTileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - LegacyTileSource * OpenSeadragon - LegacyTileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - MouseTracker * OpenSeadragon - MouseTracker
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Navigator * OpenSeadragon - Navigator
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -125,7 +125,8 @@ $.Navigator = function( options ){
immediateRender: true, immediateRender: true,
blendTime: 0, blendTime: 0,
animationTime: options.animationTime, animationTime: options.animationTime,
autoResize: options.autoResize, // disable autoResize since resize behavior is implemented differently by the navigator
autoResize: false,
// prevent resizing the navigator from adding unwanted space around the image // prevent resizing the navigator from adding unwanted space around the image
minZoomImageRatio: 1.0, minZoomImageRatio: 1.0,
background: options.background, background: options.background,
@ -183,6 +184,7 @@ $.Navigator = function( options ){
style.styleFloat = 'left'; //IE style.styleFloat = 'left'; //IE
style.zIndex = 999999999; style.zIndex = 999999999;
style.cursor = 'default'; style.cursor = 'default';
style.boxSizing = 'content-box';
}( this.displayRegion.style, this.borderWidth )); }( this.displayRegion.style, this.borderWidth ));
$.setElementPointerEventsNone( this.displayRegion ); $.setElementPointerEventsNone( this.displayRegion );
$.setElementTouchActionNone( this.displayRegion ); $.setElementTouchActionNone( this.displayRegion );
@ -222,19 +224,19 @@ $.Navigator = function( options ){
this.displayRegionContainer.appendChild(this.displayRegion); this.displayRegionContainer.appendChild(this.displayRegion);
this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer); this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
function rotate(degrees) { function rotate(degrees, immediately) {
_setTransformRotate(_this.displayRegionContainer, degrees); _setTransformRotate(_this.displayRegionContainer, degrees);
_setTransformRotate(_this.displayRegion, -degrees); _setTransformRotate(_this.displayRegion, -degrees);
_this.viewport.setRotation(degrees); _this.viewport.setRotation(degrees, immediately);
} }
if (options.navigatorRotate) { if (options.navigatorRotate) {
var degrees = options.viewer.viewport ? var degrees = options.viewer.viewport ?
options.viewer.viewport.getRotation() : options.viewer.viewport.getRotation() :
options.viewer.degrees || 0; options.viewer.degrees || 0;
rotate(degrees); rotate(degrees, true);
options.viewer.addHandler("rotate", function (args) { options.viewer.addHandler("rotate", function (args) {
rotate(args.degrees); rotate(args.degrees, args.immediately);
}); });
} }
@ -322,6 +324,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
this.width = width; this.width = width;
this.element.style.width = typeof (width) === "number" ? (width + 'px') : width; this.element.style.width = typeof (width) === "number" ? (width + 'px') : width;
this._resizeWithViewer = false; this._resizeWithViewer = false;
this.updateSize();
}, },
/** /**
@ -332,6 +335,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
this.height = height; this.height = height;
this.element.style.height = typeof (height) === "number" ? (height + 'px') : height; this.element.style.height = typeof (height) === "number" ? (height + 'px') : height;
this._resizeWithViewer = false; this._resizeWithViewer = false;
this.updateSize();
}, },
/** /**
@ -393,15 +397,20 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false) bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false)
.minus( this.totalBorderWidths ); .minus( this.totalBorderWidths );
if (!this.navigatorRotate) {
var degrees = viewport.getRotation(true);
_setTransformRotate(this.displayRegion, -degrees);
}
//update style for navigator-box //update style for navigator-box
var style = this.displayRegion.style; var style = this.displayRegion.style;
style.display = this.world.getItemCount() ? 'block' : 'none'; style.display = this.world.getItemCount() ? 'block' : 'none';
style.top = Math.round( topleft.y ) + 'px'; style.top = topleft.y.toFixed(2) + "px";
style.left = Math.round( topleft.x ) + 'px'; style.left = topleft.x.toFixed(2) + "px";
var width = Math.abs( topleft.x - bottomright.x ); var width = bottomright.x - topleft.x;
var height = Math.abs( topleft.y - bottomright.y ); var height = bottomright.y - topleft.y;
// make sure width and height are non-negative so IE doesn't throw // make sure width and height are non-negative so IE doesn't throw
style.width = Math.round( Math.max( width, 0 ) ) + 'px'; style.width = Math.round( Math.max( width, 0 ) ) + 'px';
style.height = Math.round( Math.max( height, 0 ) ) + 'px'; style.height = Math.round( Math.max( height, 0 ) ) + 'px';

View File

@ -2,7 +2,7 @@
* OpenSeadragon * OpenSeadragon
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -60,7 +60,7 @@
/* /*
* Portions of this source file taken from mattsnider.com: * Portions of this source file taken from mattsnider.com:
* *
* Copyright (c) 2006-2022 Matt Snider * Copyright (c) 2006-2013 Matt Snider
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -499,6 +499,12 @@
* @property {Number} [timeout=30000] * @property {Number} [timeout=30000]
* The max number of milliseconds that an image job may take to complete. * The max number of milliseconds that an image job may take to complete.
* *
* @property {Number} [tileRetryMax=0]
* The max number of retries when a tile download fails. By default it's 0, so retries are disabled.
*
* @property {Number} [tileRetryDelay=2500]
* Milliseconds to wait after each tile retry if tileRetryMax is set.
*
* @property {Boolean} [useCanvas=true] * @property {Boolean} [useCanvas=true]
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported. * Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
* *
@ -563,50 +569,50 @@
* viewing the first image and the 'next' button will wrap to the first * viewing the first image and the 'next' button will wrap to the first
* image when viewing the last image. * image when viewing the last image.
* *
* @property {String} zoomInButton *@property {String|Element} zoomInButton
* Set the id of the custom 'Zoom in' button to use. * Set the id or element of the custom 'Zoom in' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} zoomOutButton * @property {String|Element} zoomOutButton
* Set the id of the custom 'Zoom out' button to use. * Set the id or element of the custom 'Zoom out' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} homeButton * @property {String|Element} homeButton
* Set the id of the custom 'Go home' button to use. * Set the id or element of the custom 'Go home' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} fullPageButton * @property {String|Element} fullPageButton
* Set the id of the custom 'Toggle full page' button to use. * Set the id or element of the custom 'Toggle full page' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} rotateLeftButton * @property {String|Element} rotateLeftButton
* Set the id of the custom 'Rotate left' button to use. * Set the id or element of the custom 'Rotate left' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} rotateRightButton * @property {String|Element} rotateRightButton
* Set the id of the custom 'Rotate right' button to use. * Set the id or element of the custom 'Rotate right' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} previousButton * @property {String|Element} previousButton
* Set the id of the custom 'Previous page' button to use. * Set the id or element of the custom 'Previous page' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
* *
* @property {String} nextButton * @property {String|Element} nextButton
* Set the id of the custom 'Next page' button to use. * Set the id or element of the custom 'Next page' button to use.
* This is useful to have a custom button anywhere in the web page.<br> * This is useful to have a custom button anywhere in the web page.<br>
* To only change the button images, consider using * To only change the button images, consider using
* {@link OpenSeadragon.Options.navImages} * {@link OpenSeadragon.Options.navImages}
@ -830,14 +836,16 @@ function OpenSeadragon( options ){
* @private * @private
*/ */
var class2type = { var class2type = {
'[object Boolean]': 'boolean', '[object Boolean]': 'boolean',
'[object Number]': 'number', '[object Number]': 'number',
'[object String]': 'string', '[object String]': 'string',
'[object Function]': 'function', '[object Function]': 'function',
'[object Array]': 'array', '[object AsyncFunction]': 'function',
'[object Date]': 'date', '[object Promise]': 'promise',
'[object RegExp]': 'regexp', '[object Array]': 'array',
'[object Object]': 'object' '[object Date]': 'date',
'[object RegExp]': 'regexp',
'[object Object]': 'object'
}, },
// Save a reference to some core methods // Save a reference to some core methods
toString = Object.prototype.toString, toString = Object.prototype.toString,
@ -853,7 +861,6 @@ function OpenSeadragon( options ){
return $.type(obj) === "function"; return $.type(obj) === "function";
}; };
/** /**
* Taken from jQuery 1.6.1 * Taken from jQuery 1.6.1
* @function isArray * @function isArray
@ -1359,6 +1366,8 @@ function OpenSeadragon( options ){
maxImageCacheCount: 200, maxImageCacheCount: 200,
timeout: 30000, timeout: 30000,
useCanvas: true, // Use canvas element for drawing if available useCanvas: true, // Use canvas element for drawing if available
tileRetryMax: 0,
tileRetryDelay: 2500,
//INTERFACE RESOURCE SETTINGS //INTERFACE RESOURCE SETTINGS
prefixUrl: "/images/", prefixUrl: "/images/",
@ -2247,25 +2256,12 @@ function OpenSeadragon( options ){
event.stopPropagation(); event.stopPropagation();
}, },
// Deprecated
/**
* Similar to OpenSeadragon.delegate, but it does not immediately call
* the method on the object, returning a function which can be called
* repeatedly to delegate the method. It also allows additional arguments
* to be passed during construction which will be added during each
* invocation, and each invocation can add additional arguments as well.
*
* @function
* @param {Object} object
* @param {Function} method
* @param [args] any additional arguments are passed as arguments to the
* created callback
* @returns {Function}
*/
createCallback: function( object, method ) { createCallback: function( object, method ) {
//TODO: This pattern is painful to use and debug. It's much cleaner //TODO: This pattern is painful to use and debug. It's much cleaner
// to use pinning plus anonymous functions. Get rid of this // to use pinning plus anonymous functions. Get rid of this
// pattern! // pattern!
console.error('The createCallback function is deprecated and will be removed in future versions. Please use alternativeFunction instead.');
var initialArgs = [], var initialArgs = [],
i; i;
for ( i = 2; i < arguments.length; i++ ) { for ( i = 2; i < arguments.length; i++ ) {

View File

@ -2,7 +2,7 @@
* OpenSeadragon - OsmTileSource * OpenSeadragon - OsmTileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Overlay * OpenSeadragon - Overlay
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Point * OpenSeadragon - Point
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Profiler * OpenSeadragon - Profiler
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Rect * OpenSeadragon - Rect
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ReferenceStrip * OpenSeadragon - ReferenceStrip
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -300,7 +300,8 @@ function onStripClick( event ) {
var page; var page;
if ( 'horizontal' === this.scroll ) { if ( 'horizontal' === this.scroll ) {
page = Math.floor(event.position.x / this.panelWidth); // +4px fix to solve problem with precision on thumbnail selection if there is a lot of them
page = Math.floor(event.position.x / (this.panelWidth + 4));
} else { } else {
page = Math.floor(event.position.y / this.panelHeight); page = Math.floor(event.position.y / this.panelHeight);
} }

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Spring * OpenSeadragon - Spring
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - getString/setString * OpenSeadragon - getString/setString
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Tile * OpenSeadragon - Tile
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -44,7 +44,7 @@
* coordinates. * coordinates.
* @param {Boolean} exists Is this tile a part of a sparse image? ( Also has * @param {Boolean} exists Is this tile a part of a sparse image? ( Also has
* this tile failed to load? ) * this tile failed to load? )
* @param {String|() => String} url The URL of this tile's image or a function that returns a url. * @param {String|Function} url The URL of this tile's image or a function that returns a url.
* @param {CanvasRenderingContext2D} context2D The context2D of this tile if it * @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
* is provided directly by the tile source. * is provided directly by the tile source.
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request . * @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
@ -98,7 +98,7 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* Private property to hold string url or url retriever function. * Private property to hold string url or url retriever function.
* Consumers should access via Tile.getUrl() * Consumers should access via Tile.getUrl()
* @private * @private
* @member {String|() => String} url * @member {String|Function} url
* @memberof OpenSeadragon.Tile# * @memberof OpenSeadragon.Tile#
*/ */
this._url = url; this._url = url;

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TileCache * OpenSeadragon - TileCache
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TiledImage * OpenSeadragon - TiledImage
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -147,6 +147,9 @@ $.TiledImage = function( options ) {
var degrees = options.degrees || 0; var degrees = options.degrees || 0;
delete options.degrees; delete options.degrees;
var ajaxHeaders = options.ajaxHeaders;
delete options.ajaxHeaders;
$.extend( true, this, { $.extend( true, this, {
//internal state properties //internal state properties
@ -238,6 +241,9 @@ $.TiledImage = function( options ) {
tiledImage: _this tiledImage: _this
}, args)); }, args));
}; };
this._ownAjaxHeaders = {};
this.setAjaxHeaders(ajaxHeaders, false);
}; };
$.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.TiledImage.prototype */{ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.TiledImage.prototype */{
@ -1003,6 +1009,90 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
}); });
}, },
/**
* Update headers to include when making AJAX requests.
*
* Unless `propagate` is set to false (which is likely only useful in rare circumstances),
* the updated headers are propagated to all tiles and queued image loader jobs.
*
* Note that the rules for merging headers still apply, i.e. headers returned by
* {@link OpenSeadragon.TileSource#getTileAjaxHeaders} take precedence over
* the headers here in the tiled image (`TiledImage.ajaxHeaders`).
*
* @function
* @param {Object} ajaxHeaders Updated AJAX headers, which will be merged over any headers specified in {@link OpenSeadragon.Options}.
* @param {Boolean} [propagate=true] Whether to propagate updated headers to existing tiles and queued image loader jobs.
*/
setAjaxHeaders: function(ajaxHeaders, propagate) {
if (ajaxHeaders === null) {
ajaxHeaders = {};
}
if (!$.isPlainObject(ajaxHeaders)) {
console.error('[TiledImage.setAjaxHeaders] Ignoring invalid headers, must be a plain object');
return;
}
this._ownAjaxHeaders = ajaxHeaders;
this._updateAjaxHeaders(propagate);
},
/**
* Update headers to include when making AJAX requests.
*
* This function has the same effect as calling {@link OpenSeadragon.TiledImage#setAjaxHeaders},
* except that the headers for this tiled image do not change. This is especially useful
* for propagating updated headers from {@link OpenSeadragon.TileSource#getTileAjaxHeaders}
* to existing tiles.
*
* @private
* @function
* @param {Boolean} [propagate=true] Whether to propagate updated headers to existing tiles and queued image loader jobs.
*/
_updateAjaxHeaders: function(propagate) {
if (propagate === undefined) {
propagate = true;
}
// merge with viewer's headers
if ($.isPlainObject(this.viewer.ajaxHeaders)) {
this.ajaxHeaders = $.extend({}, this.viewer.ajaxHeaders, this._ownAjaxHeaders);
} else {
this.ajaxHeaders = this._ownAjaxHeaders;
}
// propagate header updates to all tiles and queued image loader jobs
if (propagate) {
var numTiles, xMod, yMod, tile;
for (var level in this.tilesMatrix) {
numTiles = this.source.getNumTiles(level);
for (var x in this.tilesMatrix[level]) {
xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
for (var y in this.tilesMatrix[level][x]) {
yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
tile = this.tilesMatrix[level][x][y];
tile.loadWithAjax = this.loadTilesWithAjax;
if (tile.loadWithAjax) {
var tileAjaxHeaders = this.source.getTileAjaxHeaders( level, xMod, yMod );
tile.ajaxHeaders = $.extend({}, this.ajaxHeaders, tileAjaxHeaders);
} else {
tile.ajaxHeaders = null;
}
}
}
}
for (var i = 0; i < this._imageLoader.jobQueue.length; i++) {
var job = this._imageLoader.jobQueue[i];
job.loadWithAjax = job.tile.loadWithAjax;
job.ajaxHeaders = job.tile.loadWithAjax ? job.tile.ajaxHeaders : null;
}
}
},
// private // private
_setScale: function(scale, immediately) { _setScale: function(scale, immediately) {
var sameTarget = (this._scaleSpring.target.value === scale); var sameTarget = (this._scaleSpring.target.value === scale);
@ -1621,6 +1711,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
tile.loading = false; tile.loading = false;
tile.exists = false; tile.exists = false;
return; return;
} else {
tile.exists = true;
} }
if ( time < this.lastResetTime ) { if ( time < this.lastResetTime ) {
@ -1656,9 +1748,14 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/ */
_setTileLoaded: function(tile, data, cutoff, tileRequest) { _setTileLoaded: function(tile, data, cutoff, tileRequest) {
var increment = 0, var increment = 0,
eventFinished = false,
_this = this; _this = this;
function getCompletionCallback() { function getCompletionCallback() {
if (eventFinished) {
$.console.error("Event 'tile-loaded' argument getCompletionCallback must be called synchronously. " +
"Its return value should be called asynchronously.");
}
increment++; increment++;
return completionCallback; return completionCallback;
} }
@ -1690,7 +1787,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @event tile-loaded * @event tile-loaded
* @memberof OpenSeadragon.Viewer * @memberof OpenSeadragon.Viewer
* @type {object} * @type {object}
* @property {Image || *} image - The image (data) of the tile. Deprecated. * @property {Image|*} image - The image (data) of the tile. Deprecated.
* @property {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object * @property {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile. * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded. * @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
@ -1700,6 +1797,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* marked as entirely loaded when the callback has been called once for each * marked as entirely loaded when the callback has been called once for each
* call to getCompletionCallback. * call to getCompletionCallback.
*/ */
var fallbackCompletion = getCompletionCallback();
this.viewer.raiseEvent("tile-loaded", { this.viewer.raiseEvent("tile-loaded", {
tile: tile, tile: tile,
tiledImage: this, tiledImage: this,
@ -1711,8 +1810,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
data: data, data: data,
getCompletionCallback: getCompletionCallback getCompletionCallback: getCompletionCallback
}); });
eventFinished = true;
// In case the completion callback is never called, we at least force it once. // In case the completion callback is never called, we at least force it once.
getCompletionCallback()(); fallbackCompletion();
}, },
/** /**
@ -1864,7 +1964,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
if (lastDrawn.length > 1 && if (lastDrawn.length > 1 &&
imageZoom > this.smoothTileEdgesMinZoom && imageZoom > this.smoothTileEdgesMinZoom &&
!this.iOSDevice && !this.iOSDevice &&
this.getRotation(true) % 360 === 0 && // TODO: support tile edge smoothing with tiled image rotation. this.getRotation(true) % 360 === 0 && // TODO: support tile edge smoothing with tiled image rotation (viewport rotation is not a problem).
this._drawer.viewer.viewport.getFlip() === false && // TODO: support tile edge smoothing with viewport flip (tiled image flip is not a problem).
$.supportsCanvas && this.viewer.useCanvas) { $.supportsCanvas && this.viewer.useCanvas) {
// When zoomed in a lot (>100%) the tile edges are visible. // When zoomed in a lot (>100%) the tile edges are visible.
// So we have to composite them at ~100% and scale them up together. // So we have to composite them at ~100% and scale them up together.
@ -1954,6 +2055,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
if (sketchScale) { if (sketchScale) {
clipPoint = clipPoint.times(sketchScale); clipPoint = clipPoint.times(sketchScale);
} }
if (sketchTranslate) {
clipPoint = clipPoint.plus(sketchTranslate);
}
return clipPoint; return clipPoint;
}); });
}); });

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TileSource * OpenSeadragon - TileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -618,7 +618,7 @@ $.TileSource.prototype = {
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
* @returns {String|() => string} url - A string for the url or a function that returns a url string. * @returns {String|Function} url - A string for the url or a function that returns a url string.
* @throws {Error} * @throws {Error}
*/ */
getTileUrl: function( level, x, y ) { getTileUrl: function( level, x, y ) {
@ -663,6 +663,11 @@ $.TileSource.prototype = {
* The headers returned here will override headers specified at the Viewer or TiledImage level. * The headers returned here will override headers specified at the Viewer or TiledImage level.
* Specifying a falsy value for a header will clear its existing value set at the Viewer or * Specifying a falsy value for a header will clear its existing value set at the Viewer or
* TiledImage level (if any). * TiledImage level (if any).
*
* Note that the headers of existing tiles don't automatically change when this function
* returns updated headers. To do that, you need to call {@link OpenSeadragon.Viewer#setAjaxHeaders}
* and propagate the changes.
*
* @function * @function
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
@ -932,7 +937,7 @@ function processResponse( xhr ){
throw new Error( $.getString( "Errors.Status", status, statusText ) ); throw new Error( $.getString( "Errors.Status", status, statusText ) );
} }
if( responseText.match(/\s*<.*/) ){ if( responseText.match(/^\s*<.*/) ){
try{ try{
data = ( xhr.responseXML && xhr.responseXML.documentElement ) ? data = ( xhr.responseXML && xhr.responseXML.documentElement ) ?
xhr.responseXML : xhr.responseXML :

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TileSourceCollection * OpenSeadragon - TileSourceCollection
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TmsTileSource * OpenSeadragon - TmsTileSource
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Viewer * OpenSeadragon - Viewer
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -204,6 +204,8 @@ $.Viewer = function( options ) {
prevContainerSize: null, prevContainerSize: null,
animating: false, animating: false,
forceRedraw: false, forceRedraw: false,
needsResize: false,
forceResize: false,
mouseInside: false, mouseInside: false,
group: null, group: null,
// whether we should be continuously zooming // whether we should be continuously zooming
@ -306,7 +308,9 @@ $.Viewer = function( options ) {
nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ), nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ),
nonPrimaryReleaseHandler: $.delegate( this, onCanvasNonPrimaryRelease ), nonPrimaryReleaseHandler: $.delegate( this, onCanvasNonPrimaryRelease ),
scrollHandler: $.delegate( this, onCanvasScroll ), scrollHandler: $.delegate( this, onCanvasScroll ),
pinchHandler: $.delegate( this, onCanvasPinch ) pinchHandler: $.delegate( this, onCanvasPinch ),
focusHandler: $.delegate( this, onCanvasFocus ),
blurHandler: $.delegate( this, onCanvasBlur ),
}); });
this.outerTracker = new $.MouseTracker({ this.outerTracker = new $.MouseTracker({
@ -330,6 +334,17 @@ $.Viewer = function( options ) {
THIS[ this.hash ].prevContainerSize = _getSafeElemSize( this.container ); THIS[ this.hash ].prevContainerSize = _getSafeElemSize( this.container );
if(window.ResizeObserver){
this._autoResizePolling = false;
this._resizeObserver = new ResizeObserver(function(){
THIS[_this.hash].needsResize = true;
});
this._resizeObserver.observe(this.container, {});
} else {
this._autoResizePolling = true;
}
// Create the world // Create the world
this.world = new $.World({ this.world = new $.World({
viewer: this viewer: this
@ -395,7 +410,9 @@ $.Viewer = function( options ) {
// Create the image loader // Create the image loader
this.imageLoader = new $.ImageLoader({ this.imageLoader = new $.ImageLoader({
jobLimit: this.imageLoaderLimit, jobLimit: this.imageLoaderLimit,
timeout: options.timeout timeout: options.timeout,
tileRetryMax: this.tileRetryMax,
tileRetryDelay: this.tileRetryDelay
}); });
// Create the tile cache // Create the tile cache
@ -788,6 +805,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
//TODO: implement this... //TODO: implement this...
//this.unbindSequenceControls() //this.unbindSequenceControls()
//this.unbindStandardControls() //this.unbindStandardControls()
if (this._resizeObserver){
this._resizeObserver.disconnect();
}
if (this.referenceStrip) { if (this.referenceStrip) {
this.referenceStrip.destroy(); this.referenceStrip.destroy();
@ -948,7 +968,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* Turns debugging mode on or off for this viewer. * Turns debugging mode on or off for this viewer.
* *
* @function * @function
* @param {Boolean} true to turn debug on, false to turn debug off. * @param {Boolean} debugMode true to turn debug on, false to turn debug off.
*/ */
setDebugMode: function(debugMode){ setDebugMode: function(debugMode){
@ -960,6 +980,63 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.forceRedraw(); this.forceRedraw();
}, },
/**
* Update headers to include when making AJAX requests.
*
* Unless `propagate` is set to false (which is likely only useful in rare circumstances),
* the updated headers are propagated to all tiled images, each of which will subsequently
* propagate the changed headers to all their tiles.
* If applicable, the headers of the viewer's navigator and reference strip will also be updated.
*
* Note that the rules for merging headers still apply, i.e. headers returned by
* {@link OpenSeadragon.TileSource#getTileAjaxHeaders} take precedence over
* `TiledImage.ajaxHeaders`, which take precedence over the headers here in the viewer.
*
* @function
* @param {Object} ajaxHeaders Updated AJAX headers.
* @param {Boolean} [propagate=true] Whether to propagate updated headers to tiled images, etc.
*/
setAjaxHeaders: function(ajaxHeaders, propagate) {
if (ajaxHeaders === null) {
ajaxHeaders = {};
}
if (!$.isPlainObject(ajaxHeaders)) {
console.error('[Viewer.setAjaxHeaders] Ignoring invalid headers, must be a plain object');
return;
}
if (propagate === undefined) {
propagate = true;
}
this.ajaxHeaders = ajaxHeaders;
if (propagate) {
for (var i = 0; i < this.world.getItemCount(); i++) {
this.world.getItemAt(i)._updateAjaxHeaders(true);
}
if (this.navigator) {
this.navigator.setAjaxHeaders(this.ajaxHeaders, true);
}
if (this.referenceStrip && this.referenceStrip.miniViewers) {
for (var key in this.referenceStrip.miniViewers) {
this.referenceStrip.miniViewers[key].setAjaxHeaders(this.ajaxHeaders, true);
}
}
}
},
/**
* Adds the given button to this viewer.
*
* @function
* @param {OpenSeadragon.Button} button
*/
addButton: function( button ){
this.buttonGroup.addButton(button);
},
/** /**
* @function * @function
* @returns {Boolean} * @returns {Boolean}
@ -1374,7 +1451,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* A set of headers to include when making tile AJAX requests. * A set of headers to include when making tile AJAX requests.
* Note that these headers will be merged over any headers specified in {@link OpenSeadragon.Options}. * Note that these headers will be merged over any headers specified in {@link OpenSeadragon.Options}.
* Specifying a falsy value for a header will clear its existing value set at the Viewer level (if any). * Specifying a falsy value for a header will clear its existing value set at the Viewer level (if any).
* requests.
* @param {Function} [options.success] A function that gets called when the image is * @param {Function} [options.success] A function that gets called when the image is
* successfully added. It's passed the event object which contains a single property: * successfully added. It's passed the event object which contains a single property:
* "item", which is the resulting instance of TiledImage. * "item", which is the resulting instance of TiledImage.
@ -1422,10 +1498,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
if (options.loadTilesWithAjax === undefined) { if (options.loadTilesWithAjax === undefined) {
options.loadTilesWithAjax = this.loadTilesWithAjax; options.loadTilesWithAjax = this.loadTilesWithAjax;
} }
if (options.ajaxHeaders === undefined || options.ajaxHeaders === null) { if (!$.isPlainObject(options.ajaxHeaders)) {
options.ajaxHeaders = this.ajaxHeaders; options.ajaxHeaders = {};
} else if ($.isPlainObject(options.ajaxHeaders) && $.isPlainObject(this.ajaxHeaders)) {
options.ajaxHeaders = $.extend({}, this.ajaxHeaders, options.ajaxHeaders);
} }
var myQueueItem = { var myQueueItem = {
@ -1682,6 +1756,14 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
return this; return this;
}, },
/**
* Force the viewer to reset its size to match its container.
*/
forceResize: function() {
THIS[this.hash].needsResize = true;
THIS[this.hash].forceResize = true;
},
/** /**
* @function * @function
* @returns {OpenSeadragon.Viewer} Chainable. * @returns {OpenSeadragon.Viewer} Chainable.
@ -2733,7 +2815,7 @@ function onCanvasKeyDown( event ) {
if ( !canvasKeyDownEventArgs.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { if ( !canvasKeyDownEventArgs.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch( event.keyCode ){ switch( event.keyCode ){
case 38://up arrow case 38://up arrow/shift uparrow
if (!canvasKeyDownEventArgs.preventVerticalPan) { if (!canvasKeyDownEventArgs.preventVerticalPan) {
if ( event.shift ) { if ( event.shift ) {
this.viewport.zoomBy(1.1); this.viewport.zoomBy(1.1);
@ -2744,7 +2826,7 @@ function onCanvasKeyDown( event ) {
} }
event.preventDefault = true; event.preventDefault = true;
break; break;
case 40://down arrow case 40://down arrow/shift downarrow
if (!canvasKeyDownEventArgs.preventVerticalPan) { if (!canvasKeyDownEventArgs.preventVerticalPan) {
if ( event.shift ) { if ( event.shift ) {
this.viewport.zoomBy(0.9); this.viewport.zoomBy(0.9);
@ -2769,35 +2851,12 @@ function onCanvasKeyDown( event ) {
} }
event.preventDefault = true; event.preventDefault = true;
break; break;
default: case 187://=|+
//console.log( 'navigator keycode %s', event.keyCode );
event.preventDefault = false;
break;
}
} else {
event.preventDefault = false;
}
}
function onCanvasKeyPress( event ) {
var canvasKeyPressEventArgs = {
originalEvent: event.originalEvent,
preventDefaultAction: false,
preventVerticalPan: event.preventVerticalPan || !this.panVertical,
preventHorizontalPan: event.preventHorizontalPan || !this.panHorizontal
};
// This event is documented in onCanvasKeyDown
this.raiseEvent('canvas-key', canvasKeyPressEventArgs);
if ( !canvasKeyPressEventArgs.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch( event.keyCode ){
case 43://=|+
case 61://=|+
this.viewport.zoomBy(1.1); this.viewport.zoomBy(1.1);
this.viewport.applyConstraints(); this.viewport.applyConstraints();
event.preventDefault = true; event.preventDefault = true;
break; break;
case 45://-|_ case 189://-|_
this.viewport.zoomBy(0.9); this.viewport.zoomBy(0.9);
this.viewport.applyConstraints(); this.viewport.applyConstraints();
event.preventDefault = true; event.preventDefault = true;
@ -2807,74 +2866,71 @@ function onCanvasKeyPress( event ) {
this.viewport.applyConstraints(); this.viewport.applyConstraints();
event.preventDefault = true; event.preventDefault = true;
break; break;
case 119://w case 87://W/w
case 87://W if (!canvasKeyDownEventArgs.preventVerticalPan) {
if (!canvasKeyPressEventArgs.preventVerticalPan) {
if ( event.shift ) { if ( event.shift ) {
this.viewport.zoomBy(1.1); this.viewport.zoomBy(1.1);
} else { } else {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, -40))); this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, -40)));
} }
this.viewport.applyConstraints(); this.viewport.applyConstraints();
}
event.preventDefault = true;
break;
case 115://s
case 83://S
if (!canvasKeyPressEventArgs.preventVerticalPan) {
if ( event.shift ) {
this.viewport.zoomBy(0.9);
} else {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, 40)));
}
this.viewport.applyConstraints();
} }
event.preventDefault = true; event.preventDefault = true;
break; break;
case 97://a case 83://S/s
if (!canvasKeyPressEventArgs.preventHorizontalPan) { if (!canvasKeyDownEventArgs.preventVerticalPan) {
if ( event.shift ) {
this.viewport.zoomBy(0.9);
} else {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, 40)));
}
this.viewport.applyConstraints();
}
event.preventDefault = true;
break;
case 65://a/A
if (!canvasKeyDownEventArgs.preventHorizontalPan) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40, 0))); this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40, 0)));
this.viewport.applyConstraints(); this.viewport.applyConstraints();
} }
event.preventDefault = true; event.preventDefault = true;
break; break;
case 100://d case 68://d/D
if (!canvasKeyPressEventArgs.preventHorizontalPan) { if (!canvasKeyDownEventArgs.preventHorizontalPan) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40, 0))); this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40, 0)));
this.viewport.applyConstraints(); this.viewport.applyConstraints();
} }
event.preventDefault = true; event.preventDefault = true;
break; break;
case 114: //r - clockwise rotation case 82: //r - clockwise rotation/R - counterclockwise rotation
if(this.viewport.flipped){ if(event.shift){
this.viewport.setRotation(this.viewport.getRotation() - this.rotationIncrement); if(this.viewport.flipped){
} else{ this.viewport.setRotation(this.viewport.getRotation() + this.rotationIncrement);
this.viewport.setRotation(this.viewport.getRotation() + this.rotationIncrement); } else{
} this.viewport.setRotation(this.viewport.getRotation() - this.rotationIncrement);
this.viewport.applyConstraints(); }
event.preventDefault = true; }else{
break; if(this.viewport.flipped){
case 82: //R - counterclockwise rotation this.viewport.setRotation(this.viewport.getRotation() - this.rotationIncrement);
if(this.viewport.flipped){ } else{
this.viewport.setRotation(this.viewport.getRotation() + this.rotationIncrement); this.viewport.setRotation(this.viewport.getRotation() + this.rotationIncrement);
} else{ }
this.viewport.setRotation(this.viewport.getRotation() - this.rotationIncrement); }
} this.viewport.applyConstraints();
this.viewport.applyConstraints(); event.preventDefault = true;
event.preventDefault = true; break;
break; case 70: //f/F
case 102: //f this.viewport.toggleFlip();
this.viewport.toggleFlip(); event.preventDefault = true;
event.preventDefault = true; break;
break; case 74: //j - previous image source
case 106: //j - previous image source this.goToPreviousPage();
this.goToPreviousPage(); break;
break; case 75: //k - next image source
case 107: //k - next image source this.goToNextPage();
this.goToNextPage(); break;
break;
default: default:
// console.log( 'navigator keycode %s', event.keyCode ); //console.log( 'navigator keycode %s', event.keyCode );
event.preventDefault = false; event.preventDefault = false;
break; break;
} }
@ -2883,7 +2939,24 @@ function onCanvasKeyPress( event ) {
} }
} }
function onCanvasKeyPress( event ) {
var canvasKeyPressEventArgs = {
originalEvent: event.originalEvent,
};
/**
* Raised when a keyboard key is pressed and the focus is on the {@link OpenSeadragon.Viewer#canvas} element.
*
* @event canvas-key-press
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent('canvas-key-press', canvasKeyPressEventArgs);
}
function onCanvasClick( event ) { function onCanvasClick( event ) {
var gestureSettings; var gestureSettings;
@ -3051,17 +3124,16 @@ function onCanvasDrag( event ) {
this.viewport.centerSpringX.target.value += delta.x; this.viewport.centerSpringX.target.value += delta.x;
this.viewport.centerSpringY.target.value += delta.y; this.viewport.centerSpringY.target.value += delta.y;
var bounds = this.viewport.getBounds();
var constrainedBounds = this.viewport.getConstrainedBounds(); var constrainedBounds = this.viewport.getConstrainedBounds();
this.viewport.centerSpringX.target.value -= delta.x; this.viewport.centerSpringX.target.value -= delta.x;
this.viewport.centerSpringY.target.value -= delta.y; this.viewport.centerSpringY.target.value -= delta.y;
if (bounds.x !== constrainedBounds.x) { if (constrainedBounds.xConstrained) {
event.delta.x = 0; event.delta.x = 0;
} }
if (bounds.y !== constrainedBounds.y) { if (constrainedBounds.yConstrained) {
event.delta.y = 0; event.delta.y = 0;
} }
} }
@ -3396,11 +3468,49 @@ function onCanvasPinch( event ) {
event.gesturePoints[0].currentPos.x - event.gesturePoints[1].currentPos.x); event.gesturePoints[0].currentPos.x - event.gesturePoints[1].currentPos.x);
var angle2 = Math.atan2(event.gesturePoints[0].lastPos.y - event.gesturePoints[1].lastPos.y, var angle2 = Math.atan2(event.gesturePoints[0].lastPos.y - event.gesturePoints[1].lastPos.y,
event.gesturePoints[0].lastPos.x - event.gesturePoints[1].lastPos.x); event.gesturePoints[0].lastPos.x - event.gesturePoints[1].lastPos.x);
this.viewport.setRotation(this.viewport.getRotation() + ((angle1 - angle2) * (180 / Math.PI))); centerPt = this.viewport.pointFromPixel( event.center, true );
this.viewport.rotateTo(this.viewport.getRotation(true) + ((angle1 - angle2) * (180 / Math.PI)), centerPt, true);
} }
} }
} }
function onCanvasFocus( event ) {
/**
* Raised when the {@link OpenSeadragon.Viewer#canvas} element gets keyboard focus.
*
* @event canvas-focus
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-focus', {
tracker: event.eventSource,
originalEvent: event.originalEvent
});
}
function onCanvasBlur( event ) {
/**
* Raised when the {@link OpenSeadragon.Viewer#canvas} element loses keyboard focus.
*
* @event canvas-blur
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-blur', {
tracker: event.eventSource,
originalEvent: event.originalEvent
});
}
function onCanvasScroll( event ) { function onCanvasScroll( event ) {
var canvasScrollEventArgs, var canvasScrollEventArgs,
gestureSettings, gestureSettings,
@ -3550,6 +3660,27 @@ function updateMulti( viewer ) {
} }
} }
function doViewerResize(viewer, containerSize){
var viewport = viewer.viewport;
var zoom = viewport.getZoom();
var center = viewport.getCenter();
viewport.resize(containerSize, viewer.preserveImageSizeOnResize);
viewport.panTo(center, true);
var resizeRatio;
if (viewer.preserveImageSizeOnResize) {
resizeRatio = THIS[viewer.hash].prevContainerSize.x / containerSize.x;
} else {
var origin = new $.Point(0, 0);
var prevDiag = new $.Point(THIS[viewer.hash].prevContainerSize.x, THIS[viewer.hash].prevContainerSize.y).distanceTo(origin);
var newDiag = new $.Point(containerSize.x, containerSize.y).distanceTo(origin);
resizeRatio = newDiag / prevDiag * THIS[viewer.hash].prevContainerSize.x / containerSize.x;
}
viewport.zoomTo(zoom * resizeRatio, null, true);
THIS[viewer.hash].prevContainerSize = containerSize;
THIS[viewer.hash].forceRedraw = true;
THIS[viewer.hash].needsResize = false;
THIS[viewer.hash].forceResize = false;
}
function updateOnce( viewer ) { function updateOnce( viewer ) {
//viewer.profiler.beginUpdate(); //viewer.profiler.beginUpdate();
@ -3557,30 +3688,23 @@ function updateOnce( viewer ) {
if (viewer._opening || !THIS[viewer.hash]) { if (viewer._opening || !THIS[viewer.hash]) {
return; return;
} }
if (viewer.autoResize || THIS[viewer.hash].forceResize){
if (viewer.autoResize) { var containerSize;
var containerSize = _getSafeElemSize(viewer.container); if(viewer._autoResizePolling){
var prevContainerSize = THIS[viewer.hash].prevContainerSize; containerSize = _getSafeElemSize(viewer.container);
if (!containerSize.equals(prevContainerSize)) { var prevContainerSize = THIS[viewer.hash].prevContainerSize;
var viewport = viewer.viewport; if (!containerSize.equals(prevContainerSize)) {
if (viewer.preserveImageSizeOnResize) { THIS[viewer.hash].needsResize = true;
var resizeRatio = prevContainerSize.x / containerSize.x;
var zoom = viewport.getZoom() * resizeRatio;
var center = viewport.getCenter();
viewport.resize(containerSize, false);
viewport.zoomTo(zoom, null, true);
viewport.panTo(center, true);
} else {
// maintain image position
var oldBounds = viewport.getBounds();
viewport.resize(containerSize, true);
viewport.fitBoundsWithConstraints(oldBounds, true);
} }
THIS[viewer.hash].prevContainerSize = containerSize;
THIS[viewer.hash].forceRedraw = true;
} }
if(THIS[viewer.hash].needsResize){
doViewerResize(viewer, containerSize || _getSafeElemSize(viewer.container));
}
} }
var viewportChange = viewer.viewport.update(); var viewportChange = viewer.viewport.update();
var animated = viewer.world.update() || viewportChange; var animated = viewer.world.update() || viewportChange;

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Viewport * OpenSeadragon - Viewport
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
@ -483,7 +483,7 @@ $.Viewport.prototype = {
); );
newZoomPixel = this._pixelFromPoint(this.zoomPoint, bounds); newZoomPixel = this._pixelFromPoint(this.zoomPoint, bounds);
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel ); deltaZoomPixels = newZoomPixel.minus( oldZoomPixel ).rotate(-this.getRotation(true));
deltaZoomPoints = deltaZoomPixels.divide( this._containerInnerSize.x * zoom ); deltaZoomPoints = deltaZoomPixels.divide( this._containerInnerSize.x * zoom );
return centerTarget.plus( deltaZoomPoints ); return centerTarget.plus( deltaZoomPoints );
@ -514,34 +514,37 @@ $.Viewport.prototype = {
* @param {OpenSeadragon.Rect} bounds * @param {OpenSeadragon.Rect} bounds
* @returns {OpenSeadragon.Rect} constrained bounds. * @returns {OpenSeadragon.Rect} constrained bounds.
*/ */
_applyBoundaryConstraints: function(bounds) { _applyBoundaryConstraints: function(bounds) {
var newBounds = new $.Rect( var newBounds = this.viewportToViewerElementRectangle(bounds).getBoundingBox();
bounds.x, var cb = this.viewportToViewerElementRectangle(this._contentBoundsNoRotate).getBoundingBox();
bounds.y,
bounds.width, var xConstrained = false;
bounds.height); var yConstrained = false;
if (this.wrapHorizontal) { if (this.wrapHorizontal) {
//do nothing //do nothing
} else { } else {
var boundsRight = newBounds.x + newBounds.width; var boundsRight = newBounds.x + newBounds.width;
var contentRight = this._contentBoundsNoRotate.x + this._contentBoundsNoRotate.width; var contentRight = cb.x + cb.width;
var horizontalThreshold, leftDx, rightDx; var horizontalThreshold, leftDx, rightDx;
if (newBounds.width > this._contentBoundsNoRotate.width) { if (newBounds.width > cb.width) {
horizontalThreshold = this.visibilityRatio * this._contentBoundsNoRotate.width; horizontalThreshold = this.visibilityRatio * cb.width;
} else { } else {
horizontalThreshold = this.visibilityRatio * newBounds.width; horizontalThreshold = this.visibilityRatio * newBounds.width;
} }
leftDx = this._contentBoundsNoRotate.x - boundsRight + horizontalThreshold; leftDx = cb.x - boundsRight + horizontalThreshold;
rightDx = contentRight - newBounds.x - horizontalThreshold; rightDx = contentRight - newBounds.x - horizontalThreshold;
if (horizontalThreshold > this._contentBoundsNoRotate.width) { if (horizontalThreshold > cb.width) {
newBounds.x += (leftDx + rightDx) / 2; newBounds.x += (leftDx + rightDx) / 2;
xConstrained = true;
} else if (rightDx < 0) { } else if (rightDx < 0) {
newBounds.x += rightDx; newBounds.x += rightDx;
xConstrained = true;
} else if (leftDx > 0) { } else if (leftDx > 0) {
newBounds.x += leftDx; newBounds.x += leftDx;
xConstrained = true;
} }
} }
@ -550,28 +553,37 @@ $.Viewport.prototype = {
//do nothing //do nothing
} else { } else {
var boundsBottom = newBounds.y + newBounds.height; var boundsBottom = newBounds.y + newBounds.height;
var contentBottom = this._contentBoundsNoRotate.y + this._contentBoundsNoRotate.height; var contentBottom = cb.y + cb.height;
var verticalThreshold, topDy, bottomDy; var verticalThreshold, topDy, bottomDy;
if (newBounds.height > this._contentBoundsNoRotate.height) { if (newBounds.height > cb.height) {
verticalThreshold = this.visibilityRatio * this._contentBoundsNoRotate.height; verticalThreshold = this.visibilityRatio * cb.height;
} else{ } else{
verticalThreshold = this.visibilityRatio * newBounds.height; verticalThreshold = this.visibilityRatio * newBounds.height;
} }
topDy = this._contentBoundsNoRotate.y - boundsBottom + verticalThreshold; topDy = cb.y - boundsBottom + verticalThreshold;
bottomDy = contentBottom - newBounds.y - verticalThreshold; bottomDy = contentBottom - newBounds.y - verticalThreshold;
if (verticalThreshold > this._contentBoundsNoRotate.height) { if (verticalThreshold > cb.height) {
newBounds.y += (topDy + bottomDy) / 2; newBounds.y += (topDy + bottomDy) / 2;
yConstrained = true;
} else if (bottomDy < 0) { } else if (bottomDy < 0) {
newBounds.y += bottomDy; newBounds.y += bottomDy;
yConstrained = true;
} else if (topDy > 0) { } else if (topDy > 0) {
newBounds.y += topDy; newBounds.y += topDy;
yConstrained = true;
} }
} }
return newBounds; var constraintApplied = xConstrained || yConstrained;
var newViewportBounds = constraintApplied ? this.viewerElementToViewportRectangle(newBounds) : bounds.clone();
newViewportBounds.xConstrained = xConstrained;
newViewportBounds.yConstrained = yConstrained;
newViewportBounds.constraintApplied = constraintApplied;
return newViewportBounds;
}, },
/** /**
@ -605,7 +617,7 @@ $.Viewport.prototype = {
* @function * @function
* @param {Boolean} [immediately=false] * @param {Boolean} [immediately=false]
* @returns {OpenSeadragon.Viewport} Chainable. * @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:constrain * @fires OpenSeadragon.Viewer.event:constrain if constraints were applied
*/ */
applyConstraints: function(immediately) { applyConstraints: function(immediately) {
var actualZoom = this.getZoom(); var actualZoom = this.getZoom();
@ -615,17 +627,13 @@ $.Viewport.prototype = {
this.zoomTo(constrainedZoom, this.zoomPoint, immediately); this.zoomTo(constrainedZoom, this.zoomPoint, immediately);
} }
var bounds = this.getBoundsNoRotate(); var constrainedBounds = this.getConstrainedBounds(false);
var constrainedBounds = this._applyBoundaryConstraints(bounds);
this._raiseConstraintsEvent(immediately);
if (bounds.x !== constrainedBounds.x || if(constrainedBounds.constraintApplied){
bounds.y !== constrainedBounds.y || this.fitBounds(constrainedBounds, immediately);
immediately) { this._raiseConstraintsEvent(immediately);
this.fitBounds(
constrainedBounds.rotate(-this.getRotation(true)),
immediately);
} }
return this; return this;
}, },
@ -675,45 +683,53 @@ $.Viewport.prototype = {
newBounds.y = center.y - newBounds.height / 2; newBounds.y = center.y - newBounds.height / 2;
var newZoom = 1.0 / newBounds.width; var newZoom = 1.0 / newBounds.width;
if (constraints) {
var newBoundsAspectRatio = newBounds.getAspectRatio();
var newConstrainedZoom = this._applyZoomConstraints(newZoom);
if (newZoom !== newConstrainedZoom) {
newZoom = newConstrainedZoom;
newBounds.width = 1.0 / newZoom;
newBounds.x = center.x - newBounds.width / 2;
newBounds.height = newBounds.width / newBoundsAspectRatio;
newBounds.y = center.y - newBounds.height / 2;
}
newBounds = this._applyBoundaryConstraints(newBounds);
center = newBounds.getCenter();
this._raiseConstraintsEvent(immediately);
}
if (immediately) { if (immediately) {
this.panTo(center, true); this.panTo(center, true);
return this.zoomTo(newZoom, null, true); this.zoomTo(newZoom, null, true);
if(constraints){
this.applyConstraints(true);
}
return this;
} }
this.panTo(this.getCenter(true), true); var currentCenter = this.getCenter(true);
this.zoomTo(this.getZoom(true), null, true); var currentZoom = this.getZoom(true);
this.panTo(currentCenter, true);
this.zoomTo(currentZoom, null, true);
var oldBounds = this.getBounds(); var oldBounds = this.getBounds();
var oldZoom = this.getZoom(); var oldZoom = this.getZoom();
if (oldZoom === 0 || Math.abs(newZoom / oldZoom - 1) < 0.00000001) { if (oldZoom === 0 || Math.abs(newZoom / oldZoom - 1) < 0.00000001) {
this.zoomTo(newZoom, true); this.zoomTo(newZoom, null, true);
return this.panTo(center, immediately); this.panTo(center, immediately);
if(constraints){
this.applyConstraints(false);
}
return this;
} }
newBounds = newBounds.rotate(-this.getRotation()); if(constraints){
var referencePoint = newBounds.getTopLeft().times(newZoom) this.panTo(center, false);
.minus(oldBounds.getTopLeft().times(oldZoom))
.divide(newZoom - oldZoom);
return this.zoomTo(newZoom, referencePoint, immediately); newZoom = this._applyZoomConstraints(newZoom);
this.zoomTo(newZoom, null, false);
var constrainedBounds = this.getConstrainedBounds();
this.panTo(currentCenter, true);
this.zoomTo(currentZoom, null, true);
this.fitBounds(constrainedBounds);
} else {
var rotatedNewBounds = newBounds.rotate(-this.getRotation());
var referencePoint = rotatedNewBounds.getTopLeft().times(newZoom)
.minus(oldBounds.getTopLeft().times(oldZoom))
.divide(newZoom - oldZoom);
this.zoomTo(newZoom, referencePoint, immediately);
}
return this;
}, },
/** /**
@ -787,7 +803,10 @@ $.Viewport.prototype = {
* Returns bounds taking constraints into account * Returns bounds taking constraints into account
* Added to improve constrained panning * Added to improve constrained panning
* @param {Boolean} current - Pass true for the current location; defaults to false (target location). * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
* @returns {OpenSeadragon.Viewport} Chainable. * @returns {OpenSeadragon.Rect} The bounds in viewport coordinates after applying constraints. The returned $.Rect
* contains additional properties constraintsApplied, xConstrained and yConstrained.
* These flags indicate whether the viewport bounds were modified by the constraints
* of the viewer rectangle, and in which dimension(s).
*/ */
getConstrainedBounds: function(current) { getConstrainedBounds: function(current) {
var bounds, var bounds,
@ -1059,7 +1078,10 @@ $.Viewport.prototype = {
if( this.viewer ){ if( this.viewer ){
/** /**
* Raised when the viewer is resized (see {@link OpenSeadragon.Viewport#resize}). * Raised when a viewer resize operation is initiated (see {@link OpenSeadragon.Viewport#resize}).
* This event happens before the viewport bounds have been updated.
* See also {@link OpenSeadragon.Viewer#after-resize} which reflects
* the new viewport bounds following the resize action.
* *
* @event resize * @event resize
* @memberof OpenSeadragon.Viewer * @memberof OpenSeadragon.Viewer
@ -1075,7 +1097,29 @@ $.Viewport.prototype = {
}); });
} }
return this.fitBounds( newBounds, true ); var output = this.fitBounds( newBounds, true );
if( this.viewer ){
/**
* Raised after the viewer is resized (see {@link OpenSeadragon.Viewport#resize}).
* See also {@link OpenSeadragon.Viewer#resize} event which happens
* before the new bounds have been calculated and applied.
*
* @event after-resize
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.Point} newContainerSize
* @property {Boolean} maintain
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'after-resize', {
newContainerSize: newContainerSize,
maintain: maintain
});
}
return output;
}, },
// private // private

View File

@ -2,7 +2,7 @@
* OpenSeadragon - World * OpenSeadragon - World
* *
* Copyright (C) 2009 CodePlex Foundation * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2022 OpenSeadragon contributors * Copyright (C) 2010-2023 OpenSeadragon contributors
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are

View File

@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>OpenSeadragon QUnit</title> <title>OpenSeadragon QUnit</title>
<link rel="stylesheet" href="/node_modules/qunitjs/qunit/qunit.css"> <link rel="stylesheet" href="/node_modules/qunit/qunit/qunit.css">
<link rel="stylesheet" href="/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css"> <link rel="stylesheet" href="/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
<link rel="stylesheet" href="/test/helpers/test.css"> <link rel="stylesheet" href="/test/helpers/test.css">
</head> </head>
@ -15,7 +15,7 @@
var isCoverageTest = true; var isCoverageTest = true;
</script> </script>
<script src="/node_modules/qunitjs/qunit/qunit.js"></script> <script src="/node_modules/qunit/qunit/qunit.js"></script>
<script src="/test/lib/jquery-1.9.1.min.js"></script> <script src="/test/lib/jquery-1.9.1.min.js"></script>
<script src="/test/lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js"></script> <script src="/test/lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js"></script>
<script src="/test/lib/jquery.simulate.js"></script> <script src="/test/lib/jquery.simulate.js"></script>
@ -65,6 +65,7 @@
<!-- Polyfill must be inserted first because it is testing functions <!-- Polyfill must be inserted first because it is testing functions
reassignments which could be done by other test. --> reassignments which could be done by other test. -->
<script src="/test/modules/polyfills.js"></script> <script src="/test/modules/polyfills.js"></script>
<script src="/test/modules/event-source.js"></script>
<script src="/test/modules/basic.js"></script> <script src="/test/modules/basic.js"></script>
<script src="/test/modules/strings.js"></script> <script src="/test/modules/strings.js"></script>
<script src="/test/modules/formats.js"></script> <script src="/test/modules/formats.js"></script>

View File

@ -18,7 +18,8 @@
"jpg", "jpg",
"png", "png",
"gif" "gif"
] ]
} }
] ],
"xmp": "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n <rdf:Description rdf:about=\"\"\n xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"\n xmlns:aux=\"http://ns.adobe.com/exif/1.0/aux/\"\n xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\"\n xmlns:Iptc4xmpCore=\"http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/\"\n xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"\n xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"\n xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n xmp:CreatorTool=\"Capture One 11 Macintosh\"\n xmp:CreateDate=\"2019-08-21T20:54:20\"\n xmp:Rating=\"4\"\n aux:SerialNumber=\"GP001601\"\n aux:ImageNumber=\"30012\"\n aux:Firmware=\"IQ280, Factory Firmware: 8.05.36\"\n photoshop:CaptionWriter=\"\"\n photoshop:Headline=\"\"\n photoshop:City=\"\"\n photoshop:State=\"\"\n photoshop:Country=\"\"\n photoshop:Source=\"\"\n photoshop:Instructions=\"\"\n photoshop:AuthorsPosition=\"\"\n photoshop:TransmissionReference=\"\"\n photoshop:Credit=\"\"\n photoshop:LegacyIPTCDigest=\"B135BFFD6A4CFB049F834A3F370A9AE3\"\n photoshop:DateCreated=\"2019-08-21T20:54:20\"\n Iptc4xmpCore:Location=\"\"\n Iptc4xmpCore:IntellectualGenre=\"\"\n Iptc4xmpCore:CountryCode=\"\"\n tiff:ImageWidth=\"2088\"\n tiff:ImageLength=\"3000\"\n tiff:Compression=\"1\"\n tiff:PhotometricInterpretation=\"2\"\n tiff:Orientation=\"1\"\n tiff:SamplesPerPixel=\"3\"\n tiff:PlanarConfiguration=\"1\"\n tiff:XResolution=\"400/1\"\n tiff:YResolution=\"400/1\"\n tiff:ResolutionUnit=\"2\"\n tiff:Make=\"Phase One\"\n tiff:Model=\"IQ280\"\n tiff:Software=\"Capture One 11 Macintosh\"\n exif:ExifVersion=\"0230\"\n exif:PixelXDimension=\"2088\"\n exif:PixelYDimension=\"3000\"\n exif:ExposureTime=\"1/4\"\n exif:ExposureProgram=\"1\"\n exif:ShutterSpeedValue=\"2/1\"\n exif:MeteringMode=\"1\"\n exif:LightSource=\"255\"\n exif:FocalPlaneXResolution=\"63015385/32768\"\n exif:FocalPlaneYResolution=\"63015385/32768\"\n exif:FocalPlaneResolutionUnit=\"3\"\n exif:FileSource=\"3\"\n exif:SceneType=\"1\"\n exif:WhiteBalance=\"5\"\n exif:ImageUniqueID=\"00E058000075000004019E064100753C\">\n <Iptc4xmpCore:CreatorContactInfo\n Iptc4xmpCore:CiEmailWork=\"digicc@library.illinois.edu\"\n Iptc4xmpCore:CiTelWork=\"+1(217)2442062\"\n Iptc4xmpCore:CiAdrPcode=\"61801\"\n Iptc4xmpCore:CiUrlWork=\"http://www.library.illinois.edu/staff/preservation/digitization/\"\n Iptc4xmpCore:CiAdrExtadr=\"1408 W. Gregory Drive\"\n Iptc4xmpCore:CiAdrCity=\"Urbana\"\n Iptc4xmpCore:CiAdrCtry=\"United States\"\n Iptc4xmpCore:CiAdrRegion=\"Illinois\"/>\n <tiff:BitsPerSample>\n <rdf:Seq>\n <rdf:li>8 8 8</rdf:li>\n </rdf:Seq>\n </tiff:BitsPerSample>\n <exif:ISOSpeedRatings>\n <rdf:Seq>\n <rdf:li>50</rdf:li>\n </rdf:Seq>\n </exif:ISOSpeedRatings>\n <dc:creator>\n <rdf:Seq>\n <rdf:li>University of Illinois Library</rdf:li>\n </rdf:Seq>\n </dc:creator>\n </rdf:Description>\n </rdf:RDF>"
} }

View File

@ -25,7 +25,7 @@
id: "contentDiv", id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/", prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/testpattern.dzi", tileSources: "../data/testpattern.dzi",
showNavigator:true showNavigator: true
}); });
</script> </script>
</body> </body>

View File

@ -3,111 +3,163 @@
<head> <head>
<title>OpenSeadragon fitBoundsWithConstraints() Demo</title> <title>OpenSeadragon fitBoundsWithConstraints() Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script> <script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
<style type="text/css"> <style type="text/css">
.openseadragon1 { .openseadragon1 {
width: 800px; width: 800px;
height: 600px; height: 500px;
border:thin black solid;
margin-right:20px;
} }
#buttons button{
#highlights li { width:10em;
cursor: pointer; text-align:center;
margin:5px;
}
.layout{
display:grid;
grid-template-columns:auto 1fr;
padding:10px;
}
.method{
border:medium gray solid;
margin:2px;
background-color:rgb(240, 240, 240)
}
.method.selected{
border:medium red solid;
background-color: lightgoldenrodyellow;
} }
</style> </style>
</head> </head>
<body> <body>
<div> <div class="layout">
Simple demo page to show 'viewport.fitBounds().applyConstraints()' issue. <div id="contentDiv" class="openseadragon1"></div>
<div id="controls">
<div>
Simple demo page to show viewport.fitBounds() with and without constraints. The viewer
is set up with visibilityRatio = 1 and constrainDuringPan = true to clearly demonstrate the
constraints.
</div>
<h3>Pick a method to use:</h3>
<div>
<div class="method selected" data-value="0">
<pre>viewport.fitBounds(bounds); //Ignores constraints</pre>
</div>
<div class="method" data-value="1">
<pre>viewport.fitBoundsWithConstraints(bounds);</pre>
</div>
<div class="method" data-value="4">
<pre>viewport.fitBoundsWithConstraints(bounds, true); //immediate</pre>
</div>
<div class="method" data-value="2">
<pre>//Initially ignore constraints
viewport.fitBounds(bounds);
//Apply constraints after 1 second delay
setTimeout(() => viewport.applyConstraints(), 1000);</pre>
</div>
</div>
<button id="rotate">Rotate the viewer</button>
<h3>Click to fit overlay bounds:</h3>
<div id="buttons"></div>
<h4>overlay.getBounds(viewer.viewport):</h4>
<pre class="bounds">Pick an overlay above to show the bounds</pre>
</div>
</div> </div>
<div id="contentDiv" class="openseadragon1"></div>
<div id="highlights"></div>
<select onchange="changeMethod(this.value);">
<option value=0>viewport.fitBoundsWithConstraints(bounds);</option>
<option value=1>viewport.fitBounds(bounds);</option>
<option value=2>viewport.fitBounds(bounds).applyConstraints();</option>
</select>
<input type="button" value="Go home" onclick="goHome()"/>
<script type="text/javascript"> <script type="text/javascript">
var _viewer; var viewer;
var _fittingMethod = 0; var _fittingMethod = 0;
viewer = window.viewer = OpenSeadragon({
var _highlights = [ id: "contentDiv",
{"queryPoint":[0.13789887359998443,0.43710575899579285], "radius":0.004479581945070337,"text":"Pipe"},
{"queryPoint":[0.5923298766583593,0.6461653354541856], "radius":0.013175241014912752,"text":"Fuel here"},
{"queryPoint":[0.43920338711232304,0.7483181389302148], "radius":0.09222668710438928, "text":"Wheel"},
{"queryPoint":[0.07341677959486298,0.9028719921872319], "radius":0.08996845561083797, "text":"Nothing special"}
];
var generateUniqueHash = (function() {
var counter = 0;
return function() {
return "openseadragon_" + (counter++);
};
})();
var _viewer = OpenSeadragon({
element: document.getElementById("contentDiv"),
showNavigationControl: false,
prefixUrl: "../../build/openseadragon/images/", prefixUrl: "../../build/openseadragon/images/",
hash: generateUniqueHash(), //this is only needed if you want to instantiate more than one viewer at a time. tileSources: "../data/testpattern.dzi",
tileSources: { minZoomImageRatio: 0,
Image: { maxZoomPixelRatio: 10,
xmlns: "http://schemas.microsoft.com/deepzoom/2008", visibilityRatio:1,
Url: 'http://cdn.photosynth.net/ps2/19d5cf2b-77ed-439f-ac21-d3046320384c/packet/undistorted/img0043/', constrainDuringPan:true,
Format: "jpg", });
Overlap: 1,
TileSize: 510, viewer.addHandler("open", function(event) {
Size: { var elt = document.createElement("div");
Width: 4592, elt.className = "runtime-overlay";
Height: 3448 elt.style.background = "green";
elt.style.outline = "3px solid red";
elt.style.opacity = "0.7";
elt.textContent = "Within the image";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Rect(0.21, 0.21, 0.099, 0.299),
rotationMode: OpenSeadragon.OverlayRotationMode.BOUNDING_BOX
});
elt = document.createElement("div");
elt.className = "runtime-overlay";
elt.style.background = "white";
elt.style.opacity = "0.5";
elt.style.outline = "5px solid pink";
elt.textContent = "Left edge rectangle";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Rect(-0.4, 0.7, 0.7, 0.15)
});
var elt = document.createElement("div");
elt.className = "runtime-overlay";
elt.style.background = "lightblue";
elt.style.outline = "3px solid purple";
elt.style.opacity = "0.7";
elt.textContent = "Top right square";
viewer.addOverlay({
element: elt,
location: new OpenSeadragon.Rect(0.9, -0.1, 0.2, 0.2),
rotationMode: OpenSeadragon.OverlayRotationMode.BOUNDING_BOX
});
viewer.currentOverlays.forEach(overlay=>{
var text = $(overlay.element).text();
var div=$('<div>').appendTo('#buttons');
var buttons=$('<button>').text(text).appendTo(div).on('click',()=>{
var bounds = overlay.getBounds(viewer.viewport);
$('.bounds').text(JSON.stringify(bounds,null,2));
var _fittingMethod = parseInt($('.method.selected').data('value'));
if (_fittingMethod === 0) {
viewer.viewport.fitBounds(bounds, false);
} }
} else if (_fittingMethod === 1) {
} viewer.viewport.fitBoundsWithConstraints(bounds, false);
}
else if (_fittingMethod === 4) {
viewer.viewport.fitBoundsWithConstraints(bounds, true);
}
else if (_fittingMethod === 2) {
viewer.viewport.fitBounds(bounds, false);
setTimeout(()=>viewer.viewport.applyConstraints(), 1000);
}
});
})
viewer.viewport.zoomTo(0.5, null, true);
}); });
_viewer.addHandler("open", function() { $('.method').on('click',function(){
var str = "<ul>"; $('.method').removeClass('selected');
for (var i=0; i<_highlights.length; ++i) { $(this).addClass('selected');
var highlight = _highlights[i]; })
str += "<li onclick='gotoHighlight("+i+")'>"+highlight.text+"</li>"; $("#rotate").click(function() {
} viewer.viewport.setRotation(viewer.viewport.getRotation() - 22.5);
str += "</ul>"; $("#degrees").text(viewer.viewport.getRotation() + "deg");
document.getElementById("highlights").innerHTML = str;
}); });
function gotoHighlight(index) {
var highlight = _highlights[index];
var viewport = _viewer.viewport;
var contentSize = viewport.contentSize;
var scaling = 1.0 / viewport.viewportToImageZoom(viewport.getZoom());
var radius = highlight.radius*Math.min(contentSize.x, contentSize.y);/*annotation.accurateRadius*scaling;*/
var center = new OpenSeadragon.Point(contentSize.x*highlight.queryPoint[0], contentSize.y*highlight.queryPoint[1]);
var bounds = viewport.imageToViewportRectangle(new OpenSeadragon.Rect(center.x-radius, center.y-radius, radius*2, radius*2));
if (_fittingMethod === 0) {
viewport.fitBoundsWithConstraints(bounds, false);
}
else if (_fittingMethod === 1) {
viewport.fitBounds(bounds, false);
}
else if (_fittingMethod === 2) {
viewport.fitBounds(bounds, false).applyConstraints();
}
}
function changeMethod(value) {
_fittingMethod = parseInt(value, 10);
}
function goHome() {
_viewer.viewport.goHome();
}
</script> </script>
</body> </body>
</html> </html>

236
test/demo/resizeviewer.html Normal file
View File

@ -0,0 +1,236 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Viewer Resizing Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
<script type="text/javascript" src='../lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js'></script>
<link rel="stylesheet" href="../lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
<style type="text/css">
.outer-container{
width:800px;
height:600px;
margin-right:20px;
border: medium gray dashed;
background-color:beige;
position:relative;
display:flex;
flex-direction:row;
}
.inner-container{
width:800px;
height:600px;
border: thin black solid;
background-color:paleturquoise;
position:absolute;
left:50%;
top: 50%;
transform: translate(-50%, -50%);
}
.v2-container{
display:flex;
flex-direction:row;
height:400px;
}
.narrow{
width:50px;
background-color:pink;
height:100%;
}
.wide{
width:150px;
background-color:burlywood;
height:100%;
}
.v2-container:not([data-index="1"]) .narrow{
display:none;
}
.v2-container:not([data-index="2"]) .wide{
display:none;
}
#viewer, #viewer2 {
width: 100%;
height: 100%;
}
#buttons button{
width:18em;
text-align:center;
margin:5px;
}
.layout{
display:grid;
grid-template-columns:auto 1fr;
row-gap:10px;
padding:10px;
}
.method{
border:medium gray solid;
margin:2px;
background-color:rgb(240, 240, 240)
}
.method.selected{
border:medium red solid;
background-color: lightgoldenrodyellow;
}
.options{
display:grid;
grid-template-columns: 50% 50%;
row-gap:10px;
}
</style>
</head>
<body>
<div class="layout">
<div class="outer-container">
<div class="inner-container">
<div id="viewer"></div>
</div>
</div>
<div id="controls">
<div>
Simple demo page to show viewer behavior during resizing of the container.
The viewers' container elements are styled with width and height of 100%,
with dimensions set by CSS properties on a parent element.
</div>
<h3>Pick options to test:</h3>
<p>These options apply to both of the demo viewers on the left (top and bottom).</p>
<div class="options">
<div class="method preserve-method selected" data-value="0">
<pre>preserveImageSizeOnResize: false</pre>
</div>
<div class="method preserve-method" data-value="1">
<pre>preserveImageSizeOnResize: true</pre>
</div>
<div class="method auto-method selected" data-value="1">
<pre>autoResize: true</pre>
</div>
<div class="method auto-method" data-value="0">
<pre>autoResize: false</pre>
</div>
</div>
<h3>Click to resize the viewer:</h3>
<div id="buttons">
<div>
<button>Resize width only</button>
</div>
<div>
<button>Resize height only</button>
</div>
<div>
<button>Resize with constant aspect ratio</button>
</div>
<div>
<button>Toggle resizable inner container</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var viewer = window.v1 = OpenSeadragon({
id: "viewer",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/iiif_2_0_sizes/info.json",
minZoomImageRatio: 0,
maxZoomPixelRatio: 10,
visibilityRatio:0.5,
constrainDuringPan:false,
showNavigator: true,
});
updateViewer();
var buttons=$('#buttons button').on('click',function(){
switch($(this).text()){
case 'Resize width only': resizeWidth(this); break;
case 'Resize height only': resizeHeight(this); break;
case 'Resize with constant aspect ratio': resizeBoth(this); break;
case 'Toggle resizable inner container': toggleResizable(this); break;
}
});
function updateViewer(){
viewer.preserveImageSizeOnResize = !!parseInt($('.preserve-method.selected').data('value'));
viewer.autoResize = !!parseInt($('.auto-method.selected').data('value'));
}
$('.preserve-method').on('click',function(){
$('.preserve-method').removeClass('selected');
$(this).addClass('selected');
});
$('.auto-method').on('click',function(){
$('.auto-method').removeClass('selected');
$(this).addClass('selected');
});
$('.method').on('click',updateViewer);
var container = $('.inner-container');
function resizeWidth(b){
if(container.height() !== 600) return;
if(container.width()==800){
container.width(600);
$('#buttons button').prop('disabled', true);
$(b).prop('disabled', false);
} else {
container.width(800);
$('#buttons button').prop('disabled', false);
}
}
function resizeHeight(b){
if(container.width() !== 800) return;
if(container.height()==600){
container.height(450);
$('#buttons button').prop('disabled', true);
$(b).prop('disabled', false);
} else {
container.height(600);
$('#buttons button').prop('disabled', false);
}
}
function resizeBoth(b){
if(container.height()==600){
container.width(600);
container.height(450);
$('#buttons button').prop('disabled', true);
$(b).prop('disabled', false);
} else {
container.width(800);
container.height(600);
$('#buttons button').prop('disabled', false);
}
}
function toggleResizable(b){
if(container.isResizable){
container.isResizable = false;
container.resizable('destroy');
container.width(800);
container.height(600);
$('#buttons button').prop('disabled', false);
} else {
container.isResizable = true;
// container.resizable({containment:"parent", maxWidth: 800, maxHeight: 600,});
container.resizable();
$('#buttons button').prop('disabled', true);
container.width(800);
container.height(600);
$(b).prop('disabled', false);
}
}
</script>
</body>
</html>

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Basic Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
<style type="text/css">
.openseadragon1 {
width: 800px;
height: 600px;
}
</style>
</head>
<body>
<div>
Simple demo page. The navigator region is expected to rotate when the image is rotated.
The default behaviour is to keep the region still as the image below it rotates.
</div>
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var viewer = OpenSeadragon({
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/testpattern.dzi",
showNavigator: true,
navigatorRotate: false
});
viewer.viewport.setRotation(45)
</script>
</body>
</html>

View File

@ -245,4 +245,130 @@
tileSource: staticHeaderTileSource tileSource: staticHeaderTileSource
}); });
}); });
QUnit.test('Viewer headers can be updated', function(assert) {
var done = assert.async();
var newHeaders = {
'X-Viewer-Header': 'ViewerHeaderValue-Updated',
'X-Viewer-Header2': 'ViewerHeaderValue2'
}
var newHeaders2 = {
Range: 'test',
}
var tileLoaded = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded);
// set new Viewer headers and propagate to TiledImage and Tile
viewer.setAjaxHeaders(newHeaders);
viewer.addHandler('tile-loaded', tileLoaded2);
};
var tileLoaded2 = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded2);
assert.deepEqual(viewer.ajaxHeaders, newHeaders);
assert.deepEqual(evt.tiledImage.ajaxHeaders, newHeaders);
assert.deepEqual(
evt.tile.ajaxHeaders,
OpenSeadragon.extend(
{}, viewer.ajaxHeaders, evt.tiledImage.ajaxHeaders,
{ Range: getTileRangeHeader(evt.tile.level, evt.tile.x, evt.tile.y) }
)
);
// set new Viewer headers and propagate to TiledImage and Tile
viewer.setAjaxHeaders(newHeaders2, true);
viewer.addHandler('tile-loaded', tileLoaded3);
};
var tileLoaded3 = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded3);
assert.deepEqual(viewer.ajaxHeaders, newHeaders2);
assert.deepEqual(evt.tiledImage.ajaxHeaders, newHeaders2);
assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header'], undefined);
assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header2'], undefined);
// 'Range' header entry set per tile and must not be overwritten by Viewer header
assert.equal(evt.tile.ajaxHeaders.Range, getTileRangeHeader(evt.tile.level, evt.tile.x, evt.tile.y));
// set new Viewer headers but do not propagate to TiledImage and Tile
viewer.setAjaxHeaders(newHeaders, false);
viewer.addHandler('tile-loaded', tileLoaded4);
};
var tileLoaded4 = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded4);
assert.deepEqual(viewer.ajaxHeaders, newHeaders);
assert.deepEqual(evt.tiledImage.ajaxHeaders, newHeaders2);
assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header'], undefined);
assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header2'], undefined);
done();
};
viewer.addHandler('tile-loaded', tileLoaded);
viewer.open(customTileSource);
});
QUnit.test('TiledImage headers can be updated', function(assert) {
var done = assert.async();
var tileSourceHeaders = {
'X-Tile-Header': 'TileHeaderValue'
}
var newHeaders = {
'X-TiledImage-Header': 'TiledImageHeaderValue-Updated',
'X-TiledImage-Header2': 'TiledImageHeaderValue2'
}
var newHeaders2 = {
'X-Viewer-Header': 'ViewerHeaderValue-Updated',
'X-Tile-Header': 'TileHeaderValue-Updated'
}
var tileLoaded = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded);
// set new TiledImage headers and propagate to Tile
evt.tiledImage.setAjaxHeaders(newHeaders);
viewer.addHandler('tile-loaded', tileLoaded2);
};
var tileLoaded2 = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded2);
assert.deepEqual(viewer.ajaxHeaders, { 'X-Viewer-Header': 'ViewerHeaderValue' });
assert.deepEqual(evt.tiledImage._ownAjaxHeaders, newHeaders);
assert.deepEqual(evt.tiledImage.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders));
assert.deepEqual(evt.tile.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders, tileSourceHeaders));
// set new TiledImage headers (that overwrite header entries of Viewer and Tile) and propagate to Tile
evt.tiledImage.setAjaxHeaders(newHeaders2, true);
viewer.addHandler('tile-loaded', tileLoaded3);
};
var tileLoaded3 = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded3);
assert.deepEqual(viewer.ajaxHeaders, { 'X-Viewer-Header': 'ViewerHeaderValue' });
assert.deepEqual(evt.tiledImage._ownAjaxHeaders, newHeaders2);
assert.deepEqual(evt.tiledImage.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders2));
assert.deepEqual(evt.tile.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders2, tileSourceHeaders));
// set new TiledImage headers but do not propagate to Tile
evt.tiledImage.setAjaxHeaders(null, false);
viewer.addHandler('tile-loaded', tileLoaded4);
};
var tileLoaded4 = function tileLoaded(evt) {
viewer.removeHandler('tile-loaded', tileLoaded4);
assert.deepEqual(viewer.ajaxHeaders, { 'X-Viewer-Header': 'ViewerHeaderValue' });
assert.deepEqual(evt.tiledImage._ownAjaxHeaders, {});
assert.deepEqual(evt.tiledImage.ajaxHeaders, viewer.ajaxHeaders);
assert.deepEqual(evt.tile.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders2, tileSourceHeaders));
done();
};
viewer.addHandler('tile-loaded', tileLoaded);
viewer.addTiledImage({
ajaxHeaders: {
'X-TiledImage-Header': 'TiledImageHeaderValue'
},
tileSource: OpenSeadragon.extend({}, customTileSource, {
getTileAjaxHeaders: function() {
return tileSourceHeaders;
}
}),
});
});
})(); })();

View File

@ -224,32 +224,43 @@
}); });
QUnit.test('FullScreen', function(assert) { QUnit.test('FullScreen', function(assert) {
var done = assert.async(); const done = assert.async();
if (!OpenSeadragon.supportsFullScreen) { if (!OpenSeadragon.supportsFullScreen) {
assert.expect(0); assert.expect(0);
done(); done();
return; return;
} }
viewer.addHandler("open", function () { viewer.addHandler('open', function () {
assert.ok(!OpenSeadragon.isFullScreen(), 'Started out not fullscreen'); assert.ok(!OpenSeadragon.isFullScreen(), 'Started out not fullscreen');
var checkEnteringPreFullScreen = function(event) { const checkEnteringPreFullScreen = (event) => {
viewer.removeHandler('pre-full-screen', checkEnteringPreFullScreen); viewer.removeHandler('pre-full-screen', checkEnteringPreFullScreen);
assert.ok(event.fullScreen, 'Switching to fullscreen'); assert.ok(event.fullScreen, 'Switching to fullscreen');
assert.ok(!OpenSeadragon.isFullScreen(), 'Not yet fullscreen'); assert.ok(!OpenSeadragon.isFullScreen(), 'Not yet fullscreen');
}; };
// The fullscreen mode is always denied during tests so we are const checkExitingFullScreen = (event) => {
// exiting directly.
var checkExitingFullScreen = function(event) {
viewer.removeHandler('full-screen', checkExitingFullScreen); viewer.removeHandler('full-screen', checkExitingFullScreen);
assert.ok(!event.fullScreen, 'Exiting fullscreen'); assert.ok(!event.fullScreen, 'Disabling fullscreen');
assert.ok(!OpenSeadragon.isFullScreen(), 'Disabled fullscreen'); assert.ok(!OpenSeadragon.isFullScreen(), 'Fullscreen disabled');
done(); done();
}
// The 'new' headless mode allows us to enter fullscreen, so verify
// that we see the correct values returned. We will then close out
// of fullscreen to check the same values when exiting.
const checkAcquiredFullScreen = (event) => {
viewer.removeHandler('full-screen', checkAcquiredFullScreen);
viewer.addHandler('full-screen', checkExitingFullScreen);
assert.ok(event.fullScreen, 'Acquired fullscreen');
assert.ok(OpenSeadragon.isFullScreen(), 'Fullscreen enabled');
viewer.setFullScreen(false);
}; };
viewer.addHandler("pre-full-screen", checkEnteringPreFullScreen);
viewer.addHandler("full-screen", checkExitingFullScreen);
viewer.addHandler('pre-full-screen', checkEnteringPreFullScreen);
viewer.addHandler('full-screen', checkAcquiredFullScreen);
viewer.setFullScreen(true); viewer.setFullScreen(true);
}); });

View File

@ -0,0 +1,85 @@
/* global QUnit, $, TouchUtil, Util, testLog */
(function () {
var context, result=[], eName = "test", eventCounter = 0, finished = false;
function evaluateTest(e) {
if (finished) return;
finished = true;
e.assert.strictEqual(JSON.stringify(result), JSON.stringify(e.expected), e.message);
e.done();
}
function executor(i, ms) {
if (ms === undefined) return function (e) {
eventCounter++;
result.push(i);
if (eventCounter === context.numberOfHandlers(eName)) {
evaluateTest(e);
}
};
return function (e) {
return new Promise(function (resolve) {
setTimeout(function () {
eventCounter++;
result.push(i);
if (eventCounter === context.numberOfHandlers(eName)) {
evaluateTest(e);
}
resolve();
}, ms);
});
}
}
function runTest(e) {
context.raiseEvent(eName, e);
}
QUnit.module( 'EventSource', {
beforeEach: function () {
context = new OpenSeadragon.EventSource();
eventCounter = 0;
result = [];
finished = false;
}
} );
// ----------
QUnit.test('EventSource: no events', function(assert) {
context.addHandler(eName, evaluateTest);
runTest({
assert: assert,
done: assert.async(),
expected: [],
message: 'No handlers registered - arrays should be empty.'
});
});
QUnit.test('EventSource: simple callbacks order', function(assert) {
context.addHandler(eName, executor(1));
context.addHandler(eName, executor(2));
context.addHandler(eName, executor(3));
runTest({
assert: assert,
done: assert.async(),
expected: [1, 2, 3],
message: 'Simple callback order should follow [1,2,3].'
});
});
QUnit.test('EventSource: priority callbacks order', function(assert) {
context.addHandler(eName, executor(1), undefined, 20);
context.addHandler(eName, executor(2), undefined, 124);
context.addHandler(eName, executor(3), undefined, -5);
context.addHandler(eName, executor(4));
context.addHandler(eName, executor(5), undefined, -2);
runTest({
assert: assert,
done: assert.async(),
expected: [2, 1, 4, 5, 3],
message: 'Prioritized callback order should follow [2,1,4,5,3].'
});
});
} )();

View File

@ -248,14 +248,8 @@
}; };
var dragNavigatorBackToCenter = function () { var dragNavigatorBackToCenter = function () {
var start = viewer.viewport.getBounds().getTopLeft(), var delta = viewer.viewport.getHomeBounds().getCenter().minus(viewer.viewport.getCenter()).times(displayRegionWidth);
target = new OpenSeadragon.Point(0.5 - viewer.viewport.getBounds().width / 2, simulateNavigatorDrag(viewer.navigator, delta.x, delta.y);
1 / viewer.source.aspectRatio / 2 - viewer.viewport.getBounds().height / 2),
delta = target.minus(start);
if (viewer.source.aspectRatio < 1) {
delta.y *= viewer.source.aspectRatio;
}
simulateNavigatorDrag(viewer.navigator, delta.x * displayRegionWidth, delta.y * displayRegionHeight);
}; };
var resizeElement = function ($element, width, height) { var resizeElement = function ($element, width, height) {

View File

@ -9,6 +9,7 @@
testLog.reset(); testLog.reset();
// eslint-disable-next-line new-cap
viewer = OpenSeadragon({ viewer = OpenSeadragon({
id: 'example', id: 'example',
prefixUrl: '/build/openseadragon/images/', prefixUrl: '/build/openseadragon/images/',
@ -517,12 +518,17 @@
var image = viewer.world.getItemAt(0); var image = viewer.world.getItemAt(0);
assert.equal(image.getFullyLoaded(), false, 'not fully loaded at first'); assert.equal(image.getFullyLoaded(), false, 'not fully loaded at first');
// Zoom out enough that we don't start out with all the tiles loaded.
viewer.viewport.zoomBy(0.5, null, true);
var count = 0; var count = 0;
var fullyLoadedChangeHandler = function(event) { var fullyLoadedChangeHandler = function(event) {
if (count === 0) { if (count === 0) {
assert.equal(event.fullyLoaded, true, 'event includes true fullyLoaded property'); assert.equal(event.fullyLoaded, true, 'event includes true fullyLoaded property');
assert.equal(image.getFullyLoaded(), true, 'image is fully loaded after event'); assert.equal(image.getFullyLoaded(), true, 'image is fully loaded after event');
// Zoom in enough that it needs to load some new tiles.
viewer.viewport.zoomBy(5, null, true); viewer.viewport.zoomBy(5, null, true);
} else if (count === 1) { } else if (count === 1) {
assert.equal(event.fullyLoaded, false, 'event includes false fullyLoaded property'); assert.equal(event.fullyLoaded, false, 'event includes false fullyLoaded property');

View File

@ -539,7 +539,7 @@
var bounds = viewport.getBounds(); var bounds = viewport.getBounds();
Util.assertRectangleEquals( Util.assertRectangleEquals(
assert, assert,
new OpenSeadragon.Rect(1.2071067811865466, 0.20710678118654746, Math.sqrt(2), Math.sqrt(2), 45), new OpenSeadragon.Rect(1.0, 0.0, Math.sqrt(2), Math.sqrt(2), 45),
bounds, bounds,
EPSILON, EPSILON,
"Viewport.applyConstraints with rotation should move viewport."); "Viewport.applyConstraints with rotation should move viewport.");
@ -564,7 +564,7 @@
var bounds = viewport.getBounds(); var bounds = viewport.getBounds();
Util.assertRectangleEquals( Util.assertRectangleEquals(
assert, assert,
new OpenSeadragon.Rect(1.2071067811865466, 0.20710678118654746, Math.sqrt(2), Math.sqrt(2), 45), new OpenSeadragon.Rect(1.0, 0.0, Math.sqrt(2), Math.sqrt(2), 45),
bounds, bounds,
EPSILON, EPSILON,
"Viewport.applyConstraints flipped and with rotation should move viewport."); "Viewport.applyConstraints flipped and with rotation should move viewport.");

View File

@ -3,14 +3,14 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>OpenSeadragon QUnit</title> <title>OpenSeadragon QUnit</title>
<link rel="stylesheet" href="/node_modules/qunitjs/qunit/qunit.css"> <link rel="stylesheet" href="/node_modules/qunit/qunit/qunit.css">
<link rel="stylesheet" href="/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css"> <link rel="stylesheet" href="/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
<link rel="stylesheet" href="/test/helpers/test.css"> <link rel="stylesheet" href="/test/helpers/test.css">
</head> </head>
<body> <body>
<div id="qunit"></div> <div id="qunit"></div>
<div id="qunit-fixture"></div> <div id="qunit-fixture"></div>
<script src="/node_modules/qunitjs/qunit/qunit.js"></script> <script src="/node_modules/qunit/qunit/qunit.js"></script>
<script src="/test/lib/jquery-1.9.1.min.js"></script> <script src="/test/lib/jquery-1.9.1.min.js"></script>
<script src="/test/lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js"></script> <script src="/test/lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js"></script>
<script src="/test/lib/jquery.simulate.js"></script> <script src="/test/lib/jquery.simulate.js"></script>
@ -22,6 +22,7 @@
<!-- Polyfill must be inserted first because it is testing functions <!-- Polyfill must be inserted first because it is testing functions
reassignments which could be done by other test. --> reassignments which could be done by other test. -->
<script src="/test/modules/polyfills.js"></script> <script src="/test/modules/polyfills.js"></script>
<script src="/test/modules/event-source.js"></script>
<script src="/test/modules/viewerretrieval.js"></script> <script src="/test/modules/viewerretrieval.js"></script>
<script src="/test/modules/basic.js"></script> <script src="/test/modules/basic.js"></script>
<script src="/test/modules/strings.js"></script> <script src="/test/modules/strings.js"></script>
@ -50,6 +51,6 @@
<script src="/test/modules/tilesource-dynamic-url.js"></script> <script src="/test/modules/tilesource-dynamic-url.js"></script>
<!--The navigator tests are the slowest (for now; hopefully they can be sped up) <!--The navigator tests are the slowest (for now; hopefully they can be sped up)
so we put them last. --> so we put them last. -->
<!-- The navigator tests are failing right now, so we have them disabled for the moment <script src="/test/modules/navigator.js"></script> --> <script src="/test/modules/navigator.js"></script>
</body> </body>
</html> </html>