diff --git a/src/tiledimage.js b/src/tiledimage.js
index 80204328..368a8c41 100644
--- a/src/tiledimage.js
+++ b/src/tiledimage.js
@@ -1,36 +1,36 @@
/*
- * OpenSeadragon - TiledImage
- *
- * Copyright (C) 2009 CodePlex Foundation
- * Copyright (C) 2010-2013 OpenSeadragon contributors
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * - Neither the name of CodePlex Foundation nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
+* OpenSeadragon - TiledImage
+*
+* Copyright (C) 2009 CodePlex Foundation
+* Copyright (C) 2010-2013 OpenSeadragon contributors
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+* - Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+*
+* - Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* - Neither the name of CodePlex Foundation nor the names of its
+* contributors may be used to endorse or promote products derived from
+* this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
(function( $ ){
@@ -72,7 +72,8 @@
* @param {Boolean} [options.iOSDevice] - See {@link OpenSeadragon.Options}.
* @param {Number} [options.opacity=1] - Set to draw at proportional opacity. If zero, images will not draw.
* @param {Boolean} [options.preload=false] - Set true to load even when the image is hidden by zero opacity.
- * @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
+ * @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible
+ values.
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
* @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.
* @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}.
@@ -218,21 +219,21 @@ $.TiledImage = function( options ) {
// We need a callback to give image manipulation a chance to happen
this._drawingHandler = function(args) {
- /**
- * This event is fired just before the tile is drawn giving the application a chance to alter the image.
- *
- * NOTE: This event is only fired when the drawer is using a <canvas>.
- *
- * @event tile-drawing
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.Tile} tile - The Tile being drawn.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into.
- * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
+ /**
+ * This event is fired just before the tile is drawn giving the application a chance to alter the image.
+ *
+ * NOTE: This event is only fired when the drawer is using a <canvas>.
+ *
+ * @event tile-drawing
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
+ * @property {OpenSeadragon.Tile} tile - The Tile being drawn.
+ * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
+ * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into.
+ * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
_this.viewer.raiseEvent('tile-drawing', $.extend({
tiledImage: _this
}, args));
@@ -423,7 +424,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
/**
* @returns {OpenSeadragon.Point} The TiledImage's content size, in window coordinates.
*/
- getSizeInWindowCoordinates: function() {
+ getSizeInWindowCoordinates: function() {
var topLeft = this.imageToWindowCoordinates(new $.Point(0, 0));
var bottomRight = this.imageToWindowCoordinates(this.getContentSize());
return new $.Point(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
@@ -592,7 +593,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/
windowToImageCoordinates: function( pixel ) {
var viewerCoordinates = pixel.minus(
- OpenSeadragon.getElementPosition( this.viewer.element ));
+ OpenSeadragon.getElementPosition( this.viewer.element ));
return this.viewerElementToImageCoordinates( viewerCoordinates );
},
@@ -604,7 +605,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
imageToWindowCoordinates: function( pixel ) {
var viewerCoordinates = this.imageToViewerElementCoordinates( pixel );
return viewerCoordinates.plus(
- OpenSeadragon.getElementPosition( this.viewer.element ));
+ OpenSeadragon.getElementPosition( this.viewer.element ));
},
// private
@@ -633,7 +634,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/
viewportToImageZoom: function( viewportZoom ) {
var ratio = this._scaleSpring.current.value *
- this.viewport._containerInnerSize.x / this.source.dimensions.x;
+ this.viewport._containerInnerSize.x / this.source.dimensions.x;
return ratio * viewportZoom;
},
@@ -650,7 +651,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/
imageToViewportZoom: function( imageZoom ) {
var ratio = this._scaleSpring.current.value *
- this.viewport._containerInnerSize.x / this.source.dimensions.x;
+ this.viewport._containerInnerSize.x / this.source.dimensions.x;
return imageZoom / ratio;
},
@@ -666,7 +667,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
if (immediately) {
if (sameTarget && this._xSpring.current.value === position.x &&
- this._ySpring.current.value === position.y) {
+ this._ySpring.current.value === position.y) {
return;
}
@@ -1162,8 +1163,7 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
);
// Update the level and keep track of 'best' tile to load
- bestTile = updateLevel(
- this,
+ bestTile = this._updateLevel(
haveDrawn,
drawLevel,
level,
@@ -1176,17 +1176,17 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
// Stop the loop if lower-res tiles would all be covered by
// already drawn tiles
- if (providesCoverage(this.coverage, level)) {
+ if ($.TileSource._providesCoverage(this.coverage, level)) {
break;
}
}
// Perform the actual drawing
- drawTiles(this, this.lastDrawn);
+ this._drawTiles(this.lastDrawn);
// Load the new 'best' tile
if (bestTile && !bestTile.context2D) {
- loadTile(this, bestTile, currentTime);
+ this._loadTile(bestTile, currentTime);
this._needsDraw = true;
this._setFullyLoaded(false);
} else {
@@ -1233,586 +1233,855 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
topLeft: topLeftTile,
bottomRight: bottomRightTile,
};
- }
-});
-
-/**
- * @private
- * @inner
- * Updates all tiles at a given resolution level.
- * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @param {Boolean} haveDrawn
- * @param {Boolean} drawLevel
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} levelVisibility
- * @param {OpenSeadragon.Point} viewportTL - The index of the most top-left visible tile.
- * @param {OpenSeadragon.Point} viewportBR - The index of the most bottom-right visible tile.
- * @param {Number} currentTime
- * @param {OpenSeadragon.Tile} best - The current "best" tile to draw.
- */
-function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
- levelVisibility, drawArea, currentTime, best) {
-
- var topLeftBound = drawArea.getBoundingBox().getTopLeft();
- var bottomRightBound = drawArea.getBoundingBox().getBottomRight();
-
- if (tiledImage.viewer) {
- /**
- * - Needs documentation -
- *
- * @event update-level
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {Object} havedrawn
- * @property {Object} level
- * @property {Object} opacity
- * @property {Object} visibility
- * @property {OpenSeadragon.Rect} drawArea
- * @property {Object} topleft deprecated, use drawArea instead
- * @property {Object} bottomright deprecated, use drawArea instead
- * @property {Object} currenttime
- * @property {Object} best
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- tiledImage.viewer.raiseEvent('update-level', {
- tiledImage: tiledImage,
- havedrawn: haveDrawn,
- level: level,
- opacity: levelOpacity,
- visibility: levelVisibility,
- drawArea: drawArea,
- topleft: topLeftBound,
- bottomright: bottomRightBound,
- currenttime: currentTime,
- best: best
- });
- }
-
- resetCoverage(tiledImage.coverage, level);
- resetCoverage(tiledImage.loadingCoverage, level);
-
- //OK, a new drawing so do your calculations
- var cornerTiles = tiledImage._getCornerTiles(level, topLeftBound, bottomRightBound);
- var topLeftTile = cornerTiles.topLeft;
- var bottomRightTile = cornerTiles.bottomRight;
- var numberOfTiles = tiledImage.source.getNumTiles(level);
-
- var viewportCenter = tiledImage.viewport.pixelFromPoint(
- tiledImage.viewport.getCenter());
-
- if (tiledImage.getFlip()) {
- // The right-most tile can be narrower than the others. When flipped,
- // this tile is now on the left. Because it is narrower than the normal
- // left-most tile, the subsequent tiles may not be wide enough to completely
- // fill the viewport. Fix this by rendering an extra column of tiles. If we
- // are not wrapping, make sure we never render more than the number of tiles
- // in the image.
- bottomRightTile.x += 1;
- if (!tiledImage.wrapHorizontal) {
- bottomRightTile.x = Math.min(bottomRightTile.x, numberOfTiles.x - 1);
- }
- }
-
- for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
- for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
-
- var flippedX;
- if (tiledImage.getFlip()) {
- var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
- flippedX = x + numberOfTiles.x - xMod - xMod - 1;
- } else {
- flippedX = x;
- }
-
- if (drawArea.intersection(tiledImage.getTileBounds(level, flippedX, y)) === null) {
- // This tile is outside of the viewport, no need to draw it
- continue;
- }
-
- best = updateTile(
- tiledImage,
- drawLevel,
- haveDrawn,
- flippedX, y,
- level,
- levelOpacity,
- levelVisibility,
- viewportCenter,
- numberOfTiles,
- currentTime,
- best
- );
-
- }
- }
-
- return best;
-}
-
-/**
- * @private
- * @inner
- * Update a single tile at a particular resolution level.
- * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @param {Boolean} haveDrawn
- * @param {Boolean} drawLevel
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} levelVisibility
- * @param {OpenSeadragon.Point} viewportCenter
- * @param {Number} numberOfTiles
- * @param {Number} currentTime
- * @param {OpenSeadragon.Tile} best - The current "best" tile to draw.
- */
-function updateTile( tiledImage, haveDrawn, drawLevel, x, y, level, levelOpacity, levelVisibility, viewportCenter, numberOfTiles, currentTime, best){
-
- var tile = getTile(
- x, y,
- level,
- tiledImage,
- tiledImage.source,
- tiledImage.tilesMatrix,
- currentTime,
- numberOfTiles,
- tiledImage._worldWidthCurrent,
- tiledImage._worldHeightCurrent
- ),
- drawTile = drawLevel;
-
- if( tiledImage.viewer ){
- /**
- * - Needs documentation -
- *
- * @event update-tile
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} tile
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- tiledImage.viewer.raiseEvent( 'update-tile', {
- tiledImage: tiledImage,
- tile: tile
- });
- }
-
- setCoverage( tiledImage.coverage, level, x, y, false );
-
- var loadingCoverage = tile.loaded || tile.loading || isCovered(tiledImage.loadingCoverage, level, x, y);
- setCoverage(tiledImage.loadingCoverage, level, x, y, loadingCoverage);
-
- if ( !tile.exists ) {
- return best;
- }
-
- if ( haveDrawn && !drawTile ) {
- if ( isCovered( tiledImage.coverage, level, x, y ) ) {
- setCoverage( tiledImage.coverage, level, x, y, true );
- } else {
- drawTile = true;
- }
- }
-
- if ( !drawTile ) {
- return best;
- }
-
- positionTile(
- tile,
- tiledImage.source.tileOverlap,
- tiledImage.viewport,
- viewportCenter,
- levelVisibility,
- tiledImage
- );
-
- if (!tile.loaded) {
- if (tile.context2D) {
- setTileLoaded(tiledImage, tile);
- } else {
- var imageRecord = tiledImage._tileCache.getImageRecord(tile.cacheKey);
- if (imageRecord) {
- var image = imageRecord.getImage();
- setTileLoaded(tiledImage, tile, image);
- }
- }
- }
-
- if ( tile.loaded ) {
- var needsDraw = blendTile(
- tiledImage,
- tile,
- x, y,
- level,
- levelOpacity,
- currentTime
- );
-
- if ( needsDraw ) {
- tiledImage._needsDraw = true;
- }
- } else if ( tile.loading ) {
- // the tile is already in the download queue
- tiledImage._tilesLoading++;
- } else if (!loadingCoverage) {
- best = compareTiles( best, tile );
- }
-
- return best;
-}
-
-/**
- * @private
- * @inner
- * Obtains a tile at the given location.
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.TileSource} tileSource
- * @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.
- * @param {Number} time
- * @param {Number} numTiles
- * @param {Number} worldWidth
- * @param {Number} worldHeight
- * @returns {OpenSeadragon.Tile}
- */
-function getTile(
- x, y,
- level,
- tiledImage,
- tileSource,
- tilesMatrix,
- time,
- numTiles,
- worldWidth,
- worldHeight
-) {
- var xMod,
- yMod,
- bounds,
- sourceBounds,
- exists,
- url,
- post,
- ajaxHeaders,
- context2D,
- tile;
-
- if ( !tilesMatrix[ level ] ) {
- tilesMatrix[ level ] = {};
- }
- if ( !tilesMatrix[ level ][ x ] ) {
- tilesMatrix[ level ][ x ] = {};
- }
-
- if ( !tilesMatrix[ level ][ x ][ y ] || !tilesMatrix[ level ][ x ][ y ].flipped !== !tiledImage.flipped ) {
- xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
- yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
- bounds = tiledImage.getTileBounds( level, x, y );
- sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
- exists = tileSource.tileExists( level, xMod, yMod );
- url = tileSource.getTileUrl( level, xMod, yMod );
- post = tileSource.getTilePostData( level, xMod, yMod );
-
- // Headers are only applicable if loadTilesWithAjax is set
- if (tiledImage.loadTilesWithAjax) {
- ajaxHeaders = tileSource.getTileAjaxHeaders( level, xMod, yMod );
- // Combine tile AJAX headers with tiled image AJAX headers (if applicable)
- if ($.isPlainObject(tiledImage.ajaxHeaders)) {
- ajaxHeaders = $.extend({}, tiledImage.ajaxHeaders, ajaxHeaders);
- }
- } else {
- ajaxHeaders = null;
- }
-
- context2D = tileSource.getContext2D ?
- tileSource.getContext2D(level, xMod, yMod) : undefined;
-
- tile = new $.Tile(
- level,
- x,
- y,
- bounds,
- exists,
- url,
- context2D,
- tiledImage.loadTilesWithAjax,
- ajaxHeaders,
- sourceBounds,
- post
- );
-
- if (tiledImage.getFlip()) {
- if (xMod === 0) {
- tile.isRightMost = true;
- }
- } else {
- if (xMod === numTiles.x - 1) {
- tile.isRightMost = true;
- }
- }
-
- if (yMod === numTiles.y - 1) {
- tile.isBottomMost = true;
- }
-
- tile.flipped = tiledImage.flipped;
-
- tilesMatrix[ level ][ x ][ y ] = tile;
- }
-
- tile = tilesMatrix[ level ][ x ][ y ];
- tile.lastTouchTime = time;
-
- return tile;
-}
-
-/**
- * @private
- * @inner
- * Dispatch a job to the ImageLoader to load the Image for a Tile.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} time
- */
-function loadTile( tiledImage, tile, time ) {
- tile.loading = true;
- tiledImage._imageLoader.addJob({
- src: tile.url,
- postData: tile.postData,
- loadWithAjax: tile.loadWithAjax,
- ajaxHeaders: tile.ajaxHeaders,
- crossOriginPolicy: tiledImage.crossOriginPolicy,
- ajaxWithCredentials: tiledImage.ajaxWithCredentials,
- callback: function( image, errorMsg, tileRequest ){
- onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest );
- },
- abort: function() {
- tile.loading = false;
- }
- });
-}
-
-/**
- * @private
- * @inner
- * Callback fired when a Tile's Image finished downloading.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} time
- * @param {Image} image
- * @param {String} errorMsg
- * @param {XMLHttpRequest} tileRequest
- */
-function onTileLoad( tiledImage, tile, time, image, errorMsg, tileRequest ) {
- if ( !image ) {
- $.console.error( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
- /**
- * Triggered when a tile fails to load.
- *
- * @event tile-load-failed
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Tile} tile - The tile that failed to load.
- * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.
- * @property {number} time - The time in milliseconds when the tile load began.
- * @property {string} message - The error message.
- * @property {XMLHttpRequest} tileRequest - The XMLHttpRequest used to load the tile if available.
- */
- tiledImage.viewer.raiseEvent("tile-load-failed", {
- tile: tile,
- tiledImage: tiledImage,
- time: time,
- message: errorMsg,
- tileRequest: tileRequest
- });
- tile.loading = false;
- tile.exists = false;
- return;
- }
-
- if ( time < tiledImage.lastResetTime ) {
- $.console.warn( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
- tile.loading = false;
- return;
- }
-
- var finish = function() {
- var cutoff = tiledImage.source.getClosestLevel();
- setTileLoaded(tiledImage, tile, image, cutoff, tileRequest);
- };
-
- // Check if we're mid-update; this can happen on IE8 because image load events for
- // cached images happen immediately there
- if ( !tiledImage._midDraw ) {
- finish();
- } else {
- // Wait until after the update, in case caching unloads any tiles
- window.setTimeout( finish, 1);
- }
-}
-
-/**
- * @private
- * @inner
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Image} image
- * @param {Number} cutoff
- */
-function setTileLoaded(tiledImage, tile, image, cutoff, tileRequest) {
- var increment = 0;
-
- function getCompletionCallback() {
- increment++;
- return completionCallback;
- }
-
- function completionCallback() {
- increment--;
- if (increment === 0) {
- tile.loading = false;
- tile.loaded = true;
- if (!tile.context2D) {
- tiledImage._tileCache.cacheTile({
- image: image,
- tile: tile,
- cutoff: cutoff,
- tiledImage: tiledImage
- });
- }
- tiledImage._needsDraw = true;
- }
- }
+ },
/**
- * Triggered when a tile has just been loaded in memory. That means that the
- * image has been downloaded and can be modified before being drawn to the canvas.
- *
- * @event tile-loaded
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {Image} image - The image of the tile.
- * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
- * @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
- * @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
- * @property {function} getCompletionCallback - A function giving a callback to call
- * when the asynchronous processing of the image is done. The image will be
- * marked as entirely loaded when the callback has been called once for each
- * call to getCompletionCallback.
+ * @private
+ * Updates all tiles at a given resolution level.
+ * @param {Boolean} haveDrawn
+ * @param {Boolean} drawLevel
+ * @param {Number} level
+ * @param {Number} levelOpacity
+ * @param {Number} levelVisibility
+ * @param {OpenSeadragon.Rect} drawArea
+ * @param {Number} currentTime
+ * @param {OpenSeadragon.Tile} best - The current "best" tile to draw.
*/
- tiledImage.viewer.raiseEvent("tile-loaded", {
- tile: tile,
- tiledImage: tiledImage,
- tileRequest: tileRequest,
- image: image,
- getCompletionCallback: getCompletionCallback
- });
- // In case the completion callback is never called, we at least force it once.
- getCompletionCallback()();
-}
+ _updateLevel: function(haveDrawn, drawLevel, level, levelOpacity,
+ levelVisibility, drawArea, currentTime, best) {
-/**
- * @private
- * @inner
- * @param {OpenSeadragon.Tile} tile
- * @param {Boolean} overlap
- * @param {OpenSeadragon.Viewport} viewport
- * @param {OpenSeadragon.Point} viewportCenter
- * @param {Number} levelVisibility
- * @param {OpenSeadragon.TiledImage} tiledImage
- */
-function positionTile( tile, overlap, viewport, viewportCenter, levelVisibility, tiledImage ){
- var boundsTL = tile.bounds.getTopLeft();
+ var topLeftBound = drawArea.getBoundingBox().getTopLeft();
+ var bottomRightBound = drawArea.getBoundingBox().getBottomRight();
- boundsTL.x *= tiledImage._scaleSpring.current.value;
- boundsTL.y *= tiledImage._scaleSpring.current.value;
- boundsTL.x += tiledImage._xSpring.current.value;
- boundsTL.y += tiledImage._ySpring.current.value;
+ if (this.viewer) {
+ /**
+ * - Needs documentation -
+ *
+ * @event update-level
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
+ * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
+ * @property {Object} havedrawn
+ * @property {Object} level
+ * @property {Object} opacity
+ * @property {Object} visibility
+ * @property {OpenSeadragon.Rect} drawArea
+ * @property {Object} topleft deprecated, use drawArea instead
+ * @property {Object} bottomright deprecated, use drawArea instead
+ * @property {Object} currenttime
+ * @property {Object} best
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.viewer.raiseEvent('update-level', {
+ tiledImage: this,
+ havedrawn: haveDrawn,
+ level: level,
+ opacity: levelOpacity,
+ visibility: levelVisibility,
+ drawArea: drawArea,
+ topleft: topLeftBound,
+ bottomright: bottomRightBound,
+ currenttime: currentTime,
+ best: best
+ });
+ }
- var boundsSize = tile.bounds.getSize();
+ $.TileSource._resetCoverage(this.coverage, level);
+ $.TileSource._resetCoverage(this.loadingCoverage, level);
- boundsSize.x *= tiledImage._scaleSpring.current.value;
- boundsSize.y *= tiledImage._scaleSpring.current.value;
+ //OK, a new drawing so do your calculations
+ var cornerTiles = this._getCornerTiles(level, topLeftBound, bottomRightBound);
+ var topLeftTile = cornerTiles.topLeft;
+ var bottomRightTile = cornerTiles.bottomRight;
+ var numberOfTiles = this.source.getNumTiles(level);
- var positionC = viewport.pixelFromPointNoRotate(boundsTL, true),
- positionT = viewport.pixelFromPointNoRotate(boundsTL, false),
- sizeC = viewport.deltaPixelsFromPointsNoRotate(boundsSize, true),
- sizeT = viewport.deltaPixelsFromPointsNoRotate(boundsSize, false),
- tileCenter = positionT.plus( sizeT.divide( 2 ) ),
- tileSquaredDistance = viewportCenter.squaredDistanceTo( tileCenter );
+ var viewportCenter = this.viewport.pixelFromPoint(this.viewport.getCenter());
- if ( !overlap ) {
- sizeC = sizeC.plus( new $.Point( 1, 1 ) );
+ if (this.getFlip()) {
+ // The right-most tile can be narrower than the others. When flipped,
+ // this tile is now on the left. Because it is narrower than the normal
+ // left-most tile, the subsequent tiles may not be wide enough to completely
+ // fill the viewport. Fix this by rendering an extra column of tiles. If we
+ // are not wrapping, make sure we never render more than the number of tiles
+ // in the image.
+ bottomRightTile.x += 1;
+ if (!this.wrapHorizontal) {
+ bottomRightTile.x = Math.min(bottomRightTile.x, numberOfTiles.x - 1);
+ }
+ }
+
+ for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
+ for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
+
+ var flippedX;
+ if (this.getFlip()) {
+ var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
+ flippedX = x + numberOfTiles.x - xMod - xMod - 1;
+ } else {
+ flippedX = x;
+ }
+
+ if (drawArea.intersection(this.getTileBounds(level, flippedX, y)) === null) {
+ // This tile is outside of the viewport, no need to draw it
+ continue;
+ }
+
+ best = this._updateTile(
+ drawLevel,
+ haveDrawn,
+ flippedX, y,
+ level,
+ levelOpacity,
+ levelVisibility,
+ viewportCenter,
+ numberOfTiles,
+ currentTime,
+ best
+ );
+ }
+ }
+
+ return best;
+ },
+
+ /**
+ * @private
+ * @inner
+ * Update a single tile at a particular resolution level.
+ * @param {Boolean} haveDrawn
+ * @param {Boolean} drawLevel
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} level
+ * @param {Number} levelOpacity
+ * @param {Number} levelVisibility
+ * @param {OpenSeadragon.Point} viewportCenter
+ * @param {Number} numberOfTiles
+ * @param {Number} currentTime
+ * @param {OpenSeadragon.Tile} best - The current "best" tile to draw.
+ */
+ _updateTile: function( haveDrawn, drawLevel, x, y, level, levelOpacity,
+ levelVisibility, viewportCenter, numberOfTiles, currentTime, best){
+
+ var tile = this._getTile(
+ x, y,
+ level,
+ currentTime,
+ numberOfTiles,
+ this._worldWidthCurrent,
+ this._worldHeightCurrent
+ ),
+ drawTile = drawLevel;
+
+ if( this.viewer ){
+ /**
+ * - Needs documentation -
+ *
+ * @event update-tile
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
+ * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
+ * @property {OpenSeadragon.Tile} tile
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.viewer.raiseEvent( 'update-tile', {
+ tiledImage: this,
+ tile: tile
+ });
+ }
+
+ $.TileSource._setCoverage( this.coverage, level, x, y, false );
+
+ var loadingCoverage = tile.loaded || tile.loading || $.TileSource._isCovered(this.loadingCoverage, level, x, y);
+ $.TileSource._setCoverage(this.loadingCoverage, level, x, y, loadingCoverage);
+
+ if ( !tile.exists ) {
+ return best;
+ }
+
+ if ( haveDrawn && !drawTile ) {
+ if ( $.TileSource._isCovered( this.coverage, level, x, y ) ) {
+ $.TileSource._setCoverage( this.coverage, level, x, y, true );
+ } else {
+ drawTile = true;
+ }
+ }
+
+ if ( !drawTile ) {
+ return best;
+ }
+
+ this._positionTile(
+ tile,
+ this.source.tileOverlap,
+ this.viewport,
+ viewportCenter,
+ levelVisibility
+ );
+
+ if (!tile.loaded) {
+ if (tile.context2D) {
+ this._setTileLoaded(tile);
+ } else {
+ var imageRecord = this._tileCache.getImageRecord(tile.cacheKey);
+ if (imageRecord) {
+ var image = imageRecord.getImage();
+ this._setTileLoaded(tile, image);
+ }
+ }
+ }
+
+ if ( tile.loaded ) {
+ var needsDraw = this._blendTile(
+ tile,
+ x, y,
+ level,
+ levelOpacity,
+ currentTime
+ );
+
+ if ( needsDraw ) {
+ this._needsDraw = true;
+ }
+ } else if ( tile.loading ) {
+ // the tile is already in the download queue
+ this._tilesLoading++;
+ } else if (!loadingCoverage) {
+ best = this._compareTiles( best, tile );
+ }
+
+ return best;
+ },
+
+ /**
+ * @private
+ * @inner
+ * Obtains a tile at the given location.
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} level
+ * @param {Number} time
+ * @param {Number} numTiles
+ * @param {Number} worldWidth
+ * @param {Number} worldHeight
+ * @returns {OpenSeadragon.Tile}
+ */
+ _getTile: function(
+ x, y,
+ level,
+ time,
+ numTiles,
+ worldWidth,
+ worldHeight
+ ) {
+ var xMod,
+ yMod,
+ bounds,
+ sourceBounds,
+ exists,
+ url,
+ post,
+ ajaxHeaders,
+ context2D,
+ tile,
+ tilesMatrix = this.tilesMatrix,
+ tileSource = this.source;
+
+ if ( !tilesMatrix[ level ] ) {
+ tilesMatrix[ level ] = {};
+ }
+ if ( !tilesMatrix[ level ][ x ] ) {
+ tilesMatrix[ level ][ x ] = {};
+ }
+
+ if ( !tilesMatrix[ level ][ x ][ y ] || !tilesMatrix[ level ][ x ][ y ].flipped !== !this.flipped ) {
+ xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
+ yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
+ bounds = this.getTileBounds( level, x, y );
+ sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
+ exists = tileSource.tileExists( level, xMod, yMod );
+ url = tileSource.getTileUrl( level, xMod, yMod );
+ post = tileSource.getTilePostData( level, xMod, yMod );
+
+ // Headers are only applicable if loadTilesWithAjax is set
+ if (this.loadTilesWithAjax) {
+ ajaxHeaders = tileSource.getTileAjaxHeaders( level, xMod, yMod );
+ // Combine tile AJAX headers with tiled image AJAX headers (if applicable)
+ if ($.isPlainObject(this.ajaxHeaders)) {
+ ajaxHeaders = $.extend({}, this.ajaxHeaders, ajaxHeaders);
+ }
+ } else {
+ ajaxHeaders = null;
+ }
+
+ context2D = tileSource.getContext2D ?
+ tileSource.getContext2D(level, xMod, yMod) : undefined;
+
+ tile = new $.Tile(
+ level,
+ x,
+ y,
+ bounds,
+ exists,
+ url,
+ context2D,
+ this.loadTilesWithAjax,
+ ajaxHeaders,
+ sourceBounds,
+ post
+ );
+
+ if (this.getFlip()) {
+ if (xMod === 0) {
+ tile.isRightMost = true;
+ }
+ } else {
+ if (xMod === numTiles.x - 1) {
+ tile.isRightMost = true;
+ }
+ }
+
+ if (yMod === numTiles.y - 1) {
+ tile.isBottomMost = true;
+ }
+
+ tile.flipped = this.flipped;
+
+ tilesMatrix[ level ][ x ][ y ] = tile;
+ }
+
+ tile = tilesMatrix[ level ][ x ][ y ];
+ tile.lastTouchTime = time;
+
+ return tile;
+ },
+
+ /**
+ * @private
+ * @inner
+ * Dispatch a job to the ImageLoader to load the Image for a Tile.
+ * @param {OpenSeadragon.Tile} tile
+ * @param {Number} time
+ */
+ _loadTile: function(tile, time ) {
+ var _this = this;
+ tile.loading = true;
+ this._imageLoader.addJob({
+ src: tile.url,
+ postData: tile.postData,
+ loadWithAjax: tile.loadWithAjax,
+ ajaxHeaders: tile.ajaxHeaders,
+ crossOriginPolicy: this.crossOriginPolicy,
+ ajaxWithCredentials: this.ajaxWithCredentials,
+ callback: function( image, errorMsg, tileRequest ){
+ _this._onTileLoad( tile, time, image, errorMsg, tileRequest );
+ },
+ abort: function() {
+ tile.loading = false;
+ }
+ });
+ },
+
+ /**
+ * @private
+ * @inner
+ * Callback fired when a Tile's Image finished downloading.
+ * @param {OpenSeadragon.Tile} tile
+ * @param {Number} time
+ * @param {Image} image
+ * @param {String} errorMsg
+ * @param {XMLHttpRequest} tileRequest
+ */
+ _onTileLoad: function( tile, time, image, errorMsg, tileRequest ) {
+ if ( !image ) {
+ $.console.error( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg );
+ /**
+ * Triggered when a tile fails to load.
+ *
+ * @event tile-load-failed
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Tile} tile - The tile that failed to load.
+ * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.
+ * @property {number} time - The time in milliseconds when the tile load began.
+ * @property {string} message - The error message.
+ * @property {XMLHttpRequest} tileRequest - The XMLHttpRequest used to load the tile if available.
+ */
+ this.viewer.raiseEvent("tile-load-failed", {
+ tile: tile,
+ tiledImage: this,
+ time: time,
+ message: errorMsg,
+ tileRequest: tileRequest
+ });
+ tile.loading = false;
+ tile.exists = false;
+ return;
+ }
+
+ if ( time < this.lastResetTime ) {
+ $.console.warn( "Ignoring tile %s loaded before reset: %s", tile, tile.url );
+ tile.loading = false;
+ return;
+ }
+
+ var _this = this,
+ finish = function() {
+ var ccc = _this.source;
+ var cutoff = ccc.getClosestLevel();
+ _this._setTileLoaded(tile, image, cutoff, tileRequest);
+ };
+
+ // Check if we're mid-update; this can happen on IE8 because image load events for
+ // cached images happen immediately there
+ if ( !this._midDraw ) {
+ finish();
+ } else {
+ // Wait until after the update, in case caching unloads any tiles
+ window.setTimeout( finish, 1);
+ }
+ },
+
+ /**
+ * @private
+ * @inner
+ * @param {OpenSeadragon.Tile} tile
+ * @param {Image || undefined} image
+ * @param {Number || undefined} cutoff
+ * @param {XMLHttpRequest || undefined} tileRequest
+ */
+ _setTileLoaded: function(tile, image, cutoff, tileRequest) {
+ var increment = 0,
+ _this = this;
+
+ function getCompletionCallback() {
+ increment++;
+ return completionCallback;
+ }
+
+ function completionCallback() {
+ increment--;
+ if (increment === 0) {
+ tile.loading = false;
+ tile.loaded = true;
+ if (!tile.context2D) {
+ _this._tileCache.cacheTile({
+ image: image,
+ tile: tile,
+ cutoff: cutoff,
+ tiledImage: _this
+ });
+ }
+ _this._needsDraw = true;
+ }
+ }
+
+ /**
+ * Triggered when a tile has just been loaded in memory. That means that the
+ * image has been downloaded and can be modified before being drawn to the canvas.
+ *
+ * @event tile-loaded
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {Image} image - The image of the tile.
+ * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
+ * @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
+ * @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
+ * @property {function} getCompletionCallback - A function giving a callback to call
+ * when the asynchronous processing of the image is done. The image will be
+ * marked as entirely loaded when the callback has been called once for each
+ * call to getCompletionCallback.
+ */
+ this.viewer.raiseEvent("tile-loaded", {
+ tile: tile,
+ tiledImage: this,
+ tileRequest: tileRequest,
+ image: image,
+ getCompletionCallback: getCompletionCallback
+ });
+ // In case the completion callback is never called, we at least force it once.
+ getCompletionCallback()();
+ },
+
+ /**
+ * @private
+ * @inner
+ * @param {OpenSeadragon.Tile} tile
+ * @param {Boolean} overlap
+ * @param {OpenSeadragon.Viewport} viewport
+ * @param {OpenSeadragon.Point} viewportCenter
+ * @param {Number} levelVisibility
+ */
+ _positionTile: function( tile, overlap, viewport, viewportCenter, levelVisibility ){
+ var boundsTL = tile.bounds.getTopLeft();
+
+ boundsTL.x *= this._scaleSpring.current.value;
+ boundsTL.y *= this._scaleSpring.current.value;
+ boundsTL.x += this._xSpring.current.value;
+ boundsTL.y += this._ySpring.current.value;
+
+ var boundsSize = tile.bounds.getSize();
+
+ boundsSize.x *= this._scaleSpring.current.value;
+ boundsSize.y *= this._scaleSpring.current.value;
+
+ var positionC = viewport.pixelFromPointNoRotate(boundsTL, true),
+ positionT = viewport.pixelFromPointNoRotate(boundsTL, false),
+ sizeC = viewport.deltaPixelsFromPointsNoRotate(boundsSize, true),
+ sizeT = viewport.deltaPixelsFromPointsNoRotate(boundsSize, false),
+ tileCenter = positionT.plus( sizeT.divide( 2 ) ),
+ tileSquaredDistance = viewportCenter.squaredDistanceTo( tileCenter );
+
+ if ( !overlap ) {
+ sizeC = sizeC.plus( new $.Point( 1, 1 ) );
+ }
+
+ if (tile.isRightMost && this.wrapHorizontal) {
+ sizeC.x += 0.75; // Otherwise Firefox and Safari show seams
+ }
+
+ if (tile.isBottomMost && this.wrapVertical) {
+ sizeC.y += 0.75; // Otherwise Firefox and Safari show seams
+ }
+
+ tile.position = positionC;
+ tile.size = sizeC;
+ tile.squaredDistance = tileSquaredDistance;
+ tile.visibility = levelVisibility;
+ },
+
+ /**
+ * @private
+ * @inner
+ * Updates the opacity of a tile according to the time it has been on screen
+ * to perform a fade-in.
+ * Updates coverage once a tile is fully opaque.
+ * Returns whether the fade-in has completed.
+ *
+ * @param {OpenSeadragon.Tile} tile
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} level
+ * @param {Number} levelOpacity
+ * @param {Number} currentTime
+ * @returns {Boolean}
+ */
+ _blendTile: function( tile, x, y, level, levelOpacity, currentTime ){
+ var blendTimeMillis = 1000 * this.blendTime,
+ deltaTime,
+ opacity;
+
+ if ( !tile.blendStart ) {
+ tile.blendStart = currentTime;
+ }
+
+ deltaTime = currentTime - tile.blendStart;
+ opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
+
+ if ( this.alwaysBlend ) {
+ opacity *= levelOpacity;
+ }
+
+ tile.opacity = opacity;
+
+ this.lastDrawn.push( tile );
+
+ if ( opacity === 1 ) {
+ $.TileSource._setCoverage( this.coverage, level, x, y, true );
+ this._hasOpaqueTile = true;
+ } else if ( deltaTime < blendTimeMillis ) {
+ return true;
+ }
+
+ return false;
+ },
+
+
+ /**
+ * @private
+ * @inner
+ * Determines whether the 'last best' tile for the area is better than the
+ * tile in question.
+ *
+ * @param {OpenSeadragon.Tile} previousBest
+ * @param {OpenSeadragon.Tile} tile
+ * @returns {OpenSeadragon.Tile} The new best tile.
+ */
+ _compareTiles: function( previousBest, tile ) {
+ if ( !previousBest ) {
+ return tile;
+ }
+
+ if ( tile.visibility > previousBest.visibility ) {
+ return tile;
+ } else if ( tile.visibility === previousBest.visibility ) {
+ if ( tile.squaredDistance < previousBest.squaredDistance ) {
+ return tile;
+ }
+ }
+ return previousBest;
+ },
+
+ /**
+ * @private
+ * @inner
+ * Draws a TiledImage.
+ * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
+ */
+ _drawTiles: function( lastDrawn ) {
+ if (this.opacity === 0 || (lastDrawn.length === 0 && !this.placeholderFillStyle)) {
+ return;
+ }
+
+ var tile = lastDrawn[0];
+ var useSketch;
+
+ if (tile) {
+ useSketch = this.opacity < 1 ||
+ (this.compositeOperation && this.compositeOperation !== 'source-over') ||
+ (!this._isBottomItem() && tile._hasTransparencyChannel());
+ }
+
+ var sketchScale;
+ var sketchTranslate;
+
+ var zoom = this.viewport.getZoom(true);
+ var imageZoom = this.viewportToImageZoom(zoom);
+
+ if (lastDrawn.length > 1 &&
+ imageZoom > this.smoothTileEdgesMinZoom &&
+ !this.iOSDevice &&
+ this.getRotation(true) % 360 === 0 && // TODO: support tile edge smoothing with tiled image rotation.
+ $.supportsCanvas && this.viewer.useCanvas) {
+ // When zoomed in a lot (>100%) the tile edges are visible.
+ // So we have to composite them at ~100% and scale them up together.
+ // Note: Disabled on iOS devices per default as it causes a native crash
+ useSketch = true;
+ sketchScale = tile.getScaleForEdgeSmoothing();
+ sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
+ this._drawer.getCanvasSize(false),
+ this._drawer.getCanvasSize(true));
+ }
+
+ var bounds;
+ if (useSketch) {
+ if (!sketchScale) {
+ // Except when edge smoothing, we only clean the part of the
+ // sketch canvas we are going to use for performance reasons.
+ bounds = this.viewport.viewportToViewerElementRectangle(
+ this.getClippedBounds(true))
+ .getIntegerBoundingBox();
+
+ if(this._drawer.viewer.viewport.getFlip()) {
+ if (this.viewport.degrees !== 0 || this.getRotation(true) % 360 !== 0) {
+ bounds.x = this._drawer.viewer.container.clientWidth - (bounds.x + bounds.width);
+ }
+ }
+
+ bounds = bounds.times($.pixelDensityRatio);
+ }
+ this._drawer._clear(true, bounds);
+ }
+
+ // When scaling, we must rotate only when blending the sketch canvas to
+ // avoid interpolation
+ if (!sketchScale) {
+ if (this.viewport.degrees !== 0) {
+ this._drawer._offsetForRotation({
+ degrees: this.viewport.degrees,
+ useSketch: useSketch
+ });
+ }
+ if (this.getRotation(true) % 360 !== 0) {
+ this._drawer._offsetForRotation({
+ degrees: this.getRotation(true),
+ point: this.viewport.pixelFromPointNoRotate(
+ this._getRotationPoint(true), true),
+ useSketch: useSketch
+ });
+ }
+
+ if (this.viewport.degrees === 0 && this.getRotation(true) % 360 === 0){
+ if(this._drawer.viewer.viewport.getFlip()) {
+ this._drawer._flip();
+ }
+ }
+ }
+
+ var usedClip = false;
+ if ( this._clip ) {
+ this._drawer.saveContext(useSketch);
+
+ var box = this.imageToViewportRectangle(this._clip, true);
+ box = box.rotate(-this.getRotation(true), this._getRotationPoint(true));
+ var clipRect = this._drawer.viewportToDrawerRectangle(box);
+ if (sketchScale) {
+ clipRect = clipRect.times(sketchScale);
+ }
+ if (sketchTranslate) {
+ clipRect = clipRect.translate(sketchTranslate);
+ }
+ this._drawer.setClip(clipRect, useSketch);
+
+ usedClip = true;
+ }
+
+ if (this._croppingPolygons) {
+ this._drawer.saveContext(useSketch);
+ try {
+ var polygons = this._croppingPolygons.map(function (polygon) {
+ return polygon.map(function (coord) {
+ var point = this
+ .imageToViewportCoordinates(coord.x, coord.y, true)
+ .rotate(-this.getRotation(true), this._getRotationPoint(true));
+ var clipPoint = this._drawer.viewportCoordToDrawerCoord(point);
+ if (sketchScale) {
+ clipPoint = clipPoint.times(sketchScale);
+ }
+ return clipPoint;
+ });
+ });
+ this._drawer.clipWithPolygons(polygons, useSketch);
+ } catch (e) {
+ $.console.error(e);
+ }
+ usedClip = true;
+ }
+
+ if ( this.placeholderFillStyle && this._hasOpaqueTile === false ) {
+ var placeholderRect = this._drawer.viewportToDrawerRectangle(this.getBounds(true));
+ if (sketchScale) {
+ placeholderRect = placeholderRect.times(sketchScale);
+ }
+ if (sketchTranslate) {
+ placeholderRect = placeholderRect.translate(sketchTranslate);
+ }
+
+ var fillStyle = null;
+ if ( typeof this.placeholderFillStyle === "function" ) {
+ fillStyle = this.placeholderFillStyle(this, this._drawer.context);
+ }
+ else {
+ fillStyle = this.placeholderFillStyle;
+ }
+
+ this._drawer.drawRectangle(placeholderRect, fillStyle, useSketch);
+ }
+
+ var subPixelRoundingRule = determineSubPixelRoundingRule(this.subPixelRoundingForTransparency);
+
+ var shouldRoundPositionAndSize = false;
+
+ if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS) {
+ shouldRoundPositionAndSize = true;
+ } else if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST) {
+ var isAnimating = this.viewer && this.viewer.isAnimating();
+ shouldRoundPositionAndSize = !isAnimating;
+ }
+
+ for (var i = lastDrawn.length - 1; i >= 0; i--) {
+ tile = lastDrawn[ i ];
+ this._drawer.drawTile( tile, this._drawingHandler, useSketch, sketchScale, sketchTranslate, shouldRoundPositionAndSize );
+ tile.beingDrawn = true;
+
+ if( this.viewer ){
+ /**
+ * - Needs documentation -
+ *
+ * @event tile-drawn
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
+ * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
+ * @property {OpenSeadragon.Tile} tile
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.viewer.raiseEvent( 'tile-drawn', {
+ tiledImage: this,
+ tile: tile
+ });
+ }
+ }
+
+ if ( usedClip ) {
+ this._drawer.restoreContext( useSketch );
+ }
+
+ if (!sketchScale) {
+ if (this.getRotation(true) % 360 !== 0) {
+ this._drawer._restoreRotationChanges(useSketch);
+ }
+ if (this.viewport.degrees !== 0) {
+ this._drawer._restoreRotationChanges(useSketch);
+ }
+ }
+
+ if (useSketch) {
+ if (sketchScale) {
+ if (this.viewport.degrees !== 0) {
+ this._drawer._offsetForRotation({
+ degrees: this.viewport.degrees,
+ useSketch: false
+ });
+ }
+ if (this.getRotation(true) % 360 !== 0) {
+ this._drawer._offsetForRotation({
+ degrees: this.getRotation(true),
+ point: this.viewport.pixelFromPointNoRotate(
+ this._getRotationPoint(true), true),
+ useSketch: false
+ });
+ }
+ }
+ this._drawer.blendSketch({
+ opacity: this.opacity,
+ scale: sketchScale,
+ translate: sketchTranslate,
+ compositeOperation: this.compositeOperation,
+ bounds: bounds
+ });
+ if (sketchScale) {
+ if (this.getRotation(true) % 360 !== 0) {
+ this._drawer._restoreRotationChanges(false);
+ }
+ if (this.viewport.degrees !== 0) {
+ this._drawer._restoreRotationChanges(false);
+ }
+ }
+ }
+
+ if (!sketchScale) {
+ if (this.viewport.degrees === 0 && this.getRotation(true) % 360 === 0){
+ if(this._drawer.viewer.viewport.getFlip()) {
+ this._drawer._flip();
+ }
+ }
+ }
+
+ this._drawDebugInfo( lastDrawn );
+ },
+
+ /**
+ * @private
+ * @inner
+ * Draws special debug information for a TiledImage if in debug mode.
+ * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
+ */
+ _drawDebugInfo: function( lastDrawn ) {
+ if( this.debugMode ) {
+ for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
+ var tile = lastDrawn[ i ];
+ try {
+ this._drawer.drawDebugInfo(tile, lastDrawn.length, i, this);
+ } catch(e) {
+ $.console.error(e);
+ }
+ }
+ }
}
-
- if (tile.isRightMost && tiledImage.wrapHorizontal) {
- sizeC.x += 0.75; // Otherwise Firefox and Safari show seams
- }
-
- if (tile.isBottomMost && tiledImage.wrapVertical) {
- sizeC.y += 0.75; // Otherwise Firefox and Safari show seams
- }
-
- tile.position = positionC;
- tile.size = sizeC;
- tile.squaredDistance = tileSquaredDistance;
- tile.visibility = levelVisibility;
-}
-
-/**
- * @private
- * @inner
- * Updates the opacity of a tile according to the time it has been on screen
- * to perform a fade-in.
- * Updates coverage once a tile is fully opaque.
- * Returns whether the fade-in has completed.
- *
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile} tile
- * @param {Number} x
- * @param {Number} y
- * @param {Number} level
- * @param {Number} levelOpacity
- * @param {Number} currentTime
- * @returns {Boolean}
- */
-function blendTile( tiledImage, tile, x, y, level, levelOpacity, currentTime ){
- var blendTimeMillis = 1000 * tiledImage.blendTime,
- deltaTime,
- opacity;
-
- if ( !tile.blendStart ) {
- tile.blendStart = currentTime;
- }
-
- deltaTime = currentTime - tile.blendStart;
- opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
-
- if ( tiledImage.alwaysBlend ) {
- opacity *= levelOpacity;
- }
-
- tile.opacity = opacity;
-
- tiledImage.lastDrawn.push( tile );
-
- if ( opacity === 1 ) {
- setCoverage( tiledImage.coverage, level, x, y, true );
- tiledImage._hasOpaqueTile = true;
- } else if ( deltaTime < blendTimeMillis ) {
- return true;
- }
-
- return false;
-}
+});
/**
* @private
@@ -1831,7 +2100,7 @@ function blendTile( tiledImage, tile, x, y, level, levelOpacity, currentTime ){
* @param {Number} y - The Y position of the tile.
* @returns {Boolean}
*/
-function providesCoverage( coverage, level, x, y ) {
+$.TileSource._providesCoverage = function( coverage, level, x, y ) {
var rows,
cols,
i, j;
@@ -1861,7 +2130,7 @@ function providesCoverage( coverage, level, x, y ) {
coverage[ level ][ x ][ y ] === undefined ||
coverage[ level ][ x ][ y ] === true
);
-}
+};
/**
* @private
@@ -1876,18 +2145,18 @@ function providesCoverage( coverage, level, x, y ) {
* @param {Number} y - The Y position of the tile.
* @returns {Boolean}
*/
-function isCovered( coverage, level, x, y ) {
+$.TileSource._isCovered = function( coverage, level, x, y ) {
if ( x === undefined || y === undefined ) {
- return providesCoverage( coverage, level + 1 );
+ return this._providesCoverage( coverage, level + 1 );
} else {
return (
- providesCoverage( coverage, level + 1, 2 * x, 2 * y ) &&
- providesCoverage( coverage, level + 1, 2 * x, 2 * y + 1 ) &&
- providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y ) &&
- providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y + 1 )
+ this._providesCoverage( coverage, level + 1, 2 * x, 2 * y ) &&
+ this._providesCoverage( coverage, level + 1, 2 * x, 2 * y + 1 ) &&
+ this._providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y ) &&
+ this._providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y + 1 )
);
}
-}
+};
/**
* @private
@@ -1900,7 +2169,7 @@ function isCovered( coverage, level, x, y ) {
* @param {Number} y - The Y position of the tile.
* @param {Boolean} covers - Whether the tile provides coverage.
*/
-function setCoverage( coverage, level, x, y, covers ) {
+$.TileSource._setCoverage = function( coverage, level, x, y, covers ) {
if ( !coverage[ level ] ) {
$.console.warn(
"Setting coverage for a tile before its level's coverage has been reset: %s",
@@ -1914,7 +2183,7 @@ function setCoverage( coverage, level, x, y, covers ) {
}
coverage[ level ][ x ][ y ] = covers;
-}
+};
/**
* @private
@@ -1926,35 +2195,9 @@ function setCoverage( coverage, level, x, y, covers ) {
* @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
* @param {Number} level - The resolution level of tiles to completely reset.
*/
-function resetCoverage( coverage, level ) {
+$.TileSource._resetCoverage = function( coverage, level ) {
coverage[ level ] = {};
-}
-
-/**
- * @private
- * @inner
- * Determines whether the 'last best' tile for the area is better than the
- * tile in question.
- *
- * @param {OpenSeadragon.Tile} previousBest
- * @param {OpenSeadragon.Tile} tile
- * @returns {OpenSeadragon.Tile} The new best tile.
- */
-function compareTiles( previousBest, tile ) {
- if ( !previousBest ) {
- return tile;
- }
-
- if ( tile.visibility > previousBest.visibility ) {
- return tile;
- } else if ( tile.visibility === previousBest.visibility ) {
- if ( tile.squaredDistance < previousBest.squaredDistance ) {
- return tile;
- }
- }
-
- return previousBest;
-}
+};
/**
* @private
@@ -1973,7 +2216,7 @@ var DEFAULT_SUBPIXEL_ROUNDING_RULE = $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER;
* @returns {Boolean} Returns true if the input value is none of the expected
* {@link SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS}, {@link SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST} or {@link SUBPIXEL_ROUNDING_OCCURRENCES.NEVER} value.
*/
- function isSubPixelRoundingRuleUnknown(value) {
+function isSubPixelRoundingRuleUnknown(value) {
return value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS &&
value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST &&
value !== $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER;
@@ -1988,7 +2231,7 @@ var DEFAULT_SUBPIXEL_ROUNDING_RULE = $.SUBPIXEL_ROUNDING_OCCURRENCES.NEVER;
* @param {SUBPIXEL_ROUNDING_OCCURRENCES} value - The subpixel rounding enum value to normalize.
* @returns {SUBPIXEL_ROUNDING_OCCURRENCES} Returns a valid subpixel rounding enum value.
*/
- function normalizeSubPixelRoundingRule(value) {
+function normalizeSubPixelRoundingRule(value) {
if (isSubPixelRoundingRuleUnknown(value)) {
return DEFAULT_SUBPIXEL_ROUNDING_RULE;
}
@@ -2023,266 +2266,4 @@ function determineSubPixelRoundingRule(subPixelRoundingRules) {
return normalizeSubPixelRoundingRule(subPixelRoundingRule);
}
-/**
- * @private
- * @inner
- * Draws a TiledImage.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
- */
-function drawTiles( tiledImage, lastDrawn ) {
- if (tiledImage.opacity === 0 || (lastDrawn.length === 0 && !tiledImage.placeholderFillStyle)) {
- return;
- }
-
- var tile = lastDrawn[0];
- var useSketch;
-
- if (tile) {
- useSketch = tiledImage.opacity < 1 ||
- (tiledImage.compositeOperation &&
- tiledImage.compositeOperation !== 'source-over') ||
- (!tiledImage._isBottomItem() && tile._hasTransparencyChannel());
- }
-
- var sketchScale;
- var sketchTranslate;
-
- var zoom = tiledImage.viewport.getZoom(true);
- var imageZoom = tiledImage.viewportToImageZoom(zoom);
-
- if (lastDrawn.length > 1 &&
- imageZoom > tiledImage.smoothTileEdgesMinZoom &&
- !tiledImage.iOSDevice &&
- tiledImage.getRotation(true) % 360 === 0 && // TODO: support tile edge smoothing with tiled image rotation.
- $.supportsCanvas && tiledImage.viewer.useCanvas) {
- // When zoomed in a lot (>100%) the tile edges are visible.
- // So we have to composite them at ~100% and scale them up together.
- // Note: Disabled on iOS devices per default as it causes a native crash
- useSketch = true;
- sketchScale = tile.getScaleForEdgeSmoothing();
- sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
- tiledImage._drawer.getCanvasSize(false),
- tiledImage._drawer.getCanvasSize(true));
- }
-
- var bounds;
- if (useSketch) {
- if (!sketchScale) {
- // Except when edge smoothing, we only clean the part of the
- // sketch canvas we are going to use for performance reasons.
- bounds = tiledImage.viewport.viewportToViewerElementRectangle(
- tiledImage.getClippedBounds(true))
- .getIntegerBoundingBox();
-
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- if (tiledImage.viewport.degrees !== 0 || tiledImage.getRotation(true) % 360 !== 0){
- bounds.x = tiledImage._drawer.viewer.container.clientWidth - (bounds.x + bounds.width);
- }
- }
-
- bounds = bounds.times($.pixelDensityRatio);
- }
- tiledImage._drawer._clear(true, bounds);
- }
-
- // When scaling, we must rotate only when blending the sketch canvas to
- // avoid interpolation
- if (!sketchScale) {
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.viewport.degrees,
- useSketch: useSketch
- });
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: tiledImage.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true),
- useSketch: useSketch
- });
- }
-
- if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
- }
-
- var usedClip = false;
- if ( tiledImage._clip ) {
- tiledImage._drawer.saveContext(useSketch);
-
- var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true);
- box = box.rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
- var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box);
- if (sketchScale) {
- clipRect = clipRect.times(sketchScale);
- }
- if (sketchTranslate) {
- clipRect = clipRect.translate(sketchTranslate);
- }
- tiledImage._drawer.setClip(clipRect, useSketch);
-
- usedClip = true;
- }
-
- if (tiledImage._croppingPolygons) {
- tiledImage._drawer.saveContext(useSketch);
- try {
- var polygons = tiledImage._croppingPolygons.map(function (polygon) {
- return polygon.map(function (coord) {
- var point = tiledImage
- .imageToViewportCoordinates(coord.x, coord.y, true)
- .rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
- var clipPoint = tiledImage._drawer.viewportCoordToDrawerCoord(point);
- if (sketchScale) {
- clipPoint = clipPoint.times(sketchScale);
- }
- return clipPoint;
- });
- });
- tiledImage._drawer.clipWithPolygons(polygons, useSketch);
- } catch (e) {
- $.console.error(e);
- }
- usedClip = true;
- }
-
- if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
- var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));
- if (sketchScale) {
- placeholderRect = placeholderRect.times(sketchScale);
- }
- if (sketchTranslate) {
- placeholderRect = placeholderRect.translate(sketchTranslate);
- }
-
- var fillStyle = null;
- if ( typeof tiledImage.placeholderFillStyle === "function" ) {
- fillStyle = tiledImage.placeholderFillStyle(tiledImage, tiledImage._drawer.context);
- }
- else {
- fillStyle = tiledImage.placeholderFillStyle;
- }
-
- tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch);
- }
-
- var subPixelRoundingRule = determineSubPixelRoundingRule(tiledImage.subPixelRoundingForTransparency);
-
- var shouldRoundPositionAndSize = false;
-
- if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ALWAYS) {
- shouldRoundPositionAndSize = true;
- } else if (subPixelRoundingRule === $.SUBPIXEL_ROUNDING_OCCURRENCES.ONLY_AT_REST) {
- var isAnimating = tiledImage.viewer && tiledImage.viewer.isAnimating();
- shouldRoundPositionAndSize = !isAnimating;
- }
-
- for (var i = lastDrawn.length - 1; i >= 0; i--) {
- tile = lastDrawn[ i ];
- tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate, shouldRoundPositionAndSize );
- tile.beingDrawn = true;
-
- if( tiledImage.viewer ){
- /**
- * - Needs documentation -
- *
- * @event tile-drawn
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
- * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.
- * @property {OpenSeadragon.Tile} tile
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- tiledImage.viewer.raiseEvent( 'tile-drawn', {
- tiledImage: tiledImage,
- tile: tile
- });
- }
- }
-
- if ( usedClip ) {
- tiledImage._drawer.restoreContext( useSketch );
- }
-
- if (!sketchScale) {
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._restoreRotationChanges(useSketch);
- }
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._restoreRotationChanges(useSketch);
- }
- }
-
- if (useSketch) {
- if (sketchScale) {
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.viewport.degrees,
- useSketch: false
- });
- }
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._offsetForRotation({
- degrees: tiledImage.getRotation(true),
- point: tiledImage.viewport.pixelFromPointNoRotate(
- tiledImage._getRotationPoint(true), true),
- useSketch: false
- });
- }
- }
- tiledImage._drawer.blendSketch({
- opacity: tiledImage.opacity,
- scale: sketchScale,
- translate: sketchTranslate,
- compositeOperation: tiledImage.compositeOperation,
- bounds: bounds
- });
- if (sketchScale) {
- if (tiledImage.getRotation(true) % 360 !== 0) {
- tiledImage._drawer._restoreRotationChanges(false);
- }
- if (tiledImage.viewport.degrees !== 0) {
- tiledImage._drawer._restoreRotationChanges(false);
- }
- }
- }
-
- if (!sketchScale) {
- if (tiledImage.viewport.degrees === 0 && tiledImage.getRotation(true) % 360 === 0){
- if(tiledImage._drawer.viewer.viewport.getFlip()) {
- tiledImage._drawer._flip();
- }
- }
- }
-
- drawDebugInfo( tiledImage, lastDrawn );
-}
-
-/**
- * @private
- * @inner
- * Draws special debug information for a TiledImage if in debug mode.
- * @param {OpenSeadragon.TiledImage} tiledImage
- * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.
- */
-function drawDebugInfo( tiledImage, lastDrawn ) {
- if( tiledImage.debugMode ) {
- for ( var i = lastDrawn.length - 1; i >= 0; i-- ) {
- var tile = lastDrawn[ i ];
- try {
- tiledImage._drawer.drawDebugInfo(
- tile, lastDrawn.length, i, tiledImage);
- } catch(e) {
- $.console.error(e);
- }
- }
- }
-}
-
}( OpenSeadragon ));
diff --git a/test/modules/basic.js b/test/modules/basic.js
index 2eefd509..6cc2ad2b 100644
--- a/test/modules/basic.js
+++ b/test/modules/basic.js
@@ -58,7 +58,7 @@
assert.equal($(".openseadragon-message").length, 1, "Open failures should display a message");
- assert.ok(testLog.log.contains('["AJAX request returned %d: %s",404,"/test/data/not-a-real-file"]'),
+ assert.ok(testLog.error.contains('["AJAX request returned %d: %s",404,"/test/data/not-a-real-file"]'),
"AJAX failures should be logged to the console");
done();
diff --git a/test/modules/strings.js b/test/modules/strings.js
index ec1bf87a..78a324c5 100644
--- a/test/modules/strings.js
+++ b/test/modules/strings.js
@@ -22,11 +22,11 @@
QUnit.test("getInvalidString", function(assert) {
assert.equal(OpenSeadragon.getString("Greeting"), "", "Handled unset string key");
- assert.ok(testLog.log.contains('["Untranslated source string:","Greeting"]'),
+ assert.ok(testLog.error.contains('["Untranslated source string:","Greeting"]'),
'Invalid string keys are logged');
assert.equal(OpenSeadragon.getString("Errors"), "", "Handled requesting parent key");
- assert.ok(testLog.log.contains('["Untranslated source string:","Errors"]'),
+ assert.ok(testLog.error.contains('["Untranslated source string:","Errors"]'),
'Invalid string parent keys are logged');
});