mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-24 22:26:10 +03:00
Merge pull request #2148 from Aiosa/master
Delegate tile fetching and caching to the TileSource
This commit is contained in:
commit
1d327d673e
@ -347,15 +347,16 @@ $.Drawer.prototype = {
|
|||||||
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
|
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
|
||||||
* position and size of tiles supporting alpha channel in non-transparency
|
* position and size of tiles supporting alpha channel in non-transparency
|
||||||
* context.
|
* context.
|
||||||
|
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
|
||||||
*/
|
*/
|
||||||
drawTile: function(tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize) {
|
drawTile: function( tile, drawingHandler, useSketch, scale, translate, shouldRoundPositionAndSize, source) {
|
||||||
$.console.assert(tile, '[Drawer.drawTile] tile is required');
|
$.console.assert(tile, '[Drawer.drawTile] tile is required');
|
||||||
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
|
$.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required');
|
||||||
|
|
||||||
if (this.useCanvas) {
|
if (this.useCanvas) {
|
||||||
var context = this._getContext(useSketch);
|
var context = this._getContext(useSketch);
|
||||||
scale = scale || 1;
|
scale = scale || 1;
|
||||||
tile.drawCanvas(context, drawingHandler, scale, translate, shouldRoundPositionAndSize);
|
tile.drawCanvas(context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source);
|
||||||
} else {
|
} else {
|
||||||
tile.drawHTML( this.canvas );
|
tile.drawHTML( this.canvas );
|
||||||
}
|
}
|
||||||
|
@ -35,21 +35,23 @@
|
|||||||
(function($){
|
(function($){
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
|
||||||
* @class ImageJob
|
* @class ImageJob
|
||||||
* @classdesc Handles downloading of a single image.
|
* @classdesc Handles downloading of a single image.
|
||||||
* @param {Object} options - Options for this ImageJob.
|
* @param {Object} options - Options for this ImageJob.
|
||||||
* @param {String} [options.src] - URL of image to download.
|
* @param {String} [options.src] - URL of image to download.
|
||||||
|
* @param {Tile} [options.tile] - Tile that belongs the data to.
|
||||||
|
* @param {TileSource} [options.source] - Image loading strategy
|
||||||
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
* @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.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||||
|
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
|
||||||
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
|
* @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,
|
* @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.callback] - Called once image has been downloaded.
|
||||||
* @param {Function} [options.abort] - Called when this image job is aborted.
|
* @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.
|
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
|
||||||
*/
|
*/
|
||||||
function ImageJob (options) {
|
$.ImageJob = function(options) {
|
||||||
|
|
||||||
$.extend(true, this, {
|
$.extend(true, this, {
|
||||||
timeout: $.DEFAULT_SETTINGS.timeout,
|
timeout: $.DEFAULT_SETTINGS.timeout,
|
||||||
@ -57,107 +59,61 @@ function ImageJob (options) {
|
|||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image object which will contain downloaded image.
|
* Data object which will contain downloaded image data.
|
||||||
* @member {Image} image
|
* @member {Image|*} image data object, by default an Image object (depends on TileSource)
|
||||||
* @memberof OpenSeadragon.ImageJob#
|
* @memberof OpenSeadragon.ImageJob#
|
||||||
*/
|
*/
|
||||||
this.image = null;
|
this.data = null;
|
||||||
}
|
|
||||||
|
|
||||||
ImageJob.prototype = {
|
/**
|
||||||
errorMsg: null,
|
* User workspace to populate with helper variables
|
||||||
|
* @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 = {
|
||||||
/**
|
/**
|
||||||
* Starts the image job.
|
* Starts the image job.
|
||||||
* @method
|
* @method
|
||||||
*/
|
*/
|
||||||
start: function(){
|
start: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var selfAbort = this.abort;
|
var selfAbort = this.abort;
|
||||||
|
|
||||||
this.image = new Image();
|
this.jobId = window.setTimeout(function () {
|
||||||
|
self.finish(null, null, "Image load exceeded timeout (" + self.timeout + " ms)");
|
||||||
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.timeout + " ms)";
|
|
||||||
self.finish(false);
|
|
||||||
}, this.timeout);
|
}, this.timeout);
|
||||||
|
|
||||||
// Load the tile with an AJAX request if the loadWithAjax option is
|
this.abort = function() {
|
||||||
// set. Otherwise load the image by setting the source proprety of the image object.
|
self.source.downloadTileAbort(self);
|
||||||
if (this.loadWithAjax) {
|
if (typeof selfAbort === "function") {
|
||||||
this.request = $.makeAjaxRequest({
|
selfAbort();
|
||||||
url: this.src,
|
|
||||||
withCredentials: this.ajaxWithCredentials,
|
|
||||||
headers: this.ajaxHeaders,
|
|
||||||
responseType: "arraybuffer",
|
|
||||||
postData: this.postData,
|
|
||||||
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: Ajax returned " + request.status;
|
|
||||||
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;
|
this.source.downloadTileStart(this);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
finish: function(successful) {
|
/**
|
||||||
this.image.onload = this.image.onerror = this.image.onabort = null;
|
* Finish this job.
|
||||||
if (!successful) {
|
* @param {*} data data that has been downloaded
|
||||||
this.image = null;
|
* @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;
|
||||||
|
|
||||||
if (this.jobId) {
|
if (this.jobId) {
|
||||||
window.clearTimeout(this.jobId);
|
window.clearTimeout(this.jobId);
|
||||||
@ -165,7 +121,6 @@ ImageJob.prototype = {
|
|||||||
|
|
||||||
this.callback(this);
|
this.callback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,23 +151,38 @@ $.ImageLoader.prototype = {
|
|||||||
* @method
|
* @method
|
||||||
* @param {Object} options - Options for this job.
|
* @param {Object} options - Options for this job.
|
||||||
* @param {String} [options.src] - URL of image to download.
|
* @param {String} [options.src] - URL of image to download.
|
||||||
|
* @param {Tile} [options.tile] - Tile that belongs the data to. The tile instance
|
||||||
|
* is not internally used and serves for custom TileSources implementations.
|
||||||
|
* @param {TileSource} [options.source] - Image loading strategy
|
||||||
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
* @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.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
||||||
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
|
* @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,
|
* @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
|
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
|
||||||
* requests.
|
* requests.
|
||||||
* @param {Function} [options.callback] - Called once image has been downloaded.
|
* @param {Function} [options.callback] - Called once image has been downloaded.
|
||||||
* @param {Function} [options.abort] - Called when this image job is aborted.
|
* @param {Function} [options.abort] - Called when this image job is aborted.
|
||||||
*/
|
*/
|
||||||
addJob: function(options) {
|
addJob: function(options) {
|
||||||
|
if (!options.source) {
|
||||||
|
$.console.error('ImageLoader.prototype.addJob() requires [options.source]. ' +
|
||||||
|
'TileSource since new API defines how images are fetched. Creating a dummy TileSource.');
|
||||||
|
var implementation = $.TileSource.prototype;
|
||||||
|
options.source = {
|
||||||
|
downloadTileStart: implementation.downloadTileStart,
|
||||||
|
downloadTileAbort: implementation.downloadTileAbort
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
tile: options.tile || {},
|
||||||
|
source: options.source,
|
||||||
loadWithAjax: options.loadWithAjax,
|
loadWithAjax: options.loadWithAjax,
|
||||||
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
|
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
|
||||||
crossOriginPolicy: options.crossOriginPolicy,
|
crossOriginPolicy: options.crossOriginPolicy,
|
||||||
@ -222,7 +192,7 @@ $.ImageLoader.prototype = {
|
|||||||
abort: options.abort,
|
abort: options.abort,
|
||||||
timeout: this.timeout
|
timeout: this.timeout
|
||||||
},
|
},
|
||||||
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();
|
||||||
@ -268,7 +238,7 @@ function completeJob(loader, job, callback) {
|
|||||||
loader.jobsInProgress++;
|
loader.jobsInProgress++;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(job.image, job.errorMsg, job.request);
|
callback(job.data, job.errorMsg, job.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
}(OpenSeadragon));
|
}(OpenSeadragon));
|
||||||
|
@ -2327,7 +2327,7 @@ function OpenSeadragon( options ){
|
|||||||
* @param {Object} options.headers - headers to add to the AJAX request
|
* @param {Object} options.headers - headers to add to the AJAX request
|
||||||
* @param {String} options.responseType - the response type of the AJAX request
|
* @param {String} options.responseType - the response type of the AJAX request
|
||||||
* @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
* @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
|
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
|
||||||
* @throws {Error}
|
* @throws {Error}
|
||||||
* @returns {XMLHttpRequest}
|
* @returns {XMLHttpRequest}
|
||||||
|
85
src/tile.js
85
src/tile.js
@ -53,7 +53,7 @@
|
|||||||
* drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
|
* drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
|
||||||
* with HTML the entire tile is always used.
|
* with HTML the entire tile is always used.
|
||||||
* @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
* @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
|
* @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) {
|
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData, cacheKey) {
|
||||||
@ -82,11 +82,11 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
|||||||
*/
|
*/
|
||||||
this.bounds = bounds;
|
this.bounds = bounds;
|
||||||
/**
|
/**
|
||||||
* The portion of the tile to use as the source of the drawing operation, in pixels. Note that
|
* The portion of the tile to use as the source of the drawing operation, in pixels. Note that
|
||||||
* this only works when drawing with canvas; when drawing with HTML the entire tile is always used.
|
* this only works when drawing with canvas; when drawing with HTML the entire tile is always used.
|
||||||
* @member {OpenSeadragon.Rect} sourceBounds
|
* @member {OpenSeadragon.Rect} sourceBounds
|
||||||
* @memberof OpenSeadragon.Tile#
|
* @memberof OpenSeadragon.Tile#
|
||||||
*/
|
*/
|
||||||
this.sourceBounds = sourceBounds;
|
this.sourceBounds = sourceBounds;
|
||||||
/**
|
/**
|
||||||
* Is this tile a part of a sparse image? Also has this tile failed to load?
|
* Is this tile a part of a sparse image? Also has this tile failed to load?
|
||||||
@ -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
|
* 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
|
* 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,
|
* @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#
|
* @memberof OpenSeadragon.Tile#
|
||||||
*/
|
*/
|
||||||
this.postData = postData;
|
this.postData = postData;
|
||||||
@ -127,18 +127,18 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
|||||||
* @memberof OpenSeadragon.Tile#
|
* @memberof OpenSeadragon.Tile#
|
||||||
*/
|
*/
|
||||||
this.ajaxHeaders = ajaxHeaders;
|
this.ajaxHeaders = ajaxHeaders;
|
||||||
/**
|
|
||||||
* The unique cache key for this tile.
|
|
||||||
* @member {String} cacheKey
|
|
||||||
* @memberof OpenSeadragon.Tile#
|
|
||||||
*/
|
|
||||||
if (cacheKey === undefined) {
|
if (cacheKey === undefined) {
|
||||||
$.console.error("Tile constructor needs 'cacheKey' variable: creation tile cache" +
|
$.console.error("Tile constructor needs 'cacheKey' variable: creation tile cache" +
|
||||||
" in Tile class is deprecated. TileSource.prototype.getTileHashKey will be used.");
|
" in Tile class is deprecated. TileSource.prototype.getTileHashKey will be used.");
|
||||||
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
|
cacheKey = $.TileSource.prototype.getTileHashKey(level, x, y, url, ajaxHeaders, postData);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* The unique cache key for this tile.
|
||||||
|
* @member {String} cacheKey
|
||||||
|
* @memberof OpenSeadragon.Tile#
|
||||||
|
*/
|
||||||
this.cacheKey = cacheKey;
|
this.cacheKey = cacheKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this tile loaded?
|
* Is this tile loaded?
|
||||||
* @member {Boolean} loaded
|
* @member {Boolean} loaded
|
||||||
@ -164,12 +164,6 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
|||||||
* @memberof OpenSeadragon.Tile#
|
* @memberof OpenSeadragon.Tile#
|
||||||
*/
|
*/
|
||||||
this.imgElement = null;
|
this.imgElement = null;
|
||||||
/**
|
|
||||||
* The Image object for this tile.
|
|
||||||
* @member {Object} image
|
|
||||||
* @memberof OpenSeadragon.Tile#
|
|
||||||
*/
|
|
||||||
this.image = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The alias of this.element.style.
|
* The alias of this.element.style.
|
||||||
@ -222,6 +216,13 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
|
|||||||
*/
|
*/
|
||||||
this.visibility = null;
|
this.visibility = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transparency indicator of this tile.
|
||||||
|
* @member {Boolean} hasTransparency true if tile contains transparency for correct rendering
|
||||||
|
* @memberof OpenSeadragon.Tile#
|
||||||
|
*/
|
||||||
|
this.hasTransparency = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this tile is currently being drawn.
|
* Whether this tile is currently being drawn.
|
||||||
* @member {Boolean} beingDrawn
|
* @member {Boolean} beingDrawn
|
||||||
@ -266,6 +267,8 @@ $.Tile.prototype = {
|
|||||||
|
|
||||||
// private
|
// private
|
||||||
_hasTransparencyChannel: function() {
|
_hasTransparencyChannel: function() {
|
||||||
|
console.warn("Tile.prototype._hasTransparencyChannel() has been " +
|
||||||
|
"deprecated and will be removed in the future. Use TileSource.prototype.hasTransparency() instead.");
|
||||||
return !!this.context2D || this.url.match('.png');
|
return !!this.context2D || this.url.match('.png');
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -294,8 +297,13 @@ $.Tile.prototype = {
|
|||||||
// content during animation of the container size.
|
// content during animation of the container size.
|
||||||
|
|
||||||
if ( !this.element ) {
|
if ( !this.element ) {
|
||||||
|
var image = this.getImage();
|
||||||
|
if (!image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.element = $.makeNeutralElement( "div" );
|
this.element = $.makeNeutralElement( "div" );
|
||||||
this.imgElement = this.cacheImageRecord.getImage().cloneNode();
|
this.imgElement = image.cloneNode();
|
||||||
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
|
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
|
||||||
this.imgElement.style.width = "100%";
|
this.imgElement.style.width = "100%";
|
||||||
this.imgElement.style.height = "100%";
|
this.imgElement.style.height = "100%";
|
||||||
@ -322,6 +330,35 @@ $.Tile.prototype = {
|
|||||||
$.setElementOpacity( this.element, this.opacity );
|
$.setElementOpacity( this.element, this.opacity );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the CanvasRenderingContext2D instance for tile image data drawn
|
||||||
|
* onto Canvas if enabled and available
|
||||||
|
* @return {CanvasRenderingContext2D}
|
||||||
|
*/
|
||||||
|
getCanvasContext: function() {
|
||||||
|
return this.context2D || this.cacheImageRecord.getRenderedContext();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the tile in a canvas-based context.
|
* Renders the tile in a canvas-based context.
|
||||||
* @function
|
* @function
|
||||||
@ -334,8 +371,9 @@ $.Tile.prototype = {
|
|||||||
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
|
* @param {Boolean} [shouldRoundPositionAndSize] - Tells whether to round
|
||||||
* position and size of tiles supporting alpha channel in non-transparency
|
* position and size of tiles supporting alpha channel in non-transparency
|
||||||
* context.
|
* context.
|
||||||
|
* @param {OpenSeadragon.TileSource} source - The source specification of the tile.
|
||||||
*/
|
*/
|
||||||
drawCanvas: function( context, drawingHandler, scale, translate, shouldRoundPositionAndSize ) {
|
drawCanvas: function( context, drawingHandler, scale, translate, shouldRoundPositionAndSize, source) {
|
||||||
|
|
||||||
var position = this.position.times($.pixelDensityRatio),
|
var position = this.position.times($.pixelDensityRatio),
|
||||||
size = this.size.times($.pixelDensityRatio),
|
size = this.size.times($.pixelDensityRatio),
|
||||||
@ -348,7 +386,7 @@ $.Tile.prototype = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rendered = this.context2D || this.cacheImageRecord.getRenderedContext();
|
rendered = this.getCanvasContext();
|
||||||
|
|
||||||
if ( !this.loaded || !rendered ){
|
if ( !this.loaded || !rendered ){
|
||||||
$.console.warn(
|
$.console.warn(
|
||||||
@ -360,7 +398,6 @@ $.Tile.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.save();
|
context.save();
|
||||||
|
|
||||||
context.globalAlpha = this.opacity;
|
context.globalAlpha = this.opacity;
|
||||||
|
|
||||||
if (typeof scale === 'number' && scale !== 1) {
|
if (typeof scale === 'number' && scale !== 1) {
|
||||||
@ -378,7 +415,7 @@ $.Tile.prototype = {
|
|||||||
//ie its done fading or fading is turned off, and if we are drawing
|
//ie its done fading or fading is turned off, and if we are drawing
|
||||||
//an image with an alpha channel, then the only way
|
//an image with an alpha channel, then the only way
|
||||||
//to avoid seeing the tile underneath is to clear the rectangle
|
//to avoid seeing the tile underneath is to clear the rectangle
|
||||||
if (context.globalAlpha === 1 && this._hasTransparencyChannel()) {
|
if (context.globalAlpha === 1 && this.hasTransparency) {
|
||||||
if (shouldRoundPositionAndSize) {
|
if (shouldRoundPositionAndSize) {
|
||||||
// Round to the nearest whole pixel so we don't get seams from overlap.
|
// Round to the nearest whole pixel so we don't get seams from overlap.
|
||||||
position.x = Math.round(position.x);
|
position.x = Math.round(position.x);
|
||||||
|
@ -46,43 +46,22 @@ var TileRecord = function( options ) {
|
|||||||
// private class
|
// private class
|
||||||
var ImageRecord = function(options) {
|
var ImageRecord = function(options) {
|
||||||
$.console.assert( options, "[ImageRecord] options is required" );
|
$.console.assert( options, "[ImageRecord] options is required" );
|
||||||
$.console.assert( options.image, "[ImageRecord] options.image is required" );
|
$.console.assert( options.data, "[ImageRecord] options.data is required" );
|
||||||
this._image = options.image;
|
|
||||||
this._tiles = [];
|
this._tiles = [];
|
||||||
|
|
||||||
|
options.create.apply(null, [this, options.data, options.ownerTile]);
|
||||||
|
this._destroyImplementation = options.destroy.bind(null, this);
|
||||||
|
this.getImage = options.getImage.bind(null, this);
|
||||||
|
this.getData = options.getData.bind(null, this);
|
||||||
|
this.getRenderedContext = options.getRenderedContext.bind(null, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageRecord.prototype = {
|
ImageRecord.prototype = {
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
this._image = null;
|
this._destroyImplementation();
|
||||||
this._renderedContext = null;
|
|
||||||
this._tiles = null;
|
this._tiles = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
getImage: function() {
|
|
||||||
return this._image;
|
|
||||||
},
|
|
||||||
|
|
||||||
getRenderedContext: function() {
|
|
||||||
if (!this._renderedContext) {
|
|
||||||
var canvas = document.createElement( 'canvas' );
|
|
||||||
canvas.width = this._image.width;
|
|
||||||
canvas.height = this._image.height;
|
|
||||||
this._renderedContext = canvas.getContext('2d');
|
|
||||||
this._renderedContext.drawImage( this._image, 0, 0 );
|
|
||||||
//since we are caching the prerendered image on a canvas
|
|
||||||
//allow the image to not be held in memory
|
|
||||||
this._image = null;
|
|
||||||
}
|
|
||||||
return this._renderedContext;
|
|
||||||
},
|
|
||||||
|
|
||||||
setRenderedContext: function(renderedContext) {
|
|
||||||
$.console.error("ImageRecord.setRenderedContext is deprecated. " +
|
|
||||||
"The rendered context should be created by the ImageRecord " +
|
|
||||||
"itself when calling ImageRecord.getRenderedContext.");
|
|
||||||
this._renderedContext = renderedContext;
|
|
||||||
},
|
|
||||||
|
|
||||||
addTile: function(tile) {
|
addTile: function(tile) {
|
||||||
$.console.assert(tile, '[ImageRecord.addTile] tile is required');
|
$.console.assert(tile, '[ImageRecord.addTile] tile is required');
|
||||||
this._tiles.push(tile);
|
this._tiles.push(tile);
|
||||||
@ -158,9 +137,22 @@ $.TileCache.prototype = {
|
|||||||
|
|
||||||
var imageRecord = this._imagesLoaded[options.tile.cacheKey];
|
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" );
|
|
||||||
|
if (!options.data) {
|
||||||
|
$.console.error("[TileCache.cacheTile] options.image was renamed to options.data. '.image' attribute " +
|
||||||
|
"has been deprecated and will be removed in the future.");
|
||||||
|
options.data = options.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.console.assert( options.data, "[TileCache.cacheTile] options.data is required to create an ImageRecord" );
|
||||||
imageRecord = this._imagesLoaded[options.tile.cacheKey] = new ImageRecord({
|
imageRecord = this._imagesLoaded[options.tile.cacheKey] = new ImageRecord({
|
||||||
image: options.image
|
data: options.data,
|
||||||
|
ownerTile: options.tile,
|
||||||
|
create: options.tiledImage.source.createTileCache,
|
||||||
|
destroy: options.tiledImage.source.destroyTileCache,
|
||||||
|
getImage: options.tiledImage.source.getTileCacheDataAsImage,
|
||||||
|
getData: options.tiledImage.source.getTileCacheData,
|
||||||
|
getRenderedContext: options.tiledImage.source.getTileCacheDataAsContext2D,
|
||||||
});
|
});
|
||||||
|
|
||||||
this._imagesLoadedCount++;
|
this._imagesLoadedCount++;
|
||||||
@ -196,7 +188,7 @@ $.TileCache.prototype = {
|
|||||||
worstLevel = worstTile.level;
|
worstLevel = worstTile.level;
|
||||||
|
|
||||||
if ( prevTime < worstTime ||
|
if ( prevTime < worstTime ||
|
||||||
( prevTime === worstTime && prevLevel > worstLevel ) ) {
|
( prevTime === worstTime && prevLevel > worstLevel ) ) {
|
||||||
worstTile = prevTile;
|
worstTile = prevTile;
|
||||||
worstTileIndex = i;
|
worstTileIndex = i;
|
||||||
worstTileRecord = prevTileRecord;
|
worstTileRecord = prevTileRecord;
|
||||||
|
@ -1427,8 +1427,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
} else {
|
} else {
|
||||||
var imageRecord = this._tileCache.getImageRecord(tile.cacheKey);
|
var imageRecord = this._tileCache.getImageRecord(tile.cacheKey);
|
||||||
if (imageRecord) {
|
if (imageRecord) {
|
||||||
var image = imageRecord.getImage();
|
this._setTileLoaded(tile, imageRecord.getData());
|
||||||
this._setTileLoaded(tile, image);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1571,13 +1570,15 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
tile.loading = true;
|
tile.loading = true;
|
||||||
this._imageLoader.addJob({
|
this._imageLoader.addJob({
|
||||||
src: tile.url,
|
src: tile.url,
|
||||||
|
tile: tile,
|
||||||
|
source: this.source,
|
||||||
postData: tile.postData,
|
postData: tile.postData,
|
||||||
loadWithAjax: tile.loadWithAjax,
|
loadWithAjax: tile.loadWithAjax,
|
||||||
ajaxHeaders: tile.ajaxHeaders,
|
ajaxHeaders: tile.ajaxHeaders,
|
||||||
crossOriginPolicy: this.crossOriginPolicy,
|
crossOriginPolicy: this.crossOriginPolicy,
|
||||||
ajaxWithCredentials: this.ajaxWithCredentials,
|
ajaxWithCredentials: this.ajaxWithCredentials,
|
||||||
callback: function( image, errorMsg, tileRequest ){
|
callback: function( data, errorMsg, tileRequest ){
|
||||||
_this._onTileLoad( tile, time, image, errorMsg, tileRequest );
|
_this._onTileLoad( tile, time, data, errorMsg, tileRequest );
|
||||||
},
|
},
|
||||||
abort: function() {
|
abort: function() {
|
||||||
tile.loading = false;
|
tile.loading = false;
|
||||||
@ -1591,12 +1592,12 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* Callback fired when a Tile's Image finished downloading.
|
* Callback fired when a Tile's Image finished downloading.
|
||||||
* @param {OpenSeadragon.Tile} tile
|
* @param {OpenSeadragon.Tile} tile
|
||||||
* @param {Number} time
|
* @param {Number} time
|
||||||
* @param {Image} image
|
* @param {*} data image data
|
||||||
* @param {String} errorMsg
|
* @param {String} errorMsg
|
||||||
* @param {XMLHttpRequest} tileRequest
|
* @param {XMLHttpRequest} tileRequest
|
||||||
*/
|
*/
|
||||||
_onTileLoad: function( tile, time, image, errorMsg, tileRequest ) {
|
_onTileLoad: function( tile, time, data, errorMsg, tileRequest ) {
|
||||||
if ( !image ) {
|
if ( !data ) {
|
||||||
$.console.error( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
|
$.console.error( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
|
||||||
/**
|
/**
|
||||||
* Triggered when a tile fails to load.
|
* Triggered when a tile fails to load.
|
||||||
@ -1632,7 +1633,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
finish = function() {
|
finish = function() {
|
||||||
var ccc = _this.source;
|
var ccc = _this.source;
|
||||||
var cutoff = ccc.getClosestLevel();
|
var cutoff = ccc.getClosestLevel();
|
||||||
_this._setTileLoaded(tile, image, cutoff, tileRequest);
|
_this._setTileLoaded(tile, data, 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
|
||||||
@ -1649,11 +1650,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @private
|
* @private
|
||||||
* @inner
|
* @inner
|
||||||
* @param {OpenSeadragon.Tile} tile
|
* @param {OpenSeadragon.Tile} tile
|
||||||
* @param {Image|undefined} image
|
* @param {*} data image data, the data sent to ImageJob.prototype.finish(), by default an Image object
|
||||||
* @param {Number|undefined} cutoff
|
* @param {Number|undefined} cutoff
|
||||||
* @param {XMLHttpRequest|undefined} tileRequest
|
* @param {XMLHttpRequest|undefined} tileRequest
|
||||||
*/
|
*/
|
||||||
_setTileLoaded: function(tile, image, cutoff, tileRequest) {
|
_setTileLoaded: function(tile, data, cutoff, tileRequest) {
|
||||||
var increment = 0,
|
var increment = 0,
|
||||||
_this = this;
|
_this = this;
|
||||||
|
|
||||||
@ -1667,9 +1668,12 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
if (increment === 0) {
|
if (increment === 0) {
|
||||||
tile.loading = false;
|
tile.loading = false;
|
||||||
tile.loaded = true;
|
tile.loaded = true;
|
||||||
|
tile.hasTransparency = _this.source.hasTransparency(
|
||||||
|
tile.context2D, tile.url, tile.ajaxHeaders, tile.postData
|
||||||
|
);
|
||||||
if (!tile.context2D) {
|
if (!tile.context2D) {
|
||||||
_this._tileCache.cacheTile({
|
_this._tileCache.cacheTile({
|
||||||
image: image,
|
data: data,
|
||||||
tile: tile,
|
tile: tile,
|
||||||
cutoff: cutoff,
|
cutoff: cutoff,
|
||||||
tiledImage: _this
|
tiledImage: _this
|
||||||
@ -1686,7 +1690,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @event tile-loaded
|
* @event tile-loaded
|
||||||
* @memberof OpenSeadragon.Viewer
|
* @memberof OpenSeadragon.Viewer
|
||||||
* @type {object}
|
* @type {object}
|
||||||
* @property {Image} image - The image of the tile.
|
* @property {Image || *} image - The image (data) of the tile. Deprecated.
|
||||||
|
* @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.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} tileRequest - The AJAX request that loaded this tile (if applicable).
|
* @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
|
||||||
@ -1699,7 +1704,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
tile: tile,
|
tile: tile,
|
||||||
tiledImage: this,
|
tiledImage: this,
|
||||||
tileRequest: tileRequest,
|
tileRequest: tileRequest,
|
||||||
image: image,
|
get image() {
|
||||||
|
$.console.error("[tile-loaded] event 'image' has been deprecated. Use 'data' property instead.");
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
data: data,
|
||||||
getCompletionCallback: getCompletionCallback
|
getCompletionCallback: getCompletionCallback
|
||||||
});
|
});
|
||||||
// In case the completion callback is never called, we at least force it once.
|
// In case the completion callback is never called, we at least force it once.
|
||||||
@ -1842,7 +1851,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
if (tile) {
|
if (tile) {
|
||||||
useSketch = this.opacity < 1 ||
|
useSketch = this.opacity < 1 ||
|
||||||
(this.compositeOperation && this.compositeOperation !== 'source-over') ||
|
(this.compositeOperation && this.compositeOperation !== 'source-over') ||
|
||||||
(!this._isBottomItem() && tile._hasTransparencyChannel());
|
(!this._isBottomItem() &&
|
||||||
|
this.source.hasTransparency(tile.context2D, tile.url, tile.ajaxHeaders, tile.postData));
|
||||||
}
|
}
|
||||||
|
|
||||||
var sketchScale;
|
var sketchScale;
|
||||||
@ -1984,7 +1994,8 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
|
|
||||||
for (var i = lastDrawn.length - 1; i >= 0; i--) {
|
for (var i = lastDrawn.length - 1; i >= 0; i--) {
|
||||||
tile = lastDrawn[ i ];
|
tile = lastDrawn[ i ];
|
||||||
this._drawer.drawTile( tile, this._drawingHandler, useSketch, sketchScale, sketchTranslate, shouldRoundPositionAndSize );
|
this._drawer.drawTile( tile, this._drawingHandler, useSketch, sketchScale,
|
||||||
|
sketchTranslate, shouldRoundPositionAndSize, this.source );
|
||||||
tile.beingDrawn = true;
|
tile.beingDrawn = true;
|
||||||
|
|
||||||
if( this.viewer ){
|
if( this.viewer ){
|
||||||
|
@ -553,7 +553,7 @@ $.TileSource.prototype = {
|
|||||||
* @property {String} message
|
* @property {String} message
|
||||||
* @property {String} source
|
* @property {String} source
|
||||||
* @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
|
* @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.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
_this.raiseEvent( 'open-failed', {
|
_this.raiseEvent( 'open-failed', {
|
||||||
@ -709,6 +709,194 @@ $.TileSource.prototype = {
|
|||||||
y >= 0 &&
|
y >= 0 &&
|
||||||
x < numTiles.x &&
|
x < numTiles.x &&
|
||||||
y < numTiles.y;
|
y < numTiles.y;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decide whether tiles have transparency: this is crucial for correct images blending.
|
||||||
|
* @return {boolean} true if the image has transparency
|
||||||
|
*/
|
||||||
|
hasTransparency: function(context2D, url, ajaxHeaders, post) {
|
||||||
|
return !!context2D || url.match('.png');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download tile data.
|
||||||
|
* 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 {Boolean} [context.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
|
||||||
|
* @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 TileSource::getPostData) or null
|
||||||
|
* @param {*} [context.userData] - Empty object to attach your own data and helper variables to.
|
||||||
|
* @param {Function} [context.finish] - Should be called unless abort() was executed, e.g. on all occasions,
|
||||||
|
* 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,
|
||||||
|
image = new Image();
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
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
|
||||||
|
// set. Otherwise load the image by setting the source proprety of the image object.
|
||||||
|
if (context.loadWithAjax) {
|
||||||
|
dataStore.request = $.makeAjaxRequest({
|
||||||
|
url: context.src,
|
||||||
|
withCredentials: context.ajaxWithCredentials,
|
||||||
|
headers: context.ajaxHeaders,
|
||||||
|
responseType: "arraybuffer",
|
||||||
|
postData: context.postData,
|
||||||
|
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) {
|
||||||
|
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.
|
||||||
|
image.src = (window.URL || window.webkitURL).createObjectURL(blb);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(request) {
|
||||||
|
finish("Image load aborted - XHR error");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (context.crossOriginPolicy !== false) {
|
||||||
|
image.crossOrigin = context.crossOriginPolicy;
|
||||||
|
}
|
||||||
|
image.src = context.src;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide means of aborting the execution.
|
||||||
|
* 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) {
|
||||||
|
if (context.userData.request) {
|
||||||
|
context.userData.request.abort();
|
||||||
|
}
|
||||||
|
var image = context.userData.image;
|
||||||
|
if (context.userData.image) {
|
||||||
|
image.onload = image.onerror = image.onabort = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create cache object from the result of the download process. The
|
||||||
|
* cacheObject parameter should be used to attach the data to, there are no
|
||||||
|
* conventions on how it should be stored - all the logic is implemented within *TileCache() functions.
|
||||||
|
*
|
||||||
|
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||||
|
* @param {object} cacheObject context cache object
|
||||||
|
* @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) {
|
||||||
|
cacheObject._data = data;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache object destructor, unset all properties you created to allow GC collection.
|
||||||
|
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||||
|
* @param {object} cacheObject context cache object
|
||||||
|
*/
|
||||||
|
destroyTileCache: function (cacheObject) {
|
||||||
|
cacheObject._data = null;
|
||||||
|
cacheObject._renderedContext = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Raw data getter
|
||||||
|
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||||
|
* @param {object} cacheObject context cache object
|
||||||
|
* @return {*} cache data
|
||||||
|
*/
|
||||||
|
getTileCacheData: function(cacheObject) {
|
||||||
|
return cacheObject._data;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility image element getter
|
||||||
|
* - plugins might need image representation of the data
|
||||||
|
* - div HTML rendering relies on image element presence
|
||||||
|
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||||
|
* @param {object} cacheObject context cache object
|
||||||
|
* @return {Image} cache data as an Image
|
||||||
|
*/
|
||||||
|
getTileCacheDataAsImage: function(cacheObject) {
|
||||||
|
return cacheObject._data; //the data itself by default is Image
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility context 2D getter
|
||||||
|
* - most heavily used rendering method is a canvas-based approach,
|
||||||
|
* convert the data to a canvas and return it's 2D context
|
||||||
|
* Note that if you override any of *TileCache() functions, you should override all of them.
|
||||||
|
* @param {object} cacheObject context cache object
|
||||||
|
* @return {CanvasRenderingContext2D} context of the canvas representation of the cache data
|
||||||
|
*/
|
||||||
|
getTileCacheDataAsContext2D: function(cacheObject) {
|
||||||
|
if (!cacheObject._renderedContext) {
|
||||||
|
var canvas = document.createElement( 'canvas' );
|
||||||
|
canvas.width = cacheObject._data.width;
|
||||||
|
canvas.height = cacheObject._data.height;
|
||||||
|
cacheObject._renderedContext = canvas.getContext('2d');
|
||||||
|
cacheObject._renderedContext.drawImage( cacheObject._data, 0, 0 );
|
||||||
|
//since we are caching the prerendered image on a canvas
|
||||||
|
//allow the image to not be held in memory
|
||||||
|
cacheObject._data = null;
|
||||||
|
}
|
||||||
|
return cacheObject._renderedContext;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -305,7 +305,7 @@
|
|||||||
|
|
||||||
|
|
||||||
// The Wikipedia logo has CORS enabled
|
// 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) {
|
QUnit.test( 'CrossOriginPolicyMissing', function (assert) {
|
||||||
var done = assert.async();
|
var done = assert.async();
|
||||||
|
@ -19,10 +19,12 @@
|
|||||||
raiseEvent: function() {}
|
raiseEvent: function() {}
|
||||||
};
|
};
|
||||||
var fakeTiledImage0 = {
|
var fakeTiledImage0 = {
|
||||||
viewer: fakeViewer
|
viewer: fakeViewer,
|
||||||
|
source: OpenSeadragon.TileSource.prototype
|
||||||
};
|
};
|
||||||
var fakeTiledImage1 = {
|
var fakeTiledImage1 = {
|
||||||
viewer: fakeViewer
|
viewer: fakeViewer,
|
||||||
|
source: OpenSeadragon.TileSource.prototype
|
||||||
};
|
};
|
||||||
|
|
||||||
var fakeTile0 = {
|
var fakeTile0 = {
|
||||||
@ -74,7 +76,8 @@
|
|||||||
raiseEvent: function() {}
|
raiseEvent: function() {}
|
||||||
};
|
};
|
||||||
var fakeTiledImage0 = {
|
var fakeTiledImage0 = {
|
||||||
viewer: fakeViewer
|
viewer: fakeViewer,
|
||||||
|
source: OpenSeadragon.TileSource.prototype
|
||||||
};
|
};
|
||||||
|
|
||||||
var fakeTile0 = {
|
var fakeTile0 = {
|
||||||
|
Loading…
Reference in New Issue
Block a user