Finish on image job now accepts request argument. Further comments cleanup. Deprecation message for image property in tile loaded event. Removal of downloadTileFinish(). More robust aborting that cleans up an image properties when aborted (not done until now).

This commit is contained in:
Jirka 2022-05-04 15:16:13 +02:00
parent 150e750ece
commit 8a2c998cb9
6 changed files with 88 additions and 67 deletions

View File

@ -44,7 +44,7 @@
* @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 {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null
* see TileSource::getPostData) or null
* @param {Function} [options.callback] - Called once image has been downloaded.
* @param {Function} [options.abort] - Called when this image job is aborted.
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
@ -65,15 +65,21 @@ $.ImageJob = function(options) {
/**
* User workspace to populate with helper variables
* @member {*} user data, for people to append their data
* @member {*} userData to append custom data and avoid namespace collision
* @memberof OpenSeadragon.ImageJob#
*/
this.userData = {};
/**
* Error message holder
* @member {string} error message
* @memberof OpenSeadragon.ImageJob#
* @private
*/
this.errorMsg = null;
};
$.ImageJob.prototype = {
errorMsg: null,
/**
* Starts the image job.
* @method
@ -83,7 +89,7 @@ $.ImageJob.prototype = {
var selfAbort = this.abort;
this.jobId = window.setTimeout(function () {
self.finish(false, "Image load exceeded timeout (" + self.timeout + " ms)");
self.finish(null, "Image load exceeded timeout (" + self.timeout + " ms)");
}, this.timeout);
this.abort = function() {
@ -96,9 +102,16 @@ $.ImageJob.prototype = {
this.source.downloadTileStart(this);
},
finish: function(successful, errorMessage) {
/**
* Finish this job.
* @param {*} data data that has been downloaded
* @param {XMLHttpRequest} request reference to the request if used
* @param {string} errorMessage description upon failure
*/
finish: function(data, request, errorMessage ) {
this.data = data;
this.request = request;
this.errorMsg = errorMessage;
this.data = this.source.downloadTileFinish(this, successful);
if (this.jobId) {
window.clearTimeout(this.jobId);
@ -141,7 +154,7 @@ $.ImageLoader.prototype = {
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null
* see TileSource::getPostData) or null
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
* requests.
* @param {Function} [options.callback] - Called once image has been downloaded.
@ -154,8 +167,7 @@ $.ImageLoader.prototype = {
var implementation = $.TileSource.prototype;
options.source = {
downloadTileStart: implementation.downloadTileStart,
downloadTileAbort: implementation.downloadTileAbort,
downloadTileFinish: implementation.downloadTileFinish
downloadTileAbort: implementation.downloadTileAbort
};
}

View File

@ -2321,7 +2321,7 @@ function OpenSeadragon( options ){
* @param {Object} options.headers - headers to add to the AJAX request
* @param {String} options.responseType - the response type of the the AJAX request
* @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData), GET method used if null
* see TileSource::getPostData), GET method used if null
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
* @throws {Error}
* @returns {XMLHttpRequest}

View File

@ -53,7 +53,7 @@
* drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
* with HTML the entire tile is always used.
* @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null
* see TileSource::getPostData) or null
* @param {String} cacheKey key to act as a tile cache, must be unique for tiles with unique image data
*/
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData, cacheKey) {
@ -104,7 +104,7 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* Post parameters for this tile. For example, it can be an URL-encoded string
* in k1=v1&k2=v2... format, or a JSON, or a FormData instance... or null if no POST request used
* @member {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null
* see TileSource::getPostData) or null
* @memberof OpenSeadragon.Tile#
*/
this.postData = postData;
@ -218,7 +218,7 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
/**
* The transparency indicator of this tile.
* @member {Boolean} true if tile contains transparency for correct rendering
* @member {Boolean} hasTransparency true if tile contains transparency for correct rendering
* @memberof OpenSeadragon.Tile#
*/
this.hasTransparency = false;
@ -297,8 +297,8 @@ $.Tile.prototype = {
// content during animation of the container size.
if ( !this.element ) {
var image = this.image;
if (!this.image) {
var image = this.getImage();
if (!image) {
return;
}
@ -334,20 +334,28 @@ $.Tile.prototype = {
* The Image object for this tile.
* @member {Object} image
* @memberof OpenSeadragon.Tile#
* @deprecated
* @return {Image}
*/
get image() {
$.console.error("[Tile.image] property has been deprecated. Use [Tile.prototype.getImage] instead.");
return this.getImage();
},
/**
* Get the Image object for this tile.
* @return {Image}
*/
getImage: function() {
return this.cacheImageRecord.getImage();
},
/**
* The CanvasRenderingContext2D instance for tile image data drawn
* Get the CanvasRenderingContext2D instance for tile image data drawn
* onto Canvas if enabled and available
* @member {CanvasRenderingContext2D} canvasContext
* @memberof OpenSeadragon.Tile#
* @return {CanvasRenderingContext2D}
*/
get canvasContext() {
getCanvasContext: function() {
return this.context2D || this.cacheImageRecord.getRenderedContext();
},
@ -378,7 +386,7 @@ $.Tile.prototype = {
return;
}
rendered = this.canvasContext;
rendered = this.getCanvasContext();
if ( !this.loaded || !rendered ){
$.console.warn(

View File

@ -1649,7 +1649,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @private
* @inner
* @param {OpenSeadragon.Tile} tile
* @param {*} data image data, the output of TileSource.prototype.downloadTileFinish(), by default Image object
* @param {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
* @param {Number || undefined} cutoff
* @param {XMLHttpRequest || undefined} tileRequest
*/
@ -1690,8 +1690,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {Image || *} image - The image (data) of the tile. Deprecated.
* @property {*} data - image data, the output of TileSource.prototype.downloadTileFinish(),
* by default Image object
* @property {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
* @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
@ -1705,7 +1704,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
tiledImage: this,
tileRequest: tileRequest,
get image() {
$.console.error("[tile-loaded] event property 'image' has been deprecated and will be removed.");
$.console.error("[tile-loaded] event 'image' has been deprecated. Use 'data' property instead.");
return data;
},
data: data,

View File

@ -553,7 +553,7 @@ $.TileSource.prototype = {
* @property {String} message
* @property {String} source
* @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null
* see TileSource::getPostData) or null
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', {
@ -721,31 +721,47 @@ $.TileSource.prototype = {
/**
* Download tile data.
* Note that if you override any of downloadTile*() functions, you should override all of them.
* Note that if you override this function, you should override also downloadTileAbort().
* @param {ImageJob} context job context that you have to call finish(...) on.
* @param {String} [context.src] - URL of image to download.
* @param {String} [context.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [context.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [context.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSrouce::getPostData) or null
*
* see TileSource::getPostData) or null
* @param {*} [context.userData] - Empty object to attach your own data and helper variables to.
* @param {Function} [context.callback] - Called automatically once image has been downloaded (triggered by finish).
* @param {Function} [context.finish] - Should be called unless abort() was executed, e.g. on all occasions,
* be it successful or unsuccessful request.
* @param {Function} [context.abort] - Called when this image job is aborted.
* @param {Number} [context.timeout] - The max number of milliseconds that this image job may take to complete.
* be it successful or unsuccessful request.
* Usage: context.finish(data, request, errMessage). Pass the downloaded data object or null upon failure.
* Add also reference to an ajax request if used. Provide error message in case of failure.
* @param {Function} [context.abort] - Called automatically when the job times out.
* Usage: context.abort().
* @param {Function} [context.callback] @private - Called automatically once image has been downloaded
* (triggered by finish).
* @param {Number} [context.timeout] @private - The max number of milliseconds that
* this image job may take to complete.
* @param {string} [context.errorMsg] @private - The final error message, default null (set by finish).
*/
downloadTileStart: function (context) {
var dataStore = context.userData;
dataStore.image = new Image();
var dataStore = context.userData,
image = new Image();
dataStore.image.onload = function(){
context.finish(true);
dataStore.image = image;
dataStore.request = null;
var finish = function(error) {
if (!image) {
context.finish(null, dataStore.request, "Image load failed: undefined Image instance.");
return;
}
image.onload = image.onerror = image.onabort = null;
context.finish(error ? null : image, dataStore.request, error);
};
dataStore.image.onabort = dataStore.image.onerror = function() {
context.finish(false, "Image load aborted");
image.onload = function () {
finish();
};
image.onabort = image.onerror = function() {
finish("Image load aborted.");
};
// Load the tile with an AJAX request if the loadWithAjax option is
@ -779,53 +795,39 @@ $.TileSource.prototype = {
}
// If the blob is empty for some reason consider the image load a failure.
if (blb.size === 0) {
context.finish(false, "Empty image response.");
finish("Empty image response.");
} else {
// Create a URL for the blob data and make it the source of the image object.
// This will still trigger Image.onload to indicate a successful tile load.
dataStore.image.src = (window.URL || window.webkitURL).createObjectURL(blb);
image.src = (window.URL || window.webkitURL).createObjectURL(blb);
}
},
error: function(request) {
context.finish(false, "Image load aborted - XHR error");
finish("Image load aborted - XHR error");
}
});
} else {
if (context.crossOriginPolicy !== false) {
dataStore.image.crossOrigin = context.crossOriginPolicy;
image.crossOrigin = context.crossOriginPolicy;
}
dataStore.image.src = context.src;
image.src = context.src;
}
},
/**
* Provide means of aborting the execution.
* Note that if you override any of downloadTile*() functions, you should override all of them.
* Note that if you override this function, you should override also downloadTileStart().
* @param {ImageJob} context job, the same object as with downloadTileStart(..)
* @param {*} [context.userData] - Empty object to attach (and mainly read) your own data.
*/
downloadTileAbort: function (context) {
context.userData.request.abort();
},
/**
* Note that if you override any of downloadTile*() functions, you should override all of them,
* unless you just want to for example here change the format of the data.
* Note that unless this function returns Image object, you should also override *TileCache() functions
* to re-define how the data is being cached.
* @param {ImageJob} context job, the same object as with downloadTileStart(..)
* @param {*} [context.userData] - Empty object to attach (and mainly read) your own data.
* @param successful true if successful
* @return {* || null} tile data in a format you want to have in the system, or null to indicate missing data
* also for example, a default value (white image? error image?) can be returned if the request was unsuccessful
*/
downloadTileFinish: function (context, successful) {
var image = context.userData.image;
if (!image) {
return null;
if (context.userData.request) {
context.userData.request.abort();
}
var image = context.userData.image;
if (context.userData.image) {
image.onload = image.onerror = image.onabort = null;
}
image.onload = image.onerror = image.onabort = null;
return successful ? image : null;
},
/**
@ -835,7 +837,7 @@ $.TileSource.prototype = {
*
* Note that if you override any of *TileCache() functions, you should override all of them.
* @param {object} cacheObject context cache object
* @param {*} data the result of downloadTileFinish() function
* @param {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
* @param {Tile} tile instance the cache was created with
*/
createTileCache: function(cacheObject, data, tile) {

View File

@ -305,7 +305,7 @@
// The Wikipedia logo has CORS enabled
var corsImg = 'http://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png';
var corsImg = 'https://upload.wikimedia.org/wikipedia/en/b/bc/Wiki.png';
QUnit.test( 'CrossOriginPolicyMissing', function (assert) {
var done = assert.async();