mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-29 00:26:10 +03:00
Merge pull request #1055 from seanich/ajax-tile-loading
Add options for loading tiles via AJAX and custom AJAX request headers. (resolves #1032)
This commit is contained in:
commit
9fa1ac2bf9
@ -32,15 +32,26 @@
|
|||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* 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.
|
||||||
|
*/
|
||||||
|
function ImageJob (options) {
|
||||||
|
|
||||||
$.extend( true, this, {
|
$.extend(true, this, {
|
||||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||||
jobId: null
|
jobId: null
|
||||||
}, options );
|
}, options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image object which will contain downloaded image.
|
* Image object which will contain downloaded image.
|
||||||
@ -52,42 +63,103 @@ function ImageJob ( options ) {
|
|||||||
|
|
||||||
ImageJob.prototype = {
|
ImageJob.prototype = {
|
||||||
errorMsg: null,
|
errorMsg: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the image job.
|
||||||
|
* @method
|
||||||
|
*/
|
||||||
start: function(){
|
start: function(){
|
||||||
var _this = this;
|
var self = this;
|
||||||
|
var selfAbort = this.abort;
|
||||||
|
|
||||||
this.image = new Image();
|
this.image = new Image();
|
||||||
|
|
||||||
if ( this.crossOriginPolicy !== false ) {
|
|
||||||
this.image.crossOrigin = this.crossOriginPolicy;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.image.onload = function(){
|
this.image.onload = function(){
|
||||||
_this.finish( true );
|
self.finish(true);
|
||||||
};
|
};
|
||||||
this.image.onabort = this.image.onerror = function(){
|
this.image.onabort = this.image.onerror = function() {
|
||||||
_this.errorMsg = "Image load aborted";
|
self.errorMsg = "Image load aborted";
|
||||||
_this.finish( false );
|
self.finish(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.jobId = window.setTimeout( function(){
|
this.jobId = window.setTimeout(function(){
|
||||||
_this.errorMsg = "Image load exceeded timeout";
|
self.errorMsg = "Image load exceeded timeout";
|
||||||
_this.finish( false );
|
self.finish(false);
|
||||||
}, this.timeout);
|
}, this.timeout);
|
||||||
|
|
||||||
this.image.src = this.src;
|
// 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.src = this.src;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
finish: function( successful ) {
|
finish: function(successful) {
|
||||||
this.image.onload = this.image.onerror = this.image.onabort = null;
|
this.image.onload = this.image.onerror = this.image.onabort = null;
|
||||||
if (!successful) {
|
if (!successful) {
|
||||||
this.image = null;
|
this.image = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this.jobId ) {
|
if (this.jobId) {
|
||||||
window.clearTimeout( this.jobId );
|
window.clearTimeout(this.jobId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.callback( this );
|
this.callback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -100,13 +172,13 @@ ImageJob.prototype = {
|
|||||||
* @param {Object} options - Options for this ImageLoader.
|
* @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.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.
|
||||||
*/
|
*/
|
||||||
$.ImageLoader = function( options ) {
|
$.ImageLoader = function(options) {
|
||||||
|
|
||||||
$.extend( true, this, {
|
$.extend(true, this, {
|
||||||
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
||||||
jobQueue: [],
|
jobQueue: [],
|
||||||
jobsInProgress: 0
|
jobsInProgress: 0
|
||||||
}, options );
|
}, options);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -116,22 +188,31 @@ $.ImageLoader.prototype = {
|
|||||||
/**
|
/**
|
||||||
* Add an unloaded image to the loader queue.
|
* Add an unloaded image to the loader queue.
|
||||||
* @method
|
* @method
|
||||||
* @param {String} src - URL of image to download.
|
* @param {Object} options - Options for this job.
|
||||||
* @param {String} crossOriginPolicy - CORS policy to use for downloads
|
* @param {String} [options.src] - URL of image to download.
|
||||||
* @param {Function} callback - Called once image has been downloaded.
|
* @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,
|
var _this = this,
|
||||||
complete = function( job ) {
|
complete = function(job) {
|
||||||
completeJob( _this, job, options.callback );
|
completeJob(_this, job, options.callback);
|
||||||
},
|
},
|
||||||
jobOptions = {
|
jobOptions = {
|
||||||
src: options.src,
|
src: options.src,
|
||||||
|
loadWithAjax: options.loadWithAjax,
|
||||||
|
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
|
||||||
crossOriginPolicy: options.crossOriginPolicy,
|
crossOriginPolicy: options.crossOriginPolicy,
|
||||||
|
ajaxWithCredentials: options.ajaxWithCredentials,
|
||||||
callback: complete,
|
callback: complete,
|
||||||
abort: options.abort
|
abort: options.abort
|
||||||
},
|
},
|
||||||
newJob = new ImageJob( jobOptions );
|
newJob = new ImageJob(jobOptions);
|
||||||
|
|
||||||
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
|
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
|
||||||
newJob.start();
|
newJob.start();
|
||||||
@ -166,18 +247,18 @@ $.ImageLoader.prototype = {
|
|||||||
* @param job - The ImageJob that has completed.
|
* @param job - The ImageJob that has completed.
|
||||||
* @param callback - Called once cleanup is finished.
|
* @param callback - Called once cleanup is finished.
|
||||||
*/
|
*/
|
||||||
function completeJob( loader, job, callback ) {
|
function completeJob(loader, job, callback) {
|
||||||
var nextJob;
|
var nextJob;
|
||||||
|
|
||||||
loader.jobsInProgress--;
|
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 = loader.jobQueue.shift();
|
||||||
nextJob.start();
|
nextJob.start();
|
||||||
loader.jobsInProgress++;
|
loader.jobsInProgress++;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback( job.image, job.errorMsg );
|
callback(job.image, job.errorMsg, job.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
}( OpenSeadragon ));
|
}(OpenSeadragon));
|
||||||
|
@ -584,9 +584,16 @@
|
|||||||
* not use CORS, and the canvas will be tainted.
|
* not use CORS, and the canvas will be tainted.
|
||||||
*
|
*
|
||||||
* @property {Boolean} [ajaxWithCredentials=false]
|
* @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.
|
* 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.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1005,6 +1012,8 @@ function OpenSeadragon( options ){
|
|||||||
initialPage: 0,
|
initialPage: 0,
|
||||||
crossOriginPolicy: false,
|
crossOriginPolicy: false,
|
||||||
ajaxWithCredentials: false,
|
ajaxWithCredentials: false,
|
||||||
|
loadTilesWithAjax: false,
|
||||||
|
ajaxHeaders: {},
|
||||||
|
|
||||||
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
|
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
|
||||||
panHorizontal: true,
|
panHorizontal: true,
|
||||||
@ -2120,11 +2129,16 @@ function OpenSeadragon( options ){
|
|||||||
* @param {String} options.url - the url to request
|
* @param {String} options.url - the url to request
|
||||||
* @param {Function} options.success - a function to call on a successful response
|
* @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 {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
|
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
|
||||||
* @throws {Error}
|
* @throws {Error}
|
||||||
|
* @returns {XMLHttpRequest}
|
||||||
*/
|
*/
|
||||||
makeAjaxRequest: function( url, onSuccess, onError ) {
|
makeAjaxRequest: function( url, onSuccess, onError ) {
|
||||||
var withCredentials;
|
var withCredentials;
|
||||||
|
var headers;
|
||||||
|
var responseType;
|
||||||
|
|
||||||
// Note that our preferred API is that you pass in a single object; the named
|
// Note that our preferred API is that you pass in a single object; the named
|
||||||
// arguments are for legacy support.
|
// arguments are for legacy support.
|
||||||
@ -2132,6 +2146,8 @@ function OpenSeadragon( options ){
|
|||||||
onSuccess = url.success;
|
onSuccess = url.success;
|
||||||
onError = url.error;
|
onError = url.error;
|
||||||
withCredentials = url.withCredentials;
|
withCredentials = url.withCredentials;
|
||||||
|
headers = url.headers;
|
||||||
|
responseType = url.responseType || null;
|
||||||
url = url.url;
|
url = url.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2147,9 +2163,9 @@ function OpenSeadragon( options ){
|
|||||||
if ( request.readyState == 4 ) {
|
if ( request.readyState == 4 ) {
|
||||||
request.onreadystatechange = function(){};
|
request.onreadystatechange = function(){};
|
||||||
|
|
||||||
// With protocols other than http/https, the status is 200
|
// With protocols other than http/https, a successful request status is in
|
||||||
// on Firefox and 0 on other browsers
|
// the 200's on Firefox and 0 on other browsers
|
||||||
if ( request.status === 200 ||
|
if ( (request.status >= 200 && request.status < 300) ||
|
||||||
( request.status === 0 &&
|
( request.status === 0 &&
|
||||||
protocol !== "http:" &&
|
protocol !== "http:" &&
|
||||||
protocol !== "https:" )) {
|
protocol !== "https:" )) {
|
||||||
@ -2167,11 +2183,23 @@ function OpenSeadragon( options ){
|
|||||||
try {
|
try {
|
||||||
request.open( "GET", url, true );
|
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) {
|
if (withCredentials) {
|
||||||
request.withCredentials = true;
|
request.withCredentials = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
request.send( null );
|
request.send(null);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var msg = e.message;
|
var msg = e.message;
|
||||||
|
|
||||||
@ -2231,6 +2259,8 @@ function OpenSeadragon( options ){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
27
src/tile.js
27
src/tile.js
@ -47,8 +47,10 @@
|
|||||||
* @param {String} url The URL of this tile's image.
|
* @param {String} url The URL of this tile's image.
|
||||||
* @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
|
* @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
|
||||||
* is provided directly by the tile source.
|
* is provided directly by the tile source.
|
||||||
|
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
|
||||||
|
* @param {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.
|
* The zoom level this tile belongs to.
|
||||||
* @member {Number} level
|
* @member {Number} level
|
||||||
@ -91,6 +93,29 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D) {
|
|||||||
* @memberOf OpenSeadragon.Tile#
|
* @memberOf OpenSeadragon.Tile#
|
||||||
*/
|
*/
|
||||||
this.context2D = context2D;
|
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?
|
* Is this tile loaded?
|
||||||
* @member {Boolean} 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.
|
* may temporarily surpass that number, but should eventually come back down to the max specified.
|
||||||
* @param {Object} options - Tile info.
|
* @param {Object} options - Tile info.
|
||||||
* @param {OpenSeadragon.Tile} options.tile - The tile to cache.
|
* @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 {Image} options.image - The image of the tile to cache.
|
||||||
* @param {OpenSeadragon.TiledImage} options.tiledImage - The TiledImage that owns that tile.
|
* @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
|
* @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 ) {
|
cacheTile: function( options ) {
|
||||||
$.console.assert( options, "[TileCache.cacheTile] options is required" );
|
$.console.assert( options, "[TileCache.cacheTile] options is required" );
|
||||||
$.console.assert( options.tile, "[TileCache.cacheTile] options.tile 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" );
|
$.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" );
|
||||||
|
|
||||||
var cutoff = options.cutoff || 0;
|
var cutoff = options.cutoff || 0;
|
||||||
var insertionIndex = this._tilesLoaded.length;
|
var insertionIndex = this._tilesLoaded.length;
|
||||||
|
|
||||||
var imageRecord = this._imagesLoaded[options.tile.url];
|
var imageRecord = this._imagesLoaded[options.tile.cacheKey];
|
||||||
if (!imageRecord) {
|
if (!imageRecord) {
|
||||||
$.console.assert( options.image, "[TileCache.cacheTile] options.image is required to create an 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
|
image: options.image
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -232,9 +233,9 @@ $.TileCache.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
getImageRecord: function(url) {
|
getImageRecord: function(cacheKey) {
|
||||||
$.console.assert(url, '[TileCache.getImageRecord] url is required');
|
$.console.assert(cacheKey, '[TileCache.getImageRecord] cacheKey is required');
|
||||||
return this._imagesLoaded[url];
|
return this._imagesLoaded[cacheKey];
|
||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
@ -246,11 +247,11 @@ $.TileCache.prototype = {
|
|||||||
tile.unload();
|
tile.unload();
|
||||||
tile.cacheImageRecord = null;
|
tile.cacheImageRecord = null;
|
||||||
|
|
||||||
var imageRecord = this._imagesLoaded[tile.url];
|
var imageRecord = this._imagesLoaded[tile.cacheKey];
|
||||||
imageRecord.removeTile(tile);
|
imageRecord.removeTile(tile);
|
||||||
if (!imageRecord.getTileCount()) {
|
if (!imageRecord.getTileCount()) {
|
||||||
imageRecord.destroy();
|
imageRecord.destroy();
|
||||||
delete this._imagesLoaded[tile.url];
|
delete this._imagesLoaded[tile.cacheKey];
|
||||||
this._imagesLoadedCount--;
|
this._imagesLoadedCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,12 @@
|
|||||||
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
|
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
|
||||||
* @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - 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 {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 ) {
|
$.TiledImage = function( options ) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
@ -161,6 +167,7 @@ $.TiledImage = function( options ) {
|
|||||||
iOSDevice: $.DEFAULT_SETTINGS.iOSDevice,
|
iOSDevice: $.DEFAULT_SETTINGS.iOSDevice,
|
||||||
debugMode: $.DEFAULT_SETTINGS.debugMode,
|
debugMode: $.DEFAULT_SETTINGS.debugMode,
|
||||||
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
|
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
|
||||||
|
ajaxWithCredentials: $.DEFAULT_SETTINGS.ajaxWithCredentials,
|
||||||
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
||||||
opacity: $.DEFAULT_SETTINGS.opacity,
|
opacity: $.DEFAULT_SETTINGS.opacity,
|
||||||
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
|
compositeOperation: $.DEFAULT_SETTINGS.compositeOperation
|
||||||
@ -1179,6 +1186,7 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
|||||||
var tile = getTile(
|
var tile = getTile(
|
||||||
x, y,
|
x, y,
|
||||||
level,
|
level,
|
||||||
|
tiledImage,
|
||||||
tiledImage.source,
|
tiledImage.source,
|
||||||
tiledImage.tilesMatrix,
|
tiledImage.tilesMatrix,
|
||||||
currentTime,
|
currentTime,
|
||||||
@ -1237,7 +1245,7 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
|||||||
if (tile.context2D) {
|
if (tile.context2D) {
|
||||||
setTileLoaded(tiledImage, tile);
|
setTileLoaded(tiledImage, tile);
|
||||||
} else {
|
} else {
|
||||||
var imageRecord = tiledImage._tileCache.getImageRecord(tile.url);
|
var imageRecord = tiledImage._tileCache.getImageRecord(tile.cacheKey);
|
||||||
if (imageRecord) {
|
if (imageRecord) {
|
||||||
var image = imageRecord.getImage();
|
var image = imageRecord.getImage();
|
||||||
setTileLoaded(tiledImage, tile, image);
|
setTileLoaded(tiledImage, tile, image);
|
||||||
@ -1275,6 +1283,7 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
|||||||
* @param {Number} x
|
* @param {Number} x
|
||||||
* @param {Number} y
|
* @param {Number} y
|
||||||
* @param {Number} level
|
* @param {Number} level
|
||||||
|
* @param {OpenSeadragon.TiledImage} tiledImage
|
||||||
* @param {OpenSeadragon.TileSource} tileSource
|
* @param {OpenSeadragon.TileSource} tileSource
|
||||||
* @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.
|
* @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.
|
||||||
* @param {Number} time
|
* @param {Number} time
|
||||||
@ -1283,12 +1292,23 @@ function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity
|
|||||||
* @param {Number} worldHeight
|
* @param {Number} worldHeight
|
||||||
* @returns {OpenSeadragon.Tile}
|
* @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,
|
var xMod,
|
||||||
yMod,
|
yMod,
|
||||||
bounds,
|
bounds,
|
||||||
exists,
|
exists,
|
||||||
url,
|
url,
|
||||||
|
ajaxHeaders,
|
||||||
context2D,
|
context2D,
|
||||||
tile;
|
tile;
|
||||||
|
|
||||||
@ -1305,6 +1325,18 @@ function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, worldWid
|
|||||||
bounds = tileSource.getTileBounds( level, xMod, yMod );
|
bounds = tileSource.getTileBounds( level, xMod, yMod );
|
||||||
exists = tileSource.tileExists( level, xMod, yMod );
|
exists = tileSource.tileExists( level, xMod, yMod );
|
||||||
url = tileSource.getTileUrl( 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 ?
|
context2D = tileSource.getContext2D ?
|
||||||
tileSource.getContext2D(level, xMod, yMod) : undefined;
|
tileSource.getContext2D(level, xMod, yMod) : undefined;
|
||||||
|
|
||||||
@ -1318,7 +1350,9 @@ function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, worldWid
|
|||||||
bounds,
|
bounds,
|
||||||
exists,
|
exists,
|
||||||
url,
|
url,
|
||||||
context2D
|
context2D,
|
||||||
|
tiledImage.loadTilesWithAjax,
|
||||||
|
ajaxHeaders
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1340,9 +1374,12 @@ function loadTile( tiledImage, tile, time ) {
|
|||||||
tile.loading = true;
|
tile.loading = true;
|
||||||
tiledImage._imageLoader.addJob({
|
tiledImage._imageLoader.addJob({
|
||||||
src: tile.url,
|
src: tile.url,
|
||||||
|
loadWithAjax: tile.loadWithAjax,
|
||||||
|
ajaxHeaders: tile.ajaxHeaders,
|
||||||
crossOriginPolicy: tiledImage.crossOriginPolicy,
|
crossOriginPolicy: tiledImage.crossOriginPolicy,
|
||||||
callback: function( image, errorMsg ){
|
ajaxWithCredentials: tiledImage.ajaxWithCredentials,
|
||||||
onTileLoad( tiledImage, tile, time, image, errorMsg );
|
callback: function( image, errorMsg, tileRequest ){
|
||||||
|
onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest );
|
||||||
},
|
},
|
||||||
abort: function() {
|
abort: function() {
|
||||||
tile.loading = false;
|
tile.loading = false;
|
||||||
@ -1359,8 +1396,9 @@ function loadTile( tiledImage, tile, time ) {
|
|||||||
* @param {Number} time
|
* @param {Number} time
|
||||||
* @param {Image} image
|
* @param {Image} image
|
||||||
* @param {String} errorMsg
|
* @param {String} errorMsg
|
||||||
|
* @param {XMLHttpRequest} tileRequest
|
||||||
*/
|
*/
|
||||||
function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
function onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest ) {
|
||||||
if ( !image ) {
|
if ( !image ) {
|
||||||
$.console.log( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
|
$.console.log( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
|
||||||
/**
|
/**
|
||||||
@ -1373,8 +1411,15 @@ function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
|||||||
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.
|
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.
|
||||||
* @property {number} time - The time in milliseconds when the tile load began.
|
* @property {number} time - The time in milliseconds when the tile load began.
|
||||||
* @property {string} message - The error message.
|
* @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.loading = false;
|
||||||
tile.exists = false;
|
tile.exists = false;
|
||||||
return;
|
return;
|
||||||
@ -1389,7 +1434,7 @@ function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
|||||||
var finish = function() {
|
var finish = function() {
|
||||||
var cutoff = Math.ceil( Math.log(
|
var cutoff = Math.ceil( Math.log(
|
||||||
tiledImage.source.getTileWidth(tile.level) ) / Math.log( 2 ) );
|
tiledImage.source.getTileWidth(tile.level) ) / Math.log( 2 ) );
|
||||||
setTileLoaded(tiledImage, tile, image, cutoff);
|
setTileLoaded(tiledImage, tile, image, cutoff, tileRequest);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if we're mid-update; this can happen on IE8 because image load events for
|
// Check if we're mid-update; this can happen on IE8 because image load events for
|
||||||
@ -1410,7 +1455,7 @@ function onTileLoad( tiledImage, tile, time, image, errorMsg ) {
|
|||||||
* @param {Image} image
|
* @param {Image} image
|
||||||
* @param {Number} cutoff
|
* @param {Number} cutoff
|
||||||
*/
|
*/
|
||||||
function setTileLoaded(tiledImage, tile, image, cutoff) {
|
function setTileLoaded(tiledImage, tile, image, cutoff, tileRequest) {
|
||||||
var increment = 0;
|
var increment = 0;
|
||||||
|
|
||||||
function getCompletionCallback() {
|
function getCompletionCallback() {
|
||||||
@ -1445,6 +1490,7 @@ function setTileLoaded(tiledImage, tile, image, cutoff) {
|
|||||||
* @property {Image} image - The image of the tile.
|
* @property {Image} image - The image of the tile.
|
||||||
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
|
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
|
||||||
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
|
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
|
||||||
|
* @property {XMLHttpRequest} tiledImage - The AJAX request that loaded this tile (if applicable).
|
||||||
* @property {function} getCompletionCallback - A function giving a callback to call
|
* @property {function} getCompletionCallback - A function giving a callback to call
|
||||||
* when the asynchronous processing of the image is done. The image will be
|
* 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
|
* marked as entirely loaded when the callback has been called once for each
|
||||||
@ -1453,6 +1499,7 @@ function setTileLoaded(tiledImage, tile, image, cutoff) {
|
|||||||
tiledImage.viewer.raiseEvent("tile-loaded", {
|
tiledImage.viewer.raiseEvent("tile-loaded", {
|
||||||
tile: tile,
|
tile: tile,
|
||||||
tiledImage: tiledImage,
|
tiledImage: tiledImage,
|
||||||
|
tileRequest: tileRequest,
|
||||||
image: image,
|
image: image,
|
||||||
getCompletionCallback: getCompletionCallback
|
getCompletionCallback: getCompletionCallback
|
||||||
});
|
});
|
||||||
|
@ -65,6 +65,8 @@
|
|||||||
* @param {Boolean} [options.ajaxWithCredentials]
|
* @param {Boolean} [options.ajaxWithCredentials]
|
||||||
* If this TileSource needs to make an AJAX call, this specifies whether to set
|
* If this TileSource needs to make an AJAX call, this specifies whether to set
|
||||||
* the XHR's withCredentials (for accessing secure data).
|
* 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]
|
* @param {Number} [options.width]
|
||||||
* Width of the source image at max resolution in pixels.
|
* Width of the source image at max resolution in pixels.
|
||||||
* @param {Number} [options.height]
|
* @param {Number} [options.height]
|
||||||
@ -475,6 +477,7 @@ $.TileSource.prototype = {
|
|||||||
$.makeAjaxRequest( {
|
$.makeAjaxRequest( {
|
||||||
url: url,
|
url: url,
|
||||||
withCredentials: this.ajaxWithCredentials,
|
withCredentials: this.ajaxWithCredentials,
|
||||||
|
headers: this.ajaxHeaders,
|
||||||
success: function( xhr ) {
|
success: function( xhr ) {
|
||||||
var data = processResponse( xhr );
|
var data = processResponse( xhr );
|
||||||
callback( data );
|
callback( data );
|
||||||
@ -559,7 +562,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.
|
* region specified by the given x, y, and level components.
|
||||||
* This method is not implemented by this class other than to throw an Error
|
* 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
|
* announcing you have to implement it. Because of the variety of tile
|
||||||
@ -575,6 +578,23 @@ $.TileSource.prototype = {
|
|||||||
throw new Error( "Method not implemented." );
|
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
|
* @function
|
||||||
* @param {Number} level
|
* @param {Number} level
|
||||||
|
@ -1232,6 +1232,15 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
||||||
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||||
* overriding viewer.crossOriginPolicy.
|
* 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
|
* @param {Function} [options.success] A function that gets called when the image is
|
||||||
* successfully added. It's passed the event object which contains a single property:
|
* successfully added. It's passed the event object which contains a single property:
|
||||||
* "item", the resulting TiledImage.
|
* "item", the resulting TiledImage.
|
||||||
@ -1270,6 +1279,17 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
if (options.crossOriginPolicy === undefined) {
|
if (options.crossOriginPolicy === undefined) {
|
||||||
options.crossOriginPolicy = options.tileSource.crossOriginPolicy !== undefined ? options.tileSource.crossOriginPolicy : this.crossOriginPolicy;
|
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 = {
|
var myQueueItem = {
|
||||||
options: options
|
options: options
|
||||||
@ -1384,6 +1404,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
|
smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
|
||||||
iOSDevice: _this.iOSDevice,
|
iOSDevice: _this.iOSDevice,
|
||||||
crossOriginPolicy: queueItem.options.crossOriginPolicy,
|
crossOriginPolicy: queueItem.options.crossOriginPolicy,
|
||||||
|
ajaxWithCredentials: queueItem.options.ajaxWithCredentials,
|
||||||
|
loadTilesWithAjax: queueItem.options.loadTilesWithAjax,
|
||||||
|
ajaxHeaders: queueItem.options.ajaxHeaders,
|
||||||
debugMode: _this.debugMode
|
debugMode: _this.debugMode
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2156,6 +2179,7 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
|
|||||||
crossOriginPolicy: imgOptions.crossOriginPolicy !== undefined ?
|
crossOriginPolicy: imgOptions.crossOriginPolicy !== undefined ?
|
||||||
imgOptions.crossOriginPolicy : viewer.crossOriginPolicy,
|
imgOptions.crossOriginPolicy : viewer.crossOriginPolicy,
|
||||||
ajaxWithCredentials: viewer.ajaxWithCredentials,
|
ajaxWithCredentials: viewer.ajaxWithCredentials,
|
||||||
|
ajaxHeaders: viewer.ajaxHeaders,
|
||||||
useCanvas: viewer.useCanvas,
|
useCanvas: viewer.useCanvas,
|
||||||
success: function( event ) {
|
success: function( event ) {
|
||||||
successCallback( event.tileSource );
|
successCallback( event.tileSource );
|
||||||
|
@ -80,6 +80,7 @@
|
|||||||
<script src="/test/modules/tilesourcecollection.js"></script>
|
<script src="/test/modules/tilesourcecollection.js"></script>
|
||||||
<script src="/test/modules/spring.js"></script>
|
<script src="/test/modules/spring.js"></script>
|
||||||
<script src="/test/modules/rectangle.js"></script>
|
<script src="/test/modules/rectangle.js"></script>
|
||||||
|
<script src="/test/modules/ajax-tiles.js"></script>
|
||||||
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
|
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
|
||||||
so we put them last. -->
|
so we put them last. -->
|
||||||
<script src="/test/modules/navigator.js"></script>
|
<script src="/test/modules/navigator.js"></script>
|
||||||
|
BIN
test/data/testpattern.blob
Normal file
BIN
test/data/testpattern.blob
Normal file
Binary file not shown.
After Width: | Height: | Size: 508 KiB |
75
test/demo/customheaders.html
Normal file
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>
|
242
test/modules/ajax-tiles.js
Normal file
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
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
@ -25,12 +25,14 @@
|
|||||||
|
|
||||||
var fakeTile0 = {
|
var fakeTile0 = {
|
||||||
url: 'foo.jpg',
|
url: 'foo.jpg',
|
||||||
|
cacheKey: 'foo.jpg',
|
||||||
image: {},
|
image: {},
|
||||||
unload: function() {}
|
unload: function() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
var fakeTile1 = {
|
var fakeTile1 = {
|
||||||
url: 'foo.jpg',
|
url: 'foo.jpg',
|
||||||
|
cacheKey: 'foo.jpg',
|
||||||
image: {},
|
image: {},
|
||||||
unload: function() {}
|
unload: function() {}
|
||||||
};
|
};
|
||||||
@ -74,18 +76,21 @@
|
|||||||
|
|
||||||
var fakeTile0 = {
|
var fakeTile0 = {
|
||||||
url: 'different.jpg',
|
url: 'different.jpg',
|
||||||
|
cacheKey: 'different.jpg',
|
||||||
image: {},
|
image: {},
|
||||||
unload: function() {}
|
unload: function() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
var fakeTile1 = {
|
var fakeTile1 = {
|
||||||
url: 'same.jpg',
|
url: 'same.jpg',
|
||||||
|
cacheKey: 'same.jpg',
|
||||||
image: {},
|
image: {},
|
||||||
unload: function() {}
|
unload: function() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
var fakeTile2 = {
|
var fakeTile2 = {
|
||||||
url: 'same.jpg',
|
url: 'same.jpg',
|
||||||
|
cacheKey: 'same.jpg',
|
||||||
image: {},
|
image: {},
|
||||||
unload: function() {}
|
unload: function() {}
|
||||||
};
|
};
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
<script src="/test/modules/tilesourcecollection.js"></script>
|
<script src="/test/modules/tilesourcecollection.js"></script>
|
||||||
<script src="/test/modules/spring.js"></script>
|
<script src="/test/modules/spring.js"></script>
|
||||||
<script src="/test/modules/rectangle.js"></script>
|
<script src="/test/modules/rectangle.js"></script>
|
||||||
|
<script src="/test/modules/ajax-tiles.js"></script>
|
||||||
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
|
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
|
||||||
so we put them last. -->
|
so we put them last. -->
|
||||||
<script src="/test/modules/navigator.js"></script>
|
<script src="/test/modules/navigator.js"></script>
|
||||||
|
Loading…
Reference in New Issue
Block a user