Merge branch 'master' into ms-reference-strip

* master: (276 commits)
  Changelog for #2280 and #2238
  remove trailing space
  fix problem with click precision on ReferenceStrip
  Changelog for #2273
  Also add documentation for tileRetryDelay
  try fix with check for null and undefined
  fix build error
  Add tileRetryMax documentation.
  Revert async support and event breaking support in EventSource.
  Changelog for #2276
  add box-sizing property to the navigator display region
  Implement support for async function and promise type recognition with $.type. Add $.Promise proxy. Implement support for promises in EventSource. Implement ability to abort events as well as prioritize events.
  Changelog for #2270
  issues/2192 fix.
  Starting 4.0.1
  Version 4.0.0
  JSDoc fixes
  Changelog for #2256
  Changelog for #2249
  removed polling vs resizeviewer option from demo
  ...
This commit is contained in:
Mark Salsbery 2023-02-01 09:26:49 -08:00
commit e0f9c94bb5
118 changed files with 14634 additions and 7559 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ instrumented/
/nbproject/private/
.directory
local-test
.DS_Store

View File

@ -1,6 +1,6 @@
language: node_js
sudo: false
node_js:
- "stable"
- "16.14.2"
before_install:
- npm install -g grunt-cli

View File

@ -3,20 +3,20 @@
OpenSeadragon is truly a community project; we welcome your involvement!
When contributing, please attempt to match the code style already in the codebase.
However, we are in the process of changing our code style (see issue [#456](https://github.com/openseadragon/openseadragon/issues/456)), so avoid spaces inside parentheses and square brackets. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](http://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see [idiomatic.js](https://github.com/rwldrn/idiomatic.js/).
However, we are in the process of changing our code style (see issue [#456](https://github.com/openseadragon/openseadragon/issues/456)), so avoid spaces inside parentheses and square brackets. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](https://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see [idiomatic.js](https://github.com/rwldrn/idiomatic.js/).
When fixing bugs and adding features, when appropriate please also:
* Update related doc comments (we use [JSDoc 3](http://usejsdoc.org/))
* Update related doc comments (we use [JSDoc 3](https://jsdoc.app/))
* Add/update related unit tests
If you're new to the project, check out our [good first bug](https://github.com/openseadragon/openseadragon/issues?labels=good+first+bug&page=1&state=open) issues for some places to dip your toe in the water.
If you're new to the project, check out our [good first issues](https://github.com/openseadragon/openseadragon/issues?labels=good+first+issue&page=1&state=open) for some places to dip your toe in the water.
If you're new to open source in general, check out [GitHub's open source intro guide](https://guides.github.com/activities/contributing-to-open-source/).
### First Time Setup
All command-line operations for building and testing OpenSeadragon are scripted using [Grunt](http://gruntjs.com/) which is based on [Node.js](http://nodejs.org/). To get set up:
All command-line operations for building and testing OpenSeadragon are scripted using [Grunt](https://gruntjs.com/) which is based on [Node.js](https://nodejs.org/). To get set up:
1. Install Node, if you haven't already (available at the link above)
1. Install the Grunt command line runner (if you haven't already); on the command line, run `npm install -g grunt-cli`
@ -57,7 +57,7 @@ You can also publish the built version to the site-build repository. This assume
### Testing
Our tests are based on [QUnit](http://qunitjs.com/) and [Puppeteer](https://github.com/GoogleChrome/puppeteer); they're both installed when you run `npm install`. To run on the command line:
Our tests are based on [QUnit](https://qunitjs.com/) and [Puppeteer](https://github.com/GoogleChrome/puppeteer); they're both installed when you run `npm install`. To run on the command line:
grunt test

View File

@ -197,7 +197,7 @@ module.exports = function(grunt) {
},
eslint: {
options: {
configFile: '.eslintrc.json'
overrideConfigFile: '.eslintrc.json'
},
target: sources
},

View File

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

View File

@ -1,18 +1,29 @@
# OpenSeadragon
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master)](http://travis-ci.org/openseadragon/openseadragon)
<!-- [![Gitter][gitter-badge]][gitter]
[![Build Status][build-badge]][build] -->
An open-source, web-based viewer for zoomable images, implemented in pure JavaScript.
See it in action and get started using it at [http://openseadragon.github.io/](http://openseadragon.github.io/).
See it in action and get started using it at [https://openseadragon.github.io/][openseadragon].
## Stable Builds
See the [GitHub releases page](https://github.com/openseadragon/openseadragon/releases).
See the [GitHub releases page][github-releases].
## Development
If you want to use OpenSeadragon in your own projects, you can find the latest stable build, API documentation, and example code at [http://openseadragon.github.io/](http://openseadragon.github.io/). If you want to modify OpenSeadragon and/or contribute to its development, read the [contributing guide](https://github.com/openseadragon/openseadragon/blob/master/CONTRIBUTING.md) for instructions.
If you want to use OpenSeadragon in your own projects, you can find the latest stable build, API documentation, and example code at [https://openseadragon.github.io/][openseadragon]. If you want to modify OpenSeadragon and/or contribute to its development, read the [contributing guide][github-contributing] for instructions.
## License
OpenSeadragon is released under the New BSD license. For details, see the [LICENSE.txt file](https://github.com/openseadragon/openseadragon/blob/master/LICENSE.txt).
OpenSeadragon is released under the New BSD license. For details, see the [LICENSE.txt file][github-license].
[openseadragon]: https://openseadragon.github.io/
<!-- [gitter-badge]: https://badges.gitter.im/Join%20Chat.svg
[gitter]: https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
[build-badge]: https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master
[build]: https://travis-ci.org/openseadragon/openseadragon -->
[github-releases]: https://github.com/openseadragon/openseadragon/releases
[github-contributing]: https://github.com/openseadragon/openseadragon/blob/master/CONTRIBUTING.md
[github-license]: https://github.com/openseadragon/openseadragon/blob/master/LICENSE.txt

View File

@ -1,7 +1,60 @@
OPENSEADRAGON CHANGELOG
=======================
3.0.0: (In progress)
4.0.1: (in progress...)
* 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)
* 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)
4.0.0:
* NEW BEHAVIOR: Setting the viewport rotation now animates by default (pass false 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)
* 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)
* Improved the constraints that keep the image in the viewer, specifically when zoomed out a lot (#2160 @joedf, #2246 @pearcetm)
* You can now provide an element for the navigator (as an alternative to an ID) (#1303 @cameronbaney, #2166 #2175 @joedf)
* Now supporting IIIF "id" and "identifier" in addition to "@id" (#2173 @ahankinson)
* 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)
* The viewer now emits before-destroy and destroy events (#2239 @pearcetm)
* Auto resize detection is now more efficient (#2256 @pearcetm)
* Improved documentation (#2211 @shyamkumaryadav)
* 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: 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: On startup you would get an unnecessary "Viewer.buttons is deprecated" warning (#2201 @joedf, #2219 @jssullivan, #2212 @joedf)
3.1.0:
* Added subPixelRoundingForTransparency Viewer option to address seams that can appear in semi-transparent images (#2075 @TanukiSharp)
* Added Viewer.isAnimating() (#2075 @TanukiSharp)
* Added isFullScreen method to Viewer (#2067 @JachiOnuoha)
* Added option to include POST data when loading files via Ajax (#2072 @Aiosa)
* Exposed TiledImage's private functions for better maintainability (#2134 @Aiosa)
* Tile cache keys are now generated by the tile source, so it's easier to override them as needed (#2138 @Aiosa)
* Pinch to zoom now zooms around the center of the pinch, rather than the center of the viewer (#2158 @cavenel)
* Added fallback and deprecation warning for Viewer.buttons (which got changed to buttonGroup in 3.0.0) (#2153 @devbyjonah)
* Fixed an issue where turning off panVertical or panHorizontal would not affect the panning keyboard combos (#2069 @JachiOnuoha)
* Cleaned up console.logs so that errors and warnings use console.error and console.warn as appropriate (#2073 @Abhishek-90)
* Improved documentation (#2067 @JachiOnuoha, #2112 @shyamkumaryadav, #2152 @joedf, #2155 @samwilson)
* Fixed: Setting useCanvas to false would break the viewer (#2116 @rvv-bouvet)
* Allow silencing multi-image warnings on viewport coordinate conversion functions (#2120 @claycoleman)
* Fixed: Swiping fast multiple times made contact points in MouseTracker out of sync for touch events (#2121 @ronnymikalsen)
* Made MouseTracker more robust in certain scenarios (#2134, #2147 @Aiosa)
* Fixed an issue where full page mode wouldn't grow properly if you resized the window (#2100 @TanukiSharp)
* Now if you pass an error handler into makeAjaxRequest, it doesn't report errors into the console (#2142 @Aiosa)
* Fixed error caused by attaching MouseTracker to the page's document element (#2145 @tdiprima)
* Fixed an issue that would sometimes cause problems with freeing up ImageTileSource memory (#2162 @pearcetm)
3.0.0:
* BREAKING CHANGE: Dropped support for older browsers (IE < 11) (#1872 #1949 #1951 @msalsbery, #1950 @rmontroy)
* BREAKING CHANGE: Removed deprecated OpenSeadragon.getEvent function (#1949 @msalsbery)
@ -55,6 +108,10 @@ OPENSEADRAGON CHANGELOG
* Now ensuring that the new item is already in the navigator when the "add-item" event fires (#2005 @RammasEchor)
* Added keys to change image in sequence mode (j: previous, k: next) (#2007 @RammasEchor)
* Fixed a bug where the navigator wouldn't pick up opacity/composite changes made while it is loading (#2018 @crydell)
* Explicitly set passive:false for wheel event handlers to suppress console warnings. Fixes #1669 (#2043 @msalsbery)
* Viewer's canvas-click events now include an originalTarget property so you can know which element received the click (#2037 @iangilman)
* Added method for getting the size of an image in window coordinates (#2049 @superbland)
* Added a setMaxLevel function to TileSource so you can change its maxLevel if needed (#2059, #2066 @kim-sanghoon)
2.4.2:

9444
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "openseadragon",
"version": "2.4.2",
"version": "4.0.0",
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
"keywords": [
"image",
@ -29,15 +29,15 @@
"url": "https://github.com/openseadragon/openseadragon.git"
},
"devDependencies": {
"grunt": "^1.1.0",
"grunt": "^1.4.1",
"grunt-contrib-clean": "^2.0.0",
"grunt-contrib-compress": "^1.6.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^2.1.0",
"grunt-contrib-qunit": "^3.1.0",
"grunt-contrib-uglify": "^4.0.1",
"grunt-contrib-compress": "^2.0.0",
"grunt-contrib-concat": "^2.0.0",
"grunt-contrib-connect": "^3.0.0",
"grunt-contrib-qunit": "^6.2.0",
"grunt-contrib-uglify": "^5.0.1",
"grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^23.0.0",
"grunt-eslint": "^24.0.0",
"grunt-git-describe": "^2.4.4",
"grunt-istanbul": "^0.8.0",
"grunt-text-replace": "^0.4.0",

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Button
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -398,6 +398,7 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
disable: function(){
this.notifyGroupExit();
this.element.disabled = true;
this.tracker.setTracking(false);
$.setElementOpacity( this.element, 0.2, true );
},
@ -406,6 +407,7 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
*/
enable: function(){
this.element.disabled = false;
this.tracker.setTracking(true);
$.setElementOpacity( this.element, 1.0, true );
this.notifyGroupEnter();
},

View File

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

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Control
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -74,6 +74,7 @@ $.ControlAnchor = {
* @param {Element} container - the element to control will be anchored too.
*/
$.Control = function ( element, options, container ) {
var parent = element.parentNode;
if (typeof options === 'number')
{
@ -150,6 +151,7 @@ $.Control = function ( element, options, container ) {
} else {
parent.appendChild( this.wrapper );
}
};
/** @lends OpenSeadragon.Control.prototype */
@ -169,7 +171,7 @@ $.Control.prototype = {
/**
* Determines if the control is currently visible.
* @function
* @return {Boolean} true if currently visible, false otherwise.
* @returns {Boolean} true if currently visible, false otherwise.
*/
isVisible: function() {
return this.wrapper.style.display !== "none";

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ControlDock
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -149,7 +149,7 @@
/**
* @function
* @return {OpenSeadragon.ControlDock} Chainable.
* @returns {OpenSeadragon.ControlDock} Chainable.
*/
removeControl: function ( element ) {
element = $.getElement( element );
@ -165,7 +165,7 @@
/**
* @function
* @return {OpenSeadragon.ControlDock} Chainable.
* @returns {OpenSeadragon.ControlDock} Chainable.
*/
clearControls: function () {
while ( this.controls.length > 0 ) {
@ -178,7 +178,7 @@
/**
* @function
* @return {Boolean}
* @returns {Boolean}
*/
areControlsEnabled: function () {
var i;
@ -195,7 +195,7 @@
/**
* @function
* @return {OpenSeadragon.ControlDock} Chainable.
* @returns {OpenSeadragon.ControlDock} Chainable.
*/
setControlsEnabled: function( enabled ) {
var i;

View File

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

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Drawer
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -176,6 +176,7 @@ $.Drawer.prototype = {
* This function does not take rotation into account, thus assuming provided
* point is at 0 degree.
* @param {OpenSeadragon.Point} point - the pixel point to convert
* @returns {OpenSeadragon.Point} Point in drawer coordinate system.
*/
viewportCoordToDrawerCoord: function(point) {
var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
@ -208,7 +209,7 @@ $.Drawer.prototype = {
/**
* Set the opacity of the drawer.
* @param {Number} opacity
* @return {OpenSeadragon.Drawer} Chainable.
* @returns {OpenSeadragon.Drawer} Chainable.
*/
setOpacity: function( opacity ) {
$.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.");
@ -264,7 +265,7 @@ $.Drawer.prototype = {
},
/**
* @return {Boolean} True if rotation is supported.
* @returns {Boolean} True if rotation is supported.
*/
canRotate: function() {
return this.useCanvas;
@ -321,7 +322,7 @@ $.Drawer.prototype = {
* Scale from OpenSeadragon viewer rectangle to drawer rectangle
* (ignoring rotation)
* @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.
* @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
* @returns {OpenSeadragon.Rect} Rectangle in drawer coordinate system.
*/
viewportToDrawerRectangle: function(rectangle) {
var topLeft = this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(), true);
@ -344,15 +345,19 @@ $.Drawer.prototype = {
* where <code>rendered</code> is the context with the pre-drawn image.
* @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1.
* @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
* position and size of tiles supporting alpha channel in non-transparency
* context.
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
*/
drawTile: function(tile, drawingHandler, useSketch, scale, translate) {
drawTile: function( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
$.console.assert(tile, '[Drawer.drawTile] tile is required');
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
if (this.useCanvas) {
var context = this._getContext(useSketch);
scale = scale || 1;
tile.drawCanvas(context, drawingHandler, scale, translate);
tile.drawCanvas(context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source);
} else {
tile.drawHTML( this.canvas );
}
@ -544,8 +549,8 @@ $.Drawer.prototype = {
context.strokeStyle = this.debugGridColor[colorIndex];
context.fillStyle = this.debugGridColor[colorIndex];
if ( this.viewport.degrees !== 0 ) {
this._offsetForRotation({degrees: this.viewport.degrees});
if (this.viewport.getRotation(true) % 360 !== 0 ) {
this._offsetForRotation({degrees: this.viewport.getRotation(true)});
}
if (tiledImage.getRotation(true) % 360 !== 0) {
this._offsetForRotation({
@ -554,7 +559,8 @@ $.Drawer.prototype = {
tiledImage._getRotationPoint(true), true)
});
}
if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(tiledImage._drawer.viewer.viewport.getFlip()) {
tiledImage._drawer._flip();
}
@ -572,7 +578,7 @@ $.Drawer.prototype = {
// Rotate the text the right way around.
context.translate( tileCenterX, tileCenterY );
context.rotate( Math.PI / 180 * -this.viewport.degrees );
context.rotate( Math.PI / 180 * -this.viewport.getRotation(true) );
context.translate( -tileCenterX, -tileCenterY );
if( tile.x === 0 && tile.y === 0 ){
@ -618,14 +624,15 @@ $.Drawer.prototype = {
(tile.position.y + 70) * $.pixelDensityRatio
);
if ( this.viewport.degrees !== 0 ) {
if (this.viewport.getRotation(true) % 360 !== 0 ) {
this._restoreRotationChanges();
}
if (tiledImage.getRotation(true) % 360 !== 0) {
this._restoreRotationChanges();
}
if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
if (tiledImage.viewport.getRotation(true) % 360 === 0 &&
tiledImage.getRotation(true) % 360 === 0) {
if(tiledImage._drawer.viewer.viewport.getFlip()) {
tiledImage._drawer._flip();
}

View File

@ -2,7 +2,7 @@
* OpenSeadragon - DziTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -124,10 +124,11 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
* @function
* @param {Object|XMLDocument} data - the raw configuration
* @param {String} url - the url the data was retrieved from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
*/
configure: function( data, url ){
configure: function( data, url, postData ){
var options;

View File

@ -2,7 +2,7 @@
* OpenSeadragon - EventSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* 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)
* for a given event.
* for a given event. It is not removable with removeHandler().
* @function
* @param {String} eventName - Name of event to register.
* @param {OpenSeadragon.EventHandler} handler - Function to call when event
@ -67,8 +67,9 @@ $.EventSource.prototype = {
* to the handler.
* @param {Number} [times=1] - The number of times to handle the event
* 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;
times = times || 1;
var count = 0;
@ -77,9 +78,9 @@ $.EventSource.prototype = {
if (count === times) {
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 {OpenSeadragon.EventHandler} handler - Function to call when event is triggered.
* @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 ];
if ( !events ) {
this.events[ eventName ] = events = [];
}
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--;
}
}
},
@ -122,6 +131,18 @@ $.EventSource.prototype = {
}
},
/**
* Get the amount of handlers registered for a given event.
* @param {String} eventName - Name of event to inspect.
* @returns {number} amount of events
*/
numberOfHandlers: function (eventName) {
var events = this.events[ eventName ];
if ( !events ) {
return 0;
}
return events.length;
},
/**
* Remove all event handlers for a given event type. If no type is given all
@ -144,7 +165,7 @@ $.EventSource.prototype = {
* @function
* @param {String} eventName - Name of event to get handlers for.
*/
getHandler: function ( eventName ) {
getHandler: function ( eventName) {
var events = this.events[ eventName ];
if ( !events || !events.length ) {
return null;
@ -174,15 +195,12 @@ $.EventSource.prototype = {
raiseEvent: function( eventName, eventArgs ) {
//uncomment if you want to get a log of all events
//$.console.log( eventName );
var handler = this.getHandler( eventName );
if ( handler ) {
if ( !eventArgs ) {
eventArgs = {};
}
handler( this, eventArgs );
return handler( this, eventArgs || {} );
}
return undefined;
}
};

View File

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

View File

@ -2,7 +2,7 @@
* OpenSeadragon - IIIFTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -51,8 +51,11 @@ $.IIIFTileSource = function( options ){
$.extend( true, this, options );
if ( !( this.height && this.width && this['@id'] ) ) {
throw new Error( 'IIIF required parameters not provided.' );
/* Normalizes v3-style 'id' keys to an "_id" internal property */
this._id = this["@id"] || this["id"] || this['identifier'] || null;
if ( !( this.height && this.width && this._id) ) {
throw new Error( 'IIIF required parameters (width, height, or id) not provided.' );
}
options.tileSizePerScaleFactor = {};
@ -131,7 +134,7 @@ $.IIIFTileSource = function( options ){
if (!options.maxLevel && !this.emulateLegacyImagePyramid) {
if (!this.scale_factors) {
options.maxLevel = Number(Math.ceil(Math.log(Math.max(this.width, this.height), 2)));
options.maxLevel = Number(Math.round(Math.log(Math.max(this.width, this.height), 2)));
} else {
var maxScaleFactor = Math.max.apply(null, this.scale_factors);
options.maxLevel = Math.round(Math.log(maxScaleFactor) * Math.LOG2E);
@ -147,7 +150,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
* this tile source.
* @function
* @param {Object|Array} data
* @param {String} optional - url
* @param {String} [url] - url
*/
supports: function( data, url ) {
@ -180,35 +183,43 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
},
/**
* A static function used to prepare an incoming IIIF Image API info.json
* response for processing by the tile handler. Normalizes data for all
* versions of IIIF (1.0, 1.1, 2.x, 3.x) and returns a data object that
* may be passed to the IIIFTileSource.
*
* @function
* @static
* @param {Object} data - the raw configuration
* @example <caption>IIIF 1.1 Info Looks like this</caption>
* @param {String} url - the url configuration was retrieved from
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} A normalized IIIF data object
* @example <caption>IIIF 2.x Info Looks like this</caption>
* {
* "@context" : "http://library.stanford.edu/iiif/image-api/1.1/context.json",
* "@id" : "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C",
* "width" : 6000,
* "height" : 4000,
* "scale_factors" : [ 1, 2, 4 ],
* "tile_width" : 1024,
* "tile_height" : 1024,
* "formats" : [ "jpg", "png" ],
* "qualities" : [ "native", "grey" ],
* "profile" : "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0"
* "@context": "http://iiif.io/api/image/2/context.json",
* "@id": "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C",
* "protocol": "http://iiif.io/api/image",
* "height": 1024,
* "width": 775,
* "tiles" : [{"width":256, "scaleFactors":[1,2,4,8]}],
* "profile": ["http://iiif.io/api/image/2/level1.json", {
* "qualities": [ "native", "bitonal", "grey", "color" ],
* "formats": [ "jpg", "png", "gif" ]
* }]
* }
*/
configure: function( data, url ){
configure: function( data, url, postData ){
// Try to deduce our version and fake it upwards if needed
if ( !$.isPlainObject(data) ) {
var options = configureFromXml10( data );
options['@context'] = "http://iiif.io/api/image/1.0/context.json";
options['@id'] = url.replace('/info.xml', '');
options["@id"] = url.replace('/info.xml', '');
options.version = 1;
return options;
} else {
if ( !data['@context'] ) {
data['@context'] = 'http://iiif.io/api/image/1.0/context.json';
data['@id'] = url.replace('/info.json', '');
data["@id"] = url.replace('/info.json', '');
data.version = 1;
} else {
var context = data['@context'];
@ -237,10 +248,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
$.console.error('Data has a @context property which contains no known IIIF context URI.');
}
}
if ( !data['@id'] && data['id'] ) {
data['@id'] = data['id'];
}
if(data.preferredFormats) {
if (data.preferredFormats) {
for (var f = 0; f < data.preferredFormats.length; f++ ) {
if ( OpenSeadragon.imageFormatSupported(data.preferredFormats[f]) ) {
data.tileFormat = data.preferredFormats[f];
@ -368,8 +377,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
scale = Math.pow( 0.5, this.maxLevel - level ),
//# image dimensions at this level
levelWidth = Math.ceil( this.width * scale ),
levelHeight = Math.ceil( this.height * scale ),
levelWidth = Math.round( this.width * scale ),
levelHeight = Math.round( this.height * scale ),
//## iiif region
tileWidth,
@ -389,8 +398,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
tileWidth = this.getTileWidth(level);
tileHeight = this.getTileHeight(level);
iiifTileSizeWidth = Math.ceil( tileWidth / scale );
iiifTileSizeHeight = Math.ceil( tileHeight / scale );
iiifTileSizeWidth = Math.round( tileWidth / scale );
iiifTileSizeHeight = Math.round( tileHeight / scale );
if (this.version === 1) {
iiifQuality = "native." + this.tileFormat;
} else {
@ -417,8 +426,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
} else {
iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
}
iiifSizeW = Math.ceil( iiifTileW * scale );
iiifSizeH = Math.ceil( iiifTileH * scale );
iiifSizeW = Math.round( iiifTileW * scale );
iiifSizeH = Math.round( iiifTileH * scale );
if ( this.version === 2 && iiifSizeW === this.width ) {
iiifSize = "full";
} else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) {
@ -429,7 +438,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
iiifSize = iiifSizeW + ",";
}
}
uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' );
uri = [ this._id, iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' );
return uri;
},
@ -444,8 +453,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
/**
* Determine whether arbitrary tile requests can be made against a service with the given profile
* @function
* @param {array} profile - IIIF profile array
* @throws {Error}
* @param {Object} options
* @param {Array|String} options.profile
* @param {Number} options.version
* @param {String[]} options.extraFeatures
* @returns {Boolean}
*/
function canBeTiled ( options ) {
var level0Profiles = [
@ -477,7 +489,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
var levels = [];
for(var i = 0; i < options.sizes.length; i++) {
levels.push({
url: options['@id'] + '/full/' + options.sizes[i].width + ',' +
url: options._id + '/full/' + options.sizes[i].width + ',' +
(options.version === 3 ? options.sizes[i].height : '') +
'/0/default.' + options.tileFormat,
width: options.sizes[i].width,

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ImageLoader
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -35,126 +35,89 @@
(function($){
/**
* @private
* @class ImageJob
* @classdesc Handles downloading of a single image.
* @param {Object} options - Options for this ImageJob.
* @param {String} [options.src] - URL of image to download.
* @param {Tile} [options.tile] - Tile that belongs the data to.
* @param {TileSource} [options.source] - Image loading strategy
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null
* @param {Function} [options.callback] - Called once image has been downloaded.
* @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.tries] - Actual number of the current try.
*/
function ImageJob (options) {
$.ImageJob = function(options) {
$.extend(true, this, {
timeout: $.DEFAULT_SETTINGS.timeout,
jobId: null
jobId: null,
tries: 0
}, options);
/**
* Image object which will contain downloaded image.
* @member {Image} image
* Data object which will contain downloaded image data.
* @member {Image|*} image data object, by default an Image object (depends on TileSource)
* @memberof OpenSeadragon.ImageJob#
*/
this.image = null;
}
this.data = null;
ImageJob.prototype = {
errorMsg: null,
/**
* User workspace to populate with helper variables
* @member {*} userData to append custom data and avoid namespace collision
* @memberof OpenSeadragon.ImageJob#
*/
this.userData = {};
/**
* Error message holder
* @member {string} error message
* @memberof OpenSeadragon.ImageJob#
* @private
*/
this.errorMsg = null;
};
$.ImageJob.prototype = {
/**
* Starts the image job.
* @method
*/
start: function(){
start: function() {
this.tries++;
var self = this;
var selfAbort = this.abort;
this.image = new Image();
this.image.onload = function(){
self.finish(true);
};
this.image.onabort = this.image.onerror = function() {
self.errorMsg = "Image load aborted";
self.finish(false);
};
this.jobId = window.setTimeout(function(){
self.errorMsg = "Image load exceeded timeout (" + self.timeout + " ms)";
self.finish(false);
this.jobId = window.setTimeout(function () {
self.finish(null, null, "Image load exceeded timeout (" + self.timeout + " ms)");
}, this.timeout);
// Load the tile with an AJAX request if the loadWithAjax option is
// set. Otherwise load the image by setting the source proprety of the image object.
if (this.loadWithAjax) {
this.request = $.makeAjaxRequest({
url: this.src,
withCredentials: this.ajaxWithCredentials,
headers: this.ajaxHeaders,
responseType: "arraybuffer",
success: function(request) {
var blb;
// Make the raw data into a blob.
// BlobBuilder fallback adapted from
// http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility
try {
blb = new window.Blob([request.response]);
} catch (e) {
var BlobBuilder = (
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder
);
if (e.name === 'TypeError' && BlobBuilder) {
var bb = new BlobBuilder();
bb.append(request.response);
blb = bb.getBlob();
}
}
// If the blob is empty for some reason consider the image load a failure.
if (blb.size === 0) {
self.errorMsg = "Empty image response.";
self.finish(false);
}
// Create a URL for the blob data and make it the source of the image object.
// This will still trigger Image.onload to indicate a successful tile load.
var url = (window.URL || window.webkitURL).createObjectURL(blb);
self.image.src = url;
},
error: function(request) {
self.errorMsg = "Image load aborted - XHR error";
self.finish(false);
}
});
// Provide a function to properly abort the request.
this.abort = function() {
self.request.abort();
// Call the existing abort function if available
self.source.downloadTileAbort(self);
if (typeof selfAbort === "function") {
selfAbort();
}
};
} else {
if (this.crossOriginPolicy !== false) {
this.image.crossOrigin = this.crossOriginPolicy;
}
this.image.src = this.src;
}
this.source.downloadTileStart(this);
},
finish: function(successful) {
this.image.onload = this.image.onerror = this.image.onabort = null;
if (!successful) {
this.image = null;
}
/**
* Finish this job.
* @param {*} data data that has been downloaded
* @param {XMLHttpRequest} request reference to the request if used
* @param {string} errorMessage description upon failure
*/
finish: function(data, request, errorMessage ) {
this.data = data;
this.request = request;
this.errorMsg = errorMessage;
if (this.jobId) {
window.clearTimeout(this.jobId);
@ -162,7 +125,6 @@ ImageJob.prototype = {
this.callback(this);
}
};
/**
@ -180,6 +142,7 @@ $.ImageLoader = function(options) {
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
timeout: $.DEFAULT_SETTINGS.timeout,
jobQueue: [],
failedTiles: [],
jobsInProgress: 0
}, options);
@ -193,30 +156,48 @@ $.ImageLoader.prototype = {
* @method
* @param {Object} options - Options for this job.
* @param {String} [options.src] - URL of image to download.
* @param {Tile} [options.tile] - Tile that belongs the data to. The tile instance
* is not internally used and serves for custom TileSources implementations.
* @param {TileSource} [options.source] - Image loading strategy
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
* requests.
* @param {Function} [options.callback] - Called once image has been downloaded.
* @param {Function} [options.abort] - Called when this image job is aborted.
*/
addJob: function(options) {
if (!options.source) {
$.console.error('ImageLoader.prototype.addJob() requires [options.source]. ' +
'TileSource since new API defines how images are fetched. Creating a dummy TileSource.');
var implementation = $.TileSource.prototype;
options.source = {
downloadTileStart: implementation.downloadTileStart,
downloadTileAbort: implementation.downloadTileAbort
};
}
var _this = this,
complete = function(job) {
completeJob(_this, job, options.callback);
},
jobOptions = {
src: options.src,
tile: options.tile || {},
source: options.source,
loadWithAjax: options.loadWithAjax,
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
crossOriginPolicy: options.crossOriginPolicy,
ajaxWithCredentials: options.ajaxWithCredentials,
postData: options.postData,
callback: complete,
abort: options.abort,
timeout: this.timeout
},
newJob = new ImageJob(jobOptions);
newJob = new $.ImageJob(jobOptions);
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
newJob.start();
@ -244,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
* @private
* @param loader - ImageLoader used to start job.
@ -252,6 +234,9 @@ $.ImageLoader.prototype = {
* @param callback - Called once cleanup is finished.
*/
function completeJob(loader, job, callback) {
if (job.errorMsg !== '' && (job.image === null || job.image === undefined) && job.tries < 1 + loader.tileRetryMax) {
loader.failedTiles.push(job);
}
var nextJob;
loader.jobsInProgress--;
@ -262,7 +247,17 @@ function completeJob(loader, job, callback) {
loader.jobsInProgress++;
}
callback(job.image, job.errorMsg, job.request);
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);
}
}(OpenSeadragon));

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ImageTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -89,10 +89,11 @@
* @function
* @param {Object} options - the options
* @param {String} dataUrl - the url the image was retrieved from, if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
*/
configure: function (options, dataUrl) {
configure: function (options, dataUrl, postData) {
return options;
},
/**
@ -271,9 +272,11 @@
*/
_freeupCanvasMemory: function () {
for (var i = 0; i < this.levels.length; i++) {
if(this.levels[i].context2D){
this.levels[i].context2D.canvas.height = 0;
this.levels[i].context2D.canvas.width = 0;
}
}
},
});

View File

@ -2,7 +2,7 @@
* OpenSeadragon - LegacyTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -122,10 +122,11 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
* @function
* @param {Object|XMLDocument} configuration - the raw configuration
* @param {String} dataUrl - the url the data was retrieved from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
*/
configure: function( configuration, dataUrl ){
configure: function( configuration, dataUrl, postData ){
var options;

View File

@ -2,7 +2,7 @@
* OpenSeadragon - MouseTracker
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -165,8 +165,8 @@
this.dblClickTimeThreshold = options.dblClickTimeThreshold || $.DEFAULT_SETTINGS.dblClickTimeThreshold;
/**
* The maximum distance allowed between two pointer click events
* to be treated as a click gesture.
* @member {Number} clickDistThreshold
* to be treated as a double-click gesture.
* @member {Number} dblClickDistThreshold
* @memberof OpenSeadragon.MouseTracker#
*/
this.dblClickDistThreshold = options.dblClickDistThreshold || $.DEFAULT_SETTINGS.dblClickDistThreshold;
@ -271,7 +271,10 @@
lastPinchDist: 0,
currentPinchDist: 0,
lastPinchCenter: null,
currentPinchCenter: null
currentPinchCenter: null,
// Tracking for drag
sentDragEvent: false
};
this.hasGestureHandlers = !!( this.pressHandler || this.nonPrimaryPressHandler ||
@ -737,6 +740,8 @@
* True if the original event is a touch event, otherwise false. <span style="color:red;">Deprecated. Use pointerType and/or originalEvent instead.</span>
* @param {Object} event.originalEvent
* The original event object.
* @param {Element} event.originalTarget
* The DOM element clicked on.
* @param {Object} event.userData
* Arbitrary user-defined object.
*/
@ -1191,7 +1196,7 @@
* the emulated event, a synthetic event object created with values from the actual DOM event,
* or null if no DOM event applies. Emulated events can occur on eventType "wheel" on legacy mouse-scroll
* event emitting user agents.
* @property {Boolean} isStopable
* @property {Boolean} isStoppable
* True if propagation of the event (e.g. bubbling) can be stopped with stopPropagation/stopImmediatePropagation.
* @property {Boolean} isCancelable
* True if the event's default handling by the browser can be prevented with preventDefault.
@ -1398,7 +1403,6 @@
--this.contacts;
if (this.contacts < 0) {
$.console.warn('GesturePointList.removeContact() Implausible contacts value');
this.contacts = 0;
}
}
@ -1444,6 +1448,8 @@
for ( i = 0; i < pointerListCount; i++ ) {
delegate.activePointersLists.pop();
}
delegate.sentDragEvent = false;
}
/**
@ -1463,7 +1469,7 @@
tracker.element,
event,
delegate[ event ],
false
event === $.MouseTracker.wheelEventName ? { passive: false, capture: false } : false
);
}
@ -2816,10 +2822,7 @@
// If child element relinquishes capture to a parent we may get here
// from a pointerleave event while a pointerup event will never be received.
// In that case, we'll clean up the contact count
if ( (pointsList.type === 'mouse' || pointsList.type === 'pen') &&
pointsList.contacts > 0 ) {
pointsList.removeContact();
}
listLength = pointsList.removeById( gPoint.id );
} else {
@ -2838,7 +2841,7 @@
function getEventProcessDefaults( tracker, eventInfo ) {
switch ( eventInfo.eventType ) {
case 'pointermove':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = false;
eventInfo.preventGesture = !tracker.hasGestureHandlers;
@ -2850,28 +2853,28 @@
case 'keydown':
case 'keyup':
case 'keypress':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = false; // onContextMenu(), onKeyDown(), onKeyUp(), onKeyPress() may set true
eventInfo.preventGesture = false;
eventInfo.stopPropagation = false;
break;
case 'pointerdown':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = false; // updatePointerDown() may set true (tracker.hasGestureHandlers)
eventInfo.preventGesture = !tracker.hasGestureHandlers;
eventInfo.stopPropagation = false;
break;
case 'pointerup':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = false;
eventInfo.preventGesture = !tracker.hasGestureHandlers;
eventInfo.stopPropagation = false;
break;
case 'wheel':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = false; // handleWheelEvent() may set true
eventInfo.preventGesture = !tracker.hasScrollHandler;
@ -2880,21 +2883,21 @@
case 'gotpointercapture':
case 'lostpointercapture':
case 'pointercancel':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = false;
eventInfo.preventDefault = false;
eventInfo.preventGesture = false;
eventInfo.stopPropagation = false;
break;
case 'click':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = !!tracker.clickHandler;
eventInfo.preventGesture = false;
eventInfo.stopPropagation = false;
break;
case 'dblclick':
eventInfo.isStopable = true;
eventInfo.isStoppable = true;
eventInfo.isCancelable = true;
eventInfo.preventDefault = !!tracker.dblClickHandler;
eventInfo.preventGesture = false;
@ -2905,7 +2908,7 @@
case 'pointerenter':
case 'pointerleave':
default:
eventInfo.isStopable = false;
eventInfo.isStoppable = false;
eventInfo.isCancelable = false;
eventInfo.preventDefault = false;
eventInfo.preventGesture = false;
@ -3272,6 +3275,7 @@
//updateGPoint.captured = true; // Handled by updatePointerCaptured()
updateGPoint.insideElementPressed = true;
updateGPoint.insideElement = true;
updateGPoint.originalTarget = eventInfo.originalEvent.target;
updateGPoint.contactPos = gPoint.currentPos;
updateGPoint.contactTime = gPoint.currentTime;
updateGPoint.lastPos = updateGPoint.currentPos;
@ -3282,12 +3286,12 @@
gPoint = updateGPoint;
} else {
// Initialize for tracking and add to the tracking list (no pointerenter event occurred before this)
$.console.warn('pointerdown event on untracked pointer');
// NOTE: pointerdown event on untracked pointer
gPoint.captured = false; // Handled by updatePointerCaptured()
gPoint.insideElementPressed = true;
gPoint.insideElement = true;
gPoint.originalTarget = eventInfo.originalEvent.target;
startTrackingPointer( pointsList, gPoint );
return;
}
pointsList.addContact();
@ -3431,8 +3435,8 @@
releasePoint = updateGPoint.currentPos;
releaseTime = updateGPoint.currentTime;
} else {
// should never get here...we'll start to track pointer anyway
$.console.warn('updatePointerUp(): pointerup on untracked gPoint');
// NOTE: updatePointerUp(): pointerup on untracked gPoint
// ...we'll start to track pointer again
gPoint.captured = false; // Handled by updatePointerCaptured()
gPoint.insideElementPressed = false;
gPoint.insideElement = true;
@ -3455,7 +3459,7 @@
if ( pointsList.contacts === 0 ) {
// Release (pressed in our element)
if ( tracker.releaseHandler ) {
if ( tracker.releaseHandler && releasePoint ) {
tracker.releaseHandler(
{
eventSource: tracker,
@ -3472,7 +3476,7 @@
}
// Drag End
if ( tracker.dragEndHandler ) {
if ( tracker.dragEndHandler && delegate.sentDragEvent ) {
tracker.dragEndHandler(
{
eventSource: tracker,
@ -3488,6 +3492,9 @@
);
}
// We want to clear this flag regardless of whether we fired the dragEndHandler
delegate.sentDragEvent = false;
// Click / Double-Click
if ( ( tracker.clickHandler || tracker.dblClickHandler ) && updateGPoint.insideElement ) {
quick = releaseTime - updateGPoint.contactTime <= tracker.clickTimeThreshold &&
@ -3504,6 +3511,7 @@
shift: eventInfo.originalEvent.shiftKey,
isTouchEvent: updateGPoint.type === 'touch',
originalEvent: eventInfo.originalEvent,
originalTarget: updateGPoint.originalTarget,
userData: tracker.userData
}
);
@ -3553,7 +3561,7 @@
eventInfo.shouldReleaseCapture = false;
// Release (pressed in another element)
if ( tracker.releaseHandler ) {
if ( tracker.releaseHandler && releasePoint ) {
tracker.releaseHandler(
{
eventSource: tracker,
@ -3674,6 +3682,7 @@
}
);
eventInfo.preventDefault = true;
delegate.sentDragEvent = true;
}
} else if ( pointsList.contacts === 2 ) {
// Move (2 contacts, use center)

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Navigator
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -45,7 +45,9 @@
* @memberof OpenSeadragon
* @extends OpenSeadragon.Viewer
* @extends OpenSeadragon.EventSource
* @param {Object} options
* @param {Object} options - Navigator options
* @param {Element} [options.element] - An element to use for the navigator.
* @param {String} [options.id] - Id of the element to use for the navigator. However, this is ignored if {@link options.element} is provided.
*/
$.Navigator = function( options ){
@ -55,8 +57,31 @@ $.Navigator = function( options ){
navigatorSize;
//We may need to create a new element and id if they did not
//provide the id for the existing element
if( !options.id ){
//provide the id for the existing element or the element itself
if( options.element || options.id ){
if ( options.element ) {
if ( options.id ){
$.console.warn("Given option.id for Navigator was ignored since option.element was provided and is being used instead.");
}
// Don't overwrite the element's id if it has one already
if ( options.element.id ) {
options.id = options.element.id;
} else {
options.id = 'navigator-' + $.now();
}
this.element = options.element;
} else {
this.element = document.getElementById( options.id );
}
options.controlOptions = {
anchor: $.ControlAnchor.NONE,
attachToViewer: false,
autoFade: false
};
} else {
options.id = 'navigator-' + $.now();
this.element = $.makeNeutralElement( "div" );
options.controlOptions = {
@ -82,14 +107,6 @@ $.Navigator = function( options ){
options.controlOptions.width = options.width;
}
}
} else {
this.element = document.getElementById( options.id );
options.controlOptions = {
anchor: $.ControlAnchor.NONE,
attachToViewer: false,
autoFade: false
};
}
this.element.id = options.id;
this.element.className += ' navigator';
@ -107,8 +124,9 @@ $.Navigator = function( options ){
showSequenceControl: false,
immediateRender: true,
blendTime: 0,
animationTime: 0,
autoResize: options.autoResize,
animationTime: options.animationTime,
// disable autoResize since resize behavior is implemented differently by the navigator
autoResize: false,
// prevent resizing the navigator from adding unwanted space around the image
minZoomImageRatio: 1.0,
background: options.background,
@ -166,6 +184,7 @@ $.Navigator = function( options ){
style.styleFloat = 'left'; //IE
style.zIndex = 999999999;
style.cursor = 'default';
style.boxSizing = 'content-box';
}( this.displayRegion.style, this.borderWidth ));
$.setElementPointerEventsNone( this.displayRegion );
$.setElementTouchActionNone( this.displayRegion );

View File

@ -2,7 +2,7 @@
* OpenSeadragon
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -60,7 +60,7 @@
/*
* Portions of this source file taken from mattsnider.com:
*
* Copyright (c) 2006-2013 Matt Snider
* Copyright (c) 2006-2022 Matt Snider
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -163,6 +163,10 @@
* The colors of grids in debug mode. Each tiled image's grid uses a consecutive color.
* If there are more tiled images than provided colors, the color vector is recycled.
*
* @property {Boolean} [silenceMultiImageWarnings=false]
* Silences warnings when calling viewport coordinate functions with multi-image.
* Useful when you're overlaying multiple images on top of one another.
*
* @property {Number} [blendTime=0]
* Specifies the duration of animation as higher or lower level tiles are
* replacing the existing tile.
@ -209,6 +213,17 @@
* You can pass a CSS color value like "#FF8800".
* When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern.
*
* @property {Object} [subPixelRoundingForTransparency=null]
* Determines when subpixel rounding should be applied for tiles when rendering images that support transparency.
* This property is a subpixel rounding enum values dictionary [{@link BROWSERS}] --> {@link SUBPIXEL_ROUNDING_OCCURRENCES}.
* The key is a {@link BROWSERS} value, and the value is one of {@link SUBPIXEL_ROUNDING_OCCURRENCES},
* indicating, for a given browser, when to apply subpixel rounding.
* Key '*' is the fallback value for any browser not specified in the dictionary.
* This property has a simple mode, and one can set it directly to
* {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER}, {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}
* in order to apply this rule for all browser. The values {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS} would be equivalent to { '*', SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS }.
* The default is {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} for all browsers, for backward compatibility reason.
*
* @property {Number} [degrees=0]
* Initial rotation.
*
@ -317,7 +332,7 @@
*
* @property {Number} [animationTime=1.2]
* Specifies the animation duration per each {@link OpenSeadragon.Spring}
* which occur when the image is dragged or zoomed.
* which occur when the image is dragged, zoomed or rotated.
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]
* Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})
@ -326,6 +341,9 @@
* @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture
* @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsMouse.dblClickDragToZoom=false] - Zoom on dragging through
* double-click gesture ( single click and next click to drag). Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsMouse.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
* the zoom is centered at the canvas center.
@ -336,11 +354,15 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]
* Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsTouch.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture
* @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsTouch.dblClickDragToZoom=true] - Zoom on dragging through
* double-click gesture ( single click and next click to drag). Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsTouch.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
* the zoom is centered at the canvas center.
@ -351,7 +373,7 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]
* Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsPen.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture
* @property {Boolean} [gestureSettingsPen.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
@ -366,11 +388,14 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]
* Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})
* @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsUnknown.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture
* @property {Boolean} [gestureSettingsUnknown.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsUnknown.dblClickDragToZoom=false] - Zoom on dragging through
* double-click gesture ( single click and next click to drag). Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture
* @property {Boolean} [gestureSettingsUnknown.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
* the zoom is centered at the canvas center.
@ -385,6 +410,9 @@
* @property {Number} [zoomPerScroll=1.2]
* The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
*
* @property {Number} [zoomPerDblClickDrag=1.2]
* The "zoom distance" per double-click mouse drag. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the double-click-drag-to-Zoom feature (also see gestureSettings[Mouse|Touch|Pen].dblClickDragToZoom).</em>
*
* @property {Number} [zoomPerSecond=1.0]
* Sets the zoom amount per second when zoomIn/zoomOut buttons are pressed and held.
* The value is a factor of the current zoom, so 1.0 (the default) disables zooming when the zoomIn/zoomOut buttons
@ -395,6 +423,11 @@
* @property {Boolean} [showNavigator=false]
* Set to true to make the navigator minimap appear.
*
* @property {Element} [navigatorElement=null]
* The element to hold the navigator minimap.
* If an element is specified, the Id option (see navigatorId) is ignored.
* If no element nor ID is specified, a div element will be generated accordingly.
*
* @property {String} [navigatorId=navigator-GENERATED DATE]
* The ID of a div to hold the navigator minimap.
* If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, navigator[Top|Left|Height|Width] and navigatorAutoFade options will be ignored.
@ -462,6 +495,12 @@
* @property {Number} [timeout=30000]
* 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]
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
*
@ -648,6 +687,20 @@
* @property {Object} [ajaxHeaders={}]
* A set of headers to include when making AJAX requests for tile sources or tiles.
*
* @property {Boolean} [splitHashDataForPost=false]
* Allows to treat _first_ hash ('#') symbol as a separator for POST data:
* URL to be opened by a {@link OpenSeadragon.TileSource} can thus look like: http://some.url#postdata=here.
* The whole URL is used to fetch image info metadata and it is then split to 'http://some.url' and
* 'postdata=here'; post data is given to the {@link OpenSeadragon.TileSource} of the choice and can be further
* used within tile requests (see TileSource methods).
* NOTE: {@link OpenSeadragon.TileSource.prototype.configure} return value should contain the post data
* if you want to use it later - so that it is given to your constructor later.
* NOTE: usually, post data is expected to be ampersand-separated (just like GET parameters), and is NOT USED
* to fetch tile image data unless explicitly programmed, or if loadTilesWithAjax=false 4
* (but it is still used for the initial image info request).
* NOTE: passing POST data from URL by this feature only supports string values, however,
* TileSource can send any data using POST as long as the header is correct
* (@see OpenSeadragon.TileSource.prototype.getTilePostData)
*/
/**
@ -783,6 +836,8 @@ function OpenSeadragon( options ){
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object AsyncFunction]': 'function',
'[object Promise]': 'promise',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regexp',
@ -802,7 +857,6 @@ function OpenSeadragon( options ){
return $.type(obj) === "function";
};
/**
* Taken from jQuery 1.6.1
* @function isArray
@ -888,7 +942,7 @@ function OpenSeadragon( options ){
/**
* Shim around Object.freeze. Does nothing if Object.freeze is not supported.
* @param {Object} obj The object to freeze.
* @return {Object} obj The frozen object.
* @returns {Object} obj The frozen object.
*/
$.freezeObject = function(obj) {
if (Object.freeze) {
@ -1067,8 +1121,19 @@ function OpenSeadragon( options ){
if ( options !== null || options !== undefined ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
var descriptor = Object.getOwnPropertyDescriptor(options, name);
if (descriptor !== undefined) {
if (descriptor.get || descriptor.set) {
Object.defineProperty(target, name, descriptor);
continue;
}
copy = descriptor.value;
} else {
$.console.warn('Could not copy inherited property "' + name + '".');
continue;
}
// Prevent never-ending loop
if ( target === copy ) {
@ -1077,6 +1142,8 @@ function OpenSeadragon( options ){
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( OpenSeadragon.isPlainObject( copy ) || ( copyIsArray = OpenSeadragon.isArray( copy ) ) ) ) {
src = target[ name ];
if ( copyIsArray ) {
copyIsArray = false;
clone = src && OpenSeadragon.isArray( src ) ? src : [];
@ -1129,6 +1196,7 @@ function OpenSeadragon( options ){
ajaxWithCredentials: false,
loadTilesWithAjax: false,
ajaxHeaders: {},
splitHashDataForPost: false,
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
panHorizontal: true,
@ -1155,6 +1223,7 @@ function OpenSeadragon( options ){
scrollToZoom: true,
clickToZoom: true,
dblClickToZoom: false,
dblClickDragToZoom: false,
pinchToZoom: false,
zoomToRefPoint: true,
flickEnabled: false,
@ -1167,6 +1236,7 @@ function OpenSeadragon( options ){
scrollToZoom: false,
clickToZoom: false,
dblClickToZoom: true,
dblClickDragToZoom: true,
pinchToZoom: true,
zoomToRefPoint: true,
flickEnabled: true,
@ -1179,6 +1249,7 @@ function OpenSeadragon( options ){
scrollToZoom: false,
clickToZoom: true,
dblClickToZoom: false,
dblClickDragToZoom: false,
pinchToZoom: false,
zoomToRefPoint: true,
flickEnabled: false,
@ -1191,6 +1262,7 @@ function OpenSeadragon( options ){
scrollToZoom: false,
clickToZoom: false,
dblClickToZoom: true,
dblClickDragToZoom: false,
pinchToZoom: true,
zoomToRefPoint: true,
flickEnabled: true,
@ -1200,6 +1272,7 @@ function OpenSeadragon( options ){
},
zoomPerClick: 2,
zoomPerScroll: 1.2,
zoomPerDblClickDrag: 1.2,
zoomPerSecond: 1.0,
blendTime: 0,
alwaysBlend: false,
@ -1235,6 +1308,7 @@ function OpenSeadragon( options ){
//VIEWPORT NAVIGATOR SETTINGS
showNavigator: false,
navigatorElement: null,
navigatorId: null,
navigatorPosition: null,
navigatorSizeRatio: 0.2,
@ -1263,6 +1337,7 @@ function OpenSeadragon( options ){
compositeOperation: null,
imageSmoothingEnabled: true,
placeholderFillStyle: null,
subPixelRoundingForTransparency: null,
//REFERENCE STRIP SETTINGS
showReferenceStrip: false,
@ -1286,6 +1361,8 @@ function OpenSeadragon( options ){
maxImageCacheCount: 200,
timeout: 30000,
useCanvas: true, // Use canvas element for drawing if available
tileRetryMax: 0,
tileRetryDelay: 2500,
//INTERFACE RESOURCE SETTINGS
prefixUrl: "/images/",
@ -1348,7 +1425,9 @@ function OpenSeadragon( options ){
//DEVELOPER SETTINGS
debugMode: false,
debugGridColor: ['#437AB2', '#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02', '#A6761D', '#666666']
debugGridColor: ['#437AB2', '#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02', '#A6761D', '#666666'],
silenceMultiImageWarnings: false
},
@ -1403,6 +1482,20 @@ function OpenSeadragon( options ){
CHROMEEDGE: 7
},
/**
* An enumeration of when subpixel rounding should occur.
* @static
* @type {Object}
* @property {Number} NEVER Never apply subpixel rounding for transparency.
* @property {Number} ONLY_AT_REST Do not apply subpixel rounding for transparency during animation (panning, zoom, rotation) and apply it once animation is over.
* @property {Number} ALWAYS Apply subpixel rounding for transparency during animation and when animation is over.
*/
SUBPIXEL_ROUNDING_OCCURRENCES: {
NEVER: 0,
ONLY_AT_REST: 1,
ALWAYS: 2
},
/**
* Keep track of which {@link Viewer}s have been created.
* - Key: {@link Element} to which a Viewer is attached.
@ -1587,8 +1680,8 @@ function OpenSeadragon( options ){
/**
* Compute the modulo of a number but makes sure to always return
* a positive value.
* @param {Number} number the number to computes the modulo of
* a positive value (also known as Euclidean modulo).
* @param {Number} number the number to compute the modulo of
* @param {Number} modulo the modulo
* @returns {Number} the result of the modulo of number
*/
@ -1600,6 +1693,7 @@ function OpenSeadragon( options ){
return result;
},
/**
* Determines if a point is within the bounding rectangle of the given element (hit-test).
* @function
@ -1939,7 +2033,7 @@ function OpenSeadragon( options ){
*/
setElementPointerEvents: function( element, value ) {
element = $.getElement( element );
if ( typeof element.style.pointerEvents !== 'undefined' ) {
if (typeof element.style !== 'undefined' && typeof element.style.pointerEvents !== 'undefined' ) {
element.style.pointerEvents = value;
}
},
@ -2048,7 +2142,7 @@ function OpenSeadragon( options ){
* @param {Boolean} [options.capture]
* @param {Boolean} [options.passive]
* @param {Boolean} [options.once]
* @return {String} The protocol (http:, https:, file:, ftp: ...)
* @returns {String} The protocol (http:, https:, file:, ftp: ...)
*/
normalizeEventListenerOptions: function (options) {
var opts;
@ -2212,7 +2306,7 @@ function OpenSeadragon( options ){
* @function
* @private
* @param {String} url The url to retrieve the protocol from.
* @return {String} The protocol (http:, https:, file:, ftp: ...)
* @returns {String} The protocol (http:, https:, file:, ftp: ...)
*/
getUrlProtocol: function( url ) {
var match = url.match(/^([a-z]+:)\/\//i);
@ -2272,7 +2366,9 @@ function OpenSeadragon( options ){
* @param {Function} options.success - a function to call on a successful response
* @param {Function} options.error - a function to call on when an error occurs
* @param {Object} options.headers - headers to add to the AJAX request
* @param {String} options.responseType - the response type of the the AJAX request
* @param {String} options.responseType - the response type of the AJAX request
* @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData), GET method used if null
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
* @throws {Error}
* @returns {XMLHttpRequest}
@ -2281,6 +2377,7 @@ function OpenSeadragon( options ){
var withCredentials;
var headers;
var responseType;
var postData;
// Note that our preferred API is that you pass in a single object; the named
// arguments are for legacy support.
@ -2290,6 +2387,7 @@ function OpenSeadragon( options ){
withCredentials = url.withCredentials;
headers = url.headers;
responseType = url.responseType || null;
postData = url.postData || null;
url = url.url;
}
@ -2313,17 +2411,18 @@ function OpenSeadragon( options ){
protocol !== "https:" )) {
onSuccess( request );
} else {
$.console.log( "AJAX request returned %d: %s", request.status, url );
if ( $.isFunction( onError ) ) {
onError( request );
} else {
$.console.error( "AJAX request returned %d: %s", request.status, url );
}
}
}
};
var method = postData ? "POST" : "GET";
try {
request.open( "GET", url, true );
request.open( method, url, true );
if (responseType) {
request.responseType = responseType;
@ -2341,9 +2440,9 @@ function OpenSeadragon( options ){
request.withCredentials = true;
}
request.send(null);
request.send(postData);
} catch (e) {
$.console.log( "%s while making AJAX request: %s", e.name, e.message );
$.console.error( "%s while making AJAX request: %s", e.name, e.message );
request.onreadystatechange = function(){};

View File

@ -2,7 +2,7 @@
* OpenSeadragon - OsmTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -122,10 +122,11 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
* @function
* @param {Object} data - the raw configuration
* @param {String} url - the url the data was retrieved from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
*/
configure: function( data, url ){
configure: function( data, url, postData ){
return data;
},

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Overlay
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -299,18 +299,18 @@
this.adjust(position, size);
var rotate = 0;
if (viewport.degrees &&
if (viewport.getRotation(true) &&
this.rotationMode !== $.OverlayRotationMode.NO_ROTATION) {
// BOUNDING_BOX is only valid if both directions get scaled.
// Get replaced by EXACT otherwise.
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX &&
this.width !== null && this.height !== null) {
var rect = new $.Rect(position.x, position.y, size.x, size.y);
var boundingBox = this._getBoundingBox(rect, viewport.degrees);
var boundingBox = this._getBoundingBox(rect, viewport.getRotation(true));
position = boundingBox.getTopLeft();
size = boundingBox.getSize();
} else {
rotate = viewport.degrees;
rotate = viewport.getRotation(true);
}
}
@ -447,7 +447,7 @@
// private
_adjustBoundsForRotation: function(viewport, bounds) {
if (!viewport ||
viewport.degrees === 0 ||
viewport.getRotation(true) === 0 ||
this.rotationMode === $.OverlayRotationMode.EXACT) {
return bounds;
}
@ -467,7 +467,7 @@
}
// NO_ROTATION case
return bounds.rotate(-viewport.degrees,
return bounds.rotate(-viewport.getRotation(true),
this._getPlacementPoint(bounds));
}
};

View File

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

View File

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

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Rect
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -230,7 +230,7 @@ $.Rect.prototype = {
* Returns the width and height component as a vector OpenSeadragon.Point
* @function
* @returns {OpenSeadragon.Point} The 2 dimensional vector representing the
* the width and height of the rectangle.
* width and height of the rectangle.
*/
getSize: function() {
return new $.Point(this.width, this.height);
@ -240,7 +240,7 @@ $.Rect.prototype = {
* Determines if two Rectangles have equivalent components.
* @function
* @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.
* @return {Boolean} 'true' if all components are equal, otherwise 'false'.
* @returns {Boolean} 'true' if all components are equal, otherwise 'false'.
*/
equals: function(other) {
return (other instanceof $.Rect) &&
@ -287,7 +287,7 @@ $.Rect.prototype = {
* Returns the smallest rectangle that will contain this and the given
* rectangle bounding boxes.
* @param {OpenSeadragon.Rect} rect
* @return {OpenSeadragon.Rect} The new rectangle.
* @returns {OpenSeadragon.Rect} The new rectangle.
*/
union: function(rect) {
var thisBoundingBox = this.getBoundingBox();
@ -313,7 +313,7 @@ $.Rect.prototype = {
* Returns the bounding box of the intersection of this rectangle with the
* given rectangle.
* @param {OpenSeadragon.Rect} rect
* @return {OpenSeadragon.Rect} the bounding box of the intersection
* @returns {OpenSeadragon.Rect} the bounding box of the intersection
* or null if the rectangles don't intersect.
*/
intersection: function(rect) {
@ -441,7 +441,7 @@ $.Rect.prototype = {
* @param {Number} degrees The angle in degrees to rotate.
* @param {OpenSeadragon.Point} [pivot] The point about which to rotate.
* Defaults to the center of the rectangle.
* @return {OpenSeadragon.Rect}
* @returns {OpenSeadragon.Rect}
*/
rotate: function(degrees, pivot) {
degrees = $.positiveModulo(degrees, 360);

View File

@ -2,7 +2,7 @@
* OpenSeadragon - ReferenceStrip
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -288,7 +288,7 @@ $.ReferenceStrip.prototype = {
*/
update: function () {
if ( THIS[this.id].animating ) {
$.console.log( 'image reference strip update' );
// $.console.log( 'image reference strip update' );
return true;
}
return false;
@ -321,7 +321,8 @@ function onStripClick( event ) {
var page;
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 {
page = Math.floor(event.position.y / this.panelHeight);
}

View File

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

View File

@ -2,7 +2,7 @@
* OpenSeadragon - getString/setString
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -83,7 +83,7 @@ $.extend( $, /** @lends OpenSeadragon */{
string = container[ props[ i ] ];
if ( typeof ( string ) !== "string" ) {
$.console.log( "Untranslated source string:", prop );
$.console.error( "Untranslated source string:", prop );
string = ""; // FIXME: this breaks gettext()-style convention, which would return source
}

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Tile
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -44,7 +44,7 @@
* coordinates.
* @param {Boolean} exists Is this tile a part of a sparse image? ( Also has
* this tile failed to load? )
* @param {String} url The URL of this tile's image.
* @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
* is provided directly by the tile source.
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
@ -52,8 +52,11 @@
* @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the
* drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
* with HTML the entire tile is always used.
* @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null
* @param {String} cacheKey key to act as a tile cache, must be unique for tiles with unique image data
*/
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds) {
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData, cacheKey) {
/**
* The zoom level this tile belongs to.
* @member {Number} level
@ -92,11 +95,21 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
*/
this.exists = exists;
/**
* The URL of this tile's image.
* @member {String} url
* Private property to hold string url or url retriever function.
* Consumers should access via Tile.getUrl()
* @private
* @member {String|Function} url
* @memberof OpenSeadragon.Tile#
*/
this.url = url;
this._url = url;
/**
* Post parameters for this tile. For example, it can be an URL-encoded string
* in k1=v1&k2=v2... format, or a JSON, or a FormData instance... or null if no POST request used
* @member {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null
* @memberof OpenSeadragon.Tile#
*/
this.postData = postData;
/**
* The context2D of this tile if it is provided directly by the tile source.
* @member {CanvasRenderingContext2D} context2D
@ -116,16 +129,18 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* @memberof OpenSeadragon.Tile#
*/
this.ajaxHeaders = ajaxHeaders;
if (cacheKey === undefined) {
$.console.warn("Tile constructor needs 'cacheKey' variable: creation tile cache" +
" in Tile class is deprecated. TileSource.prototype.getTileHashKey will be used.");
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
}
/**
* The unique cache key for this tile.
* @member {String} cacheKey
* @memberof OpenSeadragon.Tile#
*/
if (this.ajaxHeaders) {
this.cacheKey = this.url + "+" + JSON.stringify(this.ajaxHeaders);
} else {
this.cacheKey = this.url;
}
this.cacheKey = cacheKey;
/**
* Is this tile loaded?
* @member {Boolean} loaded
@ -151,12 +166,6 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* @memberof OpenSeadragon.Tile#
*/
this.imgElement = null;
/**
* The Image object for this tile.
* @member {Object} image
* @memberof OpenSeadragon.Tile#
*/
this.image = null;
/**
* The alias of this.element.style.
@ -209,6 +218,13 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
*/
this.visibility = null;
/**
* The transparency indicator of this tile.
* @member {Boolean} hasTransparency true if tile contains transparency for correct rendering
* @memberof OpenSeadragon.Tile#
*/
this.hasTransparency = false;
/**
* Whether this tile is currently being drawn.
* @member {Boolean} beingDrawn
@ -253,7 +269,9 @@ $.Tile.prototype = {
// private
_hasTransparencyChannel: function() {
return !!this.context2D || this.url.match('.png');
console.warn("Tile.prototype._hasTransparencyChannel() has been " +
"deprecated and will be removed in the future. Use TileSource.prototype.hasTransparency() instead.");
return !!this.context2D || this.getUrl().match('.png');
},
/**
@ -281,8 +299,13 @@ $.Tile.prototype = {
// content during animation of the container size.
if ( !this.element ) {
var image = this.getImage();
if (!image) {
return;
}
this.element = $.makeNeutralElement( "div" );
this.imgElement = this.cacheImageRecord.getImage().cloneNode();
this.imgElement = image.cloneNode();
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
this.imgElement.style.width = "100%";
this.imgElement.style.height = "100%";
@ -309,6 +332,59 @@ $.Tile.prototype = {
$.setElementOpacity( this.element, this.opacity );
},
/**
* The Image object for this tile.
* @member {Object} image
* @memberof OpenSeadragon.Tile#
* @deprecated
* @returns {Image}
*/
get image() {
$.console.error("[Tile.image] property has been deprecated. Use [Tile.prototype.getImage] instead.");
return this.getImage();
},
/**
* The URL of this tile's image.
* @member {String} url
* @memberof OpenSeadragon.Tile#
* @deprecated
* @returns {String}
*/
get url() {
$.console.error("[Tile.url] property has been deprecated. Use [Tile.prototype.getUrl] instead.");
return this.getUrl();
},
/**
* Get the Image object for this tile.
* @returns {Image}
*/
getImage: function() {
return this.cacheImageRecord.getImage();
},
/**
* Get the url string for this tile.
* @returns {String}
*/
getUrl: function() {
if (typeof this._url === 'function') {
return this._url();
}
return this._url;
},
/**
* Get the CanvasRenderingContext2D instance for tile image data drawn
* onto Canvas if enabled and available
* @returns {CanvasRenderingContext2D}
*/
getCanvasContext: function() {
return this.context2D || this.cacheImageRecord.getRenderedContext();
},
/**
* Renders the tile in a canvas-based context.
* @function
@ -318,8 +394,12 @@ $.Tile.prototype = {
* where <code>rendered</code> is the context with the pre-drawn image.
* @param {Number} [scale=1] - Apply a scale to position and size
* @param {OpenSeadragon.Point} [translate] - A translation vector
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
* position and size of tiles supporting alpha channel in non-transparency
* context.
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
*/
drawCanvas: function( context, drawingHandler, scale, translate ) {
drawCanvas: function( context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
var position = this.position.times($.pixelDensityRatio),
size = this.size.times($.pixelDensityRatio),
@ -332,7 +412,7 @@ $.Tile.prototype = {
return;
}
rendered = this.context2D || this.cacheImageRecord.getRenderedContext();
rendered = this.getCanvasContext();
if ( !this.loaded || !rendered ){
$.console.warn(
@ -344,7 +424,6 @@ $.Tile.prototype = {
}
context.save();
context.globalAlpha = this.opacity;
if (typeof scale === 'number' && scale !== 1) {
@ -362,7 +441,15 @@ $.Tile.prototype = {
//ie its done fading or fading is turned off, and if we are drawing
//an image with an alpha channel, then the only way
//to avoid seeing the tile underneath is to clear the rectangle
if (context.globalAlpha === 1 && this._hasTransparencyChannel()) {
if (context.globalAlpha === 1 && this.hasTransparency) {
if (shouldRoundPositionAndSize) {
// Round to the nearest whole pixel so we don't get seams from overlap.
position.x = Math.round(position.x);
position.y = Math.round(position.y);
size.x = Math.round(size.x);
size.y = Math.round(size.y);
}
//clearing only the inside of the rectangle occupied
//by the png prevents edge flikering
context.clearRect(
@ -408,7 +495,7 @@ $.Tile.prototype = {
/**
* Get the ratio between current and original size.
* @function
* @return {Float}
* @returns {Float}
*/
getScaleForEdgeSmoothing: function() {
var context;
@ -430,7 +517,7 @@ $.Tile.prototype = {
* Needed to avoid swimming and twitching.
* @function
* @param {Number} [scale=1] - Scale to be applied to position.
* @return {OpenSeadragon.Point}
* @returns {OpenSeadragon.Point}
*/
getTranslationForEdgeSmoothing: function(scale, canvasSize, sketchCanvasSize) {
// The translation vector must have positive values, otherwise the image goes a bit off

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TileCache
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -46,43 +46,22 @@ var TileRecord = function( options ) {
// private class
var ImageRecord = function(options) {
$.console.assert( options, "[ImageRecord] options is required" );
$.console.assert( options.image, "[ImageRecord] options.image is required" );
this._image = options.image;
$.console.assert( options.data, "[ImageRecord] options.data is required" );
this._tiles = [];
options.create.apply(null, [this, options.data, options.ownerTile]);
this._destroyImplementation = options.destroy.bind(null, this);
this.getImage = options.getImage.bind(null, this);
this.getData = options.getData.bind(null, this);
this.getRenderedContext = options.getRenderedContext.bind(null, this);
};
ImageRecord.prototype = {
destroy: function() {
this._image = null;
this._renderedContext = null;
this._destroyImplementation();
this._tiles = null;
},
getImage: function() {
return this._image;
},
getRenderedContext: function() {
if (!this._renderedContext) {
var canvas = document.createElement( 'canvas' );
canvas.width = this._image.width;
canvas.height = this._image.height;
this._renderedContext = canvas.getContext('2d');
this._renderedContext.drawImage( this._image, 0, 0 );
//since we are caching the prerendered image on a canvas
//allow the image to not be held in memory
this._image = null;
}
return this._renderedContext;
},
setRenderedContext: function(renderedContext) {
$.console.error("ImageRecord.setRenderedContext is deprecated. " +
"The rendered context should be created by the ImageRecord " +
"itself when calling ImageRecord.getRenderedContext.");
this._renderedContext = renderedContext;
},
addTile: function(tile) {
$.console.assert(tile, '[ImageRecord.addTile] tile is required');
this._tiles.push(tile);
@ -158,9 +137,22 @@ $.TileCache.prototype = {
var imageRecord = this._imagesLoaded[options.tile.cacheKey];
if (!imageRecord) {
$.console.assert( options.image, "[TileCache.cacheTile] options.image is required to create an ImageRecord" );
if (!options.data) {
$.console.error("[TileCache.cacheTile] options.image was renamed to options.data. '.image' attribute " +
"has been deprecated and will be removed in the future.");
options.data = options.image;
}
$.console.assert( options.data, "[TileCache.cacheTile] options.data is required to create an ImageRecord" );
imageRecord = this._imagesLoaded[options.tile.cacheKey] = new ImageRecord({
image: options.image
data: options.data,
ownerTile: options.tile,
create: options.tiledImage.source.createTileCache,
destroy: options.tiledImage.source.destroyTileCache,
getImage: options.tiledImage.source.getTileCacheDataAsImage,
getData: options.tiledImage.source.getTileCacheData,
getRenderedContext: options.tiledImage.source.getTileCacheDataAsContext2D,
});
this._imagesLoadedCount++;

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -69,6 +69,9 @@
* the XHR's withCredentials (for accessing secure data).
* @param {Object} [options.ajaxHeaders]
* A set of headers to include in AJAX requests.
* @param {Boolean} [options.splitHashDataForPost]
* First occurrence of '#' in the options.url is used to split URL
* and the latter part is treated as POST data (applies to getImageInfo(...))
* @param {Number} [options.width]
* Width of the source image at max resolution in pixels.
* @param {Number} [options.height]
@ -275,12 +278,32 @@ $.TileSource.prototype = {
return this._tileHeight;
},
/**
* Set the maxLevel to the given level, and perform the memoization of
* getLevelScale with the new maxLevel. This function can be useful if the
* memoization is required before the first call of getLevelScale, or both
* memoized getLevelScale and maxLevel should be changed accordingly.
* @function
* @param {Number} level
*/
setMaxLevel: function( level ) {
this.maxLevel = level;
this._memoizeLevelScale();
},
/**
* @function
* @param {Number} level
*/
getLevelScale: function( level ) {
// if getLevelScale is not memoized, we generate the memoized version
// at the first call and return the result
this._memoizeLevelScale();
return this.getLevelScale( level );
},
// private
_memoizeLevelScale: function() {
// see https://github.com/openseadragon/openseadragon/issues/22
// we use the tilesources implementation of getLevelScale to generate
// a memoized re-implementation
@ -292,7 +315,6 @@ $.TileSource.prototype = {
this.getLevelScale = function( _level ){
return levelScaleCache[ _level ];
};
return this.getLevelScale( level );
},
/**
@ -426,6 +448,15 @@ $.TileSource.prototype = {
}
}
var postData = null;
if (this.splitHashDataForPost) {
var hashIdx = url.indexOf("#");
if (hashIdx !== -1) {
postData = url.substring(hashIdx + 1);
url = url.substr(0, hashIdx);
}
}
callback = function( data ){
if( typeof (data) === "string" ) {
data = $.parseXml( data );
@ -447,7 +478,7 @@ $.TileSource.prototype = {
return;
}
options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
options = $TileSource.prototype.configure.apply( _this, [ data, url, postData ]);
if (options.ajaxWithCredentials === undefined) {
options.ajaxWithCredentials = _this.ajaxWithCredentials;
}
@ -482,6 +513,7 @@ $.TileSource.prototype = {
// request info via xhr asynchronously.
$.makeAjaxRequest( {
url: url,
postData: postData,
withCredentials: this.ajaxWithCredentials,
headers: this.ajaxHeaders,
success: function( xhr ) {
@ -497,7 +529,7 @@ $.TileSource.prototype = {
exception rather than the second one raised when we try to access xhr.status
*/
try {
msg = "HTTP " + xhr.status + " attempting to load TileSource";
msg = "HTTP " + xhr.status + " attempting to load TileSource: " + url;
} catch ( e ) {
var formattedExc;
if ( typeof ( exc ) === "undefined" || !exc.toString ) {
@ -506,9 +538,11 @@ $.TileSource.prototype = {
formattedExc = exc.toString();
}
msg = formattedExc + " attempting to load TileSource";
msg = formattedExc + " attempting to load TileSource: " + url;
}
$.console.error(msg);
/***
* Raised when an error occurs loading a TileSource.
*
@ -518,11 +552,14 @@ $.TileSource.prototype = {
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {String} message
* @property {String} source
* @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', {
message: msg,
source: url
source: url,
postData: postData
});
}
});
@ -542,7 +579,7 @@ $.TileSource.prototype = {
* @param {String|Object|Array|Document} data
* @param {String} url - the url the data was loaded
* from if any.
* @return {Boolean}
* @returns {Boolean}
*/
supports: function( data, url ) {
return false;
@ -559,11 +596,14 @@ $.TileSource.prototype = {
* @param {String|Object|Array|Document} data
* @param {String} url - the url the data was loaded
* from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null value obtained from
* the protocol URL after '#' sign if flag splitHashDataForPost set to 'true'
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure the tile source constructor (include all values you want to
* instantiate the TileSource subclass with - what _options_ object should contain).
* @throws {Error}
*/
configure: function( data, url ) {
configure: function( data, url, postData ) {
throw new Error( "Method not implemented." );
},
@ -578,12 +618,44 @@ $.TileSource.prototype = {
* @param {Number} level
* @param {Number} x
* @param {Number} y
* @returns {String|Function} url - A string for the url or a function that returns a url string.
* @throws {Error}
*/
getTileUrl: function( level, x, y ) {
throw new Error( "Method not implemented." );
},
/**
* Must use AJAX in order to work, i.e. loadTilesWithAjax = true is set.
* If a value is returned, ajax issues POST request to the tile url.
* If null is returned, ajax issues GET request.
* The return value must comply to the header 'content type'.
*
* Examples (USED HEADER --> getTilePostData CODE):
* 'Content-type': 'application/x-www-form-urlencoded' -->
* return "key1=value=1&key2=value2";
*
* 'Content-type': 'application/x-www-form-urlencoded' -->
* return JSON.stringify({key: "value", number: 5});
*
* 'Content-type': 'multipart/form-data' -->
* let result = new FormData();
* result.append("data", myData);
* return result;
*
* IMPORTANT: in case you move all the logic on image fetching
* to post data, you must re-define 'getTileHashKey(...)' to
* stay unique for different tile images.
*
* @param {Number} level
* @param {Number} x
* @param {Number} y
* @returns {*|null} post data to send with tile configuration request
*/
getTilePostData: function( level, x, y ) {
return null;
},
/**
* Responsible for retrieving the headers which will be attached to the image request for the
* region specified by the given x, y, and level components.
@ -601,6 +673,32 @@ $.TileSource.prototype = {
return {};
},
/**
* The tile cache object is uniquely determined by this key and used to lookup
* the image data in cache: keys should be different if images are different.
*
* In case a tile has context2D property defined (TileSource.prototype.getContext2D)
* or its context2D is set manually; the cache is not used and this function
* is irrelevant.
* Note: default behaviour does not take into account post data.
* @param {Number} level tile level it was fetched with
* @param {Number} x x-coordinate in the pyramid level
* @param {Number} y y-coordinate in the pyramid level
* @param {String} url the tile was fetched with
* @param {Object} ajaxHeaders the tile was fetched with
* @param {*} postData data the tile was fetched with (type depends on getTilePostData(..) return type)
*/
getTileHashKey: function(level, x, y, url, ajaxHeaders, postData) {
function withHeaders(hash) {
return ajaxHeaders ? hash + "+" + JSON.stringify(ajaxHeaders) : hash;
}
if (typeof url !== "string") {
return withHeaders(level + "/" + x + "_" + y);
}
return withHeaders(url);
},
/**
* @function
* @param {Number} level
@ -615,6 +713,194 @@ $.TileSource.prototype = {
y >= 0 &&
x < numTiles.x &&
y < numTiles.y;
},
/**
* Decide whether tiles have transparency: this is crucial for correct images blending.
* @returns {boolean} true if the image has transparency
*/
hasTransparency: function(context2D, url, ajaxHeaders, post) {
return !!context2D || url.match('.png');
},
/**
* Download tile data.
* Note that if you override this function, you should override also downloadTileAbort().
* @param {ImageJob} context job context that you have to call finish(...) on.
* @param {String} [context.src] - URL of image to download.
* @param {String} [context.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [context.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {Boolean} [context.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
* @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [context.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null
* @param {*} [context.userData] - Empty object to attach your own data and helper variables to.
* @param {Function} [context.finish] - Should be called unless abort() was executed, e.g. on all occasions,
* be it successful or unsuccessful request.
* Usage: context.finish(data, request, errMessage). Pass the downloaded data object or null upon failure.
* Add also reference to an ajax request if used. Provide error message in case of failure.
* @param {Function} [context.abort] - Called automatically when the job times out.
* Usage: context.abort().
* @param {Function} [context.callback] @private - Called automatically once image has been downloaded
* (triggered by finish).
* @param {Number} [context.timeout] @private - The max number of milliseconds that
* this image job may take to complete.
* @param {string} [context.errorMsg] @private - The final error message, default null (set by finish).
*/
downloadTileStart: function (context) {
var dataStore = context.userData,
image = new Image();
dataStore.image = image;
dataStore.request = null;
var finish = function(error) {
if (!image) {
context.finish(null, dataStore.request, "Image load failed: undefined Image instance.");
return;
}
image.onload = image.onerror = image.onabort = null;
context.finish(error ? null : image, dataStore.request, error);
};
image.onload = function () {
finish();
};
image.onabort = image.onerror = function() {
finish("Image load aborted.");
};
// Load the tile with an AJAX request if the loadWithAjax option is
// set. Otherwise load the image by setting the source proprety of the image object.
if (context.loadWithAjax) {
dataStore.request = $.makeAjaxRequest({
url: context.src,
withCredentials: context.ajaxWithCredentials,
headers: context.ajaxHeaders,
responseType: "arraybuffer",
postData: context.postData,
success: function(request) {
var blb;
// Make the raw data into a blob.
// BlobBuilder fallback adapted from
// http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility
try {
blb = new window.Blob([request.response]);
} catch (e) {
var BlobBuilder = (
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder
);
if (e.name === 'TypeError' && BlobBuilder) {
var bb = new BlobBuilder();
bb.append(request.response);
blb = bb.getBlob();
}
}
// If the blob is empty for some reason consider the image load a failure.
if (blb.size === 0) {
finish("Empty image response.");
} else {
// Create a URL for the blob data and make it the source of the image object.
// This will still trigger Image.onload to indicate a successful tile load.
image.src = (window.URL || window.webkitURL).createObjectURL(blb);
}
},
error: function(request) {
finish("Image load aborted - XHR error");
}
});
} else {
if (context.crossOriginPolicy !== false) {
image.crossOrigin = context.crossOriginPolicy;
}
image.src = context.src;
}
},
/**
* Provide means of aborting the execution.
* Note that if you override this function, you should override also downloadTileStart().
* @param {ImageJob} context job, the same object as with downloadTileStart(..)
* @param {*} [context.userData] - Empty object to attach (and mainly read) your own data.
*/
downloadTileAbort: function (context) {
if (context.userData.request) {
context.userData.request.abort();
}
var image = context.userData.image;
if (context.userData.image) {
image.onload = image.onerror = image.onabort = null;
}
},
/**
* Create cache object from the result of the download process. The
* cacheObject parameter should be used to attach the data to, there are no
* conventions on how it should be stored - all the logic is implemented within *TileCache() functions.
*
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
* @param {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
* @param {Tile} tile instance the cache was created with
*/
createTileCache: function(cacheObject, data, tile) {
cacheObject._data = data;
},
/**
* Cache object destructor, unset all properties you created to allow GC collection.
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
*/
destroyTileCache: function (cacheObject) {
cacheObject._data = null;
cacheObject._renderedContext = null;
},
/**
* Raw data getter
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
* @returns {*} cache data
*/
getTileCacheData: function(cacheObject) {
return cacheObject._data;
},
/**
* Compatibility image element getter
* - plugins might need image representation of the data
* - div HTML rendering relies on image element presence
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
* @returns {Image} cache data as an Image
*/
getTileCacheDataAsImage: function(cacheObject) {
return cacheObject._data; //the data itself by default is Image
},
/**
* Compatibility context 2D getter
* - most heavily used rendering method is a canvas-based approach,
* convert the data to a canvas and return it's 2D context
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
* @returns {CanvasRenderingContext2D} context of the canvas representation of the cache data
*/
getTileCacheDataAsContext2D: function(cacheObject) {
if (!cacheObject._renderedContext) {
var canvas = document.createElement( 'canvas' );
canvas.width = cacheObject._data.width;
canvas.height = cacheObject._data.height;
cacheObject._renderedContext = canvas.getContext('2d');
cacheObject._renderedContext.drawImage( cacheObject._data, 0, 0 );
//since we are caching the prerendered image on a canvas
//allow the image to not be held in memory
cacheObject._data = null;
}
return cacheObject._renderedContext;
}
};

View File

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

View File

@ -2,7 +2,7 @@
* OpenSeadragon - TmsTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -111,10 +111,11 @@ $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
* @function
* @param {Object} data - the raw configuration
* @param {String} url - the url the data was retrieved from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
*/
configure: function( data, url ){
configure: function( data, url, postData ){
return data;
},

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Viewer
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -182,7 +182,7 @@ $.Viewer = function( options ) {
navImages: null,
//interface button controls
buttons: null,
buttonGroup: null,
//TODO: this is defunct so safely remove it
profiler: null
@ -204,6 +204,8 @@ $.Viewer = function( options ) {
prevContainerSize: null,
animating: false,
forceRedraw: false,
needsResize: false,
forceResize: false,
mouseInside: false,
group: null,
// whether we should be continuously zooming
@ -212,7 +214,9 @@ $.Viewer = function( options ) {
zoomFactor: null,
lastZoomTime: null,
fullPage: false,
onfullscreenchange: null
onfullscreenchange: null,
lastClickTime: null,
draggingToZoom: false,
};
this._sequenceIndex = 0;
@ -326,6 +330,17 @@ $.Viewer = function( options ) {
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
this.world = new $.World({
viewer: this
@ -382,7 +397,8 @@ $.Viewer = function( options ) {
flipped: this.flipped,
navigatorRotate: this.navigatorRotate,
homeFillsViewer: this.homeFillsViewer,
margins: this.viewportMargins
margins: this.viewportMargins,
silenceMultiImageWarnings: this.silenceMultiImageWarnings
});
this.viewport._setContentBounds(this.world.getHomeBounds(), this.world.getContentFactor());
@ -390,7 +406,9 @@ $.Viewer = function( options ) {
// Create the image loader
this.imageLoader = new $.ImageLoader({
jobLimit: this.imageLoaderLimit,
timeout: options.timeout
timeout: options.timeout,
tileRetryMax: this.tileRetryMax,
tileRetryDelay: this.tileRetryDelay
});
// Create the tile cache
@ -408,8 +426,6 @@ $.Viewer = function( options ) {
// Overlay container
this.overlaysContainer = $.makeNeutralElement( "div" );
$.setElementPointerEventsNone( this.overlaysContainer );
$.setElementTouchActionNone( this.overlaysContainer );
this.canvas.appendChild( this.overlaysContainer );
// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons
@ -432,6 +448,7 @@ $.Viewer = function( options ) {
//Instantiate a navigator if configured
if ( this.showNavigator){
this.navigator = new $.Navigator({
element: this.navigatorElement,
id: this.navigatorId,
position: this.navigatorPosition,
sizeRatio: this.navigatorSizeRatio,
@ -449,7 +466,8 @@ $.Viewer = function( options ) {
opacity: this.navigatorOpacity,
borderColor: this.navigatorBorderColor,
displayRegionColor: this.navigatorDisplayRegionColor,
crossOriginPolicy: this.crossOriginPolicy
crossOriginPolicy: this.crossOriginPolicy,
animationTime: this.animationTime,
});
}
@ -490,7 +508,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {Boolean}
* @returns {Boolean}
*/
isOpen: function () {
return !!this.world.getItemCount();
@ -508,6 +526,12 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
return this.open( tileSource );
},
//deprecated
get buttons () {
$.console.warn('Viewer.buttons is deprecated; Please use Viewer.buttonGroup');
return this.buttonGroup;
},
/**
* Open tiled images into the viewer, closing any others.
* To get the TiledImage instance created by open, add an event listener for
@ -522,7 +546,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* of the options parameter for {@link OpenSeadragon.Viewer#addTiledImage}.
* @param {Number} initialPage - If sequenceMode is true, display this page initially
* for the given tileSources. If specified, will overwrite the Viewer's existing initialPage property.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:open
* @fires OpenSeadragon.Viewer.event:open-failed
*/
@ -694,7 +718,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:close
*/
close: function ( ) {
@ -715,6 +739,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
}
THIS[ this.hash ].animating = false;
this.world.removeAll();
this.imageLoader.clear();
@ -746,6 +771,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* viewer = null; //important
*
* @function
* @fires OpenSeadragon.Viewer.event:before-destroy
* @fires OpenSeadragon.Viewer.event:destroy
*/
destroy: function( ) {
if ( !THIS[ this.hash ] ) {
@ -753,6 +780,17 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
return;
}
/**
* Raised when the viewer is about to be destroyed (see {@link OpenSeadragon.Viewer#before-destroy}).
*
* @event before-destroy
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'before-destroy' );
this._removeUpdatePixelDensityRatioEvent();
this.close();
@ -763,6 +801,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
//TODO: implement this...
//this.unbindSequenceControls()
//this.unbindStandardControls()
if (this._resizeObserver){
this._resizeObserver.disconnect();
}
if (this.referenceStrip) {
this.referenceStrip.destroy();
@ -785,7 +826,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.navigator = null;
}
this.removeAllHandlers();
if (this.buttonGroup) {
this.buttonGroup.destroy();
@ -831,11 +871,24 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
// clear our reference to the main element - they will need to pass it in again, creating a new viewer
this.element = null;
/**
* Raised when the viewer is destroyed (see {@link OpenSeadragon.Viewer#destroy}).
*
* @event destroy
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'destroy' );
this.removeAllHandlers();
},
/**
* @function
* @return {Boolean}
* @returns {Boolean}
*/
isMouseNavEnabled: function () {
return this.innerTracker.isTracking();
@ -844,7 +897,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @param {Boolean} enabled - true to enable, false to disable
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:mouse-enabled
*/
setMouseNavEnabled: function( enabled ){
@ -867,7 +920,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {Boolean}
* @returns {Boolean}
*/
areControlsEnabled: function () {
var enabled = this.controls.length,
@ -884,7 +937,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
*
* @function
* @param {Boolean} true to show, false to hide.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:controls-enabled
*/
setControlsEnabled: function( enabled ) {
@ -925,7 +978,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {Boolean}
* @returns {Boolean}
*/
isFullPage: function () {
return THIS[ this.hash ].fullPage;
@ -937,7 +990,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @function
* @param {Boolean} fullPage
* If true, enter full page mode. If false, exit full page mode.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:pre-full-page
* @fires OpenSeadragon.Viewer.event:full-page
*/
@ -1041,8 +1094,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
$.addClass( this.element, 'fullpage' );
body.appendChild( this.element );
this.element.style.height = $.getWindowSize().y + 'px';
this.element.style.width = $.getWindowSize().x + 'px';
this.element.style.height = '100vh';
this.element.style.width = '100vw';
if ( this.toolbar && this.toolbar.element ) {
this.element.style.height = (
@ -1152,7 +1205,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @function
* @param {Boolean} fullScreen
* If true, enter full screen mode. If false, exit full screen mode.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:pre-full-screen
* @fires OpenSeadragon.Viewer.event:full-screen
*/
@ -1247,17 +1300,26 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {Boolean}
* @returns {Boolean}
*/
isVisible: function () {
return this.container.style.visibility !== "hidden";
},
//
/**
* @function
* @returns {Boolean} returns true if the viewer is in fullscreen
*/
isFullScreen: function () {
return $.isFullScreen() && this.isFullPage();
},
/**
* @function
* @param {Boolean} visible
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:visible
*/
setVisible: function( visible ){
@ -1496,7 +1558,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
ajaxWithCredentials: queueItem.options.ajaxWithCredentials,
loadTilesWithAjax: queueItem.options.loadTilesWithAjax,
ajaxHeaders: queueItem.options.ajaxHeaders,
debugMode: _this.debugMode
debugMode: _this.debugMode,
subPixelRoundingForTransparency: _this.subPixelRoundingForTransparency
});
if (_this.collectionMode) {
@ -1635,9 +1698,17 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
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
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
*/
bindSequenceControls: function(){
@ -1726,7 +1797,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
*/
bindStandardControls: function(){
//////////////////////////////////////////////////////////////////////////
@ -1908,7 +1979,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* Gets the active page of a sequence
* @function
* @return {Number}
* @returns {Number}
*/
currentPage: function() {
return this._sequenceIndex;
@ -1916,7 +1987,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
/**
* @function
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:page
*/
goToPage: function( page ){
@ -1965,7 +2036,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @param {function} [onDraw] - If supplied the callback is called when the overlay
* needs to be drawn. It it the responsibility of the callback to do any drawing/positioning.
* It is passed position, size and element.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:add-overlay
*/
addOverlay: function( element, location, placement, onDraw ) {
@ -2023,7 +2094,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @param {OpenSeadragon.Placement} [placement=OpenSeadragon.Placement.TOP_LEFT] - The position of the
* viewport which the location coordinates will be treated as relative
* to.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:update-overlay
*/
updateOverlay: function( element, location, placement ) {
@ -2064,7 +2135,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @method
* @param {Element|String} element - A reference to the element or an
* element id which represent the ovelay content to be removed.
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:remove-overlay
*/
removeOverlay: function( element ) {
@ -2100,7 +2171,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* Removes all currently configured Overlays from this Viewer and schedules
* an update.
* @method
* @return {OpenSeadragon.Viewer} Chainable.
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:clear-overlay
*/
clearOverlays: function() {
@ -2127,7 +2198,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @method
* @param {Element|String} element - A reference to the element or an
* element id which represents the overlay content.
* @return {OpenSeadragon.Overlay} the matching overlay or null if none found.
* @returns {OpenSeadragon.Overlay} the matching overlay or null if none found.
*/
getOverlayById: function( element ) {
var i;
@ -2208,7 +2279,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* Gets this viewer's gesture settings for the given pointer device type.
* @method
* @param {String} type - The pointer device type to get the gesture settings for ("mouse", "touch", "pen", etc.).
* @return {OpenSeadragon.GestureSettings}
* @returns {OpenSeadragon.GestureSettings}
*/
gestureSettingsByDeviceType: function ( type ) {
switch ( type ) {
@ -2350,6 +2421,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
}
this.goToPage( next );
},
isAnimating: function () {
return THIS[ this.hash ].animating;
},
});
@ -2419,6 +2494,7 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
ajaxWithCredentials: viewer.ajaxWithCredentials,
ajaxHeaders: imgOptions.ajaxHeaders ?
imgOptions.ajaxHeaders : viewer.ajaxHeaders,
splitHashDataForPost: viewer.splitHashDataForPost,
useCanvas: viewer.useCanvas,
success: function( event ) {
successCallback( event.tileSource );
@ -2659,8 +2735,8 @@ function onCanvasKeyDown( event ) {
var canvasKeyDownEventArgs = {
originalEvent: event.originalEvent,
preventDefaultAction: false,
preventVerticalPan: event.preventVerticalPan,
preventHorizontalPan: event.preventHorizontalPan
preventVerticalPan: event.preventVerticalPan || !this.panVertical,
preventHorizontalPan: event.preventHorizontalPan || !this.panHorizontal
};
/**
@ -2730,12 +2806,24 @@ function onCanvasKeyPress( event ) {
var canvasKeyPressEventArgs = {
originalEvent: event.originalEvent,
preventDefaultAction: false,
preventVerticalPan: event.preventVerticalPan,
preventHorizontalPan: event.preventHorizontalPan
preventVerticalPan: event.preventVerticalPan || !this.panVertical,
preventHorizontalPan: event.preventHorizontalPan || !this.panHorizontal
};
// This event is documented in onCanvasKeyDown
this.raiseEvent('canvas-key', canvasKeyPressEventArgs);
/**
* 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 {Boolean} preventDefaultAction - Set to true to prevent default keyboard behaviour. Default: false.
* @property {Boolean} preventVerticalPan - Set to true to prevent keyboard vertical panning. Default: false.
* @property {Boolean} preventHorizontalPan - Set to true to prevent keyboard horizontal panning. Default: false.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent('canvas-key-press', canvasKeyPressEventArgs);
if ( !canvasKeyPressEventArgs.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
switch( event.keyCode ){
@ -2795,18 +2883,18 @@ function onCanvasKeyPress( event ) {
break;
case 114: //r - clockwise rotation
if(this.viewport.flipped){
this.viewport.setRotation($.positiveModulo(this.viewport.degrees - this.rotationIncrement, 360));
this.viewport.setRotation(this.viewport.getRotation() - this.rotationIncrement);
} else{
this.viewport.setRotation($.positiveModulo(this.viewport.degrees + this.rotationIncrement, 360));
this.viewport.setRotation(this.viewport.getRotation() + this.rotationIncrement);
}
this.viewport.applyConstraints();
event.preventDefault = true;
break;
case 82: //R - counterclockwise rotation
if(this.viewport.flipped){
this.viewport.setRotation($.positiveModulo(this.viewport.degrees + this.rotationIncrement, 360));
this.viewport.setRotation(this.viewport.getRotation() + this.rotationIncrement);
} else{
this.viewport.setRotation($.positiveModulo(this.viewport.degrees - this.rotationIncrement, 360));
this.viewport.setRotation(this.viewport.getRotation() - this.rotationIncrement);
}
this.viewport.applyConstraints();
event.preventDefault = true;
@ -2831,6 +2919,8 @@ function onCanvasKeyPress( event ) {
}
}
function onCanvasClick( event ) {
var gestureSettings;
@ -2850,6 +2940,7 @@ function onCanvasClick( event ) {
quick: event.quick,
shift: event.shift,
originalEvent: event.originalEvent,
originalTarget: event.originalTarget,
preventDefaultAction: false
};
@ -2865,20 +2956,35 @@ function onCanvasClick( event ) {
* @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
* @property {Element} originalTarget - The DOM element clicked on.
* @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-click', canvasClickEventArgs);
if ( !canvasClickEventArgs.preventDefaultAction && this.viewport && event.quick ) {
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
if ( gestureSettings.clickToZoom ) {
if (gestureSettings.clickToZoom === true){
this.viewport.zoomBy(
event.shift ? 1.0 / this.zoomPerClick : this.zoomPerClick,
gestureSettings.zoomToRefPoint ? this.viewport.pointFromPixel( event.position, true ) : null
);
this.viewport.applyConstraints();
}
if( gestureSettings.dblClickDragToZoom){
if(THIS[ this.hash ].draggingToZoom === true){
THIS[ this.hash ].lastClickTime = null;
THIS[ this.hash ].draggingToZoom = false;
}
else{
THIS[ this.hash ].lastClickTime = $.now();
}
}
}
}
@ -2958,7 +3064,13 @@ function onCanvasDrag( event ) {
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
if ( gestureSettings.dragToPan && !canvasDragEventArgs.preventDefaultAction && this.viewport ) {
if(!canvasDragEventArgs.preventDefaultAction && this.viewport){
if (gestureSettings.dblClickDragToZoom && THIS[ this.hash ].draggingToZoom){
var factor = Math.pow( this.zoomPerDblClickDrag, event.delta.y / 50);
this.viewport.zoomBy(factor);
}
else if (gestureSettings.dragToPan && !THIS[ this.hash ].draggingToZoom) {
if( !this.panHorizontal ){
event.delta.x = 0;
}
@ -2975,26 +3087,28 @@ function onCanvasDrag( event ) {
this.viewport.centerSpringX.target.value += delta.x;
this.viewport.centerSpringY.target.value += delta.y;
var bounds = this.viewport.getBounds();
var constrainedBounds = this.viewport.getConstrainedBounds();
this.viewport.centerSpringX.target.value -= delta.x;
this.viewport.centerSpringY.target.value -= delta.y;
if (bounds.x !== constrainedBounds.x) {
if (constrainedBounds.xConstrained) {
event.delta.x = 0;
}
if (bounds.y !== constrainedBounds.y) {
if (constrainedBounds.yConstrained) {
event.delta.y = 0;
}
}
this.viewport.panBy( this.viewport.deltaPointsFromPixels( event.delta.negate() ), gestureSettings.flickEnabled && !this.constrainDuringPan);
}
}
}
function onCanvasDragEnd( event ) {
var gestureSettings;
var canvasDragEndEventArgs = {
tracker: event.eventSource,
pointerType: event.pointerType,
@ -3025,9 +3139,11 @@ function onCanvasDragEnd( event ) {
*/
this.raiseEvent('canvas-drag-end', canvasDragEndEventArgs);
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
if (!canvasDragEndEventArgs.preventDefaultAction && this.viewport) {
var gestureSettings = this.gestureSettingsByDeviceType(event.pointerType);
if (gestureSettings.flickEnabled &&
if ( !THIS[ this.hash ].draggingToZoom &&
gestureSettings.flickEnabled &&
event.speed >= gestureSettings.flickMinSpeed) {
var amplitudeX = 0;
if (this.panHorizontal) {
@ -3047,6 +3163,13 @@ function onCanvasDragEnd( event ) {
}
this.viewport.applyConstraints();
}
if( gestureSettings.dblClickDragToZoom && THIS[ this.hash ].draggingToZoom === true ){
THIS[ this.hash ].draggingToZoom = false;
}
}
function onCanvasEnter( event ) {
@ -3110,6 +3233,8 @@ function onCanvasLeave( event ) {
}
function onCanvasPress( event ) {
var gestureSettings;
/**
* Raised when the primary mouse button is pressed or touch starts on the {@link OpenSeadragon.Viewer#canvas} element.
*
@ -3133,6 +3258,24 @@ function onCanvasPress( event ) {
insideElementReleased: event.insideElementReleased,
originalEvent: event.originalEvent
});
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
if ( gestureSettings.dblClickDragToZoom ){
var lastClickTime = THIS[ this.hash ].lastClickTime;
var currClickTime = $.now();
if ( lastClickTime === null) {
return;
}
if ((currClickTime - lastClickTime) < this.dblClickTimeThreshold) {
THIS[ this.hash ].draggingToZoom = true;
}
THIS[ this.hash ].lastClickTime = null;
}
}
function onCanvasRelease( event ) {
@ -3266,9 +3409,6 @@ function onCanvasPinch( event ) {
if ( gestureSettings.pinchToZoom &&
(!canvasPinchEventArgs.preventDefaultPanAction || !canvasPinchEventArgs.preventDefaultZoomAction) ) {
centerPt = this.viewport.pointFromPixel( event.center, true );
if ( !canvasPinchEventArgs.preventDefaultZoomAction ) {
this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true );
}
if ( gestureSettings.zoomToRefPoint && !canvasPinchEventArgs.preventDefaultPanAction ) {
lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true );
panByPt = lastCenterPt.minus( centerPt );
@ -3280,6 +3420,9 @@ function onCanvasPinch( event ) {
}
this.viewport.panBy(panByPt, true);
}
if ( !canvasPinchEventArgs.preventDefaultZoomAction ) {
this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true );
}
this.viewport.applyConstraints();
}
if ( gestureSettings.pinchRotate && !canvasPinchEventArgs.preventDefaultRotateAction ) {
@ -3442,6 +3585,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 ) {
//viewer.profiler.beginUpdate();
@ -3449,29 +3613,22 @@ function updateOnce( viewer ) {
if (viewer._opening || !THIS[viewer.hash]) {
return;
}
if (viewer.autoResize) {
var containerSize = _getSafeElemSize(viewer.container);
if (viewer.autoResize || THIS[viewer.hash].forceResize){
var containerSize;
if(viewer._autoResizePolling){
containerSize = _getSafeElemSize(viewer.container);
var prevContainerSize = THIS[viewer.hash].prevContainerSize;
if (!containerSize.equals(prevContainerSize)) {
var viewport = viewer.viewport;
if (viewer.preserveImageSizeOnResize) {
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;
THIS[viewer.hash].needsResize = true;
}
}
if(THIS[viewer.hash].needsResize){
doViewerResize(viewer, containerSize || _getSafeElemSize(viewer.container));
}
}
var viewportChange = viewer.viewport.update();
var animated = viewer.world.update() || viewportChange;
@ -3494,7 +3651,9 @@ function updateOnce( viewer ) {
animated = viewer.referenceStrip.update( viewer.viewport ) || animated;
}
if ( !THIS[ viewer.hash ].animating && animated ) {
var currentAnimating = THIS[ viewer.hash ].animating;
if ( !currentAnimating && animated ) {
/**
* Raised when any spring animation starts (zoom, pan, etc.).
*
@ -3508,7 +3667,13 @@ function updateOnce( viewer ) {
abortControlsAutoHide( viewer );
}
if ( animated || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) {
var isAnimationFinished = currentAnimating && !animated;
if ( isAnimationFinished ) {
THIS[ viewer.hash ].animating = false;
}
if ( animated || isAnimationFinished || THIS[ viewer.hash ].forceRedraw || viewer.world.needsDraw() ) {
drawWorld( viewer );
viewer._drawOverlays();
if( viewer.navigator ){
@ -3532,7 +3697,7 @@ function updateOnce( viewer ) {
}
}
if ( THIS[ viewer.hash ].animating && !animated ) {
if ( isAnimationFinished ) {
/**
* Raised when any spring animation ends (zoom, pan, etc.).
*
@ -3683,9 +3848,9 @@ function onRotateLeft() {
var currRotation = this.viewport.getRotation();
if ( this.viewport.flipped ){
currRotation = $.positiveModulo(currRotation + this.rotationIncrement, 360);
currRotation += this.rotationIncrement;
} else {
currRotation = $.positiveModulo(currRotation - this.rotationIncrement, 360);
currRotation -= this.rotationIncrement;
}
this.viewport.setRotation(currRotation);
}
@ -3696,9 +3861,9 @@ function onRotateRight() {
var currRotation = this.viewport.getRotation();
if ( this.viewport.flipped ){
currRotation = $.positiveModulo(currRotation - this.rotationIncrement, 360);
currRotation -= this.rotationIncrement;
} else {
currRotation = $.positiveModulo(currRotation + this.rotationIncrement, 360);
currRotation += this.rotationIncrement;
}
this.viewport.setRotation(currRotation);
}

View File

@ -2,7 +2,7 @@
* OpenSeadragon - Viewport
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
* Copyright (C) 2010-2022 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -54,6 +54,7 @@
* @param {Number} [options.maxZoomLevel] - See maxZoomLevel in {@link OpenSeadragon.Options}.
* @param {Number} [options.degrees] - See degrees in {@link OpenSeadragon.Options}.
* @param {Boolean} [options.homeFillsViewer] - See homeFillsViewer in {@link OpenSeadragon.Options}.
* @param {Boolean} [options.silenceMultiImageWarnings] - See silenceMultiImageWarnings in {@link OpenSeadragon.Options}.
*/
$.Viewport = function( options ) {
@ -85,6 +86,9 @@ $.Viewport = function( options ) {
delete options.margins;
options.initialDegrees = options.degrees;
delete options.degrees;
$.extend( true, this, {
//required settings
@ -93,6 +97,7 @@ $.Viewport = function( options ) {
//internal state properties
zoomPoint: null,
rotationPivot: null,
viewer: null,
//configurable options
@ -106,9 +111,10 @@ $.Viewport = function( options ) {
defaultZoomLevel: $.DEFAULT_SETTINGS.defaultZoomLevel,
minZoomLevel: $.DEFAULT_SETTINGS.minZoomLevel,
maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel,
degrees: $.DEFAULT_SETTINGS.degrees,
initialDegrees: $.DEFAULT_SETTINGS.degrees,
flipped: $.DEFAULT_SETTINGS.flipped,
homeFillsViewer: $.DEFAULT_SETTINGS.homeFillsViewer
homeFillsViewer: $.DEFAULT_SETTINGS.homeFillsViewer,
silenceMultiImageWarnings: $.DEFAULT_SETTINGS.silenceMultiImageWarnings
}, options );
@ -131,9 +137,16 @@ $.Viewport = function( options ) {
animationTime: this.animationTime
});
this.degreesSpring = new $.Spring({
initial: options.initialDegrees,
springStiffness: this.springStiffness,
animationTime: this.animationTime
});
this._oldCenterX = this.centerSpringX.current.value;
this._oldCenterY = this.centerSpringY.current.value;
this._oldZoom = this.zoomSpring.current.value;
this._oldDegrees = this.degreesSpring.current.value;
this._setContentBounds(new $.Rect(0, 0, 1, 1), 1);
@ -143,11 +156,24 @@ $.Viewport = function( options ) {
/** @lends OpenSeadragon.Viewport.prototype */
$.Viewport.prototype = {
// deprecated
get degrees () {
$.console.warn('Accessing [Viewport.degrees] is deprecated. Use viewport.getRotation instead.');
return this.getRotation();
},
// deprecated
set degrees (degrees) {
$.console.warn('Setting [Viewport.degrees] is deprecated. Use viewport.rotateTo, viewport.rotateBy, or viewport.setRotation instead.');
this.rotateTo(degrees);
},
/**
* Updates the viewport's home bounds and constraints for the given content size.
* @function
* @param {OpenSeadragon.Point} contentSize - size of the content in content units
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:reset-size
*/
resetContentSize: function(contentSize) {
@ -182,7 +208,7 @@ $.Viewport.prototype = {
this._contentSizeNoRotate = this._contentBoundsNoRotate.getSize().times(
contentFactor);
this._contentBounds = bounds.rotate(this.degrees).getBoundingBox();
this._contentBounds = bounds.rotate(this.getRotation()).getBoundingBox();
this._contentSize = this._contentBounds.getSize().times(contentFactor);
this._contentAspectRatio = this._contentSize.x / this._contentSize.y;
@ -365,7 +391,7 @@ $.Viewport.prototype = {
* @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.
*/
getBounds: function(current) {
return this.getBoundsNoRotate(current).rotate(-this.getRotation());
return this.getBoundsNoRotate(current).rotate(-this.getRotation(current));
},
/**
@ -397,7 +423,7 @@ $.Viewport.prototype = {
*/
getBoundsWithMargins: function(current) {
return this.getBoundsNoRotateWithMargins(current).rotate(
-this.getRotation(), this.getCenter(current));
-this.getRotation(current), this.getCenter(current));
},
/**
@ -457,7 +483,7 @@ $.Viewport.prototype = {
);
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 );
return centerTarget.plus( deltaZoomPoints );
@ -486,52 +512,78 @@ $.Viewport.prototype = {
* @function
* @private
* @param {OpenSeadragon.Rect} bounds
* @return {OpenSeadragon.Rect} constrained bounds.
* @returns {OpenSeadragon.Rect} constrained bounds.
*/
_applyBoundaryConstraints: function(bounds) {
var newBounds = new $.Rect(
bounds.x,
bounds.y,
bounds.width,
bounds.height);
var newBounds = this.viewportToViewerElementRectangle(bounds).getBoundingBox();
var cb = this.viewportToViewerElementRectangle(this._contentBoundsNoRotate).getBoundingBox();
var xConstrained = false;
var yConstrained = false;
if (this.wrapHorizontal) {
//do nothing
} else {
var horizontalThreshold = this.visibilityRatio * newBounds.width;
var boundsRight = newBounds.x + newBounds.width;
var contentRight = this._contentBoundsNoRotate.x + this._contentBoundsNoRotate.width;
var leftDx = this._contentBoundsNoRotate.x - boundsRight + horizontalThreshold;
var rightDx = contentRight - newBounds.x - horizontalThreshold;
var contentRight = cb.x + cb.width;
if (horizontalThreshold > this._contentBoundsNoRotate.width) {
var horizontalThreshold, leftDx, rightDx;
if (newBounds.width > cb.width) {
horizontalThreshold = this.visibilityRatio * cb.width;
} else {
horizontalThreshold = this.visibilityRatio * newBounds.width;
}
leftDx = cb.x - boundsRight + horizontalThreshold;
rightDx = contentRight - newBounds.x - horizontalThreshold;
if (horizontalThreshold > cb.width) {
newBounds.x += (leftDx + rightDx) / 2;
xConstrained = true;
} else if (rightDx < 0) {
newBounds.x += rightDx;
xConstrained = true;
} else if (leftDx > 0) {
newBounds.x += leftDx;
xConstrained = true;
}
}
if (this.wrapVertical) {
//do nothing
} else {
var verticalThreshold = this.visibilityRatio * newBounds.height;
var boundsBottom = newBounds.y + newBounds.height;
var contentBottom = this._contentBoundsNoRotate.y + this._contentBoundsNoRotate.height;
var topDy = this._contentBoundsNoRotate.y - boundsBottom + verticalThreshold;
var bottomDy = contentBottom - newBounds.y - verticalThreshold;
var contentBottom = cb.y + cb.height;
if (verticalThreshold > this._contentBoundsNoRotate.height) {
var verticalThreshold, topDy, bottomDy;
if (newBounds.height > cb.height) {
verticalThreshold = this.visibilityRatio * cb.height;
} else{
verticalThreshold = this.visibilityRatio * newBounds.height;
}
topDy = cb.y - boundsBottom + verticalThreshold;
bottomDy = contentBottom - newBounds.y - verticalThreshold;
if (verticalThreshold > cb.height) {
newBounds.y += (topDy + bottomDy) / 2;
yConstrained = true;
} else if (bottomDy < 0) {
newBounds.y += bottomDy;
yConstrained = true;
} else if (topDy > 0) {
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;
},
/**
@ -564,8 +616,8 @@ $.Viewport.prototype = {
* zooming and panning to the closest acceptable zoom and location.
* @function
* @param {Boolean} [immediately=false]
* @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:constrain
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:constrain if constraints were applied
*/
applyConstraints: function(immediately) {
var actualZoom = this.getZoom();
@ -575,17 +627,13 @@ $.Viewport.prototype = {
this.zoomTo(constrainedZoom, this.zoomPoint, immediately);
}
var bounds = this.getBoundsNoRotate();
var constrainedBounds = this._applyBoundaryConstraints(bounds);
this._raiseConstraintsEvent(immediately);
var constrainedBounds = this.getConstrainedBounds(false);
if (bounds.x !== constrainedBounds.x ||
bounds.y !== constrainedBounds.y ||
immediately) {
this.fitBounds(
constrainedBounds.rotate(-this.getRotation()),
immediately);
if(constrainedBounds.constraintApplied){
this.fitBounds(constrainedBounds, immediately);
this._raiseConstraintsEvent(immediately);
}
return this;
},
@ -593,7 +641,7 @@ $.Viewport.prototype = {
* Equivalent to {@link OpenSeadragon.Viewport#applyConstraints}
* @function
* @param {Boolean} [immediately=false]
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:constrain
*/
ensureVisible: function(immediately) {
@ -605,7 +653,7 @@ $.Viewport.prototype = {
* @private
* @param {OpenSeadragon.Rect} bounds
* @param {Object} options (immediately=false, constraints=false)
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
_fitBounds: function(bounds, options) {
options = options || {};
@ -635,45 +683,51 @@ $.Viewport.prototype = {
newBounds.y = center.y - newBounds.height / 2;
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) {
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);
this.zoomTo(this.getZoom(true), null, true);
var currentCenter = this.getCenter(true);
var currentZoom = this.getZoom(true);
this.panTo(currentCenter, true);
this.zoomTo(currentZoom, null, true);
var oldBounds = this.getBounds();
var oldZoom = this.getZoom();
if (oldZoom === 0 || Math.abs(newZoom / oldZoom - 1) < 0.00000001) {
this.zoomTo(newZoom, true);
return this.panTo(center, immediately);
this.zoomTo(newZoom, null, true);
this.panTo(center, immediately);
if(constraints){
this.applyConstraints(false);
}
return this;
}
newBounds = newBounds.rotate(-this.getRotation());
var referencePoint = newBounds.getTopLeft().times(newZoom)
if(constraints){
this.panTo(center, false);
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);
return this.zoomTo(newZoom, referencePoint, immediately);
this.zoomTo(newZoom, referencePoint, immediately);
}
return this;
},
/**
@ -686,7 +740,7 @@ $.Viewport.prototype = {
* @function
* @param {OpenSeadragon.Rect} bounds
* @param {Boolean} [immediately=false]
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
fitBounds: function(bounds, immediately) {
return this._fitBounds(bounds, {
@ -705,7 +759,7 @@ $.Viewport.prototype = {
* @function
* @param {OpenSeadragon.Rect} bounds
* @param {Boolean} [immediately=false]
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
fitBoundsWithConstraints: function(bounds, immediately) {
return this._fitBounds(bounds, {
@ -717,7 +771,7 @@ $.Viewport.prototype = {
/**
* Zooms so the image just fills the viewer vertically.
* @param {Boolean} immediately
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
fitVertically: function(immediately) {
var box = new $.Rect(
@ -731,7 +785,7 @@ $.Viewport.prototype = {
/**
* Zooms so the image just fills the viewer horizontally.
* @param {Boolean} immediately
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
fitHorizontally: function(immediately) {
var box = new $.Rect(
@ -747,7 +801,10 @@ $.Viewport.prototype = {
* Returns bounds taking constraints into account
* Added to improve constrained panning
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
* @return {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) {
var bounds,
@ -764,7 +821,7 @@ $.Viewport.prototype = {
* @function
* @param {OpenSeadragon.Point} delta
* @param {Boolean} immediately
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:pan
*/
panBy: function( delta, immediately ) {
@ -779,7 +836,7 @@ $.Viewport.prototype = {
* @function
* @param {OpenSeadragon.Point} center
* @param {Boolean} immediately
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:pan
*/
panTo: function( center, immediately ) {
@ -814,7 +871,7 @@ $.Viewport.prototype = {
/**
* @function
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:zoom
*/
zoomBy: function(factor, refPoint, immediately) {
@ -829,7 +886,7 @@ $.Viewport.prototype = {
* @param {OpenSeadragon.Point} [refPoint] The point which will stay at
* the same screen location. Defaults to the viewport center.
* @param {Boolean} [immediately=false]
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:zoom
*/
zoomTo: function(zoom, refPoint, immediately) {
@ -876,13 +933,90 @@ $.Viewport.prototype = {
* Rotates this viewport to the angle specified.
* @function
* @param {Number} degrees The degrees to set the rotation to.
* @return {OpenSeadragon.Viewport} Chainable.
* @param {Boolean} [immediately=false] Whether to animate to the new angle
* or rotate immediately.
* * @returns {OpenSeadragon.Viewport} Chainable.
*/
setRotation: function(degrees) {
setRotation: function(degrees, immediately) {
return this.rotateTo(degrees, null, immediately);
},
/**
* Gets the current rotation in degrees.
* @function
* @param {Boolean} [current=false] True for current rotation, false for target.
* @returns {Number} The current rotation in degrees.
*/
getRotation: function(current) {
return current ?
this.degreesSpring.current.value :
this.degreesSpring.target.value;
},
/**
* Rotates this viewport to the angle specified around a pivot point. Alias for rotateTo.
* @function
* @param {Number} degrees The degrees to set the rotation to.
* @param {OpenSeadragon.Point} [pivot] (Optional) point in viewport coordinates
* around which the rotation should be performed. Defaults to the center of the viewport.
* @param {Boolean} [immediately=false] Whether to animate to the new angle
* or rotate immediately.
* * @returns {OpenSeadragon.Viewport} Chainable.
*/
setRotationWithPivot: function(degrees, pivot, immediately) {
return this.rotateTo(degrees, pivot, immediately);
},
/**
* Rotates this viewport to the angle specified.
* @function
* @param {Number} degrees The degrees to set the rotation to.
* @param {OpenSeadragon.Point} [pivot] (Optional) point in viewport coordinates
* around which the rotation should be performed. Defaults to the center of the viewport.
* @param {Boolean} [immediately=false] Whether to animate to the new angle
* or rotate immediately.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
rotateTo: function(degrees, pivot, immediately){
if (!this.viewer || !this.viewer.drawer.canRotate()) {
return this;
}
this.degrees = $.positiveModulo(degrees, 360);
if (this.degreesSpring.target.value === degrees &&
this.degreesSpring.isAtTargetValue()) {
return this;
}
this.rotationPivot = pivot instanceof $.Point &&
!isNaN(pivot.x) &&
!isNaN(pivot.y) ?
pivot :
null;
if (immediately) {
if(this.rotationPivot){
var changeInDegrees = degrees - this._oldDegrees;
if(!changeInDegrees){
this.rotationPivot = null;
return this;
}
this._rotateAboutPivot(degrees);
} else{
this.degreesSpring.resetTo(degrees);
}
} else {
var normalizedFrom = $.positiveModulo(this.degreesSpring.current.value, 360);
var normalizedTo = $.positiveModulo(degrees, 360);
var diff = normalizedTo - normalizedFrom;
if (diff > 180) {
normalizedTo -= 360;
} else if (diff < -180) {
normalizedTo += 360;
}
var reverseDiff = normalizedFrom - normalizedTo;
this.degreesSpring.resetTo(degrees + reverseDiff);
this.degreesSpring.springTo(degrees);
}
this._setContentBounds(
this.viewer.world.getHomeBounds(),
this.viewer.world.getContentFactor());
@ -896,24 +1030,31 @@ $.Viewport.prototype = {
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
* @property {Number} degrees - The number of degrees the rotation was set to.
* @property {Boolean} immediately - Whether the rotation happened immediately or was animated
* @property {OpenSeadragon.Point} pivot - The point in viewport coordinates around which the rotation (if any) happened
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent('rotate', {degrees: degrees});
this.viewer.raiseEvent('rotate', {degrees: degrees, immediately: !!immediately, pivot: this.rotationPivot || this.getCenter()});
return this;
},
/**
* Gets the current rotation in degrees.
* Rotates this viewport by the angle specified.
* @function
* @return {Number} The current rotation in degrees.
* @param {Number} degrees The degrees by which to rotate the viewport.
* @param {OpenSeadragon.Point} [pivot] (Optional) point in viewport coordinates
* around which the rotation should be performed. Defaults to the center of the viewport.
* * @param {Boolean} [immediately=false] Whether to animate to the new angle
* or rotate immediately.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
getRotation: function() {
return this.degrees;
rotateBy: function(degrees, pivot, immediately){
return this.rotateTo(this.degreesSpring.target.value + degrees, pivot, immediately);
},
/**
* @function
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:resize
*/
resize: function( newContainerSize, maintain ) {
@ -963,7 +1104,7 @@ $.Viewport.prototype = {
},
/**
* Update the zoom and center (X and Y) springs.
* Update the zoom, degrees, and center (X and Y) springs.
* @function
* @returns {Boolean} True if any change has been made, false otherwise.
*/
@ -972,21 +1113,55 @@ $.Viewport.prototype = {
this._adjustCenterSpringsForZoomPoint(function() {
_this.zoomSpring.update();
});
if(this.degreesSpring.isAtTargetValue()){
this.rotationPivot = null;
}
this.centerSpringX.update();
this.centerSpringY.update();
if(this.rotationPivot){
this._rotateAboutPivot(true);
}
else{
this.degreesSpring.update();
}
var changed = this.centerSpringX.current.value !== this._oldCenterX ||
this.centerSpringY.current.value !== this._oldCenterY ||
this.zoomSpring.current.value !== this._oldZoom;
this.zoomSpring.current.value !== this._oldZoom ||
this.degreesSpring.current.value !== this._oldDegrees;
this._oldCenterX = this.centerSpringX.current.value;
this._oldCenterY = this.centerSpringY.current.value;
this._oldZoom = this.zoomSpring.current.value;
this._oldDegrees = this.degreesSpring.current.value;
return changed;
},
// private - pass true to use spring, or a number for degrees for immediate rotation
_rotateAboutPivot: function(degreesOrUseSpring){
var useSpring = degreesOrUseSpring === true;
var delta = this.rotationPivot.minus(this.getCenter());
this.centerSpringX.shiftBy(delta.x);
this.centerSpringY.shiftBy(delta.y);
if(useSpring){
this.degreesSpring.update();
} else {
this.degreesSpring.resetTo(degreesOrUseSpring);
}
var changeInDegrees = this.degreesSpring.current.value - this._oldDegrees;
var rdelta = delta.rotate(changeInDegrees * -1).times(-1);
this.centerSpringX.shiftBy(rdelta.x);
this.centerSpringY.shiftBy(rdelta.y);
},
// private
_adjustCenterSpringsForZoomPoint: function(zoomSpringHandler) {
if (this.zoomPoint) {
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
@ -1033,7 +1208,7 @@ $.Viewport.prototype = {
*/
deltaPixelsFromPoints: function(deltaPoints, current) {
return this.deltaPixelsFromPointsNoRotate(
deltaPoints.rotate(this.getRotation()),
deltaPoints.rotate(this.getRotation(current)),
current);
},
@ -1062,7 +1237,7 @@ $.Viewport.prototype = {
*/
deltaPointsFromPixels: function(deltaPixels, current) {
return this.deltaPointsFromPixelsNoRotate(deltaPixels, current)
.rotate(-this.getRotation());
.rotate(-this.getRotation(current));
},
/**
@ -1104,7 +1279,7 @@ $.Viewport.prototype = {
// private
_pixelFromPoint: function(point, bounds) {
return this._pixelFromPointNoRotate(
point.rotate(this.getRotation(), this.getCenter(true)),
point.rotate(this.getRotation(true), this.getCenter(true)),
bounds);
},
@ -1137,8 +1312,8 @@ $.Viewport.prototype = {
*/
pointFromPixel: function(pixel, current) {
return this.pointFromPixelNoRotate(pixel, current).rotate(
-this.getRotation(),
this.getCenter(true)
-this.getRotation(current),
this.getCenter(current)
);
},
@ -1159,7 +1334,7 @@ $.Viewport.prototype = {
* @param {(OpenSeadragon.Point|Number)} viewerX either a point or the X
* coordinate in viewport coordinate system.
* @param {Number} [viewerY] Y coordinate in viewport coordinate system.
* @return {OpenSeadragon.Point} a point representing the coordinates in the image.
* @returns {OpenSeadragon.Point} a point representing the coordinates in the image.
*/
viewportToImageCoordinates: function(viewerX, viewerY) {
if (viewerX instanceof $.Point) {
@ -1170,8 +1345,10 @@ $.Viewport.prototype = {
if (this.viewer) {
var count = this.viewer.world.getItemCount();
if (count > 1) {
if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.viewportToImageCoordinates] is not accurate ' +
'with multi-image; use TiledImage.viewportToImageCoordinates instead.');
}
} else if (count === 1) {
// It is better to use TiledImage.viewportToImageCoordinates
// because this._contentBoundsNoRotate can not be relied on
@ -1203,7 +1380,7 @@ $.Viewport.prototype = {
* @param {(OpenSeadragon.Point | Number)} imageX the point or the
* X coordinate in image coordinate system.
* @param {Number} [imageY] Y coordinate in image coordinate system.
* @return {OpenSeadragon.Point} a point representing the coordinates in the viewport.
* @returns {OpenSeadragon.Point} a point representing the coordinates in the viewport.
*/
imageToViewportCoordinates: function(imageX, imageY) {
if (imageX instanceof $.Point) {
@ -1214,8 +1391,10 @@ $.Viewport.prototype = {
if (this.viewer) {
var count = this.viewer.world.getItemCount();
if (count > 1) {
if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.imageToViewportCoordinates] is not accurate ' +
'with multi-image; use TiledImage.imageToViewportCoordinates instead.');
}
} else if (count === 1) {
// It is better to use TiledImage.viewportToImageCoordinates
// because this._contentBoundsNoRotate can not be relied on
@ -1256,8 +1435,10 @@ $.Viewport.prototype = {
if (this.viewer) {
var count = this.viewer.world.getItemCount();
if (count > 1) {
if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.imageToViewportRectangle] is not accurate ' +
'with multi-image; use TiledImage.imageToViewportRectangle instead.');
}
} else if (count === 1) {
// It is better to use TiledImage.imageToViewportRectangle
// because this._contentBoundsNoRotate can not be relied on
@ -1304,8 +1485,10 @@ $.Viewport.prototype = {
if (this.viewer) {
var count = this.viewer.world.getItemCount();
if (count > 1) {
if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.viewportToImageRectangle] is not accurate ' +
'with multi-image; use TiledImage.viewportToImageRectangle instead.');
}
} else if (count === 1) {
// It is better to use TiledImage.viewportToImageCoordinates
// because this._contentBoundsNoRotate can not be relied on
@ -1469,8 +1652,10 @@ $.Viewport.prototype = {
if (this.viewer) {
var count = this.viewer.world.getItemCount();
if (count > 1) {
if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.viewportToImageZoom] is not ' +
'accurate with multi-image.');
}
} else if (count === 1) {
// It is better to use TiledImage.viewportToImageZoom
// because this._contentBoundsNoRotate can not be relied on
@ -1503,8 +1688,10 @@ $.Viewport.prototype = {
if (this.viewer) {
var count = this.viewer.world.getItemCount();
if (count > 1) {
if (!this.silenceMultiImageWarnings) {
$.console.error('[Viewport.imageToViewportZoom] is not accurate ' +
'with multi-image.');
}
} else if (count === 1) {
// It is better to use TiledImage.imageToViewportZoom
// because this._contentBoundsNoRotate can not be relied on
@ -1524,7 +1711,7 @@ $.Viewport.prototype = {
/**
* Toggles flip state and demands a new drawing on navigator and viewer objects.
* @function
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
toggleFlip: function() {
this.setFlip(!this.getFlip());
@ -1534,7 +1721,7 @@ $.Viewport.prototype = {
/**
* Get flip state stored on viewport.
* @function
* @return {Boolean} Flip state.
* @returns {Boolean} Flip state.
*/
getFlip: function() {
return this.flipped;
@ -1544,7 +1731,7 @@ $.Viewport.prototype = {
* Sets flip state according to the state input argument.
* @function
* @param {Boolean} state - Flip state to set.
* @return {OpenSeadragon.Viewport} Chainable.
* @returns {OpenSeadragon.Viewport} Chainable.
*/
setFlip: function( state ) {
if ( this.flipped === state ) {

View File

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

View File

@ -122,10 +122,11 @@
* @function
* @param {Object} data - the raw configuration
* @param {String} url - the url the data was retrieved from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient
* @param {String} postData - HTTP POST data in k=v&k2=v2... form or null
* @returns {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor.
*/
configure: function(data, url) {
configure: function(data, url, postData) {
return data;
},

View File

@ -65,6 +65,7 @@
<!-- Polyfill must be inserted first because it is testing functions
reassignments which could be done by other test. -->
<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/strings.js"></script>
<script src="/test/modules/formats.js"></script>
@ -86,6 +87,7 @@
<script src="/test/modules/spring.js"></script>
<script src="/test/modules/rectangle.js"></script>
<script src="/test/modules/ajax-tiles.js"></script>
<script src="/test/modules/ajax-post-data.js"></script>
<script src="/test/modules/imageloader.js"></script>
<script src="/test/modules/iiif.js"></script>
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -0,0 +1,15 @@
{
"@context": "http://iiif.io/api/image/3/context.json",
"id": "http://localhost:8000/test/data/iiif_3_0_sizes",
"protocol": "http://iiif.io/api/image",
"width": 6976,
"height": 5074,
"profile": "level0",
"sizes" : [
{"width" : 400, "height" : 291},
{"width" : 800, "height" : 582},
{"width" : 1600, "height" : 1164},
{"width" : 3200, "height": 2328},
{"width" : 6976, "height": 5074}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,11 @@
{
"@context": "http://iiif.io/api/image/3/context.json",
"id": "http://localhost:8000/test/data/iiif_3_0_tiled",
"protocol": "http://iiif.io/api/image",
"height": 1024,
"width": 775,
"tiles" : [{"width":256, "scaleFactors":[1,2,4,8]}],
"profile": "level2",
"extraQualities": ["bitonal", "grey", "color"],
"extraFormats": ["jpg", "png", "gif" ]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

View File

@ -0,0 +1,11 @@
{
"@context": "http://iiif.io/api/image/3/context.json",
"id": "http://localhost:8000/test/data/iiif_3_0_tiled_sf1",
"protocol": "http://iiif.io/api/image",
"height": 1024,
"width": 775,
"tiles" : [{"width": 256, "scaleFactors": [1]}],
"profile": "level2",
"extraQualities": ["bitonal", "grey", "color"],
"extraFormats": ["jpg", "png", "gif" ]
}

View File

@ -27,7 +27,6 @@
tileSources: "../data/testpattern.dzi",
showNavigator:true
});
</script>
</body>
</html>

View File

@ -3,110 +3,162 @@
<head>
<title>OpenSeadragon fitBoundsWithConstraints() 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;
height: 500px;
border:thin black solid;
margin-right:20px;
}
#highlights li {
cursor: pointer;
#buttons button{
width:10em;
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>
</head>
<body>
<div class="layout">
<div id="contentDiv" class="openseadragon1"></div>
<div id="controls">
<div>
Simple demo page to show 'viewport.fitBounds().applyConstraints()' issue.
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>
<div id="contentDiv" class="openseadragon1"></div>
<div id="highlights"></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>
<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">
var _viewer;
var viewer;
var _fittingMethod = 0;
var _highlights = [
{"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,
viewer = window.viewer = OpenSeadragon({
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
hash: generateUniqueHash(), //this is only needed if you want to instantiate more than one viewer at a time.
tileSources: {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: 'http://cdn.photosynth.net/ps2/19d5cf2b-77ed-439f-ac21-d3046320384c/packet/undistorted/img0043/',
Format: "jpg",
Overlap: 1,
TileSize: 510,
Size: {
Width: 4592,
Height: 3448
}
}
}
});
_viewer.addHandler("open", function() {
var str = "<ul>";
for (var i=0; i<_highlights.length; ++i) {
var highlight = _highlights[i];
str += "<li onclick='gotoHighlight("+i+")'>"+highlight.text+"</li>";
}
str += "</ul>";
document.getElementById("highlights").innerHTML = str;
tileSources: "../data/testpattern.dzi",
minZoomImageRatio: 0,
maxZoomPixelRatio: 10,
visibilityRatio:1,
constrainDuringPan:true,
});
function gotoHighlight(index) {
var highlight = _highlights[index];
viewer.addHandler("open", function(event) {
var elt = document.createElement("div");
elt.className = "runtime-overlay";
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
});
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));
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) {
viewport.fitBoundsWithConstraints(bounds, false);
viewer.viewport.fitBounds(bounds, false);
}
else if (_fittingMethod === 1) {
viewport.fitBounds(bounds, false);
viewer.viewport.fitBoundsWithConstraints(bounds, false);
}
else if (_fittingMethod === 4) {
viewer.viewport.fitBoundsWithConstraints(bounds, true);
}
else if (_fittingMethod === 2) {
viewport.fitBounds(bounds, false).applyConstraints();
}
viewer.viewport.fitBounds(bounds, false);
setTimeout(()=>viewer.viewport.applyConstraints(), 1000);
}
});
})
function changeMethod(value) {
_fittingMethod = parseInt(value, 10);
}
viewer.viewport.zoomTo(0.5, null, true);
function goHome() {
_viewer.viewport.goHome();
}
});
$('.method').on('click',function(){
$('.method').removeClass('selected');
$(this).addClass('selected');
})
$("#rotate").click(function() {
viewer.viewport.setRotation(viewer.viewport.getRotation() - 22.5);
$("#degrees").text(viewer.viewport.getRotation() + "deg");
});
</script>
</body>

View File

@ -25,7 +25,7 @@
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "http://wellcomelibrary.org/iiif-img/b11768265-0/a6801943-b8b4-4674-908c-7d5b27e70569/info.json",
tileSources: "../data/iiif_2_0_tiled/info.json",
showNavigator:true
});

Some files were not shown because too many files have changed in this diff Show More