Merge conflict resolved
@ -1,9 +1,14 @@
|
||||
OPENSEADRAGON CHANGELOG
|
||||
=======================
|
||||
|
||||
2.2.2: (in progress)
|
||||
2.3.0: (in progress)
|
||||
|
||||
* BREAKING CHANGE: Tile.distance has been removed (#1027)
|
||||
* BREAKING CHANGE: Viewer's canvas-click event is now fired before it initiates the zoom (#1148)
|
||||
* BREAKING CHANGE: Viewer's canvas-drag event is now fired before it pans (#1149)
|
||||
* Optimization: Use the squared distance when comparing tiles (#1027)
|
||||
* You can now prevent canvas-click events from zooming on a per-event basis (#1148)
|
||||
* You can now prevent canvas-drag events from panning on a per-event basis (#1149)
|
||||
* You can now set the rotation of individual tiled images (#1006)
|
||||
* Fixed CORS bug in IE 10 (#967)
|
||||
* Added support for commonjs (#984)
|
||||
@ -15,7 +20,6 @@ OPENSEADRAGON CHANGELOG
|
||||
* Fixed: Initial tile load wasn't happening in parallel (#1014)
|
||||
* Added Zoomify tile source (#863)
|
||||
* Fixed problem with "sparse image" DZI files (#995)
|
||||
* Optimization: Use the squared distance when comparing tiles (#1027)
|
||||
* Fix IndexSizeError on IE and Edge that occurred under certain circumstances (e.g. multi-image with transparency) (#1035)
|
||||
* ImageTileSource now works in IE8 (#1041)
|
||||
* LegacyTileSource now allows any image URLs regardless of type (#1056)
|
||||
@ -28,6 +32,26 @@ OPENSEADRAGON CHANGELOG
|
||||
* Fixed an issue causing overlays to be mis-positioned in some circumstances (#1119)
|
||||
* Fixed: ImageTileSource would sometimes produce a double image (#1123)
|
||||
* Fixed: console.debug caused exceptions on IE10 (#1129)
|
||||
* Better compression for our UI images (#1134)
|
||||
* Fixed: the reference strip would leak memory when opening new sets of images (#1175)
|
||||
* You can now load tiles via AJAX and custom AJAX request headers (#1055)
|
||||
* Added fix for supporting weird filenames that look like JSONs (#1189)
|
||||
* Fixed: zoomTo/zoomBy ignore refPoint if immediately is true (#1184)
|
||||
* Enabled configuration of ImageLoader timeout (#1192)
|
||||
* Viewer.open() now supports an initialPage argument for sequenceMode (#1196)
|
||||
* Fixed: IIPImageServer didn't work with the latest OSD release (#1199)
|
||||
* Now clamping pixel ratio density to a minimum of 1, fixing display issues on low density devices (#1200)
|
||||
* Improved calculation for determining which level to load first (#1198)
|
||||
* Fixed: setItemIndex method not working with navigator inside "open" event (#1201)
|
||||
* The navigator now picks up opacity and compositeOperation changes (#1203)
|
||||
* New events for opacity and compositeOperation changes (#1203)
|
||||
* Fixed: The reference strip didn't show the initial page if it wasn't the first page (#1208)
|
||||
* Added support for setting debug mode after the Viewer object has been constructed (#1224)
|
||||
* Fixed: Sometimes the image would stick to the mouse when right-clicking and left-clicking simultaneously (#1223)
|
||||
* Fixed issue with transparent images sometimes disappearing on Safari (#1222)
|
||||
* Better calculation for TileCache release cutoff (#1214)
|
||||
* Fixed: One image failing to load could cause the others to never load (#1229)
|
||||
* Added functions for dynamically adding and removing the reference strip in sequence mode (#1213)
|
||||
|
||||
2.2.1:
|
||||
|
||||
|
BIN
images/button_grouphover.png
Executable file
After Width: | Height: | Size: 1.5 KiB |
BIN
images/button_hover.png
Executable file
After Width: | Height: | Size: 1.8 KiB |
BIN
images/button_pressed.png
Executable file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/button_rest.png
Executable file
After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 977 B |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
psd/button.psd
Executable file
@ -426,22 +426,22 @@ $.Drawer.prototype = {
|
||||
this.context.globalCompositeOperation = compositeOperation;
|
||||
}
|
||||
if (bounds) {
|
||||
// Internet Explorer and Microsoft Edge throw IndexSizeError
|
||||
// Internet Explorer, Microsoft Edge, and Safari have problems
|
||||
// when you call context.drawImage with negative x or y
|
||||
// or width or height greater than the canvas width or height respectively
|
||||
// or x + width or y + height greater than the canvas width or height respectively.
|
||||
if (bounds.x < 0) {
|
||||
bounds.width += bounds.x;
|
||||
bounds.x = 0;
|
||||
}
|
||||
if (bounds.width > this.canvas.width) {
|
||||
bounds.width = this.canvas.width;
|
||||
if (bounds.x + bounds.width > this.canvas.width) {
|
||||
bounds.width = this.canvas.width - bounds.x;
|
||||
}
|
||||
if (bounds.y < 0) {
|
||||
bounds.height += bounds.y;
|
||||
bounds.y = 0;
|
||||
}
|
||||
if (bounds.height > this.canvas.height) {
|
||||
bounds.height = this.canvas.height;
|
||||
if (bounds.y + bounds.height > this.canvas.height) {
|
||||
bounds.height = this.canvas.height - bounds.y;
|
||||
}
|
||||
|
||||
this.context.drawImage(
|
||||
|
@ -140,7 +140,7 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
|
||||
|
||||
if (url && !options.tilesUrl) {
|
||||
options.tilesUrl = url.replace(
|
||||
/([^\/]+?)(\.(dzi|xml|js))?(\?[^\/]*)?\/?$/, '$1_files/');
|
||||
/([^\/]+?)(\.(dzi|xml|js)?(\?[^\/]*)?)?\/?$/, '$1_files/');
|
||||
|
||||
if (url.search(/\.(dzi|xml|js)\?/) != -1) {
|
||||
options.queryParams = url.match(/\?.*/);
|
||||
|
@ -32,15 +32,27 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
(function($){
|
||||
|
||||
// private class
|
||||
function ImageJob ( options ) {
|
||||
/**
|
||||
* @private
|
||||
* @class ImageJob
|
||||
* @classdesc Handles downloading of a single image.
|
||||
* @param {Object} options - Options for this ImageJob.
|
||||
* @param {String} [options.src] - URL of image to download.
|
||||
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
||||
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
|
||||
* @param {Function} [options.callback] - Called once image has been downloaded.
|
||||
* @param {Function} [options.abort] - Called when this image job is aborted.
|
||||
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
|
||||
*/
|
||||
function ImageJob (options) {
|
||||
|
||||
$.extend( true, this, {
|
||||
$.extend(true, this, {
|
||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||
jobId: null
|
||||
}, options );
|
||||
}, options);
|
||||
|
||||
/**
|
||||
* Image object which will contain downloaded image.
|
||||
@ -52,42 +64,103 @@ function ImageJob ( options ) {
|
||||
|
||||
ImageJob.prototype = {
|
||||
errorMsg: null,
|
||||
|
||||
/**
|
||||
* Starts the image job.
|
||||
* @method
|
||||
*/
|
||||
start: function(){
|
||||
var _this = this;
|
||||
var self = this;
|
||||
var selfAbort = this.abort;
|
||||
|
||||
this.image = new Image();
|
||||
|
||||
if ( this.crossOriginPolicy !== false ) {
|
||||
this.image.onload = function(){
|
||||
self.finish(true);
|
||||
};
|
||||
this.image.onabort = this.image.onerror = function() {
|
||||
self.errorMsg = "Image load aborted";
|
||||
self.finish(false);
|
||||
};
|
||||
|
||||
this.jobId = window.setTimeout(function(){
|
||||
self.errorMsg = "Image load exceeded timeout";
|
||||
self.finish(false);
|
||||
}, this.timeout);
|
||||
|
||||
// Load the tile with an AJAX request if the loadWithAjax option is
|
||||
// set. Otherwise load the image by setting the source proprety of the image object.
|
||||
if (this.loadWithAjax) {
|
||||
this.request = $.makeAjaxRequest({
|
||||
url: this.src,
|
||||
withCredentials: this.ajaxWithCredentials,
|
||||
headers: this.ajaxHeaders,
|
||||
responseType: "arraybuffer",
|
||||
success: function(request) {
|
||||
var blb;
|
||||
// Make the raw data into a blob.
|
||||
// BlobBuilder fallback adapted from
|
||||
// http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility
|
||||
try {
|
||||
blb = new window.Blob([request.response]);
|
||||
} catch (e) {
|
||||
var BlobBuilder = (
|
||||
window.BlobBuilder ||
|
||||
window.WebKitBlobBuilder ||
|
||||
window.MozBlobBuilder ||
|
||||
window.MSBlobBuilder
|
||||
);
|
||||
if (e.name === 'TypeError' && BlobBuilder) {
|
||||
var bb = new BlobBuilder();
|
||||
bb.append(request.response);
|
||||
blb = bb.getBlob();
|
||||
}
|
||||
}
|
||||
// If the blob is empty for some reason consider the image load a failure.
|
||||
if (blb.size === 0) {
|
||||
self.errorMsg = "Empty image response.";
|
||||
self.finish(false);
|
||||
}
|
||||
// Create a URL for the blob data and make it the source of the image object.
|
||||
// This will still trigger Image.onload to indicate a successful tile load.
|
||||
var url = (window.URL || window.webkitURL).createObjectURL(blb);
|
||||
self.image.src = url;
|
||||
},
|
||||
error: function(request) {
|
||||
self.errorMsg = "Image load aborted - XHR error";
|
||||
self.finish(false);
|
||||
}
|
||||
});
|
||||
|
||||
// Provide a function to properly abort the request.
|
||||
this.abort = function() {
|
||||
self.request.abort();
|
||||
|
||||
// Call the existing abort function if available
|
||||
if (typeof selfAbort === "function") {
|
||||
selfAbort();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
if (this.crossOriginPolicy !== false) {
|
||||
this.image.crossOrigin = this.crossOriginPolicy;
|
||||
}
|
||||
|
||||
this.image.onload = function(){
|
||||
_this.finish( true );
|
||||
};
|
||||
this.image.onabort = this.image.onerror = function(){
|
||||
_this.errorMsg = "Image load aborted";
|
||||
_this.finish( false );
|
||||
};
|
||||
|
||||
this.jobId = window.setTimeout( function(){
|
||||
_this.errorMsg = "Image load exceeded timeout";
|
||||
_this.finish( false );
|
||||
}, this.timeout);
|
||||
|
||||
this.image.src = this.src;
|
||||
}
|
||||
},
|
||||
|
||||
finish: function( successful ) {
|
||||
finish: function(successful) {
|
||||
this.image.onload = this.image.onerror = this.image.onabort = null;
|
||||
if (!successful) {
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
if ( this.jobId ) {
|
||||
window.clearTimeout( this.jobId );
|
||||
if (this.jobId) {
|
||||
window.clearTimeout(this.jobId);
|
||||
}
|
||||
|
||||
this.callback( this );
|
||||
this.callback(this);
|
||||
}
|
||||
|
||||
};
|
||||
@ -99,14 +172,16 @@ ImageJob.prototype = {
|
||||
* 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.
|
||||
* @param {Number} [options.timeout] - The max number of milliseconds that an image job may take to complete.
|
||||
*/
|
||||
$.ImageLoader = function( options ) {
|
||||
$.ImageLoader = function(options) {
|
||||
|
||||
$.extend( true, this, {
|
||||
$.extend(true, this, {
|
||||
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||
jobQueue: [],
|
||||
jobsInProgress: 0
|
||||
}, options );
|
||||
}, options);
|
||||
|
||||
};
|
||||
|
||||
@ -116,22 +191,32 @@ $.ImageLoader.prototype = {
|
||||
/**
|
||||
* Add an unloaded image to the loader queue.
|
||||
* @method
|
||||
* @param {String} src - URL of image to download.
|
||||
* @param {String} crossOriginPolicy - CORS policy to use for downloads
|
||||
* @param {Function} callback - Called once image has been downloaded.
|
||||
* @param {Object} options - Options for this job.
|
||||
* @param {String} [options.src] - URL of image to download.
|
||||
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
||||
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
|
||||
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
|
||||
* requests.
|
||||
* @param {Function} [options.callback] - Called once image has been downloaded.
|
||||
* @param {Function} [options.abort] - Called when this image job is aborted.
|
||||
*/
|
||||
addJob: function( options ) {
|
||||
addJob: function(options) {
|
||||
var _this = this,
|
||||
complete = function( job ) {
|
||||
completeJob( _this, job, options.callback );
|
||||
complete = function(job) {
|
||||
completeJob(_this, job, options.callback);
|
||||
},
|
||||
jobOptions = {
|
||||
src: options.src,
|
||||
loadWithAjax: options.loadWithAjax,
|
||||
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
|
||||
crossOriginPolicy: options.crossOriginPolicy,
|
||||
ajaxWithCredentials: options.ajaxWithCredentials,
|
||||
callback: complete,
|
||||
abort: options.abort
|
||||
abort: options.abort,
|
||||
timeout: this.timeout
|
||||
},
|
||||
newJob = new ImageJob( jobOptions );
|
||||
newJob = new ImageJob(jobOptions);
|
||||
|
||||
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
|
||||
newJob.start();
|
||||
@ -166,18 +251,18 @@ $.ImageLoader.prototype = {
|
||||
* @param job - The ImageJob that has completed.
|
||||
* @param callback - Called once cleanup is finished.
|
||||
*/
|
||||
function completeJob( loader, job, callback ) {
|
||||
function completeJob(loader, job, callback) {
|
||||
var nextJob;
|
||||
|
||||
loader.jobsInProgress--;
|
||||
|
||||
if ( (!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
|
||||
if ((!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
|
||||
nextJob = loader.jobQueue.shift();
|
||||
nextJob.start();
|
||||
loader.jobsInProgress++;
|
||||
}
|
||||
|
||||
callback( job.image, job.errorMsg );
|
||||
callback(job.image, job.errorMsg, job.request);
|
||||
}
|
||||
|
||||
}( OpenSeadragon ));
|
||||
}(OpenSeadragon));
|
||||
|
@ -2894,6 +2894,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// A primary mouse button may have been released while the non-primary button was down
|
||||
if (pointsList.contacts > 0 && pointsList.type === 'mouse') {
|
||||
// Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223
|
||||
pointsList.contacts--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -231,8 +231,10 @@ $.Navigator = function( options ){
|
||||
});
|
||||
|
||||
viewer.world.addHandler("item-index-change", function(event) {
|
||||
window.setTimeout(function(){
|
||||
var item = _this.world.getItemAt(event.previousIndex);
|
||||
_this.world.setItemIndex(item, event.newIndex);
|
||||
}, 1);
|
||||
});
|
||||
|
||||
viewer.world.addHandler("remove-item", function(event) {
|
||||
@ -345,8 +347,18 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
_this._matchBounds(myItem, original);
|
||||
}
|
||||
|
||||
function matchOpacity() {
|
||||
_this._matchOpacity(myItem, original);
|
||||
}
|
||||
|
||||
function matchCompositeOperation() {
|
||||
_this._matchCompositeOperation(myItem, original);
|
||||
}
|
||||
|
||||
original.addHandler('bounds-change', matchBounds);
|
||||
original.addHandler('clip-change', matchBounds);
|
||||
original.addHandler('opacity-change', matchOpacity);
|
||||
original.addHandler('composite-operation-change', matchCompositeOperation);
|
||||
}
|
||||
});
|
||||
|
||||
@ -374,6 +386,16 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
||||
myItem.setWidth(bounds.width, immediately);
|
||||
myItem.setRotation(theirItem.getRotation(), immediately);
|
||||
myItem.setClip(theirItem.getClip());
|
||||
},
|
||||
|
||||
// private
|
||||
_matchOpacity: function(myItem, theirItem) {
|
||||
myItem.setOpacity(theirItem.opacity);
|
||||
},
|
||||
|
||||
// private
|
||||
_matchCompositeOperation: function(myItem, theirItem) {
|
||||
myItem.setCompositeOperation(theirItem.compositeOperation);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -411,6 +411,7 @@
|
||||
* The max number of images we should keep in memory (per drawer).
|
||||
*
|
||||
* @property {Number} [timeout=30000]
|
||||
* The max number of milliseconds that an image job may take to complete.
|
||||
*
|
||||
* @property {Boolean} [useCanvas=true]
|
||||
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
|
||||
@ -584,9 +585,16 @@
|
||||
* 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).
|
||||
* Whether to set the withCredentials XHR flag for AJAX requests.
|
||||
* Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
|
||||
*
|
||||
* @property {Boolean} [loadTilesWithAjax=false]
|
||||
* Whether to load tile data using AJAX requests.
|
||||
* Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
|
||||
*
|
||||
* @property {Object} [ajaxHeaders={}]
|
||||
* A set of headers to include when making AJAX requests for tile sources or tiles.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -867,7 +875,8 @@ function OpenSeadragon( options ){
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* A ratio comparing the device screen's pixel density to the canvas's backing store pixel density,
|
||||
* clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.
|
||||
* @member {Number} pixelDensityRatio
|
||||
* @memberof OpenSeadragon
|
||||
*/
|
||||
@ -880,7 +889,7 @@ function OpenSeadragon( options ){
|
||||
context.msBackingStorePixelRatio ||
|
||||
context.oBackingStorePixelRatio ||
|
||||
context.backingStorePixelRatio || 1;
|
||||
return devicePixelRatio / backingStoreRatio;
|
||||
return Math.max(devicePixelRatio, 1) / backingStoreRatio;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
@ -1005,6 +1014,8 @@ function OpenSeadragon( options ){
|
||||
initialPage: 0,
|
||||
crossOriginPolicy: false,
|
||||
ajaxWithCredentials: false,
|
||||
loadTilesWithAjax: false,
|
||||
ajaxHeaders: {},
|
||||
|
||||
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
|
||||
panHorizontal: true,
|
||||
@ -2120,11 +2131,16 @@ function OpenSeadragon( 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 {Object} options.headers - headers to add to the AJAX request
|
||||
* @param {String} options.responseType - the response type of the the AJAX request
|
||||
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
|
||||
* @throws {Error}
|
||||
* @returns {XMLHttpRequest}
|
||||
*/
|
||||
makeAjaxRequest: function( url, onSuccess, onError ) {
|
||||
var withCredentials;
|
||||
var headers;
|
||||
var responseType;
|
||||
|
||||
// Note that our preferred API is that you pass in a single object; the named
|
||||
// arguments are for legacy support.
|
||||
@ -2132,6 +2148,8 @@ function OpenSeadragon( options ){
|
||||
onSuccess = url.success;
|
||||
onError = url.error;
|
||||
withCredentials = url.withCredentials;
|
||||
headers = url.headers;
|
||||
responseType = url.responseType || null;
|
||||
url = url.url;
|
||||
}
|
||||
|
||||
@ -2147,9 +2165,9 @@ function OpenSeadragon( options ){
|
||||
if ( request.readyState == 4 ) {
|
||||
request.onreadystatechange = function(){};
|
||||
|
||||
// With protocols other than http/https, the status is 200
|
||||
// on Firefox and 0 on other browsers
|
||||
if ( request.status === 200 ||
|
||||
// With protocols other than http/https, a successful request status is in
|
||||
// the 200's on Firefox and 0 on other browsers
|
||||
if ( (request.status >= 200 && request.status < 300) ||
|
||||
( request.status === 0 &&
|
||||
protocol !== "http:" &&
|
||||
protocol !== "https:" )) {
|
||||
@ -2167,11 +2185,23 @@ function OpenSeadragon( options ){
|
||||
try {
|
||||
request.open( "GET", url, true );
|
||||
|
||||
if (responseType) {
|
||||
request.responseType = responseType;
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
for (var headerName in headers) {
|
||||
if (headers.hasOwnProperty(headerName) && headers[headerName]) {
|
||||
request.setRequestHeader(headerName, headers[headerName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (withCredentials) {
|
||||
request.withCredentials = true;
|
||||
}
|
||||
|
||||
request.send( null );
|
||||
request.send(null);
|
||||
} catch (e) {
|
||||
var msg = e.message;
|
||||
|
||||
@ -2231,6 +2261,8 @@ function OpenSeadragon( options ){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return request;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -178,6 +178,7 @@ $.ReferenceStrip = function ( options ) {
|
||||
this.panelWidth = ( viewerSize.x * this.sizeRatio ) + 8;
|
||||
this.panelHeight = ( viewerSize.y * this.sizeRatio ) + 8;
|
||||
this.panels = [];
|
||||
this.miniViewers = {};
|
||||
|
||||
/*jshint loopfunc:true*/
|
||||
for ( i = 0; i < viewer.tileSources.length; i++ ) {
|
||||
@ -293,6 +294,12 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
||||
|
||||
// Overrides Viewer.destroy
|
||||
destroy: function() {
|
||||
if (this.miniViewers) {
|
||||
for (var key in this.miniViewers) {
|
||||
this.miniViewers[key].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.element) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
@ -463,6 +470,8 @@ function loadPanels( strip, viewerSize, scroll ) {
|
||||
miniViewer.displayRegion
|
||||
);
|
||||
|
||||
strip.miniViewers[element.id] = miniViewer;
|
||||
|
||||
element.activePanel = true;
|
||||
}
|
||||
}
|
||||
|
27
src/tile.js
@ -47,8 +47,10 @@
|
||||
* @param {String} url The URL of this tile's image.
|
||||
* @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
|
||||
* is provided directly by the tile source.
|
||||
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
|
||||
* @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).
|
||||
*/
|
||||
$.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
||||
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders) {
|
||||
/**
|
||||
* The zoom level this tile belongs to.
|
||||
* @member {Number} level
|
||||
@ -91,6 +93,29 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
||||
* @memberOf OpenSeadragon.Tile#
|
||||
*/
|
||||
this.context2D = context2D;
|
||||
/**
|
||||
* Whether to load this tile's image with an AJAX request.
|
||||
* @member {Boolean} loadWithAjax
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.loadWithAjax = loadWithAjax;
|
||||
/**
|
||||
* The headers to be used in requesting this tile's image.
|
||||
* Only used if loadWithAjax is set to true.
|
||||
* @member {Object} ajaxHeaders
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
this.ajaxHeaders = ajaxHeaders;
|
||||
/**
|
||||
* The unique cache key for this tile.
|
||||
* @member {String} cacheKey
|
||||
* @memberof OpenSeadragon.Tile#
|
||||
*/
|
||||
if (this.ajaxHeaders) {
|
||||
this.cacheKey = this.url + "+" + JSON.stringify(this.ajaxHeaders);
|
||||
} else {
|
||||
this.cacheKey = this.url;
|
||||
}
|
||||
/**
|
||||
* Is this tile loaded?
|
||||
* @member {Boolean} loaded
|
||||
|
@ -140,6 +140,7 @@ $.TileCache.prototype = {
|
||||
* 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 {String} options.tile.cacheKey - The unique key used to identify this tile in the cache.
|
||||
* @param {Image} options.image - The image of 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
|
||||
@ -149,16 +150,16 @@ $.TileCache.prototype = {
|
||||
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.cacheKey, "[TileCache.cacheTile] options.tile.cacheKey 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];
|
||||
var imageRecord = this._imagesLoaded[options.tile.cacheKey];
|
||||
if (!imageRecord) {
|
||||
$.console.assert( options.image, "[TileCache.cacheTile] options.image is required to create an ImageRecord" );
|
||||
imageRecord = this._imagesLoaded[options.tile.url] = new ImageRecord({
|
||||
imageRecord = this._imagesLoaded[options.tile.cacheKey] = new ImageRecord({
|
||||
image: options.image
|
||||
});
|
||||
|
||||
@ -232,9 +233,9 @@ $.TileCache.prototype = {
|
||||
},
|
||||
|
||||
// private
|
||||
getImageRecord: function(url) {
|
||||
$.console.assert(url, '[TileCache.getImageRecord] url is required');
|
||||
return this._imagesLoaded[url];
|
||||
getImageRecord: function(cacheKey) {
|
||||
$.console.assert(cacheKey, '[TileCache.getImageRecord] cacheKey is required');
|
||||
return this._imagesLoaded[cacheKey];
|
||||
},
|
||||
|
||||
// private
|
||||
@ -246,11 +247,11 @@ $.TileCache.prototype = {
|
||||
tile.unload();
|
||||
tile.cacheImageRecord = null;
|
||||
|
||||
var imageRecord = this._imagesLoaded[tile.url];
|
||||
var imageRecord = this._imagesLoaded[tile.cacheKey];
|
||||
imageRecord.removeTile(tile);
|
||||
if (!imageRecord.getTileCount()) {
|
||||
imageRecord.destroy();
|
||||
delete this._imagesLoaded[tile.url];
|
||||
delete this._imagesLoaded[tile.cacheKey];
|
||||
this._imagesLoadedCount--;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,12 @@
|
||||
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
|
||||
* @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.
|
||||
* @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}.
|
||||
* @param {Boolean} [options.ajaxWithCredentials] - See {@link OpenSeadragon.Options}.
|
||||
* @param {Boolean} [options.loadTilesWithAjax]
|
||||
* Whether to load tile data using AJAX requests.
|
||||
* Defaults to the setting in {@link OpenSeadragon.Options}.
|
||||
* @param {Object} [options.ajaxHeaders={}]
|
||||
* A set of headers to include when making tile AJAX requests.
|
||||
*/
|
||||
$.TiledImage = function( options ) {
|
||||
var _this = this;
|
||||
@ -161,6 +167,7 @@ $.TiledImage = function( options ) {
|
||||
iOSDevice: $.DEFAULT_SETTINGS.iOSDevice,
|
||||
debugMode: $.DEFAULT_SETTINGS.debugMode,
|
||||
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
|
||||
ajaxWithCredentials: $.DEFAULT_SETTINGS.ajaxWithCredentials,
|
||||
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
||||
opacity: $.DEFAULT_SETTINGS.opacity,
|
||||
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
|
||||
@ -764,10 +771,28 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
|
||||
/**
|
||||
* @param {Number} opacity Opacity the tiled image should be drawn at.
|
||||
* @fires OpenSeadragon.TiledImage.event:opacity-change
|
||||
*/
|
||||
setOpacity: function(opacity) {
|
||||
if (opacity === this.opacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.opacity = opacity;
|
||||
this._needsDraw = true;
|
||||
/**
|
||||
* Raised when the TiledImage's opacity is changed.
|
||||
* @event opacity-change
|
||||
* @memberOf OpenSeadragon.TiledImage
|
||||
* @type {object}
|
||||
* @property {Number} opacity - The new opacity value.
|
||||
* @property {OpenSeadragon.TiledImage} eventSource - A reference to the
|
||||
* TiledImage which raised the event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent('opacity-change', {
|
||||
opacity: this.opacity
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -821,10 +846,28 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
|
||||
/**
|
||||
* @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation.
|
||||
* @fires OpenSeadragon.TiledImage.event:composite-operation-change
|
||||
*/
|
||||
setCompositeOperation: function(compositeOperation) {
|
||||
if (compositeOperation === this.compositeOperation) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.compositeOperation = compositeOperation;
|
||||
this._needsDraw = true;
|
||||
/**
|
||||
* Raised when the TiledImage's opacity is changed.
|
||||
* @event composite-operation-change
|
||||
* @memberOf OpenSeadragon.TiledImage
|
||||
* @type {object}
|
||||
* @property {String} compositeOperation - The new compositeOperation value.
|
||||
* @property {OpenSeadragon.TiledImage} eventSource - A reference to the
|
||||
* TiledImage which raised the event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent('composite-operation-change', {
|
||||
compositeOperation: this.compositeOperation
|
||||
});
|
||||
},
|
||||
|
||||
// private
|
||||
@ -971,7 +1014,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
||||
var targetZeroRatio = viewport.deltaPixelsFromPointsNoRotate(
|
||||
this.source.getPixelRatio(
|
||||
Math.max(
|
||||
this.source.getClosestLevel(viewport.containerSize) - 1,
|
||||
this.source.getClosestLevel(),
|
||||
0
|
||||
)
|
||||
),
|
||||
@ -1179,6 +1222,7 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
||||
var tile = getTile(
|
||||
x, y,
|
||||
level,
|
||||
tiledImage,
|
||||
tiledImage.source,
|
||||
tiledImage.tilesMatrix,
|
||||
currentTime,
|
||||
@ -1237,7 +1281,7 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
||||
if (tile.context2D) {
|
||||
setTileLoaded(tiledImage, tile);
|
||||
} else {
|
||||
var imageRecord = tiledImage._tileCache.getImageRecord(tile.url);
|
||||
var imageRecord = tiledImage._tileCache.getImageRecord(tile.cacheKey);
|
||||
if (imageRecord) {
|
||||
var image = imageRecord.getImage();
|
||||
setTileLoaded(tiledImage, tile, image);
|
||||
@ -1275,6 +1319,7 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} level
|
||||
* @param {OpenSeadragon.TiledImage} tiledImage
|
||||
* @param {OpenSeadragon.TileSource} tileSource
|
||||
* @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.
|
||||
* @param {Number} time
|
||||
@ -1283,12 +1328,23 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
||||
* @param {Number} worldHeight
|
||||
* @returns {OpenSeadragon.Tile}
|
||||
*/
|
||||
function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, worldWidth, worldHeight ) {
|
||||
function getTile(
|
||||
x, y,
|
||||
level,
|
||||
tiledImage,
|
||||
tileSource,
|
||||
tilesMatrix,
|
||||
time,
|
||||
numTiles,
|
||||
worldWidth,
|
||||
worldHeight
|
||||
) {
|
||||
var xMod,
|
||||
yMod,
|
||||
bounds,
|
||||
exists,
|
||||
url,
|
||||
ajaxHeaders,
|
||||
context2D,
|
||||
tile;
|
||||
|
||||
@ -1305,6 +1361,18 @@ function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, worldWid
|
||||
bounds = tileSource.getTileBounds( level, xMod, yMod );
|
||||
exists = tileSource.tileExists( level, xMod, yMod );
|
||||
url = tileSource.getTileUrl( level, xMod, yMod );
|
||||
|
||||
// Headers are only applicable if loadTilesWithAjax is set
|
||||
if (tiledImage.loadTilesWithAjax) {
|
||||
ajaxHeaders = tileSource.getTileAjaxHeaders( level, xMod, yMod );
|
||||
// Combine tile AJAX headers with tiled image AJAX headers (if applicable)
|
||||
if ($.isPlainObject(tiledImage.ajaxHeaders)) {
|
||||
ajaxHeaders = $.extend({}, tiledImage.ajaxHeaders, ajaxHeaders);
|
||||
}
|
||||
} else {
|
||||
ajaxHeaders = null;
|
||||
}
|
||||
|
||||
context2D = tileSource.getContext2D ?
|
||||
tileSource.getContext2D(level, xMod, yMod) : undefined;
|
||||
|
||||
@ -1318,7 +1386,9 @@ function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, worldWid
|
||||
bounds,
|
||||
exists,
|
||||
url,
|
||||
context2D
|
||||
context2D,
|
||||
tiledImage.loadTilesWithAjax,
|
||||
ajaxHeaders
|
||||
);
|
||||
}
|
||||
|
||||
@ -1340,9 +1410,12 @@ function loadTile( tiledImage, tile, time ) {
|
||||
tile.loading = true;
|
||||
tiledImage._imageLoader.addJob({
|
||||
src: tile.url,
|
||||
loadWithAjax: tile.loadWithAjax,
|
||||
ajaxHeaders: tile.ajaxHeaders,
|
||||
crossOriginPolicy: tiledImage.crossOriginPolicy,
|
||||
callback: function( image, errorMsg ){
|
||||
onTileLoad( tiledImage, tile, time, image, errorMsg );
|
||||
ajaxWithCredentials: tiledImage.ajaxWithCredentials,
|
||||
callback: function( image, errorMsg, tileRequest ){
|
||||
onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest );
|
||||
},
|
||||
abort: function() {
|
||||
tile.loading = false;
|
||||
@ -1359,8 +1432,9 @@ function loadTile( tiledImage, tile, time ) {
|
||||
* @param {Number} time
|
||||
* @param {Image} image
|
||||
* @param {String} errorMsg
|
||||
* @param {XMLHttpRequest} tileRequest
|
||||
*/
|
||||
function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
||||
function onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest ) {
|
||||
if ( !image ) {
|
||||
$.console.log( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
|
||||
/**
|
||||
@ -1373,8 +1447,15 @@ function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.
|
||||
* @property {number} time - The time in milliseconds when the tile load began.
|
||||
* @property {string} message - The error message.
|
||||
* @property {XMLHttpRequest} tileRequest - The XMLHttpRequest used to load the tile if available.
|
||||
*/
|
||||
tiledImage.viewer.raiseEvent("tile-load-failed", {tile: tile, tiledImage: tiledImage, time: time, message: errorMsg});
|
||||
tiledImage.viewer.raiseEvent("tile-load-failed", {
|
||||
tile: tile,
|
||||
tiledImage: tiledImage,
|
||||
time: time,
|
||||
message: errorMsg,
|
||||
tileRequest: tileRequest
|
||||
});
|
||||
tile.loading = false;
|
||||
tile.exists = false;
|
||||
return;
|
||||
@ -1387,9 +1468,8 @@ function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
||||
}
|
||||
|
||||
var finish = function() {
|
||||
var cutoff = Math.ceil( Math.log(
|
||||
tiledImage.source.getTileWidth(tile.level) ) / Math.log( 2 ) );
|
||||
setTileLoaded(tiledImage, tile, image, cutoff);
|
||||
var cutoff = tiledImage.source.getClosestLevel();
|
||||
setTileLoaded(tiledImage, tile, image, cutoff, tileRequest);
|
||||
};
|
||||
|
||||
// Check if we're mid-update; this can happen on IE8 because image load events for
|
||||
@ -1410,7 +1490,7 @@ function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
||||
* @param {Image} image
|
||||
* @param {Number} cutoff
|
||||
*/
|
||||
function setTileLoaded(tiledImage, tile, image, cutoff) {
|
||||
function setTileLoaded(tiledImage, tile, image, cutoff, tileRequest) {
|
||||
var increment = 0;
|
||||
|
||||
function getCompletionCallback() {
|
||||
@ -1445,6 +1525,7 @@ function setTileLoaded(tiledImage, tile, image, cutoff) {
|
||||
* @property {Image} image - The image of the tile.
|
||||
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
|
||||
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
|
||||
* @property {XMLHttpRequest} tiledImage - The AJAX request that loaded this tile (if applicable).
|
||||
* @property {function} getCompletionCallback - A function giving a callback to call
|
||||
* when the asynchronous processing of the image is done. The image will be
|
||||
* marked as entirely loaded when the callback has been called once for each
|
||||
@ -1453,6 +1534,7 @@ function setTileLoaded(tiledImage, tile, image, cutoff) {
|
||||
tiledImage.viewer.raiseEvent("tile-loaded", {
|
||||
tile: tile,
|
||||
tiledImage: tiledImage,
|
||||
tileRequest: tileRequest,
|
||||
image: image,
|
||||
getCompletionCallback: getCompletionCallback
|
||||
});
|
||||
|
@ -65,6 +65,8 @@
|
||||
* @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 {Object} [options.ajaxHeaders]
|
||||
* A set of headers to include in AJAX requests.
|
||||
* @param {Number} [options.width]
|
||||
* Width of the source image at max resolution in pixels.
|
||||
* @param {Number} [options.height]
|
||||
@ -318,25 +320,20 @@ $.TileSource.prototype = {
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @returns {Number} The highest level in this tile source that can be contained in a single tile.
|
||||
*/
|
||||
getClosestLevel: function( rect ) {
|
||||
getClosestLevel: function() {
|
||||
var i,
|
||||
tilesPerSide,
|
||||
tiles;
|
||||
|
||||
for( i = this.minLevel; i < this.maxLevel; i++ ){
|
||||
tiles = this.getNumTiles( i );
|
||||
tilesPerSide = new $.Point(
|
||||
Math.floor( rect.x / this.getTileWidth(i) ),
|
||||
Math.floor( rect.y / this.getTileHeight(i) )
|
||||
);
|
||||
|
||||
if( tiles.x + 1 >= tilesPerSide.x && tiles.y + 1 >= tilesPerSide.y ){
|
||||
for (i = this.minLevel + 1; i <= this.maxLevel; i++){
|
||||
tiles = this.getNumTiles(i);
|
||||
if (tiles.x > 1 || tiles.y > 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Math.max( 0, i - 1 );
|
||||
|
||||
return i - 1;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -475,6 +472,7 @@ $.TileSource.prototype = {
|
||||
$.makeAjaxRequest( {
|
||||
url: url,
|
||||
withCredentials: this.ajaxWithCredentials,
|
||||
headers: this.ajaxHeaders,
|
||||
success: function( xhr ) {
|
||||
var data = processResponse( xhr );
|
||||
callback( data );
|
||||
@ -559,7 +557,7 @@ $.TileSource.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for retriving the url which will return an image for the
|
||||
* Responsible for retrieving the url which will return an image for the
|
||||
* region specified by the given x, y, and level components.
|
||||
* This method is not implemented by this class other than to throw an Error
|
||||
* announcing you have to implement it. Because of the variety of tile
|
||||
@ -575,6 +573,23 @@ $.TileSource.prototype = {
|
||||
throw new Error( "Method not implemented." );
|
||||
},
|
||||
|
||||
/**
|
||||
* Responsible for retrieving the headers which will be attached to the image request for the
|
||||
* region specified by the given x, y, and level components.
|
||||
* This option is only relevant if {@link OpenSeadragon.Options}.loadTilesWithAjax is set to true.
|
||||
* The headers returned here will override headers specified at the Viewer or TiledImage level.
|
||||
* Specifying a falsy value for a header will clear its existing value set at the Viewer or
|
||||
* TiledImage level (if any).
|
||||
* @function
|
||||
* @param {Number} level
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @returns {Object}
|
||||
*/
|
||||
getTileAjaxHeaders: function( level, x, y ) {
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @param {Number} level
|
||||
@ -629,7 +644,11 @@ function processResponse( xhr ){
|
||||
data = xhr.responseText;
|
||||
}
|
||||
}else if( responseText.match(/\s*[\{\[].*/) ){
|
||||
try{
|
||||
data = $.parseJSON(responseText);
|
||||
} catch(e){
|
||||
data = responseText;
|
||||
}
|
||||
}else{
|
||||
data = responseText;
|
||||
}
|
||||
|
234
src/viewer.js
@ -96,6 +96,12 @@ $.Viewer = function( options ) {
|
||||
//internal state and dom identifiers
|
||||
id: options.id,
|
||||
hash: options.hash || nextHash++,
|
||||
/**
|
||||
* Index for page to be shown first next time open() is called (only used in sequenceMode).
|
||||
* @member {Number} initialPage
|
||||
* @memberof OpenSeadragon.Viewer#
|
||||
*/
|
||||
initialPage: 0,
|
||||
|
||||
//dom nodes
|
||||
/**
|
||||
@ -370,7 +376,8 @@ $.Viewer = function( options ) {
|
||||
|
||||
// Create the image loader
|
||||
this.imageLoader = new $.ImageLoader({
|
||||
jobLimit: this.imageLoaderLimit
|
||||
jobLimit: this.imageLoaderLimit,
|
||||
timeout: options.timeout
|
||||
});
|
||||
|
||||
// Create the tile cache
|
||||
@ -481,11 +488,13 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
* except for the index property; images are added in sequence.
|
||||
* A TileSource specifier is anything you could pass as the tileSource property
|
||||
* of the options parameter for {@link OpenSeadragon.Viewer#addTiledImage}.
|
||||
* @param {Number} initialPage - If sequenceMode is true, display this page initially
|
||||
* for the given tileSources. If specified, will overwrite the Viewer's existing initialPage property.
|
||||
* @return {OpenSeadragon.Viewer} Chainable.
|
||||
* @fires OpenSeadragon.Viewer.event:open
|
||||
* @fires OpenSeadragon.Viewer.event:open-failed
|
||||
*/
|
||||
open: function (tileSources) {
|
||||
open: function (tileSources, initialPage) {
|
||||
var _this = this;
|
||||
|
||||
this.close();
|
||||
@ -500,23 +509,17 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
this.referenceStrip = null;
|
||||
}
|
||||
|
||||
if (typeof initialPage != 'undefined' && !isNaN(initialPage)) {
|
||||
this.initialPage = initialPage;
|
||||
}
|
||||
|
||||
this.tileSources = tileSources;
|
||||
this._sequenceIndex = Math.max(0, Math.min(this.tileSources.length - 1, this.initialPage));
|
||||
if (this.tileSources.length) {
|
||||
this.open(this.tileSources[this._sequenceIndex]);
|
||||
|
||||
if ( this.showReferenceStrip ){
|
||||
this.referenceStrip = new $.ReferenceStrip({
|
||||
id: this.referenceStripElement,
|
||||
position: this.referenceStripPosition,
|
||||
sizeRatio: this.referenceStripSizeRatio,
|
||||
scroll: this.referenceStripScroll,
|
||||
height: this.referenceStripHeight,
|
||||
width: this.referenceStripWidth,
|
||||
tileSources: this.tileSources,
|
||||
prefixUrl: this.prefixUrl,
|
||||
viewer: this
|
||||
});
|
||||
this.addReferenceStrip();
|
||||
}
|
||||
}
|
||||
|
||||
@ -845,6 +848,22 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Turns debugging mode on or off for this viewer.
|
||||
*
|
||||
* @function
|
||||
* @param {Boolean} true to turn debug on, false to turn debug off.
|
||||
*/
|
||||
setDebugMode: function(debugMode){
|
||||
|
||||
for (var i = 0; i < this.world.getItemCount(); i++) {
|
||||
this.world.getItemAt(i).debugMode = debugMode;
|
||||
}
|
||||
|
||||
this.debugMode = debugMode;
|
||||
this.forceRedraw();
|
||||
},
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @return {Boolean}
|
||||
@ -1232,6 +1251,15 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
||||
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||
* overriding viewer.crossOriginPolicy.
|
||||
* @param {Boolean} [options.ajaxWithCredentials] Whether to set withCredentials on tile AJAX
|
||||
* @param {Boolean} [options.loadTilesWithAjax]
|
||||
* Whether to load tile data using AJAX requests.
|
||||
* Defaults to the setting in {@link OpenSeadragon.Options}.
|
||||
* @param {Object} [options.ajaxHeaders]
|
||||
* A set of headers to include when making tile AJAX requests.
|
||||
* Note that these headers will be merged over any headers specified in {@link OpenSeadragon.Options}.
|
||||
* Specifying a falsy value for a header will clear its existing value set at the Viewer level (if any).
|
||||
* requests.
|
||||
* @param {Function} [options.success] A function that gets called when the image is
|
||||
* successfully added. It's passed the event object which contains a single property:
|
||||
* "item", the resulting TiledImage.
|
||||
@ -1270,6 +1298,17 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
if (options.crossOriginPolicy === undefined) {
|
||||
options.crossOriginPolicy = options.tileSource.crossOriginPolicy !== undefined ? options.tileSource.crossOriginPolicy : this.crossOriginPolicy;
|
||||
}
|
||||
if (options.ajaxWithCredentials === undefined) {
|
||||
options.ajaxWithCredentials = this.ajaxWithCredentials;
|
||||
}
|
||||
if (options.loadTilesWithAjax === undefined) {
|
||||
options.loadTilesWithAjax = this.loadTilesWithAjax;
|
||||
}
|
||||
if (options.ajaxHeaders === undefined || options.ajaxHeaders === null) {
|
||||
options.ajaxHeaders = this.ajaxHeaders;
|
||||
} else if ($.isPlainObject(options.ajaxHeaders) && $.isPlainObject(this.ajaxHeaders)) {
|
||||
options.ajaxHeaders = $.extend({}, this.ajaxHeaders, options.ajaxHeaders);
|
||||
}
|
||||
|
||||
var myQueueItem = {
|
||||
options: options
|
||||
@ -1332,11 +1371,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
|
||||
this._loadQueue.push(myQueueItem);
|
||||
|
||||
getTileSourceImplementation( this, options.tileSource, options, function( tileSource ) {
|
||||
|
||||
myQueueItem.tileSource = tileSource;
|
||||
|
||||
// add everybody at the front of the queue that's ready to go
|
||||
function processReadyItems() {
|
||||
var queueItem, tiledImage, optionsClone;
|
||||
while (_this._loadQueue.length) {
|
||||
queueItem = _this._loadQueue[0];
|
||||
@ -1384,6 +1419,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
|
||||
iOSDevice: _this.iOSDevice,
|
||||
crossOriginPolicy: queueItem.options.crossOriginPolicy,
|
||||
ajaxWithCredentials: queueItem.options.ajaxWithCredentials,
|
||||
loadTilesWithAjax: queueItem.options.loadTilesWithAjax,
|
||||
ajaxHeaders: queueItem.options.ajaxHeaders,
|
||||
debugMode: _this.debugMode
|
||||
});
|
||||
|
||||
@ -1419,9 +1457,20 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTileSourceImplementation( this, options.tileSource, options, function( tileSource ) {
|
||||
|
||||
myQueueItem.tileSource = tileSource;
|
||||
|
||||
// add everybody at the front of the queue that's ready to go
|
||||
processReadyItems();
|
||||
}, function( event ) {
|
||||
event.options = options;
|
||||
raiseAddItemFailed(event);
|
||||
|
||||
// add everybody at the front of the queue that's ready to go
|
||||
processReadyItems();
|
||||
} );
|
||||
},
|
||||
|
||||
@ -2094,6 +2143,52 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
*/
|
||||
_cancelPendingImages: function() {
|
||||
this._loadQueue = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the reference strip and disables displaying it.
|
||||
* @function
|
||||
*/
|
||||
removeReferenceStrip: function() {
|
||||
this.showReferenceStrip = false;
|
||||
|
||||
if (this.referenceStrip) {
|
||||
this.referenceStrip.destroy();
|
||||
this.referenceStrip = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables and displays the reference strip based on the currently set tileSources.
|
||||
* Works only when the Viewer has sequenceMode set to true.
|
||||
* @function
|
||||
*/
|
||||
addReferenceStrip: function() {
|
||||
this.showReferenceStrip = true;
|
||||
|
||||
if (this.sequenceMode) {
|
||||
if (this.referenceStrip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.tileSources.length && this.tileSources.length > 1) {
|
||||
this.referenceStrip = new $.ReferenceStrip({
|
||||
id: this.referenceStripElement,
|
||||
position: this.referenceStripPosition,
|
||||
sizeRatio: this.referenceStripSizeRatio,
|
||||
scroll: this.referenceStripScroll,
|
||||
height: this.referenceStripHeight,
|
||||
width: this.referenceStripWidth,
|
||||
tileSources: this.tileSources,
|
||||
prefixUrl: this.prefixUrl,
|
||||
viewer: this
|
||||
});
|
||||
|
||||
this.referenceStrip.setFocus( this._sequenceIndex );
|
||||
}
|
||||
} else {
|
||||
$.console.warn('Attempting to display a reference strip while "sequenceMode" is off.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -2113,6 +2208,7 @@ function _getSafeElemSize (oElement) {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @private
|
||||
@ -2128,7 +2224,12 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
|
||||
tileSource = $.parseXml( tileSource );
|
||||
//json should start with "{" or "[" and end with "}" or "]"
|
||||
} else if ( tileSource.match(/^\s*[\{\[].*[\}\]]\s*$/ ) ) {
|
||||
tileSource = $.parseJSON(tileSource);
|
||||
try {
|
||||
var tileSourceJ = $.parseJSON(tileSource);
|
||||
tileSource = tileSourceJ;
|
||||
} catch (e) {
|
||||
//tileSource = tileSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2156,6 +2257,7 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
|
||||
crossOriginPolicy: imgOptions.crossOriginPolicy !== undefined ?
|
||||
imgOptions.crossOriginPolicy : viewer.crossOriginPolicy,
|
||||
ajaxWithCredentials: viewer.ajaxWithCredentials,
|
||||
ajaxHeaders: viewer.ajaxHeaders,
|
||||
useCanvas: viewer.useCanvas,
|
||||
success: function( event ) {
|
||||
successCallback( event.tileSource );
|
||||
@ -2463,16 +2565,15 @@ function onCanvasClick( event ) {
|
||||
this.canvas.focus();
|
||||
}
|
||||
|
||||
if ( !event.preventDefaultAction && this.viewport && event.quick ) {
|
||||
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
|
||||
if ( gestureSettings.clickToZoom ) {
|
||||
this.viewport.zoomBy(
|
||||
event.shift ? 1.0 / this.zoomPerClick : this.zoomPerClick,
|
||||
this.viewport.pointFromPixel( event.position, true )
|
||||
);
|
||||
this.viewport.applyConstraints();
|
||||
}
|
||||
}
|
||||
var canvasClickEventArgs = {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
quick: event.quick,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent,
|
||||
preventDefaultAction: event.preventDefaultAction
|
||||
};
|
||||
|
||||
/**
|
||||
* Raised when a mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.
|
||||
*
|
||||
@ -2485,15 +2586,21 @@ function onCanvasClick( event ) {
|
||||
* @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
|
||||
* @property {Boolean} shift - True if the shift key was pressed during this event.
|
||||
* @property {Object} originalEvent - The original DOM event.
|
||||
* @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent( 'canvas-click', {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
quick: event.quick,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent
|
||||
});
|
||||
this.raiseEvent( 'canvas-click', canvasClickEventArgs);
|
||||
|
||||
if ( !canvasClickEventArgs.preventDefaultAction && this.viewport && event.quick ) {
|
||||
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
|
||||
if ( gestureSettings.clickToZoom ) {
|
||||
this.viewport.zoomBy(
|
||||
event.shift ? 1.0 / this.zoomPerClick : this.zoomPerClick,
|
||||
this.viewport.pointFromPixel( event.position, true )
|
||||
);
|
||||
this.viewport.applyConstraints();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onCanvasDblClick( event ) {
|
||||
@ -2533,6 +2640,35 @@ function onCanvasDblClick( event ) {
|
||||
function onCanvasDrag( event ) {
|
||||
var gestureSettings;
|
||||
|
||||
var canvasDragEventArgs = {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
delta: event.delta,
|
||||
speed: event.speed,
|
||||
direction: event.direction,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent,
|
||||
preventDefaultAction: event.preventDefaultAction
|
||||
};
|
||||
/**
|
||||
* Raised when a mouse or touch drag operation occurs on the {@link OpenSeadragon.Viewer#canvas} element.
|
||||
*
|
||||
* @event canvas-drag
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
|
||||
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
|
||||
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
|
||||
* @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
|
||||
* @property {Number} speed - Current computed speed, in pixels per second.
|
||||
* @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
|
||||
* @property {Boolean} shift - True if the shift key was pressed during this event.
|
||||
* @property {Object} originalEvent - The original DOM event.
|
||||
* @property {Boolean} preventDefaultAction - Set to true to prevent default drag behaviour. Default: false.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent( 'canvas-drag', canvasDragEventArgs);
|
||||
|
||||
if ( !event.preventDefaultAction && this.viewport ) {
|
||||
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
|
||||
if( !this.panHorizontal ){
|
||||
@ -2565,32 +2701,6 @@ function onCanvasDrag( event ) {
|
||||
|
||||
this.viewport.panBy( this.viewport.deltaPointsFromPixels( event.delta.negate() ), gestureSettings.flickEnabled );
|
||||
}
|
||||
|
||||
/**
|
||||
* Raised when a mouse or touch drag operation occurs on the {@link OpenSeadragon.Viewer#canvas} element.
|
||||
*
|
||||
* @event canvas-drag
|
||||
* @memberof OpenSeadragon.Viewer
|
||||
* @type {object}
|
||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
|
||||
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
|
||||
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
|
||||
* @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
|
||||
* @property {Number} speed - Current computed speed, in pixels per second.
|
||||
* @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
|
||||
* @property {Boolean} shift - True if the shift key was pressed during this event.
|
||||
* @property {Object} originalEvent - The original DOM event.
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.raiseEvent( 'canvas-drag', {
|
||||
tracker: event.eventSource,
|
||||
position: event.position,
|
||||
delta: event.delta,
|
||||
speed: event.speed,
|
||||
direction: event.direction,
|
||||
shift: event.shift,
|
||||
originalEvent: event.originalEvent
|
||||
});
|
||||
}
|
||||
|
||||
function onCanvasDragEnd( event ) {
|
||||
|
@ -823,7 +823,8 @@ $.Viewport.prototype = {
|
||||
* @return {OpenSeadragon.Viewport} Chainable.
|
||||
* @fires OpenSeadragon.Viewer.event:zoom
|
||||
*/
|
||||
zoomTo: function( zoom, refPoint, immediately ) {
|
||||
zoomTo: function(zoom, refPoint, immediately) {
|
||||
var _this = this;
|
||||
|
||||
this.zoomPoint = refPoint instanceof $.Point &&
|
||||
!isNaN(refPoint.x) &&
|
||||
@ -831,13 +832,15 @@ $.Viewport.prototype = {
|
||||
refPoint :
|
||||
null;
|
||||
|
||||
if ( immediately ) {
|
||||
this.zoomSpring.resetTo( zoom );
|
||||
if (immediately) {
|
||||
this._adjustCenterSpringsForZoomPoint(function() {
|
||||
_this.zoomSpring.resetTo(zoom);
|
||||
});
|
||||
} else {
|
||||
this.zoomSpring.springTo( zoom );
|
||||
this.zoomSpring.springTo(zoom);
|
||||
}
|
||||
|
||||
if( this.viewer ){
|
||||
if (this.viewer) {
|
||||
/**
|
||||
* Raised when the viewport zoom level changes (see {@link OpenSeadragon.Viewport#zoomBy} and {@link OpenSeadragon.Viewport#zoomTo}).
|
||||
*
|
||||
@ -850,7 +853,7 @@ $.Viewport.prototype = {
|
||||
* @property {Boolean} immediately
|
||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||
*/
|
||||
this.viewer.raiseEvent( 'zoom', {
|
||||
this.viewer.raiseEvent('zoom', {
|
||||
zoom: zoom,
|
||||
refPoint: refPoint,
|
||||
immediately: immediately
|
||||
@ -956,25 +959,10 @@ $.Viewport.prototype = {
|
||||
* @returns {Boolean} True if any change has been made, false otherwise.
|
||||
*/
|
||||
update: function() {
|
||||
|
||||
if (this.zoomPoint) {
|
||||
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
||||
this.zoomSpring.update();
|
||||
var newZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
||||
|
||||
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
|
||||
var deltaZoomPoints = this.deltaPointsFromPixels(
|
||||
deltaZoomPixels, true);
|
||||
|
||||
this.centerSpringX.shiftBy(deltaZoomPoints.x);
|
||||
this.centerSpringY.shiftBy(deltaZoomPoints.y);
|
||||
|
||||
if (this.zoomSpring.isAtTargetValue()) {
|
||||
this.zoomPoint = null;
|
||||
}
|
||||
} else {
|
||||
this.zoomSpring.update();
|
||||
}
|
||||
var _this = this;
|
||||
this._adjustCenterSpringsForZoomPoint(function() {
|
||||
_this.zoomSpring.update();
|
||||
});
|
||||
|
||||
this.centerSpringX.update();
|
||||
this.centerSpringY.update();
|
||||
@ -990,6 +978,27 @@ $.Viewport.prototype = {
|
||||
return changed;
|
||||
},
|
||||
|
||||
_adjustCenterSpringsForZoomPoint: function(zoomSpringHandler) {
|
||||
if (this.zoomPoint) {
|
||||
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
||||
zoomSpringHandler();
|
||||
var newZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
||||
|
||||
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
|
||||
var deltaZoomPoints = this.deltaPointsFromPixels(
|
||||
deltaZoomPixels, true);
|
||||
|
||||
this.centerSpringX.shiftBy(deltaZoomPoints.x);
|
||||
this.centerSpringY.shiftBy(deltaZoomPoints.y);
|
||||
|
||||
if (this.zoomSpring.isAtTargetValue()) {
|
||||
this.zoomPoint = null;
|
||||
}
|
||||
} else {
|
||||
zoomSpringHandler();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a delta (translation vector) from viewport coordinates to pixels
|
||||
* coordinates. This method does not take rotation into account.
|
||||
@ -1226,6 +1235,7 @@ $.Viewport.prototype = {
|
||||
* in image coordinate system.
|
||||
* @param {Number} [pixelWidth] the width in pixel of the rectangle.
|
||||
* @param {Number} [pixelHeight] the height in pixel of the rectangle.
|
||||
* @returns {OpenSeadragon.Rect} This image's bounds in viewport coordinates
|
||||
*/
|
||||
imageToViewportRectangle: function(imageX, imageY, pixelWidth, pixelHeight) {
|
||||
var rect = imageX;
|
||||
|
@ -18,7 +18,7 @@
|
||||
* tilesUrl: "/test/data/zoomify/"
|
||||
* }
|
||||
*
|
||||
* The tileSize is currently hardcoded to 256 (the usual Zoomify default). The tileUrl must the the path to the image _directory_.
|
||||
* The tileSize is currently hardcoded to 256 (the usual Zoomify default). The tileUrl must the path to the image _directory_.
|
||||
*
|
||||
* 2) Loading image metadata from xml file: (CURRENTLY NOT SUPPORTED)
|
||||
*
|
||||
|
@ -10,6 +10,11 @@
|
||||
<body>
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
|
||||
<script>
|
||||
var isCoverageTest = true;
|
||||
</script>
|
||||
|
||||
<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>
|
||||
@ -80,6 +85,8 @@
|
||||
<script src="/test/modules/tilesourcecollection.js"></script>
|
||||
<script src="/test/modules/spring.js"></script>
|
||||
<script src="/test/modules/rectangle.js"></script>
|
||||
<script src="/test/modules/ajax-tiles.js"></script>
|
||||
<script src="/test/modules/imageloader.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>
|
||||
|
BIN
test/data/testpattern.blob
Normal file
After Width: | Height: | Size: 508 KiB |
75
test/demo/customheaders.html
Normal file
@ -0,0 +1,75 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenSeadragon Custom Request Headers 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">
|
||||
|
||||
.osd-viewer {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Demo of how the loadTilesWithAjax and ajaxHeaders options as well as the getTileHeaders() method on TileSource can be applied.
|
||||
</p>
|
||||
<p>
|
||||
Examine the network requests in your browser developer tools to see the custom headers sent with each request.
|
||||
</p>
|
||||
<div id="contentDiv" class="osd-viewer"></div>
|
||||
<script type="text/javascript">
|
||||
// These values are generated by a script that concatenates all the tile files and records
|
||||
// their byte ranges in a multi-dimensional array.
|
||||
var tileManifest = {"tileRanges":[[[[0,3467]]],[[[3467,6954]]],[[[344916,348425]]],[[[348425,351948]]],[[[351948,355576]]],[[[355576,359520]]],[[[359520,364663]]],[[[364663,374196]]],[[[374196,407307]]],[[[407307,435465],[435465,463663]],[[463663,491839],[491839,520078]]],[[[6954,29582],[29582,50315],[50315,71936],[71936,92703]],[[92703,113385],[113385,133265],[133265,154763],[154763,175710]],[[175710,197306],[197306,218807],[218807,242177],[242177,263007]],[[263007,283790],[283790,304822],[304822,325691],[325691,344916]]]],"totalSize":520078}
|
||||
|
||||
// This tile source demonstrates how you can retrieve individual tiles from a single file
|
||||
// using the Range header.
|
||||
var myCustomTileSource = {
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
tileWidth: 254,
|
||||
tileHeight: 254,
|
||||
tileOverlap: 1,
|
||||
maxLevel: 10,
|
||||
minLevel: 0,
|
||||
// The tile URL is always the same. Only the Range header changes
|
||||
getTileUrl: function () {
|
||||
return "/test/data/testpattern.blob";
|
||||
},
|
||||
// This method will send the appropriate range header for this tile based on the data
|
||||
// in tileByteRanges.
|
||||
getTileAjaxHeaders: function(level, x, y) {
|
||||
return {
|
||||
Range: "bytes=" + tileManifest.tileRanges[level][x][y].join("-") + "/" + tileManifest.totalSize
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
var viewer = OpenSeadragon({
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
showNavigator: true,
|
||||
loadTilesWithAjax: true,
|
||||
ajaxHeaders: {
|
||||
// Example of using the viewer-level ajaxHeaders option
|
||||
// for providing an authentication token.
|
||||
"Authentication": "Bearer MY_AUTH_TOKEN"
|
||||
}
|
||||
});
|
||||
|
||||
viewer.addTiledImage({
|
||||
// The headers specified here will be combined with those in the Viewer object (if any)
|
||||
ajaxHeaders: {
|
||||
"X-My-TiledImage-Header": "Something"
|
||||
},
|
||||
tileSource: myCustomTileSource
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
44
test/demo/setdebugmode.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>OpenSeadragon Basic Demo</title>
|
||||
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
|
||||
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
|
||||
<style type="text/css">
|
||||
.openseadragon1 {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div>
|
||||
Turn debug mode on and off after viewer has been created.
|
||||
<button onclick="debugModeOn()">Debug Mode On</button>
|
||||
<button onclick="debugModeOff()">Debug Mode Off</button>
|
||||
</div>
|
||||
<div id="contentDiv" class="openseadragon1"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
var viewer = OpenSeadragon({
|
||||
// debugMode: true,
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
tileSources: "../data/testpattern.dzi",
|
||||
showNavigator: true,
|
||||
debugMode: true
|
||||
});
|
||||
|
||||
function debugModeOn() {
|
||||
viewer.setDebugMode(true);
|
||||
}
|
||||
|
||||
function debugModeOff() {
|
||||
viewer.setDebugMode(false);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
38
test/demo/zoomify.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenSeadragon Zoomify Demo</title>
|
||||
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
|
||||
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
|
||||
<style type="text/css">
|
||||
|
||||
.openseadragon1 {
|
||||
width: 800px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Simple demo page to show a default OpenSeadragon viewer with a Zoomify tile source.
|
||||
</div>
|
||||
<div id="contentDiv" class="openseadragon1"></div>
|
||||
<script type="text/javascript">
|
||||
|
||||
var viewer = OpenSeadragon({
|
||||
debugMode: true,
|
||||
id: "contentDiv",
|
||||
prefixUrl: "../../build/openseadragon/images/",
|
||||
tileSources: [{
|
||||
type: "zoomifytileservice",
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
tilesUrl: "../data/zoomify/"
|
||||
}],
|
||||
showNavigator:true
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
242
test/modules/ajax-tiles.js
Normal file
@ -0,0 +1,242 @@
|
||||
/* global module, asyncTest, start, $, ok, equal, deepEqual, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer;
|
||||
|
||||
// These values are generated by a script that concatenates all the tile files and records
|
||||
// their byte ranges in a multi-dimensional array.
|
||||
|
||||
// eslint-disable-next-line
|
||||
var tileManifest = {"tileRanges":[[[[0,3467]]],[[[3467,6954]]],[[[344916,348425]]],[[[348425,351948]]],[[[351948,355576]]],[[[355576,359520]]],[[[359520,364663]]],[[[364663,374196]]],[[[374196,407307]]],[[[407307,435465],[435465,463663]],[[463663,491839],[491839,520078]]],[[[6954,29582],[29582,50315],[50315,71936],[71936,92703]],[[92703,113385],[113385,133265],[133265,154763],[154763,175710]],[[175710,197306],[197306,218807],[218807,242177],[242177,263007]],[[263007,283790],[283790,304822],[304822,325691],[325691,344916]]]],"totalSize":520078}
|
||||
|
||||
function getTileRangeHeader(level, x, y) {
|
||||
return 'bytes=' + tileManifest.tileRanges[level][x][y].join('-') + '/' + tileManifest.totalSize;
|
||||
}
|
||||
|
||||
// This tile source demonstrates how you can retrieve individual tiles from a single file
|
||||
// using the Range header.
|
||||
var customTileSource = {
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
tileWidth: 254,
|
||||
tileHeight: 254,
|
||||
tileOverlap: 1,
|
||||
maxLevel: 10,
|
||||
minLevel: 0,
|
||||
// The tile URL is always the same. Only the Range header changes
|
||||
getTileUrl: function () {
|
||||
return '/test/data/testpattern.blob';
|
||||
},
|
||||
// This method will send the appropriate range header for this tile based on the data
|
||||
// in tileByteRanges.
|
||||
getTileAjaxHeaders: function(level, x, y) {
|
||||
return {
|
||||
Range: getTileRangeHeader(level, x, y)
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
module('AJAX-Tiles', {
|
||||
setup: function() {
|
||||
$('<div id="example"></div>').appendTo('#qunit-fixture');
|
||||
|
||||
testLog.reset();
|
||||
|
||||
viewer = OpenSeadragon({
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100, // Faster animation = faster tests,
|
||||
loadTilesWithAjax: true,
|
||||
ajaxHeaders: {
|
||||
'X-Viewer-Header': 'ViewerHeaderValue'
|
||||
}
|
||||
});
|
||||
},
|
||||
teardown: function() {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
asyncTest('tile-loaded event includes AJAX request object', function() {
|
||||
var tileLoaded = function tileLoaded(evt) {
|
||||
viewer.removeHandler('tile-loaded', tileLoaded);
|
||||
ok(evt.tileRequest, 'Event includes tileRequest property');
|
||||
equal(evt.tileRequest.readyState, XMLHttpRequest.DONE, 'tileRequest is in completed state');
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-loaded', tileLoaded);
|
||||
viewer.open(customTileSource);
|
||||
});
|
||||
|
||||
asyncTest('withCredentials is set in tile AJAX requests', function() {
|
||||
var tileLoaded = function tileLoaded(evt) {
|
||||
viewer.removeHandler('tile-loaded', tileLoaded);
|
||||
ok(evt.tileRequest, 'Event includes tileRequest property');
|
||||
equal(evt.tileRequest.readyState, XMLHttpRequest.DONE, 'tileRequest is in completed state');
|
||||
equal(evt.tileRequest.withCredentials, true, 'withCredentials is set in tile request');
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-loaded', tileLoaded);
|
||||
viewer.addTiledImage({
|
||||
tileSource: customTileSource,
|
||||
ajaxWithCredentials: true
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest('tile-load-failed event includes AJAX request object', function() {
|
||||
// Create a tile source that points to a broken URL
|
||||
var brokenTileSource = OpenSeadragon.extend({}, customTileSource, {
|
||||
getTileUrl: function () {
|
||||
return '/test/data/testpattern.blob.invalid';
|
||||
}
|
||||
});
|
||||
|
||||
var tileLoadFailed = function tileLoadFailed(evt) {
|
||||
viewer.removeHandler('tile-load-failed', tileLoadFailed);
|
||||
ok(evt.tileRequest, 'Event includes tileRequest property');
|
||||
equal(evt.tileRequest.readyState, XMLHttpRequest.DONE, 'tileRequest is in completed state');
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-load-failed', tileLoadFailed);
|
||||
viewer.open(brokenTileSource);
|
||||
});
|
||||
|
||||
asyncTest('Headers can be set per-tile', function() {
|
||||
var tileLoaded = function tileLoaded(evt) {
|
||||
viewer.removeHandler('tile-loaded', tileLoaded);
|
||||
var tile = evt.tile;
|
||||
ok(tile, 'tile property exists on event');
|
||||
ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property');
|
||||
equal(tile.ajaxHeaders.Range, getTileRangeHeader(tile.level, tile.x, tile.y), 'Tile has correct range header.');
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-loaded', tileLoaded);
|
||||
|
||||
viewer.open(customTileSource);
|
||||
});
|
||||
|
||||
asyncTest('Headers are propagated correctly', function() {
|
||||
// Create a tile source that sets a static header for tiles
|
||||
var staticHeaderTileSource = OpenSeadragon.extend({}, customTileSource, {
|
||||
getTileAjaxHeaders: function() {
|
||||
return {
|
||||
'X-Tile-Header': 'TileHeaderValue'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var expectedHeaders = {
|
||||
'X-Viewer-Header': 'ViewerHeaderValue',
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue',
|
||||
'X-Tile-Header': 'TileHeaderValue'
|
||||
};
|
||||
|
||||
var tileLoaded = function tileLoaded(evt) {
|
||||
viewer.removeHandler('tile-loaded', tileLoaded);
|
||||
var tile = evt.tile;
|
||||
ok(tile, 'tile property exists on event');
|
||||
ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property');
|
||||
deepEqual(
|
||||
tile.ajaxHeaders, expectedHeaders,
|
||||
'Tile headers include headers set on Viewer and TiledImage'
|
||||
);
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-loaded', tileLoaded);
|
||||
|
||||
viewer.addTiledImage({
|
||||
ajaxHeaders: {
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue'
|
||||
},
|
||||
tileSource: staticHeaderTileSource
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest('Viewer headers are overwritten by TiledImage', function() {
|
||||
// Create a tile source that sets a static header for tiles
|
||||
var staticHeaderTileSource = OpenSeadragon.extend({}, customTileSource, {
|
||||
getTileAjaxHeaders: function() {
|
||||
return {
|
||||
'X-Tile-Header': 'TileHeaderValue'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var expectedHeaders = {
|
||||
'X-Viewer-Header': 'ViewerHeaderValue-Overwritten',
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue',
|
||||
'X-Tile-Header': 'TileHeaderValue'
|
||||
};
|
||||
|
||||
var tileLoaded = function tileLoaded(evt) {
|
||||
viewer.removeHandler('tile-loaded', tileLoaded);
|
||||
var tile = evt.tile;
|
||||
ok(tile, 'tile property exists on event');
|
||||
ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property');
|
||||
deepEqual(
|
||||
tile.ajaxHeaders, expectedHeaders,
|
||||
'TiledImage header overwrites viewer header'
|
||||
);
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-loaded', tileLoaded);
|
||||
|
||||
viewer.addTiledImage({
|
||||
ajaxHeaders: {
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue',
|
||||
'X-Viewer-Header': 'ViewerHeaderValue-Overwritten'
|
||||
},
|
||||
tileSource: staticHeaderTileSource
|
||||
});
|
||||
});
|
||||
|
||||
asyncTest('TiledImage headers are overwritten by Tile', function() {
|
||||
|
||||
var expectedHeaders = {
|
||||
'X-Viewer-Header': 'ViewerHeaderValue',
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue-Overwritten',
|
||||
'X-Tile-Header': 'TileHeaderValue'
|
||||
};
|
||||
|
||||
var tileLoaded = function tileLoaded(evt) {
|
||||
viewer.removeHandler('tile-loaded', tileLoaded);
|
||||
var tile = evt.tile;
|
||||
ok(tile, 'tile property exists on event');
|
||||
ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property');
|
||||
deepEqual(
|
||||
tile.ajaxHeaders, expectedHeaders,
|
||||
'Tile header overwrites TiledImage header'
|
||||
);
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('tile-loaded', tileLoaded);
|
||||
|
||||
// Create a tile source that sets a static header for tiles
|
||||
var staticHeaderTileSource = OpenSeadragon.extend({}, customTileSource, {
|
||||
getTileAjaxHeaders: function() {
|
||||
return {
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue-Overwritten',
|
||||
'X-Tile-Header': 'TileHeaderValue'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
viewer.addTiledImage({
|
||||
ajaxHeaders: {
|
||||
'X-TiledImage-Header': 'TiledImageHeaderValue'
|
||||
},
|
||||
tileSource: staticHeaderTileSource
|
||||
});
|
||||
});
|
||||
})();
|
@ -424,11 +424,47 @@
|
||||
|
||||
} );
|
||||
|
||||
|
||||
asyncTest('SetDebugMode', function() {
|
||||
ok(viewer, 'Viewer exists');
|
||||
|
||||
var checkImageTilesDebugState = function (expectedState) {
|
||||
|
||||
for (var i = 0; i < viewer.world.getItemCount(); i++) {
|
||||
if(viewer.world.getItemAt(i).debugMode != expectedState) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
|
||||
//Ensure we start with debug mode turned off
|
||||
viewer.setDebugMode(false);
|
||||
ok(checkImageTilesDebugState(false), "All image tiles have debug mode turned off.");
|
||||
ok(!viewer.debugMode, "Viewer debug mode is turned off.");
|
||||
|
||||
//Turn debug mode on and check that the Viewer and all tiled images are in debug mode.
|
||||
viewer.setDebugMode(true);
|
||||
ok(checkImageTilesDebugState(true), "All image tiles have debug mode turned on.");
|
||||
ok(viewer.debugMode, "Viewer debug mode is turned on.");
|
||||
|
||||
start();
|
||||
};
|
||||
|
||||
viewer.addHandler('open', openHandler);
|
||||
viewer.open('/test/data/testpattern.dzi');
|
||||
});
|
||||
|
||||
//Version numbers are injected by the build process, so skip version tests if we are only running code coverage
|
||||
if(!window.isCoverageTest ){
|
||||
test('version object', function() {
|
||||
equal(typeof OpenSeadragon.version.versionStr, "string", "versionStr should be a string");
|
||||
ok(OpenSeadragon.version.major >= 0, "major should be a positive number");
|
||||
ok(OpenSeadragon.version.minor >= 0, "minor should be a positive number");
|
||||
ok(OpenSeadragon.version.revision >= 0, "revision should be a positive number");
|
||||
});
|
||||
|
||||
}
|
||||
})();
|
||||
|
@ -36,6 +36,9 @@
|
||||
testImplicitTilesUrl(
|
||||
'/iiipsrv?DeepZoom=/path/my.dzi', '/iiipsrv?DeepZoom=/path/my_files/',
|
||||
'querystring in dzi url should not be ignored before slashes');
|
||||
testImplicitTilesUrl(
|
||||
'/fcg-bin/iipsrv.fcgi?Deepzoom=123test.tif.dzi', '/fcg-bin/iipsrv.fcgi?Deepzoom=123test.tif_files/',
|
||||
'filename in querystring does not have to contain slash');
|
||||
});
|
||||
|
||||
}());
|
||||
|
88
test/modules/imageloader.js
Normal file
@ -0,0 +1,88 @@
|
||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||
|
||||
(function() {
|
||||
var viewer,
|
||||
baseOptions = {
|
||||
id: 'example',
|
||||
prefixUrl: '/build/openseadragon/images/',
|
||||
springStiffness: 100 // Faster animation = faster tests
|
||||
};
|
||||
|
||||
module('ImageLoader', {
|
||||
setup: function () {
|
||||
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||
|
||||
testLog.reset();
|
||||
},
|
||||
teardown: function () {
|
||||
if (viewer && viewer.close) {
|
||||
viewer.close();
|
||||
}
|
||||
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// ----------
|
||||
|
||||
test('Default timeout', function() {
|
||||
var actual,
|
||||
expected = OpenSeadragon.DEFAULT_SETTINGS.timeout,
|
||||
message,
|
||||
options = OpenSeadragon.extend(true, baseOptions, {
|
||||
imageLoaderLimit: 1
|
||||
}),
|
||||
viewer = OpenSeadragon(options),
|
||||
imageLoader = viewer.imageLoader;
|
||||
|
||||
message = 'ImageLoader timeout should be set to the default value of ' + expected + ' when none is specified';
|
||||
actual = imageLoader.timeout;
|
||||
equal(actual, expected, message);
|
||||
|
||||
// Manually seize the ImageLoader
|
||||
imageLoader.jobsInProgress = imageLoader.jobLimit;
|
||||
imageLoader.addJob({
|
||||
src: 'test',
|
||||
loadWithAjax: false,
|
||||
crossOriginPolicy: 'test',
|
||||
ajaxWithCredentials: false,
|
||||
abort: function() {}
|
||||
});
|
||||
|
||||
message = 'ImageJob should inherit the ImageLoader timeout value';
|
||||
actual = imageLoader.jobQueue.shift().timeout;
|
||||
equal(actual, expected, message);
|
||||
});
|
||||
|
||||
// ----------
|
||||
|
||||
test('Configure timeout', function() {
|
||||
var actual,
|
||||
expected = 123456,
|
||||
message,
|
||||
options = OpenSeadragon.extend(true, baseOptions, {
|
||||
imageLoaderLimit: 1,
|
||||
timeout: expected
|
||||
}),
|
||||
viewer = OpenSeadragon(options),
|
||||
imageLoader = viewer.imageLoader;
|
||||
|
||||
message = 'ImageLoader timeout should be configurable';
|
||||
actual = imageLoader.timeout;
|
||||
equal(actual, expected, message);
|
||||
|
||||
imageLoader.jobsInProgress = imageLoader.jobLimit;
|
||||
imageLoader.addJob({
|
||||
src: 'test',
|
||||
loadWithAjax: false,
|
||||
crossOriginPolicy: 'test',
|
||||
ajaxWithCredentials: false,
|
||||
abort: function() {}
|
||||
});
|
||||
|
||||
message = 'ImageJob should inherit the ImageLoader timeout value';
|
||||
actual = imageLoader.jobQueue.shift().timeout;
|
||||
equal(actual, expected, message);
|
||||
});
|
||||
|
||||
})();
|
@ -844,6 +844,88 @@
|
||||
viewer.addHandler('open', openHandler);
|
||||
});
|
||||
|
||||
asyncTest('Item opacity is synchronized', function() {
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
var navOpenHandler = function(event) {
|
||||
if (viewer.navigator.world.getItemCount() === 2) {
|
||||
viewer.navigator.world.removeHandler('add-item', navOpenHandler);
|
||||
|
||||
setTimeout(function() {
|
||||
// Test initial formation
|
||||
for (var i = 0; i < 2; i++) {
|
||||
equal(viewer.navigator.world.getItemAt(i).getOpacity(),
|
||||
viewer.world.getItemAt(i).getOpacity(), 'opacity is the same');
|
||||
}
|
||||
|
||||
// Try changing the opacity of one
|
||||
viewer.world.getItemAt(1).setOpacity(0.5);
|
||||
equal(viewer.navigator.world.getItemAt(1).getOpacity(),
|
||||
viewer.world.getItemAt(1).getOpacity(), 'opacity is the same after change');
|
||||
|
||||
start();
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
var openHandler = function() {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
viewer.navigator.world.addHandler('add-item', navOpenHandler);
|
||||
// The navigator may already have added the items.
|
||||
navOpenHandler();
|
||||
};
|
||||
|
||||
viewer.addHandler('open', openHandler);
|
||||
});
|
||||
|
||||
asyncTest('Item composite operation is synchronized', function() {
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
var navOpenHandler = function(event) {
|
||||
if (viewer.navigator.world.getItemCount() === 2) {
|
||||
viewer.navigator.world.removeHandler('add-item', navOpenHandler);
|
||||
|
||||
setTimeout(function() {
|
||||
// Test initial formation
|
||||
for (var i = 0; i < 2; i++) {
|
||||
equal(viewer.navigator.world.getItemAt(i).getCompositeOperation(),
|
||||
viewer.world.getItemAt(i).getCompositeOperation(), 'composite operation is the same');
|
||||
}
|
||||
|
||||
// Try changing the composite operation of one
|
||||
viewer.world.getItemAt(1).setCompositeOperation('multiply');
|
||||
equal(viewer.navigator.world.getItemAt(1).getCompositeOperation(),
|
||||
viewer.world.getItemAt(1).getCompositeOperation(), 'composite operation is the same after change');
|
||||
|
||||
start();
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
var openHandler = function() {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
viewer.navigator.world.addHandler('add-item', navOpenHandler);
|
||||
// The navigator may already have added the items.
|
||||
navOpenHandler();
|
||||
};
|
||||
|
||||
viewer.addHandler('open', openHandler);
|
||||
});
|
||||
|
||||
asyncTest('Viewer options transmitted to navigator', function() {
|
||||
|
||||
viewer = OpenSeadragon({
|
||||
|
@ -25,12 +25,14 @@
|
||||
|
||||
var fakeTile0 = {
|
||||
url: 'foo.jpg',
|
||||
cacheKey: 'foo.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var fakeTile1 = {
|
||||
url: 'foo.jpg',
|
||||
cacheKey: 'foo.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
@ -74,18 +76,21 @@
|
||||
|
||||
var fakeTile0 = {
|
||||
url: 'different.jpg',
|
||||
cacheKey: 'different.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var fakeTile1 = {
|
||||
url: 'same.jpg',
|
||||
cacheKey: 'same.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
||||
var fakeTile2 = {
|
||||
url: 'same.jpg',
|
||||
cacheKey: 'same.jpg',
|
||||
image: {},
|
||||
unload: function() {}
|
||||
};
|
||||
|
@ -36,7 +36,7 @@
|
||||
var TALL_PATH = '/test/data/tall.dzi';
|
||||
var WIDE_PATH = '/test/data/wide.dzi';
|
||||
|
||||
var testZoomLevels = [-1, 0, 0.1, 0.5, 4, 10];
|
||||
var testZoomLevels = [0.1, 0.2, 0.5, 1, 4, 10];
|
||||
|
||||
var testPoints = [
|
||||
new OpenSeadragon.Point(0, 0),
|
||||
@ -59,7 +59,6 @@
|
||||
var reopenViewerHelper = function(config) {
|
||||
var expected, level, actual, i = 0;
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
var viewport = viewer.viewport;
|
||||
expected = config.processExpected(level, expected);
|
||||
actual = viewport[config.method]();
|
||||
@ -70,7 +69,7 @@
|
||||
"Test " + config.method + " with zoom level of " + level + ". Expected : " + expected + ", got " + actual
|
||||
);
|
||||
i++;
|
||||
if(i < testZoomLevels.length){
|
||||
if (i < testZoomLevels.length) {
|
||||
level = expected = testZoomLevels[i];
|
||||
var viewerConfig = {
|
||||
id: VIEWER_ID,
|
||||
@ -80,15 +79,22 @@
|
||||
|
||||
viewerConfig[config.property] = level;
|
||||
viewer = OpenSeadragon(viewerConfig);
|
||||
viewer.addHandler('open', openHandler);
|
||||
viewer.addOnceHandler('open', openHandler);
|
||||
viewer.open(DZI_PATH);
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
};
|
||||
viewer.addHandler('open', openHandler);
|
||||
level = expected = testZoomLevels[i];
|
||||
viewer[config.property] = level;
|
||||
var viewerConfig = {
|
||||
id: VIEWER_ID,
|
||||
prefixUrl: PREFIX_URL,
|
||||
springStiffness: SPRING_STIFFNESS
|
||||
};
|
||||
|
||||
viewerConfig[config.property] = level;
|
||||
viewer = OpenSeadragon(viewerConfig);
|
||||
viewer.addOnceHandler('open', openHandler);
|
||||
viewer.open(DZI_PATH);
|
||||
};
|
||||
|
||||
@ -211,15 +217,9 @@
|
||||
property: 'defaultZoomLevel',
|
||||
method: 'getHomeBounds',
|
||||
processExpected: function(level, expected) {
|
||||
// Have to special case this to avoid dividing by 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
|
||||
var position = 0.5 - (sideLength / 2.0);
|
||||
expected = new OpenSeadragon.Rect(position, position, sideLength, sideLength);
|
||||
}
|
||||
return expected;
|
||||
return new OpenSeadragon.Rect(position, position, sideLength, sideLength);
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -333,44 +333,39 @@
|
||||
// I don't use the helper for this one because it sets a couple more
|
||||
// properties that would need special casing.
|
||||
asyncTest('getHomeZoomWithHomeFillsViewer', function() {
|
||||
var expected, level, i = 0;
|
||||
var i = 0;
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
var viewport = viewer.viewport;
|
||||
viewport.zoomTo(ZOOM_FACTOR, null, true);
|
||||
|
||||
// Special cases for oddball levels
|
||||
if (level === -1) {
|
||||
expected = 0.25;
|
||||
} else if(level === 0){
|
||||
expected = 1;
|
||||
}
|
||||
|
||||
equal(
|
||||
viewport.getHomeZoom(),
|
||||
expected,
|
||||
"Test getHomeZoom with homeFillsViewer = true and default zoom level of " + expected
|
||||
testZoomLevels[i],
|
||||
"Test getHomeZoom with homeFillsViewer = true and default zoom level of " + testZoomLevels[i]
|
||||
);
|
||||
i++;
|
||||
if(i < testZoomLevels.length){
|
||||
level = expected = testZoomLevels[i];
|
||||
if (i < testZoomLevels.length) {
|
||||
viewer = OpenSeadragon({
|
||||
id: VIEWER_ID,
|
||||
prefixUrl: PREFIX_URL,
|
||||
springStiffness: SPRING_STIFFNESS,
|
||||
defaultZoomLevel: level,
|
||||
defaultZoomLevel: testZoomLevels[i],
|
||||
homeFillsViewer: true
|
||||
});
|
||||
viewer.addHandler('open', openHandler);
|
||||
viewer.addOnceHandler('open', openHandler);
|
||||
viewer.open(TALL_PATH); // use a different image for homeFillsViewer
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
};
|
||||
viewer.addHandler('open', openHandler);
|
||||
level = expected = testZoomLevels[i];
|
||||
viewer.homeFillsViewer = true;
|
||||
viewer.defaultZoomLevel = expected;
|
||||
viewer = OpenSeadragon({
|
||||
id: VIEWER_ID,
|
||||
prefixUrl: PREFIX_URL,
|
||||
springStiffness: SPRING_STIFFNESS,
|
||||
defaultZoomLevel: testZoomLevels[i],
|
||||
homeFillsViewer: true
|
||||
});
|
||||
viewer.addOnceHandler('open', openHandler);
|
||||
viewer.open(TALL_PATH); // use a different image for homeFillsViewer
|
||||
});
|
||||
|
||||
@ -725,27 +720,18 @@
|
||||
viewer.open(DZI_PATH);
|
||||
});
|
||||
|
||||
asyncTest('zoomBy', function(){
|
||||
asyncTest('zoomBy no ref point', function() {
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
var viewport = viewer.viewport;
|
||||
|
||||
for (var i = 0; i < testZoomLevels.length; i++){
|
||||
for (var i = 0; i < testZoomLevels.length; i++) {
|
||||
viewport.zoomBy(testZoomLevels[i], null, true);
|
||||
propEqual(
|
||||
viewport.getZoom(),
|
||||
testZoomLevels[i],
|
||||
"Zoomed by the correct amount."
|
||||
);
|
||||
|
||||
// now use a ref point
|
||||
// TODO: check the ending position due to ref point
|
||||
viewport.zoomBy(testZoomLevels[i], testPoints[i], true);
|
||||
propEqual(
|
||||
viewport.getZoom(),
|
||||
testZoomLevels[i],
|
||||
"Zoomed by the correct amount."
|
||||
);
|
||||
}
|
||||
|
||||
start();
|
||||
@ -754,27 +740,88 @@
|
||||
viewer.open(DZI_PATH);
|
||||
});
|
||||
|
||||
asyncTest('zoomTo', function(){
|
||||
asyncTest('zoomBy with ref point', function() {
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
var viewport = viewer.viewport;
|
||||
|
||||
for (var i = 0; i < testZoomLevels.length; i++){
|
||||
var expectedCenters = [
|
||||
new OpenSeadragon.Point(5, 5),
|
||||
new OpenSeadragon.Point(6.996, 6.996),
|
||||
new OpenSeadragon.Point(7.246, 6.996),
|
||||
new OpenSeadragon.Point(7.246, 6.996),
|
||||
new OpenSeadragon.Point(7.621, 7.371),
|
||||
new OpenSeadragon.Point(7.621, 7.371),
|
||||
];
|
||||
|
||||
for (var i = 0; i < testZoomLevels.length; i++) {
|
||||
viewport.zoomBy(testZoomLevels[i], testPoints[i], true);
|
||||
propEqual(
|
||||
viewport.getZoom(),
|
||||
testZoomLevels[i],
|
||||
"Zoomed by the correct amount."
|
||||
);
|
||||
assertPointsEquals(
|
||||
viewport.getCenter(),
|
||||
expectedCenters[i],
|
||||
1e-14,
|
||||
"Panned to the correct location."
|
||||
);
|
||||
}
|
||||
|
||||
start();
|
||||
};
|
||||
viewer.addHandler('open', openHandler);
|
||||
viewer.open(DZI_PATH);
|
||||
});
|
||||
|
||||
asyncTest('zoomTo no ref point', function() {
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
var viewport = viewer.viewport;
|
||||
|
||||
for (var i = 0; i < testZoomLevels.length; i++) {
|
||||
viewport.zoomTo(testZoomLevels[i], null, true);
|
||||
propEqual(
|
||||
viewport.getZoom(),
|
||||
testZoomLevels[i],
|
||||
"Zoomed to the correct level."
|
||||
);
|
||||
}
|
||||
|
||||
// now use a ref point
|
||||
// TODO: check the ending position due to ref point
|
||||
start();
|
||||
};
|
||||
viewer.addHandler('open', openHandler);
|
||||
viewer.open(DZI_PATH);
|
||||
});
|
||||
|
||||
asyncTest('zoomTo with ref point', function() {
|
||||
var openHandler = function(event) {
|
||||
viewer.removeHandler('open', openHandler);
|
||||
var viewport = viewer.viewport;
|
||||
|
||||
var expectedCenters = [
|
||||
new OpenSeadragon.Point(5, 5),
|
||||
new OpenSeadragon.Point(4.7505, 4.7505),
|
||||
new OpenSeadragon.Point(4.6005, 4.7505),
|
||||
new OpenSeadragon.Point(4.8455, 4.9955),
|
||||
new OpenSeadragon.Point(5.2205, 5.3705),
|
||||
new OpenSeadragon.Point(5.2205, 5.3705),
|
||||
];
|
||||
|
||||
for (var i = 0; i < testZoomLevels.length; i++) {
|
||||
viewport.zoomTo(testZoomLevels[i], testPoints[i], true);
|
||||
propEqual(
|
||||
viewport.getZoom(),
|
||||
testZoomLevels[i],
|
||||
"Zoomed to the correct level."
|
||||
);
|
||||
assertPointsEquals(
|
||||
viewport.getCenter(),
|
||||
expectedCenters[i],
|
||||
1e-14,
|
||||
"Panned to the correct location."
|
||||
);
|
||||
}
|
||||
|
||||
start();
|
||||
|
@ -42,6 +42,8 @@
|
||||
<script src="/test/modules/tilesourcecollection.js"></script>
|
||||
<script src="/test/modules/spring.js"></script>
|
||||
<script src="/test/modules/rectangle.js"></script>
|
||||
<script src="/test/modules/ajax-tiles.js"></script>
|
||||
<script src="/test/modules/imageloader.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>
|
||||
|