Merge pull request #553 from openseadragon/ajax

Allowing XHRs withCredentials for authenticated requests
This commit is contained in:
Ian Gilman 2015-01-15 14:35:06 -08:00
commit c55daff222
4 changed files with 133 additions and 78 deletions

View File

@ -39,6 +39,7 @@ OPENSEADRAGON CHANGELOG
* Rect and Point toString() functions are now consistent: rounding values to nearest hundredth * Rect and Point toString() functions are now consistent: rounding values to nearest hundredth
* Overlays appear in the DOM immediately on open or addOverlay (#507) * Overlays appear in the DOM immediately on open or addOverlay (#507)
* imageLoaderLimit now works (#544) * imageLoaderLimit now works (#544)
* Added ajaxWithCredentials option (#543)
1.2.1: (in progress) 1.2.1: (in progress)

View File

@ -559,8 +559,12 @@
* If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage. * If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage.
* *
* @property {String|Boolean} [crossOriginPolicy=false] * @property {String|Boolean} [crossOriginPolicy=false]
* Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will
* not use CORS, and the canvas will be tainted. * 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).
* Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.
* *
*/ */
@ -919,6 +923,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
tileHost: null, tileHost: null,
initialPage: 0, initialPage: 0,
crossOriginPolicy: false, crossOriginPolicy: false,
ajaxWithCredentials: false,
//PAN AND ZOOM SETTINGS AND CONSTRAINTS //PAN AND ZOOM SETTINGS AND CONSTRAINTS
panHorizontal: true, panHorizontal: true,
@ -1925,13 +1930,25 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
/** /**
* Makes an AJAX request. * Makes an AJAX request.
* @function * @param {Object} options
* @param {String} url - the url to request * @param {String} options.url - the url to request
* @param {Function} onSuccess - a function to call on a successful response * @param {Function} options.success - a function to call on a successful response
* @param {Function} onError - a function to call on when an error occurs * @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} * @throws {Error}
*/ */
makeAjaxRequest: function( url, onSuccess, onError ) { 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;
withCredentials = url.withCredentials;
url = url.url;
}
var protocol = $.getUrlProtocol( url ); var protocol = $.getUrlProtocol( url );
var request = $.createAjaxRequest( protocol === "file:" ); var request = $.createAjaxRequest( protocol === "file:" );
@ -1958,6 +1975,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
} }
}; };
if (withCredentials) {
request.withCredentials = true;
}
try { try {
request.open( "GET", url, true ); request.open( "GET", url, true );
request.send( null ); request.send( null );

View File

@ -38,44 +38,52 @@
/** /**
* @class TileSource * @class TileSource
* @classdesc The TileSource contains the most basic implementation required to create a * @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 * smooth transition between layers in an image pyramid. It has only a single key
* interface that must be implemented to complete it key functionality: * interface that must be implemented to complete its key functionality:
* 'getTileUrl'. It also has several optional interfaces that can be * 'getTileUrl'. It also has several optional interfaces that can be
* implemented if a new TileSource wishes to support configuration via a simple * implemented if a new TileSource wishes to support configuration via a simple
* object or array ('configure') and if the tile source supports or requires * 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'). * ('getImageInfo').
* <br/> * <br/>
* 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 * side in M (in pixels), where N is the smallest integer which satisfies
* <strong>2^(N+1) >= M</strong>. * <strong>2^(N+1) >= M</strong>.
* *
* @memberof OpenSeadragon * @memberof OpenSeadragon
* @extends OpenSeadragon.EventSource * @extends OpenSeadragon.EventSource
* @param {Number|Object|Array|String} width * @param {Object} options
* If more than a single argument is supplied, the traditional use of * You can either specify a URL, or literally define the TileSource (by specifying
* positional parameters is supplied and width is expected to be the width * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,
* source image at its max resolution in pixels. If a single argument is supplied and * the extending class is expected to implement 'getImageInfo' and 'configure'.
* it is an Object or Array, the construction is assumed to occur through * For the latter, the construction is assumed to occur through
* the extending classes implementation of 'configure'. Finally if only a * the extending classes implementation of 'configure'.
* single argument is supplied and it is a String, the extending class is * @param {String} [options.url]
* expected to implement 'getImageInfo' and 'configure'. * The URL for the data necessary for this TileSource.
* @param {Number} height * @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. * 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. * 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 * Tile size determines the point at which the image pyramid must be
* divided into a matrix of smaller images. * 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. * 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. * The minimum level to attempt to load.
* @param {Number} maxLevel * @param {Number} [options.maxLevel]
* The maximum level to attempt to load. * The maximum level to attempt to load.
*/ */
$.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) { $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) {
var callback = null, var _this = this;
args = arguments,
var args = arguments,
options, options,
i; i;
@ -102,19 +110,23 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
//source //source
$.extend( true, this, options ); $.extend( true, this, options );
//Any functions that are passed as arguments are bound to the ready callback if (!this.success) {
/*jshint loopfunc:true*/ //Any functions that are passed as arguments are bound to the ready callback
for ( i = 0; i < arguments.length; i++ ) { for ( i = 0; i < arguments.length; i++ ) {
if ( $.isFunction( arguments[ i ] ) ) { if ( $.isFunction( arguments[ i ] ) ) {
callback = arguments[ i ]; this.success = arguments[ i ];
this.addHandler( 'ready', function ( event ) { //only one callback per constructor
callback( event ); break;
} ); }
//only one callback per constructor
break;
} }
} }
if (this.success) {
this.addHandler( 'ready', function ( event ) {
_this.success( event );
} );
}
/** /**
* Ratio of width to height * Ratio of width to height
* @member {Number} aspectRatio * @member {Number} aspectRatio
@ -154,6 +166,10 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
*/ */
if( 'string' == $.type( arguments[ 0 ] ) ){ if( 'string' == $.type( arguments[ 0 ] ) ){
this.url = arguments[0];
}
if (this.url) {
//in case the getImageInfo method is overriden and/or implies an //in case the getImageInfo method is overriden and/or implies an
//async mechanism set some safe defaults first //async mechanism set some safe defaults first
this.aspectRatio = 1; this.aspectRatio = 1;
@ -165,7 +181,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
this.ready = false; this.ready = false;
//configuration via url implies the extending class //configuration via url implies the extending class
//implements and 'configure' //implements and 'configure'
this.getImageInfo( arguments[ 0 ] ); this.getImageInfo( this.url );
} else { } else {
@ -185,8 +201,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
Math.log( 2 ) Math.log( 2 )
) : 0 ) : 0
); );
if( callback && $.isFunction( callback ) ){ if( this.success && $.isFunction( this.success ) ){
callback( this ); this.success( this );
} }
} }
@ -355,6 +371,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
} }
options = $TileSource.prototype.configure.apply( _this, [ data, url ]); options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
if (options.ajaxWithCredentials === undefined) {
options.ajaxWithCredentials = _this.ajaxWithCredentials;
}
readySource = new $TileSource( options ); readySource = new $TileSource( options );
_this.ready = true; _this.ready = true;
/** /**
@ -383,45 +403,50 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
}); });
} else { } else {
// request info via xhr asynchronously. // request info via xhr asynchronously.
$.makeAjaxRequest( url, function( xhr ) { $.makeAjaxRequest( {
var data = processResponse( xhr ); url: url,
callback( data ); withCredentials: this.ajaxWithCredentials,
}, function ( xhr, exc ) { success: function( xhr ) {
var msg; 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 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 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 exception rather than the second one raised when we try to access xhr.status
*/ */
try { try {
msg = "HTTP " + xhr.status + " attempting to load TileSource"; msg = "HTTP " + xhr.status + " attempting to load TileSource";
} catch ( e ) { } catch ( e ) {
var formattedExc; var formattedExc;
if ( typeof( exc ) == "undefined" || !exc.toString ) { if ( typeof( exc ) == "undefined" || !exc.toString ) {
formattedExc = "Unknown error"; formattedExc = "Unknown error";
} else { } else {
formattedExc = exc.toString(); 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
});
}); });
} }

View File

@ -2058,14 +2058,22 @@ function getTileSourceImplementation( viewer, tileSource, successCallback,
setTimeout( function() { setTimeout( function() {
if ( $.type( tileSource ) == 'string' ) { if ( $.type( tileSource ) == 'string' ) {
//If its still a string it means it must be a url at this point //If its still a string it means it must be a url at this point
tileSource = new $.TileSource( tileSource, function( event ) { tileSource = new $.TileSource({
successCallback( event.tileSource ); url: tileSource,
ajaxWithCredentials: viewer.ajaxWithCredentials,
success: function( event ) {
successCallback( event.tileSource );
}
}); });
tileSource.addHandler( 'open-failed', function( event ) { tileSource.addHandler( 'open-failed', function( event ) {
failCallback( event ); failCallback( event );
} ); } );
} else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ) { } else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ) {
if (tileSource.ajaxWithCredentials === undefined) {
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
}
if ( $.isFunction( tileSource.getTileUrl ) ) { if ( $.isFunction( tileSource.getTileUrl ) ) {
//Custom tile source //Custom tile source
var customTileSource = new $.TileSource( tileSource ); var customTileSource = new $.TileSource( tileSource );