diff --git a/src/imageloader.js b/src/imageloader.js index c89850b7..1fea59b3 100644 --- a/src/imageloader.js +++ b/src/imageloader.js @@ -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 }; } diff --git a/src/openseadragon.js b/src/openseadragon.js index 4cdb0d2b..9c84c369 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -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} diff --git a/src/tile.js b/src/tile.js index 492d4d70..be68d29a 100644 --- a/src/tile.js +++ b/src/tile.js @@ -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( diff --git a/src/tiledimage.js b/src/tiledimage.js index 46ac109d..c60bd08a 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -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, diff --git a/src/tilesource.js b/src/tilesource.js index 93bd00b8..59a35d62 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -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) { diff --git a/test/modules/basic.js b/test/modules/basic.js index d5635fcf..457adeab 100644 --- a/test/modules/basic.js +++ b/test/modules/basic.js @@ -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();