From f1cdf906535262783a9a94cb2dcdd5362e47b55c Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 5 Feb 2016 09:14:43 -0800 Subject: [PATCH 1/8] Added "fully-loaded" event to TiledImage --- src/tiledimage.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 7772c186..b9746c08 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -815,8 +815,19 @@ function updateViewport( tiledImage ) { // Load the new 'best' tile if (best && !best.context2D) { loadTile( tiledImage, best, currentTime ); + } else { + /** + * Fired every time the TiledImage is drawn when all the necessary tiles for the + * current view have been fully loaded. + * + * @event fully-loaded + * @memberof OpenSeadragon.TiledImage + * @type {object} + * @property {OpenSeadragon.TiledImage} eventSource - A reference to the TiledImage which raised the event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + tiledImage.raiseEvent('fully-loaded'); } - } From 23b955c6b4dc5452b5f906587fb2e463b36d8aba Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 10 Aug 2016 10:35:08 -0700 Subject: [PATCH 2/8] Updated with improved API --- src/tiledimage.js | 46 +++++++++++++++++++++++++++++--------- test/modules/tiledimage.js | 40 +++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index c273223b..6acfc08c 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -163,6 +163,8 @@ $.TiledImage = function( options ) { }, options ); + this._fullyLoaded = false; + this._xSpring = new $.Spring({ initial: x, springStiffness: this.springStiffness, @@ -218,6 +220,37 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag return this._needsDraw; }, + /** + * @returns {Boolean} Whether all tiles necessary for this TiledImage to draw at the current view have been loaded. + */ + getFullyLoaded: function() { + return this._fullyLoaded; + }, + + // private + _setFullyLoaded: function(flag) { + if (flag === this._fullyLoaded) { + return; + } + + this._fullyLoaded = flag; + + /** + * Fired when the TiledImage's "fully loaded" flag (whether all tiles necessary for this TiledImage + * to draw at the current view have been loaded) changes. + * + * @event fully-loaded-change + * @memberof OpenSeadragon.TiledImage + * @type {object} + * @property {Boolean} fullyLoaded - The new "fully loaded" value. + * @property {OpenSeadragon.TiledImage} eventSource - A reference to the TiledImage which raised the event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent('fully-loaded-change', { + fullyLoaded: this._fullyLoaded + }); + }, + /** * Clears all tiles and triggers an update on the next call to * {@link OpenSeadragon.TiledImage#update}. @@ -914,18 +947,9 @@ function updateViewport( tiledImage ) { // Load the new 'best' tile if (best && !best.context2D) { loadTile( tiledImage, best, currentTime ); + tiledImage._setFullyLoaded(false); } else { - /** - * Fired every time the TiledImage is drawn when all the necessary tiles for the - * current view have been fully loaded. - * - * @event fully-loaded - * @memberof OpenSeadragon.TiledImage - * @type {object} - * @property {OpenSeadragon.TiledImage} eventSource - A reference to the TiledImage which raised the event. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - tiledImage.raiseEvent('fully-loaded'); + tiledImage._setFullyLoaded(true); } } diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index d7a46aef..3dc0eb33 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -222,6 +222,7 @@ }); }); + // ---------- asyncTest('getClipBounds', function() { var clip = new OpenSeadragon.Rect(100, 200, 800, 500); @@ -292,6 +293,7 @@ }); }); + // ---------- asyncTest('fitBounds', function() { function assertRectEquals(actual, expected, message) { @@ -338,6 +340,7 @@ ]); }); + // ---------- asyncTest('fitBounds in constructor', function() { function assertRectEquals(actual, expected, message) { @@ -383,6 +386,7 @@ }]); }); + // ---------- asyncTest('fitBounds with clipping', function() { function assertRectEquals(actual, expected, message) { @@ -426,4 +430,40 @@ fitBoundsPlacement: OpenSeadragon.Placement.TOP_LEFT }]); }); + + // ---------- + asyncTest('fullyLoaded', function() { + viewer.addHandler('open', function openHandler() { + viewer.removeHandler('open', openHandler); + + var image = viewer.world.getItemAt(0); + equal(image.getFullyLoaded(), false, 'not fully loaded at first'); + + var count = 0; + + var fullyLoadedChangeHandler = function(event) { + if (count === 0) { + equal(event.fullyLoaded, true, 'event includes true fullyLoaded property'); + equal(image.getFullyLoaded(), true, 'image is fully loaded after event'); + viewer.viewport.zoomBy(5, null, true); + } else if (count === 1) { + equal(event.fullyLoaded, false, 'event includes false fullyLoaded property'); + equal(image.getFullyLoaded(), false, 'image is not fully loaded after zoom'); + } else { + image.removeHandler('fully-loaded-change', fullyLoadedChangeHandler); + equal(image.getFullyLoaded(), true, 'image is once again fully loaded'); + start(); + } + + count++; + }; + + image.addHandler('fully-loaded-change', fullyLoadedChangeHandler); + }); + + viewer.open([{ + tileSource: '/test/data/tall.dzi', + }]); + }); + })(); From dde379ba12dc17e5565f627cb38485a391258fed Mon Sep 17 00:00:00 2001 From: Rick Burgstaler Date: Tue, 9 Aug 2016 16:36:12 -0500 Subject: [PATCH 3/8] Make tileSources option smarter about detecting when a json string or xml string has been passed in The tileSources option was only using a test looking for the presence of a "{", "[", or "<" character to determine if a json string or xml string was passed in. It is possible for a url to contain one of these characters as well which would break using the url as a tileSources parameter. The following is an example of a breaking url: http://myurl.org/{25fb14f0-a839-4c4d-8c97-dd1d67b2cb35}/MyImage.xml This patch resolves this issue. --- src/viewer.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index 02556bcf..92f0bb80 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2121,9 +2121,11 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal //allow plain xml strings or json strings to be parsed here if ( $.type( tileSource ) == 'string' ) { - if ( tileSource.match( /\s*<.*/ ) ) { + //xml should start with "<" and end with ">" + if ( tileSource.match( /^\s*<.*>\s*$/ ) ) { tileSource = $.parseXml( tileSource ); - } else if ( tileSource.match( /\s*[\{\[].*/ ) ) { + //json should start with "{" or "[" and end with "}" or "]" + } else if ( tileSource.match(/^\s*[\{\[].*[\}\]]\s*$/ ) ) { tileSource = $.parseJSON(tileSource); } } From d03b6a7569687f3fdff7a1fa7b036d4c5416811e Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 11 Aug 2016 09:48:23 -0700 Subject: [PATCH 4/8] changelog for #999 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index eb18e085..dea0843c 100644 --- a/changelog.txt +++ b/changelog.txt @@ -7,6 +7,7 @@ OPENSEADRAGON CHANGELOG * Added support for commonjs (#984) * Added an option to addTiledImage to change the crossOriginPolicy (#981) * Fixed issue with tiles not appearing with wrapHorizontal/wrapVertical if you pan too far away from the origin (#987) +* The Viewer's tileSources option is now smarter about detecting JSON vs XML vs URL (#999) 2.2.1: From 77726a684f744d405ab81afe5980942e63a98cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20=C3=96berg?= Date: Wed, 9 Mar 2016 16:54:06 +0100 Subject: [PATCH 5/8] Use control anchor configuration for custom toolbar also --- src/viewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/viewer.js b/src/viewer.js index 92f0bb80..94dc6248 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1746,7 +1746,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, if( this.toolbar ){ this.toolbar.addControl( this.navControl, - {anchor: $.ControlAnchor.TOP_LEFT} + {anchor: this.navigationControlAnchor || $.ControlAnchor.TOP_LEFT} ); } else { this.addControl( From f887b4fefc2daf67ab6a7dc0f477e77e573c0c14 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 15 Aug 2016 14:37:06 -0700 Subject: [PATCH 6/8] Changelog for #1004 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index dea0843c..c44d04d7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ OPENSEADRAGON CHANGELOG * Added an option to addTiledImage to change the crossOriginPolicy (#981) * Fixed issue with tiles not appearing with wrapHorizontal/wrapVertical if you pan too far away from the origin (#987) * The Viewer's tileSources option is now smarter about detecting JSON vs XML vs URL (#999) +* The navigationControlAnchor option now works for custom toolbar as well (#1004) 2.2.1: From ae1e2757baf8b8092c6003b989d4c1f886d109c8 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 16 Aug 2016 09:48:29 -0700 Subject: [PATCH 7/8] Changelog for #837 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index c44d04d7..8a661e31 100644 --- a/changelog.txt +++ b/changelog.txt @@ -9,6 +9,7 @@ OPENSEADRAGON CHANGELOG * Fixed issue with tiles not appearing with wrapHorizontal/wrapVertical if you pan too far away from the origin (#987) * The Viewer's tileSources option is now smarter about detecting JSON vs XML vs URL (#999) * The navigationControlAnchor option now works for custom toolbar as well (#1004) +* Added getFullyLoaded method and "fully-loaded-change" event to TiledImage to know when tiles are fully loaded (#837) 2.2.1: From 168bbfea29a5fd259790fca89b9e58627384e7dd Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 16 Aug 2016 10:18:39 -0700 Subject: [PATCH 8/8] Fixed test issues --- test/modules/multi-image.js | 77 ++++++++++++++++++++----------------- test/modules/tiledimage.js | 4 +- 2 files changed, 44 insertions(+), 37 deletions(-) diff --git a/test/modules/multi-image.js b/test/modules/multi-image.js index 1cfd0515..4cbd5211 100644 --- a/test/modules/multi-image.js +++ b/test/modules/multi-image.js @@ -211,44 +211,51 @@ asyncTest('Transparent image on top of others', function() { viewer.open('/test/data/testpattern.dzi'); - // TODO: replace with fully-loaded event listener when available. - setTimeout(function() { - var imageData = viewer.drawer.context.getImageData(0, 0, 500, 500); - // Pixel 250,250 will be in the hole of the A - var expectedVal = getPixelValue(imageData, 250, 250); + var density = OpenSeadragon.pixelDensityRatio; - notEqual(expectedVal.r, 0, 'Red channel should not be 0'); - notEqual(expectedVal.g, 0, 'Green channel should not be 0'); - notEqual(expectedVal.b, 0, 'Blue channel should not be 0'); - notEqual(expectedVal.a, 0, 'Alpha channel should not be 0'); + viewer.addHandler('open', function() { + var firstImage = viewer.world.getItemAt(0); + firstImage.addHandler('fully-loaded-change', function() { + var imageData = viewer.drawer.context.getImageData(0, 0, + 500 * OpenSeadragon.pixelDensityRatio, 500 * density); - viewer.addSimpleImage({ - url: '/test/data/A.png' + // Pixel 250,250 will be in the hole of the A + var expectedVal = getPixelValue(imageData, 250 * density, 250 * density); + + notEqual(expectedVal.r, 0, 'Red channel should not be 0'); + notEqual(expectedVal.g, 0, 'Green channel should not be 0'); + notEqual(expectedVal.b, 0, 'Blue channel should not be 0'); + notEqual(expectedVal.a, 0, 'Alpha channel should not be 0'); + + viewer.addSimpleImage({ + url: '/test/data/A.png', + success: function() { + var secondImage = viewer.world.getItemAt(1); + secondImage.addHandler('fully-loaded-change', function() { + var imageData = viewer.drawer.context.getImageData(0, 0, 500 * density, 500 * density); + var actualVal = getPixelValue(imageData, 250 * density, 250 * density); + + equal(actualVal.r, expectedVal.r, + 'Red channel should not change in transparent part of the A'); + equal(actualVal.g, expectedVal.g, + 'Green channel should not change in transparent part of the A'); + equal(actualVal.b, expectedVal.b, + 'Blue channel should not change in transparent part of the A'); + equal(actualVal.a, expectedVal.a, + 'Alpha channel should not change in transparent part of the A'); + + var onAVal = getPixelValue(imageData, 333 * density, 250 * density); + equal(onAVal.r, 0, 'Red channel should be null on the A'); + equal(onAVal.g, 0, 'Green channel should be null on the A'); + equal(onAVal.b, 0, 'Blue channel should be null on the A'); + equal(onAVal.a, 255, 'Alpha channel should be 255 on the A'); + + start(); + }); + } + }); }); - - // TODO: replace with fully-loaded event listener when available. - setTimeout(function() { - var imageData = viewer.drawer.context.getImageData(0, 0, 500, 500); - var actualVal = getPixelValue(imageData, 250, 250); - - equal(actualVal.r, expectedVal.r, - 'Red channel should not change in transparent part of the A'); - equal(actualVal.g, expectedVal.g, - 'Green channel should not change in transparent part of the A'); - equal(actualVal.b, expectedVal.b, - 'Blue channel should not change in transparent part of the A'); - equal(actualVal.a, expectedVal.a, - 'Alpha channel should not change in transparent part of the A'); - - var onAVal = getPixelValue(imageData, 333, 250); - equal(onAVal.r, 0, 'Red channel should be null on the A'); - equal(onAVal.g, 0, 'Green channel should be null on the A'); - equal(onAVal.b, 0, 'Blue channel should be null on the A'); - equal(onAVal.a, 255, 'Alpha channel should be 255 on the A'); - - start(); - }, 500); - }, 500); + }); function getPixelValue(imageData, x, y) { var offset = 4 * (y * imageData.width + x); diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 3dc0eb33..6dcae216 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -207,8 +207,8 @@ Util.spyOnce(viewer.drawer, 'setClip', function(rect) { var homeBounds = viewer.viewport.getHomeBounds(); - var canvasClip = viewer.viewport - .viewportToViewerElementRectangle(homeBounds); + var canvasClip = viewer.drawer + .viewportToDrawerRectangle(homeBounds); var precision = 0.00000001; Util.assertRectangleEquals(rect, canvasClip, precision, 'clipping should be ' + canvasClip);