From c820f9f918eeb9f2ddc2f5f10369ae7a48cb2832 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 2 Jan 2015 15:45:46 -0800 Subject: [PATCH 1/2] Added ajaxWithCredentials option --- src/openseadragon.js | 21 +++++++- src/tilesource.js | 124 +++++++++++++++++++++++++------------------ src/viewer.js | 12 ++++- 3 files changed, 100 insertions(+), 57 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 07abcd42..81028d19 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -551,8 +551,11 @@ * If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage. * * @property {String|Boolean} [crossOriginPolicy=false] - * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will - * not use CORS, and the canvas will be tainted. + * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will + * not use CORS, and the canvas will be tainted. + * + * @property {Boolean} [ajaxWithCredentials=false] + * Whether to set the withCredentials XHR flag for AJAX requests (when loading tile sources). * */ @@ -911,6 +914,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ tileHost: null, initialPage: 0, crossOriginPolicy: false, + ajaxWithCredentials: false, //PAN AND ZOOM SETTINGS AND CONSTRAINTS panHorizontal: true, @@ -1923,6 +1927,15 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ * @throws {Error} */ makeAjaxRequest: function( url, onSuccess, onError ) { + var withCredentials; + + if( $.isPlainObject( url ) ){ + onSuccess = url.success; + onError = url.error; + withCredentials = url.withCredentials; + url = url.url; + } + var protocol = $.getUrlProtocol( url ); var request = $.createAjaxRequest( protocol === "file:" ); @@ -1949,6 +1962,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ } }; + if (withCredentials) { + request.withCredentials = true; + } + try { request.open( "GET", url, true ); request.send( null ); diff --git a/src/tilesource.js b/src/tilesource.js index deecc742..8d6ab5fb 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -74,8 +74,9 @@ * The maximum level to attempt to load. */ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) { - var callback = null, - args = arguments, + var _this = this; + + var args = arguments, options, i; @@ -102,19 +103,23 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve //source $.extend( true, this, options ); - //Any functions that are passed as arguments are bound to the ready callback - /*jshint loopfunc:true*/ - for ( i = 0; i < arguments.length; i++ ) { - if ( $.isFunction( arguments[ i ] ) ) { - callback = arguments[ i ]; - this.addHandler( 'ready', function ( event ) { - callback( event ); - } ); - //only one callback per constructor - break; + if (!this.success) { + //Any functions that are passed as arguments are bound to the ready callback + for ( i = 0; i < arguments.length; i++ ) { + if ( $.isFunction( arguments[ i ] ) ) { + this.success = arguments[ i ]; + //only one callback per constructor + break; + } } } + if (this.success) { + this.addHandler( 'ready', function ( event ) { + _this.success( event ); + } ); + } + /** * Ratio of width to height * @member {Number} aspectRatio @@ -127,7 +132,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve */ /** * The size of the image tiles used to compose the image. - * Please note that tileSize may be deprecated in a future release. + * Please note that tileSize may be deprecated in a future release. * Instead the getTileSize(level) function should be used. * @member {Number} tileSize * @memberof OpenSeadragon.TileSource# @@ -148,12 +153,16 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve * @memberof OpenSeadragon.TileSource# */ /** - * + * * @member {Boolean} ready * @memberof OpenSeadragon.TileSource# */ if( 'string' == $.type( arguments[ 0 ] ) ){ + this.url = arguments[0]; + } + + if (this.url) { //in case the getImageInfo method is overriden and/or implies an //async mechanism set some safe defaults first this.aspectRatio = 1; @@ -165,7 +174,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve this.ready = false; //configuration via url implies the extending class //implements and 'configure' - this.getImageInfo( arguments[ 0 ] ); + this.getImageInfo( this.url ); } else { @@ -185,8 +194,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve Math.log( 2 ) ) : 0 ); - if( callback && $.isFunction( callback ) ){ - callback( this ); + if( this.success && $.isFunction( this.success ) ){ + this.success( this ); } } @@ -197,7 +206,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ /** - * Return the tileSize for a given level. + * Return the tileSize for a given level. * Subclasses should override this if tileSizes can be different at different levels * such as in IIIFTileSource. Code should use this function rather than reading * from .tileSize directly. tileSize may be deprecated in a future release. @@ -355,6 +364,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ } options = $TileSource.prototype.configure.apply( _this, [ data, url ]); + if (options.ajaxWithCredentials === undefined) { + options.ajaxWithCredentials = _this.ajaxWithCredentials; + } + readySource = new $TileSource( options ); _this.ready = true; /** @@ -383,45 +396,50 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ }); } else { // request info via xhr asynchronously. - $.makeAjaxRequest( url, function( xhr ) { - var data = processResponse( xhr ); - callback( data ); - }, function ( xhr, exc ) { - var msg; + $.makeAjaxRequest( { + url: url, + withCredentials: this.ajaxWithCredentials, + success: function( xhr ) { + var data = processResponse( xhr ); + callback( data ); + }, + error: function ( xhr, exc ) { + var msg; - /* - IE < 10 will block XHR requests to different origins. Any property access on the request - object will raise an exception which we'll attempt to handle by formatting the original - exception rather than the second one raised when we try to access xhr.status - */ - try { - msg = "HTTP " + xhr.status + " attempting to load TileSource"; - } catch ( e ) { - var formattedExc; - if ( typeof( exc ) == "undefined" || !exc.toString ) { - formattedExc = "Unknown error"; - } else { - formattedExc = exc.toString(); + /* + IE < 10 will block XHR requests to different origins. Any property access on the request + object will raise an exception which we'll attempt to handle by formatting the original + exception rather than the second one raised when we try to access xhr.status + */ + try { + msg = "HTTP " + xhr.status + " attempting to load TileSource"; + } catch ( e ) { + var formattedExc; + if ( typeof( exc ) == "undefined" || !exc.toString ) { + formattedExc = "Unknown error"; + } else { + formattedExc = exc.toString(); + } + + msg = formattedExc + " attempting to load TileSource"; } - msg = formattedExc + " attempting to load TileSource"; + /*** + * Raised when an error occurs loading a TileSource. + * + * @event open-failed + * @memberof OpenSeadragon.TileSource + * @type {object} + * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event. + * @property {String} message + * @property {String} source + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + _this.raiseEvent( 'open-failed', { + message: msg, + source: url + }); } - - /*** - * Raised when an error occurs loading a TileSource. - * - * @event open-failed - * @memberof OpenSeadragon.TileSource - * @type {object} - * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event. - * @property {String} message - * @property {String} source - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - _this.raiseEvent( 'open-failed', { - message: msg, - source: url - }); }); } diff --git a/src/viewer.js b/src/viewer.js index 781af981..5dcdd1cd 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2051,14 +2051,22 @@ function getTileSourceImplementation( viewer, tileSource, successCallback, setTimeout( function() { if ( $.type( tileSource ) == 'string' ) { //If its still a string it means it must be a url at this point - tileSource = new $.TileSource( tileSource, function( event ) { - successCallback( event.tileSource ); + tileSource = new $.TileSource({ + url: tileSource, + ajaxWithCredentials: viewer.ajaxWithCredentials, + success: function( event ) { + successCallback( event.tileSource ); + } }); tileSource.addHandler( 'open-failed', function( event ) { failCallback( event ); } ); } else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ) { + if (tileSource.ajaxWithCredentials === undefined) { + tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials; + } + if ( $.isFunction( tileSource.getTileUrl ) ) { //Custom tile source var customTileSource = new $.TileSource( tileSource ); From a336b2366701ca44dbc92ea6dcdfe10447128795 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 2 Jan 2015 16:07:11 -0800 Subject: [PATCH 2/2] Documentation for ajaxWithCredentials-related changes --- changelog.txt | 1 + src/openseadragon.js | 12 ++++++++---- src/tilesource.js | 41 ++++++++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/changelog.txt b/changelog.txt index 98a94fd0..9649171b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -39,6 +39,7 @@ OPENSEADRAGON CHANGELOG * Rect and Point toString() functions are now consistent: rounding values to nearest hundredth * Overlays appear in the DOM immediately on open or addOverlay (#507) * imageLoaderLimit now works (#544) +* Added ajaxWithCredentials option (#543) 1.2.0: (in progress) diff --git a/src/openseadragon.js b/src/openseadragon.js index 81028d19..dc7a5638 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -556,6 +556,7 @@ * * @property {Boolean} [ajaxWithCredentials=false] * Whether to set the withCredentials XHR flag for AJAX requests (when loading tile sources). + * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level. * */ @@ -1920,15 +1921,18 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ /** * Makes an AJAX request. - * @function - * @param {String} url - the url to request - * @param {Function} onSuccess - a function to call on a successful response - * @param {Function} onError - a function to call on when an error occurs + * @param {Object} options + * @param {String} options.url - the url to request + * @param {Function} options.success - a function to call on a successful response + * @param {Function} options.error - a function to call on when an error occurs + * @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials * @throws {Error} */ makeAjaxRequest: function( url, onSuccess, onError ) { var withCredentials; + // Note that our preferred API is that you pass in a single object; the named + // arguments are for legacy support. if( $.isPlainObject( url ) ){ onSuccess = url.success; onError = url.error; diff --git a/src/tilesource.js b/src/tilesource.js index 8d6ab5fb..1c0d29d2 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -38,39 +38,46 @@ /** * @class TileSource * @classdesc The TileSource contains the most basic implementation required to create a - * smooth transition between layer in an image pyramid. It has only a single key - * interface that must be implemented to complete it key functionality: + * smooth transition between layers in an image pyramid. It has only a single key + * interface that must be implemented to complete its key functionality: * 'getTileUrl'. It also has several optional interfaces that can be * implemented if a new TileSource wishes to support configuration via a simple * object or array ('configure') and if the tile source supports or requires - * configuration via retreival of a document on the network ala AJAX or JSONP, + * configuration via retrieval of a document on the network ala AJAX or JSONP, * ('getImageInfo'). *
- * By default the image pyramid is split into N layers where the images longest + * By default the image pyramid is split into N layers where the image's longest * side in M (in pixels), where N is the smallest integer which satisfies * 2^(N+1) >= M. * * @memberof OpenSeadragon * @extends OpenSeadragon.EventSource - * @param {Number|Object|Array|String} width - * If more than a single argument is supplied, the traditional use of - * positional parameters is supplied and width is expected to be the width - * source image at its max resolution in pixels. If a single argument is supplied and - * it is an Object or Array, the construction is assumed to occur through - * the extending classes implementation of 'configure'. Finally if only a - * single argument is supplied and it is a String, the extending class is - * expected to implement 'getImageInfo' and 'configure'. - * @param {Number} height + * @param {Object} options + * You can either specify a URL, or literally define the TileSource (by specifying + * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former, + * the extending class is expected to implement 'getImageInfo' and 'configure'. + * For the latter, the construction is assumed to occur through + * the extending classes implementation of 'configure'. + * @param {String} [options.url] + * The URL for the data necessary for this TileSource. + * @param {Function} [options.success] + * A function to be called upon successful creation. + * @param {Boolean} [options.ajaxWithCredentials] + * If this TileSource needs to make an AJAX call, this specifies whether to set + * the XHR's withCredentials (for accessing secure data). + * @param {Number} [options.width] * Width of the source image at max resolution in pixels. - * @param {Number} tileSize + * @param {Number} [options.height] + * Height of the source image at max resolution in pixels. + * @param {Number} [options.tileSize] * The size of the tiles to assumed to make up each pyramid layer in pixels. * Tile size determines the point at which the image pyramid must be * divided into a matrix of smaller images. - * @param {Number} tileOverlap + * @param {Number} [options.tileOverlap] * The number of pixels each tile is expected to overlap touching tiles. - * @param {Number} minLevel + * @param {Number} [options.minLevel] * The minimum level to attempt to load. - * @param {Number} maxLevel + * @param {Number} [options.maxLevel] * The maximum level to attempt to load. */ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) {