diff --git a/src/tiledimage.js b/src/tiledimage.js index e4749fbc..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,8 +947,10 @@ function updateViewport( tiledImage ) { // Load the new 'best' tile if (best && !best.context2D) { loadTile( tiledImage, best, currentTime ); + tiledImage._setFullyLoaded(false); + } else { + 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', + }]); + }); + })();