mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-28 16:16:08 +03:00
269 lines
9.6 KiB
JavaScript
269 lines
9.6 KiB
JavaScript
/*
|
|
* OpenSeadragon - ImageLoader
|
|
*
|
|
* Copyright (C) 2009 CodePlex Foundation
|
|
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* - Neither the name of CodePlex Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
(function($){
|
|
|
|
/**
|
|
* @private
|
|
* @class ImageJob
|
|
* @classdesc Handles downloading of a single image.
|
|
* @param {Object} options - Options for this ImageJob.
|
|
* @param {String} [options.src] - URL of image to download.
|
|
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
|
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
|
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
|
|
* @param {Function} [options.callback] - Called once image has been downloaded.
|
|
* @param {Function} [options.abort] - Called when this image job is aborted.
|
|
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
|
|
*/
|
|
function ImageJob (options) {
|
|
|
|
$.extend(true, this, {
|
|
timeout: $.DEFAULT_SETTINGS.timeout,
|
|
jobId: null
|
|
}, options);
|
|
|
|
/**
|
|
* Image object which will contain downloaded image.
|
|
* @member {Image} image
|
|
* @memberof OpenSeadragon.ImageJob#
|
|
*/
|
|
this.image = null;
|
|
}
|
|
|
|
ImageJob.prototype = {
|
|
errorMsg: null,
|
|
|
|
/**
|
|
* Starts the image job.
|
|
* @method
|
|
*/
|
|
start: function(){
|
|
var self = this;
|
|
var selfAbort = this.abort;
|
|
|
|
this.image = new Image();
|
|
|
|
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);
|
|
|
|
// 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) {
|
|
this.image.onload = this.image.onerror = this.image.onabort = null;
|
|
if (!successful) {
|
|
this.image = null;
|
|
}
|
|
|
|
if (this.jobId) {
|
|
window.clearTimeout(this.jobId);
|
|
}
|
|
|
|
this.callback(this);
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @class ImageLoader
|
|
* @memberof OpenSeadragon
|
|
* @classdesc Handles downloading of a set of images using asynchronous queue pattern.
|
|
* You generally won't have to interact with the ImageLoader directly.
|
|
* @param {Object} options - Options for this ImageLoader.
|
|
* @param {Number} [options.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.
|
|
* @param {Number} [options.timeout] - The max number of milliseconds that an image job may take to complete.
|
|
*/
|
|
$.ImageLoader = function(options) {
|
|
|
|
$.extend(true, this, {
|
|
jobLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
|
|
timeout: $.DEFAULT_SETTINGS.timeout,
|
|
jobQueue: [],
|
|
jobsInProgress: 0
|
|
}, options);
|
|
|
|
};
|
|
|
|
/** @lends OpenSeadragon.ImageLoader.prototype */
|
|
$.ImageLoader.prototype = {
|
|
|
|
/**
|
|
* Add an unloaded image to the loader queue.
|
|
* @method
|
|
* @param {Object} options - Options for this job.
|
|
* @param {String} [options.src] - URL of image to download.
|
|
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
|
|
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
|
|
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
|
|
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
|
|
* requests.
|
|
* @param {Function} [options.callback] - Called once image has been downloaded.
|
|
* @param {Function} [options.abort] - Called when this image job is aborted.
|
|
*/
|
|
addJob: function(options) {
|
|
var _this = this,
|
|
complete = function(job) {
|
|
completeJob(_this, job, options.callback);
|
|
},
|
|
jobOptions = {
|
|
src: options.src,
|
|
loadWithAjax: options.loadWithAjax,
|
|
ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null,
|
|
crossOriginPolicy: options.crossOriginPolicy,
|
|
ajaxWithCredentials: options.ajaxWithCredentials,
|
|
callback: complete,
|
|
abort: options.abort,
|
|
timeout: this.timeout
|
|
},
|
|
newJob = new ImageJob(jobOptions);
|
|
|
|
if ( !this.jobLimit || this.jobsInProgress < this.jobLimit ) {
|
|
newJob.start();
|
|
this.jobsInProgress++;
|
|
}
|
|
else {
|
|
this.jobQueue.push( newJob );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Clear any unstarted image loading jobs from the queue.
|
|
* @method
|
|
*/
|
|
clear: function() {
|
|
for( var i = 0; i < this.jobQueue.length; i++ ) {
|
|
var job = this.jobQueue[i];
|
|
if ( typeof job.abort === "function" ) {
|
|
job.abort();
|
|
}
|
|
}
|
|
|
|
this.jobQueue = [];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Cleans up ImageJob once completed.
|
|
* @method
|
|
* @private
|
|
* @param loader - ImageLoader used to start job.
|
|
* @param job - The ImageJob that has completed.
|
|
* @param callback - Called once cleanup is finished.
|
|
*/
|
|
function completeJob(loader, job, callback) {
|
|
var nextJob;
|
|
|
|
loader.jobsInProgress--;
|
|
|
|
if ((!loader.jobLimit || loader.jobsInProgress < loader.jobLimit) && loader.jobQueue.length > 0) {
|
|
nextJob = loader.jobQueue.shift();
|
|
nextJob.start();
|
|
loader.jobsInProgress++;
|
|
}
|
|
|
|
callback(job.image, job.errorMsg, job.request);
|
|
}
|
|
|
|
}(OpenSeadragon));
|