/* * 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($){ /** * @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 {TileSource} [options.source] - Image loading strategy * @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 {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * see TileSrouce::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. */ $.ImageJob = function(options) { $.extend(true, this, { timeout: $.DEFAULT_SETTINGS.timeout, jobId: null }, options); /** * Data object which will contain downloaded image data. * @member {Image|*} image data object, by default an Image object (depends on TileSource) * @memberof OpenSeadragon.ImageJob# */ this.data = null; /** * User workspace to populate with helper variables * @member {*} user data, for people to append their data * @memberof OpenSeadragon.ImageJob# */ this.userData = {}; }; $.ImageJob.prototype = { errorMsg: null, /** * Starts the image job. * @method */ start: function() { var self = this; var selfAbort = this.abort; this.jobId = window.setTimeout(function () { self.finish(false, "Image load exceeded timeout (" + self.timeout + " ms)"); }, this.timeout); this.abort = function() { self.source.downloadTileAbort(self); if (typeof selfAbort === "function") { selfAbort(); } }; this.source.downloadTileStart(this); }, finish: function(successful, errorMessage) { this.errorMsg = errorMessage; this.data = this.source.downloadTileFinish(this, successful); 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 {TileSource} [options.source] - Image loading strategy * @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 {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form, * see TileSrouce::getPostData) or null * @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) { 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, downloadTileFinish: implementation.downloadTileFinish }; } var _this = this, complete = function(job) { completeJob(_this, job, options.callback); }, jobOptions = { src: options.src, source: options.source, loadWithAjax: options.loadWithAjax, ajaxHeaders: options.loadWithAjax ? options.ajaxHeaders : null, crossOriginPolicy: options.crossOriginPolicy, ajaxWithCredentials: options.ajaxWithCredentials, postData: options.postData, 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.data, job.errorMsg, job.request); //todo job.request might not exist } }(OpenSeadragon));