mirror of
https://github.com/openseadragon/openseadragon.git
synced 2025-01-19 17:21:50 +03:00
commit
3842779746
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
node_modules
|
||||
build/
|
||||
sftp-config.json
|
||||
coverage/
|
||||
temp/
|
||||
|
62
Gruntfile.js
62
Gruntfile.js
@ -1,3 +1,5 @@
|
||||
/* global module */
|
||||
|
||||
module.exports = function(grunt) {
|
||||
|
||||
// ----------
|
||||
@ -5,7 +7,7 @@ module.exports = function(grunt) {
|
||||
grunt.loadNpmTasks("grunt-contrib-concat");
|
||||
grunt.loadNpmTasks("grunt-contrib-jshint");
|
||||
grunt.loadNpmTasks("grunt-contrib-uglify");
|
||||
grunt.loadNpmTasks("grunt-contrib-qunit");
|
||||
grunt.loadNpmTasks("grunt-qunit-istanbul");
|
||||
grunt.loadNpmTasks("grunt-contrib-connect");
|
||||
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||
grunt.loadNpmTasks("grunt-contrib-clean");
|
||||
@ -44,11 +46,14 @@ module.exports = function(grunt) {
|
||||
"src/referencestrip.js",
|
||||
"src/displayrectangle.js",
|
||||
"src/spring.js",
|
||||
"src/imageLoader.js",
|
||||
"src/imageloader.js",
|
||||
"src/tile.js",
|
||||
"src/overlay.js",
|
||||
"src/drawer.js",
|
||||
"src/viewport.js"
|
||||
"src/viewport.js",
|
||||
"src/tiledimage.js",
|
||||
"src/tilecache.js",
|
||||
"src/world.js"
|
||||
];
|
||||
|
||||
// ----------
|
||||
@ -69,6 +74,7 @@ module.exports = function(grunt) {
|
||||
clean: {
|
||||
build: ["build"],
|
||||
package: [packageDir],
|
||||
coverage: ["coverage"],
|
||||
release: {
|
||||
src: [releaseRoot],
|
||||
options: {
|
||||
@ -134,10 +140,26 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
qunit: {
|
||||
normal: {
|
||||
options: {
|
||||
urls: [ "http://localhost:8000/test/test.html" ]
|
||||
}
|
||||
},
|
||||
coverage: {
|
||||
options: {
|
||||
urls: [ "http://localhost:8000/test/coverage.html" ],
|
||||
coverage: {
|
||||
src: ['src/*.js'],
|
||||
htmlReport: 'coverage/html/',
|
||||
instrumentedFiles: 'temp/',
|
||||
baseUrl: '.',
|
||||
disposeCollector: true
|
||||
}
|
||||
}
|
||||
},
|
||||
all: {
|
||||
options: {
|
||||
timeout: 10000,
|
||||
urls: [ "http://localhost:8000/test/test.html" ]
|
||||
timeout: 10000
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -151,7 +173,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
watch: {
|
||||
files: [ "Gruntfile.js", "src/*.js", "images/*" ],
|
||||
tasks: "build"
|
||||
tasks: "watchTask"
|
||||
},
|
||||
jshint: {
|
||||
options: {
|
||||
@ -206,6 +228,8 @@ module.exports = function(grunt) {
|
||||
});
|
||||
|
||||
// ----------
|
||||
// Bower task.
|
||||
// Generates the Bower file for site-build.
|
||||
grunt.registerTask("bower", function() {
|
||||
var path = "../site-build/bower.json";
|
||||
var data = grunt.file.readJSON(path);
|
||||
@ -213,6 +237,18 @@ module.exports = function(grunt) {
|
||||
grunt.file.write(path, JSON.stringify(data, null, 2) + "\n");
|
||||
});
|
||||
|
||||
// ----------
|
||||
// Watch task.
|
||||
// Called from the watch feature; does a full build or a minbuild, depending on
|
||||
// whether you used --min on the command line.
|
||||
grunt.registerTask("watchTask", function() {
|
||||
if (grunt.option('min')) {
|
||||
grunt.task.run("minbuild");
|
||||
} else {
|
||||
grunt.task.run("build");
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
// Build task.
|
||||
// Cleans out the build folder and builds the code and images into it, checking lint.
|
||||
@ -221,10 +257,22 @@ module.exports = function(grunt) {
|
||||
"uglify", "replace:cleanPaths", "copy:build"
|
||||
]);
|
||||
|
||||
// ----------
|
||||
// Minimal build task.
|
||||
// For use during development as desired. Creates only the unminified version.
|
||||
grunt.registerTask("minbuild", [
|
||||
"git-describe", "concat", "copy:build"
|
||||
]);
|
||||
|
||||
// ----------
|
||||
// Test task.
|
||||
// Builds and runs unit tests.
|
||||
grunt.registerTask("test", ["build", "connect", "qunit"]);
|
||||
grunt.registerTask("test", ["build", "connect", "qunit:normal"]);
|
||||
|
||||
// ----------
|
||||
// Coverage task.
|
||||
// Outputs unit test code coverage report.
|
||||
grunt.registerTask("coverage", ["clean:coverage", "connect", "qunit:coverage"]);
|
||||
|
||||
// ----------
|
||||
// Package task.
|
||||
|
@ -63,6 +63,12 @@ and open `http://localhost:8000/test/test.html` in your browser.
|
||||
|
||||
Another good page, if you want to interactively test out your changes, is `http://localhost:8000/test/demo/basic.html`.
|
||||
|
||||
You can also get a report of the tests' code coverage:
|
||||
|
||||
grunt coverage
|
||||
|
||||
The report shows up at `coverage/html/index.html` viewable in a browser.
|
||||
|
||||
### Contributing
|
||||
|
||||
OpenSeadragon is truly a community project; we welcome your involvement!
|
||||
|
@ -1,6 +1,54 @@
|
||||
OPENSEADRAGON CHANGELOG
|
||||
=======================
|
||||
|
||||
1.3.0: (in progress)
|
||||
|
||||
* True multi-image mode (#450)
|
||||
* BREAKING CHANGE: Passing an array for the tileSources option is no longer enough to trigger sequence mode; you have to set the sequenceMode option to true as well
|
||||
* BREAKING CHANGE: Navigator no longer sends an open event when its viewer opens
|
||||
* BREAKING CHANGE: Viewer.drawers and Viewer.drawersContainer no longer exist
|
||||
* BREAKING CHANGE: A Viewer's Drawer and Viewport are now made once per Viewer and reused for every image that Viewer opens (rather than being recreated for every open); this means if you change Viewer options between opens, the behavior is different now.
|
||||
* DEPRECATION: use Viewer.addTiledImage instead of Viewer.addLayer
|
||||
* addTiledImage supports positioning config properties
|
||||
* DEPRECATION: use World.getItemAt instead of Viewer.getLayerAtLevel
|
||||
* DEPRECATION: use World.getIndexOfItem instead of Viewer.getLevelOfLayer
|
||||
* DEPRECATION: use World.getItemCount instead of Viewer.getLayersCount
|
||||
* DEPRECATION: use World.setItemIndex instead of Viewer.setLayerLevel
|
||||
* DEPRECATION: use World.removeItem instead of Viewer.removeLayer
|
||||
* DEPRECATION: use World.needsDraw instead of Drawer.needsUpdate
|
||||
* DEPRECATION: use TileCache.numTilesLoaded instead of Drawer.numTilesLoaded
|
||||
* DEPRECATION: use World.resetItems instead of Drawer.reset
|
||||
* DEPRECATION: use Drawer.clear and World.draw instead of Drawer.update
|
||||
* DEPRECATION: the layersAspectRatioEpsilon option is no longer necessary
|
||||
* DEPRECATION: Viewer's add-layer event is now World's add-item event
|
||||
* DEPRECATION: Viewer's layer-level-changed event is now World's item-index-change event
|
||||
* DEPRECATION: Viewer's remove-layer event is now World's remove-item event
|
||||
* DEPRECATION: Viewer's add-layer-failed event is now add-item-failed
|
||||
* DEPRECATION: TileSourceCollection has been retired in favor of World
|
||||
* DEPRECATION: collectionMode no longer draws outlines or reflections for items
|
||||
* Drawer has been split into three classes:
|
||||
* TiledImage, tile management and positioning for a single tiled image
|
||||
* TileCache, tile caching for all images
|
||||
* Drawer, tile drawing for all images
|
||||
* New class: World, keeps track of multiple images in the scene
|
||||
* Viewer now has world and tileCache properties
|
||||
* Rect and Point now have clone functions
|
||||
* New Viewport method for managing homeBounds as well as constraints: setHomeBounds
|
||||
* Viewport.open supports positioning config properties
|
||||
* Margins option to push the home region in from the edges of the Viewer (#505)
|
||||
* Rect and Point toString() functions are now consistent: rounding values to nearest hundredth
|
||||
* Overlays appear in the DOM immediately on open or addOverlay (#507)
|
||||
* imageLoaderLimit now works (#544)
|
||||
* Turning off scrollToZoom in gestureSettings now allows scroll events to propagate
|
||||
* You can now set a minZoomLevel that's greater than the home zoom level
|
||||
* Added union() to OpenSeadragon.Rect
|
||||
* Fixed an error in fitBounds if the new and old bounds were extremely close in size
|
||||
* Added ajaxWithCredentials option (#543)
|
||||
* Added viewport-change event for after the viewport changes but before it's drawn
|
||||
* A spring's current value is now updated immediately on reset (#524)
|
||||
* Fixed an error in fitBounds that occurred sometimes with immediately = true
|
||||
* Added support for HDPI (retina) displays (#583)
|
||||
|
||||
1.2.2: (in progress)
|
||||
|
||||
* Corrected IIIF tile source to use canonical syntax (#586)
|
||||
|
@ -8,7 +8,8 @@
|
||||
"*.sublime-workspace"
|
||||
],
|
||||
"folder_exclude_patterns": [
|
||||
"node_modules"
|
||||
"node_modules",
|
||||
"coverage"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -10,11 +10,11 @@
|
||||
"grunt-git-describe": "^2.3.2",
|
||||
"grunt-contrib-uglify": "^0.4.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-contrib-qunit": "^0.5.1",
|
||||
"grunt-contrib-jshint": "^0.10.0",
|
||||
"grunt-contrib-compress": "^0.9.1",
|
||||
"grunt-contrib-connect": "^0.7.1",
|
||||
"qunitjs": "^1.14.0"
|
||||
"qunitjs": "^1.14.0",
|
||||
"grunt-qunit-istanbul": "^0.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
|
1207
src/drawer.js
1207
src/drawer.js
File diff suppressed because it is too large
Load Diff
@ -34,16 +34,7 @@
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @class ImageJob
|
||||
* @classdesc Handles loading a single image for use in a single {@link OpenSeadragon.Tile}.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @param {String} source - URL of image to download.
|
||||
* @param {String} crossOriginPolicy - CORS policy to use for downloads
|
||||
* @param {Function} callback - Called once image has finished downloading.
|
||||
*/
|
||||
// private class
|
||||
function ImageJob ( options ) {
|
||||
|
||||
$.extend( true, this, {
|
||||
@ -60,11 +51,6 @@ function ImageJob ( options ) {
|
||||
}
|
||||
|
||||
ImageJob.prototype = {
|
||||
|
||||
/**
|
||||
* Initiates downloading of associated image.
|
||||
* @method
|
||||
*/
|
||||
start: function(){
|
||||
var _this = this;
|
||||
|
||||
@ -104,20 +90,24 @@ ImageJob.prototype = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @class
|
||||
* @class ImageLoader
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Handles downloading of a set of images using asynchronous queue pattern.
|
||||
* You generally won't have to interact with the ImageLoader directly.
|
||||
* @param {Object} options - Options for this ImageLoader.
|
||||
* @param {Number} [options.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.
|
||||
*/
|
||||
$.ImageLoader = function() {
|
||||
$.ImageLoader = function( options ) {
|
||||
|
||||
$.extend( true, this, {
|
||||
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
||||
jobQueue: [],
|
||||
jobsInProgress: 0
|
||||
});
|
||||
}, options );
|
||||
|
||||
};
|
||||
|
||||
$.ImageLoader.prototype = {
|
||||
$.ImageLoader.prototype = /** @lends OpenSeadragon.ImageLoader.prototype */{
|
||||
|
||||
/**
|
||||
* Add an unloaded image to the loader queue.
|
||||
@ -172,10 +162,10 @@ function completeJob( loader, job, callback ) {
|
||||
if ( (!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
|
||||
nextJob = loader.jobQueue.shift();
|
||||
nextJob.start();
|
||||
loader.jobsInProgress++;
|
||||
}
|
||||
|
||||
callback( job.image );
|
||||
}
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
122
src/navigator.js
122
src/navigator.js
@ -173,7 +173,10 @@ $.Navigator = function( options ){
|
||||
options.controlOptions
|
||||
);
|
||||
|
||||
if ( options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE && options.controlOptions.anchor != $.ControlAnchor.NONE ) {
|
||||
this._resizeWithViewer = options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE &&
|
||||
options.controlOptions.anchor != $.ControlAnchor.NONE;
|
||||
|
||||
if ( this._resizeWithViewer ) {
|
||||
if ( options.width && options.height ) {
|
||||
this.element.style.height = typeof ( options.height ) == "number" ? ( options.height + 'px' ) : options.height;
|
||||
this.element.style.width = typeof ( options.width ) == "number" ? ( options.width + 'px' ) : options.width;
|
||||
@ -194,8 +197,7 @@ $.Navigator = function( options ){
|
||||
this.displayRegionContainer.appendChild(this.displayRegion);
|
||||
this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
|
||||
|
||||
if (options.navigatorRotate)
|
||||
{
|
||||
if (options.navigatorRotate) {
|
||||
options.viewer.addHandler("rotate", function (args) {
|
||||
_setTransformRotate(_this.displayRegionContainer, args.degrees);
|
||||
_setTransformRotate(_this.displayRegion, -args.degrees);
|
||||
@ -213,6 +215,32 @@ $.Navigator = function( options ){
|
||||
scrollHandler: $.delegate( this, onCanvasScroll )
|
||||
});
|
||||
|
||||
this.addHandler("reset-size", function() {
|
||||
if (_this.viewport) {
|
||||
_this.viewport.goHome(true);
|
||||
}
|
||||
});
|
||||
|
||||
this.addHandler("reset-size", function() {
|
||||
if (_this.viewport) {
|
||||
_this.viewport.goHome(true);
|
||||
}
|
||||
});
|
||||
|
||||
viewer.world.addHandler("item-index-change", function(event) {
|
||||
var item = _this.world.getItemAt(event.previousIndex);
|
||||
_this.world.setItemIndex(item, event.newIndex);
|
||||
});
|
||||
|
||||
viewer.world.addHandler("remove-item", function(event) {
|
||||
var theirItem = event.item;
|
||||
var myItem = _this._getMatchingItem(theirItem);
|
||||
if (myItem) {
|
||||
_this.world.removeItem(myItem);
|
||||
}
|
||||
});
|
||||
|
||||
this.update(viewer.viewport);
|
||||
};
|
||||
|
||||
$.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.Navigator.prototype */{
|
||||
@ -228,23 +256,13 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
(this.container.clientWidth === 0 ? 1 : this.container.clientWidth),
|
||||
(this.container.clientHeight === 0 ? 1 : this.container.clientHeight)
|
||||
);
|
||||
|
||||
if ( !containerSize.equals( this.oldContainerSize ) ) {
|
||||
var oldBounds = this.viewport.getBounds();
|
||||
var oldCenter = this.viewport.getCenter();
|
||||
this.viewport.resize( containerSize, true );
|
||||
var imageHeight = 1 / this.source.aspectRatio;
|
||||
var newWidth = oldBounds.width <= 1 ? oldBounds.width : 1;
|
||||
var newHeight = oldBounds.height <= imageHeight ?
|
||||
oldBounds.height : imageHeight;
|
||||
var newBounds = new $.Rect(
|
||||
oldCenter.x - ( newWidth / 2.0 ),
|
||||
oldCenter.y - ( newHeight / 2.0 ),
|
||||
newWidth,
|
||||
newHeight
|
||||
);
|
||||
this.viewport.fitBounds( newBounds, true );
|
||||
this.viewport.goHome(true);
|
||||
this.oldContainerSize = containerSize;
|
||||
this.drawer.update();
|
||||
this.drawer.clear();
|
||||
this.world.draw();
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -264,28 +282,36 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
bottomright;
|
||||
|
||||
viewerSize = $.getElementSize( this.viewer.element );
|
||||
if ( !viewerSize.equals( this.oldViewerSize ) ) {
|
||||
if ( this._resizeWithViewer && !viewerSize.equals( this.oldViewerSize ) ) {
|
||||
this.oldViewerSize = viewerSize;
|
||||
if ( this.maintainSizeRatio ) {
|
||||
|
||||
if ( this.maintainSizeRatio || !this.elementArea) {
|
||||
newWidth = viewerSize.x * this.sizeRatio;
|
||||
newHeight = viewerSize.y * this.sizeRatio;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
newWidth = Math.sqrt(this.elementArea * (viewerSize.x / viewerSize.y));
|
||||
newHeight = this.elementArea / newWidth;
|
||||
}
|
||||
|
||||
this.element.style.width = Math.round( newWidth ) + 'px';
|
||||
this.element.style.height = Math.round( newHeight ) + 'px';
|
||||
|
||||
if (!this.elementArea) {
|
||||
this.elementArea = newWidth * newHeight;
|
||||
}
|
||||
|
||||
this.updateSize();
|
||||
}
|
||||
|
||||
if( viewport && this.viewport ) {
|
||||
bounds = viewport.getBounds( true );
|
||||
topleft = this.viewport.pixelFromPoint( bounds.getTopLeft(), false );
|
||||
bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight(), false ).minus( this.totalBorderWidths );
|
||||
bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight(), false )
|
||||
.minus( this.totalBorderWidths );
|
||||
|
||||
//update style for navigator-box
|
||||
(function(style) {
|
||||
var style = this.displayRegion.style;
|
||||
style.display = this.world.getItemCount() ? 'block' : 'none';
|
||||
|
||||
style.top = Math.round( topleft.y ) + 'px';
|
||||
style.left = Math.round( topleft.x ) + 'px';
|
||||
@ -295,24 +321,52 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
// make sure width and height are non-negative so IE doesn't throw
|
||||
style.width = Math.round( Math.max( width, 0 ) ) + 'px';
|
||||
style.height = Math.round( Math.max( height, 0 ) ) + 'px';
|
||||
|
||||
}( this.displayRegion.style ));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
open: function( source ) {
|
||||
this.updateSize();
|
||||
var containerSize = this.viewer.viewport.containerSize.times( this.sizeRatio );
|
||||
var ts = source.getTileSize(source.maxLevel);
|
||||
if ( ts > containerSize.x || ts > containerSize.y ) {
|
||||
this.minPixelRatio = Math.min( containerSize.x, containerSize.y ) / ts;
|
||||
} else {
|
||||
this.minPixelRatio = this.viewer.minPixelRatio;
|
||||
// overrides Viewer.addTiledImage
|
||||
addTiledImage: function(options) {
|
||||
var _this = this;
|
||||
|
||||
var original = options.originalTiledImage;
|
||||
delete options.original;
|
||||
|
||||
var optionsClone = $.extend({}, options, {
|
||||
success: function(event) {
|
||||
var myItem = event.item;
|
||||
myItem._originalForNavigator = original;
|
||||
_this._matchBounds(myItem, original, true);
|
||||
|
||||
original.addHandler('bounds-change', function() {
|
||||
_this._matchBounds(myItem, original);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
|
||||
},
|
||||
|
||||
// private
|
||||
_getMatchingItem: function(theirItem) {
|
||||
var count = this.world.getItemCount();
|
||||
var item;
|
||||
for (var i = 0; i < count; i++) {
|
||||
item = this.world.getItemAt(i);
|
||||
if (item._originalForNavigator === theirItem) {
|
||||
return item;
|
||||
}
|
||||
return $.Viewer.prototype.open.apply( this, [ source ] );
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
// private
|
||||
_matchBounds: function(myItem, theirItem, immediately) {
|
||||
var bounds = theirItem.getBounds();
|
||||
myItem.setPosition(bounds.getTopLeft(), immediately);
|
||||
myItem.setWidth(bounds.width, immediately);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -126,20 +126,14 @@
|
||||
* The element to append the viewer's container element to. If not provided, the 'id' property must be provided.
|
||||
* If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
|
||||
*
|
||||
* @property {Array|String|Function|Object} [tileSources=null]
|
||||
* Tile source(s) to open initially. This is a complex parameter; see
|
||||
* {@link OpenSeadragon.Viewer#open} for details.
|
||||
*
|
||||
* @property {Number} [tabIndex=0]
|
||||
* Tabbing order index to assign to the viewer element. Positive values are selected in increasing order. When tabIndex is 0
|
||||
* source order is used. A negative value omits the viewer from the tabbing order.
|
||||
*
|
||||
* @property {Array|String|Function|Object[]|Array[]|String[]|Function[]} [tileSources=null]
|
||||
* As an Array, the tileSource can hold either Objects or mixed
|
||||
* types of Arrays of Objects, Strings, or Functions. When a value is a String,
|
||||
* the tileSource is used to create a {@link OpenSeadragon.DziTileSource}.
|
||||
* When a value is a Function, the function is used to create a new
|
||||
* {@link OpenSeadragon.TileSource} whose abstract method
|
||||
* getUrl( level, x, y ) is implemented by the function. Finally, when it
|
||||
* is an Array of objects, it is used to create a
|
||||
* {@link OpenSeadragon.LegacyTileSource}.
|
||||
*
|
||||
* @property {Array} overlays Array of objects defining permanent overlays of
|
||||
* the viewer. The overlays added via this option and later removed with
|
||||
* {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new
|
||||
@ -215,9 +209,6 @@
|
||||
* @property {Number} [opacity=1]
|
||||
* Opacity of the drawer (1=opaque, 0=transparent)
|
||||
*
|
||||
* @property {Number} [layersAspectRatioEpsilon=0.0001]
|
||||
* Maximum aspectRatio mismatch between 2 layers.
|
||||
*
|
||||
* @property {Number} [degrees=0]
|
||||
* Initial rotation.
|
||||
*
|
||||
@ -265,10 +256,14 @@
|
||||
* @property {Number} [visibilityRatio=0.5]
|
||||
* The percentage ( as a number from 0 to 1 ) of the source image which
|
||||
* must be kept within the viewport. If the image is dragged beyond that
|
||||
* limit, it will 'bounce' back until the minimum visibility ration is
|
||||
* limit, it will 'bounce' back until the minimum visibility ratio is
|
||||
* achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to
|
||||
* true will provide the effect of an infinitely scrolling viewport.
|
||||
*
|
||||
* @property {Object} [viewportMargins={}]
|
||||
* Pushes the "home" region in from the sides by the specified amounts.
|
||||
* Possible subproperties (Numbers, in screen coordinates): left, top, right, bottom.
|
||||
*
|
||||
* @property {Number} [imageLoaderLimit=0]
|
||||
* The maximum number of image requests to make concurrently. By default
|
||||
* it is set to 0 allowing the browser to make the maximum number of
|
||||
@ -455,8 +450,8 @@
|
||||
* this setting when set to false.
|
||||
*
|
||||
* @property {Boolean} [showSequenceControl=true]
|
||||
* If the viewer has been configured with a sequence of tile sources, then
|
||||
* provide buttons for navigating forward and backward through the images.
|
||||
* If sequenceMode is true, then provide buttons for navigating forward and
|
||||
* backward through the images.
|
||||
*
|
||||
* @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT]
|
||||
* Placement of the default sequence controls.
|
||||
@ -514,26 +509,29 @@
|
||||
* To only change the button images, consider using
|
||||
* {@link OpenSeadragon.Options.navImages}
|
||||
*
|
||||
* @property {Boolean} [sequenceMode=false]
|
||||
* Set to true to have the viewer treat your tilesources as a sequence of images to
|
||||
* be opened one at a time rather than all at once.
|
||||
*
|
||||
* @property {Number} [initialPage=0]
|
||||
* If the viewer has been configured with a sequence of tile sources, display this page initially.
|
||||
* If sequenceMode is true, display this page initially.
|
||||
*
|
||||
* @property {Boolean} [preserveViewport=false]
|
||||
* If the viewer has been configured with a sequence of tile sources, then
|
||||
* normally navigating through each image resets the viewport to 'home'
|
||||
* position. If preserveViewport is set to true, then the viewport position
|
||||
* is preserved when navigating between images in the sequence.
|
||||
* If sequenceMode is true, then normally navigating to through each image resets the
|
||||
* viewport to 'home' position. If preserveViewport is set to true, then the viewport
|
||||
* position is preserved when navigating between images in the sequence.
|
||||
*
|
||||
* @property {Boolean} [preserveOverlays=false]
|
||||
* If the viewer has been configured with a sequence of tile sources, then
|
||||
* normally navigating through each image resets the overlays.
|
||||
* If sequenceMode is true, then normally navigating to through each image
|
||||
* resets the overlays.
|
||||
* If preserveOverlays is set to true, then the overlays
|
||||
* are preserved when navigating between images in the sequence.
|
||||
* Note: setting preserveOverlays overrides any overlays specified in the
|
||||
* "overlays" property.
|
||||
*
|
||||
* @property {Boolean} [showReferenceStrip=false]
|
||||
* If the viewer has been configured with a sequence of tile sources, then
|
||||
* display a scrolling strip of image thumbnails for navigating through the images.
|
||||
* If sequenceMode is true, then display a scrolling strip of image thumbnails for
|
||||
* navigating through the images.
|
||||
*
|
||||
* @property {String} [referenceStripScroll='horizontal']
|
||||
*
|
||||
@ -548,17 +546,30 @@
|
||||
* @property {Number} [referenceStripSizeRatio=0.2]
|
||||
*
|
||||
* @property {Boolean} [collectionMode=false]
|
||||
* Set to true to have the viewer arrange your TiledImages in a grid or line.
|
||||
*
|
||||
* @property {Number} [collectionRows=3]
|
||||
* If collectionMode is true, specifies how many rows the grid should have. Use 1 to make a line.
|
||||
* If collectionLayout is 'vertical', specifies how many columns instead.
|
||||
*
|
||||
* @property {String} [collectionLayout='horizontal']
|
||||
* If collectionMode is true, specifies whether to arrange vertically or horizontally.
|
||||
*
|
||||
* @property {Number} [collectionTileSize=800]
|
||||
* If collectionMode is true, specifies the size, in viewport coordinates, for each TiledImage to fit into.
|
||||
* The TiledImage will be centered within a square of the specified size.
|
||||
*
|
||||
* @property {Number} [collectionTileMargin=80]
|
||||
* If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage.
|
||||
*
|
||||
* @property {String|Boolean} [crossOriginPolicy=false]
|
||||
* Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will
|
||||
* not use CORS, and the canvas will be tainted.
|
||||
*
|
||||
* @property {Boolean} [ajaxWithCredentials=false]
|
||||
* Whether to set the withCredentials XHR flag for AJAX requests (when loading tile sources).
|
||||
* Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -809,6 +820,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
canvasElement.getContext( '2d' ) );
|
||||
}());
|
||||
|
||||
/**
|
||||
* A ratio comparing the device screen's pixel density to the canvas's backing store pixel density. Defaults to 1 if canvas isn't supported by the browser.
|
||||
* @member {Number} pixelDensityRatio
|
||||
* @memberof OpenSeadragon
|
||||
*/
|
||||
$.pixelDensityRatio = (function () {
|
||||
if ( $.supportsCanvas ) {
|
||||
var context = document.createElement('canvas').getContext('2d');
|
||||
var devicePixelRatio = window.devicePixelRatio || 1;
|
||||
var backingStoreRatio = context.webkitBackingStorePixelRatio ||
|
||||
context.mozBackingStorePixelRatio ||
|
||||
context.msBackingStorePixelRatio ||
|
||||
context.oBackingStorePixelRatio ||
|
||||
context.backingStorePixelRatio || 1;
|
||||
return devicePixelRatio / backingStoreRatio;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}());
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
||||
@ -916,6 +946,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
tileHost: null,
|
||||
initialPage: 0,
|
||||
crossOriginPolicy: false,
|
||||
ajaxWithCredentials: false,
|
||||
|
||||
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
|
||||
panHorizontal: true,
|
||||
@ -988,9 +1019,6 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
// APPEARANCE
|
||||
opacity: 1,
|
||||
|
||||
// LAYERS SETTINGS
|
||||
layersAspectRatioEpsilon: 0.0001,
|
||||
|
||||
//REFERENCE STRIP SETTINGS
|
||||
showReferenceStrip: false,
|
||||
referenceStripScroll: 'horizontal',
|
||||
@ -1005,6 +1033,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
collectionLayout: 'horizontal', //vertical
|
||||
collectionMode: false,
|
||||
collectionTileSize: 800,
|
||||
collectionTileMargin: 80,
|
||||
|
||||
//PERFORMANCE SETTINGS
|
||||
imageLoaderLimit: 0,
|
||||
@ -1924,13 +1953,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
|
||||
/**
|
||||
* Makes an AJAX request.
|
||||
* @function
|
||||
* @param {String} url - the url to request
|
||||
* @param {Function} onSuccess - a function to call on a successful response
|
||||
* @param {Function} onError - a function to call on when an error occurs
|
||||
* @param {Object} options
|
||||
* @param {String} options.url - the url to request
|
||||
* @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 {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
|
||||
* @throws {Error}
|
||||
*/
|
||||
makeAjaxRequest: function( url, onSuccess, onError ) {
|
||||
var withCredentials;
|
||||
|
||||
// Note that our preferred API is that you pass in a single object; the named
|
||||
// arguments are for legacy support.
|
||||
if( $.isPlainObject( url ) ){
|
||||
onSuccess = url.success;
|
||||
onError = url.error;
|
||||
withCredentials = url.withCredentials;
|
||||
url = url.url;
|
||||
}
|
||||
|
||||
var protocol = $.getUrlProtocol( url );
|
||||
var request = $.createAjaxRequest( protocol === "file:" );
|
||||
|
||||
@ -1957,6 +1998,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
}
|
||||
};
|
||||
|
||||
if (withCredentials) {
|
||||
request.withCredentials = true;
|
||||
}
|
||||
|
||||
try {
|
||||
request.open( "GET", url, true );
|
||||
request.send( null );
|
||||
@ -2271,7 +2316,8 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
debug: nullfunction,
|
||||
info: nullfunction,
|
||||
warn: nullfunction,
|
||||
error: nullfunction
|
||||
error: nullfunction,
|
||||
assert: nullfunction
|
||||
};
|
||||
|
||||
|
||||
|
@ -60,6 +60,13 @@ $.Point = function( x, y ) {
|
||||
};
|
||||
|
||||
$.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
|
||||
/**
|
||||
* @function
|
||||
* @returns {OpenSeadragon.Point} a duplicate of this Point
|
||||
*/
|
||||
clone: function() {
|
||||
return new $.Point(this.x, this.y);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add another Point to this point and return a new Point.
|
||||
@ -189,7 +196,7 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
|
||||
* @returns {String} A string representation of this point.
|
||||
*/
|
||||
toString: function() {
|
||||
return "(" + Math.round(this.x) + "," + Math.round(this.y) + ")";
|
||||
return "(" + (Math.round(this.x * 100) / 100) + "," + (Math.round(this.y * 100) / 100) + ")";
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -75,6 +75,13 @@ $.Rect = function( x, y, width, height ) {
|
||||
};
|
||||
|
||||
$.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
||||
/**
|
||||
* @function
|
||||
* @returns {OpenSeadragon.Rect} a duplicate of this Rect
|
||||
*/
|
||||
clone: function() {
|
||||
return new $.Rect(this.x, this.y, this.width, this.height);
|
||||
},
|
||||
|
||||
/**
|
||||
* The aspect ratio is simply the ratio of width to height.
|
||||
@ -194,6 +201,21 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the smallest rectangle that will contain this and the given rectangle.
|
||||
* @param {OpenSeadragon.Rect} rect
|
||||
* @return {OpenSeadragon.Rect} The new rectangle.
|
||||
*/
|
||||
// ----------
|
||||
union: function(rect) {
|
||||
var left = Math.min(this.x, rect.x);
|
||||
var top = Math.min(this.y, rect.y);
|
||||
var right = Math.max(this.x + this.width, rect.x + rect.width);
|
||||
var bottom = Math.max(this.y + this.height, rect.y + rect.height);
|
||||
|
||||
return new OpenSeadragon.Rect(left, top, right - left, bottom - top);
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotates a rectangle around a point. Currently only 90, 180, and 270
|
||||
* degrees are supported.
|
||||
@ -257,10 +279,10 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
|
||||
*/
|
||||
toString: function() {
|
||||
return "[" +
|
||||
Math.round(this.x*100) + "," +
|
||||
Math.round(this.y*100) + "," +
|
||||
Math.round(this.width*100) + "x" +
|
||||
Math.round(this.height*100) +
|
||||
(Math.round(this.x*100) / 100) + "," +
|
||||
(Math.round(this.y*100) / 100) + "," +
|
||||
(Math.round(this.width*100) / 100) + "x" +
|
||||
(Math.round(this.height*100) / 100) +
|
||||
"]";
|
||||
}
|
||||
};
|
||||
|
@ -290,6 +290,13 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
// Overrides Viewer.destroy
|
||||
destroy: function() {
|
||||
if (this.element) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
}
|
||||
|
||||
} );
|
||||
|
@ -117,10 +117,8 @@ $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
|
||||
* @param {Number} target
|
||||
*/
|
||||
resetTo: function( target ) {
|
||||
this.target.value = target;
|
||||
this.target.time = this.current.time;
|
||||
this.start.value = this.target.value;
|
||||
this.start.time = this.target.time;
|
||||
this.start.value = this.target.value = this.current.value = target;
|
||||
this.start.time = this.target.time = this.current.time = $.now();
|
||||
},
|
||||
|
||||
/**
|
||||
|
47
src/tile.js
47
src/tile.js
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
var TILE_CACHE = {};
|
||||
|
||||
/**
|
||||
* @class Tile
|
||||
* @memberof OpenSeadragon
|
||||
@ -231,7 +231,8 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
* Renders the tile in a canvas-based context.
|
||||
* @function
|
||||
* @param {Canvas} context
|
||||
* @param {Function} method for firing the drawing event. drawingHandler({context, tile, rendered})
|
||||
* @param {Function} drawingHandler - Method for firing the drawing event.
|
||||
* drawingHandler({context, tile, rendered})
|
||||
* where <code>rendered</code> is the context with the pre-drawn image.
|
||||
*/
|
||||
drawCanvas: function( context, drawingHandler ) {
|
||||
@ -241,16 +242,23 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
rendered,
|
||||
canvas;
|
||||
|
||||
if ( !this.loaded || !( this.image || TILE_CACHE[ this.url ] ) ){
|
||||
if (!this.cacheImageRecord) {
|
||||
$.console.warn('[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached', this.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
rendered = this.cacheImageRecord.getRenderedContext();
|
||||
|
||||
if ( !this.loaded || !( this.image || rendered) ){
|
||||
$.console.warn(
|
||||
"Attempting to draw tile %s when it's not yet loaded.",
|
||||
this.toString()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
context.globalAlpha = this.opacity;
|
||||
|
||||
//context.save();
|
||||
context.globalAlpha = this.opacity;
|
||||
|
||||
//if we are supposed to be rendering fully opaque rectangle,
|
||||
//ie its done fading or fading is turned off, and if we are drawing
|
||||
@ -260,46 +268,40 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
//clearing only the inside of the rectangle occupied
|
||||
//by the png prevents edge flikering
|
||||
context.clearRect(
|
||||
position.x+1,
|
||||
position.y+1,
|
||||
size.x-2,
|
||||
size.y-2
|
||||
(position.x * $.pixelDensityRatio)+1,
|
||||
(position.y * $.pixelDensityRatio)+1,
|
||||
(size.x * $.pixelDensityRatio)-2,
|
||||
(size.y * $.pixelDensityRatio)-2
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
if( !TILE_CACHE[ this.url ] ){
|
||||
if(!rendered){
|
||||
canvas = document.createElement( 'canvas' );
|
||||
canvas.width = this.image.width;
|
||||
canvas.height = this.image.height;
|
||||
rendered = canvas.getContext('2d');
|
||||
rendered.drawImage( this.image, 0, 0 );
|
||||
TILE_CACHE[ this.url ] = rendered;
|
||||
this.cacheImageRecord.setRenderedContext(rendered);
|
||||
//since we are caching the prerendered image on a canvas
|
||||
//allow the image to not be held in memory
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
rendered = TILE_CACHE[ this.url ];
|
||||
|
||||
// This gives the application a chance to make image manipulation changes as we are rendering the image
|
||||
drawingHandler({context: context, tile: this, rendered: rendered});
|
||||
|
||||
//rendered.save();
|
||||
context.drawImage(
|
||||
rendered.canvas,
|
||||
0,
|
||||
0,
|
||||
rendered.canvas.width,
|
||||
rendered.canvas.height,
|
||||
position.x,
|
||||
position.y,
|
||||
size.x,
|
||||
size.y
|
||||
position.x * $.pixelDensityRatio,
|
||||
position.y * $.pixelDensityRatio,
|
||||
size.x * $.pixelDensityRatio,
|
||||
size.y * $.pixelDensityRatio
|
||||
);
|
||||
//rendered.restore();
|
||||
|
||||
//context.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -313,9 +315,6 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
if ( this.element && this.element.parentNode ) {
|
||||
this.element.parentNode.removeChild( this.element );
|
||||
}
|
||||
if ( TILE_CACHE[ this.url ]){
|
||||
delete TILE_CACHE[ this.url ];
|
||||
}
|
||||
|
||||
this.element = null;
|
||||
this.imgElement = null;
|
||||
|
238
src/tilecache.js
Normal file
238
src/tilecache.js
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* OpenSeadragon - TileCache
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
// private class
|
||||
var TileRecord = function( options ) {
|
||||
$.console.assert( options, "[TileCache.cacheTile] options is required" );
|
||||
$.console.assert( options.tile, "[TileCache.cacheTile] options.tile is required" );
|
||||
$.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" );
|
||||
this.tile = options.tile;
|
||||
this.tiledImage = options.tiledImage;
|
||||
};
|
||||
|
||||
// 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;
|
||||
this._tiles = [];
|
||||
};
|
||||
|
||||
ImageRecord.prototype = {
|
||||
destroy: function() {
|
||||
this._image = null;
|
||||
this._renderedContext = null;
|
||||
this._tiles = null;
|
||||
},
|
||||
|
||||
getImage: function() {
|
||||
return this._image;
|
||||
},
|
||||
|
||||
getRenderedContext: function() {
|
||||
return this._renderedContext;
|
||||
},
|
||||
|
||||
setRenderedContext: function(renderedContext) {
|
||||
this._renderedContext = renderedContext;
|
||||
},
|
||||
|
||||
addTile: function(tile) {
|
||||
$.console.assert(tile, '[ImageRecord.addTile] tile is required');
|
||||
this._tiles.push(tile);
|
||||
},
|
||||
|
||||
removeTile: function(tile) {
|
||||
for (var i = 0; i < this._tiles.length; i++) {
|
||||
if (this._tiles[i] === tile) {
|
||||
this._tiles.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$.console.warn('[ImageRecord.removeTile] trying to remove unknown tile', tile);
|
||||
},
|
||||
|
||||
getTileCount: function() {
|
||||
return this._tiles.length;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @class TileCache
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Stores all the tiles displayed in a {@link OpenSeadragon.Viewer}.
|
||||
* You generally won't have to interact with the TileCache directly.
|
||||
* @param {Object} options - Configuration for this TileCache.
|
||||
* @param {Number} [options.maxImageCacheCount] - See maxImageCacheCount in
|
||||
* {@link OpenSeadragon.Options} for details.
|
||||
*/
|
||||
$.TileCache = function( options ) {
|
||||
options = options || {};
|
||||
|
||||
this._maxImageCacheCount = options.maxImageCacheCount || $.DEFAULT_SETTINGS.maxImageCacheCount;
|
||||
this._tilesLoaded = [];
|
||||
this._imagesLoaded = [];
|
||||
this._imagesLoadedCount = 0;
|
||||
};
|
||||
|
||||
$.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{
|
||||
/**
|
||||
* @returns {Number} The total number of tiles that have been loaded by
|
||||
* this TileCache.
|
||||
*/
|
||||
numTilesLoaded: function() {
|
||||
return this._tilesLoaded.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Caches the specified tile, removing an old tile if necessary to stay under the
|
||||
* maxImageCacheCount specified on construction. Note that if multiple tiles reference
|
||||
* the same image, there may be more tiles than maxImageCacheCount; the goal is to keep
|
||||
* the number of images below that number. Note, as well, that even the number of images
|
||||
* may temporarily surpass that number, but should eventually come back down to the max specified.
|
||||
* @param {Object} options - Tile info.
|
||||
* @param {OpenSeadragon.Tile} options.tile - The tile to cache.
|
||||
* @param {OpenSeadragon.TiledImage} options.tiledImage - The TiledImage that owns that tile.
|
||||
* @param {Number} [options.cutoff=0] - If adding this tile goes over the cache max count, this
|
||||
* function will release an old tile. The cutoff option specifies a tile level at or below which
|
||||
* tiles will not be released.
|
||||
*/
|
||||
cacheTile: function( options ) {
|
||||
$.console.assert( options, "[TileCache.cacheTile] options is required" );
|
||||
$.console.assert( options.tile, "[TileCache.cacheTile] options.tile is required" );
|
||||
$.console.assert( options.tile.url, "[TileCache.cacheTile] options.tile.url is required" );
|
||||
$.console.assert( options.tile.image, "[TileCache.cacheTile] options.tile.image is required" );
|
||||
$.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" );
|
||||
|
||||
var cutoff = options.cutoff || 0;
|
||||
var insertionIndex = this._tilesLoaded.length;
|
||||
|
||||
var imageRecord = this._imagesLoaded[options.tile.url];
|
||||
if (!imageRecord) {
|
||||
imageRecord = this._imagesLoaded[options.tile.url] = new ImageRecord({
|
||||
image: options.tile.image
|
||||
});
|
||||
|
||||
this._imagesLoadedCount++;
|
||||
}
|
||||
|
||||
imageRecord.addTile(options.tile);
|
||||
options.tile.cacheImageRecord = imageRecord;
|
||||
|
||||
// Note that just because we're unloading a tile doesn't necessarily mean
|
||||
// we're unloading an image. With repeated calls it should sort itself out, though.
|
||||
if ( this._imagesLoadedCount > this._maxImageCacheCount ) {
|
||||
var worstTile = null;
|
||||
var worstTileIndex = -1;
|
||||
var prevTile, worstTime, worstLevel, prevTime, prevLevel, prevTileRecord;
|
||||
|
||||
for ( var i = this._tilesLoaded.length - 1; i >= 0; i-- ) {
|
||||
prevTileRecord = this._tilesLoaded[ i ];
|
||||
prevTile = prevTileRecord.tile;
|
||||
|
||||
if ( prevTile.level <= cutoff || prevTile.beingDrawn ) {
|
||||
continue;
|
||||
} else if ( !worstTile ) {
|
||||
worstTile = prevTile;
|
||||
worstTileIndex = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
prevTime = prevTile.lastTouchTime;
|
||||
worstTime = worstTile.lastTouchTime;
|
||||
prevLevel = prevTile.level;
|
||||
worstLevel = worstTile.level;
|
||||
|
||||
if ( prevTime < worstTime ||
|
||||
( prevTime == worstTime && prevLevel > worstLevel ) ) {
|
||||
worstTile = prevTile;
|
||||
worstTileIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ( worstTile && worstTileIndex >= 0 ) {
|
||||
this._unloadTile(worstTile);
|
||||
insertionIndex = worstTileIndex;
|
||||
}
|
||||
}
|
||||
|
||||
this._tilesLoaded[ insertionIndex ] = new TileRecord({
|
||||
tile: options.tile,
|
||||
tiledImage: options.tiledImage
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all tiles associated with the specified tiledImage.
|
||||
* @param {OpenSeadragon.TiledImage} tiledImage
|
||||
*/
|
||||
clearTilesFor: function( tiledImage ) {
|
||||
$.console.assert(tiledImage, '[TileCache.clearTilesFor] tiledImage is required');
|
||||
var tileRecord;
|
||||
for ( var i = 0; i < this._tilesLoaded.length; ++i ) {
|
||||
tileRecord = this._tilesLoaded[ i ];
|
||||
if ( tileRecord.tiledImage === tiledImage ) {
|
||||
this._unloadTile(tileRecord.tile);
|
||||
this._tilesLoaded.splice( i, 1 );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
getImageRecord: function(url) {
|
||||
$.console.assert(url, '[TileCache.getImageRecord] url is required');
|
||||
return this._imagesLoaded[url];
|
||||
},
|
||||
|
||||
// private
|
||||
_unloadTile: function(tile) {
|
||||
$.console.assert(tile, '[TileCache._unloadTile] tile is required');
|
||||
tile.unload();
|
||||
tile.cacheImageRecord = null;
|
||||
|
||||
var imageRecord = this._imagesLoaded[tile.url];
|
||||
imageRecord.removeTile(tile);
|
||||
if (!imageRecord.getTileCount()) {
|
||||
imageRecord.destroy();
|
||||
delete this._imagesLoaded[tile.url];
|
||||
this._imagesLoadedCount--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}( OpenSeadragon ));
|
1149
src/tiledimage.js
Normal file
1149
src/tiledimage.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -38,44 +38,52 @@
|
||||
/**
|
||||
* @class TileSource
|
||||
* @classdesc The TileSource contains the most basic implementation required to create a
|
||||
* smooth transition between layer in an image pyramid. It has only a single key
|
||||
* interface that must be implemented to complete it key functionality:
|
||||
* smooth transition between layers in an image pyramid. It has only a single key
|
||||
* interface that must be implemented to complete its key functionality:
|
||||
* 'getTileUrl'. It also has several optional interfaces that can be
|
||||
* implemented if a new TileSource wishes to support configuration via a simple
|
||||
* object or array ('configure') and if the tile source supports or requires
|
||||
* configuration via retreival of a document on the network ala AJAX or JSONP,
|
||||
* configuration via retrieval of a document on the network ala AJAX or JSONP,
|
||||
* ('getImageInfo').
|
||||
* <br/>
|
||||
* By default the image pyramid is split into N layers where the images longest
|
||||
* By default the image pyramid is split into N layers where the image's longest
|
||||
* side in M (in pixels), where N is the smallest integer which satisfies
|
||||
* <strong>2^(N+1) >= M</strong>.
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.EventSource
|
||||
* @param {Number|Object|Array|String} width
|
||||
* If more than a single argument is supplied, the traditional use of
|
||||
* positional parameters is supplied and width is expected to be the width
|
||||
* source image at its max resolution in pixels. If a single argument is supplied and
|
||||
* it is an Object or Array, the construction is assumed to occur through
|
||||
* the extending classes implementation of 'configure'. Finally if only a
|
||||
* single argument is supplied and it is a String, the extending class is
|
||||
* expected to implement 'getImageInfo' and 'configure'.
|
||||
* @param {Number} height
|
||||
* @param {Object} options
|
||||
* You can either specify a URL, or literally define the TileSource (by specifying
|
||||
* width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,
|
||||
* the extending class is expected to implement 'getImageInfo' and 'configure'.
|
||||
* For the latter, the construction is assumed to occur through
|
||||
* the extending classes implementation of 'configure'.
|
||||
* @param {String} [options.url]
|
||||
* The URL for the data necessary for this TileSource.
|
||||
* @param {Function} [options.success]
|
||||
* A function to be called upon successful creation.
|
||||
* @param {Boolean} [options.ajaxWithCredentials]
|
||||
* If this TileSource needs to make an AJAX call, this specifies whether to set
|
||||
* the XHR's withCredentials (for accessing secure data).
|
||||
* @param {Number} [options.width]
|
||||
* Width of the source image at max resolution in pixels.
|
||||
* @param {Number} tileSize
|
||||
* @param {Number} [options.height]
|
||||
* Height of the source image at max resolution in pixels.
|
||||
* @param {Number} [options.tileSize]
|
||||
* The size of the tiles to assumed to make up each pyramid layer in pixels.
|
||||
* Tile size determines the point at which the image pyramid must be
|
||||
* divided into a matrix of smaller images.
|
||||
* @param {Number} tileOverlap
|
||||
* @param {Number} [options.tileOverlap]
|
||||
* The number of pixels each tile is expected to overlap touching tiles.
|
||||
* @param {Number} minLevel
|
||||
* @param {Number} [options.minLevel]
|
||||
* The minimum level to attempt to load.
|
||||
* @param {Number} maxLevel
|
||||
* @param {Number} [options.maxLevel]
|
||||
* The maximum level to attempt to load.
|
||||
*/
|
||||
$.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) {
|
||||
var callback = null,
|
||||
args = arguments,
|
||||
var _this = this;
|
||||
|
||||
var args = arguments,
|
||||
options,
|
||||
i;
|
||||
|
||||
@ -102,18 +110,22 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
||||
//source
|
||||
$.extend( true, this, options );
|
||||
|
||||
if (!this.success) {
|
||||
//Any functions that are passed as arguments are bound to the ready callback
|
||||
/*jshint loopfunc:true*/
|
||||
for ( i = 0; i < arguments.length; i++ ) {
|
||||
if ( $.isFunction( arguments[ i ] ) ) {
|
||||
callback = arguments[ i ];
|
||||
this.addHandler( 'ready', function ( event ) {
|
||||
callback( event );
|
||||
} );
|
||||
this.success = arguments[ i ];
|
||||
//only one callback per constructor
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.success) {
|
||||
this.addHandler( 'ready', function ( event ) {
|
||||
_this.success( event );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ratio of width to height
|
||||
@ -154,6 +166,10 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
||||
*/
|
||||
|
||||
if( 'string' == $.type( arguments[ 0 ] ) ){
|
||||
this.url = arguments[0];
|
||||
}
|
||||
|
||||
if (this.url) {
|
||||
//in case the getImageInfo method is overriden and/or implies an
|
||||
//async mechanism set some safe defaults first
|
||||
this.aspectRatio = 1;
|
||||
@ -165,7 +181,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
||||
this.ready = false;
|
||||
//configuration via url implies the extending class
|
||||
//implements and 'configure'
|
||||
this.getImageInfo( arguments[ 0 ] );
|
||||
this.getImageInfo( this.url );
|
||||
|
||||
} else {
|
||||
|
||||
@ -185,8 +201,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
||||
Math.log( 2 )
|
||||
) : 0
|
||||
);
|
||||
if( callback && $.isFunction( callback ) ){
|
||||
callback( this );
|
||||
if( this.success && $.isFunction( this.success ) ){
|
||||
this.success( this );
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,6 +371,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
||||
}
|
||||
|
||||
options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
|
||||
if (options.ajaxWithCredentials === undefined) {
|
||||
options.ajaxWithCredentials = _this.ajaxWithCredentials;
|
||||
}
|
||||
|
||||
readySource = new $TileSource( options );
|
||||
_this.ready = true;
|
||||
/**
|
||||
@ -383,10 +403,14 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
||||
});
|
||||
} else {
|
||||
// request info via xhr asynchronously.
|
||||
$.makeAjaxRequest( url, function( xhr ) {
|
||||
$.makeAjaxRequest( {
|
||||
url: url,
|
||||
withCredentials: this.ajaxWithCredentials,
|
||||
success: function( xhr ) {
|
||||
var data = processResponse( xhr );
|
||||
callback( data );
|
||||
}, function ( xhr, exc ) {
|
||||
},
|
||||
error: function ( xhr, exc ) {
|
||||
var msg;
|
||||
|
||||
/*
|
||||
@ -422,6 +446,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
|
||||
message: msg,
|
||||
source: url
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -34,110 +34,9 @@
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @class TileSourceCollection
|
||||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.TileSource
|
||||
*/
|
||||
// deprecated
|
||||
$.TileSourceCollection = function( tileSize, tileSources, rows, layout ) {
|
||||
var options;
|
||||
|
||||
if( $.isPlainObject( tileSize ) ){
|
||||
options = tileSize;
|
||||
}else{
|
||||
options = {
|
||||
tileSize: arguments[ 0 ],
|
||||
tileSources: arguments[ 1 ],
|
||||
rows: arguments[ 2 ],
|
||||
layout: arguments[ 3 ]
|
||||
$.console.error('TileSourceCollection is deprecated; use World instead');
|
||||
};
|
||||
}
|
||||
|
||||
if( !options.layout ){
|
||||
options.layout = 'horizontal';
|
||||
}
|
||||
|
||||
var minLevel = 0,
|
||||
levelSize = 1.0,
|
||||
tilesPerRow = Math.ceil( options.tileSources.length / options.rows ),
|
||||
longSide = tilesPerRow >= options.rows ?
|
||||
tilesPerRow :
|
||||
options.rows;
|
||||
|
||||
if( 'horizontal' == options.layout ){
|
||||
options.width = ( options.tileSize ) * tilesPerRow;
|
||||
options.height = ( options.tileSize ) * options.rows;
|
||||
} else {
|
||||
options.height = ( options.tileSize ) * tilesPerRow;
|
||||
options.width = ( options.tileSize ) * options.rows;
|
||||
}
|
||||
|
||||
options.tileOverlap = -options.tileMargin;
|
||||
options.tilesPerRow = tilesPerRow;
|
||||
|
||||
//Set min level to avoid loading sublevels since collection is a
|
||||
//different kind of abstraction
|
||||
|
||||
while( levelSize < ( options.tileSize ) * longSide ){
|
||||
//$.console.log( '%s levelSize %s minLevel %s', options.tileSize * longSide, levelSize, minLevel );
|
||||
levelSize = levelSize * 2.0;
|
||||
minLevel++;
|
||||
}
|
||||
options.minLevel = minLevel;
|
||||
|
||||
//for( var name in options ){
|
||||
// $.console.log( 'Collection %s %s', name, options[ name ] );
|
||||
//}
|
||||
|
||||
$.TileSource.apply( this, [ options ] );
|
||||
|
||||
};
|
||||
|
||||
$.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.TileSourceCollection.prototype */{
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
getTileBounds: function( level, x, y ) {
|
||||
var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ),
|
||||
px = this.tileSize * x - this.tileOverlap,
|
||||
py = this.tileSize * y - this.tileOverlap,
|
||||
sx = this.tileSize + 1 * this.tileOverlap,
|
||||
sy = this.tileSize + 1 * this.tileOverlap,
|
||||
scale = 1.0 / dimensionsScaled.x;
|
||||
|
||||
sx = Math.min( sx, dimensionsScaled.x - px );
|
||||
sy = Math.min( sy, dimensionsScaled.y - py );
|
||||
|
||||
return new $.Rect( px * scale, py * scale, sx * scale, sy * scale );
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @function
|
||||
*/
|
||||
configure: function( data, url ){
|
||||
return;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
getTileUrl: function( level, x, y ) {
|
||||
//$.console.log([ level, '/', x, '_', y ].join( '' ));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}( OpenSeadragon ));
|
||||
|
1109
src/viewer.js
1109
src/viewer.js
File diff suppressed because it is too large
Load Diff
375
src/viewport.js
375
src/viewport.js
@ -37,10 +37,23 @@
|
||||
|
||||
/**
|
||||
* @class Viewport
|
||||
* @classdesc Handles coordinate-related functionality (zoom, pan, rotation, etc.) for an {@link OpenSeadragon.Viewer}.
|
||||
* A new instance is created for each TileSource opened (see {@link OpenSeadragon.Viewer#viewport}).
|
||||
*
|
||||
* @memberof OpenSeadragon
|
||||
* @classdesc Handles coordinate-related functionality (zoom, pan, rotation, etc.)
|
||||
* for an {@link OpenSeadragon.Viewer}.
|
||||
* @param {Object} options - Options for this Viewport.
|
||||
* @param {Object} [options.margins] - See viewportMargins in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.springStiffness] - See springStiffness in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.animationTime] - See animationTime in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.minZoomImageRatio] - See minZoomImageRatio in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.maxZoomPixelRatio] - See maxZoomPixelRatio in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.visibilityRatio] - See visibilityRatio in {@link OpenSeadragon.Options}.
|
||||
* @param {Boolean} [options.wrapHorizontal] - See wrapHorizontal in {@link OpenSeadragon.Options}.
|
||||
* @param {Boolean} [options.wrapVertical] - See wrapVertical in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.defaultZoomLevel] - See defaultZoomLevel in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.minZoomLevel] - See minZoomLevel in {@link OpenSeadragon.Options}.
|
||||
* @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}.
|
||||
*/
|
||||
$.Viewport = function( options ) {
|
||||
|
||||
@ -63,6 +76,15 @@ $.Viewport = function( options ) {
|
||||
delete options.config;
|
||||
}
|
||||
|
||||
this._margins = $.extend({
|
||||
left: 0,
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}, options.margins || {});
|
||||
|
||||
delete options.margins;
|
||||
|
||||
$.extend( true, this, {
|
||||
|
||||
//required settings
|
||||
@ -89,6 +111,11 @@ $.Viewport = function( options ) {
|
||||
|
||||
}, options );
|
||||
|
||||
this._containerInnerSize = new $.Point(
|
||||
Math.max(1, this.containerSize.x - (this._margins.left + this._margins.right)),
|
||||
Math.max(1, this.containerSize.y - (this._margins.top + this._margins.bottom))
|
||||
);
|
||||
|
||||
this.centerSpringX = new $.Spring({
|
||||
initial: 0,
|
||||
springStiffness: this.springStiffness,
|
||||
@ -105,44 +132,77 @@ $.Viewport = function( options ) {
|
||||
animationTime: this.animationTime
|
||||
});
|
||||
|
||||
this._oldCenterX = this.centerSpringX.current.value;
|
||||
this._oldCenterY = this.centerSpringY.current.value;
|
||||
this._oldZoom = this.zoomSpring.current.value;
|
||||
|
||||
if (this.contentSize) {
|
||||
this.resetContentSize( this.contentSize );
|
||||
} else {
|
||||
this.setHomeBounds(new $.Rect(0, 0, 1, 1), 1);
|
||||
}
|
||||
|
||||
this.goHome( true );
|
||||
this.update();
|
||||
};
|
||||
|
||||
$.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @fires OpenSeadragon.Viewer.event:reset-size
|
||||
*/
|
||||
resetContentSize: function( contentSize ){
|
||||
this.contentSize = contentSize;
|
||||
$.console.assert(contentSize, "[Viewport.resetContentSize] contentSize is required");
|
||||
$.console.assert(contentSize instanceof $.Point, "[Viewport.resetContentSize] contentSize must be an OpenSeadragon.Point");
|
||||
$.console.assert(contentSize.x > 0, "[Viewport.resetContentSize] contentSize.x must be greater than 0");
|
||||
$.console.assert(contentSize.y > 0, "[Viewport.resetContentSize] contentSize.y must be greater than 0");
|
||||
|
||||
this.setHomeBounds(new $.Rect(0, 0, 1, contentSize.y / contentSize.x), contentSize.x);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the viewport's home bounds and constraints.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Rect} bounds - the new bounds in viewport coordinates
|
||||
* @param {Number} contentFactor - how many content units per viewport unit
|
||||
* @fires OpenSeadragon.Viewer.event:reset-size
|
||||
*/
|
||||
setHomeBounds: function(bounds, contentFactor) {
|
||||
$.console.assert(bounds, "[Viewport.setHomeBounds] bounds is required");
|
||||
$.console.assert(bounds instanceof $.Rect, "[Viewport.setHomeBounds] bounds must be an OpenSeadragon.Rect");
|
||||
$.console.assert(bounds.width > 0, "[Viewport.setHomeBounds] bounds.width must be greater than 0");
|
||||
$.console.assert(bounds.height > 0, "[Viewport.setHomeBounds] bounds.height must be greater than 0");
|
||||
|
||||
this.homeBounds = bounds.clone();
|
||||
this.contentSize = this.homeBounds.getSize().times(contentFactor);
|
||||
this.contentAspectX = this.contentSize.x / this.contentSize.y;
|
||||
this.contentAspectY = this.contentSize.y / this.contentSize.x;
|
||||
this.fitWidthBounds = new $.Rect( 0, 0, 1, this.contentAspectY );
|
||||
this.fitHeightBounds = new $.Rect( 0, 0, this.contentAspectY, this.contentAspectY);
|
||||
|
||||
this.homeBounds = new $.Rect( 0, 0, 1, this.contentAspectY );
|
||||
|
||||
if( this.viewer ){
|
||||
/**
|
||||
* Raised when the viewer's content size is reset (see {@link OpenSeadragon.Viewport#resetContentSize}).
|
||||
* Raised when the viewer's content size or home bounds are reset
|
||||
* (see {@link OpenSeadragon.Viewport#resetContentSize},
|
||||
* {@link OpenSeadragon.Viewport#setHomeBounds}).
|
||||
*
|
||||
* @event reset-size
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
|
||||
* @property {OpenSeadragon.Point} contentSize
|
||||
* @property {OpenSeadragon.Rect} homeBounds
|
||||
* @property {Number} contentFactor
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.viewer.raiseEvent( 'reset-size', {
|
||||
contentSize: contentSize
|
||||
contentSize: this.contentSize.clone(),
|
||||
contentFactor: contentFactor,
|
||||
homeBounds: this.homeBounds.clone()
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -155,15 +215,18 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
var aspectFactor =
|
||||
this.contentAspectX / this.getAspectRatio();
|
||||
|
||||
var output;
|
||||
if( this.homeFillsViewer ){ // fill the viewer and clip the image
|
||||
return ( aspectFactor >= 1) ?
|
||||
output = ( aspectFactor >= 1) ?
|
||||
aspectFactor :
|
||||
1;
|
||||
} else {
|
||||
return ( aspectFactor >= 1 ) ?
|
||||
output = ( aspectFactor >= 1 ) ?
|
||||
1 :
|
||||
aspectFactor;
|
||||
}
|
||||
|
||||
return output / this.homeBounds.width;
|
||||
}
|
||||
},
|
||||
|
||||
@ -216,16 +279,18 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
this.minZoomLevel :
|
||||
this.minZoomImageRatio * homeZoom;
|
||||
|
||||
return Math.min( zoom, homeZoom );
|
||||
return zoom;
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
*/
|
||||
getMaxZoom: function() {
|
||||
var zoom = this.maxZoomLevel ?
|
||||
this.maxZoomLevel :
|
||||
( this.contentSize.x * this.maxZoomPixelRatio / this.containerSize.x );
|
||||
var zoom = this.maxZoomLevel;
|
||||
if (!zoom) {
|
||||
zoom = this.contentSize.x * this.maxZoomPixelRatio / this._containerInnerSize.x;
|
||||
zoom /= this.homeBounds.width;
|
||||
}
|
||||
|
||||
return Math.max( zoom, this.getHomeZoom() );
|
||||
},
|
||||
@ -234,11 +299,12 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* @function
|
||||
*/
|
||||
getAspectRatio: function() {
|
||||
return this.containerSize.x / this.containerSize.y;
|
||||
return this._containerInnerSize.x / this._containerInnerSize.y;
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @returns {OpenSeadragon.Point} The size of the container, in screen coordinates.
|
||||
*/
|
||||
getContainerSize: function() {
|
||||
return new $.Point(
|
||||
@ -250,6 +316,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
/**
|
||||
* @function
|
||||
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
|
||||
* @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.
|
||||
*/
|
||||
getBounds: function( current ) {
|
||||
var center = this.getCenter( current ),
|
||||
@ -264,6 +331,22 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
|
||||
* @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,
|
||||
* including the space taken by margins, in viewport coordinates.
|
||||
*/
|
||||
getBoundsWithMargins: function( current ) {
|
||||
var bounds = this.getBounds(current);
|
||||
var factor = this._containerInnerSize.x * this.getZoom(current);
|
||||
bounds.x -= this._margins.left / factor;
|
||||
bounds.y -= this._margins.top / factor;
|
||||
bounds.width += (this._margins.left + this._margins.right) / factor;
|
||||
bounds.height += (this._margins.top + this._margins.bottom) / factor;
|
||||
return bounds;
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
|
||||
@ -304,13 +387,9 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
height
|
||||
);
|
||||
|
||||
newZoomPixel = this.zoomPoint.minus(
|
||||
bounds.getTopLeft()
|
||||
).times(
|
||||
this.containerSize.x / bounds.width
|
||||
);
|
||||
newZoomPixel = this._pixelFromPoint(this.zoomPoint, bounds);
|
||||
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel );
|
||||
deltaZoomPoints = deltaZoomPixels.divide( this.containerSize.x * zoom );
|
||||
deltaZoomPoints = deltaZoomPixels.divide( this._containerInnerSize.x * zoom );
|
||||
|
||||
return centerTarget.plus( deltaZoomPoints );
|
||||
},
|
||||
@ -335,13 +414,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* @return {OpenSeadragon.Rect} constrained bounds.
|
||||
*/
|
||||
_applyBoundaryConstraints: function( bounds, immediately ) {
|
||||
var horizontalThreshold,
|
||||
verticalThreshold,
|
||||
left,
|
||||
right,
|
||||
top,
|
||||
bottom,
|
||||
dx = 0,
|
||||
var dx = 0,
|
||||
dy = 0,
|
||||
newBounds = new $.Rect(
|
||||
bounds.x,
|
||||
@ -350,49 +423,52 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
bounds.height
|
||||
);
|
||||
|
||||
horizontalThreshold = this.visibilityRatio * newBounds.width;
|
||||
verticalThreshold = this.visibilityRatio * newBounds.height;
|
||||
|
||||
left = newBounds.x + newBounds.width;
|
||||
right = 1 - newBounds.x;
|
||||
top = newBounds.y + newBounds.height;
|
||||
bottom = this.contentAspectY - newBounds.y;
|
||||
var horizontalThreshold = this.visibilityRatio * newBounds.width;
|
||||
var verticalThreshold = this.visibilityRatio * newBounds.height;
|
||||
|
||||
if ( this.wrapHorizontal ) {
|
||||
//do nothing
|
||||
} else {
|
||||
if ( left < horizontalThreshold ) {
|
||||
dx = horizontalThreshold - left;
|
||||
var thresholdLeft = newBounds.x + (newBounds.width - horizontalThreshold);
|
||||
if (this.homeBounds.x > thresholdLeft) {
|
||||
dx = this.homeBounds.x - thresholdLeft;
|
||||
}
|
||||
|
||||
var homeRight = this.homeBounds.x + this.homeBounds.width;
|
||||
var thresholdRight = newBounds.x + horizontalThreshold;
|
||||
if (homeRight < thresholdRight) {
|
||||
var newDx = homeRight - thresholdRight;
|
||||
if (dx) {
|
||||
dx = (dx + newDx) / 2;
|
||||
} else {
|
||||
dx = newDx;
|
||||
}
|
||||
if ( right < horizontalThreshold ) {
|
||||
dx = dx ?
|
||||
( dx + right - horizontalThreshold ) / 2 :
|
||||
( right - horizontalThreshold );
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.wrapVertical ) {
|
||||
//do nothing
|
||||
} else {
|
||||
if ( top < verticalThreshold ) {
|
||||
dy = ( verticalThreshold - top );
|
||||
var thresholdTop = newBounds.y + (newBounds.height - verticalThreshold);
|
||||
if (this.homeBounds.y > thresholdTop) {
|
||||
dy = this.homeBounds.y - thresholdTop;
|
||||
}
|
||||
|
||||
var homeBottom = this.homeBounds.y + this.homeBounds.height;
|
||||
var thresholdBottom = newBounds.y + verticalThreshold;
|
||||
if (homeBottom < thresholdBottom) {
|
||||
var newDy = homeBottom - thresholdBottom;
|
||||
if (dy) {
|
||||
dy = (dy + newDy) / 2;
|
||||
} else {
|
||||
dy = newDy;
|
||||
}
|
||||
if ( bottom < verticalThreshold ) {
|
||||
dy = dy ?
|
||||
( dy + bottom - verticalThreshold ) / 2 :
|
||||
( bottom - verticalThreshold );
|
||||
}
|
||||
}
|
||||
|
||||
if ( dx || dy || immediately ) {
|
||||
if ( dx || dy ) {
|
||||
newBounds.x += dx;
|
||||
newBounds.y += dy;
|
||||
if( newBounds.width > 1 ){
|
||||
newBounds.x = 0.5 - newBounds.width/2;
|
||||
}
|
||||
if( newBounds.height > this.contentAspectY ){
|
||||
newBounds.y = this.contentAspectY/2 - newBounds.height/2;
|
||||
}
|
||||
}
|
||||
|
||||
if( this.viewer ){
|
||||
@ -512,21 +588,28 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
}
|
||||
|
||||
newBounds = this._applyBoundaryConstraints( newBounds, immediately );
|
||||
center = newBounds.getCenter();
|
||||
}
|
||||
|
||||
if ( newZoom == oldZoom || newBounds.width == oldBounds.width ) {
|
||||
return this.panTo( constraints ? newBounds.getCenter() : center, immediately );
|
||||
if (immediately) {
|
||||
this.panTo( center, true );
|
||||
return this.zoomTo(newZoom, null, true);
|
||||
}
|
||||
|
||||
if (Math.abs(newZoom - oldZoom) < 0.00000000001 ||
|
||||
Math.abs(newBounds.width - oldBounds.width) < 0.00000000001) {
|
||||
return this.panTo( center, immediately );
|
||||
}
|
||||
|
||||
referencePoint = oldBounds.getTopLeft().times(
|
||||
this.containerSize.x / oldBounds.width
|
||||
this._containerInnerSize.x / oldBounds.width
|
||||
).minus(
|
||||
newBounds.getTopLeft().times(
|
||||
this.containerSize.x / newBounds.width
|
||||
this._containerInnerSize.x / newBounds.width
|
||||
)
|
||||
).divide(
|
||||
this.containerSize.x / oldBounds.width -
|
||||
this.containerSize.x / newBounds.width
|
||||
this._containerInnerSize.x / oldBounds.width -
|
||||
this._containerInnerSize.x / newBounds.width
|
||||
);
|
||||
|
||||
return this.zoomTo( newZoom, referencePoint, immediately );
|
||||
@ -559,53 +642,27 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Zooms so the image just fills the viewer vertically.
|
||||
* @param {Boolean} immediately
|
||||
* @return {OpenSeadragon.Viewport} Chainable.
|
||||
*/
|
||||
fitVertically: function( immediately ) {
|
||||
var center = this.getCenter();
|
||||
var box = new $.Rect(this.homeBounds.x + (this.homeBounds.width / 2), this.homeBounds.y,
|
||||
0, this.homeBounds.height);
|
||||
|
||||
if ( this.wrapHorizontal ) {
|
||||
center.x = ( 1 + ( center.x % 1 ) ) % 1;
|
||||
this.centerSpringX.resetTo( center.x );
|
||||
this.centerSpringX.update();
|
||||
}
|
||||
|
||||
if ( this.wrapVertical ) {
|
||||
center.y = (
|
||||
this.contentAspectY + ( center.y % this.contentAspectY )
|
||||
) % this.contentAspectY;
|
||||
this.centerSpringY.resetTo( center.y );
|
||||
this.centerSpringY.update();
|
||||
}
|
||||
|
||||
return this.fitBounds( this.fitHeightBounds, immediately );
|
||||
return this.fitBounds( box, immediately );
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* Zooms so the image just fills the viewer horizontally.
|
||||
* @param {Boolean} immediately
|
||||
* @return {OpenSeadragon.Viewport} Chainable.
|
||||
*/
|
||||
fitHorizontally: function( immediately ) {
|
||||
var center = this.getCenter();
|
||||
var box = new $.Rect(this.homeBounds.x, this.homeBounds.y + (this.homeBounds.height / 2),
|
||||
this.homeBounds.width, 0);
|
||||
|
||||
if ( this.wrapHorizontal ) {
|
||||
center.x = (
|
||||
this.contentAspectX + ( center.x % this.contentAspectX )
|
||||
) % this.contentAspectX;
|
||||
this.centerSpringX.resetTo( center.x );
|
||||
this.centerSpringX.update();
|
||||
}
|
||||
|
||||
if ( this.wrapVertical ) {
|
||||
center.y = ( 1 + ( center.y % 1 ) ) % 1;
|
||||
this.centerSpringY.resetTo( center.y );
|
||||
this.centerSpringY.update();
|
||||
}
|
||||
|
||||
return this.fitBounds( this.fitWidthBounds, immediately );
|
||||
return this.fitBounds( box, immediately );
|
||||
},
|
||||
|
||||
|
||||
@ -736,7 +793,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
/**
|
||||
* Raised when rotation has been changed.
|
||||
*
|
||||
* @event update-viewport
|
||||
* @event rotate
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||
@ -769,12 +826,16 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
newBounds = oldBounds,
|
||||
widthDeltaFactor;
|
||||
|
||||
this.containerSize = new $.Point(
|
||||
newContainerSize.x,
|
||||
newContainerSize.y
|
||||
this.containerSize.x = newContainerSize.x;
|
||||
this.containerSize.y = newContainerSize.y;
|
||||
|
||||
this._containerInnerSize = new $.Point(
|
||||
Math.max(1, newContainerSize.x - (this._margins.left + this._margins.right)),
|
||||
Math.max(1, newContainerSize.y - (this._margins.top + this._margins.bottom))
|
||||
);
|
||||
|
||||
if ( maintain ) {
|
||||
// TODO: widthDeltaFactor will always be 1; probably not what's intended
|
||||
widthDeltaFactor = newContainerSize.x / this.containerSize.x;
|
||||
newBounds.width = oldBounds.width * widthDeltaFactor;
|
||||
newBounds.height = newBounds.width / this.getAspectRatio();
|
||||
@ -805,10 +866,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* @function
|
||||
*/
|
||||
update: function() {
|
||||
var oldCenterX = this.centerSpringX.current.value,
|
||||
oldCenterY = this.centerSpringY.current.value,
|
||||
oldZoom = this.zoomSpring.current.value,
|
||||
oldZoomPixel,
|
||||
var oldZoomPixel,
|
||||
newZoomPixel,
|
||||
deltaZoomPixels,
|
||||
deltaZoomPoints;
|
||||
@ -819,7 +877,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
|
||||
this.zoomSpring.update();
|
||||
|
||||
if (this.zoomPoint && this.zoomSpring.current.value != oldZoom) {
|
||||
if (this.zoomPoint && this.zoomSpring.current.value != this._oldZoom) {
|
||||
newZoomPixel = this.pixelFromPoint( this.zoomPoint, true );
|
||||
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel );
|
||||
deltaZoomPoints = this.deltaPointsFromPixels( deltaZoomPixels, true );
|
||||
@ -833,9 +891,15 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
this.centerSpringX.update();
|
||||
this.centerSpringY.update();
|
||||
|
||||
return this.centerSpringX.current.value != oldCenterX ||
|
||||
this.centerSpringY.current.value != oldCenterY ||
|
||||
this.zoomSpring.current.value != oldZoom;
|
||||
var changed = this.centerSpringX.current.value != this._oldCenterX ||
|
||||
this.centerSpringY.current.value != this._oldCenterY ||
|
||||
this.zoomSpring.current.value != this._oldZoom;
|
||||
|
||||
this._oldCenterX = this.centerSpringX.current.value;
|
||||
this._oldCenterY = this.centerSpringY.current.value;
|
||||
this._oldZoom = this.zoomSpring.current.value;
|
||||
|
||||
return changed;
|
||||
},
|
||||
|
||||
|
||||
@ -846,7 +910,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
*/
|
||||
deltaPixelsFromPoints: function( deltaPoints, current ) {
|
||||
return deltaPoints.times(
|
||||
this.containerSize.x * this.getZoom( current )
|
||||
this._containerInnerSize.x * this.getZoom( current )
|
||||
);
|
||||
},
|
||||
|
||||
@ -857,7 +921,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
*/
|
||||
deltaPointsFromPixels: function( deltaPixels, current ) {
|
||||
return deltaPixels.divide(
|
||||
this.containerSize.x * this.getZoom( current )
|
||||
this._containerInnerSize.x * this.getZoom( current )
|
||||
);
|
||||
},
|
||||
|
||||
@ -867,11 +931,17 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
|
||||
*/
|
||||
pixelFromPoint: function( point, current ) {
|
||||
var bounds = this.getBounds( current );
|
||||
return this._pixelFromPoint(point, this.getBounds( current ));
|
||||
},
|
||||
|
||||
// private
|
||||
_pixelFromPoint: function( point, bounds ) {
|
||||
return point.minus(
|
||||
bounds.getTopLeft()
|
||||
).times(
|
||||
this.containerSize.x / bounds.width
|
||||
this._containerInnerSize.x / bounds.width
|
||||
).plus(
|
||||
new $.Point(this._margins.left, this._margins.top)
|
||||
);
|
||||
},
|
||||
|
||||
@ -882,17 +952,27 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
*/
|
||||
pointFromPixel: function( pixel, current ) {
|
||||
var bounds = this.getBounds( current );
|
||||
return pixel.divide(
|
||||
this.containerSize.x / bounds.width
|
||||
return pixel.minus(
|
||||
new $.Point(this._margins.left, this._margins.top)
|
||||
).divide(
|
||||
this._containerInnerSize.x / bounds.width
|
||||
).plus(
|
||||
bounds.getTopLeft()
|
||||
);
|
||||
},
|
||||
|
||||
// private
|
||||
_viewportToImageDelta: function( viewerX, viewerY ) {
|
||||
var scale = this.homeBounds.width;
|
||||
return new $.Point(viewerX * (this.contentSize.x / scale),
|
||||
viewerY * ((this.contentSize.y * this.contentAspectX) / scale));
|
||||
},
|
||||
|
||||
/**
|
||||
* Translates from OpenSeadragon viewer coordinate system to image coordinate system.
|
||||
* This method can be called either by passing X,Y coordinates or an
|
||||
* OpenSeadragon.Point
|
||||
* Note: not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point} viewerX the point in viewport coordinate system.
|
||||
* @param {Number} viewerX X coordinate in viewport coordinate system.
|
||||
@ -904,13 +984,26 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
//they passed a point instead of individual components
|
||||
return this.viewportToImageCoordinates( viewerX.x, viewerX.y );
|
||||
}
|
||||
return new $.Point( viewerX * this.contentSize.x, viewerY * this.contentSize.y * this.contentAspectX );
|
||||
|
||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
||||
$.console.error('[Viewport.viewportToImageCoordinates] is not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.');
|
||||
}
|
||||
|
||||
return this._viewportToImageDelta(viewerX - this.homeBounds.x, viewerY - this.homeBounds.y);
|
||||
},
|
||||
|
||||
// private
|
||||
_imageToViewportDelta: function( imageX, imageY ) {
|
||||
var scale = this.homeBounds.width;
|
||||
return new $.Point((imageX / this.contentSize.x) * scale,
|
||||
(imageY / this.contentSize.y / this.contentAspectX) * scale);
|
||||
},
|
||||
|
||||
/**
|
||||
* Translates from image coordinate system to OpenSeadragon viewer coordinate system
|
||||
* This method can be called either by passing X,Y coordinates or an
|
||||
* OpenSeadragon.Point
|
||||
* Note: not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Point} imageX the point in image coordinate system.
|
||||
* @param {Number} imageX X coordinate in image coordinate system.
|
||||
@ -922,7 +1015,15 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
//they passed a point instead of individual components
|
||||
return this.imageToViewportCoordinates( imageX.x, imageX.y );
|
||||
}
|
||||
return new $.Point( imageX / this.contentSize.x, imageY / this.contentSize.y / this.contentAspectX );
|
||||
|
||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
||||
$.console.error('[Viewport.imageToViewportCoordinates] is not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.');
|
||||
}
|
||||
|
||||
var point = this._imageToViewportDelta(imageX, imageY);
|
||||
point.x += this.homeBounds.x;
|
||||
point.y += this.homeBounds.y;
|
||||
return point;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -930,6 +1031,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* pixel coordinates to OpenSeadragon viewport rectangle coordinates.
|
||||
* This method can be called either by passing X,Y,width,height or an
|
||||
* OpenSeadragon.Rect
|
||||
* Note: not accurate with multi-image; use TiledImage.imageToViewportRectangle instead.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Rect} imageX the rectangle in image coordinate system.
|
||||
* @param {Number} imageX the X coordinate of the top left corner of the rectangle
|
||||
@ -950,10 +1052,11 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
rect.x, rect.y, rect.width, rect.height
|
||||
);
|
||||
}
|
||||
|
||||
coordA = this.imageToViewportCoordinates(
|
||||
imageX, imageY
|
||||
);
|
||||
coordB = this.imageToViewportCoordinates(
|
||||
coordB = this._imageToViewportDelta(
|
||||
pixelWidth, pixelHeight
|
||||
);
|
||||
return new $.Rect(
|
||||
@ -969,6 +1072,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* the viewport in point coordinates to image rectangle coordinates.
|
||||
* This method can be called either by passing X,Y,width,height or an
|
||||
* OpenSeadragon.Rect
|
||||
* Note: not accurate with multi-image; use TiledImage.viewportToImageRectangle instead.
|
||||
* @function
|
||||
* @param {OpenSeadragon.Rect} viewerX the rectangle in viewport coordinate system.
|
||||
* @param {Number} viewerX the X coordinate of the top left corner of the rectangle
|
||||
@ -989,8 +1093,9 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
rect.x, rect.y, rect.width, rect.height
|
||||
);
|
||||
}
|
||||
|
||||
coordA = this.viewportToImageCoordinates( viewerX, viewerY );
|
||||
coordB = this.viewportToImageCoordinates( pointWidth, pointHeight );
|
||||
coordB = this._viewportToImageDelta(pointWidth, pointHeight);
|
||||
return new $.Rect(
|
||||
coordA.x,
|
||||
coordA.y,
|
||||
@ -1002,6 +1107,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
/**
|
||||
* Convert pixel coordinates relative to the viewer element to image
|
||||
* coordinates.
|
||||
* Note: not accurate with multi-image.
|
||||
* @param {OpenSeadragon.Point} pixel
|
||||
* @returns {OpenSeadragon.Point}
|
||||
*/
|
||||
@ -1013,6 +1119,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
/**
|
||||
* Convert pixel coordinates relative to the image to
|
||||
* viewer element coordinates.
|
||||
* Note: not accurate with multi-image.
|
||||
* @param {OpenSeadragon.Point} pixel
|
||||
* @returns {OpenSeadragon.Point}
|
||||
*/
|
||||
@ -1023,6 +1130,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
|
||||
/**
|
||||
* Convert pixel coordinates relative to the window to image coordinates.
|
||||
* Note: not accurate with multi-image.
|
||||
* @param {OpenSeadragon.Point} pixel
|
||||
* @returns {OpenSeadragon.Point}
|
||||
*/
|
||||
@ -1034,6 +1142,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
|
||||
/**
|
||||
* Convert image coordinates to pixel coordinates relative to the window.
|
||||
* Note: not accurate with multi-image.
|
||||
* @param {OpenSeadragon.Point} pixel
|
||||
* @returns {OpenSeadragon.Point}
|
||||
*/
|
||||
@ -1091,15 +1200,21 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* 1 means original image size, 0.5 half size...
|
||||
* Viewport zoom: ratio of the displayed image's width to viewport's width.
|
||||
* 1 means identical width, 2 means image's width is twice the viewport's width...
|
||||
* Note: not accurate with multi-image.
|
||||
* @function
|
||||
* @param {Number} viewportZoom The viewport zoom
|
||||
* target zoom.
|
||||
* @returns {Number} imageZoom The image zoom
|
||||
*/
|
||||
viewportToImageZoom: function( viewportZoom ) {
|
||||
var imageWidth = this.viewer.source.dimensions.x;
|
||||
var containerWidth = this.getContainerSize().x;
|
||||
var viewportToImageZoomRatio = containerWidth / imageWidth;
|
||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
||||
$.console.error('[Viewport.viewportToImageZoom] is not accurate with multi-image.');
|
||||
}
|
||||
|
||||
var imageWidth = this.contentSize.x;
|
||||
var containerWidth = this._containerInnerSize.x;
|
||||
var scale = this.homeBounds.width;
|
||||
var viewportToImageZoomRatio = (containerWidth / imageWidth) * scale;
|
||||
return viewportZoom * viewportToImageZoomRatio;
|
||||
},
|
||||
|
||||
@ -1109,15 +1224,21 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
|
||||
* 1 means original image size, 0.5 half size...
|
||||
* Viewport zoom: ratio of the displayed image's width to viewport's width.
|
||||
* 1 means identical width, 2 means image's width is twice the viewport's width...
|
||||
* Note: not accurate with multi-image.
|
||||
* @function
|
||||
* @param {Number} imageZoom The image zoom
|
||||
* target zoom.
|
||||
* @returns {Number} viewportZoom The viewport zoom
|
||||
*/
|
||||
imageToViewportZoom: function( imageZoom ) {
|
||||
var imageWidth = this.viewer.source.dimensions.x;
|
||||
var containerWidth = this.getContainerSize().x;
|
||||
var viewportToImageZoomRatio = imageWidth / containerWidth;
|
||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
||||
$.console.error('[Viewport.imageToViewportZoom] is not accurate with multi-image.');
|
||||
}
|
||||
|
||||
var imageWidth = this.contentSize.x;
|
||||
var containerWidth = this._containerInnerSize.x;
|
||||
var scale = this.homeBounds.width;
|
||||
var viewportToImageZoomRatio = (imageWidth / containerWidth) / scale;
|
||||
return imageZoom * viewportToImageZoomRatio;
|
||||
}
|
||||
};
|
||||
|
393
src/world.js
Normal file
393
src/world.js
Normal file
@ -0,0 +1,393 @@
|
||||
/*
|
||||
* OpenSeadragon - World
|
||||
*
|
||||
* Copyright (C) 2009 CodePlex Foundation
|
||||
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of CodePlex Foundation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
/**
|
||||
* @class World
|
||||
* @memberof OpenSeadragon
|
||||
* @extends OpenSeadragon.EventSource
|
||||
* @classdesc Keeps track of all of the tiled images in the scene.
|
||||
* @param {Object} options - World options.
|
||||
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this World.
|
||||
**/
|
||||
$.World = function( options ) {
|
||||
var _this = this;
|
||||
|
||||
$.console.assert( options.viewer, "[World] options.viewer is required" );
|
||||
|
||||
$.EventSource.call( this );
|
||||
|
||||
this.viewer = options.viewer;
|
||||
this._items = [];
|
||||
this._needsDraw = false;
|
||||
this._delegatedFigureSizes = function(event) {
|
||||
_this._figureSizes();
|
||||
};
|
||||
|
||||
this._figureSizes();
|
||||
};
|
||||
|
||||
$.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.World.prototype */{
|
||||
/**
|
||||
* Add the specified item.
|
||||
* @param {OpenSeadragon.TiledImage} item - The item to add.
|
||||
* @param {Number} [options.index] - Index for the item. If not specified, goes at the top.
|
||||
* @fires OpenSeadragon.World.event:add-item
|
||||
* @fires OpenSeadragon.World.event:metrics-change
|
||||
*/
|
||||
addItem: function( item, options ) {
|
||||
$.console.assert(item, "[World.addItem] item is required");
|
||||
$.console.assert(item instanceof $.TiledImage, "[World.addItem] only TiledImages supported at this time");
|
||||
|
||||
options = options || {};
|
||||
if (options.index !== undefined) {
|
||||
var index = Math.max(0, Math.min(this._items.length, options.index));
|
||||
this._items.splice(index, 0, item);
|
||||
} else {
|
||||
this._items.push( item );
|
||||
}
|
||||
|
||||
this._figureSizes();
|
||||
this._needsDraw = true;
|
||||
|
||||
item.addHandler('bounds-change', this._delegatedFigureSizes);
|
||||
|
||||
/**
|
||||
* Raised when an item is added to the World.
|
||||
* @event add-item
|
||||
* @memberOf OpenSeadragon.World
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the World which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} item - The item that has been added.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent( 'add-item', {
|
||||
item: item
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the item at the specified index.
|
||||
* @param {Number} index - The item's index.
|
||||
* @returns {OpenSeadragon.TiledImage} The item at the specified index.
|
||||
*/
|
||||
getItemAt: function( index ) {
|
||||
$.console.assert(index !== undefined, "[World.getItemAt] index is required");
|
||||
return this._items[ index ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the index of the given item or -1 if not present.
|
||||
* @param {OpenSeadragon.TiledImage} item - The item.
|
||||
* @returns {Number} The index of the item or -1 if not present.
|
||||
*/
|
||||
getIndexOfItem: function( item ) {
|
||||
$.console.assert(item, "[World.getIndexOfItem] item is required");
|
||||
return $.indexOf( this._items, item );
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Number} The number of items used.
|
||||
*/
|
||||
getItemCount: function() {
|
||||
return this._items.length;
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the index of a item so that it appears over or under others.
|
||||
* @param {OpenSeadragon.TiledImage} item - The item to move.
|
||||
* @param {Number} index - The new index.
|
||||
* @fires OpenSeadragon.World.event:item-index-change
|
||||
*/
|
||||
setItemIndex: function( item, index ) {
|
||||
$.console.assert(item, "[World.setItemIndex] item is required");
|
||||
$.console.assert(index !== undefined, "[World.setItemIndex] index is required");
|
||||
|
||||
var oldIndex = this.getIndexOfItem( item );
|
||||
|
||||
if ( index >= this._items.length ) {
|
||||
throw new Error( "Index bigger than number of layers." );
|
||||
}
|
||||
|
||||
if ( index === oldIndex || oldIndex === -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._items.splice( oldIndex, 1 );
|
||||
this._items.splice( index, 0, item );
|
||||
this._needsDraw = true;
|
||||
|
||||
/**
|
||||
* Raised when the order of the indexes has been changed.
|
||||
* @event item-index-change
|
||||
* @memberOf OpenSeadragon.World
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} item - The item whose index has
|
||||
* been changed
|
||||
* @property {Number} previousIndex - The previous index of the item
|
||||
* @property {Number} newIndex - The new index of the item
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent( 'item-index-change', {
|
||||
item: item,
|
||||
previousIndex: oldIndex,
|
||||
newIndex: index
|
||||
} );
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove an item.
|
||||
* @param {OpenSeadragon.TiledImage} item - The item to remove.
|
||||
* @fires OpenSeadragon.World.event:remove-item
|
||||
* @fires OpenSeadragon.World.event:metrics-change
|
||||
*/
|
||||
removeItem: function( item ) {
|
||||
$.console.assert(item, "[World.removeItem] item is required");
|
||||
|
||||
var index = $.indexOf(this._items, item );
|
||||
if ( index === -1 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
item.removeHandler('bounds-change', this._delegatedFigureSizes);
|
||||
this._items.splice( index, 1 );
|
||||
this._figureSizes();
|
||||
this._needsDraw = true;
|
||||
this._raiseRemoveItem(item);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all items.
|
||||
* @fires OpenSeadragon.World.event:remove-item
|
||||
* @fires OpenSeadragon.World.event:metrics-change
|
||||
*/
|
||||
removeAll: function() {
|
||||
var item;
|
||||
for (var i = 0; i < this._items.length; i++) {
|
||||
item = this._items[i];
|
||||
item.removeHandler('bounds-change', this._delegatedFigureSizes);
|
||||
}
|
||||
|
||||
var removedItems = this._items;
|
||||
this._items = [];
|
||||
this._figureSizes();
|
||||
this._needsDraw = true;
|
||||
|
||||
for (i = 0; i < removedItems.length; i++) {
|
||||
item = removedItems[i];
|
||||
this._raiseRemoveItem(item);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all tiles and triggers updates for all items.
|
||||
*/
|
||||
resetItems: function() {
|
||||
for ( var i = 0; i < this._items.length; i++ ) {
|
||||
this._items[i].reset();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates (i.e. animates bounds of) all items.
|
||||
*/
|
||||
update: function() {
|
||||
var animated = false;
|
||||
for ( var i = 0; i < this._items.length; i++ ) {
|
||||
animated = this._items[i].update() || animated;
|
||||
}
|
||||
|
||||
return animated;
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws all items.
|
||||
*/
|
||||
draw: function() {
|
||||
for ( var i = 0; i < this._items.length; i++ ) {
|
||||
this._items[i].draw();
|
||||
}
|
||||
|
||||
this._needsDraw = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Boolean} true if any items need updating.
|
||||
*/
|
||||
needsDraw: function() {
|
||||
for ( var i = 0; i < this._items.length; i++ ) {
|
||||
if ( this._items[i].needsDraw() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this._needsDraw;
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {OpenSeadragon.Rect} The smallest rectangle that encloses all items, in viewport coordinates.
|
||||
*/
|
||||
getHomeBounds: function() {
|
||||
return this._homeBounds.clone();
|
||||
},
|
||||
|
||||
/**
|
||||
* To facilitate zoom constraints, we keep track of the pixel density of the
|
||||
* densest item in the World (i.e. the item whose content size to viewport size
|
||||
* ratio is the highest) and save it as this "content factor".
|
||||
* @returns {Number} the number of content units per viewport unit.
|
||||
*/
|
||||
getContentFactor: function() {
|
||||
return this._contentFactor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Arranges all of the TiledImages with the specified settings.
|
||||
* @param {Object} options - Specifies how to arrange.
|
||||
* @param {Boolean} [options.immediately=false] - Whether to animate to the new arrangement.
|
||||
* @param {String} [options.layout] - See collectionLayout in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.rows] - See collectionRows in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.tileSize] - See collectionTileSize in {@link OpenSeadragon.Options}.
|
||||
* @param {Number} [options.tileMargin] - See collectionTileMargin in {@link OpenSeadragon.Options}.
|
||||
* @fires OpenSeadragon.World.event:metrics-change
|
||||
*/
|
||||
arrange: function(options) {
|
||||
options = options || {};
|
||||
var immediately = options.immediately || false;
|
||||
var layout = options.layout || $.DEFAULT_SETTINGS.collectionLayout;
|
||||
var rows = options.rows || $.DEFAULT_SETTINGS.collectionRows;
|
||||
var tileSize = options.tileSize || $.DEFAULT_SETTINGS.collectionTileSize;
|
||||
var tileMargin = options.tileMargin || $.DEFAULT_SETTINGS.collectionTileMargin;
|
||||
var increment = tileSize + tileMargin;
|
||||
var wrap = Math.ceil(this._items.length / rows);
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var item, box, width, height, position;
|
||||
for (var i = 0; i < this._items.length; i++) {
|
||||
if (i && (i % wrap) === 0) {
|
||||
if (layout === 'horizontal') {
|
||||
y += increment;
|
||||
x = 0;
|
||||
} else {
|
||||
x += increment;
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
item = this._items[i];
|
||||
box = item.getBounds();
|
||||
if (box.width > box.height) {
|
||||
width = tileSize;
|
||||
} else {
|
||||
width = tileSize * (box.width / box.height);
|
||||
}
|
||||
|
||||
height = width * (box.height / box.width);
|
||||
position = new $.Point(x + ((tileSize - width) / 2),
|
||||
y + ((tileSize - height) / 2));
|
||||
|
||||
item.setPosition(position, immediately);
|
||||
item.setWidth(width, immediately);
|
||||
|
||||
if (layout === 'horizontal') {
|
||||
x += increment;
|
||||
} else {
|
||||
y += increment;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
_figureSizes: function() {
|
||||
var oldHomeBounds = this._homeBounds ? this._homeBounds.clone() : null;
|
||||
var oldContentSize = this._contentSize ? this._contentSize.clone() : null;
|
||||
var oldContentFactor = this._contentFactor || 0;
|
||||
|
||||
if ( !this._items.length ) {
|
||||
this._homeBounds = new $.Rect(0, 0, 1, 1);
|
||||
this._contentSize = new $.Point(1, 1);
|
||||
this._contentFactor = 1;
|
||||
} else {
|
||||
var bounds = this._items[0].getBounds();
|
||||
this._contentFactor = this._items[0].getContentSize().x / bounds.width;
|
||||
var left = bounds.x;
|
||||
var top = bounds.y;
|
||||
var right = bounds.x + bounds.width;
|
||||
var bottom = bounds.y + bounds.height;
|
||||
var box;
|
||||
for ( var i = 1; i < this._items.length; i++ ) {
|
||||
box = this._items[i].getBounds();
|
||||
this._contentFactor = Math.max(this._contentFactor, this._items[i].getContentSize().x / box.width);
|
||||
left = Math.min( left, box.x );
|
||||
top = Math.min( top, box.y );
|
||||
right = Math.max( right, box.x + box.width );
|
||||
bottom = Math.max( bottom, box.y + box.height );
|
||||
}
|
||||
|
||||
this._homeBounds = new $.Rect( left, top, right - left, bottom - top );
|
||||
this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor,
|
||||
this._homeBounds.height * this._contentFactor);
|
||||
}
|
||||
|
||||
if (this._contentFactor !== oldContentFactor || !this._homeBounds.equals(oldHomeBounds) ||
|
||||
!this._contentSize.equals(oldContentSize)) {
|
||||
/**
|
||||
* Raised when the home bounds or content factor change.
|
||||
* @event metrics-change
|
||||
* @memberOf OpenSeadragon.World
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent('metrics-change', {});
|
||||
}
|
||||
},
|
||||
|
||||
// private
|
||||
_raiseRemoveItem: function(item) {
|
||||
/**
|
||||
* Raised when an item is removed.
|
||||
* @event remove-item
|
||||
* @memberOf OpenSeadragon.World
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
|
||||
* @property {OpenSeadragon.TiledImage} item - The item's underlying item.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent( 'remove-item', { item: item } );
|
||||
}
|
||||
});
|
||||
|
||||
}( OpenSeadragon ));
|
79
test/coverage.html
Normal file
79
test/coverage.html
Normal file
@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>OpenSeadragon QUnit</title>
|
||||
<link rel="stylesheet" href="/node_modules/qunitjs/qunit/qunit.css">
|
||||
<link rel="stylesheet" href="/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
|
||||
<link rel="stylesheet" href="/test/helpers/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
<script src="/node_modules/qunitjs/qunit/qunit.js"></script>
|
||||
<script src="/test/lib/jquery-1.9.1.min.js"></script>
|
||||
<script src="/test/lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js"></script>
|
||||
<script src="/test/lib/jquery.simulate.js"></script>
|
||||
|
||||
<!-- OpenSeadragon sources -->
|
||||
<script src="/src/openseadragon.js"></script>
|
||||
<script src="/src/fullscreen.js"></script>
|
||||
<script src="/src/eventsource.js"></script>
|
||||
<script src="/src/mousetracker.js"></script>
|
||||
<script src="/src/control.js"></script>
|
||||
<script src="/src/controldock.js"></script>
|
||||
<script src="/src/viewer.js"></script>
|
||||
<script src="/src/navigator.js"></script>
|
||||
<script src="/src/strings.js"></script>
|
||||
<script src="/src/point.js"></script>
|
||||
<script src="/src/tilesource.js"></script>
|
||||
<script src="/src/dzitilesource.js"></script>
|
||||
<script src="/src/iiiftilesource.js"></script>
|
||||
<script src="/src/osmtilesource.js"></script>
|
||||
<script src="/src/tmstilesource.js"></script>
|
||||
<script src="/src/legacytilesource.js"></script>
|
||||
<script src="/src/tilesourcecollection.js"></script>
|
||||
<script src="/src/button.js"></script>
|
||||
<script src="/src/buttongroup.js"></script>
|
||||
<script src="/src/rectangle.js"></script>
|
||||
<script src="/src/referencestrip.js"></script>
|
||||
<script src="/src/displayrectangle.js"></script>
|
||||
<script src="/src/spring.js"></script>
|
||||
<script src="/src/imageLoader.js"></script>
|
||||
<script src="/src/tile.js"></script>
|
||||
<script src="/src/overlay.js"></script>
|
||||
<script src="/src/drawer.js"></script>
|
||||
<script src="/src/viewport.js"></script>
|
||||
<script src="/src/tiledimage.js"></script>
|
||||
<script src="/src/tilecache.js"></script>
|
||||
<script src="/src/world.js"></script>
|
||||
|
||||
<!-- Helpers -->
|
||||
<script src="/test/helpers/legacy.mouse.shim.js"></script>
|
||||
<script src="/test/helpers/test.js"></script>
|
||||
|
||||
<!-- Modules -->
|
||||
<!-- 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/basic.js"></script>
|
||||
<script src="/test/modules/strings.js"></script>
|
||||
<script src="/test/modules/formats.js"></script>
|
||||
<script src="/test/modules/utils.js"></script>
|
||||
<script src="/test/modules/events.js"></script>
|
||||
<script src="/test/modules/units.js"></script>
|
||||
<script src="/test/modules/multi-image.js"></script>
|
||||
<script src="/test/modules/overlays.js"></script>
|
||||
<script src="/test/modules/controls.js"></script>
|
||||
<script src="/test/modules/viewport.js"></script>
|
||||
<script src="/test/modules/world.js"></script>
|
||||
<script src="/test/modules/drawer.js"></script>
|
||||
<script src="/test/modules/tiledimage.js"></script>
|
||||
<script src="/test/modules/tilecache.js"></script>
|
||||
<script src="/test/modules/referencestrip.js"></script>
|
||||
<script src="/test/modules/tilesourcecollection.js"></script>
|
||||
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
|
||||
so we put them last. -->
|
||||
<script src="/test/modules/navigator.js"></script>
|
||||
</body>
|
||||
</html>
|
31
test/demo/collections/index.html
Normal file
31
test/demo/collections/index.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenSeadragon Collections Demo</title>
|
||||
<script type="text/javascript" src='../../../build/openseadragon/openseadragon.js'></script>
|
||||
<script type="text/javascript" src='../../lib/jquery-1.9.1.min.js'></script>
|
||||
<script type="text/javascript" src='main.js'></script>
|
||||
<style type="text/css">
|
||||
|
||||
html,
|
||||
body,
|
||||
.openseadragon1 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.openseadragon1.small {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.openseadragon-overlay {
|
||||
background-color: rgba(255, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="contentDiv" class="openseadragon1"></div>
|
||||
</body>
|
||||
</html>
|
381
test/demo/collections/main.js
Normal file
381
test/demo/collections/main.js
Normal file
@ -0,0 +1,381 @@
|
||||
/* globals $, App */
|
||||
|
||||
(function() {
|
||||
|
||||
window.App = {
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
var testInitialOpen = true;
|
||||
var testOverlays = false;
|
||||
var testMargins = false;
|
||||
var testNavigator = false;
|
||||
var margins;
|
||||
|
||||
var config = {
|
||||
// debugMode: true,
|
||||
zoomPerScroll: 1.02,
|
||||
showNavigator: testNavigator,
|
||||
useCanvas: true,
|
||||
// defaultZoomLevel: 2,
|
||||
// homeFillsViewer: true,
|
||||
// sequenceMode: true,
|
||||
// showReferenceStrip: true,
|
||||
// referenceStripScroll: 'vertical',
|
||||
navPrevNextWrap: false,
|
||||
preserveViewport: false,
|
||||
// collectionMode: true,
|
||||
// collectionRows: 1,
|
||||
// collectionLayout: 'vertical',
|
||||
// collectionTileSize: 10,
|
||||
// collectionTileMargin: 10,
|
||||
// wrapHorizontal: true,
|
||||
// wrapVertical: true,
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../../build/openseadragon/images/"
|
||||
};
|
||||
|
||||
var highsmith = {
|
||||
Image: {
|
||||
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/",
|
||||
Format: "jpg",
|
||||
Overlap: "2",
|
||||
TileSize: "256",
|
||||
Size: {
|
||||
Height: "9221",
|
||||
Width: "7026"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (testInitialOpen) {
|
||||
config.tileSources = [
|
||||
{
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
x: 4,
|
||||
y: 2,
|
||||
width: 2
|
||||
},
|
||||
{
|
||||
tileSource: "../../data/tall.dzi",
|
||||
x: 1.5,
|
||||
y: 0,
|
||||
width: 1
|
||||
},
|
||||
{
|
||||
tileSource: '../../data/wide.dzi',
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
height: 1
|
||||
}
|
||||
];
|
||||
|
||||
// config.tileSources = {
|
||||
// tileSource: highsmith,
|
||||
// width: 1
|
||||
// };
|
||||
}
|
||||
|
||||
if (testOverlays) {
|
||||
config.overlays = [
|
||||
{
|
||||
id: "overlay1",
|
||||
x: 2,
|
||||
y: 0,
|
||||
width: 0.25,
|
||||
height: 0.25
|
||||
},
|
||||
{
|
||||
px: 13,
|
||||
py: 120,
|
||||
width: 124,
|
||||
height: 132,
|
||||
id: "overlay"
|
||||
},
|
||||
{
|
||||
px: 400,
|
||||
py: 500,
|
||||
width: 400,
|
||||
height: 400,
|
||||
id: "fixed-overlay",
|
||||
placement: "TOP_LEFT"
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (testMargins) {
|
||||
margins = {
|
||||
top: 250,
|
||||
left: 250,
|
||||
right: 250,
|
||||
bottom: 250
|
||||
};
|
||||
|
||||
config.viewportMargins = margins;
|
||||
}
|
||||
|
||||
this.viewer = OpenSeadragon(config);
|
||||
|
||||
if (testInitialOpen) {
|
||||
function openHandler() {
|
||||
self.viewer.removeHandler('open', openHandler);
|
||||
}
|
||||
|
||||
this.viewer.addHandler( "open", openHandler);
|
||||
}
|
||||
|
||||
if (testMargins) {
|
||||
this.viewer.addHandler('animation', function() {
|
||||
var box = new OpenSeadragon.Rect(margins.left, margins.top,
|
||||
$('#contentDiv').width() - (margins.left + margins.right),
|
||||
$('#contentDiv').height() - (margins.top + margins.bottom));
|
||||
|
||||
self.viewer.drawer.debugRect(box);
|
||||
});
|
||||
}
|
||||
|
||||
if (!testInitialOpen) {
|
||||
this.basicTest();
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
shrink: function(index) {
|
||||
index = index || 0;
|
||||
var image = this.viewer.world.getItemAt(index);
|
||||
image.setWidth(image.getBounds().width * 0.3);
|
||||
},
|
||||
|
||||
// ----------
|
||||
move: function(index) {
|
||||
index = index || 0;
|
||||
var image = this.viewer.world.getItemAt(index);
|
||||
var point = image.getBounds().getTopLeft();
|
||||
point.x += image.getBounds().width * 0.3;
|
||||
image.setPosition(point);
|
||||
},
|
||||
|
||||
// ----------
|
||||
add: function() {
|
||||
var self = this;
|
||||
|
||||
this.viewer.addTiledImage({
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
width: 1,
|
||||
success: function() {
|
||||
self.viewer.viewport.goHome();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
toggle: function() {
|
||||
var $el = $(this.viewer.element);
|
||||
$el.toggleClass('small');
|
||||
},
|
||||
|
||||
// ----------
|
||||
basicTest: function() {
|
||||
var self = this;
|
||||
|
||||
this.viewer.addHandler('open', function() {
|
||||
});
|
||||
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
width: 1
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
crossTest: function() {
|
||||
var self = this;
|
||||
|
||||
this.viewer.addHandler( "open", function() {
|
||||
var options = {
|
||||
tileSource: '../../data/wide.dzi',
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
height: 1
|
||||
};
|
||||
|
||||
var addItemHandler = function( event ) {
|
||||
if ( event.options === options ) {
|
||||
self.viewer.world.removeHandler( "add-item", addItemHandler );
|
||||
self.viewer.viewport.goHome();
|
||||
}
|
||||
};
|
||||
self.viewer.world.addHandler( "add-item", addItemHandler );
|
||||
self.viewer.addTiledImage( options );
|
||||
});
|
||||
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/tall.dzi",
|
||||
x: 1.5,
|
||||
y: 0,
|
||||
width: 1
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
crossTest2: function() {
|
||||
this.viewer.open([
|
||||
{
|
||||
tileSource: "../../data/tall.dzi",
|
||||
x: 1.5,
|
||||
y: 0,
|
||||
width: 1
|
||||
},
|
||||
{
|
||||
tileSource: '../../data/wide.dzi',
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
height: 1
|
||||
}
|
||||
]);
|
||||
},
|
||||
|
||||
// ----------
|
||||
crossTest3: function() {
|
||||
var self = this;
|
||||
var expected = 2;
|
||||
var loaded = 0;
|
||||
|
||||
this.viewer.world.addHandler('add-item', function() {
|
||||
loaded++;
|
||||
if (loaded === expected) {
|
||||
// self.viewer.viewport.goHome();
|
||||
}
|
||||
});
|
||||
|
||||
this.viewer.addTiledImage({
|
||||
tileSource: "../../data/tall.dzi",
|
||||
x: 1.5,
|
||||
y: 0,
|
||||
width: 1
|
||||
});
|
||||
|
||||
this.viewer.addTiledImage({
|
||||
tileSource: '../../data/wide.dzi',
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
y: 1.5,
|
||||
height: 1
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
collectionTest: function() {
|
||||
var tileSources = [];
|
||||
var random;
|
||||
for (var i = 0; i < 10; i++) {
|
||||
random = Math.random();
|
||||
if (random < 0.33) {
|
||||
tileSources.push('../../data/testpattern.dzi');
|
||||
} else if (random < 0.66) {
|
||||
tileSources.push('../../data/tall.dzi');
|
||||
} else {
|
||||
tileSources.push('../../data/wide.dzi');
|
||||
}
|
||||
}
|
||||
|
||||
this.viewer.open(tileSources);
|
||||
},
|
||||
|
||||
// ----------
|
||||
gridTest: function() {
|
||||
var self = this;
|
||||
var startX = -3;
|
||||
var expected = 0;
|
||||
var loaded = 0;
|
||||
|
||||
this.viewer.addHandler( "open", function() {
|
||||
self.viewer.world.addHandler('add-item', function() {
|
||||
loaded++;
|
||||
if (loaded === expected) {
|
||||
self.viewer.viewport.goHome(true);
|
||||
}
|
||||
});
|
||||
|
||||
var x, y;
|
||||
for (y = 0; y < 6; y++) {
|
||||
for (x = 0; x < 6; x++) {
|
||||
if (!x && !y) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var options = {
|
||||
tileSource: '../../data/testpattern.dzi',
|
||||
x: startX + x,
|
||||
y: y,
|
||||
width: 1
|
||||
};
|
||||
|
||||
expected++;
|
||||
self.viewer.addTiledImage( options );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
x: startX,
|
||||
y: 0,
|
||||
width: 1
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
bigTest: function() {
|
||||
this.viewer.open({
|
||||
tileSource: "../../data/testpattern.dzi",
|
||||
x: -2,
|
||||
y: -2,
|
||||
width: 6
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
cjTest: function() {
|
||||
var imageKey = "e-pluribus-unum";
|
||||
var imageXML = '<?xml version="1.0" encoding="UTF-8"?><Image TileSize="254" Overlap="1" Format="png" xmlns="http://schemas.microsoft.com/deepzoom/2008"><Size Width="88560" Height="88560"/></Image>';
|
||||
var $xml = $($.parseXML(imageXML));
|
||||
var $image = $xml.find('Image');
|
||||
var $size = $xml.find('Size');
|
||||
|
||||
var dzi = {
|
||||
Image: {
|
||||
xmlns: $image.attr('xmlns'),
|
||||
Url: "http://chrisjordan.com/dzi/" + imageKey + '_files/',
|
||||
Format: $image.attr('Format'),
|
||||
Overlap: $image.attr('Overlap'),
|
||||
TileSize: $image.attr('TileSize'),
|
||||
Size: {
|
||||
Height: $size.attr('Height'),
|
||||
Width: $size.attr('Width')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.viewer.open({
|
||||
tileSource: dzi,
|
||||
width: 100
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
stanfordTest: function() {
|
||||
var info = {"@context":"http://library.stanford.edu/iiif/image-api/1.1/context.json","@id":"http://ids.lib.harvard.edu/ids/iiif/48530377","width":6251,"height":109517,"scale_factors":[1,2,4,8,16,32],"tile_width":256,"tile_height":256,"formats":["jpg"],"qualities":["native"],"profile":"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"};
|
||||
|
||||
this.viewer.open(info);
|
||||
}
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
App.init();
|
||||
});
|
||||
|
||||
})();
|
135
test/demo/item-animation.html
Normal file
135
test/demo/item-animation.html
Normal file
@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenSeadragon Collections 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">
|
||||
|
||||
html,
|
||||
body,
|
||||
.openseadragon1 {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
|
||||
// ----------
|
||||
App = {
|
||||
itemCount: 5,
|
||||
positionRange: 100,
|
||||
sizeRange: 30,
|
||||
delay: 100,
|
||||
|
||||
// ----------
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
this.index = 0;
|
||||
this.paused = false;
|
||||
|
||||
var tileSource = {
|
||||
Image: {
|
||||
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/",
|
||||
Format: "jpg",
|
||||
Overlap: "2",
|
||||
TileSize: "256",
|
||||
Size: {
|
||||
Height: "9221",
|
||||
Width: "7026"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var tileSources = [];
|
||||
for (var i = 0; i < this.itemCount; i++) {
|
||||
tileSources.push(tileSource);
|
||||
}
|
||||
|
||||
this.viewer = OpenSeadragon({
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
tileSources: tileSources
|
||||
});
|
||||
|
||||
this.viewer.addHandler('open', function() {
|
||||
self.updateCount();
|
||||
|
||||
self.viewer.viewport.fitBounds(new OpenSeadragon.Rect(0, 0,
|
||||
self.positionRange + self.sizeRange,
|
||||
self.positionRange + self.sizeRange));
|
||||
|
||||
self.animate();
|
||||
});
|
||||
|
||||
var $pause = $('.toggle-pause').click(function() {
|
||||
self.paused = !self.paused;
|
||||
$pause.text(self.paused ? 'Play' : 'Pause');
|
||||
});
|
||||
|
||||
$('.add').click(function() {
|
||||
for (var i = 0; i < self.itemCount; i++) {
|
||||
self.viewer.addTiledImage({
|
||||
tileSource: tileSource,
|
||||
x: Math.random() * self.positionRange,
|
||||
y: Math.random() * self.positionRange,
|
||||
width: Math.random() * self.sizeRange,
|
||||
success: function() {
|
||||
self.updateCount();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
animate: function() {
|
||||
var self = this;
|
||||
|
||||
if (!this.paused) {
|
||||
var item = this.viewer.world.getItemAt(this.index);
|
||||
item.setPosition(new OpenSeadragon.Point(Math.random() * this.positionRange,
|
||||
Math.random() * this.positionRange));
|
||||
|
||||
item.setWidth(Math.random() * this.sizeRange);
|
||||
|
||||
this.index = (this.index + 1) % this.viewer.world.getItemCount();
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
self.animate();
|
||||
}, this.delay);
|
||||
},
|
||||
|
||||
// ----------
|
||||
updateCount: function() {
|
||||
$('.count').text(this.viewer.world.getItemCount());
|
||||
}
|
||||
};
|
||||
|
||||
// ----------
|
||||
$(document).ready(function() {
|
||||
App.init();
|
||||
});
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="contentDiv" class="openseadragon1"></div>
|
||||
<div class="controls">
|
||||
<button class="toggle-pause">Pause</button>
|
||||
<button class="add">Add More</button>
|
||||
Image Count: <span class="count"></span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
20
test/demo/m2/README.md
Normal file
20
test/demo/m2/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# M2 Demo
|
||||
|
||||
This is an advanced demo/testbed, for proposed improvements to the new version of the Mirador project:
|
||||
|
||||
https://github.com/IIIF/m2/
|
||||
|
||||
You can see a previous version of Mirador here:
|
||||
|
||||
http://showcase.iiif.io/viewer/mirador/
|
||||
|
||||
## To Do
|
||||
|
||||
* Choosing between multiple versions of a page
|
||||
* Detail images overlaid on the page
|
||||
* Cropped images
|
||||
|
||||
### Maybe
|
||||
|
||||
* Show/hide pages?
|
||||
* Lazyloading tilesources?
|
78
test/demo/m2/index.html
Normal file
78
test/demo/m2/index.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mirador POC</title>
|
||||
<script type="text/javascript" src='../../../build/openseadragon/openseadragon.js'></script>
|
||||
<script type="text/javascript" src='../../lib/jquery-1.9.1.min.js'></script>
|
||||
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
|
||||
<script src="js/main.js"></script>
|
||||
<!-- <script src="js/harvard-tilesources.js"></script> -->
|
||||
<script src="js/openseadragon-svg-overlay.js"></script>
|
||||
<style type="text/css">
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #eee;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: inline-block;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: #ddf;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.openseadragon1 {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.viewer-position {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 30px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.scroll-cover {
|
||||
display: none;
|
||||
overflow: scroll;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<button class="thumbs">Thumbnails</button>
|
||||
<button class="scroll">Scroll</button>
|
||||
<button class="book">Book</button>
|
||||
<button class="page">Page</button>
|
||||
<div class="nav">
|
||||
<button class="previous">Previous</button>
|
||||
<button class="next">Next</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="contentDiv" class="openseadragon1 viewer-position"></div>
|
||||
<div class="scroll-cover viewer-position">
|
||||
<div class="scroll-inner"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
1
test/demo/m2/js/.gitignore
vendored
Normal file
1
test/demo/m2/js/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
harvard-tilesources.js
|
670
test/demo/m2/js/main.js
Normal file
670
test/demo/m2/js/main.js
Normal file
@ -0,0 +1,670 @@
|
||||
/* globals $, App, d3 */
|
||||
|
||||
(function() {
|
||||
// ----------
|
||||
window.App = {
|
||||
// ----------
|
||||
init: function() {
|
||||
var self = this;
|
||||
|
||||
this.maxImages = 500;
|
||||
this.mode = 'none';
|
||||
this.pageBuffer = 0.05;
|
||||
this.bigBuffer = 0.2;
|
||||
this.page = 0;
|
||||
this.modeNames = [
|
||||
'thumbs',
|
||||
'scroll',
|
||||
'book',
|
||||
'page'
|
||||
];
|
||||
|
||||
this.viewer = OpenSeadragon({
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../../build/openseadragon/images/",
|
||||
autoResize: false,
|
||||
showHomeControl: false,
|
||||
tileSources: this.getTileSources()
|
||||
});
|
||||
|
||||
this.viewer.addHandler('open', function() {
|
||||
self.$el = $(self.viewer.element);
|
||||
self.setMode({
|
||||
mode: 'thumbs',
|
||||
immediately: true
|
||||
});
|
||||
});
|
||||
|
||||
this.viewer.addHandler('canvas-drag', function() {
|
||||
if (self.mode === 'scroll') {
|
||||
var result = self.hitTest(self.viewer.viewport.getCenter());
|
||||
if (result) {
|
||||
self.page = result.index;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.viewer.addHandler('viewport-change', function(event) {
|
||||
self.applyConstraints();
|
||||
});
|
||||
|
||||
$.each(this.modeNames, function(i, v) {
|
||||
$('.' + v).click(function() {
|
||||
self.setMode({
|
||||
mode: v
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('.next').click(function() {
|
||||
self.next();
|
||||
});
|
||||
|
||||
$('.previous').click(function() {
|
||||
self.previous();
|
||||
});
|
||||
|
||||
$(window).keyup(function(event) {
|
||||
if (self.mode === 'thumbs') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.which === 39) { // Right arrow
|
||||
self.next();
|
||||
} else if (event.which === 37) { // Left arrow
|
||||
self.previous();
|
||||
}
|
||||
});
|
||||
|
||||
this.$scrollInner = $('.scroll-inner');
|
||||
|
||||
this.$scrollCover = $('.scroll-cover')
|
||||
.scroll(function(event) {
|
||||
var info = self.getScrollInfo();
|
||||
if (!info || self.ignoreScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = new OpenSeadragon.Point(info.thumbBounds.getCenter().x,
|
||||
info.thumbBounds.y + (info.viewportHeight / 2) +
|
||||
(info.viewportMax * info.scrollFactor));
|
||||
|
||||
self.viewer.viewport.panTo(pos, true);
|
||||
})
|
||||
.mousemove(function(event) {
|
||||
var pixel = new OpenSeadragon.Point(event.clientX, event.clientY);
|
||||
var result = self.hitTest(self.viewer.viewport.pointFromPixel(pixel));
|
||||
self.updateHover(result ? result.index : -1);
|
||||
})
|
||||
.click(function(event) {
|
||||
var pixel = new OpenSeadragon.Point(event.clientX, event.clientY);
|
||||
var result = self.hitTest(self.viewer.viewport.pointFromPixel(pixel));
|
||||
if (result) {
|
||||
self.setMode({
|
||||
mode: 'page',
|
||||
page: result.index
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var svgNode = this.viewer.svgOverlay();
|
||||
|
||||
this.highlight = d3.select(svgNode).append("rect")
|
||||
.style('fill', 'none')
|
||||
.style('stroke', '#08f')
|
||||
.style('opacity', 0)
|
||||
.style('stroke-width', 0.05)
|
||||
.attr("pointer-events", "none");
|
||||
|
||||
this.hover = d3.select(svgNode).append("rect")
|
||||
.style('fill', 'none')
|
||||
.style('stroke', '#08f')
|
||||
.style('opacity', 0)
|
||||
.style('stroke-width', 0.05)
|
||||
.attr("pointer-events", "none");
|
||||
|
||||
$(window).resize(function() {
|
||||
var newSize = new OpenSeadragon.Point(self.$el.width(), self.$el.height());
|
||||
self.viewer.viewport.resize(newSize, false);
|
||||
self.setMode({
|
||||
mode: self.mode,
|
||||
immediately: true
|
||||
});
|
||||
|
||||
self.viewer.forceRedraw();
|
||||
|
||||
self.viewer.svgOverlay('resize');
|
||||
});
|
||||
|
||||
this.update();
|
||||
},
|
||||
|
||||
// ----------
|
||||
next: function() {
|
||||
var page = this.page + (this.mode === 'book' ? 2 : 1);
|
||||
if (this.mode === 'book' && page % 2 === 0 && page !== 0) {
|
||||
page --;
|
||||
}
|
||||
|
||||
this.goToPage({
|
||||
page: page
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
previous: function() {
|
||||
var page = this.page - (this.mode === 'book' ? 2 : 1);
|
||||
if (this.mode === 'book' && page % 2 === 0 && page !== 0) {
|
||||
page --;
|
||||
}
|
||||
|
||||
this.goToPage({
|
||||
page: page
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
hitTest: function(pos) {
|
||||
var count = this.viewer.world.getItemCount();
|
||||
var item, box;
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
item = this.viewer.world.getItemAt(i);
|
||||
box = item.getBounds();
|
||||
if (pos.x > box.x && pos.y > box.y && pos.x < box.x + box.width &&
|
||||
pos.y < box.y + box.height) {
|
||||
return {
|
||||
item: item,
|
||||
index: i
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
// ----------
|
||||
getScrollInfo: function() {
|
||||
if (!this.thumbBounds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var output = {};
|
||||
|
||||
var viewerWidth = this.$el.width();
|
||||
var viewerHeight = this.$el.height();
|
||||
var scrollTop = this.$scrollCover.scrollTop();
|
||||
output.scrollMax = this.$scrollInner.height() - this.$scrollCover.height();
|
||||
output.scrollFactor = (output.scrollMax > 0 ? scrollTop / output.scrollMax : 0);
|
||||
|
||||
output.thumbBounds = this.thumbBounds;
|
||||
output.viewportHeight = output.thumbBounds.width * (viewerHeight / viewerWidth);
|
||||
output.viewportMax = Math.max(0, output.thumbBounds.height - output.viewportHeight);
|
||||
return output;
|
||||
},
|
||||
|
||||
// ----------
|
||||
update: function() {
|
||||
var self = this;
|
||||
|
||||
$('.nav').toggle(this.mode === 'scroll' || this.mode === 'book' || this.mode === 'page');
|
||||
$('.previous').toggleClass('hidden', this.page <= 0);
|
||||
$('.next').toggleClass('hidden', this.page >= this.viewer.world.getItemCount() - 1);
|
||||
|
||||
$.each(this.modeNames, function(i, v) {
|
||||
$('.' + v).toggleClass('active', v === self.mode);
|
||||
});
|
||||
},
|
||||
|
||||
// ----------
|
||||
applyConstraints: function() {
|
||||
if (this.mode === 'thumbs') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.panBounds) {
|
||||
var center = this.viewer.viewport.getCenter(true);
|
||||
var viewBounds = this.viewer.viewport.getBounds(true);
|
||||
var bounds = this.panBounds.clone();
|
||||
var left = bounds.x + (viewBounds.width / 2);
|
||||
var top = bounds.y + (viewBounds.height / 2);
|
||||
var right = (bounds.x + bounds.width) - (viewBounds.width / 2);
|
||||
var bottom = (bounds.y + bounds.height) - (viewBounds.height / 2);
|
||||
|
||||
var x;
|
||||
if (left <= right) {
|
||||
x = Math.max(left, Math.min(right, center.x));
|
||||
} else {
|
||||
x = bounds.x + (bounds.width / 2);
|
||||
}
|
||||
|
||||
var y;
|
||||
if (top <= bottom) {
|
||||
y = Math.max(top, Math.min(bottom, center.y));
|
||||
} else {
|
||||
y = bounds.y + (bounds.height / 2);
|
||||
}
|
||||
|
||||
if (x !== center.x || y !== center.y) {
|
||||
this.viewer.viewport.centerSpringX.current.value = x;
|
||||
this.viewer.viewport.centerSpringY.current.value = y;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
setMode: function(config) {
|
||||
var self = this;
|
||||
|
||||
this.mode = config.mode;
|
||||
|
||||
if (config.page !== undefined) {
|
||||
this.page = config.page; // Need to do this before layout
|
||||
}
|
||||
|
||||
this.ignoreScroll = true;
|
||||
this.thumbBounds = null;
|
||||
|
||||
var layout = this.createLayout();
|
||||
|
||||
if (this.mode === 'thumbs') {
|
||||
this.viewer.gestureSettingsMouse.scrollToZoom = false;
|
||||
this.viewer.zoomPerClick = 1;
|
||||
this.viewer.panHorizontal = false;
|
||||
this.viewer.panVertical = false;
|
||||
var viewerWidth = this.$el.width();
|
||||
var width = layout.bounds.width + (this.bigBuffer * 2);
|
||||
var height = layout.bounds.height + (this.bigBuffer * 2);
|
||||
var newHeight = viewerWidth * (height / width);
|
||||
this.$scrollCover.show();
|
||||
this.$scrollInner
|
||||
.css({
|
||||
height: newHeight
|
||||
});
|
||||
} else {
|
||||
this.viewer.gestureSettingsMouse.scrollToZoom = true;
|
||||
this.viewer.zoomPerClick = 2;
|
||||
this.viewer.panHorizontal = true;
|
||||
this.viewer.panVertical = true;
|
||||
this.$scrollCover.hide();
|
||||
}
|
||||
|
||||
this.setLayout({
|
||||
layout: layout,
|
||||
immediately: config.immediately
|
||||
});
|
||||
|
||||
if (this.mode === 'thumbs') {
|
||||
// Set up thumbBounds
|
||||
this.thumbBounds = this.viewer.world.getHomeBounds();
|
||||
this.thumbBounds.x -= this.bigBuffer;
|
||||
this.thumbBounds.y -= this.bigBuffer;
|
||||
this.thumbBounds.width += (this.bigBuffer * 2);
|
||||
this.thumbBounds.height += (this.bigBuffer * 2);
|
||||
|
||||
// Scroll to the appropriate location
|
||||
var info = this.getScrollInfo();
|
||||
|
||||
var viewportBounds = this.thumbBounds.clone();
|
||||
viewportBounds.y += info.viewportMax * info.scrollFactor;
|
||||
viewportBounds.height = info.viewportHeight;
|
||||
|
||||
var itemBounds = this.viewer.world.getItemAt(this.page).getBounds();
|
||||
var top = itemBounds.y - this.bigBuffer;
|
||||
var bottom = top + itemBounds.height + (this.bigBuffer * 2);
|
||||
|
||||
var normalY;
|
||||
if (top < viewportBounds.y) {
|
||||
normalY = top - this.thumbBounds.y;
|
||||
} else if (bottom > viewportBounds.y + viewportBounds.height) {
|
||||
normalY = (bottom - info.viewportHeight) - this.thumbBounds.y;
|
||||
}
|
||||
|
||||
if (normalY !== undefined) {
|
||||
var viewportFactor = normalY / info.viewportMax;
|
||||
this.$scrollCover.scrollTop(info.scrollMax * viewportFactor);
|
||||
}
|
||||
}
|
||||
|
||||
this.goHome({
|
||||
immediately: config.immediately
|
||||
});
|
||||
|
||||
this.viewer.viewport.minZoomLevel = this.viewer.viewport.getZoom();
|
||||
|
||||
this.update();
|
||||
this.updateHighlight();
|
||||
this.updateHover(-1);
|
||||
|
||||
clearTimeout(this.scrollTimeout);
|
||||
this.scrollTimeout = setTimeout(function() {
|
||||
self.ignoreScroll = false;
|
||||
}, this.viewer.animationTime * 1000);
|
||||
},
|
||||
|
||||
// ----------
|
||||
updateHighlight: function() {
|
||||
if (this.mode !== 'thumbs') {
|
||||
this.highlight.style('opacity', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
var item = this.viewer.world.getItemAt(this.page);
|
||||
var box = item.getBounds();
|
||||
|
||||
this.highlight
|
||||
.style('opacity', 1)
|
||||
.attr("x", box.x)
|
||||
.attr("width", box.width)
|
||||
.attr("y", box.y)
|
||||
.attr("height", box.height);
|
||||
},
|
||||
|
||||
// ----------
|
||||
updateHover: function(page) {
|
||||
if (page === -1 || this.mode !== 'thumbs') {
|
||||
this.hover.style('opacity', 0);
|
||||
this.$scrollCover.css({
|
||||
'cursor': 'default'
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.$scrollCover.css({
|
||||
'cursor': 'pointer'
|
||||
});
|
||||
|
||||
var item = this.viewer.world.getItemAt(page);
|
||||
var box = item.getBounds();
|
||||
|
||||
this.hover
|
||||
.style('opacity', 0.3)
|
||||
.attr("x", box.x)
|
||||
.attr("width", box.width)
|
||||
.attr("y", box.y)
|
||||
.attr("height", box.height);
|
||||
},
|
||||
|
||||
// ----------
|
||||
goToPage: function(config) {
|
||||
var self = this;
|
||||
|
||||
var itemCount = this.viewer.world.getItemCount();
|
||||
this.page = Math.max(0, Math.min(itemCount - 1, config.page));
|
||||
|
||||
var viewerWidth = this.$el.width();
|
||||
var viewerHeight = this.$el.height();
|
||||
var bounds = this.viewer.world.getItemAt(this.page).getBounds();
|
||||
var x = bounds.x;
|
||||
var y = bounds.y;
|
||||
var width = bounds.width;
|
||||
var height = bounds.height;
|
||||
var box;
|
||||
|
||||
if (this.mode === 'book') {
|
||||
var item;
|
||||
if (this.page % 2) { // First in a pair
|
||||
item = this.viewer.world.getItemAt(this.page + 1);
|
||||
if (item) {
|
||||
width += item.getBounds().width;
|
||||
}
|
||||
} else {
|
||||
item = this.viewer.world.getItemAt(this.page - 1);
|
||||
if (item) {
|
||||
box = item.getBounds();
|
||||
x -= width;
|
||||
width += box.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x -= this.pageBuffer;
|
||||
y -= this.pageBuffer;
|
||||
width += (this.pageBuffer * 2);
|
||||
height += (this.pageBuffer * 2);
|
||||
|
||||
if (this.mode === 'scroll') {
|
||||
if (this.page === 0) {
|
||||
x = bounds.x - this.pageBuffer;
|
||||
width = height * (viewerWidth / viewerHeight);
|
||||
} else if (this.page === this.viewer.world.getItemCount() - 1) {
|
||||
width = height * (viewerWidth / viewerHeight);
|
||||
x = (bounds.x + bounds.width + this.pageBuffer) - width;
|
||||
}
|
||||
}
|
||||
|
||||
box = new OpenSeadragon.Rect(x, y, width, height);
|
||||
this.viewer.viewport.fitBounds(box, config.immediately);
|
||||
|
||||
this.panBounds = null;
|
||||
|
||||
var setPanBounds = function() {
|
||||
if (self.mode === 'page' || self.mode === 'book') {
|
||||
self.panBounds = box;
|
||||
} else if (self.mode === 'scroll') {
|
||||
self.panBounds = self.viewer.world.getItemAt(0).getBounds()
|
||||
.union(self.viewer.world.getItemAt(itemCount - 1).getBounds());
|
||||
|
||||
self.panBounds.x -= self.pageBuffer;
|
||||
self.panBounds.y -= self.pageBuffer;
|
||||
self.panBounds.width += (self.pageBuffer * 2);
|
||||
self.panBounds.height += (self.pageBuffer * 2);
|
||||
}
|
||||
};
|
||||
|
||||
clearTimeout(this.panBoundsTimeout);
|
||||
if (config.immediately) {
|
||||
setPanBounds();
|
||||
} else {
|
||||
this.panBoundsTimeout = setTimeout(setPanBounds, this.viewer.animationTime * 1000);
|
||||
}
|
||||
|
||||
this.viewer.viewport.minZoomLevel = this.viewer.viewport.getZoom();
|
||||
|
||||
this.update();
|
||||
},
|
||||
|
||||
// ----------
|
||||
createLayout: function() {
|
||||
var viewerWidth = this.$el.width();
|
||||
var viewerHeight = this.$el.height();
|
||||
var layoutConfig = {};
|
||||
|
||||
if (this.mode === 'thumbs') {
|
||||
layoutConfig.columns = Math.floor(viewerWidth / 150);
|
||||
layoutConfig.buffer = this.bigBuffer;
|
||||
layoutConfig.sameWidth = true;
|
||||
} else if (this.mode === 'scroll') {
|
||||
layoutConfig.buffer = this.pageBuffer;
|
||||
} else if (this.mode === 'book' || this.mode === 'page') {
|
||||
layoutConfig.book = (this.mode === 'book');
|
||||
var height = 1 + (this.pageBuffer * 2);
|
||||
// Note that using window here is approximate, but that's close enough.
|
||||
// We can't use viewer, because it may be stretched for the thumbs view.
|
||||
layoutConfig.buffer = (height * ($(window).width() / $(window).height())) / 2;
|
||||
}
|
||||
|
||||
var layout = {
|
||||
bounds: null,
|
||||
specs: []
|
||||
};
|
||||
|
||||
var count = this.viewer.world.getItemCount();
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
var offset = new OpenSeadragon.Point();
|
||||
var rowHeight = 0;
|
||||
var item, box;
|
||||
for (var i = 0; i < count; i++) {
|
||||
item = this.viewer.world.getItemAt(i);
|
||||
box = item.getBounds();
|
||||
|
||||
if (i === this.page) {
|
||||
offset = box.getTopLeft().minus(new OpenSeadragon.Point(x, y));
|
||||
}
|
||||
|
||||
box.x = x;
|
||||
box.y = y;
|
||||
if (layoutConfig.sameWidth) {
|
||||
box.height = box.height / box.width;
|
||||
box.width = 1;
|
||||
} else {
|
||||
box.width = box.width / box.height;
|
||||
box.height = 1;
|
||||
}
|
||||
|
||||
rowHeight = Math.max(rowHeight, box.height);
|
||||
|
||||
layout.specs.push({
|
||||
item: item,
|
||||
bounds: box
|
||||
});
|
||||
|
||||
if (layoutConfig.columns && i % layoutConfig.columns === layoutConfig.columns - 1) {
|
||||
x = 0;
|
||||
y += rowHeight + layoutConfig.buffer;
|
||||
rowHeight = 0;
|
||||
} else {
|
||||
if (!layoutConfig.book || i % 2 === 0) {
|
||||
x += layoutConfig.buffer;
|
||||
}
|
||||
|
||||
x += box.width;
|
||||
}
|
||||
}
|
||||
|
||||
var pos, spec;
|
||||
for (i = 0; i < count; i++) {
|
||||
spec = layout.specs[i];
|
||||
pos = spec.bounds.getTopLeft().plus(offset);
|
||||
spec.bounds.x = pos.x;
|
||||
spec.bounds.y = pos.y;
|
||||
|
||||
if (layout.bounds) {
|
||||
layout.bounds = layout.bounds.union(spec.bounds);
|
||||
} else {
|
||||
layout.bounds = spec.bounds.clone();
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
},
|
||||
|
||||
// ----------
|
||||
setLayout: function(config) {
|
||||
var spec;
|
||||
|
||||
for (var i = 0; i < config.layout.specs.length; i++) {
|
||||
spec = config.layout.specs[i];
|
||||
spec.item.setPosition(spec.bounds.getTopLeft(), config.immediately);
|
||||
spec.item.setWidth(spec.bounds.width, config.immediately);
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
goHome: function(config) {
|
||||
var viewerWidth = this.$el.width();
|
||||
var viewerHeight = this.$el.height();
|
||||
var layoutConfig = {};
|
||||
|
||||
if (this.mode === 'thumbs') {
|
||||
var info = this.getScrollInfo();
|
||||
var box = this.thumbBounds.clone();
|
||||
box.height = box.width * (viewerHeight / viewerWidth);
|
||||
box.y += info.viewportMax * info.scrollFactor;
|
||||
this.viewer.viewport.fitBounds(box, config.immediately);
|
||||
} else {
|
||||
this.goToPage({
|
||||
page: this.page,
|
||||
immediately: config.immediately
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// ----------
|
||||
getTileSources: function() {
|
||||
if (this.tileSources) {
|
||||
return $.map(this.tileSources.slice(0, this.maxImages), function(v, i) {
|
||||
return new OpenSeadragon.IIIFTileSource(v);
|
||||
});
|
||||
}
|
||||
|
||||
var inputs = [
|
||||
{
|
||||
Image: {
|
||||
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/",
|
||||
Format: "jpg",
|
||||
Overlap: "2",
|
||||
TileSize: "256",
|
||||
Size: {
|
||||
Width: "7026",
|
||||
Height: "9221"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
Image: {
|
||||
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
Url: "http://openseadragon.github.io/example-images/duomo/duomo_files/",
|
||||
Format: "jpg",
|
||||
Overlap: "2",
|
||||
TileSize: "256",
|
||||
Size: {
|
||||
Width: "13920",
|
||||
Height: "10200"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
// Image: {
|
||||
// xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
// Url: "../../data/tall_files/",
|
||||
// Format: "jpg",
|
||||
// Overlap: "1",
|
||||
// TileSize: "254",
|
||||
// Size: {
|
||||
// Width: "500",
|
||||
// Height: "2000"
|
||||
// }
|
||||
// }
|
||||
// }, {
|
||||
// Image: {
|
||||
// xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
// Url: "../../data/wide_files/",
|
||||
// Format: "jpg",
|
||||
// Overlap: "1",
|
||||
// TileSize: "254",
|
||||
// Size: {
|
||||
// Width: "2000",
|
||||
// Height: "500"
|
||||
// }
|
||||
// }
|
||||
// }, {
|
||||
Image: {
|
||||
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
|
||||
Url: "../../data/testpattern_files/",
|
||||
Format: "jpg",
|
||||
Overlap: "1",
|
||||
TileSize: "254",
|
||||
Size: {
|
||||
Width: "1000",
|
||||
Height: "1000"
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var outputs = [];
|
||||
for (var i = 0; i < this.maxImages; i++) {
|
||||
outputs.push(inputs[Math.floor(Math.random() * inputs.length)]);
|
||||
}
|
||||
|
||||
return outputs;
|
||||
}
|
||||
};
|
||||
|
||||
// ----------
|
||||
$(document).ready(function() {
|
||||
App.init();
|
||||
});
|
||||
})();
|
73
test/demo/m2/js/openseadragon-svg-overlay.js
Normal file
73
test/demo/m2/js/openseadragon-svg-overlay.js
Normal file
@ -0,0 +1,73 @@
|
||||
(function() {
|
||||
|
||||
if (!window.OpenSeadragon) {
|
||||
console.error('[openseadragon-svg-overlay] requires OpenSeadragon');
|
||||
return;
|
||||
}
|
||||
|
||||
var svgNS = 'http://www.w3.org/2000/svg';
|
||||
|
||||
var update = function(viewer) {
|
||||
var info = viewer._svgOverlayInfo;
|
||||
|
||||
if (info.containerWidth !== viewer.container.clientWidth) {
|
||||
info.containerWidth = viewer.container.clientWidth;
|
||||
info.svg.setAttribute('width', info.containerWidth);
|
||||
}
|
||||
|
||||
if (info.containerHeight !== viewer.container.clientHeight) {
|
||||
info.containerHeight = viewer.container.clientHeight;
|
||||
info.svg.setAttribute('height', info.containerHeight);
|
||||
}
|
||||
|
||||
var p = viewer.viewport.pixelFromPoint(new OpenSeadragon.Point(0, 0), true);
|
||||
var zoom = viewer.viewport.getZoom(true);
|
||||
var scale = viewer.container.clientWidth * zoom;
|
||||
info.node.setAttribute('transform',
|
||||
'translate(' + p.x + ',' + p.y + ') scale(' + scale + ')');
|
||||
};
|
||||
|
||||
OpenSeadragon.Viewer.prototype.svgOverlay = function(command) {
|
||||
var self = this;
|
||||
|
||||
if (command === undefined) {
|
||||
if (this._svgOverlayInfo) {
|
||||
console.error('[openseadragon-svg-overlay] already initialized on this viewer');
|
||||
return;
|
||||
}
|
||||
|
||||
var info = this._svgOverlayInfo = {
|
||||
containerWidth: 0,
|
||||
containerHeight: 0
|
||||
};
|
||||
|
||||
info.svg = document.createElementNS(svgNS, 'svg');
|
||||
info.svg.setAttribute('pointer-events', 'none');
|
||||
info.svg.style.position = 'absolute';
|
||||
info.svg.style.left = 0;
|
||||
info.svg.style.top = 0;
|
||||
info.svg.style.width = '100%';
|
||||
info.svg.style.height = '100%';
|
||||
this.container.insertBefore(info.svg, this.canvas.nextSibling);
|
||||
|
||||
info.node = document.createElementNS(svgNS, 'g');
|
||||
info.svg.appendChild(info.node);
|
||||
|
||||
this.addHandler('animation', function() {
|
||||
update(self);
|
||||
});
|
||||
|
||||
this.addHandler('open', function() {
|
||||
update(self);
|
||||
});
|
||||
|
||||
update(this);
|
||||
return info.node;
|
||||
} else if (command === 'resize') {
|
||||
update(this);
|
||||
} else {
|
||||
console.error('[openseadragon-svg-overlay] unknown command: ' + command);
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
@ -50,6 +50,7 @@
|
||||
.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', event );
|
||||
},
|
||||
|
||||
// ----------
|
||||
initializeTestDOM: function () {
|
||||
$( "#qunit-fixture" )
|
||||
.append( '<div><div id="example"></div><div id="exampleNavigator"></div></div>' )
|
||||
@ -57,14 +58,17 @@
|
||||
.append( '<div id="tallexample"></div>' );
|
||||
},
|
||||
|
||||
// ----------
|
||||
equalsWithVariance: function ( value1, value2, variance ) {
|
||||
return Math.abs( value1 - value2 ) <= variance;
|
||||
},
|
||||
|
||||
// ----------
|
||||
assessNumericValue: function ( value1, value2, variance, message ) {
|
||||
ok( Util.equalsWithVariance( value1, value2, variance ), message + " Expected:" + value1 + " Found: " + value2 + " Variance: " + variance );
|
||||
},
|
||||
|
||||
// ----------
|
||||
timeWatcher: function ( time ) {
|
||||
time = time || 2000;
|
||||
var finished = false;
|
||||
@ -85,8 +89,46 @@
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// ----------
|
||||
spyOnce: function(obj, functionName, callback) {
|
||||
var original = obj[functionName];
|
||||
obj[functionName] = function() {
|
||||
obj[functionName] = original;
|
||||
var result = callback.apply(this, arguments);
|
||||
if (result === undefined) {
|
||||
result = original.apply(this, arguments);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
},
|
||||
|
||||
// ----------
|
||||
testDeprecation: function(obj0, member0, obj1, member1) {
|
||||
var called = false;
|
||||
var errored = false;
|
||||
|
||||
if (obj1 && member1) {
|
||||
this.spyOnce(obj1, member1, function() {
|
||||
called = true;
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
called = true;
|
||||
}
|
||||
|
||||
this.spyOnce(OpenSeadragon.console, 'error', function(message) {
|
||||
if (/deprecated/.test(message)) {
|
||||
errored = true;
|
||||
}
|
||||
});
|
||||
|
||||
obj0[member0]();
|
||||
equal(called, true, 'called through for ' + member0);
|
||||
equal(errored, true, 'errored for ' + member0);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -134,6 +176,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
testConsole.assert = function(condition, message) {
|
||||
if (condition) {
|
||||
testConsole.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
OpenSeadragon.console = testConsole;
|
||||
} )();
|
||||
|
278
test/layers.js
278
test/layers.js
@ -1,278 +0,0 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
( function() {
|
||||
var viewer;
|
||||
|
||||
module( 'Layers', {
|
||||
setup: function() {
|
||||
$( '<div id="layersexample"></div>' ).appendTo( "#qunit-fixture" );
|
||||
|
||||
testLog.reset();
|
||||
|
||||
viewer = OpenSeadragon( {
|
||||
id: 'layersexample',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
});
|
||||
},
|
||||
teardown: function() {
|
||||
if ( viewer && viewer.close ) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
$( "#layersexample" ).remove();
|
||||
}
|
||||
} );
|
||||
|
||||
// ----------
|
||||
asyncTest( 'Layers operations', function() {
|
||||
expect( 23 );
|
||||
viewer.addHandler( "open", function( ) {
|
||||
equal( 1, viewer.getLayersCount( ),
|
||||
"One layer should be present after opening." );
|
||||
var options = {
|
||||
tileSource: {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [ {
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
} ]
|
||||
}
|
||||
};
|
||||
viewer.addLayer( options );
|
||||
viewer.addHandler( "add-layer", function addFirstLayerHandler( event ) {
|
||||
viewer.removeHandler( "add-layer", addFirstLayerHandler );
|
||||
var layer1 = event.drawer;
|
||||
equal( viewer.getLayersCount( ), 2,
|
||||
"2 layers should be present after adding a layer." );
|
||||
equal( options, event.options,
|
||||
"The options should be transmitted via the event." );
|
||||
equal( viewer.getLevelOfLayer( layer1 ), 1,
|
||||
"The first added layer should have a level of 1" );
|
||||
equal( viewer.getLayerAtLevel( 1 ), layer1,
|
||||
"The layer at level 1 should be the first added layer." );
|
||||
|
||||
viewer.addLayer( options );
|
||||
viewer.addHandler( "add-layer", function addSecondLayerHandler( event ) {
|
||||
viewer.removeHandler( "add-layer", addSecondLayerHandler );
|
||||
var layer2 = event.drawer;
|
||||
equal( viewer.getLayersCount( ), 3,
|
||||
"3 layers should be present after adding a second layer." );
|
||||
equal( viewer.getLevelOfLayer( layer2 ), 2,
|
||||
"If not specified, a layer should be added with the highest level." );
|
||||
equal( viewer.getLayerAtLevel( 2 ), layer2,
|
||||
"The layer at level 2 should be the second added layer." );
|
||||
|
||||
viewer.addHandler( "layer-level-changed",
|
||||
function layerLevelChangedHandler( event ) {
|
||||
viewer.removeHandler( "layer-level-changed",
|
||||
layerLevelChangedHandler );
|
||||
equal( event.drawer, layer2,
|
||||
"The layer which changed level should be layer2" );
|
||||
equal( event.previousLevel, 2, "Previous level should be 2." );
|
||||
equal( event.newLevel, 1, "New level should be 1." );
|
||||
});
|
||||
viewer.setLayerLevel( layer2, 1 );
|
||||
equal( viewer.getLevelOfLayer( layer2 ), 1,
|
||||
"Layer2 level should be 1 after setLayerLevel." );
|
||||
equal( viewer.getLevelOfLayer( layer1 ), 2,
|
||||
"Layer1 level should be 2 after setLayerLevel." );
|
||||
equal( viewer.getLayerAtLevel( 1 ), layer2,
|
||||
"The layer at level 1 should be layer2." );
|
||||
equal( viewer.getLayerAtLevel( 2 ), layer1,
|
||||
"The layer at level 2 should be layer1." );
|
||||
|
||||
options.level = 2;
|
||||
options.tileSource.levels[0].url = "data/CCyan.png";
|
||||
options.opacity = 0.5;
|
||||
viewer.addLayer( options );
|
||||
viewer.addHandler( "add-layer", function addThirdLayerHandler( event ) {
|
||||
viewer.removeHandler( "add-layer", addThirdLayerHandler );
|
||||
var layer3 = event.drawer;
|
||||
equal( viewer.getLayersCount( ), 4,
|
||||
"4 layers should be present after adding a third layer." );
|
||||
equal( viewer.getLevelOfLayer( layer3 ), 2,
|
||||
"Layer 3 should be added with level 2." );
|
||||
equal( viewer.getLevelOfLayer( layer2 ), 1,
|
||||
"Layer 2 should stay at level 1." );
|
||||
|
||||
viewer.addHandler( "remove-layer", function removeLayerHandler( event ) {
|
||||
viewer.removeHandler( "remove-layer", removeLayerHandler );
|
||||
|
||||
equal( layer2, event.drawer, "Removed layer should be layer2." );
|
||||
|
||||
equal( viewer.getLevelOfLayer( layer1 ), 2,
|
||||
"Layer 1 should be at level 2." );
|
||||
equal( viewer.getLevelOfLayer( layer2 ), -1,
|
||||
"Layer 2 should be at level -1." );
|
||||
equal( viewer.getLevelOfLayer( layer3 ), 1,
|
||||
"Layer 3 should be at level 1." );
|
||||
|
||||
});
|
||||
viewer.removeLayer( layer2 );
|
||||
|
||||
options.tileSource.levels[0].width = 500;
|
||||
viewer.addHandler( "add-layer-failed", function addLayerFailed( event ) {
|
||||
viewer.removeHandler( "add-layer-failed", addLayerFailed );
|
||||
|
||||
equal( viewer.getLayersCount(), 3 );
|
||||
|
||||
start();
|
||||
});
|
||||
viewer.addLayer( options );
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
asyncTest( 'Sequences as layers', function() {
|
||||
|
||||
var options = {
|
||||
tileSource: [{
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}, {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/BBlue.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
viewer.addHandler( "open", function openHandler() {
|
||||
viewer.removeHandler( "open", openHandler );
|
||||
|
||||
viewer.addHandler( "add-layer-failed",
|
||||
function addLayerFailedHandler( event ) {
|
||||
viewer.removeHandler( "add-layer-failed", addLayerFailedHandler );
|
||||
equal( event.message, "Sequences can not be added as layers." );
|
||||
equal( event.options, options, "Layer failed event should give the options." );
|
||||
start();
|
||||
} );
|
||||
viewer.addLayer( options );
|
||||
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
|
||||
asyncTest( 'Reassign base layer', function() {
|
||||
|
||||
var options = {
|
||||
tileSource: {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
},
|
||||
level: 0
|
||||
};
|
||||
viewer.addHandler( "open", function openHandler( ) {
|
||||
viewer.removeHandler( "open", openHandler );
|
||||
var testPatternDrawer = viewer.drawer;
|
||||
equal( viewer.drawer, testPatternDrawer, "Viewer.drawer should be set to testPatternDrawer." );
|
||||
viewer.addHandler( "add-layer", function addLayerHandler( event ) {
|
||||
viewer.removeHandler( "add-layer", addLayerHandler );
|
||||
var aDrawer = event.drawer;
|
||||
equal( viewer.drawer, aDrawer, "Viewer.drawer should be set to aDrawer." );
|
||||
viewer.setLayerLevel( aDrawer, 1 );
|
||||
equal( viewer.drawer, testPatternDrawer, "Viewer.drawer should be set back to testPatternDrawer." );
|
||||
|
||||
viewer.removeLayer( viewer.drawer );
|
||||
equal( viewer.drawer, aDrawer, "Viewer.drawer must be reassigned when removing base layer." );
|
||||
|
||||
viewer.removeLayer( viewer.drawer );
|
||||
ok( !viewer.isOpen(), "Viewer should be closed when removing last layer." );
|
||||
|
||||
start();
|
||||
});
|
||||
viewer.addLayer( options );
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
asyncTest( 'Layers and sequences', function() {
|
||||
expect( 1 );
|
||||
|
||||
// TODO: Remove workaround when issue #321 is fixed.
|
||||
// https://github.com/openseadragon/openseadragon/issues/321
|
||||
// viewer.open( [{
|
||||
// type: 'legacy-image-pyramid',
|
||||
// levels: [ {
|
||||
// url: "data/A.png",
|
||||
// width: 1000,
|
||||
// height: 1000
|
||||
// }]
|
||||
// },
|
||||
// {
|
||||
// type: 'legacy-image-pyramid',
|
||||
// levels: [ {
|
||||
// url: "data/BBlue.png",
|
||||
// width: 1000,
|
||||
// height: 1000
|
||||
// }]}] );
|
||||
|
||||
viewer.close();
|
||||
viewer = OpenSeadragon({
|
||||
id: 'layersexample',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
tileSources: [{
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
},
|
||||
{
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/BBlue.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}]
|
||||
});
|
||||
// End workaround
|
||||
|
||||
var options = {
|
||||
tileSource: {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/CCyan.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
viewer.addHandler( "open", function openHandler() {
|
||||
viewer.addHandler( "add-layer", function addLayerHandler( event ) {
|
||||
viewer.removeHandler( "add-layer", addLayerHandler );
|
||||
|
||||
var layer = event.drawer;
|
||||
try {
|
||||
viewer.setLayerLevel( layer, 0 );
|
||||
} catch (e) {
|
||||
ok( true );
|
||||
}
|
||||
start();
|
||||
} );
|
||||
viewer.addLayer( options );
|
||||
});
|
||||
|
||||
});
|
||||
})();
|
@ -95,7 +95,8 @@
|
||||
var panHandler = function() {
|
||||
viewer.removeHandler('animation-finish', panHandler);
|
||||
center = viewport.getCenter();
|
||||
ok(center.x === 0.1 && center.y === 0.1, 'Panned correctly');
|
||||
Util.assessNumericValue(center.x, 0.1, 0.00001, 'panned horizontally');
|
||||
Util.assessNumericValue(center.y, 0.1, 0.00001, 'panned vertically');
|
||||
start();
|
||||
};
|
||||
|
||||
@ -133,7 +134,7 @@
|
||||
};
|
||||
|
||||
viewer.addHandler('animation-finish', homeHandler);
|
||||
viewport.goHome(true);
|
||||
viewer.viewport.goHome(true);
|
||||
}
|
||||
|
||||
viewer.addHandler("open", opener);
|
||||
@ -260,9 +261,8 @@
|
||||
viewer.removeHandler('close', closeHandler);
|
||||
ok(!viewer.source, 'no source');
|
||||
ok(true, 'Close event was sent');
|
||||
ok(!viewer._updateRequestId, 'timer is off');
|
||||
setTimeout(function() {
|
||||
ok(!viewer._updateRequestId, 'timer is still off');
|
||||
ok(!viewer._updateRequestId, 'timer is off');
|
||||
start();
|
||||
}, 100);
|
||||
};
|
9
test/controls.js → test/modules/controls.js
vendored
9
test/controls.js → test/modules/controls.js
vendored
@ -5,7 +5,12 @@
|
||||
|
||||
module('Controls', {
|
||||
setup: function () {
|
||||
var example = $('<div id="controlsTests"></div>').appendTo("#qunit-fixture");
|
||||
var example = $('<div id="controlsTests"></div>')
|
||||
.css({
|
||||
width: 1000,
|
||||
height: 1000
|
||||
})
|
||||
.appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
|
||||
@ -324,6 +329,7 @@
|
||||
],
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
showSequenceControl: true,
|
||||
sequenceMode: true,
|
||||
navPrevNextWrap: false
|
||||
});
|
||||
viewer.addHandler('open', openHandler);
|
||||
@ -375,6 +381,7 @@
|
||||
],
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
showSequenceControl: true,
|
||||
sequenceMode: true,
|
||||
navPrevNextWrap: true
|
||||
});
|
||||
viewer.addHandler('open', openHandler);
|
84
test/modules/drawer.js
Normal file
84
test/modules/drawer.js
Normal file
@ -0,0 +1,84 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
|
||||
module('Drawer', {
|
||||
setup: function () {
|
||||
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
},
|
||||
teardown: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
var createViewer = function(options) {
|
||||
options = options || {};
|
||||
viewer = OpenSeadragon(OpenSeadragon.extend({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
}, options));
|
||||
};
|
||||
|
||||
// ----------
|
||||
asyncTest('basics', function() {
|
||||
createViewer();
|
||||
ok(viewer.drawer, 'Drawer exists');
|
||||
equal(viewer.drawer.canRotate(), OpenSeadragon.supportsCanvas, 'we can rotate if we have canvas');
|
||||
equal(viewer.drawer.getOpacity(), 1, 'starts with full opacity');
|
||||
viewer.drawer.setOpacity(0.4);
|
||||
equal(viewer.drawer.getOpacity(), 0.4, 'setting opacity works');
|
||||
start();
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('rotation', function() {
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi'
|
||||
});
|
||||
|
||||
viewer.addHandler('open', function handler(event) {
|
||||
viewer.viewport.setRotation(30);
|
||||
Util.spyOnce(viewer.drawer.context, 'rotate', function() {
|
||||
ok(true, 'drawing with new rotation');
|
||||
start();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('debug', function() {
|
||||
createViewer({
|
||||
tileSources: '/test/data/testpattern.dzi',
|
||||
debugMode: true
|
||||
});
|
||||
|
||||
Util.spyOnce(viewer.drawer, 'drawDebugInfo', function() {
|
||||
ok(true, 'drawDebugInfo is called');
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('deprecations', function() {
|
||||
createViewer();
|
||||
Util.testDeprecation(viewer.drawer, 'addOverlay', viewer, 'addOverlay');
|
||||
Util.testDeprecation(viewer.drawer, 'updateOverlay', viewer, 'updateOverlay');
|
||||
Util.testDeprecation(viewer.drawer, 'removeOverlay', viewer, 'removeOverlay');
|
||||
Util.testDeprecation(viewer.drawer, 'clearOverlays', viewer, 'clearOverlays');
|
||||
Util.testDeprecation(viewer.drawer, 'needsUpdate', viewer.world, 'needsDraw');
|
||||
Util.testDeprecation(viewer.drawer, 'numTilesLoaded', viewer.tileCache, 'numTilesLoaded');
|
||||
Util.testDeprecation(viewer.drawer, 'reset', viewer.world, 'resetItems');
|
||||
Util.testDeprecation(viewer.drawer, 'update', viewer.world, 'draw');
|
||||
start();
|
||||
});
|
||||
|
||||
})();
|
178
test/modules/multi-image.js
Normal file
178
test/modules/multi-image.js
Normal file
@ -0,0 +1,178 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog, expect */
|
||||
|
||||
( function() {
|
||||
var viewer;
|
||||
|
||||
module( 'Multi-Image', {
|
||||
setup: function() {
|
||||
$( '<div id="itemsexample"></div>' ).appendTo( "#qunit-fixture" );
|
||||
|
||||
testLog.reset();
|
||||
|
||||
viewer = OpenSeadragon( {
|
||||
id: 'itemsexample',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
});
|
||||
},
|
||||
teardown: function() {
|
||||
if ( viewer && viewer.close ) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
$( "#itemsexample" ).remove();
|
||||
}
|
||||
} );
|
||||
|
||||
// ----------
|
||||
asyncTest( 'Multi-image operations', function() {
|
||||
expect( 21 );
|
||||
viewer.addHandler( "open", function( ) {
|
||||
equal( 1, viewer.world.getItemCount( ),
|
||||
"One item should be present after opening." );
|
||||
var options = {
|
||||
tileSource: {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [ {
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
} ]
|
||||
}
|
||||
};
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function addFirstItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addFirstItemHandler );
|
||||
var item1 = event.item;
|
||||
equal( viewer.world.getItemCount( ), 2,
|
||||
"2 items should be present after adding a item." );
|
||||
equal( viewer.world.getIndexOfItem( item1 ), 1,
|
||||
"The first added item should have a index of 1" );
|
||||
equal( viewer.world.getItemAt( 1 ), item1,
|
||||
"The item at index 1 should be the first added item." );
|
||||
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function addSecondItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addSecondItemHandler );
|
||||
var item2 = event.item;
|
||||
equal( viewer.world.getItemCount( ), 3,
|
||||
"3 items should be present after adding a second item." );
|
||||
equal( viewer.world.getIndexOfItem( item2 ), 2,
|
||||
"If not specified, a item should be added with the highest index." );
|
||||
equal( viewer.world.getItemAt( 2 ), item2,
|
||||
"The item at index 2 should be the second added item." );
|
||||
|
||||
viewer.world.addHandler( "item-index-change",
|
||||
function itemIndexChangedHandler( event ) {
|
||||
viewer.world.removeHandler( "item-index-change",
|
||||
itemIndexChangedHandler );
|
||||
equal( event.item, item2,
|
||||
"The item which changed index should be item2" );
|
||||
equal( event.previousIndex, 2, "Previous index should be 2." );
|
||||
equal( event.newIndex, 1, "New index should be 1." );
|
||||
});
|
||||
viewer.world.setItemIndex( item2, 1 );
|
||||
equal( viewer.world.getIndexOfItem( item2 ), 1,
|
||||
"Item2 index should be 1 after setItemIndex." );
|
||||
equal( viewer.world.getIndexOfItem( item1 ), 2,
|
||||
"Item1 index should be 2 after setItemIndex." );
|
||||
equal( viewer.world.getItemAt( 1 ), item2,
|
||||
"The item at index 1 should be item2." );
|
||||
equal( viewer.world.getItemAt( 2 ), item1,
|
||||
"The item at index 2 should be item1." );
|
||||
|
||||
options.index = 2;
|
||||
options.tileSource.levels[0].url = "data/CCyan.png";
|
||||
viewer.addTiledImage( options );
|
||||
viewer.world.addHandler( "add-item", function addThirdItemHandler( event ) {
|
||||
viewer.world.removeHandler( "add-item", addThirdItemHandler );
|
||||
var item3 = event.item;
|
||||
equal( viewer.world.getItemCount( ), 4,
|
||||
"4 items should be present after adding a third item." );
|
||||
equal( viewer.world.getIndexOfItem( item3 ), 2,
|
||||
"Item 3 should be added with index 2." );
|
||||
equal( viewer.world.getIndexOfItem( item2 ), 1,
|
||||
"Item 2 should stay at index 1." );
|
||||
|
||||
viewer.world.addHandler( "remove-item", function removeItemHandler( event ) {
|
||||
viewer.world.removeHandler( "remove-item", removeItemHandler );
|
||||
|
||||
equal( item2, event.item, "Removed item should be item2." );
|
||||
|
||||
equal( viewer.world.getIndexOfItem( item1 ), 2,
|
||||
"Item 1 should be at index 2." );
|
||||
equal( viewer.world.getIndexOfItem( item2 ), -1,
|
||||
"Item 2 should be at index -1." );
|
||||
equal( viewer.world.getIndexOfItem( item3 ), 1,
|
||||
"Item 3 should be at index 1." );
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
viewer.world.removeItem( item2 );
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest( 'Sequences as items', function() {
|
||||
var options = {
|
||||
tileSource: [{
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/A.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}, {
|
||||
type: 'legacy-image-pyramid',
|
||||
levels: [{
|
||||
url: "data/BBlue.png",
|
||||
width: 1000,
|
||||
height: 1000
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
viewer.addHandler( "open", function openHandler() {
|
||||
viewer.removeHandler( "open", openHandler );
|
||||
|
||||
viewer.addHandler( "add-item-failed",
|
||||
function addItemFailedHandler( event ) {
|
||||
viewer.removeHandler( "add-item-failed", addItemFailedHandler );
|
||||
equal( event.message, "[Viewer.addTiledImage] Sequences can not be added; add them one at a time instead." );
|
||||
equal( event.options, options, "Item failed event should give the options." );
|
||||
start();
|
||||
} );
|
||||
viewer.addTiledImage( options );
|
||||
|
||||
});
|
||||
viewer.open( '/test/data/testpattern.dzi' );
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('items are added in order', function() {
|
||||
viewer.addHandler('open', function(event) {
|
||||
equal(viewer.world.getItemAt(0).getContentSize().y, 2000, 'first image is tall');
|
||||
equal(viewer.world.getItemAt(0).getBounds().width, 4, 'first image has 4 width');
|
||||
equal(viewer.world.getItemAt(1).getContentSize().x, 2000, 'second image is wide');
|
||||
equal(viewer.world.getItemAt(1).getBounds().width, 2, 'second image has 2 width');
|
||||
start();
|
||||
});
|
||||
|
||||
viewer.open([
|
||||
{
|
||||
tileSource: '/test/data/tall.dzi',
|
||||
width: 4
|
||||
}, {
|
||||
tileSource: '/test/data/wide.dzi',
|
||||
width: 2
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
})();
|
@ -1,6 +1,4 @@
|
||||
/* global QUnit, module, Util, $, console, test, asyncTest, start, ok, equal */
|
||||
|
||||
QUnit.config.autostart = false;
|
||||
/* global QUnit, module, Util, $, console, test, asyncTest, start, ok, equal, propEqual */
|
||||
|
||||
(function () {
|
||||
var debug = false,
|
||||
@ -31,10 +29,6 @@ QUnit.config.autostart = false;
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
start();
|
||||
});
|
||||
|
||||
var resetTestVariables = function () {
|
||||
if (viewer) {
|
||||
viewer.close();
|
||||
@ -146,7 +140,7 @@ QUnit.config.autostart = false;
|
||||
currentDisplayRegionLeft = displayRegion.position().left;
|
||||
currentDisplayWidth = displayRegion.width();
|
||||
viewerAndNavigatorDisplayReady = viewer.drawer !== null &&
|
||||
!viewer.drawer.needsUpdate() &&
|
||||
!viewer.world.needsDraw() &&
|
||||
currentDisplayWidth > 0 &&
|
||||
Util.equalsWithVariance(lastDisplayRegionLeft, currentDisplayRegionLeft, 0.0001) &&
|
||||
Util.equalsWithVariance(lastDisplayWidth, currentDisplayWidth, 0.0001) &&
|
||||
@ -166,7 +160,7 @@ QUnit.config.autostart = false;
|
||||
else {
|
||||
if (count === 40) {
|
||||
console.log("waitForViewer:" +
|
||||
viewer.drawer + ":" + viewer.drawer.needsUpdate() + ":" +
|
||||
viewer.drawer + ":" + viewer.world.needsDraw() + ":" +
|
||||
viewerAndNavigatorDisplayReady + ":" +
|
||||
lastDisplayRegionLeft + ":" + currentDisplayRegionLeft + ":" +
|
||||
lastDisplayWidth + ":" + currentDisplayWidth + ":" +
|
||||
@ -765,11 +759,11 @@ QUnit.config.autostart = false;
|
||||
var openHandler1 = function(event) {
|
||||
viewer.removeHandler('open', openHandler1);
|
||||
ok(viewer.navigator, 'navigator exists');
|
||||
viewer.navigator.addHandler('open', navOpenHandler1);
|
||||
viewer.navigator.world.addHandler('add-item', navOpenHandler1);
|
||||
};
|
||||
|
||||
var navOpenHandler1 = function(event) {
|
||||
viewer.navigator.removeHandler('open', navOpenHandler1);
|
||||
viewer.navigator.world.removeHandler('add-item', navOpenHandler1);
|
||||
equal(viewer.navigator.source, viewer.source, 'viewer and navigator have the same source');
|
||||
ok(viewer.navigator._updateRequestId, 'navigator timer is on');
|
||||
viewer.addHandler('close', closeHandler1);
|
||||
@ -785,11 +779,11 @@ QUnit.config.autostart = false;
|
||||
|
||||
var openHandler2 = function(event) {
|
||||
viewer.removeHandler('open', openHandler2);
|
||||
viewer.navigator.addHandler('open', navOpenHandler2);
|
||||
viewer.navigator.world.addHandler('add-item', navOpenHandler2);
|
||||
};
|
||||
|
||||
var navOpenHandler2 = function(event) {
|
||||
viewer.navigator.removeHandler('open', navOpenHandler2);
|
||||
viewer.navigator.world.removeHandler('add-item', navOpenHandler2);
|
||||
equal(viewer.navigator.source, viewer.source, 'viewer and navigator have the same source');
|
||||
viewer.addHandler('close', closeHandler2);
|
||||
viewer.close();
|
||||
@ -797,13 +791,56 @@ QUnit.config.autostart = false;
|
||||
|
||||
var closeHandler2 = function(event) {
|
||||
viewer.removeHandler('close', closeHandler2);
|
||||
ok(!viewer.navigator._updateRequestId, 'navigator timer is off');
|
||||
setTimeout(function() {
|
||||
ok(!viewer.navigator._updateRequestId, 'navigator timer is still off');
|
||||
ok(!viewer.navigator._updateRequestId, 'navigator timer is off');
|
||||
timeWatcher.done();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
viewer.addHandler('open', openHandler1);
|
||||
});
|
||||
|
||||
asyncTest('Item positions including collection mode', function() {
|
||||
var navAddCount = 0;
|
||||
|
||||
viewer = OpenSeadragon({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
tileSources: ['/test/data/testpattern.dzi', '/test/data/testpattern.dzi'],
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
showNavigator: true,
|
||||
collectionMode: true
|
||||
});
|
||||
|
||||
var openHandler = function() {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
viewer.navigator.world.addHandler('add-item', navOpenHandler);
|
||||
};
|
||||
|
||||
var navOpenHandler = function(event) {
|
||||
navAddCount++;
|
||||
if (navAddCount === 2) {
|
||||
viewer.navigator.world.removeHandler('add-item', navOpenHandler);
|
||||
|
||||
setTimeout(function() {
|
||||
// Test initial formation
|
||||
equal(viewer.navigator.world.getItemCount(), 2, 'navigator has both items');
|
||||
for (var i = 0; i < 2; i++) {
|
||||
propEqual(viewer.navigator.world.getItemAt(i).getBounds(),
|
||||
viewer.world.getItemAt(i).getBounds(), 'bounds are the same');
|
||||
}
|
||||
|
||||
// Try moving one
|
||||
viewer.world.getItemAt(0).setPosition(new OpenSeadragon.Point(-200, -200));
|
||||
propEqual(viewer.navigator.world.getItemAt(0).getBounds(),
|
||||
viewer.world.getItemAt(0).getBounds(), 'bounds are the same after move');
|
||||
|
||||
start();
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
viewer.addHandler('open', openHandler);
|
||||
});
|
||||
|
||||
})();
|
@ -1,4 +1,4 @@
|
||||
/* global QUnit, module, Util, $, console, test, asyncTest, start, ok, equal */
|
||||
/* global QUnit, module, Util, $, console, test, asyncTest, start, ok, equal, testLog */
|
||||
|
||||
( function() {
|
||||
var viewer;
|
||||
@ -29,7 +29,7 @@
|
||||
}
|
||||
var ready = viewer.isOpen() &&
|
||||
viewer.drawer !== null &&
|
||||
!viewer.drawer.needsUpdate() &&
|
||||
!viewer.world.needsDraw() &&
|
||||
Util.equalsWithVariance( viewer.viewport.getBounds( true ).x,
|
||||
viewer.viewport.getBounds().x, 0.000 ) &&
|
||||
Util.equalsWithVariance( viewer.viewport.getBounds( true ).y,
|
||||
@ -46,7 +46,7 @@
|
||||
}, 100 );
|
||||
} else {
|
||||
console.log( "waitForViewer:" + viewer.isOpen( ) + ":" + viewer.drawer +
|
||||
":" + viewer.drawer.needsUpdate() );
|
||||
":" + viewer.world.needsDraw() );
|
||||
handler();
|
||||
}
|
||||
}
|
||||
@ -532,4 +532,35 @@
|
||||
|
||||
} );
|
||||
|
||||
// ----------
|
||||
asyncTest('overlays appear immediately', function() {
|
||||
equal($('#immediate-overlay0').length, 0, 'overlay 0 does not exist');
|
||||
equal($('#immediate-overlay1').length, 0, 'overlay 1 does not exist');
|
||||
|
||||
viewer = OpenSeadragon( {
|
||||
id: 'example-overlays',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
tileSources: '/test/data/testpattern.dzi',
|
||||
springStiffness: 100, // Faster animation = faster tests
|
||||
overlays: [ {
|
||||
x: 0,
|
||||
y: 0,
|
||||
id: "immediate-overlay0"
|
||||
} ]
|
||||
} );
|
||||
|
||||
viewer.addHandler('open', function() {
|
||||
equal($('#immediate-overlay0').length, 1, 'overlay 0 exists');
|
||||
|
||||
viewer.addOverlay( {
|
||||
x: 0,
|
||||
y: 0,
|
||||
id: "immediate-overlay1"
|
||||
} );
|
||||
|
||||
equal($('#immediate-overlay1').length, 1, 'overlay 1 exists');
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
} )( );
|
46
test/modules/referencestrip.js
Normal file
46
test/modules/referencestrip.js
Normal file
@ -0,0 +1,46 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
|
||||
module('ReferenceStrip', {
|
||||
setup: function () {
|
||||
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
},
|
||||
teardown: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
var createViewer = function(options) {
|
||||
options = options || {};
|
||||
viewer = OpenSeadragon(OpenSeadragon.extend({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
}, options));
|
||||
};
|
||||
|
||||
// ----------
|
||||
asyncTest('basics', function() {
|
||||
createViewer({
|
||||
sequenceMode: true,
|
||||
showReferenceStrip: true,
|
||||
tileSources: [
|
||||
'/test/data/tall.dzi',
|
||||
'/test/data/wide.dzi',
|
||||
]
|
||||
});
|
||||
|
||||
ok(viewer.referenceStrip, 'referenceStrip exists');
|
||||
start();
|
||||
});
|
||||
|
||||
})();
|
111
test/modules/tilecache.js
Normal file
111
test/modules/tilecache.js
Normal file
@ -0,0 +1,111 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
(function() {
|
||||
|
||||
// ----------
|
||||
module('TileCache', {
|
||||
setup: function () {
|
||||
testLog.reset();
|
||||
},
|
||||
teardown: function () {
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('basics', function() {
|
||||
var fakeTiledImage0 = {};
|
||||
var fakeTiledImage1 = {};
|
||||
|
||||
var fakeTile0 = {
|
||||
url: 'foo.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var fakeTile1 = {
|
||||
url: 'foo.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var cache = new OpenSeadragon.TileCache();
|
||||
equal(cache.numTilesLoaded(), 0, 'no tiles to begin with');
|
||||
|
||||
cache.cacheTile({
|
||||
tile: fakeTile0,
|
||||
tiledImage: fakeTiledImage0
|
||||
});
|
||||
|
||||
equal(cache.numTilesLoaded(), 1, 'tile count after cache');
|
||||
|
||||
cache.cacheTile({
|
||||
tile: fakeTile1,
|
||||
tiledImage: fakeTiledImage1
|
||||
});
|
||||
|
||||
equal(cache.numTilesLoaded(), 2, 'tile count after second cache');
|
||||
|
||||
cache.clearTilesFor(fakeTiledImage0);
|
||||
|
||||
equal(cache.numTilesLoaded(), 1, 'tile count after first clear');
|
||||
|
||||
cache.clearTilesFor(fakeTiledImage1);
|
||||
|
||||
equal(cache.numTilesLoaded(), 0, 'tile count after second clear');
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('maxImageCacheCount', function() {
|
||||
var fakeTiledImage0 = {};
|
||||
|
||||
var fakeTile0 = {
|
||||
url: 'different.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var fakeTile1 = {
|
||||
url: 'same.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var fakeTile2 = {
|
||||
url: 'same.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var cache = new OpenSeadragon.TileCache({
|
||||
maxImageCacheCount: 1
|
||||
});
|
||||
|
||||
equal(cache.numTilesLoaded(), 0, 'no tiles to begin with');
|
||||
|
||||
cache.cacheTile({
|
||||
tile: fakeTile0,
|
||||
tiledImage: fakeTiledImage0
|
||||
});
|
||||
|
||||
equal(cache.numTilesLoaded(), 1, 'tile count after add');
|
||||
|
||||
cache.cacheTile({
|
||||
tile: fakeTile1,
|
||||
tiledImage: fakeTiledImage0
|
||||
});
|
||||
|
||||
equal(cache.numTilesLoaded(), 1, 'tile count after add of second image');
|
||||
|
||||
cache.cacheTile({
|
||||
tile: fakeTile2,
|
||||
tiledImage: fakeTiledImage0
|
||||
});
|
||||
|
||||
equal(cache.numTilesLoaded(), 2, 'tile count after additional same image');
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
})();
|
194
test/modules/tiledimage.js
Normal file
194
test/modules/tiledimage.js
Normal file
@ -0,0 +1,194 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog, propEqual */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
|
||||
module('TiledImage', {
|
||||
setup: function () {
|
||||
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
|
||||
viewer = OpenSeadragon({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
});
|
||||
},
|
||||
teardown: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
var checkBounds = function(image, expected, message) {
|
||||
var bounds = image.getBounds();
|
||||
equal(bounds.x, expected.x, message + ' x');
|
||||
equal(bounds.y, expected.y, message + ' y');
|
||||
equal(bounds.width, expected.width, message + ' width');
|
||||
equal(bounds.height, expected.height, message + ' height');
|
||||
};
|
||||
|
||||
// ----------
|
||||
asyncTest('metrics', function() {
|
||||
var handlerCount = 0;
|
||||
|
||||
viewer.addHandler('open', function(event) {
|
||||
var image = viewer.world.getItemAt(0);
|
||||
var contentSize = image.getContentSize();
|
||||
equal(contentSize.x, 500, 'contentSize.x');
|
||||
equal(contentSize.y, 2000, 'contentSize.y');
|
||||
|
||||
checkBounds(image, new OpenSeadragon.Rect(5, 6, 10, 40), 'initial bounds');
|
||||
|
||||
var scale = image.getContentSize().x / image.getBounds().width;
|
||||
var viewportPoint = new OpenSeadragon.Point(10, 11);
|
||||
var imagePoint = viewportPoint.minus(image.getBounds().getTopLeft()).times(scale);
|
||||
|
||||
propEqual(image.viewportToImageCoordinates(viewportPoint), imagePoint, 'viewportToImageCoordinates');
|
||||
propEqual(image.imageToViewportCoordinates(imagePoint), viewportPoint, 'imageToViewportCoordinates');
|
||||
|
||||
var viewportRect = new OpenSeadragon.Rect(viewportPoint.x, viewportPoint.y, 6, 7);
|
||||
var imageRect = new OpenSeadragon.Rect(imagePoint.x, imagePoint.y,
|
||||
viewportRect.width * scale, viewportRect.height * scale);
|
||||
|
||||
propEqual(image.viewportToImageRectangle(viewportRect), imageRect, 'viewportToImageRectangle');
|
||||
propEqual(image.imageToViewportRectangle(imageRect), viewportRect, 'imageToViewportRectangle');
|
||||
|
||||
image.addHandler('bounds-change', function boundsChangeHandler(event) {
|
||||
image.removeHandler('bounds-change', boundsChangeHandler);
|
||||
handlerCount++;
|
||||
});
|
||||
|
||||
image.setPosition(new OpenSeadragon.Point(7, 8));
|
||||
checkBounds(image, new OpenSeadragon.Rect(7, 8, 10, 40), 'bounds after position');
|
||||
|
||||
image.setWidth(5);
|
||||
checkBounds(image, new OpenSeadragon.Rect(7, 8, 5, 20), 'bounds after width');
|
||||
|
||||
image.setHeight(4);
|
||||
checkBounds(image, new OpenSeadragon.Rect(7, 8, 1, 4), 'bounds after width');
|
||||
|
||||
equal(handlerCount, 1, 'correct number of handlers called');
|
||||
start();
|
||||
});
|
||||
|
||||
viewer.open({
|
||||
tileSource: '/test/data/tall.dzi',
|
||||
x: 5,
|
||||
y: 6,
|
||||
width: 10
|
||||
});
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('animation', function() {
|
||||
viewer.addHandler("open", function () {
|
||||
var image = viewer.world.getItemAt(0);
|
||||
propEqual(image.getBounds(), new OpenSeadragon.Rect(0, 0, 1, 1), 'target bounds on open');
|
||||
propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds on open');
|
||||
|
||||
image.setPosition(new OpenSeadragon.Point(1, 2));
|
||||
propEqual(image.getBounds(), new OpenSeadragon.Rect(1, 2, 1, 1), 'target bounds after position');
|
||||
propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds after position');
|
||||
|
||||
image.setWidth(3);
|
||||
propEqual(image.getBounds(), new OpenSeadragon.Rect(1, 2, 3, 3), 'target bounds after width');
|
||||
propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds after width');
|
||||
|
||||
viewer.addHandler('animation-finish', function animationHandler() {
|
||||
viewer.removeHandler('animation-finish', animationHandler);
|
||||
propEqual(image.getBounds(), new OpenSeadragon.Rect(1, 2, 3, 3), 'target bounds after animation');
|
||||
propEqual(image.getBounds(true), new OpenSeadragon.Rect(1, 2, 3, 3), 'current bounds after animation');
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('update', function() {
|
||||
var handlerCount = 0;
|
||||
|
||||
viewer.addHandler('open', function(event) {
|
||||
var image = viewer.world.getItemAt(0);
|
||||
equal(image.needsDraw(), true, 'needs draw after open');
|
||||
|
||||
viewer.addHandler('update-level', function updateLevelHandler(event) {
|
||||
viewer.removeHandler('update-level', updateLevelHandler);
|
||||
handlerCount++;
|
||||
equal(event.eventSource, viewer, 'sender of update-level event was viewer');
|
||||
equal(event.tiledImage, image, 'tiledImage of update-level event is correct');
|
||||
ok('havedrawn' in event, 'update-level event includes havedrawn');
|
||||
ok('level' in event, 'update-level event includes level');
|
||||
ok('opacity' in event, 'update-level event includes opacity');
|
||||
ok('visibility' in event, 'update-level event includes visibility');
|
||||
ok('topleft' in event, 'update-level event includes topleft');
|
||||
ok('bottomright' in event, 'update-level event includes bottomright');
|
||||
ok('currenttime' in event, 'update-level event includes currenttime');
|
||||
ok('best' in event, 'update-level event includes best');
|
||||
});
|
||||
|
||||
viewer.addHandler('update-tile', function updateTileHandler(event) {
|
||||
viewer.removeHandler('update-tile', updateTileHandler);
|
||||
handlerCount++;
|
||||
equal(event.eventSource, viewer, 'sender of update-tile event was viewer');
|
||||
equal(event.tiledImage, image, 'tiledImage of update-level event is correct');
|
||||
ok(event.tile, 'update-tile event includes tile');
|
||||
});
|
||||
|
||||
viewer.addHandler('tile-drawing', function tileDrawingHandler(event) {
|
||||
viewer.removeHandler('tile-drawing', tileDrawingHandler);
|
||||
handlerCount++;
|
||||
equal(event.eventSource, viewer, 'sender of tile-drawing event was viewer');
|
||||
equal(event.tiledImage, image, 'tiledImage of update-level event is correct');
|
||||
ok(event.tile, 'tile-drawing event includes a tile');
|
||||
ok(event.context, 'tile-drawing event includes a context');
|
||||
ok(event.rendered, 'tile-drawing event includes a rendered');
|
||||
});
|
||||
|
||||
viewer.addHandler('tile-drawn', function tileDrawnHandler(event) {
|
||||
viewer.removeHandler('tile-drawn', tileDrawnHandler);
|
||||
handlerCount++;
|
||||
equal(event.eventSource, viewer, 'sender of tile-drawn event was viewer');
|
||||
equal(event.tiledImage, image, 'tiledImage of update-level event is correct');
|
||||
ok(event.tile, 'tile-drawn event includes tile');
|
||||
|
||||
equal(handlerCount, 4, 'correct number of handlers called');
|
||||
start();
|
||||
});
|
||||
|
||||
image.draw();
|
||||
});
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('reset', function() {
|
||||
viewer.addHandler('tile-drawn', function updateHandler() {
|
||||
viewer.removeHandler('tile-drawn', updateHandler);
|
||||
ok(viewer.tileCache.numTilesLoaded() > 0, 'we have tiles after tile-drawn');
|
||||
viewer.world.getItemAt(0).reset();
|
||||
equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles after reset');
|
||||
|
||||
viewer.addHandler('tile-drawn', function updateHandler2() {
|
||||
viewer.removeHandler('tile-drawn', updateHandler2);
|
||||
ok(viewer.tileCache.numTilesLoaded() > 0, 'more tiles load');
|
||||
viewer.world.getItemAt(0).destroy();
|
||||
equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles after destroy');
|
||||
start();
|
||||
});
|
||||
});
|
||||
|
||||
equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles at start');
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
})();
|
20
test/modules/tilesourcecollection.js
Normal file
20
test/modules/tilesourcecollection.js
Normal file
@ -0,0 +1,20 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
|
||||
module('TileSourceCollection', {
|
||||
setup: function () {
|
||||
testLog.reset();
|
||||
},
|
||||
teardown: function () {
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('deprecation', function() {
|
||||
Util.testDeprecation(OpenSeadragon, 'TileSourceCollection');
|
||||
start();
|
||||
});
|
||||
|
||||
})();
|
@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
var viewer;
|
||||
var VIEWER_ID = "example";
|
||||
var PREFIX_URL = "/build/openseadragon/images";
|
||||
var PREFIX_URL = "/build/openseadragon/images/";
|
||||
var SPRING_STIFFNESS = 100; // Faster animation = faster tests
|
||||
|
||||
module("viewport", {
|
||||
@ -209,7 +209,7 @@
|
||||
method: 'getHomeBounds',
|
||||
processExpected: function(level, expected) {
|
||||
// Have to special case this to avoid dividing by 0
|
||||
if(level === 0){
|
||||
if(level === -1 || level === 0){
|
||||
expected = new OpenSeadragon.Rect(0, 0, 1, 1);
|
||||
} else {
|
||||
var sideLength = 1.0 / viewer.defaultZoomLevel; // it's a square in this case
|
||||
@ -245,8 +245,10 @@
|
||||
viewport.zoomTo(ZOOM_FACTOR, null, true);
|
||||
viewport.update(); // need to call this even with immediately=true
|
||||
|
||||
// If the default zoom level is set to 0, then we expect the home zoom to be 1.
|
||||
if(level === 0){
|
||||
// Special cases for oddball levels
|
||||
if (level === -1) {
|
||||
expected = 0.25;
|
||||
} else if(level === 0){
|
||||
expected = 1;
|
||||
}
|
||||
|
||||
@ -285,11 +287,11 @@
|
||||
|
||||
for(var i = 0; i < testRects.length; i++){
|
||||
var rect = testRects[i].times(viewport.getContainerSize());
|
||||
viewport.resetContentSize(rect);
|
||||
viewport.resetContentSize(rect.getSize());
|
||||
viewport.update();
|
||||
propEqual(
|
||||
viewport.contentSize,
|
||||
rect,
|
||||
rect.getSize(),
|
||||
"Reset content size correctly."
|
||||
);
|
||||
}
|
||||
@ -407,7 +409,7 @@
|
||||
viewport.update();
|
||||
propEqual(
|
||||
viewport.getBounds(),
|
||||
new OpenSeadragon.Rect(-1.5,0,4,4),
|
||||
new OpenSeadragon.Rect(0, 1.5, 1, 1),
|
||||
"Viewport fit a tall image horizontally."
|
||||
);
|
||||
start();
|
||||
@ -424,7 +426,7 @@
|
||||
viewport.update();
|
||||
propEqual(
|
||||
viewport.getBounds(),
|
||||
new OpenSeadragon.Rect(0,0,0.25,0.25),
|
||||
new OpenSeadragon.Rect(0.375, 0, 0.25, 0.25),
|
||||
"Viewport fit a wide image vertically."
|
||||
);
|
||||
start();
|
||||
@ -719,9 +721,7 @@
|
||||
return el.times(window_boundary);
|
||||
},
|
||||
getExpected: function(orig, viewport) {
|
||||
var position, pos_point;
|
||||
position = viewer.element.getBoundingClientRect();
|
||||
pos_point = new OpenSeadragon.Point(position.top, position.left);
|
||||
var pos_point = OpenSeadragon.getElementOffset(viewer.element);
|
||||
return orig.minus(pos_point).divide(viewport.getContainerSize().x * ZOOM_FACTOR).plus(VIEWER_PADDING);
|
||||
},
|
||||
method: 'windowToViewportCoordinates'
|
||||
@ -735,9 +735,7 @@
|
||||
return el.times(viewer.source.dimensions.x);
|
||||
},
|
||||
getExpected: function(orig, viewport) {
|
||||
var position, pos_point;
|
||||
position = viewer.element.getBoundingClientRect();
|
||||
pos_point = new OpenSeadragon.Point(position.top, position.left);
|
||||
var pos_point = OpenSeadragon.getElementOffset(viewer.element);
|
||||
return orig.plus(pos_point).minus(VIEWER_PADDING.times(viewport.getContainerSize().x * ZOOM_FACTOR));
|
||||
},
|
||||
method: 'imageToWindowCoordinates'
|
||||
@ -752,9 +750,7 @@
|
||||
return el.times(window_boundary);
|
||||
},
|
||||
getExpected: function(orig, viewport) {
|
||||
var position, pos_point;
|
||||
position = viewer.element.getBoundingClientRect();
|
||||
pos_point = new OpenSeadragon.Point(position.top, position.left);
|
||||
var pos_point = OpenSeadragon.getElementOffset(viewer.element);
|
||||
return orig.minus(pos_point).divide(viewport.getContainerSize().x * ZOOM_FACTOR).plus(VIEWER_PADDING);
|
||||
},
|
||||
method: 'windowToViewportCoordinates'
|
||||
@ -768,9 +764,7 @@
|
||||
return el.times(viewer.source.dimensions.x);
|
||||
},
|
||||
getExpected: function(orig, viewport) {
|
||||
var position, pos_point;
|
||||
position = viewer.element.getBoundingClientRect();
|
||||
pos_point = new OpenSeadragon.Point(position.top, position.left);
|
||||
var pos_point = OpenSeadragon.getElementOffset(viewer.element);
|
||||
return orig.minus(VIEWER_PADDING).times(viewport.getContainerSize().x * ZOOM_FACTOR).plus(pos_point);
|
||||
},
|
||||
method: 'viewportToWindowCoordinates'
|
226
test/modules/world.js
Normal file
226
test/modules/world.js
Normal file
@ -0,0 +1,226 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
|
||||
module('World', {
|
||||
setup: function () {
|
||||
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
|
||||
viewer = OpenSeadragon({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
});
|
||||
},
|
||||
teardown: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
var checkBounds = function(expected, message) {
|
||||
var bounds = viewer.world.getHomeBounds();
|
||||
ok(bounds.equals(expected), message + ' ' + bounds.toString());
|
||||
};
|
||||
|
||||
// ----------
|
||||
asyncTest('adding a tiled image', function() {
|
||||
ok(viewer.world, 'World exists');
|
||||
|
||||
viewer.world.addHandler('add-item', function(event) {
|
||||
ok(event, 'add-item handler received event data');
|
||||
equal(event.eventSource, viewer.world, 'sender of add-item event was world');
|
||||
ok(event.item, 'add-item event includes item');
|
||||
equal(viewer.world.getItemCount(), 1, 'there is now 1 item');
|
||||
equal(event.item, viewer.world.getItemAt(0), 'item is accessible via getItemAt');
|
||||
equal(viewer.world.getIndexOfItem(event.item), 0, 'item index is 0');
|
||||
start();
|
||||
});
|
||||
|
||||
equal(viewer.world.getItemCount(), 0, 'no items to start with');
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('metrics', function() {
|
||||
viewer.addHandler('open', function(event) {
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 4, 4), 'bounds after open');
|
||||
|
||||
var expectedContentFactor = viewer.world.getItemAt(1).getContentSize().x / 2;
|
||||
equal(viewer.world.getContentFactor(), expectedContentFactor, 'content factor has changed');
|
||||
|
||||
viewer.world.addHandler('metrics-change', function metricsChangeHandler(event) {
|
||||
viewer.world.removeHandler('metrics-change', metricsChangeHandler);
|
||||
ok(event, 'metrics-change handler received event data');
|
||||
equal(event.eventSource, viewer.world, 'sender of metrics-change event was world');
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 7, 12), 'bounds after position');
|
||||
viewer.world.getItemAt(0).setWidth(20);
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 20, 20), 'bounds after size');
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
viewer.world.getItemAt(1).setPosition(new OpenSeadragon.Point(5, 10));
|
||||
});
|
||||
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 1, 1), 'default bounds');
|
||||
equal(viewer.world.getContentFactor(), 1, 'default content factor');
|
||||
|
||||
viewer.open([
|
||||
{
|
||||
tileSource: '/test/data/testpattern.dzi',
|
||||
width: 4
|
||||
}, {
|
||||
tileSource: '/test/data/testpattern.dzi',
|
||||
width: 2
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('remove/reorder tiled images', function() {
|
||||
var handlerCount = 0;
|
||||
|
||||
viewer.addHandler('open', function(event) {
|
||||
equal(viewer.world.getItemCount(), 3, 'there are now 3 items');
|
||||
var item0 = viewer.world.getItemAt(0);
|
||||
var item1 = viewer.world.getItemAt(1);
|
||||
|
||||
viewer.world.addHandler('item-index-change', function(event) {
|
||||
handlerCount++;
|
||||
ok(event, 'item-index-change handler received event data');
|
||||
equal(event.eventSource, viewer.world, 'sender of item-index-change event was world');
|
||||
equal(event.item, item0, 'item-index-change event includes correct item');
|
||||
equal(event.newIndex, 1, 'item-index-change event includes correct newIndex');
|
||||
equal(event.previousIndex, 0, 'item-index-change event includes correct previousIndex');
|
||||
equal(viewer.world.getItemAt(0), item1, 'item1 is now at index 0');
|
||||
equal(viewer.world.getItemAt(1), item0, 'item0 is now at index 1');
|
||||
});
|
||||
|
||||
viewer.world.setItemIndex(item0, 1);
|
||||
|
||||
viewer.world.addHandler('remove-item', function removeHandler(event) {
|
||||
viewer.world.removeHandler('remove-item', removeHandler);
|
||||
handlerCount++;
|
||||
ok(event, 'remove-item handler received event data');
|
||||
equal(event.eventSource, viewer.world, 'sender of remove-item event was world');
|
||||
equal(event.item, item1, 'remove-item event includes correct item');
|
||||
equal(viewer.world.getItemCount(), 2, 'after removal, only two items remain');
|
||||
equal(viewer.world.getItemAt(0), item0, 'item0 is now at index 0');
|
||||
});
|
||||
|
||||
viewer.world.removeItem(item1);
|
||||
|
||||
var removeCount = 0;
|
||||
viewer.world.addHandler('remove-item', function() {
|
||||
removeCount++;
|
||||
if (removeCount === 2) {
|
||||
handlerCount++;
|
||||
equal(viewer.world.getItemCount(), 0, 'after removeAll, no items remain');
|
||||
}
|
||||
});
|
||||
|
||||
viewer.world.removeAll();
|
||||
|
||||
equal(handlerCount, 3, 'correct number of handlers called');
|
||||
start();
|
||||
});
|
||||
|
||||
equal(viewer.world.getItemCount(), 0, 'no items to start with');
|
||||
|
||||
viewer.open([
|
||||
'/test/data/testpattern.dzi',
|
||||
'/test/data/testpattern.dzi',
|
||||
'/test/data/testpattern.dzi'
|
||||
]);
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('draw', function() {
|
||||
var handlerCount = 0;
|
||||
|
||||
viewer.addHandler('open', function(event) {
|
||||
equal(viewer.world.needsDraw(), true, 'needs draw after open');
|
||||
|
||||
viewer.addHandler('update-level', function updateHandler() {
|
||||
viewer.removeHandler('update-level', updateHandler);
|
||||
handlerCount++;
|
||||
});
|
||||
|
||||
viewer.world.draw();
|
||||
|
||||
equal(handlerCount, 1, 'correct number of handlers called');
|
||||
start();
|
||||
});
|
||||
|
||||
equal(viewer.world.needsDraw(), false, 'needs no draw at first');
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('resetItems', function() {
|
||||
viewer.addHandler('tile-drawn', function updateHandler() {
|
||||
viewer.removeHandler('tile-drawn', updateHandler);
|
||||
ok(viewer.tileCache.numTilesLoaded() > 0, 'we have tiles after tile-drawn');
|
||||
viewer.world.resetItems();
|
||||
equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles after reset');
|
||||
start();
|
||||
});
|
||||
|
||||
equal(viewer.tileCache.numTilesLoaded(), 0, 'no tiles at start');
|
||||
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
// ----------
|
||||
asyncTest('arrange', function() {
|
||||
viewer.addHandler('open', function(event) {
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 1, 1), 'all stacked');
|
||||
|
||||
viewer.world.arrange({
|
||||
layout: 'horizontal',
|
||||
rows: 1,
|
||||
tileSize: 1,
|
||||
tileMargin: 0.5
|
||||
});
|
||||
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 4, 1), 'one horizontal row');
|
||||
|
||||
viewer.world.arrange({
|
||||
layout: 'horizontal',
|
||||
rows: 2,
|
||||
tileSize: 1,
|
||||
tileMargin: 0.5
|
||||
});
|
||||
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 2.5, 2.5), 'grid');
|
||||
|
||||
viewer.world.arrange({
|
||||
layout: 'vertical',
|
||||
rows: 1,
|
||||
tileSize: 1,
|
||||
tileMargin: 0.5
|
||||
});
|
||||
|
||||
checkBounds(new OpenSeadragon.Rect(0, 0, 1, 4), 'one vertical column');
|
||||
|
||||
start();
|
||||
});
|
||||
|
||||
viewer.open([
|
||||
'/test/data/testpattern.dzi',
|
||||
'/test/data/testpattern.dzi',
|
||||
'/test/data/testpattern.dzi'
|
||||
]);
|
||||
});
|
||||
|
||||
})();
|
@ -5,7 +5,7 @@
|
||||
<title>OpenSeadragon QUnit</title>
|
||||
<link rel="stylesheet" href="/node_modules/qunitjs/qunit/qunit.css">
|
||||
<link rel="stylesheet" href="/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css">
|
||||
<link rel="stylesheet" href="/test/test.css">
|
||||
<link rel="stylesheet" href="/test/helpers/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="qunit"></div>
|
||||
@ -14,23 +14,31 @@
|
||||
<script src="/test/lib/jquery-1.9.1.min.js"></script>
|
||||
<script src="/test/lib/jquery-ui-1.10.2/js/jquery-ui-1.10.2.min.js"></script>
|
||||
<script src="/test/lib/jquery.simulate.js"></script>
|
||||
<script src="/build/openseadragon/openseadragon.min.js"></script>
|
||||
<script src="/test/legacy.mouse.shim.js"></script>
|
||||
<script src="/test/test.js"></script>
|
||||
<script src="/build/openseadragon/openseadragon.js"></script>
|
||||
<script src="/test/helpers/legacy.mouse.shim.js"></script>
|
||||
<script src="/test/helpers/test.js"></script>
|
||||
|
||||
<!-- Polyfill must be inserted first because it is testing functions
|
||||
reassignments which could be done by other test. -->
|
||||
<script src="/test/polyfills.js"></script>
|
||||
<script src="/test/basic.js"></script>
|
||||
<script src="/test/navigator.js"></script>
|
||||
<script src="/test/strings.js"></script>
|
||||
<script src="/test/formats.js"></script>
|
||||
<script src="/test/utils.js"></script>
|
||||
<script src="/test/events.js"></script>
|
||||
<script src="/test/units.js"></script>
|
||||
<script src="/test/layers.js"></script>
|
||||
<script src="/test/overlays.js"></script>
|
||||
<script src="/test/controls.js"></script>
|
||||
<script src="/test/viewport.js"></script>
|
||||
<script src="/test/modules/polyfills.js"></script>
|
||||
<script src="/test/modules/basic.js"></script>
|
||||
<script src="/test/modules/strings.js"></script>
|
||||
<script src="/test/modules/formats.js"></script>
|
||||
<script src="/test/modules/utils.js"></script>
|
||||
<script src="/test/modules/events.js"></script>
|
||||
<script src="/test/modules/units.js"></script>
|
||||
<script src="/test/modules/multi-image.js"></script>
|
||||
<script src="/test/modules/overlays.js"></script>
|
||||
<script src="/test/modules/controls.js"></script>
|
||||
<script src="/test/modules/viewport.js"></script>
|
||||
<script src="/test/modules/world.js"></script>
|
||||
<script src="/test/modules/drawer.js"></script>
|
||||
<script src="/test/modules/tiledimage.js"></script>
|
||||
<script src="/test/modules/tilecache.js"></script>
|
||||
<script src="/test/modules/referencestrip.js"></script>
|
||||
<script src="/test/modules/tilesourcecollection.js"></script>
|
||||
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
|
||||
so we put them last. -->
|
||||
<script src="/test/modules/navigator.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
x
Reference in New Issue
Block a user