diff --git a/src/imageloader.js b/src/imageloader.js
index a120433a..198882b6 100644
--- a/src/imageloader.js
+++ b/src/imageloader.js
@@ -43,7 +43,8 @@
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
- * @param {String} [options.postData] - HTTP POST data in k=v&k2=v2... form or null
+ * @param {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
+ * see TileSrouce::getPostData) or null
* @param {Function} [options.callback] - Called once image has been downloaded.
* @param {Function} [options.abort] - Called when this image job is aborted.
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
@@ -198,9 +199,10 @@ $.ImageLoader.prototype = {
* @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
- * @param {String} [options.postData] - POST parameters in k=v&k2=v2... form or null
+ * @param {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form,
+ * see TileSrouce::getPostData) or null
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
- * requests.
+ * requests.
* @param {Function} [options.callback] - Called once image has been downloaded.
* @param {Function} [options.abort] - Called when this image job is aborted.
*/
diff --git a/src/mousetracker.js b/src/mousetracker.js
index 8c811326..cf796b6a 100644
--- a/src/mousetracker.js
+++ b/src/mousetracker.js
@@ -3286,13 +3286,12 @@
gPoint = updateGPoint;
} else {
// Initialize for tracking and add to the tracking list (no pointerenter event occurred before this)
- $.console.warn('pointerdown event on untracked pointer');
+ // NOTE: pointerdown event on untracked pointer
gPoint.captured = false; // Handled by updatePointerCaptured()
gPoint.insideElementPressed = true;
gPoint.insideElement = true;
gPoint.originalTarget = eventInfo.originalEvent.target;
startTrackingPointer( pointsList, gPoint );
- return;
}
pointsList.addContact();
@@ -3436,8 +3435,8 @@
releasePoint = updateGPoint.currentPos;
releaseTime = updateGPoint.currentTime;
} else {
- // should never get here...we'll start to track pointer anyway
- $.console.warn('updatePointerUp(): pointerup on untracked gPoint');
+ // NOTE: updatePointerUp(): pointerup on untracked gPoint
+ // ...we'll start to track pointer again
gPoint.captured = false; // Handled by updatePointerCaptured()
gPoint.insideElementPressed = false;
gPoint.insideElement = true;
@@ -3460,7 +3459,7 @@
if ( pointsList.contacts === 0 ) {
// Release (pressed in our element)
- if ( tracker.releaseHandler ) {
+ if ( tracker.releaseHandler && releasePoint ) {
tracker.releaseHandler(
{
eventSource: tracker,
diff --git a/src/openseadragon.js b/src/openseadragon.js
index ef002f37..731a06b4 100644
--- a/src/openseadragon.js
+++ b/src/openseadragon.js
@@ -665,13 +665,18 @@
*
* @property {Boolean} [splitHashDataForPost=false]
* Allows to treat _first_ hash ('#') symbol as a separator for POST data:
- * URL to be opened by a {@link OpenSeadragon.TileSource} can thus look like: http://some.url#postdata=here .
- * The URL is split to 'http://some.url' and 'postdata=here'; post data is given to the
- * {@link OpenSeadragon.TileSource} of the choice and can be further used within tile requests
- * (see TileSource methods). {@link OpenSeadragon.TileSource.prototype.configure} return value
- * should contain the post data so that it is given to its subclass in the constructor.
- * NOTE: post data is expected to be ampersand-separated (just like GET parameters), and is not used
- * to fetch tile image data if loadTilesWithAjax=false (but it is still used for the initial request).
+ * URL to be opened by a {@link OpenSeadragon.TileSource} can thus look like: http://some.url#postdata=here.
+ * The whole URL is used to fetch image info metadata and it is then split to 'http://some.url' and
+ * 'postdata=here'; post data is given to the {@link OpenSeadragon.TileSource} of the choice and can be further
+ * used within tile requests (see TileSource methods).
+ * NOTE: {@link OpenSeadragon.TileSource.prototype.configure} return value should contain the post data
+ * if you want to use it later - so that it is given to your constructor later.
+ * NOTE: usually, post data is expected to be ampersand-separated (just like GET parameters), and is NOT USED
+ * to fetch tile image data unless explicitly programmed, or if loadTilesWithAjax=false 4
+ * (but it is still used for the initial image info request).
+ * NOTE: passing POST data from URL by this feature only supports string values, however,
+ * TileSource can send any data using POST as long as the header is correct
+ * (@see OpenSeadragon.TileSource.prototype.getTilePostData)
*/
/**
@@ -2315,7 +2320,8 @@ function OpenSeadragon( options ){
* @param {Function} options.error - a function to call on when an error occurs
* @param {Object} options.headers - headers to add to the AJAX request
* @param {String} options.responseType - the response type of the the AJAX request
- * @param {String} options.postData - HTTP POST data in k=v&k2=v2... form, GET method used if null
+ * @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
+ * see TileSrouce::getPostData), GET method used if null
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
* @throws {Error}
* @returns {XMLHttpRequest}
diff --git a/src/tile.js b/src/tile.js
index 8f2dfc1a..a6d40fd8 100644
--- a/src/tile.js
+++ b/src/tile.js
@@ -46,13 +46,14 @@
* this tile failed to load? )
* @param {String} url The URL of this tile's image.
* @param {CanvasRenderingContext2D} context2D The context2D of this tile if it
- * is provided directly by the tile source.
+ * is provided directly by the tile source.
* @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .
* @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).
* @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the
- * @param {String} postData HTTP POST data in k=v&k2=v2... form or null
- * drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
- * with HTML the entire tile is always used.
+ * drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
+ * with HTML the entire tile is always used.
+ * @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
+ * see TileSrouce::getPostData) or null
*/
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData) {
/**
@@ -101,7 +102,8 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
/**
* Post parameters for this tile. Either it is an URL-encoded string
* in k1=v1&k2=v2... format or null
- * @member {String} postData HTTP POST data in k=v&k2=v2... form or null
+ * @member {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
+ * see TileSrouce::getPostData) or null
* @memberof OpenSeadragon.Tile#
*/
this.postData = postData;
diff --git a/src/tiledimage.js b/src/tiledimage.js
index 80204328..b955a30b 100644
--- a/src/tiledimage.js
+++ b/src/tiledimage.js
@@ -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 (this._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,728 +1233,972 @@ $.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();
+ this._resetCoverage(this.coverage, level);
+ this._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);
+ }
+ }
- if (tile.isRightMost && tiledImage.wrapHorizontal) {
- sizeC.x += 0.75; // Otherwise Firefox and Safari show seams
- }
+ for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
+ for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
- if (tile.isBottomMost && tiledImage.wrapVertical) {
- sizeC.y += 0.75; // Otherwise Firefox and Safari show seams
- }
+ var flippedX;
+ if (this.getFlip()) {
+ var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
+ flippedX = x + numberOfTiles.x - xMod - xMod - 1;
+ } else {
+ flippedX = x;
+ }
- tile.position = positionC;
- tile.size = sizeC;
- tile.squaredDistance = tileSquaredDistance;
- tile.visibility = levelVisibility;
-}
+ if (drawArea.intersection(this.getTileBounds(level, flippedX, y)) === null) {
+ // This tile is outside of the viewport, no need to draw it
+ continue;
+ }
-/**
- * @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;
+ best = this._updateTile(
+ drawLevel,
+ haveDrawn,
+ flippedX, y,
+ level,
+ levelOpacity,
+ levelVisibility,
+ viewportCenter,
+ numberOfTiles,
+ currentTime,
+ best
+ );
+ }
+ }
- if ( !tile.blendStart ) {
- tile.blendStart = currentTime;
- }
+ return best;
+ },
- deltaTime = currentTime - tile.blendStart;
- opacity = blendTimeMillis ? Math.min( 1, deltaTime / ( blendTimeMillis ) ) : 1;
+ /**
+ * @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){
- if ( tiledImage.alwaysBlend ) {
- opacity *= levelOpacity;
- }
+ var tile = this._getTile(
+ x, y,
+ level,
+ currentTime,
+ numberOfTiles,
+ this._worldWidthCurrent,
+ this._worldHeightCurrent
+ ),
+ drawTile = drawLevel;
- tile.opacity = opacity;
+ 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
+ });
+ }
- tiledImage.lastDrawn.push( tile );
+ this._setCoverage( this.coverage, level, x, y, false );
- if ( opacity === 1 ) {
- setCoverage( tiledImage.coverage, level, x, y, true );
- tiledImage._hasOpaqueTile = true;
- } else if ( deltaTime < blendTimeMillis ) {
- return true;
- }
+ var loadingCoverage = tile.loaded || tile.loading || this._isCovered(this.loadingCoverage, level, x, y);
+ this._setCoverage(this.loadingCoverage, level, x, y, loadingCoverage);
- return false;
-}
+ if ( !tile.exists ) {
+ return best;
+ }
-/**
- * @private
- * @inner
- * Returns true if the given tile provides coverage to lower-level tiles of
- * lower resolution representing the same content. If neither x nor y is
- * given, returns true if the entire visible level provides coverage.
- *
- * Note that out-of-bounds tiles provide coverage in this sense, since
- * there's no content that they would need to cover. Tiles at non-existent
- * levels that are within the image bounds, however, do not.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of the tile.
- * @param {Number} x - The X position of the tile.
- * @param {Number} y - The Y position of the tile.
- * @returns {Boolean}
- */
-function providesCoverage( coverage, level, x, y ) {
- var rows,
- cols,
- i, j;
+ if ( haveDrawn && !drawTile ) {
+ if ( this._isCovered( this.coverage, level, x, y ) ) {
+ this._setCoverage( this.coverage, level, x, y, true );
+ } else {
+ drawTile = true;
+ }
+ }
- if ( !coverage[ level ] ) {
- return false;
- }
+ if ( !drawTile ) {
+ return best;
+ }
- if ( x === undefined || y === undefined ) {
- rows = coverage[ level ];
- for ( i in rows ) {
- if ( Object.prototype.hasOwnProperty.call( rows, i ) ) {
- cols = rows[ i ];
- for ( j in cols ) {
- if ( Object.prototype.hasOwnProperty.call( cols, j ) && !cols[ j ] ) {
- return false;
- }
+ 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);
}
}
}
- return true;
- }
+ if ( tile.loaded ) {
+ var needsDraw = this._blendTile(
+ tile,
+ x, y,
+ level,
+ levelOpacity,
+ currentTime
+ );
- return (
- coverage[ level ][ x] === undefined ||
- coverage[ level ][ x ][ y ] === undefined ||
- coverage[ level ][ x ][ y ] === true
- );
-}
+ 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 );
+ }
-/**
- * @private
- * @inner
- * Returns true if the given tile is completely covered by higher-level
- * tiles of higher resolution representing the same content. If neither x
- * nor y is given, returns true if the entire visible level is covered.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of the tile.
- * @param {Number} x - The X position of the tile.
- * @param {Number} y - The Y position of the tile.
- * @returns {Boolean}
- */
-function isCovered( coverage, level, x, y ) {
- if ( x === undefined || y === undefined ) {
- return 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 )
- );
- }
-}
+ return best;
+ },
-/**
- * @private
- * @inner
- * Sets whether the given tile provides coverage or not.
- *
- * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
- * @param {Number} level - The resolution level of the tile.
- * @param {Number} x - The X position of the tile.
- * @param {Number} y - The Y position of the tile.
- * @param {Boolean} covers - Whether the tile provides coverage.
- */
-function setCoverage( coverage, level, x, y, covers ) {
- if ( !coverage[ level ] ) {
- $.console.warn(
- "Setting coverage for a tile before its level's coverage has been reset: %s",
- level
- );
- return;
- }
+ /**
+ * @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 ( !coverage[ level ][ x ] ) {
- coverage[ level ][ x ] = {};
- }
+ if ( !tilesMatrix[ level ] ) {
+ tilesMatrix[ level ] = {};
+ }
+ if ( !tilesMatrix[ level ][ x ] ) {
+ tilesMatrix[ level ][ x ] = {};
+ }
- coverage[ level ][ x ][ y ] = covers;
-}
+ 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 );
-/**
- * @private
- * @inner
- * Resets coverage information for the given level. This should be called
- * after every draw routine. Note that at the beginning of the next draw
- * routine, coverage for every visible tile should be explicitly set.
- *
- * @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 ) {
- coverage[ level ] = {};
-}
+ // 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;
-/**
- * @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 ) {
+ /**
+ * @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 ) {
+ this._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;
}
- }
- return previousBest;
-}
+ 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);
+ }
+ }
+ }
+ },
+
+ /**
+ * @private
+ * @inner
+ * Returns true if the given tile provides coverage to lower-level tiles of
+ * lower resolution representing the same content. If neither x nor y is
+ * given, returns true if the entire visible level provides coverage.
+ *
+ * Note that out-of-bounds tiles provide coverage in this sense, since
+ * there's no content that they would need to cover. Tiles at non-existent
+ * levels that are within the image bounds, however, do not.
+ *
+ * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
+ * @param {Number} level - The resolution level of the tile.
+ * @param {Number} x - The X position of the tile.
+ * @param {Number} y - The Y position of the tile.
+ * @returns {Boolean}
+ */
+ _providesCoverage: function( coverage, level, x, y ) {
+ var rows,
+ cols,
+ i, j;
+
+ if ( !coverage[ level ] ) {
+ return false;
+ }
+
+ if ( x === undefined || y === undefined ) {
+ rows = coverage[ level ];
+ for ( i in rows ) {
+ if ( Object.prototype.hasOwnProperty.call( rows, i ) ) {
+ cols = rows[ i ];
+ for ( j in cols ) {
+ if ( Object.prototype.hasOwnProperty.call( cols, j ) && !cols[ j ] ) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ return (
+ coverage[ level ][ x] === undefined ||
+ coverage[ level ][ x ][ y ] === undefined ||
+ coverage[ level ][ x ][ y ] === true
+ );
+ },
+
+ /**
+ * @private
+ * @inner
+ * Returns true if the given tile is completely covered by higher-level
+ * tiles of higher resolution representing the same content. If neither x
+ * nor y is given, returns true if the entire visible level is covered.
+ *
+ * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
+ * @param {Number} level - The resolution level of the tile.
+ * @param {Number} x - The X position of the tile.
+ * @param {Number} y - The Y position of the tile.
+ * @returns {Boolean}
+ */
+ _isCovered: function( coverage, level, x, y ) {
+ if ( x === undefined || y === undefined ) {
+ return this._providesCoverage( coverage, level + 1 );
+ } else {
+ return (
+ 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
+ * @inner
+ * Sets whether the given tile provides coverage or not.
+ *
+ * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
+ * @param {Number} level - The resolution level of the tile.
+ * @param {Number} x - The X position of the tile.
+ * @param {Number} y - The Y position of the tile.
+ * @param {Boolean} covers - Whether the tile provides coverage.
+ */
+ _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",
+ level
+ );
+ return;
+ }
+
+ if ( !coverage[ level ][ x ] ) {
+ coverage[ level ][ x ] = {};
+ }
+
+ coverage[ level ][ x ][ y ] = covers;
+ },
+
+ /**
+ * @private
+ * @inner
+ * Resets coverage information for the given level. This should be called
+ * after every draw routine. Note that at the beginning of the next draw
+ * routine, coverage for every visible tile should be explicitly set.
+ *
+ * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.
+ * @param {Number} level - The resolution level of tiles to completely reset.
+ */
+ _resetCoverage: function( coverage, level ) {
+ coverage[ level ] = {};
+ }
+});
+
/**
* @private
@@ -1973,7 +2217,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 +2232,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 +2267,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/src/tilesource.js b/src/tilesource.js
index c1956ac5..48cfe851 100644
--- a/src/tilesource.js
+++ b/src/tilesource.js
@@ -550,7 +550,8 @@ $.TileSource.prototype = {
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {String} message
* @property {String} source
- * @property {String} postData - HTTP POST data in k=v&k2=v2... form or null
+ * @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
+ * see TileSrouce::getPostData) or null
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', {
@@ -623,13 +624,26 @@ $.TileSource.prototype = {
/**
* Must use AJAX in order to work, i.e. loadTilesWithAjax = true is set.
- * It should return url-encoded string with the following structure:
- * key=value&key2=value2...
- * or null in case GET is used instead.
+ * If a value is returned, ajax issues POST request to the tile url.
+ * If null is returned, ajax issues GET request.
+ * The return value must comply to the header 'content type'.
+ *
+ * Examples (USED HEADER --> getTilePostData CODE):
+ * 'Content-type': 'application/x-www-form-urlencoded' -->
+ * return "key1=value=1&key2=value2";
+ *
+ * 'Content-type': 'application/x-www-form-urlencoded' -->
+ * return JSON.stringify({key: "value", number: 5});
+ *
+ * 'Content-type': 'multipart/form-data' -->
+ * let result = new FormData();
+ * result.append("data", myData);
+ * return result;
+
* @param level
* @param x
* @param y
- * @return {string || null} post data to send with tile configuration request
+ * @return {* || null} post data to send with tile configuration request
*/
getTilePostData: function( level, x, y ) {
return null;
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/events.js b/test/modules/events.js
index 7a2b00ca..6900b063 100644
--- a/test/modules/events.js
+++ b/test/modules/events.js
@@ -425,7 +425,7 @@
clickCount: 2,
dblClickCount: 1,
dragCount: 0,
- dragEndCount: 2, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
+ dragEndCount: 0, // drag-end event no longer fired if pointer didn't move (#2064)
insideElementPressed: true,
insideElementReleased: true,
contacts: 0,
@@ -453,7 +453,7 @@
clickCount: 1,
dblClickCount: 0,
dragCount: 0,
- dragEndCount: 1, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
+ dragEndCount: 0, // drag-end event no longer fired if pointer didn't move (#2064)
insideElementPressed: true,
insideElementReleased: true,
contacts: 0,
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');
});