/* global QUnit, $, testLog */ (function() { var viewer; // These values are generated by a script that concatenates all the tile files and records // their byte ranges in a multi-dimensional array. // eslint-disable-next-line var tileManifest = {"tileRanges":[[[[0,3467]]],[[[3467,6954]]],[[[344916,348425]]],[[[348425,351948]]],[[[351948,355576]]],[[[355576,359520]]],[[[359520,364663]]],[[[364663,374196]]],[[[374196,407307]]],[[[407307,435465],[435465,463663]],[[463663,491839],[491839,520078]]],[[[6954,29582],[29582,50315],[50315,71936],[71936,92703]],[[92703,113385],[113385,133265],[133265,154763],[154763,175710]],[[175710,197306],[197306,218807],[218807,242177],[242177,263007]],[[263007,283790],[283790,304822],[304822,325691],[325691,344916]]]],"totalSize":520078} function getTileRangeHeader(level, x, y) { return 'bytes=' + tileManifest.tileRanges[level][x][y].join('-') + '/' + tileManifest.totalSize; } // This tile source demonstrates how you can retrieve individual tiles from a single file // using the Range header. var customTileSource = { width: 1000, height: 1000, tileWidth: 254, tileHeight: 254, tileOverlap: 1, maxLevel: 10, minLevel: 0, // The tile URL is always the same. Only the Range header changes getTileUrl: function () { return '/test/data/testpattern.blob'; }, // This method will send the appropriate range header for this tile based on the data // in tileByteRanges. getTileAjaxHeaders: function(level, x, y) { return { Range: getTileRangeHeader(level, x, y) }; }, }; QUnit.module('AJAX-Tiles', { beforeEach: function() { $('
').appendTo('#qunit-fixture'); testLog.reset(); viewer = OpenSeadragon({ id: 'example', prefixUrl: '/build/openseadragon/images/', springStiffness: 100, // Faster animation = faster tests, loadTilesWithAjax: true, ajaxHeaders: { 'X-Viewer-Header': 'ViewerHeaderValue' }, // TODO: this test proves that tile cacheKey does not change // with headers change, which by default are part of the key, // but we cannot automatically update them since users might // manually change it long before... or can we? The tile gets // reloaded, so user code should get re-executed. IMHO, // with tile propagation the best would be throw away old tile // and start anew callTileLoadedWithCachedData: true }); }, afterEach: function() { if (viewer && viewer.close) { viewer.close(); } viewer = null; } }); QUnit.test('tile-loaded event includes AJAX request object', function(assert) { var done = assert.async(); var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); assert.ok(evt.tileRequest, 'Event includes tileRequest property'); assert.equal(evt.tileRequest.readyState, XMLHttpRequest.DONE, 'tileRequest is in completed state'); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.open(customTileSource); }); QUnit.test('withCredentials is set in tile AJAX requests', function(assert) { var done = assert.async(); var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); assert.ok(evt.tileRequest, 'Event includes tileRequest property'); assert.equal(evt.tileRequest.readyState, XMLHttpRequest.DONE, 'tileRequest is in completed state'); assert.equal(evt.tileRequest.withCredentials, true, 'withCredentials is set in tile request'); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.addTiledImage({ tileSource: customTileSource, ajaxWithCredentials: true }); }); QUnit.test('tile-load-failed event includes AJAX request object', function(assert) { var done = assert.async(); // Create a tile source that points to a broken URL var brokenTileSource = OpenSeadragon.extend({}, customTileSource, { getTileUrl: function () { return '/test/data/testpattern.blob.invalid'; } }); var tileLoadFailed = function tileLoadFailed(evt) { viewer.removeHandler('tile-load-failed', tileLoadFailed); assert.ok(evt.tileRequest, 'Event includes tileRequest property'); assert.equal(evt.tileRequest.readyState, XMLHttpRequest.DONE, 'tileRequest is in completed state'); done(); }; viewer.addHandler('tile-load-failed', tileLoadFailed); viewer.open(brokenTileSource); }); QUnit.test('Headers can be set per-tile', function(assert) { var done = assert.async(); var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); var tile = evt.tile; assert.ok(tile, 'tile property exists on event'); assert.ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property'); assert.equal(tile.ajaxHeaders.Range, getTileRangeHeader(tile.level, tile.x, tile.y), 'Tile has correct range header.'); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.open(customTileSource); }); QUnit.test('Headers are propagated correctly', function(assert) { var done = assert.async(); // Create a tile source that sets a static header for tiles var staticHeaderTileSource = OpenSeadragon.extend({}, customTileSource, { getTileAjaxHeaders: function() { return { 'X-Tile-Header': 'TileHeaderValue' }; } }); var expectedHeaders = { 'X-Viewer-Header': 'ViewerHeaderValue', 'X-TiledImage-Header': 'TiledImageHeaderValue', 'X-Tile-Header': 'TileHeaderValue' }; var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); var tile = evt.tile; assert.ok(tile, 'tile property exists on event'); assert.ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property'); assert.deepEqual( tile.ajaxHeaders, expectedHeaders, 'Tile headers include headers set on Viewer and TiledImage' ); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.addTiledImage({ ajaxHeaders: { 'X-TiledImage-Header': 'TiledImageHeaderValue' }, tileSource: staticHeaderTileSource }); }); QUnit.test('Viewer headers are overwritten by TiledImage', function(assert) { var done = assert.async(); // Create a tile source that sets a static header for tiles var staticHeaderTileSource = OpenSeadragon.extend({}, customTileSource, { getTileAjaxHeaders: function() { return { 'X-Tile-Header': 'TileHeaderValue' }; } }); var expectedHeaders = { 'X-Viewer-Header': 'ViewerHeaderValue-Overwritten', 'X-TiledImage-Header': 'TiledImageHeaderValue', 'X-Tile-Header': 'TileHeaderValue' }; var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); var tile = evt.tile; assert.ok(tile, 'tile property exists on event'); assert.ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property'); assert.deepEqual( tile.ajaxHeaders, expectedHeaders, 'TiledImage header overwrites viewer header' ); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.addTiledImage({ ajaxHeaders: { 'X-TiledImage-Header': 'TiledImageHeaderValue', 'X-Viewer-Header': 'ViewerHeaderValue-Overwritten' }, tileSource: staticHeaderTileSource }); }); QUnit.test('TiledImage headers are overwritten by Tile', function(assert) { var done = assert.async(); var expectedHeaders = { 'X-Viewer-Header': 'ViewerHeaderValue', 'X-TiledImage-Header': 'TiledImageHeaderValue-Overwritten', 'X-Tile-Header': 'TileHeaderValue' }; var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); var tile = evt.tile; assert.ok(tile, 'tile property exists on event'); assert.ok(tile.ajaxHeaders, 'Tile has ajaxHeaders property'); assert.deepEqual( tile.ajaxHeaders, expectedHeaders, 'Tile header overwrites TiledImage header' ); done(); }; viewer.addHandler('tile-loaded', tileLoaded); // Create a tile source that sets a static header for tiles var staticHeaderTileSource = OpenSeadragon.extend({}, customTileSource, { getTileAjaxHeaders: function() { return { 'X-TiledImage-Header': 'TiledImageHeaderValue-Overwritten', 'X-Tile-Header': 'TileHeaderValue' }; } }); viewer.addTiledImage({ ajaxHeaders: { 'X-TiledImage-Header': 'TiledImageHeaderValue' }, tileSource: staticHeaderTileSource }); }); QUnit.test('Viewer headers can be updated', function(assert) { var done = assert.async(); var newHeaders = { 'X-Viewer-Header': 'ViewerHeaderValue-Updated', 'X-Viewer-Header2': 'ViewerHeaderValue2' } var newHeaders2 = { Range: 'test', } var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); // set new Viewer headers and propagate to TiledImage and Tile viewer.setAjaxHeaders(newHeaders); viewer.addHandler('tile-loaded', tileLoaded2); }; var tileLoaded2 = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded2); assert.deepEqual(viewer.ajaxHeaders, newHeaders); assert.deepEqual(evt.tiledImage.ajaxHeaders, newHeaders); assert.deepEqual( evt.tile.ajaxHeaders, OpenSeadragon.extend( {}, viewer.ajaxHeaders, evt.tiledImage.ajaxHeaders, { Range: getTileRangeHeader(evt.tile.level, evt.tile.x, evt.tile.y) } ) ); // set new Viewer headers and propagate to TiledImage and Tile viewer.setAjaxHeaders(newHeaders2, true); viewer.addHandler('tile-loaded', tileLoaded3); }; var tileLoaded3 = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded3); assert.deepEqual(viewer.ajaxHeaders, newHeaders2); assert.deepEqual(evt.tiledImage.ajaxHeaders, newHeaders2); assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header'], undefined); assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header2'], undefined); // 'Range' header entry set per tile and must not be overwritten by Viewer header assert.equal(evt.tile.ajaxHeaders.Range, getTileRangeHeader(evt.tile.level, evt.tile.x, evt.tile.y)); // set new Viewer headers but do not propagate to TiledImage and Tile viewer.setAjaxHeaders(newHeaders, false); viewer.addHandler('tile-loaded', tileLoaded4); }; var tileLoaded4 = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded4); assert.deepEqual(viewer.ajaxHeaders, newHeaders); assert.deepEqual(evt.tiledImage.ajaxHeaders, newHeaders2); assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header'], undefined); assert.equal(evt.tile.ajaxHeaders['X-Viewer-Header2'], undefined); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.open(customTileSource); }); QUnit.test('TiledImage headers can be updated', function(assert) { var done = assert.async(); var tileSourceHeaders = { 'X-Tile-Header': 'TileHeaderValue' } var newHeaders = { 'X-TiledImage-Header': 'TiledImageHeaderValue-Updated', 'X-TiledImage-Header2': 'TiledImageHeaderValue2' } var newHeaders2 = { 'X-Viewer-Header': 'ViewerHeaderValue-Updated', 'X-Tile-Header': 'TileHeaderValue-Updated' } var tileLoaded = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded); // set new TiledImage headers and propagate to Tile evt.tiledImage.setAjaxHeaders(newHeaders); viewer.addHandler('tile-loaded', tileLoaded2); }; var tileLoaded2 = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded2); assert.deepEqual(viewer.ajaxHeaders, { 'X-Viewer-Header': 'ViewerHeaderValue' }); assert.deepEqual(evt.tiledImage._ownAjaxHeaders, newHeaders); assert.deepEqual(evt.tiledImage.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders)); assert.deepEqual(evt.tile.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders, tileSourceHeaders)); // set new TiledImage headers (that overwrite header entries of Viewer and Tile) and propagate to Tile evt.tiledImage.setAjaxHeaders(newHeaders2, true); viewer.addHandler('tile-loaded', tileLoaded3); }; var tileLoaded3 = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded3); assert.deepEqual(viewer.ajaxHeaders, { 'X-Viewer-Header': 'ViewerHeaderValue' }); assert.deepEqual(evt.tiledImage._ownAjaxHeaders, newHeaders2); assert.deepEqual(evt.tiledImage.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders2)); assert.deepEqual(evt.tile.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders2, tileSourceHeaders)); // set new TiledImage headers but do not propagate to Tile evt.tiledImage.setAjaxHeaders(null, false); viewer.addHandler('tile-loaded', tileLoaded4); }; var tileLoaded4 = function tileLoaded(evt) { viewer.removeHandler('tile-loaded', tileLoaded4); assert.deepEqual(viewer.ajaxHeaders, { 'X-Viewer-Header': 'ViewerHeaderValue' }); assert.deepEqual(evt.tiledImage._ownAjaxHeaders, {}); assert.deepEqual(evt.tiledImage.ajaxHeaders, viewer.ajaxHeaders); assert.deepEqual(evt.tile.ajaxHeaders, OpenSeadragon.extend({}, viewer.ajaxHeaders, newHeaders2, tileSourceHeaders)); done(); }; viewer.addHandler('tile-loaded', tileLoaded); viewer.addTiledImage({ ajaxHeaders: { 'X-TiledImage-Header': 'TiledImageHeaderValue' }, tileSource: OpenSeadragon.extend({}, customTileSource, { getTileAjaxHeaders: function() { return tileSourceHeaders; } }), }); }); })();