From fc56c4e2efc6a0130b36f96e7ef327cf10184a57 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 8 Apr 2015 20:13:56 +0200 Subject: [PATCH 001/104] Add method to draw a placeholder --- src/drawer.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/drawer.js b/src/drawer.js index 1f18f102..b8d87aba 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -287,6 +287,16 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.context.clip(); }, + // private + drawPlaceholder: function(rect) { + if (!this.useCanvas) { + return; + } + + this.context.fillRect(rect.x, rect.y, rect.width, rect.height); + this.context.fillStyle = "#000000"; + }, + // private drawDebugInfo: function( tile, count, i ){ if ( this.useCanvas ) { From 67785336423d05524cd7cce32e3f870ce912d94e Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 8 Apr 2015 20:14:13 +0200 Subject: [PATCH 002/104] Call drawPlaceholder when no tiles are drawn --- src/tiledimage.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 405989b7..48ed29b1 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1157,7 +1157,7 @@ function drawTiles( tiledImage, lastDrawn ){ position, tileSource; - var usedClip = false; + var needsRestore = false; if (tiledImage._clip) { tiledImage._drawer.saveContext(); var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); @@ -1168,7 +1168,20 @@ function drawTiles( tiledImage, lastDrawn ){ size.x * $.pixelDensityRatio, size.y * $.pixelDensityRatio); tiledImage._drawer.setClip(box); - usedClip = true; + needsRestore = true; + } + + if ( lastDrawn.length === 0 ) { + tiledImage._drawer.saveContext(); + var box = tiledImage.getBounds(true); + var topLeft = tiledImage.viewport.pixelFromPoint(box.getTopLeft(), true); + var size = tiledImage.viewport.deltaPixelsFromPoints(box.getSize(), true); + box = new OpenSeadragon.Rect(topLeft.x * $.pixelDensityRatio, + topLeft.y * $.pixelDensityRatio, + size.x * $.pixelDensityRatio, + size.y * $.pixelDensityRatio); + tiledImage._drawer.drawPlaceholder(box); + needsRestore = true; } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { @@ -1203,7 +1216,7 @@ function drawTiles( tiledImage, lastDrawn ){ } } - if (usedClip) { + if ( needsRestore ) { tiledImage._drawer.restoreContext(); } } From 50e46b104e0e4c4f5ab06030fdd44fa9311829f7 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 8 Apr 2015 21:45:38 +0200 Subject: [PATCH 003/104] Extract rect calculation --- src/tiledimage.js | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 48ed29b1..464f2477 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1155,32 +1155,36 @@ function drawTiles( tiledImage, lastDrawn ){ viewer, viewport, position, - tileSource; + tileSource, + needsRestore = false; - var needsRestore = false; - if (tiledImage._clip) { - tiledImage._drawer.saveContext(); - var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); + // TODO: Move this function + var boxToDrawerRectangle = function( box ) { var topLeft = tiledImage.viewport.pixelFromPoint(box.getTopLeft(), true); var size = tiledImage.viewport.deltaPixelsFromPoints(box.getSize(), true); - box = new OpenSeadragon.Rect(topLeft.x * $.pixelDensityRatio, + + return new $.Rect( + topLeft.x * $.pixelDensityRatio, topLeft.y * $.pixelDensityRatio, - size.x * $.pixelDensityRatio, - size.y * $.pixelDensityRatio); - tiledImage._drawer.setClip(box); + size.x * $.pixelDensityRatio, + size.y * $.pixelDensityRatio + ); + }; + + if ( tiledImage._clip ) { + tiledImage._drawer.saveContext(); + var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); + var clipRect = boxToDrawerRectangle(box); + + tiledImage._drawer.setClip(clipRect); needsRestore = true; } if ( lastDrawn.length === 0 ) { tiledImage._drawer.saveContext(); - var box = tiledImage.getBounds(true); - var topLeft = tiledImage.viewport.pixelFromPoint(box.getTopLeft(), true); - var size = tiledImage.viewport.deltaPixelsFromPoints(box.getSize(), true); - box = new OpenSeadragon.Rect(topLeft.x * $.pixelDensityRatio, - topLeft.y * $.pixelDensityRatio, - size.x * $.pixelDensityRatio, - size.y * $.pixelDensityRatio); - tiledImage._drawer.drawPlaceholder(box); + var placeholderRect = boxToDrawerRectangle( tiledImage.getBounds(true) ); + + tiledImage._drawer.drawPlaceholder(placeholderRect); needsRestore = true; } @@ -1189,10 +1193,10 @@ function drawTiles( tiledImage, lastDrawn ){ tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler ); tile.beingDrawn = true; - if( tiledImage.debugMode ){ - try{ + if( tiledImage.debugMode ) { + try { tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i ); - }catch(e){ + } catch(e) { $.console.error(e); } } From 4154d96d5cc27cf86c26cbb19a5f62d2fc0e61c9 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 8 Apr 2015 21:50:49 +0200 Subject: [PATCH 004/104] Add editorconfig --- .editorconfig | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8f960391 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true From 4523454ff1f2d41d3b39fdfc5d091bf7d95cb3f4 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Thu, 9 Apr 2015 13:44:55 +0200 Subject: [PATCH 005/104] Add option to define the fillStyle of the placeholder --- src/drawer.js | 4 ++-- src/openseadragon.js | 6 +++++- src/tiledimage.js | 34 ++++++++++++++++++---------------- src/viewer.js | 3 ++- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index b8d87aba..3ebb54ca 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -288,13 +288,13 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ }, // private - drawPlaceholder: function(rect) { + drawPlaceholder: function(rect, fillStyle) { if (!this.useCanvas) { return; } + this.context.fillStyle = fillStyle || "#FFFFFF"; this.context.fillRect(rect.x, rect.y, rect.width, rect.height); - this.context.fillStyle = "#000000"; }, // private diff --git a/src/openseadragon.js b/src/openseadragon.js index 8d231cbe..e0ff9c9b 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -206,6 +206,9 @@ * @property {Number} [opacity=1] * Opacity of the drawer (1=opaque, 0=transparent) * + * @property {String|Object} [placeholderFillStyle=null] + * Draws a colored rectangle behind the tile if it is not loaded yet. + * * @property {Number} [degrees=0] * Initial rotation. * @@ -262,7 +265,7 @@ * Possible subproperties (Numbers, in screen coordinates): left, top, right, bottom. * * @property {Number} [imageLoaderLimit=0] - * The maximum number of image requests to make concurrently. By default + * The maximum number of image requests to make concurrently. By default * it is set to 0 allowing the browser to make the maximum number of * image requests in parallel as allowed by the browsers policy. * @@ -1017,6 +1020,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ // APPEARANCE opacity: 1, + placeholderFillStyle: null, //REFERENCE STRIP SETTINGS showReferenceStrip: false, diff --git a/src/tiledimage.js b/src/tiledimage.js index 464f2477..14f342f0 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -65,6 +65,7 @@ * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}. * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}. + * @param {String|Object} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}. * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}. */ $.TiledImage = function( options ) { @@ -126,21 +127,22 @@ $.TiledImage = function( options ) { coverage: {}, // A '3d' dictionary [level][x][y] --> Boolean. lastDrawn: [], // An unordered list of Tiles drawn last frame. lastResetTime: 0, // Last time for which the tiledImage was reset. - _midDraw: false, // Is the tiledImage currently updating the viewport? - _needsDraw: true, // Does the tiledImage need to update the viewport again? + _midDraw: false, // Is the tiledImage currently updating the viewport? + _needsDraw: true, // Does the tiledImage need to update the viewport again? //configurable settings - springStiffness: $.DEFAULT_SETTINGS.springStiffness, - animationTime: $.DEFAULT_SETTINGS.animationTime, - minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, - wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal, - wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, - immediateRender: $.DEFAULT_SETTINGS.immediateRender, - blendTime: $.DEFAULT_SETTINGS.blendTime, - alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend, - minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, - debugMode: $.DEFAULT_SETTINGS.debugMode, - crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy + springStiffness: $.DEFAULT_SETTINGS.springStiffness, + animationTime: $.DEFAULT_SETTINGS.animationTime, + minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, + wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal, + wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, + immediateRender: $.DEFAULT_SETTINGS.immediateRender, + blendTime: $.DEFAULT_SETTINGS.blendTime, + alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend, + minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, + debugMode: $.DEFAULT_SETTINGS.debugMode, + crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy, + placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle }, options ); @@ -1148,7 +1150,7 @@ function compareTiles( previousBest, tile ) { return previousBest; } -function drawTiles( tiledImage, lastDrawn ){ +function drawTiles( tiledImage, lastDrawn ) { var i, tile, tileKey, @@ -1180,11 +1182,11 @@ function drawTiles( tiledImage, lastDrawn ){ needsRestore = true; } - if ( lastDrawn.length === 0 ) { + if ( tiledImage.placeholderFillStyle && lastDrawn.length === 0 ) { tiledImage._drawer.saveContext(); var placeholderRect = boxToDrawerRectangle( tiledImage.getBounds(true) ); - tiledImage._drawer.drawPlaceholder(placeholderRect); + tiledImage._drawer.drawPlaceholder(placeholderRect, tiledImage.placeholderFillStyle); needsRestore = true; } diff --git a/src/viewer.js b/src/viewer.js index 7b269350..562b50b2 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1294,7 +1294,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, alwaysBlend: _this.alwaysBlend, minPixelRatio: _this.minPixelRatio, crossOriginPolicy: _this.crossOriginPolicy, - debugMode: _this.debugMode + debugMode: _this.debugMode, + placeholderFillStyle: _this.placeholderFillStyle }); _this.world.addItem( tiledImage, { From da819ac15acfbb45e3c2d9b172935a941fa97bbd Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Thu, 9 Apr 2015 14:25:07 +0200 Subject: [PATCH 006/104] Add possibility to draw gradients and images --- src/drawer.js | 8 +++++++- src/openseadragon.js | 2 +- src/tiledimage.js | 2 +- test/demo/fitboundswithconstraints.html | 8 ++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 3ebb54ca..6d989efd 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -293,7 +293,13 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } - this.context.fillStyle = fillStyle || "#FFFFFF"; + if ( typeof fillStyle === "function" ) { + this.context.fillStyle = fillStyle(this.context); + } + else { + this.context.fillStyle = fillStyle; + } + this.context.fillRect(rect.x, rect.y, rect.width, rect.height); }, diff --git a/src/openseadragon.js b/src/openseadragon.js index e0ff9c9b..ec5b0ed5 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -206,7 +206,7 @@ * @property {Number} [opacity=1] * Opacity of the drawer (1=opaque, 0=transparent) * - * @property {String|Object} [placeholderFillStyle=null] + * @property {String|Function} [placeholderFillStyle=null] * Draws a colored rectangle behind the tile if it is not loaded yet. * * @property {Number} [degrees=0] diff --git a/src/tiledimage.js b/src/tiledimage.js index 14f342f0..c13b4568 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -65,7 +65,7 @@ * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}. * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}. - * @param {String|Object} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}. + * @param {String|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}. * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}. */ $.TiledImage = function( options ) { diff --git a/test/demo/fitboundswithconstraints.html b/test/demo/fitboundswithconstraints.html index 51889d88..7e9ba1bd 100644 --- a/test/demo/fitboundswithconstraints.html +++ b/test/demo/fitboundswithconstraints.html @@ -67,6 +67,14 @@ Height: 3448 } } + }, + placeholderFillStyle: "#FF0000", + placeholderFillStyle: function(context) { + var gradient = context.createLinearGradient(0,0,1000,0); + gradient.addColorStop(0,"black"); + gradient.addColorStop(1,"red"); + + return gradient; } }); _viewer.addHandler("open", function() { From 36597d50727d6bd76456cf32f630a9306c6e34fd Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Thu, 9 Apr 2015 14:27:19 +0200 Subject: [PATCH 007/104] Revert my test --- test/demo/fitboundswithconstraints.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/demo/fitboundswithconstraints.html b/test/demo/fitboundswithconstraints.html index 7e9ba1bd..51889d88 100644 --- a/test/demo/fitboundswithconstraints.html +++ b/test/demo/fitboundswithconstraints.html @@ -67,14 +67,6 @@ Height: 3448 } } - }, - placeholderFillStyle: "#FF0000", - placeholderFillStyle: function(context) { - var gradient = context.createLinearGradient(0,0,1000,0); - gradient.addColorStop(0,"black"); - gradient.addColorStop(1,"red"); - - return gradient; } }); _viewer.addHandler("open", function() { From 1a230d8b9f4d532bd96de5d0df4c421aed53ad88 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Thu, 9 Apr 2015 19:57:28 +0200 Subject: [PATCH 008/104] Add viewportToDrawerRectangle to Drawer --- src/drawer.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/drawer.js b/src/drawer.js index 6d989efd..32c0c3d5 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -232,6 +232,22 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ } }, + /** + * Translates from OpenSeadragon viewer rectangle to drawer rectangle. + * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system. + */ + viewportToDrawerRectangle: function(rectangle) { + var topLeft = this.viewport.pixelFromPoint(rectangle.getTopLeft(), true); + var size = this.viewport.deltaPixelsFromPoints(rectangle.getSize(), true); + + return new $.Rect( + topLeft.x * $.pixelDensityRatio, + topLeft.y * $.pixelDensityRatio, + size.x * $.pixelDensityRatio, + size.y * $.pixelDensityRatio + ); + }, + /** * Draws the given tile. * @param {OpenSeadragon.Tile} tile - The tile to draw. From 5958c50d627592c61a6d19e0de90571a69269dbf Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Thu, 9 Apr 2015 19:57:55 +0200 Subject: [PATCH 009/104] Update tiledImage to use the new function of the drawer --- src/tiledimage.js | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index c13b4568..d008f6e4 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1158,36 +1158,25 @@ function drawTiles( tiledImage, lastDrawn ) { viewport, position, tileSource, - needsRestore = false; - - // TODO: Move this function - var boxToDrawerRectangle = function( box ) { - var topLeft = tiledImage.viewport.pixelFromPoint(box.getTopLeft(), true); - var size = tiledImage.viewport.deltaPixelsFromPoints(box.getSize(), true); - - return new $.Rect( - topLeft.x * $.pixelDensityRatio, - topLeft.y * $.pixelDensityRatio, - size.x * $.pixelDensityRatio, - size.y * $.pixelDensityRatio - ); - }; + contextSaved = false; if ( tiledImage._clip ) { tiledImage._drawer.saveContext(); - var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); - var clipRect = boxToDrawerRectangle(box); + contextSaved = true; + var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); + var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box); tiledImage._drawer.setClip(clipRect); - needsRestore = true; } if ( tiledImage.placeholderFillStyle && lastDrawn.length === 0 ) { - tiledImage._drawer.saveContext(); - var placeholderRect = boxToDrawerRectangle( tiledImage.getBounds(true) ); + if ( !contextSaved ) { + tiledImage._drawer.saveContext(); + contextSaved = true; + } + var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true)); tiledImage._drawer.drawPlaceholder(placeholderRect, tiledImage.placeholderFillStyle); - needsRestore = true; } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { @@ -1222,7 +1211,7 @@ function drawTiles( tiledImage, lastDrawn ) { } } - if ( needsRestore ) { + if ( contextSaved ) { tiledImage._drawer.restoreContext(); } } From f8503bd6ac3cc115ab3aad58451c766bce196ed7 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Fri, 10 Apr 2015 14:31:08 +0200 Subject: [PATCH 010/104] Add placeholderFillStyle option to tiledImage --- src/openseadragon.js | 2 +- src/tiledimage.js | 2 +- src/viewer.js | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index ec5b0ed5..b46a641e 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -206,7 +206,7 @@ * @property {Number} [opacity=1] * Opacity of the drawer (1=opaque, 0=transparent) * - * @property {String|Function} [placeholderFillStyle=null] + * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null] * Draws a colored rectangle behind the tile if it is not loaded yet. * * @property {Number} [degrees=0] diff --git a/src/tiledimage.js b/src/tiledimage.js index d008f6e4..da40bde7 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -65,7 +65,7 @@ * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}. * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}. - * @param {String|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}. + * @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}. * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}. */ $.TiledImage = function( options ) { diff --git a/src/viewer.js b/src/viewer.js index 562b50b2..a73ef24b 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1206,6 +1206,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * and "source" properties. * @param {Boolean} [options.collectionImmediately=false] If collectionMode is on, * specifies whether to snap to the new arrangement immediately or to animate to it. + * @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}. * @fires OpenSeadragon.World.event:add-item * @fires OpenSeadragon.Viewer.event:add-item-failed */ @@ -1217,6 +1218,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, this._hideMessage(); + $.extend ( true, options, { + placeholderFillStyle: _this.placeholderFillStyle + }); + var myQueueItem = { options: options }; @@ -1284,6 +1289,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, width: queueItem.options.width, height: queueItem.options.height, clip: queueItem.options.clip, + placeholderFillStyle: queueItem.options.placeholderFillStyle, springStiffness: _this.springStiffness, animationTime: _this.animationTime, minZoomImageRatio: _this.minZoomImageRatio, @@ -1294,8 +1300,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, alwaysBlend: _this.alwaysBlend, minPixelRatio: _this.minPixelRatio, crossOriginPolicy: _this.crossOriginPolicy, - debugMode: _this.debugMode, - placeholderFillStyle: _this.placeholderFillStyle + debugMode: _this.debugMode }); _this.world.addItem( tiledImage, { From ff6e604b943eed40e3523b8cb5e2b0e1ecc3bb0c Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Fri, 10 Apr 2015 16:20:54 +0200 Subject: [PATCH 011/104] Add comment --- src/drawer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drawer.js b/src/drawer.js index 32c0c3d5..c9c94214 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -235,6 +235,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ /** * Translates from OpenSeadragon viewer rectangle to drawer rectangle. * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system. + * @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system. */ viewportToDrawerRectangle: function(rectangle) { var topLeft = this.viewport.pixelFromPoint(rectangle.getTopLeft(), true); From e3ae7b56f15e892085aa5d550d8e99f2172e76e9 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Sat, 11 Apr 2015 19:01:09 +0200 Subject: [PATCH 012/104] :docs: Document placeholderFillStyle --- src/openseadragon.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index b46a641e..a73295a8 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -208,6 +208,8 @@ * * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null] * Draws a colored rectangle behind the tile if it is not loaded yet. + * You can pass a CSS color value like "#FF8800". + * When passing a function the canvas context is available as argument which is useful when you draw a gradient or pattern. * * @property {Number} [degrees=0] * Initial rotation. @@ -1016,10 +1018,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ navigatorRotate: true, // INITIAL ROTATION - degrees: 0, + degrees: 0, // APPEARANCE - opacity: 1, + opacity: 1, placeholderFillStyle: null, //REFERENCE STRIP SETTINGS From 1f130f1c54069fc818ca03c4b1df92b4fa88c646 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 13 Apr 2015 09:18:12 -0700 Subject: [PATCH 013/104] Changelog for #637 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 5ff344fb..e6d4002b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -57,6 +57,7 @@ OPENSEADRAGON CHANGELOG * Fixed: Cross Origin policy not working (#613) * Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616) * Now animating zoom spring exponentially (#631) +* Added http://editorconfig.org/ config file (#637) 1.2.1: From 425acc38f9155b1bf4d69e9c47e247277492a6f4 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Mon, 13 Apr 2015 18:59:13 +0200 Subject: [PATCH 014/104] Fix overwriting the tiledImage options --- src/viewer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index a73ef24b..57ad0f1f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1218,9 +1218,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, this._hideMessage(); - $.extend ( true, options, { - placeholderFillStyle: _this.placeholderFillStyle - }); + if (options.placeholderFillStyle === undefined) { + options.placeholderFillStyle = this.placeholderFillStyle; + } var myQueueItem = { options: options From 3ce7024e97797fc092bfbdf617ff80f380a86e37 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Mon, 13 Apr 2015 19:02:04 +0200 Subject: [PATCH 015/104] Revert to usedClip --- src/tiledimage.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index da40bde7..cea8d2b1 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1160,23 +1160,24 @@ function drawTiles( tiledImage, lastDrawn ) { tileSource, contextSaved = false; + var usedClip = false; if ( tiledImage._clip ) { tiledImage._drawer.saveContext(); - contextSaved = true; var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box); tiledImage._drawer.setClip(clipRect); + + usedClip = true; } if ( tiledImage.placeholderFillStyle && lastDrawn.length === 0 ) { - if ( !contextSaved ) { - tiledImage._drawer.saveContext(); - contextSaved = true; - } + tiledImage._drawer.saveContext(); var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true)); tiledImage._drawer.drawPlaceholder(placeholderRect, tiledImage.placeholderFillStyle); + + tiledImage._drawer.restoreContext(); } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { @@ -1211,7 +1212,7 @@ function drawTiles( tiledImage, lastDrawn ) { } } - if ( contextSaved ) { + if ( usedClip ) { tiledImage._drawer.restoreContext(); } } From 6677953d87423144b0d8679bc6ce359f3bc242ca Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Mon, 13 Apr 2015 19:05:23 +0200 Subject: [PATCH 016/104] Save and restore inside of the drawer --- src/drawer.js | 4 +++- src/tiledimage.js | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index c9c94214..bfbc38c4 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -310,14 +310,16 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } + + this.saveContext(); if ( typeof fillStyle === "function" ) { this.context.fillStyle = fillStyle(this.context); } else { this.context.fillStyle = fillStyle; } - this.context.fillRect(rect.x, rect.y, rect.width, rect.height); + this.restoreContext(); }, // private diff --git a/src/tiledimage.js b/src/tiledimage.js index cea8d2b1..039a4f7f 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1172,12 +1172,8 @@ function drawTiles( tiledImage, lastDrawn ) { } if ( tiledImage.placeholderFillStyle && lastDrawn.length === 0 ) { - tiledImage._drawer.saveContext(); - var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true)); tiledImage._drawer.drawPlaceholder(placeholderRect, tiledImage.placeholderFillStyle); - - tiledImage._drawer.restoreContext(); } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { From ec96b58169623f4318df19e35706c683a2c39b05 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Mon, 13 Apr 2015 20:08:22 +0200 Subject: [PATCH 017/104] Update to match default jshintrc style --- .jshintrc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.jshintrc b/.jshintrc index 2c3f7d32..e40e7d81 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,14 +1,14 @@ { - "browser": true, - "curly": true, - "eqeqeq": false, - "loopfunc": false, - "noarg": true, - "trailing": true, - "undef": true, - "unused": false, + "browser": true, + "curly": true, + "eqeqeq": false, + "loopfunc": false, + "noarg": true, + "trailing": true, + "undef": true, + "unused": false, - "globals": { - "OpenSeadragon": true - } + "globals": { + "OpenSeadragon": true + } } From d14ba88b70b6023f4f8b7749370b64536fba48ea Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Mon, 13 Apr 2015 20:08:31 +0200 Subject: [PATCH 018/104] Add better config --- .editorconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index 8f960391..6c76c202 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,3 +8,7 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[{package.json,.travis.yml,.jshintrc}] +indent_style = space +indent_size = 2 From cf0a43d797f57b900be3558b6acaab5445716faf Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Mon, 13 Apr 2015 20:08:40 +0200 Subject: [PATCH 019/104] :memo: Update README --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75fc5c92..2a18547b 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,8 @@ The report shows up at `coverage/html/index.html` viewable in a browser. OpenSeadragon is truly a community project; we welcome your involvement! -When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For more thoughts on code style, see https://github.com/rwldrn/idiomatic.js/. +When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](http://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see ® +[idiomatic.js](https://github.com/rwldrn/idiomatic.js/). When fixing bugs and adding features, when appropriate please also: @@ -86,6 +87,6 @@ If you're new to open source in general, check out [GitHub's open source intro g ## License -OpenSeadragon is released under the New BSD license. For details, see the file LICENSE.txt. +OpenSeadragon is released under the New BSD license. For details, see the file LICENSE.txt. [![Build Status](https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master)](http://travis-ci.org/openseadragon/openseadragon) From 6b1580824ae4808f2966ad1aa07a12263fa9d078 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Tue, 14 Apr 2015 19:43:41 +0200 Subject: [PATCH 020/104] Remove contextSaved --- src/tiledimage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 039a4f7f..94c87cb1 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1157,8 +1157,7 @@ function drawTiles( tiledImage, lastDrawn ) { viewer, viewport, position, - tileSource, - contextSaved = false; + tileSource; var usedClip = false; if ( tiledImage._clip ) { From fc827e9e8c03eb0a940a58f50419a16f276adc06 Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Tue, 14 Apr 2015 20:05:55 +0200 Subject: [PATCH 021/104] Fix idiomatic.js line --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2a18547b..efb8b4be 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,7 @@ The report shows up at `coverage/html/index.html` viewable in a browser. OpenSeadragon is truly a community project; we welcome your involvement! -When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](http://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see ® -[idiomatic.js](https://github.com/rwldrn/idiomatic.js/). +When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For easier setup you can also install [EditorConfig](http://editorconfig.org/) if your IDE is supported. For more thoughts on code style, see [idiomatic.js](https://github.com/rwldrn/idiomatic.js/). When fixing bugs and adding features, when appropriate please also: From 21d32b59f5f6e57cc6d5620f10c189bec334a5aa Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 15 Apr 2015 13:32:41 +0200 Subject: [PATCH 022/104] Pass tiledImage and context to the placeholderFillStyle function --- src/drawer.js | 12 +++--------- src/openseadragon.js | 2 +- src/tiledimage.js | 11 ++++++++++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index bfbc38c4..a38aed0a 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -310,16 +310,10 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } - - this.saveContext(); - if ( typeof fillStyle === "function" ) { - this.context.fillStyle = fillStyle(this.context); - } - else { - this.context.fillStyle = fillStyle; - } + this.context.save(); + this.context.fillStyle = fillStyle; this.context.fillRect(rect.x, rect.y, rect.width, rect.height); - this.restoreContext(); + this.context.restore(); }, // private diff --git a/src/openseadragon.js b/src/openseadragon.js index a73295a8..3e3cffbd 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -209,7 +209,7 @@ * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null] * Draws a colored rectangle behind the tile if it is not loaded yet. * You can pass a CSS color value like "#FF8800". - * When passing a function the canvas context is available as argument which is useful when you draw a gradient or pattern. + * When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern. * * @property {Number} [degrees=0] * Initial rotation. diff --git a/src/tiledimage.js b/src/tiledimage.js index 94c87cb1..88f7857c 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1172,7 +1172,16 @@ function drawTiles( tiledImage, lastDrawn ) { if ( tiledImage.placeholderFillStyle && lastDrawn.length === 0 ) { var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true)); - tiledImage._drawer.drawPlaceholder(placeholderRect, tiledImage.placeholderFillStyle); + + var fillStyle = null; + if ( typeof tiledImage.placeholderFillStyle === "function" ) { + fillStyle = tiledImage.placeholderFillStyle(tiledImage, tiledImage._drawer.context); + } + else { + fillStyle = tiledImage.placeholderFillStyle; + } + + tiledImage._drawer.drawPlaceholder(placeholderRect, fillStyle); } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { From 9df77ee91577643ae28b50b9a02583f38682c02c Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 15 Apr 2015 13:40:12 +0200 Subject: [PATCH 023/104] Return early in `drawDebugInfo` --- src/drawer.js | 148 +++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 73 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index a38aed0a..2f6cabfe 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -318,80 +318,82 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ // private drawDebugInfo: function( tile, count, i ){ - if ( this.useCanvas ) { - this.context.save(); - this.context.lineWidth = 2 * $.pixelDensityRatio; - this.context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; - this.context.strokeStyle = this.debugGridColor; - this.context.fillStyle = this.debugGridColor; - - if ( this.viewport.degrees !== 0 ) { - this._offsetForRotation( tile, this.canvas, this.context, this.viewport.degrees ); - } - - this.context.strokeRect( - tile.position.x * $.pixelDensityRatio, - tile.position.y * $.pixelDensityRatio, - tile.size.x * $.pixelDensityRatio, - tile.size.y * $.pixelDensityRatio - ); - - var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio; - var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio; - - // Rotate the text the right way around. - this.context.translate( tileCenterX, tileCenterY ); - this.context.rotate( Math.PI / 180 * -this.viewport.degrees ); - this.context.translate( -tileCenterX, -tileCenterY ); - - if( tile.x === 0 && tile.y === 0 ){ - this.context.fillText( - "Zoom: " + this.viewport.getZoom(), - tile.position.x * $.pixelDensityRatio, - (tile.position.y - 30) * $.pixelDensityRatio - ); - this.context.fillText( - "Pan: " + this.viewport.getBounds().toString(), - tile.position.x * $.pixelDensityRatio, - (tile.position.y - 20) * $.pixelDensityRatio - ); - } - this.context.fillText( - "Level: " + tile.level, - (tile.position.x + 10) * $.pixelDensityRatio, - (tile.position.y + 20) * $.pixelDensityRatio - ); - this.context.fillText( - "Column: " + tile.x, - (tile.position.x + 10) * $.pixelDensityRatio, - (tile.position.y + 30) * $.pixelDensityRatio - ); - this.context.fillText( - "Row: " + tile.y, - (tile.position.x + 10) * $.pixelDensityRatio, - (tile.position.y + 40) * $.pixelDensityRatio - ); - this.context.fillText( - "Order: " + i + " of " + count, - (tile.position.x + 10) * $.pixelDensityRatio, - (tile.position.y + 50) * $.pixelDensityRatio - ); - this.context.fillText( - "Size: " + tile.size.toString(), - (tile.position.x + 10) * $.pixelDensityRatio, - (tile.position.y + 60) * $.pixelDensityRatio - ); - this.context.fillText( - "Position: " + tile.position.toString(), - (tile.position.x + 10) * $.pixelDensityRatio, - (tile.position.y + 70) * $.pixelDensityRatio - ); - - if ( this.viewport.degrees !== 0 ) { - this._restoreRotationChanges( tile, this.canvas, this.context ); - } - this.context.restore(); + if ( !this.useCanvas ) { + return; } + + this.context.save(); + this.context.lineWidth = 2 * $.pixelDensityRatio; + this.context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; + this.context.strokeStyle = this.debugGridColor; + this.context.fillStyle = this.debugGridColor; + + if ( this.viewport.degrees !== 0 ) { + this._offsetForRotation( tile, this.canvas, this.context, this.viewport.degrees ); + } + + this.context.strokeRect( + tile.position.x * $.pixelDensityRatio, + tile.position.y * $.pixelDensityRatio, + tile.size.x * $.pixelDensityRatio, + tile.size.y * $.pixelDensityRatio + ); + + var tileCenterX = (tile.position.x + (tile.size.x / 2)) * $.pixelDensityRatio; + var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio; + + // Rotate the text the right way around. + this.context.translate( tileCenterX, tileCenterY ); + this.context.rotate( Math.PI / 180 * -this.viewport.degrees ); + this.context.translate( -tileCenterX, -tileCenterY ); + + if( tile.x === 0 && tile.y === 0 ){ + this.context.fillText( + "Zoom: " + this.viewport.getZoom(), + tile.position.x * $.pixelDensityRatio, + (tile.position.y - 30) * $.pixelDensityRatio + ); + this.context.fillText( + "Pan: " + this.viewport.getBounds().toString(), + tile.position.x * $.pixelDensityRatio, + (tile.position.y - 20) * $.pixelDensityRatio + ); + } + this.context.fillText( + "Level: " + tile.level, + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 20) * $.pixelDensityRatio + ); + this.context.fillText( + "Column: " + tile.x, + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 30) * $.pixelDensityRatio + ); + this.context.fillText( + "Row: " + tile.y, + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 40) * $.pixelDensityRatio + ); + this.context.fillText( + "Order: " + i + " of " + count, + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 50) * $.pixelDensityRatio + ); + this.context.fillText( + "Size: " + tile.size.toString(), + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 60) * $.pixelDensityRatio + ); + this.context.fillText( + "Position: " + tile.position.toString(), + (tile.position.x + 10) * $.pixelDensityRatio, + (tile.position.y + 70) * $.pixelDensityRatio + ); + + if ( this.viewport.degrees !== 0 ) { + this._restoreRotationChanges( tile, this.canvas, this.context ); + } + this.context.restore(); }, // private From 0d056145a39d1ff89885436789492a5ed246108a Mon Sep 17 00:00:00 2001 From: Philip Giuliani Date: Wed, 15 Apr 2015 18:31:56 +0200 Subject: [PATCH 024/104] :lipstick: Rename to drawRectangle --- src/drawer.js | 2 +- src/tiledimage.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2f6cabfe..e14c14e3 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -305,7 +305,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ }, // private - drawPlaceholder: function(rect, fillStyle) { + drawRectangle: function(rect, fillStyle) { if (!this.useCanvas) { return; } diff --git a/src/tiledimage.js b/src/tiledimage.js index 88f7857c..eebfa0af 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1181,7 +1181,7 @@ function drawTiles( tiledImage, lastDrawn ) { fillStyle = tiledImage.placeholderFillStyle; } - tiledImage._drawer.drawPlaceholder(placeholderRect, fillStyle); + tiledImage._drawer.drawRectangle(placeholderRect, fillStyle); } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { From 5b64beab6155849788f66ca20c76b5faf0e405b2 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 17 Apr 2015 10:22:53 -0700 Subject: [PATCH 025/104] Changelog for #635 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index e6d4002b..229e03cc 100644 --- a/changelog.txt +++ b/changelog.txt @@ -37,6 +37,7 @@ OPENSEADRAGON CHANGELOG * Viewport.open supports positioning config properties * For multi-image open, drawing isn't started until all tileSources have been opened * You can specify a clip area for each image (only works on browsers that support the HTML5 canvas) (#594) + * Added placeholderFillStyle so image rectangles can be drawn even before their tiles load (#635) * Margins option to push the home region in from the edges of the Viewer (#505) * Rect and Point toString() functions are now consistent: rounding values to nearest hundredth * Overlays appear in the DOM immediately on open or addOverlay (#507) From 44395662d1368e58632686056f0ce580eec2d2d1 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 19 Apr 2015 16:10:37 -0400 Subject: [PATCH 026/104] Add opacity support. --- src/drawer.js | 182 +++++++++++++++++++++++++++------------------- src/tile.js | 2 +- src/tiledimage.js | 34 ++++++--- src/viewer.js | 1 + 4 files changed, 133 insertions(+), 86 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index e14c14e3..2cf6c43b 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -96,6 +96,13 @@ $.Drawer = function( options ) { */ this.context = this.useCanvas ? this.canvas.getContext( "2d" ) : null; + /** + * Sketch canvas used to temporarily draw tiles which cannot be drawn directly + * to the main canvas due to opacity. + */ + this.sketchCanvas = this.useCanvas ? document.createElement( "canvas" ) : null; + this.sketchContext = this.useCanvas ? this.sketchCanvas.getContext( "2d" ) : null; + /** * @member {Element} element * @memberof OpenSeadragon.Drawer# @@ -111,8 +118,8 @@ $.Drawer = function( options ) { // check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density if (this.useCanvas) { var viewportSize = this._calculateCanvasSize(); - this.canvas.width = viewportSize.x; - this.canvas.height = viewportSize.y; + this.canvas.width = this.sketchCanvas.width = viewportSize.x; + this.canvas.height = this.sketchCanvas.height = viewportSize.y; } this.canvas.style.width = "100%"; @@ -254,21 +261,23 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @param {OpenSeadragon.Tile} tile - The tile to draw. * @param {Function} drawingHandler - Method for firing the drawing event if using canvas. * drawingHandler({context, tile, rendered}) + * @param {Boolean} useSketch - Whether to use the sketch canvas or not. * where rendered is the context with the pre-drawn image. */ - drawTile: function( tile, drawingHandler ) { + drawTile: function( tile, drawingHandler, useSketch ) { $.console.assert(tile, '[Drawer.drawTile] tile is required'); $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required'); if ( this.useCanvas ) { + var context = useSketch ? this.sketchContext : this.context; // TODO do this in a more performant way // specifically, don't save,rotate,restore every time we draw a tile if( this.viewport.degrees !== 0 ) { - this._offsetForRotation( tile, this.viewport.degrees ); - tile.drawCanvas( this.context, drawingHandler ); - this._restoreRotationChanges( tile ); + this._offsetForRotation( tile, this.viewport.degrees, useSketch ); + tile.drawCanvas( context, drawingHandler ); + this._restoreRotationChanges( tile, useSketch ); } else { - tile.drawCanvas( this.context, drawingHandler ); + tile.drawCanvas( context, drawingHandler ); } } else { tile.drawHTML( this.canvas ); @@ -276,63 +285,87 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ }, // private - saveContext: function() { + saveContext: function(useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.save(); + }, + + // private + restoreContext: function(useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.restore(); + }, + + // private + setClip: function(rect, useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.beginPath(); + context.rect(rect.x, rect.y, rect.width, rect.height); + context.clip(); + }, + + // private + drawRectangle: function(rect, fillStyle, useSketch) { + if (!this.useCanvas) { + return; + } + + var context = useSketch ? this.sketchContext : this.context; + context.save(); + context.fillStyle = fillStyle; + context.fillRect(rect.x, rect.y, rect.width, rect.height); + context.restore(); + }, + + /** + * Blends the sketch canvas in the main canvas. + * The sketch canvas is then cleared. + * @param {Float} opacity The opacity of the blending. + * @returns {undefined} + */ + blendSketch: function(opacity) { if (!this.useCanvas) { return; } this.context.save(); - }, - - // private - restoreContext: function() { - if (!this.useCanvas) { - return; - } - + this.context.globalAlpha = opacity; + this.context.drawImage(this.sketchCanvas, 0, 0); this.context.restore(); + this.sketchContext.clearRect(0, 0, + this.sketchCanvas.width, this.sketchCanvas.height); }, // private - setClip: function(rect) { - if (!this.useCanvas) { - return; - } - - this.context.beginPath(); - this.context.rect(rect.x, rect.y, rect.width, rect.height); - this.context.clip(); - }, - - // private - drawRectangle: function(rect, fillStyle) { - if (!this.useCanvas) { - return; - } - - this.context.save(); - this.context.fillStyle = fillStyle; - this.context.fillRect(rect.x, rect.y, rect.width, rect.height); - this.context.restore(); - }, - - // private - drawDebugInfo: function( tile, count, i ){ + drawDebugInfo: function( tile, count, i, useSketch ){ if ( !this.useCanvas ) { return; } - this.context.save(); - this.context.lineWidth = 2 * $.pixelDensityRatio; - this.context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; - this.context.strokeStyle = this.debugGridColor; - this.context.fillStyle = this.debugGridColor; + var context = useSketch ? this.sketchContext : this.context; + context.save(); + context.lineWidth = 2 * $.pixelDensityRatio; + context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; + context.strokeStyle = this.debugGridColor; + context.fillStyle = this.debugGridColor; if ( this.viewport.degrees !== 0 ) { - this._offsetForRotation( tile, this.canvas, this.context, this.viewport.degrees ); + this._offsetForRotation( tile, this.viewport.degrees, useSketch ); } - this.context.strokeRect( + context.strokeRect( tile.position.x * $.pixelDensityRatio, tile.position.y * $.pixelDensityRatio, tile.size.x * $.pixelDensityRatio, @@ -343,95 +376,97 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ var tileCenterY = (tile.position.y + (tile.size.y / 2)) * $.pixelDensityRatio; // Rotate the text the right way around. - this.context.translate( tileCenterX, tileCenterY ); - this.context.rotate( Math.PI / 180 * -this.viewport.degrees ); - this.context.translate( -tileCenterX, -tileCenterY ); + context.translate( tileCenterX, tileCenterY ); + context.rotate( Math.PI / 180 * -this.viewport.degrees ); + context.translate( -tileCenterX, -tileCenterY ); if( tile.x === 0 && tile.y === 0 ){ - this.context.fillText( + context.fillText( "Zoom: " + this.viewport.getZoom(), tile.position.x * $.pixelDensityRatio, (tile.position.y - 30) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Pan: " + this.viewport.getBounds().toString(), tile.position.x * $.pixelDensityRatio, (tile.position.y - 20) * $.pixelDensityRatio ); } - this.context.fillText( + context.fillText( "Level: " + tile.level, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 20) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Column: " + tile.x, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 30) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Row: " + tile.y, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 40) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Order: " + i + " of " + count, (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 50) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Size: " + tile.size.toString(), (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 60) * $.pixelDensityRatio ); - this.context.fillText( + context.fillText( "Position: " + tile.position.toString(), (tile.position.x + 10) * $.pixelDensityRatio, (tile.position.y + 70) * $.pixelDensityRatio ); if ( this.viewport.degrees !== 0 ) { - this._restoreRotationChanges( tile, this.canvas, this.context ); + this._restoreRotationChanges( tile, useSketch ); } - this.context.restore(); + context.restore(); }, // private - debugRect: function(rect) { + debugRect: function(rect, useSketch) { if ( this.useCanvas ) { - this.context.save(); - this.context.lineWidth = 2 * $.pixelDensityRatio; - this.context.strokeStyle = this.debugGridColor; - this.context.fillStyle = this.debugGridColor; + var context = useSketch ? this.sketchContext : this.context; + context.save(); + context.lineWidth = 2 * $.pixelDensityRatio; + context.strokeStyle = this.debugGridColor; + context.fillStyle = this.debugGridColor; - this.context.strokeRect( + context.strokeRect( rect.x * $.pixelDensityRatio, rect.y * $.pixelDensityRatio, rect.width * $.pixelDensityRatio, rect.height * $.pixelDensityRatio ); - this.context.restore(); + context.restore(); } }, // private - _offsetForRotation: function( tile, degrees ){ + _offsetForRotation: function( tile, degrees, useSketch ){ var cx = this.canvas.width / 2, cy = this.canvas.height / 2, px = tile.position.x - cx, py = tile.position.y - cy; - this.context.save(); + var context = useSketch ? this.sketchContext : this.context; + context.save(); - this.context.translate(cx, cy); - this.context.rotate( Math.PI / 180 * degrees); + context.translate(cx, cy); + context.rotate( Math.PI / 180 * degrees); tile.position.x = px; tile.position.y = py; }, // private - _restoreRotationChanges: function( tile ){ + _restoreRotationChanges: function( tile, useSketch ){ var cx = this.canvas.width / 2, cy = this.canvas.height / 2, px = tile.position.x + cx, @@ -440,7 +475,8 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ tile.position.x = px; tile.position.y = py; - this.context.restore(); + var context = useSketch ? this.sketchContext : this.context; + context.restore(); }, // private diff --git a/src/tile.js b/src/tile.js index 756d9d4a..3fe642df 100644 --- a/src/tile.js +++ b/src/tile.js @@ -67,7 +67,7 @@ $.Tile = function(level, x, y, bounds, exists, url) { this.y = y; /** * Where this tile fits, in normalized coordinates - * @member {OpenSeadragon.Point} bounds + * @member {OpenSeadragon.Rect} bounds * @memberof OpenSeadragon.Tile# */ this.bounds = bounds; diff --git a/src/tiledimage.js b/src/tiledimage.js index eebfa0af..a494dbd0 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -119,6 +119,12 @@ $.TiledImage = function( options ) { delete options.height; } + this.opacity = 1; + if ( options.opacity ) { + this.opacity = options.opacity; + delete options.opacity; + } + $.extend( true, this, { //internal state properties @@ -1152,20 +1158,20 @@ function compareTiles( previousBest, tile ) { function drawTiles( tiledImage, lastDrawn ) { var i, - tile, - tileKey, - viewer, - viewport, - position, - tileSource; + tile; + + if ( tiledImage.opacity <= 0 ) { + return; + } + var useSketch = tiledImage.opacity < 1; var usedClip = false; if ( tiledImage._clip ) { - tiledImage._drawer.saveContext(); + tiledImage._drawer.saveContext(useSketch); var box = tiledImage.imageToViewportRectangle(tiledImage._clip, true); var clipRect = tiledImage._drawer.viewportToDrawerRectangle(box); - tiledImage._drawer.setClip(clipRect); + tiledImage._drawer.setClip(clipRect, useSketch); usedClip = true; } @@ -1181,17 +1187,17 @@ function drawTiles( tiledImage, lastDrawn ) { fillStyle = tiledImage.placeholderFillStyle; } - tiledImage._drawer.drawRectangle(placeholderRect, fillStyle); + tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch); } for ( i = lastDrawn.length - 1; i >= 0; i-- ) { tile = lastDrawn[ i ]; - tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler ); + tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch ); tile.beingDrawn = true; if( tiledImage.debugMode ) { try { - tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i ); + tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i, useSketch ); } catch(e) { $.console.error(e); } @@ -1217,7 +1223,11 @@ function drawTiles( tiledImage, lastDrawn ) { } if ( usedClip ) { - tiledImage._drawer.restoreContext(); + tiledImage._drawer.restoreContext( useSketch ); + } + + if ( useSketch ) { + tiledImage._drawer.blendSketch( tiledImage.opacity ); } } diff --git a/src/viewer.js b/src/viewer.js index 57ad0f1f..570e60c7 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1290,6 +1290,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, height: queueItem.options.height, clip: queueItem.options.clip, placeholderFillStyle: queueItem.options.placeholderFillStyle, + opacity: queueItem.options.opacity, springStiffness: _this.springStiffness, animationTime: _this.animationTime, minZoomImageRatio: _this.minZoomImageRatio, From 9d053c545bd1fcae8a21f10875533a4a2a194994 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Mon, 20 Apr 2015 19:25:36 -0400 Subject: [PATCH 027/104] Fix Ian's comments. --- src/drawer.js | 104 +++++++++++++++++++++++++++---------------- src/openseadragon.js | 2 +- src/tiledimage.js | 51 ++++++++++++++------- src/viewer.js | 5 ++- 4 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2cf6c43b..2d39c373 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -42,11 +42,9 @@ * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer. * @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport. * @param {Element} options.element - Parent element. - * @param {Number} [options.opacity=1] - See opacity in {@link OpenSeadragon.Options} for details. * @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details. */ $.Drawer = function( options ) { - var _this = this; $.console.assert( options.viewer, "[Drawer] options.viewer is required" ); @@ -72,7 +70,9 @@ $.Drawer = function( options ) { this.viewer = options.viewer; this.viewport = options.viewport; this.debugGridColor = options.debugGridColor || $.DEFAULT_SETTINGS.debugGridColor; - this.opacity = options.opacity === undefined ? $.DEFAULT_SETTINGS.opacity : options.opacity; + if (options.opacity) { + $.console.error( "[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead" ); + } this.useCanvas = $.supportsCanvas && ( this.viewer ? this.viewer.useCanvas : true ); /** @@ -98,10 +98,10 @@ $.Drawer = function( options ) { /** * Sketch canvas used to temporarily draw tiles which cannot be drawn directly - * to the main canvas due to opacity. + * to the main canvas due to opacity. Lazily initialized. */ - this.sketchCanvas = this.useCanvas ? document.createElement( "canvas" ) : null; - this.sketchContext = this.useCanvas ? this.sketchCanvas.getContext( "2d" ) : null; + this.sketchCanvas = null; + this.sketchContext = null; /** * @member {Element} element @@ -118,8 +118,8 @@ $.Drawer = function( options ) { // check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density if (this.useCanvas) { var viewportSize = this._calculateCanvasSize(); - this.canvas.width = this.sketchCanvas.width = viewportSize.x; - this.canvas.height = this.sketchCanvas.height = viewportSize.y; + this.canvas.width = viewportSize.x; + this.canvas.height = viewportSize.y; } this.canvas.style.width = "100%"; @@ -167,8 +167,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @return {OpenSeadragon.Drawer} Chainable. */ setOpacity: function( opacity ) { - this.opacity = opacity; - $.setElementOpacity( this.canvas, this.opacity, true ); + $.console.error("drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead."); + var world = this.viewer.world; + for (var i = 0; i < world.getItemCount(); i++) { + world.getItemAt( i ).setOpacity( opacity ); + } return this; }, @@ -177,7 +180,16 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @returns {Number} */ getOpacity: function() { - return this.opacity; + $.console.error("drawer.getOpacity is deprecated. Use tiledImage.getOpacity instead."); + var world = this.viewer.world; + var maxOpacity = 0; + for (var i = 0; i < world.getItemCount(); i++) { + var opacity = world.getItemAt( i ).getOpacity(); + if ( opacity > maxOpacity ) { + maxOpacity = opacity; + } + } + return maxOpacity; }, // deprecated @@ -221,12 +233,14 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ //force unloading of current canvas (1x1 will be gc later, trick not necessarily needed) this.canvas.width = 1; this.canvas.height = 1; + this.sketchCanvas = null; + this.sketchContext = null; }, /** * Clears the Drawer so it's ready to draw another frame. */ - clear: function() { + clear: function( useSketch ) { this.canvas.innerHTML = ""; if ( this.useCanvas ) { var viewportSize = this._calculateCanvasSize(); @@ -234,8 +248,13 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.canvas.height != viewportSize.y ) { this.canvas.width = viewportSize.x; this.canvas.height = viewportSize.y; + if ( this.sketchCanvas !== null ) { + this.sketchCanvas.width = this.canvas.width; + this.sketchCanvas.height = this.canvas.height; + } } - this.context.clearRect( 0, 0, viewportSize.x, viewportSize.y ); + this._getContext( useSketch ).clearRect( + 0, 0, viewportSize.x, viewportSize.y ); } }, @@ -269,7 +288,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ $.console.assert(drawingHandler, '[Drawer.drawTile] drawingHandler is required'); if ( this.useCanvas ) { - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); // TODO do this in a more performant way // specifically, don't save,rotate,restore every time we draw a tile if( this.viewport.degrees !== 0 ) { @@ -284,24 +303,36 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ } }, - // private - saveContext: function(useSketch) { - if (!this.useCanvas) { - return; + _getContext: function( useSketch ) { + var context = this.context; + if ( useSketch ) { + if (this.sketchCanvas === null) { + this.sketchCanvas = document.createElement( "canvas" ); + this.sketchCanvas.width = this.canvas.width; + this.sketchCanvas.height = this.canvas.height; + this.sketchContext = this.sketchCanvas.getContext( "2d" ); + } + context = this.sketchContext; } - - var context = useSketch ? this.sketchContext : this.context; - context.save(); + return context; }, // private - restoreContext: function(useSketch) { + saveContext: function( useSketch ) { if (!this.useCanvas) { return; } - var context = useSketch ? this.sketchContext : this.context; - context.restore(); + this._getContext( useSketch ).save(); + }, + + // private + restoreContext: function( useSketch ) { + if (!this.useCanvas) { + return; + } + + this._getContext( useSketch ).restore(); }, // private @@ -310,7 +341,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.beginPath(); context.rect(rect.x, rect.y, rect.width, rect.height); context.clip(); @@ -322,7 +353,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return; } - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.save(); context.fillStyle = fillStyle; context.fillRect(rect.x, rect.y, rect.width, rect.height); @@ -331,12 +362,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ /** * Blends the sketch canvas in the main canvas. - * The sketch canvas is then cleared. * @param {Float} opacity The opacity of the blending. * @returns {undefined} */ blendSketch: function(opacity) { - if (!this.useCanvas) { + if (!this.useCanvas || !this.sketchCanvas) { return; } @@ -344,17 +374,15 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.context.globalAlpha = opacity; this.context.drawImage(this.sketchCanvas, 0, 0); this.context.restore(); - this.sketchContext.clearRect(0, 0, - this.sketchCanvas.width, this.sketchCanvas.height); }, // private - drawDebugInfo: function( tile, count, i, useSketch ){ + drawDebugInfo: function( tile, count, i ){ if ( !this.useCanvas ) { return; } - var context = useSketch ? this.sketchContext : this.context; + var context = this.context; context.save(); context.lineWidth = 2 * $.pixelDensityRatio; context.font = 'small-caps bold ' + (13 * $.pixelDensityRatio) + 'px arial'; @@ -362,7 +390,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ context.fillStyle = this.debugGridColor; if ( this.viewport.degrees !== 0 ) { - this._offsetForRotation( tile, this.viewport.degrees, useSketch ); + this._offsetForRotation( tile, this.viewport.degrees ); } context.strokeRect( @@ -424,15 +452,15 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ ); if ( this.viewport.degrees !== 0 ) { - this._restoreRotationChanges( tile, useSketch ); + this._restoreRotationChanges( tile ); } context.restore(); }, // private - debugRect: function(rect, useSketch) { + debugRect: function(rect) { if ( this.useCanvas ) { - var context = useSketch ? this.sketchContext : this.context; + var context = this.context; context.save(); context.lineWidth = 2 * $.pixelDensityRatio; context.strokeStyle = this.debugGridColor; @@ -456,7 +484,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ px = tile.position.x - cx, py = tile.position.y - cy; - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.save(); context.translate(cx, cy); @@ -475,7 +503,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ tile.position.x = px; tile.position.y = py; - var context = useSketch ? this.sketchContext : this.context; + var context = this._getContext( useSketch ); context.restore(); }, diff --git a/src/openseadragon.js b/src/openseadragon.js index 3e3cffbd..461f9d22 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -204,7 +204,7 @@ * If 0, adjusts to fit viewer. * * @property {Number} [opacity=1] - * Opacity of the drawer (1=opaque, 0=transparent) + * Default opacity of the tiled images (1=opaque, 0=transparent) * * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null] * Draws a colored rectangle behind the tile if it is not loaded yet. diff --git a/src/tiledimage.js b/src/tiledimage.js index a494dbd0..80515434 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -64,6 +64,7 @@ * @param {Number} [options.blendTime] - See {@link OpenSeadragon.Options}. * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}. * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}. + * @param {Number} [options.opacity=1] - Opacity the tiled image should be drawn at. * @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}. @@ -119,12 +120,6 @@ $.TiledImage = function( options ) { delete options.height; } - this.opacity = 1; - if ( options.opacity ) { - this.opacity = options.opacity; - delete options.opacity; - } - $.extend( true, this, { //internal state properties @@ -148,7 +143,8 @@ $.TiledImage = function( options ) { minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, debugMode: $.DEFAULT_SETTINGS.debugMode, crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy, - placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle + placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle, + opacity: $.DEFAULT_SETTINGS.opacity }, options ); @@ -492,6 +488,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag this._needsDraw = true; }, + /** + * @returns {Number} The TiledImage's current opacity. + */ + getOpacity: function() { + return this.opacity; + }, + + /** + * @param {Number} opacity Opacity the tiled image should be drawn at. + * @returns {OpenSeadragon.TiledImage} Chainable + */ + setOpacity: function(opacity) { + this.opacity = opacity; + this._needsDraw = true; + return this; + }, + // private _setScale: function(scale, immediately) { var sameTarget = (this._scaleSpring.target.value === scale); @@ -1161,6 +1174,7 @@ function drawTiles( tiledImage, lastDrawn ) { tile; if ( tiledImage.opacity <= 0 ) { + drawDebugInfo( tiledImage, lastDrawn ); return; } var useSketch = tiledImage.opacity < 1; @@ -1195,14 +1209,6 @@ function drawTiles( tiledImage, lastDrawn ) { tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch ); tile.beingDrawn = true; - if( tiledImage.debugMode ) { - try { - tiledImage._drawer.drawDebugInfo( tile, lastDrawn.length, i, useSketch ); - } catch(e) { - $.console.error(e); - } - } - if( tiledImage.viewer ){ /** * - Needs documentation - @@ -1228,6 +1234,21 @@ function drawTiles( tiledImage, lastDrawn ) { if ( useSketch ) { tiledImage._drawer.blendSketch( tiledImage.opacity ); + tiledImage._drawer.clear( true ); + } + drawDebugInfo( tiledImage, lastDrawn ); +} + +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 ); + } catch(e) { + $.console.error(e); + } + } } } diff --git a/src/viewer.js b/src/viewer.js index 570e60c7..2be3786e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -374,7 +374,6 @@ $.Viewer = function( options ) { viewer: this, viewport: this.viewport, element: this.canvas, - opacity: this.opacity, debugGridColor: this.debugGridColor }); @@ -1198,6 +1197,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to * (portions of the image outside of this area will not be visible). Only works on * browsers that support the HTML5 canvas. + * @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default. * @param {Function} [options.success] A function that gets called when the image is * successfully added. It's passed the event object which contains a single property: * "item", the resulting TiledImage. @@ -1221,6 +1221,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, if (options.placeholderFillStyle === undefined) { options.placeholderFillStyle = this.placeholderFillStyle; } + if (options.opacity === undefined) { + options.opacity = this.opacity; + } var myQueueItem = { options: options From dd0777141501a462253bcb4bfc698cd77912a49b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 22 Apr 2015 18:30:49 -0400 Subject: [PATCH 028/104] Fix drawer.clear --- src/drawer.js | 14 +++++++++++--- src/tiledimage.js | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index 2d39c373..939a8cf0 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -240,7 +240,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ /** * Clears the Drawer so it's ready to draw another frame. */ - clear: function( useSketch ) { + clear: function() { this.canvas.innerHTML = ""; if ( this.useCanvas ) { var viewportSize = this._calculateCanvasSize(); @@ -253,11 +253,19 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.sketchCanvas.height = this.canvas.height; } } - this._getContext( useSketch ).clearRect( - 0, 0, viewportSize.x, viewportSize.y ); + this._clear(); } }, + _clear: function ( useSketch ) { + if ( !this.useCanvas ) { + return; + } + var context = this._getContext( useSketch ); + var canvas = context.canvas; + context.clearRect( 0, 0, canvas.width, canvas.height ); + }, + /** * Translates from OpenSeadragon viewer rectangle to drawer rectangle. * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system. diff --git a/src/tiledimage.js b/src/tiledimage.js index 80515434..faa49cc9 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -497,12 +497,10 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag /** * @param {Number} opacity Opacity the tiled image should be drawn at. - * @returns {OpenSeadragon.TiledImage} Chainable */ setOpacity: function(opacity) { this.opacity = opacity; this._needsDraw = true; - return this; }, // private @@ -1178,6 +1176,9 @@ function drawTiles( tiledImage, lastDrawn ) { return; } var useSketch = tiledImage.opacity < 1; + if ( useSketch ) { + tiledImage._drawer._clear( true ); + } var usedClip = false; if ( tiledImage._clip ) { @@ -1234,7 +1235,6 @@ function drawTiles( tiledImage, lastDrawn ) { if ( useSketch ) { tiledImage._drawer.blendSketch( tiledImage.opacity ); - tiledImage._drawer.clear( true ); } drawDebugInfo( tiledImage, lastDrawn ); } From 6bc5f1ff510fca5718773cedbcc279f32c8e11c2 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 22 Apr 2015 20:12:38 -0400 Subject: [PATCH 029/104] Fix drawer tests. --- test/modules/drawer.js | 69 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/test/modules/drawer.js b/test/modules/drawer.js index c91915c3..4016ebfd 100644 --- a/test/modules/drawer.js +++ b/test/modules/drawer.js @@ -33,9 +33,6 @@ createViewer(); ok(viewer.drawer, 'Drawer exists'); equal(viewer.drawer.canRotate(), OpenSeadragon.supportsCanvas, 'we can rotate if we have canvas'); - equal(viewer.drawer.getOpacity(), 1, 'starts with full opacity'); - viewer.drawer.setOpacity(0.4); - equal(viewer.drawer.getOpacity(), 0.4, 'setting opacity works'); start(); }); @@ -67,18 +64,64 @@ }); }); + // ---------- + asyncTest('sketchCanvas', function() { + createViewer({ + tileSources: '/test/data/testpattern.dzi' + }); + var drawer = viewer.drawer; + + viewer.addHandler('tile-drawn', function noOpacityHandler() { + viewer.removeHandler('tile-drawn', noOpacityHandler); + equal(drawer.sketchCanvas, null, + 'The sketch canvas should be null if no decimal opacity is used.'); + equal(drawer.sketchContext, null, + 'The sketch context should be null if no decimal opacity is used.'); + testOpacityDecimal(); + }); + + function testOpacityDecimal() { + var tiledImage; + viewer.addTiledImage({ + tileSource: '/test/data/testpattern.dzi', + opacity: 0.5, + success: function(event) { + tiledImage = event.item; + } + }); + + viewer.addHandler('tile-drawn', function opacityDecimalHandler(event) { + if (tiledImage !== event.tiledImage) { + return; + } + viewer.removeHandler('tile-drawn', opacityDecimalHandler); + notEqual(drawer.sketchCanvas, null, + 'The sketch canvas should not be null once a decimal opacity has been used.'); + notEqual(drawer.sketchContext, null, + 'The sketch context should not be null once a decimal opacity has been used.'); + start(); + }); + } + }); + // ---------- asyncTest('deprecations', function() { - createViewer(); - Util.testDeprecation(viewer.drawer, 'addOverlay', viewer, 'addOverlay'); - Util.testDeprecation(viewer.drawer, 'updateOverlay', viewer, 'updateOverlay'); - Util.testDeprecation(viewer.drawer, 'removeOverlay', viewer, 'removeOverlay'); - Util.testDeprecation(viewer.drawer, 'clearOverlays', viewer, 'clearOverlays'); - Util.testDeprecation(viewer.drawer, 'needsUpdate', viewer.world, 'needsDraw'); - Util.testDeprecation(viewer.drawer, 'numTilesLoaded', viewer.tileCache, 'numTilesLoaded'); - Util.testDeprecation(viewer.drawer, 'reset', viewer.world, 'resetItems'); - Util.testDeprecation(viewer.drawer, 'update', viewer.world, 'draw'); - start(); + createViewer({ + tileSources: '/test/data/testpattern.dzi' + }); + viewer.world.addHandler('add-item', function() { + Util.testDeprecation(viewer.drawer, 'addOverlay', viewer, 'addOverlay'); + Util.testDeprecation(viewer.drawer, 'updateOverlay', viewer, 'updateOverlay'); + Util.testDeprecation(viewer.drawer, 'removeOverlay', viewer, 'removeOverlay'); + Util.testDeprecation(viewer.drawer, 'clearOverlays', viewer, 'clearOverlays'); + Util.testDeprecation(viewer.drawer, 'needsUpdate', viewer.world, 'needsDraw'); + Util.testDeprecation(viewer.drawer, 'numTilesLoaded', viewer.tileCache, 'numTilesLoaded'); + Util.testDeprecation(viewer.drawer, 'reset', viewer.world, 'resetItems'); + Util.testDeprecation(viewer.drawer, 'update', viewer.world, 'draw'); + Util.testDeprecation(viewer.drawer, 'setOpacity', viewer.world.getItemAt(0), 'setOpacity'); + Util.testDeprecation(viewer.drawer, 'getOpacity', viewer.world.getItemAt(0), 'getOpacity'); + start(); + }); }); })(); From 39bd3709309616ed8240dacc453f56e2813b221a Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Mon, 27 Apr 2015 19:28:18 -0400 Subject: [PATCH 030/104] Add opacity test in tiledimage. --- test/modules/tiledimage.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js index 704f4773..07f23f55 100644 --- a/test/modules/tiledimage.js +++ b/test/modules/tiledimage.js @@ -220,4 +220,41 @@ }); }); + // ---------- + asyncTest('opacity', function() { + + function testDefaultOpacity() { + viewer.removeHandler('open', testDefaultOpacity); + var image = viewer.world.getItemAt(0); + strictEqual(image.getOpacity(), 0.5, 'image has default opacity'); + + image.setOpacity(1); + strictEqual(image.getOpacity(), 1, 'opacity is set correctly'); + + viewer.addHandler('open', testTileSourceOpacity); + viewer.open({ + tileSource: '/test/data/testpattern.dzi', + opacity: 0.25 + }); + } + + function testTileSourceOpacity() { + viewer.removeHandler('open', testTileSourceOpacity); + var image = viewer.world.getItemAt(0); + strictEqual(image.getOpacity(), 0.25, 'image has correct opacity'); + + image.setOpacity(0); + strictEqual(image.getOpacity(), 0, 'opacity is set correctly'); + + start(); + } + + viewer.addHandler('open', testDefaultOpacity); + + viewer.opacity = 0.5; + viewer.open({ + tileSource: '/test/data/testpattern.dzi', + }); + }); + })(); From 6384c126e6c3eeb65bd18352e216b30c7dec29aa Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 29 Apr 2015 10:02:01 -0700 Subject: [PATCH 031/104] Changelog for #644 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 229e03cc..d5f3f231 100644 --- a/changelog.txt +++ b/changelog.txt @@ -38,6 +38,7 @@ OPENSEADRAGON CHANGELOG * For multi-image open, drawing isn't started until all tileSources have been opened * You can specify a clip area for each image (only works on browsers that support the HTML5 canvas) (#594) * Added placeholderFillStyle so image rectangles can be drawn even before their tiles load (#635) + * Ability to set opacity on individual TiledImages (#644) * Margins option to push the home region in from the edges of the Viewer (#505) * Rect and Point toString() functions are now consistent: rounding values to nearest hundredth * Overlays appear in the DOM immediately on open or addOverlay (#507) From efa8ccff35da1dc6878b75e7fb7307dce20b8b5a Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 5 May 2015 17:20:20 -0700 Subject: [PATCH 032/104] * Keyboard pan speed is now the same regardless of zoom level (#645) --- changelog.txt | 1 + src/viewer.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index d5f3f231..9b79f56a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -60,6 +60,7 @@ OPENSEADRAGON CHANGELOG * Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616) * Now animating zoom spring exponentially (#631) * Added http://editorconfig.org/ config file (#637) +* Keyboard pan speed is now the same regardless of zoom level (#645) 1.2.1: diff --git a/src/viewer.js b/src/viewer.js index 2be3786e..c8de84f3 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -2216,7 +2216,7 @@ function onCanvasKeyDown( event ) { if ( event.shift ) { this.viewport.zoomBy(1.1); } else { - this.viewport.panBy(new $.Point(0, -0.05)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, -40))); } this.viewport.applyConstraints(); return false; @@ -2224,16 +2224,16 @@ function onCanvasKeyDown( event ) { if ( event.shift ) { this.viewport.zoomBy(0.9); } else { - this.viewport.panBy(new $.Point(0, 0.05)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, 40))); } this.viewport.applyConstraints(); return false; case 37://left arrow - this.viewport.panBy(new $.Point(-0.05, 0)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40, 0))); this.viewport.applyConstraints(); return false; case 39://right arrow - this.viewport.panBy(new $.Point(0.05, 0)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40, 0))); this.viewport.applyConstraints(); return false; default: @@ -2265,7 +2265,7 @@ function onCanvasKeyPress( event ) { if ( event.shift ) { this.viewport.zoomBy(1.1); } else { - this.viewport.panBy(new $.Point(0, -0.05)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, -40))); } this.viewport.applyConstraints(); return false; @@ -2274,16 +2274,16 @@ function onCanvasKeyPress( event ) { if ( event.shift ) { this.viewport.zoomBy(0.9); } else { - this.viewport.panBy(new $.Point(0, 0.05)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0, 40))); } this.viewport.applyConstraints(); return false; case 97://a - this.viewport.panBy(new $.Point(-0.05, 0)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40, 0))); this.viewport.applyConstraints(); return false; case 100://d - this.viewport.panBy(new $.Point(0.05, 0)); + this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40, 0))); this.viewport.applyConstraints(); return false; default: From f5ede7001888402c3301440a79859ab93f0d43a1 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 9 May 2015 12:49:32 -0400 Subject: [PATCH 033/104] Fix coordinates demo. --- test/demo/coordinates.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/demo/coordinates.html b/test/demo/coordinates.html index be8cbb4c..c240120a 100644 --- a/test/demo/coordinates.html +++ b/test/demo/coordinates.html @@ -50,9 +50,9 @@ return point.x.toPrecision(4) + "," + point.y.toPrecision(4); } - var onMouseTrackerMove = function(eventSender, eventData) { - var viewerX = eventData.position.x; - var viewerY = eventData.position.y; + var onMouseTrackerMove = function(event) { + var viewerX = event.position.x; + var viewerY = event.position.y; var windowPoint = new OpenSeadragon.Point(viewerX, viewerY); $("#windowPosition").text( pointToString(windowPoint)); From dd782a427e78a0c30e111f8b6314d690f37037bf Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 16 May 2015 10:36:00 -0400 Subject: [PATCH 034/104] Add documentation on pre-full-screen event. Fix #487 --- src/viewer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/viewer.js b/src/viewer.js index c8de84f3..7143443d 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1072,6 +1072,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }; /** * Raised when the viewer is about to change to/from full-screen mode (see {@link OpenSeadragon.Viewer#setFullScreen}). + * Note: the pre-full-screen event is not raised when the user is exiting + * full-screen mode by pressing the Esc key. In that case, consider using + * the full-screen, pre-full-page or full-page events. * * @event pre-full-screen * @memberof OpenSeadragon.Viewer From 99aa1b98f921f40abaccc1ad93abb8450d90bb9f Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 26 May 2015 16:26:07 -0700 Subject: [PATCH 035/104] v2.0.0 --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 9b79f56a..dc7166b7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,7 @@ OPENSEADRAGON CHANGELOG ======================= -1.3.0: (in progress) +2.0.0: * True multi-image mode (#450) * BREAKING CHANGE: Passing an array for the tileSources option is no longer enough to trigger sequence mode; you have to set the sequenceMode option to true as well From 472ab42be772569b2f2bb92ac3fd73b9c52e4b42 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 26 May 2015 16:31:13 -0700 Subject: [PATCH 036/104] updated package.json for 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 99e60e8a..c979f4b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "OpenSeadragon", - "version": "1.2.1", + "version": "2.0.0", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "devDependencies": { "grunt": "^0.4.5", From 300cf6e7779f6a5da49add83938adb81a1d779ad Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 26 May 2015 16:34:56 -0700 Subject: [PATCH 037/104] in progress for changelog --- changelog.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.txt b/changelog.txt index dc7166b7..986c73f0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,8 @@ OPENSEADRAGON CHANGELOG ======================= +2.0.1: (in progress) + 2.0.0: * True multi-image mode (#450) From 2538f2023cc8be3f433573c7e0058cae4b48974b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Fri, 29 May 2015 11:49:58 -0400 Subject: [PATCH 038/104] Move tile caching code inside tilecache.js. --- src/tile.js | 34 +++++++++++++++------------------- src/tilecache.js | 18 ++++++++++++------ src/tiledimage.js | 5 +++-- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/tile.js b/src/tile.js index 3fe642df..3ad71ad3 100644 --- a/src/tile.js +++ b/src/tile.js @@ -190,7 +190,14 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ * @param {Element} container */ drawHTML: function( container ) { - if ( !this.loaded || !this.image ) { + if (!this.cacheImageRecord) { + $.console.warn( + '[Tile.drawHTML] attempting to draw tile %s when it\'s not cached', + this.toString()); + return; + } + + if ( !this.loaded ) { $.console.warn( "Attempting to draw tile %s when it's not yet loaded.", this.toString() @@ -239,17 +246,18 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ var position = this.position, size = this.size, - rendered, - canvas; + rendered; if (!this.cacheImageRecord) { - $.console.warn('[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached', this.toString()); + $.console.warn( + '[Tile.drawCanvas] attempting to draw tile %s when it\'s not cached', + this.toString()); return; } rendered = this.cacheImageRecord.getRenderedContext(); - if ( !this.loaded || !( this.image || rendered) ){ + if ( !this.loaded || !rendered ){ $.console.warn( "Attempting to draw tile %s when it's not yet loaded.", this.toString() @@ -276,19 +284,8 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ } - if(!rendered){ - canvas = document.createElement( 'canvas' ); - canvas.width = this.image.width; - canvas.height = this.image.height; - rendered = canvas.getContext('2d'); - rendered.drawImage( this.image, 0, 0 ); - this.cacheImageRecord.setRenderedContext(rendered); - //since we are caching the prerendered image on a canvas - //allow the image to not be held in memory - this.image = null; - } - - // This gives the application a chance to make image manipulation changes as we are rendering the image + // This gives the application a chance to make image manipulation + // changes as we are rendering the image drawingHandler({context: context, tile: this, rendered: rendered}); context.drawImage( @@ -318,7 +315,6 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ this.element = null; this.imgElement = null; - this.image = null; this.loaded = false; this.loading = false; } diff --git a/src/tilecache.js b/src/tilecache.js index 45344a05..297d4081 100644 --- a/src/tilecache.js +++ b/src/tilecache.js @@ -63,13 +63,19 @@ ImageRecord.prototype = { }, getRenderedContext: function() { + if (!this._renderedContext) { + var canvas = document.createElement( 'canvas' ); + canvas.width = this._image.width; + canvas.height = this._image.height; + this._renderedContext = canvas.getContext('2d'); + this._renderedContext.drawImage( this._image, 0, 0 ); + //since we are caching the prerendered image on a canvas + //allow the image to not be held in memory + this._image = null; + } return this._renderedContext; }, - setRenderedContext: function(renderedContext) { - this._renderedContext = renderedContext; - }, - addTile: function(tile) { $.console.assert(tile, '[ImageRecord.addTile] tile is required'); this._tiles.push(tile); @@ -135,7 +141,7 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{ $.console.assert( options, "[TileCache.cacheTile] options is required" ); $.console.assert( options.tile, "[TileCache.cacheTile] options.tile is required" ); $.console.assert( options.tile.url, "[TileCache.cacheTile] options.tile.url is required" ); - $.console.assert( options.tile.image, "[TileCache.cacheTile] options.tile.image is required" ); + $.console.assert( options.image, "[TileCache.cacheTile] options.image is required" ); $.console.assert( options.tiledImage, "[TileCache.cacheTile] options.tiledImage is required" ); var cutoff = options.cutoff || 0; @@ -144,7 +150,7 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{ var imageRecord = this._imagesLoaded[options.tile.url]; if (!imageRecord) { imageRecord = this._imagesLoaded[options.tile.url] = new ImageRecord({ - image: options.tile.image + image: options.image }); this._imagesLoadedCount++; diff --git a/src/tiledimage.js b/src/tiledimage.js index faa49cc9..4fcbab9e 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -967,10 +967,11 @@ function onTileLoad( tiledImage, tile, time, image ) { var finish = function() { tile.loading = false; tile.loaded = true; - tile.image = image; - var cutoff = Math.ceil( Math.log( tiledImage.source.getTileSize(tile.level) ) / Math.log( 2 ) ); + var cutoff = Math.ceil( Math.log( + tiledImage.source.getTileSize(tile.level) ) / Math.log( 2 ) ); tiledImage._tileCache.cacheTile({ + image: image, tile: tile, cutoff: cutoff, tiledImage: tiledImage From eda47e6fc05037781b2397d3810f5091e12abc9c Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Fri, 29 May 2015 15:10:47 -0400 Subject: [PATCH 039/104] Add tile-loaded event. --- src/tiledimage.js | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 4fcbab9e..4e68a3ab 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -715,8 +715,6 @@ function updateViewport( tiledImage ) { // Load the new 'best' tile if ( best ) { loadTile( tiledImage, best, currentTime ); - // because we haven't finished drawing, so - tiledImage._needsDraw = true; } } @@ -863,12 +861,8 @@ function updateTile( tiledImage, drawLevel, haveDrawn, x, y, level, levelOpacity var imageRecord = tiledImage._tileCache.getImageRecord(tile.url); if (imageRecord) { tile.loaded = true; - tile.image = imageRecord.getImage(); - - tiledImage._tileCache.cacheTile({ - tile: tile, - tiledImage: tiledImage - }); + var image = imageRecord.getImage(); + setTileLoaded(tiledImage, tile, image); } } @@ -965,17 +959,9 @@ function onTileLoad( tiledImage, tile, time, image ) { } var finish = function() { - tile.loading = false; - tile.loaded = true; - var cutoff = Math.ceil( Math.log( tiledImage.source.getTileSize(tile.level) ) / Math.log( 2 ) ); - tiledImage._tileCache.cacheTile({ - image: image, - tile: tile, - cutoff: cutoff, - tiledImage: tiledImage - }); + setTileLoaded(tiledImage, tile, image, cutoff); }; // Check if we're mid-update; this can happen on IE8 because image load events for @@ -990,6 +976,21 @@ function onTileLoad( tiledImage, tile, time, image ) { tiledImage._needsDraw = true; } +function setTileLoaded(tiledImage, tile, image, cutoff) { + tiledImage.viewer.raiseEvent("tile-loaded", { + tile: tile, + tiledImage: tiledImage, + image: image + }); + tile.loading = false; + tile.loaded = true; + tiledImage._tileCache.cacheTile({ + image: image, + tile: tile, + cutoff: cutoff, + tiledImage: tiledImage + }); +} function positionTile( tile, overlap, viewport, viewportCenter, levelVisibility, tiledImage ){ var boundsTL = tile.bounds.getTopLeft(); From 4c1d0f9a4cfc5309f9502f0696ab113a2aceefd6 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 2 Jun 2015 11:35:59 -0400 Subject: [PATCH 040/104] Fix Ian's comments. --- changelog.txt | 2 ++ src/tilecache.js | 8 ++++++++ src/tiledimage.js | 1 - 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 986c73f0..ec2ad4c0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,8 @@ OPENSEADRAGON CHANGELOG ======================= 2.0.1: (in progress) + * BREAKING CHANGE: the tile does not hold a reference to its image anymore. Only the tile cache keep a reference to images. + * DEPRECATION: let ImageRecord.getRenderedContext create the rendered context instead of using ImageRecord.setRenderedContext. 2.0.0: diff --git a/src/tilecache.js b/src/tilecache.js index 297d4081..ee8176ae 100644 --- a/src/tilecache.js +++ b/src/tilecache.js @@ -76,6 +76,13 @@ ImageRecord.prototype = { return this._renderedContext; }, + setRenderedContext: function(renderedContext) { + $.console.error("ImageRecord.setRenderedContext is deprecated. " + + "The rendered context should be created by the ImageRecord " + + "itself when calling ImageRecord.getRenderedContext."); + this._renderedContext = renderedContext; + }, + addTile: function(tile) { $.console.assert(tile, '[ImageRecord.addTile] tile is required'); this._tiles.push(tile); @@ -132,6 +139,7 @@ $.TileCache.prototype = /** @lends OpenSeadragon.TileCache.prototype */{ * may temporarily surpass that number, but should eventually come back down to the max specified. * @param {Object} options - Tile info. * @param {OpenSeadragon.Tile} options.tile - The tile to cache. + * @param {Image} options.image - The image of the tile to cache. * @param {OpenSeadragon.TiledImage} options.tiledImage - The TiledImage that owns that tile. * @param {Number} [options.cutoff=0] - If adding this tile goes over the cache max count, this * function will release an old tile. The cutoff option specifies a tile level at or below which diff --git a/src/tiledimage.js b/src/tiledimage.js index 4e68a3ab..e871fe42 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -860,7 +860,6 @@ function updateTile( tiledImage, drawLevel, haveDrawn, x, y, level, levelOpacity if (!tile.loaded) { var imageRecord = tiledImage._tileCache.getImageRecord(tile.url); if (imageRecord) { - tile.loaded = true; var image = imageRecord.getImage(); setTileLoaded(tiledImage, tile, image); } From be657c0b3d16315caf288c3fc9d33a5df37a9178 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 2 Jun 2015 12:59:38 -0400 Subject: [PATCH 041/104] Add getCompletionCallback method. --- src/tiledimage.js | 48 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index e871fe42..195d00d0 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -976,19 +976,51 @@ function onTileLoad( tiledImage, tile, time, image ) { } function setTileLoaded(tiledImage, tile, image, cutoff) { + var increment = 0; + + function getCompletionCallback() { + increment++; + return completionCallback; + } + + function completionCallback() { + increment--; + if (increment === 0) { + tile.loading = false; + tile.loaded = true; + tiledImage._tileCache.cacheTile({ + image: image, + tile: tile, + cutoff: cutoff, + tiledImage: tiledImage + }); + } + } + + /** + * Triggered when a tile has just been loaded in memory. That means that the + * image has been downloaded and can be modified asynchronously 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 {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 once the callback has been called as many times as + * getCompletionCallback + */ tiledImage.viewer.raiseEvent("tile-loaded", { tile: tile, tiledImage: tiledImage, - image: image - }); - tile.loading = false; - tile.loaded = true; - tiledImage._tileCache.cacheTile({ image: image, - tile: tile, - cutoff: cutoff, - tiledImage: tiledImage + getCompletionCallback: getCompletionCallback }); + // In case the completion callback is never called, we at least force it once. + getCompletionCallback()(); } function positionTile( tile, overlap, viewport, viewportCenter, levelVisibility, tiledImage ){ From aa021d87c07df57604d840291542d6a35e11e236 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 2 Jun 2015 16:41:37 -0400 Subject: [PATCH 042/104] Add coordinates conversion methods to TiledImage. --- src/tiledimage.js | 77 ++++++++++++++++++++++++++++++++++++++ test/demo/coordinates.html | 71 +++++++++++++++++++++++++---------- 2 files changed, 128 insertions(+), 20 deletions(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index faa49cc9..f49b40e1 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -406,6 +406,83 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag ); }, + /** + * Convert pixel coordinates relative to the viewer element to image + * coordinates. + * @param {OpenSeadragon.Point} pixel + * @returns {OpenSeadragon.Point} + */ + viewerElementToImageCoordinates: function( pixel ) { + var point = this.viewport.pointFromPixel( pixel, true ); + return this.viewportToImageCoordinates( point ); + }, + + /** + * Convert pixel coordinates relative to the image to + * viewer element coordinates. + * @param {OpenSeadragon.Point} pixel + * @returns {OpenSeadragon.Point} + */ + imageToViewerElementCoordinates: function( pixel ) { + var point = this.imageToViewportCoordinates( pixel ); + return this.viewport.pixelFromPoint( point, true ); + }, + + /** + * Convert pixel coordinates relative to the window to image coordinates. + * @param {OpenSeadragon.Point} pixel + * @returns {OpenSeadragon.Point} + */ + windowToImageCoordinates: function( pixel ) { + var viewerCoordinates = pixel.minus( + OpenSeadragon.getElementPosition( this.viewer.element )); + return this.viewerElementToImageCoordinates( viewerCoordinates ); + }, + + /** + * Convert image coordinates to pixel coordinates relative to the window. + * @param {OpenSeadragon.Point} pixel + * @returns {OpenSeadragon.Point} + */ + imageToWindowCoordinates: function( pixel ) { + var viewerCoordinates = this.imageToViewerElementCoordinates( pixel ); + return viewerCoordinates.plus( + OpenSeadragon.getElementPosition( this.viewer.element )); + }, + + /** + * Convert a viewport zoom to an image zoom. + * Image zoom: ratio of the original image size to displayed image size. + * 1 means original image size, 0.5 half size... + * Viewport zoom: ratio of the displayed image's width to viewport's width. + * 1 means identical width, 2 means image's width is twice the viewport's width... + * @function + * @param {Number} viewportZoom The viewport zoom + * @returns {Number} imageZoom The image zoom + */ + viewportToImageZoom: function( viewportZoom ) { + var ratio = this._scaleSpring.current.value * + this.viewport._containerInnerSize.x / this.source.dimensions.x; + return ratio * viewportZoom ; + }, + + /** + * Convert an image zoom to a viewport zoom. + * Image zoom: ratio of the original image size to displayed image size. + * 1 means original image size, 0.5 half size... + * Viewport zoom: ratio of the displayed image's width to viewport's width. + * 1 means identical width, 2 means image's width is twice the viewport's width... + * Note: not accurate with multi-image. + * @function + * @param {Number} imageZoom The image zoom + * @returns {Number} viewportZoom The viewport zoom + */ + imageToViewportZoom: function( imageZoom ) { + var ratio = this._scaleSpring.current.value * + this.viewport._containerInnerSize.x / this.source.dimensions.x; + return imageZoom / ratio; + }, + /** * Sets the TiledImage's position in the world. * @param {OpenSeadragon.Point} position - The new position, in viewport coordinates. diff --git a/test/demo/coordinates.html b/test/demo/coordinates.html index c240120a..188e87ee 100644 --- a/test/demo/coordinates.html +++ b/test/demo/coordinates.html @@ -1,21 +1,21 @@ - OpenSeadragon Basic Demo + OpenSeadragon Coordinates Demo
- Simple demo page to show a default OpenSeadragon viewer. + Simple demo page to show OpenSeadragon coordinates system.
@@ -24,16 +24,26 @@ Window (pixel) Container (pixel) - Image (pixel) + Image 1 - top left (pixel) + Image 2 - bottom right (pixel) Viewport (point) Cursor position - + + + + Zoom + - + - + + + +
From 2674104007b5b1054d323de61d07d559b2583afc Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 3 Jun 2015 13:52:19 -0400 Subject: [PATCH 043/104] Fix flickering tiles with useCanvas=false when no cache is used. Fix #661 --- changelog.txt | 1 + src/tile.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index ec2ad4c0..e5b00e19 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ OPENSEADRAGON CHANGELOG 2.0.1: (in progress) * BREAKING CHANGE: the tile does not hold a reference to its image anymore. Only the tile cache keep a reference to images. * DEPRECATION: let ImageRecord.getRenderedContext create the rendered context instead of using ImageRecord.setRenderedContext. +* Fix flickering tiles with useCanvas=false when no cache is used. (#661) 2.0.0: diff --git a/src/tile.js b/src/tile.js index 3ad71ad3..48f3503c 100644 --- a/src/tile.js +++ b/src/tile.js @@ -210,8 +210,7 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ if ( !this.element ) { this.element = $.makeNeutralElement( "div" ); - this.imgElement = $.makeNeutralElement( "img" ); - this.imgElement.src = this.url; + this.imgElement = this.cacheImageRecord.getImage().cloneNode(); this.imgElement.style.msInterpolationMode = "nearest-neighbor"; this.imgElement.style.width = "100%"; this.imgElement.style.height = "100%"; From d5c431b1cd0b77522720d366efd177ab09b4497d Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Wed, 3 Jun 2015 14:40:20 -0400 Subject: [PATCH 044/104] Integrate Ian's comments. --- changelog.txt | 5 +++-- src/tiledimage.js | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/changelog.txt b/changelog.txt index e5b00e19..9c784bdd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,8 +2,9 @@ OPENSEADRAGON CHANGELOG ======================= 2.0.1: (in progress) - * BREAKING CHANGE: the tile does not hold a reference to its image anymore. Only the tile cache keep a reference to images. - * DEPRECATION: let ImageRecord.getRenderedContext create the rendered context instead of using ImageRecord.setRenderedContext. +* BREAKING CHANGE: the tile does not hold a reference to its image anymore. Only the tile cache keep a reference to images. +* DEPRECATION: let ImageRecord.getRenderedContext create the rendered context instead of using ImageRecord.setRenderedContext. +* Added "tile-loaded" event on the viewer allowing to modify a tile before it is marked ready to be drawn. (#659) * Fix flickering tiles with useCanvas=false when no cache is used. (#661) 2.0.0: diff --git a/src/tiledimage.js b/src/tiledimage.js index 195d00d0..b843ca78 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -971,8 +971,6 @@ function onTileLoad( tiledImage, tile, time, image ) { // Wait until after the update, in case caching unloads any tiles window.setTimeout( finish, 1); } - - tiledImage._needsDraw = true; } function setTileLoaded(tiledImage, tile, image, cutoff) { @@ -994,13 +992,13 @@ function setTileLoaded(tiledImage, tile, image, cutoff) { 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 asynchronously before being - * drawn to the canvas. + * image has been downloaded and can be modified before being drawn to the canvas. * * @event tile-loaded * @memberof OpenSeadragon.Viewer @@ -1010,8 +1008,8 @@ function setTileLoaded(tiledImage, tile, image, cutoff) { * @property {OpenSeadragon.Tile} tile - The tile which has been loaded. * @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 once the callback has been called as many times as - * getCompletionCallback + * marked as .entirely loaded when the callback has been called once for each + * call to getCompletionCallback. */ tiledImage.viewer.raiseEvent("tile-loaded", { tile: tile, From 7e39a528c03708bcf762ab55069937b7f00bf44f Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 4 Jun 2015 09:47:50 -0400 Subject: [PATCH 045/104] Add tests for tile-loaded event. --- test/modules/events.js | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/modules/events.js b/test/modules/events.js index 677e805f..0c5e5df9 100644 --- a/test/modules/events.js +++ b/test/modules/events.js @@ -968,4 +968,76 @@ viewer.open( '/test/data/testpattern.dzi' ); } ); + // tile-loaded event tests + asyncTest( 'Viewer: tile-loaded event without callback.', function () { + + function tileLoaded ( event ) { + viewer.removeHandler( 'tile-loaded', tileLoaded); + var tile = event.tile; + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + setTimeout(function() { + notOk( tile.loading, "The tile should not be marked as loading."); + ok( tile.loaded, "The tile should not be marked as loaded."); + start(); + }, 0); + } + + viewer.addHandler( 'tile-loaded', tileLoaded); + viewer.open( '/test/data/testpattern.dzi' ); + } ); + + asyncTest( 'Viewer: tile-loaded event with 1 callback.', function () { + + function tileLoaded ( event ) { + viewer.removeHandler( 'tile-loaded', tileLoaded); + var tile = event.tile; + var callback = event.getCompletionCallback(); + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + ok( callback, "The event should have a callback."); + setTimeout(function() { + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + callback(); + notOk( tile.loading, "The tile should not be marked as loading."); + ok( tile.loaded, "The tile should not be marked as loaded."); + start(); + }, 0); + } + + viewer.addHandler( 'tile-loaded', tileLoaded); + viewer.open( '/test/data/testpattern.dzi' ); + } ); + + asyncTest( 'Viewer: tile-loaded event with 2 callbacks.', function () { + + function tileLoaded ( event ) { + viewer.removeHandler( 'tile-loaded', tileLoaded); + var tile = event.tile; + var callback1 = event.getCompletionCallback(); + var callback2 = event.getCompletionCallback(); + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + setTimeout(function() { + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + callback1(); + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + setTimeout(function() { + ok( tile.loading, "The tile should be marked as loading."); + notOk( tile.loaded, "The tile should not be marked as loaded."); + callback2(); + notOk( tile.loading, "The tile should not be marked as loading."); + ok( tile.loaded, "The tile should not be marked as loaded."); + start(); + }, 0); + }, 0); + } + + viewer.addHandler( 'tile-loaded', tileLoaded); + viewer.open( '/test/data/testpattern.dzi' ); + } ); + } )(); From b9a6b7910e743ccf87e2b49f8e208aad371d0ef3 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 4 Jun 2015 15:32:10 -0400 Subject: [PATCH 046/104] Add unit tests. --- test/modules/units.js | 200 +++++++++++++++++++++++++++++++++++------- 1 file changed, 169 insertions(+), 31 deletions(-) diff --git a/test/modules/units.js b/test/modules/units.js index 1ce10a09..eada67b8 100644 --- a/test/modules/units.js +++ b/test/modules/units.js @@ -1,6 +1,6 @@ /* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */ -(function() { +(function () { var viewer; module('Units', { @@ -10,8 +10,8 @@ testLog.reset(); viewer = OpenSeadragon({ - id: 'unitsexample', - prefixUrl: '/build/openseadragon/images/', + id: 'unitsexample', + prefixUrl: '/build/openseadragon/images/', springStiffness: 100 // Faster animation = faster tests }); }, @@ -30,38 +30,57 @@ Util.assessNumericValue(a.y, b.y, 0.00000001, message); } - // ---------- - asyncTest('Coordinates conversions', function() { + // Check that f^-1 ( f(x) ) = x + function checkPoint(context) { + var viewport = viewer.viewport; - function checkPoint(context) { - var viewport = viewer.viewport; - - var point = new OpenSeadragon.Point(15, 12); - var result = viewport.viewerElementToImageCoordinates( + var point = new OpenSeadragon.Point(15, 12); + var result = viewport.viewerElementToImageCoordinates( viewport.imageToViewerElementCoordinates(point)); - pointEqual(result, point, 'viewerElement and image ' + context); + pointEqual(result, point, 'viewerElement and image ' + context); - var result = viewport.windowToImageCoordinates( + result = viewport.windowToImageCoordinates( viewport.imageToWindowCoordinates(point)); - pointEqual(result, point, 'window and image ' + context); + pointEqual(result, point, 'window and image ' + context); - var result = viewport.viewerElementToViewportCoordinates( + result = viewport.viewerElementToViewportCoordinates( viewport.viewportToViewerElementCoordinates(point)); - pointEqual(result, point, 'viewerElement and viewport ' + context); + pointEqual(result, point, 'viewerElement and viewport ' + context); - var result = viewport.windowToViewportCoordinates( + result = viewport.windowToViewportCoordinates( viewport.viewportToWindowCoordinates(point)); - pointEqual(result, point, 'window and viewport ' + context); + pointEqual(result, point, 'window and viewport ' + context); + + for (var i = 0; i < viewer.world.getItemCount(); i++) { + var tiledImage = viewer.world.getItemAt(i); + result = tiledImage.viewportToImageCoordinates( + tiledImage.imageToViewportCoordinates(point)); + pointEqual(result, point, 'viewport and tiled image ' + i + context); + + result = tiledImage.viewerElementToImageCoordinates( + tiledImage.imageToViewerElementCoordinates(point)); + pointEqual(result, point, 'viewerElement and tiled image ' + i + context); + + result = tiledImage.windowToImageCoordinates( + tiledImage.imageToWindowCoordinates(point)); + pointEqual(result, point, 'window and tiled image ' + i + context); } + } + + // ---------- + asyncTest('Single image coordinates conversions', function () { viewer.addHandler("open", function () { var viewport = viewer.viewport; + var tiledImage = viewer.world.getItemAt(0); var point0_0 = new OpenSeadragon.Point(0, 0); var point = viewport.viewerElementToViewportCoordinates(point0_0); pointEqual(point, point0_0, 'When opening, viewer coordinate 0,0 is also point 0,0'); - var pixel = viewport.viewerElementToImageCoordinates(point0_0); - pointEqual(pixel, point0_0, 'When opening, viewer coordinate 0,0 is also pixel 0,0'); + var viewportPixel = viewport.viewerElementToImageCoordinates(point0_0); + pointEqual(viewportPixel, point0_0, 'When opening, viewer coordinate 0,0 is also viewport pixel 0,0'); + var imagePixel = tiledImage.viewerElementToImageCoordinates(point0_0); + pointEqual(imagePixel, point0_0, 'When opening, viewer coordinate 0,0 is also image pixel 0,0'); var viewerWidth = $(viewer.element).width(); var imageWidth = viewer.source.dimensions.x; @@ -69,15 +88,17 @@ var viewerTopRight = new OpenSeadragon.Point(viewerWidth, 0); var imageTopRight = new OpenSeadragon.Point(imageWidth, 0); - var point = viewport.viewerElementToViewportCoordinates(viewerTopRight); + point = viewport.viewerElementToViewportCoordinates(viewerTopRight); pointEqual(point, point1_0, 'Viewer top right has viewport coordinates 1,0.'); - var pixel = viewport.viewerElementToImageCoordinates(viewerTopRight); - pointEqual(pixel, imageTopRight, 'Viewer top right has viewport coordinates imageWidth,0.'); + viewportPixel = viewport.viewerElementToImageCoordinates(viewerTopRight); + pointEqual(viewportPixel, imageTopRight, 'Viewer top right has viewport pixel coordinates imageWidth,0.'); + imagePixel = tiledImage.viewerElementToImageCoordinates(viewerTopRight); + pointEqual(imagePixel, imageTopRight, 'Viewer top right has image pixel coordinates imageWidth,0.'); - checkPoint('after opening'); + checkPoint(' after opening'); viewer.addHandler('animation-finish', function animationHandler() { viewer.removeHandler('animation-finish', animationHandler); - checkPoint('after zoom and pan'); + checkPoint(' after zoom and pan'); start(); }); viewer.viewport.zoomTo(0.8).panTo(new OpenSeadragon.Point(0.1, 0.2)); @@ -85,19 +106,80 @@ viewer.open('/test/data/testpattern.dzi'); }); + + // --------- + asyncTest('Multiple images coordinates conversion', function () { + + viewer.addHandler("open", function () { + var viewport = viewer.viewport; + var tiledImage1 = viewer.world.getItemAt(0); + var tiledImage2 = viewer.world.getItemAt(1); + var imageWidth = viewer.source.dimensions.x; + var imageHeight = viewer.source.dimensions.y; + + var point0_0 = new OpenSeadragon.Point(0, 0); + var point = viewport.viewerElementToViewportCoordinates(point0_0); + pointEqual(point, point0_0, 'When opening, viewer coordinate 0,0 is also point 0,0'); + var image1Pixel = tiledImage1.viewerElementToImageCoordinates(point0_0); + pointEqual(image1Pixel, point0_0, 'When opening, viewer coordinate 0,0 is also image 1 pixel 0,0'); + var image2Pixel = tiledImage2.viewerElementToImageCoordinates(point0_0); + pointEqual(image2Pixel, + new OpenSeadragon.Point(-2 * imageWidth, -2 * imageHeight), + 'When opening, viewer coordinates 0,0 is also image 2 pixel -2*imageWidth, -2*imageHeight'); + + var viewerWidth = $(viewer.element).width(); + var viewerHeight = $(viewer.element).height(); + var viewerBottomRight = new OpenSeadragon.Point(viewerWidth, viewerHeight); + + point = viewport.viewerElementToViewportCoordinates(viewerBottomRight); + pointEqual(point, new OpenSeadragon.Point(1.5, 1.5), + 'Viewer bottom right has viewport coordinates 1.5,1.5.'); + image1Pixel = tiledImage1.viewerElementToImageCoordinates(viewerBottomRight); + pointEqual(image1Pixel, + new OpenSeadragon.Point(imageWidth * 1.5, imageHeight * 1.5), + 'Viewer bottom right has image 1 pixel coordinates imageWidth * 1.5, imageHeight * 1.5'); + image2Pixel = tiledImage2.viewerElementToImageCoordinates(viewerBottomRight); + pointEqual(image2Pixel, + new OpenSeadragon.Point(imageWidth, imageHeight), + 'Viewer bottom right has image 2 pixel coordinates imageWidth,imageHeight.'); + + + checkPoint(' after opening'); + viewer.addHandler('animation-finish', function animationHandler() { + viewer.removeHandler('animation-finish', animationHandler); + checkPoint(' after zoom and pan'); + start(); + }); + viewer.viewport.zoomTo(0.8).panTo(new OpenSeadragon.Point(0.1, 0.2)); + + start(); + }); + + viewer.open([{ + tileSource: "/test/data/testpattern.dzi" + }, { + tileSource: "/test/data/testpattern.dzi", + x: 1, + y: 1, + width: 0.5 + } + ]); + }); + + // ---------- - asyncTest('ZoomRatio', function() { + asyncTest('ZoomRatio 1 image', function () { viewer.addHandler("open", function () { var viewport = viewer.viewport; - var imageWidth = 1000; + var imageWidth = viewer.source.dimensions.x; function getCurrentImageWidth() { return viewport.viewportToViewerElementCoordinates( - new OpenSeadragon.Point(1, 0)).minus( + new OpenSeadragon.Point(1, 0)).minus( viewport.viewportToViewerElementCoordinates( - new OpenSeadragon.Point(0, 0))).x; + new OpenSeadragon.Point(0, 0))).x; } function checkZoom() { @@ -105,16 +187,16 @@ var expectedImageZoom = currentImageWidth / imageWidth; var expectedViewportZoom = viewport.getZoom(true); var actualImageZoom = viewport.viewportToImageZoom( - expectedViewportZoom); + expectedViewportZoom); equal(actualImageZoom, expectedImageZoom); - + var actualViewportZoom = viewport.imageToViewportZoom(actualImageZoom); equal(actualViewportZoom, expectedViewportZoom); } checkZoom(); - var zoomHandler = function() { + var zoomHandler = function () { viewer.removeHandler('animation-finish', zoomHandler); checkZoom(); start(); @@ -127,5 +209,61 @@ viewer.open('/test/data/testpattern.dzi'); }); + // ---------- + asyncTest('ZoomRatio 2 images', function () { + viewer.addHandler("open", function () { + + var viewport = viewer.viewport; + + var imageWidth = viewer.source.dimensions.x; + var image1 = viewer.world.getItemAt(0); + var image2 = viewer.world.getItemAt(1); + + function getCurrentImageWidth(image) { + var bounds = image.getBounds(); + return viewport.viewportToViewerElementCoordinates( + bounds.getTopRight()).minus( + viewport.viewportToViewerElementCoordinates( + bounds.getTopLeft())).x; + } + + function checkZoom(image) { + var currentImageWidth = getCurrentImageWidth(image); + var expectedImageZoom = currentImageWidth / imageWidth; + var expectedViewportZoom = viewport.getZoom(true); + var actualImageZoom = image.viewportToImageZoom( + expectedViewportZoom); + Util.assessNumericValue(actualImageZoom, expectedImageZoom, + 0.00000001); + + var actualViewportImage1Zoom = image.imageToViewportZoom(actualImageZoom); + Util.assessNumericValue( + actualViewportImage1Zoom, expectedViewportZoom, 0.00000001); + } + + checkZoom(image1); + checkZoom(image2); + + var zoomHandler = function () { + viewer.removeHandler('animation-finish', zoomHandler); + checkZoom(image1); + checkZoom(image2); + start(); + }; + + viewer.addHandler('animation-finish', zoomHandler); + viewport.zoomTo(2); + }); + + viewer.open([{ + tileSource: "/test/data/testpattern.dzi" + }, { + tileSource: "/test/data/testpattern.dzi", + x: 1, + y: 1, + width: 0.5 + } + ]); + }); })(); From 606d25dbe62b167b359a0939a465ef1728b02b8e Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Mon, 8 Jun 2015 10:38:16 -0400 Subject: [PATCH 047/104] Merge upstream. --- src/buttongroup.js | 2 +- src/iiiftilesource.js | 2 +- src/mousetracker.js | 24 +++++++++---------- .../css/smoothness/jquery-ui-1.10.2.min.css | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/buttongroup.js b/src/buttongroup.js index 49cc9c2e..77d61371 100644 --- a/src/buttongroup.js +++ b/src/buttongroup.js @@ -68,7 +68,7 @@ $.ButtonGroup = function( options ) { */ this.element = options.element || $.makeNeutralElement( "div" ); - // TODO What if there IS an options.group specified? + // TODO What if there IS an options.group specified? if( !options.group ){ this.label = $.makeNeutralElement( "label" ); //TODO: support labels for ButtonGroups diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index ca755b9f..484b7092 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -94,7 +94,7 @@ $.IIIFTileSource = function( options ){ // If we're smaller than 256, just use the short side. options.tileSize = shortDim; } - this.tile_width = options.tileSize; // So that 'full' gets used for + this.tile_width = options.tileSize; // So that 'full' gets used for this.tile_height = options.tileSize; // the region below } diff --git a/src/mousetracker.js b/src/mousetracker.js index ec677fb7..e9b5acfc 100644 --- a/src/mousetracker.js +++ b/src/mousetracker.js @@ -133,7 +133,7 @@ */ this.element = $.getElement( options.element ); /** - * The number of milliseconds within which a pointer down-up event combination + * The number of milliseconds within which a pointer down-up event combination * will be treated as a click gesture. * @member {Number} clickTimeThreshold * @memberof OpenSeadragon.MouseTracker# @@ -244,7 +244,7 @@ // Active pointers lists. Array of GesturePointList objects, one for each pointer device type. // GesturePointList objects are added each time a pointer is tracked by a new pointer device type (see getActivePointersListByType()). - // Active pointers are any pointer being tracked for this element which are in the hit-test area + // Active pointers are any pointer being tracked for this element which are in the hit-test area // of the element (for hover-capable devices) and/or have contact or a button press initiated in the element. activePointersLists: [], @@ -1032,7 +1032,7 @@ $.MouseTracker.mousePointerId = "legacy-mouse"; $.MouseTracker.maxTouchPoints = 10; } - + /////////////////////////////////////////////////////////////////////////////// // Classes and typedefs @@ -1078,7 +1078,7 @@ /** * @class GesturePointList * @classdesc Provides an abstraction for a set of active {@link OpenSeadragon.MouseTracker.GesturePoint|GesturePoint} objects for a given pointer device type. - * Active pointers are any pointer being tracked for this element which are in the hit-test area + * Active pointers are any pointer being tracked for this element which are in the hit-test area * of the element (for hover-capable devices) and/or have contact or a button press initiated in the element. * @memberof OpenSeadragon.MouseTracker * @param {String} type - The pointer device type: "mouse", "touch", "pen", etc. @@ -1198,7 +1198,7 @@ return null; } }; - + /////////////////////////////////////////////////////////////////////////////// // Utility functions @@ -1282,7 +1282,7 @@ false ); } - + clearTrackedPointers( tracker ); delegate.tracking = true; @@ -1694,7 +1694,7 @@ /** - * Handles 'wheel' events. + * Handles 'wheel' events. * The event may be simulated by the legacy mouse wheel event handler (onMouseWheel()). * * @private @@ -1943,7 +1943,7 @@ handleMouseMove( tracker, event ); } - + /** * This handler is attached to the window object (on the capture phase) to emulate mouse capture. * onMouseMove is still attached to the tracked element, so stop propagation to avoid processing twice. @@ -2191,7 +2191,7 @@ var i, touchCount = event.changedTouches.length, gPoints = []; - + for ( i = 0; i < touchCount; i++ ) { gPoints.push( { id: event.changedTouches[ i ].identifier, @@ -2420,7 +2420,7 @@ */ function startTrackingPointer( pointsList, gPoint ) { - // If isPrimary is not known for the pointer then set it according to our rules: + // If isPrimary is not known for the pointer then set it according to our rules: // true if the first pointer in the gesture, otherwise false if ( !gPoint.hasOwnProperty( 'isPrimary' ) ) { if ( pointsList.getLength() === 0 ) { @@ -2617,7 +2617,7 @@ * Gesture points associated with the event. * @param {Number} buttonChanged * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser. - * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model, + * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model, * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events. * * @returns {Boolean} True if pointers should be captured to the tracked element, otherwise false. @@ -2779,7 +2779,7 @@ * Gesture points associated with the event. * @param {Number} buttonChanged * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser. - * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model, + * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model, * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events. * * @returns {Boolean} True if pointer capture should be released from the tracked element, otherwise false. diff --git a/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css b/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css index 743ecacd..1319cfcd 100755 --- a/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css +++ b/test/lib/jquery-ui-1.10.2/css/smoothness/jquery-ui-1.10.2.min.css @@ -2,4 +2,4 @@ * http://jqueryui.com * Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css * To view and modify this theme, visit http://jqueryui.com/themeroller/ -* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:21px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:0;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:0}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:400}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:400;margin:-1px}.ui-menu .ui-state-disabled{font-weight:400;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url(images/animated-overlay.gif);height:100%;filter:alpha(opacity=25);opacity:.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted #000}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:0;background:0;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:0;border-bottom:0;border-right:0}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-tabs-loading a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:0}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file +* Copyright 2013 jQuery Foundation and other contributors Licensed MIT */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:21px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:0;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:0}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:400}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:400;margin:-1px}.ui-menu .ui-state-disabled{font-weight:400;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url(images/animated-overlay.gif);height:100%;filter:alpha(opacity=25);opacity:.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted #000}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:0;background:0;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:0;border-bottom:0;border-right:0}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active a,.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-tabs-loading a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:0}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} From fa868a039dc5a1af4b067c62be35eeae6eba5bc1 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Thu, 11 Jun 2015 10:49:32 -0400 Subject: [PATCH 048/104] Fix editorconfig config. --- .editorconfig | 3 ++- test/demo/memorycheck.html | 8 ++++---- test/modules/formats.js | 2 +- test/modules/units.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.editorconfig b/.editorconfig index 6c76c202..61b85e21 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,8 @@ # editorconfig.org root = true -[*] +# We need to specify each folder specifically to avoid including test/lib and test/data +[{Gruntfile.js,src/**,test/*,test/demo/**,test/helpers/**,test/modules/**}] indent_style = space indent_size = 4 end_of_line = lf diff --git a/test/demo/memorycheck.html b/test/demo/memorycheck.html index bebe80da..7a727fb5 100644 --- a/test/demo/memorycheck.html +++ b/test/demo/memorycheck.html @@ -23,19 +23,19 @@ + + + + +
+
+ + + + + From b1c6a75c494228c0c42d632f2bb218e4268089cb Mon Sep 17 00:00:00 2001 From: Rick Smith Date: Tue, 23 Jun 2015 16:04:40 -0600 Subject: [PATCH 059/104] When overlays are hidden, keep them that way Overlays previously had their display set to 'block' each time they were drawn. Now if their display is 'none' their display property is left alone --- src/overlay.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/overlay.js b/src/overlay.js index 0bdbbf34..0dd31ff3 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -282,7 +282,10 @@ style.left = position.x + "px"; style.top = position.y + "px"; style.position = "absolute"; - style.display = 'block'; + + if (style.display != 'none') { + style.display = 'block'; + } if ( scales ) { style.width = size.x + "px"; From d2f1e432cdb0b622e0dba9e7b49ee7b897b7a177 Mon Sep 17 00:00:00 2001 From: Rick Smith Date: Tue, 23 Jun 2015 16:08:18 -0600 Subject: [PATCH 060/104] Use spaces not tabs Phew...flame war averted. ;) --- src/overlay.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/overlay.js b/src/overlay.js index 0dd31ff3..0484ead6 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -283,9 +283,9 @@ style.top = position.y + "px"; style.position = "absolute"; - if (style.display != 'none') { + if (style.display != 'none') { style.display = 'block'; - } + } if ( scales ) { style.width = size.x + "px"; From ad2940d8976b89ae73999ddcb2477434dbb4341d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 24 Jun 2015 09:20:33 -0700 Subject: [PATCH 061/104] Changelog for #668 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index b782c509..d62a3a33 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ OPENSEADRAGON CHANGELOG * Added "tile-unloaded" event on the viewer allowing to free up memory one has allocated on a tile (#659) * Fixed flickering tiles with useCanvas=false when no cache is used (#661) * Added additional coordinates conversion methods to TiledImage (#662) +* 'display: none' no longer gets reset on overlays during draw (#668) 2.0.0: From df878f6f82a77081671eeab50aca9e23437d6e85 Mon Sep 17 00:00:00 2001 From: Grant Echols Date: Wed, 24 Jun 2015 10:48:00 -0600 Subject: [PATCH 062/104] Changed preserveImageSizeOnResize to require autoResize=true. Refactored resizing code to share more code. Removed preserveImageSizeOnResize parameter from viewer. --- src/openseadragon.js | 2 +- src/viewer.js | 46 +++++++++++++++++++------------------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 0c6df401..b7f7f5c1 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -253,7 +253,7 @@ * Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior. * * @property {Boolean} [preserveImageSizeOnResize=false] - * Set to true to have the image size preserved when the viewer is resized. This requires autoResize=false. + * Set to true to have the image size preserved when the viewer is resized. This requires autoResize=true (default). * * @property {Number} [pixelsPerWheelLine=40] * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line. diff --git a/src/viewer.js b/src/viewer.js index 50dd8831..3e318528 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -408,7 +408,6 @@ $.Viewer = function( options ) { width: this.navigatorWidth, height: this.navigatorHeight, autoResize: this.navigatorAutoResize, - preserveImageSizeOnResize: this.preserveImageSizeOnResize, prefixUrl: this.prefixUrl, viewer: this, navigatorRotate: this.navigatorRotate, @@ -2829,32 +2828,27 @@ function updateOnce( viewer ) { if ( viewer.autoResize ) { containerSize = _getSafeElemSize( viewer.container ); if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) { - // maintain image position - var oldBounds = viewer.viewport.getBounds(); - var oldCenter = viewer.viewport.getCenter(); - resizeViewportAndRecenter(viewer, containerSize, oldBounds, oldCenter); - THIS[ viewer.hash ].prevContainerSize = containerSize; - THIS[ viewer.hash ].forceRedraw = true; - } - } - else if ( viewer.preserveImageSizeOnResize ) { - containerSize = _getSafeElemSize( viewer.container ); - if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) { - var prevContainerSize = THIS[ viewer.hash ].prevContainerSize; - var bounds = viewer.viewport.getBounds(true); - var deltaX = (containerSize.x - prevContainerSize.x); - var deltaY = (containerSize.y - prevContainerSize.y); - var viewportDiff = viewer.viewport.deltaPointsFromPixels(new OpenSeadragon.Point(deltaX, deltaY), true); - viewer.viewport.resize(new OpenSeadragon.Point(containerSize.x, containerSize.y), false); - - // Keep the center of the image in the center and just adjust the amount of image shown - bounds.width += viewportDiff.x; - bounds.height += viewportDiff.y; - bounds.x -= (viewportDiff.x / 2); - bounds.y -= (viewportDiff.y / 2); - viewer.viewport.fitBoundsWithConstraints(bounds, true); - viewer.forceRedraw(); + if ( viewer.preserveImageSizeOnResize ) { + var prevContainerSize = THIS[ viewer.hash ].prevContainerSize; + var bounds = viewer.viewport.getBounds(true); + var deltaX = (containerSize.x - prevContainerSize.x); + var deltaY = (containerSize.y - prevContainerSize.y); + var viewportDiff = viewer.viewport.deltaPointsFromPixels(new OpenSeadragon.Point(deltaX, deltaY), true); + viewer.viewport.resize(new OpenSeadragon.Point(containerSize.x, containerSize.y), false); + // Keep the center of the image in the center and just adjust the amount of image shown + bounds.width += viewportDiff.x; + bounds.height += viewportDiff.y; + bounds.x -= (viewportDiff.x / 2); + bounds.y -= (viewportDiff.y / 2); + viewer.viewport.fitBoundsWithConstraints(bounds, true); + } + else { + // maintain image position + var oldBounds = viewer.viewport.getBounds(); + var oldCenter = viewer.viewport.getCenter(); + resizeViewportAndRecenter(viewer, containerSize, oldBounds, oldCenter); + } THIS[ viewer.hash ].prevContainerSize = containerSize; THIS[ viewer.hash ].forceRedraw = true; } From 0806e4d4ed445fbef748c68b62bf9c678e46a4b1 Mon Sep 17 00:00:00 2001 From: Grant Echols Date: Thu, 25 Jun 2015 14:14:05 -0600 Subject: [PATCH 063/104] Fixed comment regarding autoResize. --- src/openseadragon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index b7f7f5c1..e20926bd 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -990,7 +990,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels pixelsPerWheelLine: 40, autoResize: true, - preserveImageSizeOnResize: false, // requires autoResize=false + preserveImageSizeOnResize: false, // requires autoResize=true //DEFAULT CONTROL SETTINGS showSequenceControl: true, //SEQUENCE From 7e950fda2bece4698d5b9080fa1d33a44edc6703 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Fri, 26 Jun 2015 14:17:40 -0400 Subject: [PATCH 064/104] Add support for viewing custom tile sources with non-square tiles --- src/iiiftilesource.js | 9 +++++---- src/tiledimage.js | 2 +- src/tilesource.js | 43 ++++++++++++++++++++++++++++++------------- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index 484b7092..54779158 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -192,7 +192,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { this.tileSize = this.tileSizePerScaleFactor[scaleFactor]; } - return this.tileSize; + return new $.Point(this.tileSize, this.tileSize); }, /** @@ -229,8 +229,9 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea uri; tileSize = this.getTileSize(level); - iiifTileSizeWidth = Math.ceil( tileSize / scale ); - iiifTileSizeHeight = iiifTileSizeWidth; + + iiifTileSizeWidth = Math.ceil( tileSize.x / scale ); + iiifTileSizeHeight = Math.ceil( tileSize.y / scale ); if ( this['@context'].indexOf('/1.0/context.json') > -1 || this['@context'].indexOf('/1.1/context.json') > -1 || @@ -240,7 +241,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea iiifQuality = "default.jpg"; } - if ( levelWidth < tileSize && levelHeight < tileSize ){ + if ( levelWidth < tileSize.x && levelHeight < tileSize.y ){ iiifSize = levelWidth + ","; iiifRegion = 'full'; } else { diff --git a/src/tiledimage.js b/src/tiledimage.js index 363df7c6..699ca254 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1036,7 +1036,7 @@ function onTileLoad( tiledImage, tile, time, image ) { var finish = function() { var cutoff = Math.ceil( Math.log( - tiledImage.source.getTileSize(tile.level) ) / Math.log( 2 ) ); + tiledImage.source.getTileSize(tile.level).x ) / Math.log( 2 ) ); setTileLoaded(tiledImage, tile, image, cutoff); }; diff --git a/src/tilesource.js b/src/tilesource.js index 1c0d29d2..9ed329dc 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -73,6 +73,11 @@ * The size of the tiles to assumed to make up each pyramid layer in pixels. * Tile size determines the point at which the image pyramid must be * divided into a matrix of smaller images. + * Use options.tileWidth and options.tileHeight to support non-square tiles. + * @param {Number} [options.tileWidth] + * The width of the tiles to assumed to make up each pyramid layer in pixels. + * @param {Number} [options.tileHeight] + * The height of the tiles to assumed to make up each pyramid layer in pixels. * @param {Number} [options.tileOverlap] * The number of pixels each tile is expected to overlap touching tiles. * @param {Number} [options.minLevel] @@ -191,7 +196,10 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve this.aspectRatio = ( options.width && options.height ) ? ( options.width / options.height ) : 1; this.dimensions = new $.Point( options.width, options.height ); - this.tileSize = options.tileSize ? options.tileSize : 0; + this.tileSize = new $.Point( + ( options.tileSize ? options.tileSize : options.tileWidth ), + ( options.tileSize ? options.tileSize : options.tileHeight ) + ); this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; this.minLevel = options.minLevel ? options.minLevel : 0; this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ? @@ -219,6 +227,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * from .tileSize directly. tileSize may be deprecated in a future release. * @function * @param {Number} level + * @returns {OpenSeadragon.Point} The dimensions of tiles in level */ getTileSize: function( level ) { return this.tileSize; @@ -250,8 +259,8 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ */ getNumTiles: function( level ) { var scale = this.getLevelScale( level ), - x = Math.ceil( scale * this.dimensions.x / this.getTileSize(level) ), - y = Math.ceil( scale * this.dimensions.y / this.getTileSize(level) ); + x = Math.ceil( scale * this.dimensions.x / this.getTileSize(level).x ), + y = Math.ceil( scale * this.dimensions.y / this.getTileSize(level).y ); return new $.Point( x, y ); }, @@ -276,11 +285,19 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ getClosestLevel: function( rect ) { var i, tilesPerSide, - tiles; + tiles, + tileSize; + for( i = this.minLevel; i < this.maxLevel; i++ ){ tiles = this.getNumTiles( i ); - tilesPerSide = Math.floor( Math.max( rect.x, rect.y ) / this.getTileSize(i) ); - if( Math.max( tiles.x, tiles.y ) + 1 >= tilesPerSide ){ + tileSize = this.getTileSize(i); + + tilesPerSide = new $.Point( + Math.floor( rect.x / tileSize.x ), + Math.floor( rect.y / tileSize.y ) + ); + + if( tiles.x + 1 >= tilesPerSide.x && tiles.y + 1 >= tilesPerSide.y ){ break; } } @@ -293,9 +310,9 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * @param {OpenSeadragon.Point} point */ getTileAtPoint: function( level, point ) { - var pixel = point.times( this.dimensions.x ).times( this.getLevelScale(level ) ), - tx = Math.floor( pixel.x / this.getTileSize(level) ), - ty = Math.floor( pixel.y / this.getTileSize(level) ); + var pixel = point.times( this.dimensions.x ).times( this.getLevelScale(level) ), + tx = Math.floor( pixel.x / this.getTileSize(level).x ), + ty = Math.floor( pixel.y / this.getTileSize(level).y ); return new $.Point( tx, ty ); }, @@ -309,10 +326,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ getTileBounds: function( level, x, y ) { var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ), tileSize = this.getTileSize(level), - px = ( x === 0 ) ? 0 : tileSize * x - this.tileOverlap, - py = ( y === 0 ) ? 0 : tileSize * y - this.tileOverlap, - sx = tileSize + ( x === 0 ? 1 : 2 ) * this.tileOverlap, - sy = tileSize + ( y === 0 ? 1 : 2 ) * this.tileOverlap, + px = ( x === 0 ) ? 0 : tileSize.x * x - this.tileOverlap, + py = ( y === 0 ) ? 0 : tileSize.y * y - this.tileOverlap, + sx = tileSize.x + ( x === 0 ? 1 : 2 ) * this.tileOverlap, + sy = tileSize.y + ( y === 0 ? 1 : 2 ) * this.tileOverlap, scale = 1.0 / dimensionsScaled.x; sx = Math.min( sx, dimensionsScaled.x - px ); From 85241b124926c329c61497aa0bca25cbf7e77434 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Fri, 26 Jun 2015 16:26:09 -0400 Subject: [PATCH 065/104] Fix for maintaining IIIFTileSource support while implementing #670. Change docs to reflect that TileSource.prototype.tileSize is now an OpenSeadragon.Point --- src/iiiftilesource.js | 7 +++++-- src/tilesource.js | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index 54779158..a1e88a12 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -190,9 +190,12 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea var scaleFactor = Math.pow(2, this.maxLevel - level); // cache it in case any external code is going to read it directly if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { - this.tileSize = this.tileSizePerScaleFactor[scaleFactor]; + this.tileSize = new $.Point( + this.tileSizePerScaleFactor[scaleFactor], + this.tileSizePerScaleFactor[scaleFactor] + ); } - return new $.Point(this.tileSize, this.tileSize); + return this.tileSize; }, /** diff --git a/src/tilesource.js b/src/tilesource.js index 9ed329dc..74a9c539 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -146,7 +146,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve * The size of the image tiles used to compose the image. * Please note that tileSize may be deprecated in a future release. * Instead the getTileSize(level) function should be used. - * @member {Number} tileSize + * @member {OpenSeadragon.Point} tileSize * @memberof OpenSeadragon.TileSource# */ /** @@ -179,7 +179,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve //async mechanism set some safe defaults first this.aspectRatio = 1; this.dimensions = new $.Point( 10, 10 ); - this.tileSize = 0; + this.tileSize = new $.Point( 0, 0 ); this.tileOverlap = 0; this.minLevel = 0; this.maxLevel = 0; @@ -196,10 +196,16 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve this.aspectRatio = ( options.width && options.height ) ? ( options.width / options.height ) : 1; this.dimensions = new $.Point( options.width, options.height ); - this.tileSize = new $.Point( - ( options.tileSize ? options.tileSize : options.tileWidth ), - ( options.tileSize ? options.tileSize : options.tileHeight ) - ); + + if( options.tileSize ){ + this.tileSize = new $.Point(options.tileSize, options.tileSize); + } else { + this.tileSize = new $.Point( + (options.tileWidth ? options.tileWidth : 0), + (options.tileHeight ? options.tileHeight : 0) + ); + } + this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; this.minLevel = options.minLevel ? options.minLevel : 0; this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ? From 827fe4e8362c9ff4bd23e022fc0f3089c4fecee5 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Mon, 29 Jun 2015 13:42:09 -0400 Subject: [PATCH 066/104] Deprecate TileSource.getTileSize(), add TileSource.getTileWidth() and TileSource.getTileHeight() --- src/iiiftilesource.js | 12 ++---- src/tiledimage.js | 2 +- src/tilesource.js | 85 +++++++++++++++++++++++++++++-------------- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index a1e88a12..e053eab2 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -185,15 +185,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea * @function * @param {Number} level */ - getTileSize: function( level ){ var scaleFactor = Math.pow(2, this.maxLevel - level); // cache it in case any external code is going to read it directly if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { - this.tileSize = new $.Point( - this.tileSizePerScaleFactor[scaleFactor], - this.tileSizePerScaleFactor[scaleFactor] - ); + this.tileSize = this.tileSizePerScaleFactor[scaleFactor]; } return this.tileSize; }, @@ -233,8 +229,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea tileSize = this.getTileSize(level); - iiifTileSizeWidth = Math.ceil( tileSize.x / scale ); - iiifTileSizeHeight = Math.ceil( tileSize.y / scale ); + iiifTileSizeWidth = Math.ceil( tileSize / scale ); + iiifTileSizeHeight = Math.ceil( tileSize / scale ); if ( this['@context'].indexOf('/1.0/context.json') > -1 || this['@context'].indexOf('/1.1/context.json') > -1 || @@ -244,7 +240,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea iiifQuality = "default.jpg"; } - if ( levelWidth < tileSize.x && levelHeight < tileSize.y ){ + if ( levelWidth < tileSize && levelHeight < tileSize ){ iiifSize = levelWidth + ","; iiifRegion = 'full'; } else { diff --git a/src/tiledimage.js b/src/tiledimage.js index 699ca254..ded6b480 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1036,7 +1036,7 @@ function onTileLoad( tiledImage, tile, time, image ) { var finish = function() { var cutoff = Math.ceil( Math.log( - tiledImage.source.getTileSize(tile.level).x ) / Math.log( 2 ) ); + tiledImage.source.getTileWidth(tile.level) ) / Math.log( 2 ) ); setTileLoaded(tiledImage, tile, image, cutoff); }; diff --git a/src/tilesource.js b/src/tilesource.js index 74a9c539..1d70738e 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -146,7 +146,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve * The size of the image tiles used to compose the image. * Please note that tileSize may be deprecated in a future release. * Instead the getTileSize(level) function should be used. - * @member {OpenSeadragon.Point} tileSize + * @member {Number} tileSize * @memberof OpenSeadragon.TileSource# */ /** @@ -179,7 +179,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve //async mechanism set some safe defaults first this.aspectRatio = 1; this.dimensions = new $.Point( 10, 10 ); - this.tileSize = new $.Point( 0, 0 ); + this.tileSize = 0; this.tileOverlap = 0; this.minLevel = 0; this.maxLevel = 0; @@ -197,15 +197,10 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve ( options.width / options.height ) : 1; this.dimensions = new $.Point( options.width, options.height ); - if( options.tileSize ){ - this.tileSize = new $.Point(options.tileSize, options.tileSize); - } else { - this.tileSize = new $.Point( - (options.tileWidth ? options.tileWidth : 0), - (options.tileHeight ? options.tileHeight : 0) - ); - } - + this.tileSize = options.tileSize ? options.tileSize : 0; + this.tileWidth = options.tileWidth; + this.tileHeight = options.tileHeight; + this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; this.minLevel = options.minLevel ? options.minLevel : 0; this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ? @@ -231,13 +226,49 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * Subclasses should override this if tileSizes can be different at different levels * such as in IIIFTileSource. Code should use this function rather than reading * from .tileSize directly. tileSize may be deprecated in a future release. + * @deprecated * @function * @param {Number} level - * @returns {OpenSeadragon.Point} The dimensions of tiles in level */ getTileSize: function( level ) { + $.console.error( + "[TileSource.getTileSize] is deprecated." + + "Use TileSource.getTileWidth() and TileSource.getTileHeight() instead" + ); return this.tileSize; }, + + /** + * Return the tileWidth for a given level. + * Subclasses should override this if tileWidth can be different at different levels + * such as in IIIFTileSource. Code should use this function rather than reading + * from .tileWidth directly. + * @function + * @param {Number} level + */ + getTileWidth: function( level ) { + // If tileWidth was not set, fallback by setting it to tileSize + if( this.tileWidth === undefined){ + this.tileWidth = this.tileSize; + } + return this.tileWidth; + }, + + /** + * Return the tileHeight for a given level. + * Subclasses should override this if tileHeight can be different at different levels + * such as in IIIFTileSource. Code should use this function rather than reading + * from .tileHeight directly. + * @function + * @param {Number} level + */ + getTileHeight: function( level ) { + // If tileHeight was not set, fallback by setting it to tileSize + if( this.tileHeight === undefined ){ + this.tileHeight = this.tileSize; + } + return this.tileHeight; + }, /** * @function @@ -265,8 +296,8 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ */ getNumTiles: function( level ) { var scale = this.getLevelScale( level ), - x = Math.ceil( scale * this.dimensions.x / this.getTileSize(level).x ), - y = Math.ceil( scale * this.dimensions.y / this.getTileSize(level).y ); + x = Math.ceil( scale * this.dimensions.x / this.getTileWidth(level) ), + y = Math.ceil( scale * this.dimensions.y / this.getTileHeight(level) ); return new $.Point( x, y ); }, @@ -291,19 +322,16 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ getClosestLevel: function( rect ) { var i, tilesPerSide, - tiles, - tileSize; + tiles; for( i = this.minLevel; i < this.maxLevel; i++ ){ tiles = this.getNumTiles( i ); - tileSize = this.getTileSize(i); - tilesPerSide = new $.Point( - Math.floor( rect.x / tileSize.x ), - Math.floor( rect.y / tileSize.y ) + Math.floor( rect.x / this.getTileWidth(i) ), + Math.floor( rect.y / this.getTileHeight(i) ) ); - if( tiles.x + 1 >= tilesPerSide.x && tiles.y + 1 >= tilesPerSide.y ){ + if( tiles.x + 1 >= tilesPerSide.x || tiles.y + 1 >= tilesPerSide.y ){ break; } } @@ -317,8 +345,8 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ */ getTileAtPoint: function( level, point ) { var pixel = point.times( this.dimensions.x ).times( this.getLevelScale(level) ), - tx = Math.floor( pixel.x / this.getTileSize(level).x ), - ty = Math.floor( pixel.y / this.getTileSize(level).y ); + tx = Math.floor( pixel.x / this.getTileWidth(level) ), + ty = Math.floor( pixel.y / this.getTileHeight(level) ); return new $.Point( tx, ty ); }, @@ -331,11 +359,12 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ */ getTileBounds: function( level, x, y ) { var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ), - tileSize = this.getTileSize(level), - px = ( x === 0 ) ? 0 : tileSize.x * x - this.tileOverlap, - py = ( y === 0 ) ? 0 : tileSize.y * y - this.tileOverlap, - sx = tileSize.x + ( x === 0 ? 1 : 2 ) * this.tileOverlap, - sy = tileSize.y + ( y === 0 ? 1 : 2 ) * this.tileOverlap, + tileWidth = this.getTileWidth(level), + tileHeight = this.getTileHeight(level), + px = ( x === 0 ) ? 0 : tileWidth * x - this.tileOverlap, + py = ( y === 0 ) ? 0 : tileHeight * y - this.tileOverlap, + sx = tileWidth + ( x === 0 ? 1 : 2 ) * this.tileOverlap, + sy = tileHeight + ( y === 0 ? 1 : 2 ) * this.tileOverlap, scale = 1.0 / dimensionsScaled.x; sx = Math.min( sx, dimensionsScaled.x - px ); From 201ca8a422c952f884b95f7a9a4a1f1cb31bb879 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Mon, 29 Jun 2015 14:37:35 -0400 Subject: [PATCH 067/104] Revert changes to IIIFTileSource. Changes no longer needed for non square tiles --- src/iiiftilesource.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index e053eab2..b54d020b 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -117,6 +117,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea * @param {Object|Array} data * @param {String} optional - url */ + supports: function( data, url ) { // Version 2.0 and forwards if (data.protocol && data.protocol == 'http://iiif.io/api/image') { @@ -228,9 +229,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea uri; tileSize = this.getTileSize(level); - iiifTileSizeWidth = Math.ceil( tileSize / scale ); - iiifTileSizeHeight = Math.ceil( tileSize / scale ); + iiifTileSizeHeight = iiifTileSizeWidth; if ( this['@context'].indexOf('/1.0/context.json') > -1 || this['@context'].indexOf('/1.1/context.json') > -1 || From b09897dbf1352d91439b5e65c8e0276ed44eff4e Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 29 Jun 2015 14:16:11 -0700 Subject: [PATCH 068/104] Added changelog for #666 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index d62a3a33..e42893fb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -9,6 +9,7 @@ OPENSEADRAGON CHANGELOG * Fixed flickering tiles with useCanvas=false when no cache is used (#661) * Added additional coordinates conversion methods to TiledImage (#662) * 'display: none' no longer gets reset on overlays during draw (#668) +* Added `preserveImageSizeOnResize` option (#666) 2.0.0: From c25f3e374b1298825a9515859dc0d8114729c68f Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 29 Jun 2015 14:21:53 -0700 Subject: [PATCH 069/104] Newer versions of qunit and qunit-istanbul --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index c979f4b8..d2081b58 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,16 @@ "devDependencies": { "grunt": "^0.4.5", "grunt-contrib-clean": "^0.5.0", - "grunt-text-replace": "^0.3.11", + "grunt-contrib-compress": "^0.9.1", "grunt-contrib-concat": "^0.4.0", - "grunt-git-describe": "^2.3.2", + "grunt-contrib-connect": "^0.7.1", + "grunt-contrib-jshint": "^0.10.0", "grunt-contrib-uglify": "^0.4.0", "grunt-contrib-watch": "^0.6.1", - "grunt-contrib-jshint": "^0.10.0", - "grunt-contrib-compress": "^0.9.1", - "grunt-contrib-connect": "^0.7.1", - "qunitjs": "^1.14.0", - "grunt-qunit-istanbul": "^0.4.5" + "grunt-git-describe": "^2.3.2", + "grunt-qunit-istanbul": "^0.5.0", + "grunt-text-replace": "^0.3.11", + "qunitjs": "^1.18.0" }, "scripts": { "test": "grunt test" From 6db00ad2865c6ac5c115ff9b57645e83c07b3030 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 30 Jun 2015 10:58:51 -0700 Subject: [PATCH 070/104] Overlay doc fixes --- src/viewer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/viewer.js b/src/viewer.js index 3e318528..f93c8e23 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1710,7 +1710,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * is closed which include when changing page. * @method * @param {Element|String|Object} element - A reference to an element or an id for - * the element which will overlayed. Or an Object specifying the configuration for the overlay + * the element which will be overlayed. Or an Object specifying the configuration for the overlay * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or * rectangle which will be overlayed. * @param {OpenSeadragon.OverlayPlacement} placement - The position of the @@ -1770,6 +1770,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * Updates the overlay represented by the reference to the element or * element id moving it to the new location, relative to the new placement. * @method + * @param {Element|String} element - A reference to an element or an id for + * the element which is overlayed. * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or * rectangle which will be overlayed. * @param {OpenSeadragon.OverlayPlacement} placement - The position of the From d11c4fe1070e32560dc1a2d1345d5a006843645a Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Tue, 30 Jun 2015 17:56:06 -0400 Subject: [PATCH 071/104] Remove TileSource.tileSize. Convert IIIFTileSource to override TileSource.getTileWidth and TileSource.getTileHeight. --- src/iiiftilesource.js | 39 +++++++++++++++++++++++++++----------- src/tilesource.js | 44 +++++++++++++++---------------------------- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index b54d020b..cf7d1927 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -182,19 +182,34 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea }, /** - * Return the tileSize for the given level. + * Return the tileWidth for the given level. * @function * @param {Number} level - */ - getTileSize: function( level ){ + */ + getTileWidth: function( level ) { var scaleFactor = Math.pow(2, this.maxLevel - level); - // cache it in case any external code is going to read it directly + if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { - this.tileSize = this.tileSizePerScaleFactor[scaleFactor]; + return this.tileSizePerScaleFactor[scaleFactor]; } - return this.tileSize; + return this._tileWidth; }, + /** + * Return the tileHeight for the given level. + * @function + * @param {Number} level + */ + getTileHeight: function( level ) { + var scaleFactor = Math.pow(2, this.maxLevel - level); + + if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { + return this.tileSizePerScaleFactor[scaleFactor]; + } + return this._tileHeight; + }, + + /** * Responsible for retreiving the url which will return an image for the * region specified by the given x, y, and level components. @@ -216,7 +231,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea levelHeight = Math.ceil( this.height * scale ), //## iiif region - tileSize, + tileWidth, + tileHeight, iiifTileSizeWidth, iiifTileSizeHeight, iiifRegion, @@ -228,9 +244,10 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea iiifQuality, uri; - tileSize = this.getTileSize(level); - iiifTileSizeWidth = Math.ceil( tileSize / scale ); - iiifTileSizeHeight = iiifTileSizeWidth; + tileWidth = this.getTileSize(level); + tileHeight = this.getTileHeight(level); + iiifTileSizeWidth = Math.ceil( tileWidth / scale ); + iiifTileSizeHeight = Math.ceil( tileHeight / scale ); if ( this['@context'].indexOf('/1.0/context.json') > -1 || this['@context'].indexOf('/1.1/context.json') > -1 || @@ -240,7 +257,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea iiifQuality = "default.jpg"; } - if ( levelWidth < tileSize && levelHeight < tileSize ){ + if ( levelWidth < tileWidth && levelHeight < tileHeight ){ iiifSize = levelWidth + ","; iiifRegion = 'full'; } else { diff --git a/src/tilesource.js b/src/tilesource.js index 1d70738e..fa51915b 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -142,13 +142,6 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve * @member {OpenSeadragon.Point} dimensions * @memberof OpenSeadragon.TileSource# */ - /** - * The size of the image tiles used to compose the image. - * Please note that tileSize may be deprecated in a future release. - * Instead the getTileSize(level) function should be used. - * @member {Number} tileSize - * @memberof OpenSeadragon.TileSource# - */ /** * The overlap in pixels each tile shares with its adjacent neighbors. * @member {Number} tileOverlap @@ -179,7 +172,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve //async mechanism set some safe defaults first this.aspectRatio = 1; this.dimensions = new $.Point( 10, 10 ); - this.tileSize = 0; + this._tileWidth = 0; + this._tileHeight = 0; this.tileOverlap = 0; this.minLevel = 0; this.maxLevel = 0; @@ -197,9 +191,12 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve ( options.width / options.height ) : 1; this.dimensions = new $.Point( options.width, options.height ); - this.tileSize = options.tileSize ? options.tileSize : 0; - this.tileWidth = options.tileWidth; - this.tileHeight = options.tileHeight; + if ( options.tileSize ){ + this._tileWidth = this._tileHeight = options.tileSize; + } else { + this._tileWidth = options.tileWidth ? options.tileWidth : 0; + this._tileHeight = options.tileHeight ? options.tileHeight: 0; + } this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; this.minLevel = options.minLevel ? options.minLevel : 0; @@ -221,21 +218,12 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ - /** - * Return the tileSize for a given level. - * Subclasses should override this if tileSizes can be different at different levels - * such as in IIIFTileSource. Code should use this function rather than reading - * from .tileSize directly. tileSize may be deprecated in a future release. - * @deprecated - * @function - * @param {Number} level - */ getTileSize: function( level ) { $.console.error( "[TileSource.getTileSize] is deprecated." + "Use TileSource.getTileWidth() and TileSource.getTileHeight() instead" ); - return this.tileSize; + return this._tileWidth; }, /** @@ -247,11 +235,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * @param {Number} level */ getTileWidth: function( level ) { - // If tileWidth was not set, fallback by setting it to tileSize - if( this.tileWidth === undefined){ - this.tileWidth = this.tileSize; + if (!this._tileWidth) { + return this.getTileSize(level); } - return this.tileWidth; + return this._tileWidth; }, /** @@ -263,11 +250,10 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * @param {Number} level */ getTileHeight: function( level ) { - // If tileHeight was not set, fallback by setting it to tileSize - if( this.tileHeight === undefined ){ - this.tileHeight = this.tileSize; + if (!this._tileHeight) { + return this.getTileSize(level); } - return this.tileHeight; + return this._tileHeight; }, /** From c27a43e49e81c937d08e781f33daeaac1567ca45 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Thu, 2 Jul 2015 11:24:43 -0400 Subject: [PATCH 072/104] Fix calling the wrong function in IIIFTileSource to retrieve tile width --- src/iiiftilesource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index cf7d1927..304ef464 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -244,7 +244,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea iiifQuality, uri; - tileWidth = this.getTileSize(level); + tileWidth = this.getTileWidth(level); tileHeight = this.getTileHeight(level); iiifTileSizeWidth = Math.ceil( tileWidth / scale ); iiifTileSizeHeight = Math.ceil( tileHeight / scale ); From 339401683f50dc679fa0d0e9be87b3510ed8290c Mon Sep 17 00:00:00 2001 From: Larissa Smith Date: Thu, 2 Jul 2015 13:20:57 -0600 Subject: [PATCH 073/104] Added configuration parameter to allow a horizontal layout that has a fixed number of columns or a vertical layout that has a fixed number of rows. --- src/openseadragon.js | 5 +++++ src/viewer.js | 1 + src/world.js | 9 ++++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index e20926bd..64349972 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -559,6 +559,10 @@ * If collectionMode is true, specifies how many rows the grid should have. Use 1 to make a line. * If collectionLayout is 'vertical', specifies how many columns instead. * + * @property {Number} [collectionColumns=0] + * If collectionMode is true, specifies how many columns the grid should have. Use 1 to make a line. + * If collectionLayout is 'vertical', specifies how many rows instead. Ignored if collectionRows is not set to a falsy value. + * * @property {String} [collectionLayout='horizontal'] * If collectionMode is true, specifies whether to arrange vertically or horizontally. * @@ -1039,6 +1043,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ //COLLECTION VISUALIZATION SETTINGS collectionRows: 3, //or columns depending on layout + collectionColumns: 0, //columns in horizontal layout, rows in vertical layout collectionLayout: 'horizontal', //vertical collectionMode: false, collectionTileSize: 800, diff --git a/src/viewer.js b/src/viewer.js index f93c8e23..01560f01 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1318,6 +1318,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, _this.world.arrange({ immediately: queueItem.options.collectionImmediately, rows: _this.collectionRows, + columns: _this.collectionColumns, layout: _this.collectionLayout, tileSize: _this.collectionTileSize, tileMargin: _this.collectionTileMargin diff --git a/src/world.js b/src/world.js index c9e65225..73b28a67 100644 --- a/src/world.js +++ b/src/world.js @@ -281,6 +281,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W * @param {Boolean} [options.immediately=false] - Whether to animate to the new arrangement. * @param {String} [options.layout] - See collectionLayout in {@link OpenSeadragon.Options}. * @param {Number} [options.rows] - See collectionRows in {@link OpenSeadragon.Options}. + * @param {Number} [options.columns] - See collectionColumns in {@link OpenSeadragon.Options}. * @param {Number} [options.tileSize] - See collectionTileSize in {@link OpenSeadragon.Options}. * @param {Number} [options.tileMargin] - See collectionTileMargin in {@link OpenSeadragon.Options}. * @fires OpenSeadragon.World.event:metrics-change @@ -290,10 +291,16 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W var immediately = options.immediately || false; var layout = options.layout || $.DEFAULT_SETTINGS.collectionLayout; var rows = options.rows || $.DEFAULT_SETTINGS.collectionRows; + var columns = options.columns || $.DEFAULT_SETTINGS.collectionColumns; var tileSize = options.tileSize || $.DEFAULT_SETTINGS.collectionTileSize; var tileMargin = options.tileMargin || $.DEFAULT_SETTINGS.collectionTileMargin; var increment = tileSize + tileMargin; - var wrap = Math.ceil(this._items.length / rows); + var wrap; + if (!options.rows && columns) { + wrap = columns; + } else { + wrap = Math.ceil(this._items.length / rows); + } var x = 0; var y = 0; var item, box, width, height, position; From e93a0862bd442a1a923383cd7ff667487f0d2bd8 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 3 Jul 2015 10:19:10 -0700 Subject: [PATCH 074/104] Added demo for TileSource swapping --- test/demo/tilesource-swap.html | 96 ++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 test/demo/tilesource-swap.html diff --git a/test/demo/tilesource-swap.html b/test/demo/tilesource-swap.html new file mode 100644 index 00000000..3420927c --- /dev/null +++ b/test/demo/tilesource-swap.html @@ -0,0 +1,96 @@ + + + + TileSource Swapping + + + + + +
This is a demo of using a single image stand-in and then swapping to a full TileSource on zooming. Click the image to see it in action.
+
+ + + From 8b71876c37d99953475d64cf12c972c4d8d7182a Mon Sep 17 00:00:00 2001 From: Grant Echols Date: Mon, 6 Jul 2015 14:56:41 -0600 Subject: [PATCH 075/104] Added error message tracking into the tile loading mechanism. This exposes the reason for the error in the console.log message --- src/imageloader.js | 5 ++++- src/tiledimage.js | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/imageloader.js b/src/imageloader.js index a61c8936..ef3a4f4f 100644 --- a/src/imageloader.js +++ b/src/imageloader.js @@ -51,6 +51,7 @@ function ImageJob ( options ) { } ImageJob.prototype = { + errorMsg: null, start: function(){ var _this = this; @@ -64,10 +65,12 @@ ImageJob.prototype = { _this.finish( true ); }; this.image.onabort = this.image.onerror = function(){ + _this.errorMsg = "Image load aborted"; _this.finish( false ); }; this.jobId = window.setTimeout( function(){ + _this.errorMsg = "Image load exceeded timeout"; _this.finish( false ); }, this.timeout); @@ -173,7 +176,7 @@ function completeJob( loader, job, callback ) { loader.jobsInProgress++; } - callback( job.image ); + callback( job.image, job.errorMsg ); } }( OpenSeadragon )); diff --git a/src/tiledimage.js b/src/tiledimage.js index 363df7c6..f4f9ee3f 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1011,8 +1011,8 @@ function loadTile( tiledImage, tile, time ) { tiledImage._imageLoader.addJob({ src: tile.url, crossOriginPolicy: tiledImage.crossOriginPolicy, - callback: function( image ){ - onTileLoad( tiledImage, tile, time, image ); + callback: function( image, errorMsg ){ + onTileLoad( tiledImage, tile, time, image, errorMsg ); }, abort: function() { tile.loading = false; @@ -1020,9 +1020,9 @@ function loadTile( tiledImage, tile, time ) { }); } -function onTileLoad( tiledImage, tile, time, image ) { +function onTileLoad( tiledImage, tile, time, image, errorMsg ) { if ( !image ) { - $.console.log( "Tile %s failed to load: %s", tile, tile.url ); + $.console.log( "Tile %s failed to load: %s", tile.url, errorMsg ); if( !tiledImage.debugMode ){ tile.loading = false; tile.exists = false; From 9629f47c2c1adbcf7d59d5872e2d8a32b9578b2d Mon Sep 17 00:00:00 2001 From: Grant Echols Date: Mon, 6 Jul 2015 15:06:00 -0600 Subject: [PATCH 076/104] Added error message tracking into the tile loading mechanism. This exposes the reason for the error in the console.log message --- src/tiledimage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index f4f9ee3f..43285d4d 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1022,7 +1022,7 @@ function loadTile( tiledImage, tile, time ) { function onTileLoad( tiledImage, tile, time, image, errorMsg ) { if ( !image ) { - $.console.log( "Tile %s failed to load: %s", tile.url, errorMsg ); + $.console.log( "Tile %s failed to load: %s - error:", tile, tile.url, errorMsg ); if( !tiledImage.debugMode ){ tile.loading = false; tile.exists = false; From 553948781486bed2e2a6cef046e56be78f4855fc Mon Sep 17 00:00:00 2001 From: Grant Echols Date: Mon, 6 Jul 2015 15:07:00 -0600 Subject: [PATCH 077/104] Added error message tracking into the tile loading mechanism. This exposes the reason for the error in the console.log message --- src/tiledimage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tiledimage.js b/src/tiledimage.js index 43285d4d..960d91d9 100644 --- a/src/tiledimage.js +++ b/src/tiledimage.js @@ -1022,7 +1022,7 @@ function loadTile( tiledImage, tile, time ) { function onTileLoad( tiledImage, tile, time, image, errorMsg ) { if ( !image ) { - $.console.log( "Tile %s failed to load: %s - error:", tile, tile.url, errorMsg ); + $.console.log( "Tile %s failed to load: %s - error: %s", tile, tile.url, errorMsg ); if( !tiledImage.debugMode ){ tile.loading = false; tile.exists = false; From c1d4652eee21dd7b9cba161df15d23a21a1df1ce Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 7 Jul 2015 09:52:03 -0700 Subject: [PATCH 078/104] Changelog for #679 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index e42893fb..9c32e88d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,6 +10,7 @@ OPENSEADRAGON CHANGELOG * Added additional coordinates conversion methods to TiledImage (#662) * 'display: none' no longer gets reset on overlays during draw (#668) * Added `preserveImageSizeOnResize` option (#666) +* Better error reporting for tile load failures (#679) 2.0.0: From 57e6fdde900c08fc442e323e87d2117deba6cc6c Mon Sep 17 00:00:00 2001 From: Larissa Smith Date: Tue, 7 Jul 2015 14:09:49 -0600 Subject: [PATCH 079/104] Added tests for collectionColumns use by world.arrange. --- test/modules/world.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/modules/world.js b/test/modules/world.js index ce63f02a..0c5aebf7 100644 --- a/test/modules/world.js +++ b/test/modules/world.js @@ -213,6 +213,26 @@ checkBounds(new OpenSeadragon.Rect(0, 0, 1, 4), 'one vertical column'); + viewer.world.arrange({ + layout: 'horizontal', + rows: false, + columns: 3, + tileSize: 1, + tileMargin: 0.5 + }); + + checkBounds(new OpenSeadragon.Rect(0, 0, 4, 1), 'three horizontal columns (one horizontal row)'); + + viewer.world.arrange({ + layout: 'vertical', + rows: false, + columns: 3, + tileSize: 1, + tileMargin: 0.5 + }); + + checkBounds(new OpenSeadragon.Rect(0, 0, 1, 4), 'three vertical rows (one vertical column)'); + start(); }); From 8508c2c4509177372026f1b0afbe002d179945af Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 8 Jul 2015 08:58:46 -0700 Subject: [PATCH 080/104] Changelog for #680 --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index 9c32e88d..8bb01788 100644 --- a/changelog.txt +++ b/changelog.txt @@ -11,6 +11,7 @@ OPENSEADRAGON CHANGELOG * 'display: none' no longer gets reset on overlays during draw (#668) * Added `preserveImageSizeOnResize` option (#666) * Better error reporting for tile load failures (#679) +* Added collectionColumns as a configuration parameter (#680) 2.0.0: From df7bd2e5ced7a6c9ca576292ba3e28368536dfd8 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Fri, 10 Jul 2015 14:26:51 -0400 Subject: [PATCH 081/104] Respect non-square tiles if available from IIIFTileSources --- src/iiiftilesource.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index 304ef464..bf3da020 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -55,14 +55,19 @@ $.IIIFTileSource = function( options ){ options.tileSizePerScaleFactor = {}; // N.B. 2.0 renamed scale_factors to scaleFactors - if ( this.tile_width ) { + if ( this.tile_width && this.tile_height ) { + options.tileWidth = this.tile_width; + options.tileHeight = this.tile_height; + } else if ( this.tile_width ) { options.tileSize = this.tile_width; } else if ( this.tile_height ) { options.tileSize = this.tile_height; } else if ( this.tiles ) { // Version 2.0 forwards if ( this.tiles.length == 1 ) { - options.tileSize = this.tiles[0].width; + options.tileWidth = this.tiles[0].width; + // Use height if provided, otherwise assume square tiles and use width. + options.tileHeight = this.tiles[0].height || this.tiles[0].width; this.scale_factors = this.tiles[0].scaleFactors; } else { // Multiple tile sizes at different levels @@ -71,13 +76,15 @@ $.IIIFTileSource = function( options ){ for (var sf = 0; sf < this.tiles[t].scaleFactors.length; sf++) { var scaleFactor = this.tiles[t].scaleFactors[sf]; this.scale_factors.push(scaleFactor); - options.tileSizePerScaleFactor[scaleFactor] = this.tiles[t].width; + options.tileSizePerScaleFactor[scaleFactor] = { + width: this.tiles[t].width, + height: this.tiles[t].height || this.tiles[t].width + }; } } } } else { // use the largest of tileOptions that is smaller than the short dimension - var shortDim = Math.min( this.height, this.width ), tileOptions = [256,512,1024], smallerTiles = []; @@ -94,8 +101,6 @@ $.IIIFTileSource = function( options ){ // If we're smaller than 256, just use the short side. options.tileSize = shortDim; } - this.tile_width = options.tileSize; // So that 'full' gets used for - this.tile_height = options.tileSize; // the region below } if ( !options.maxLevel ) { @@ -190,7 +195,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea var scaleFactor = Math.pow(2, this.maxLevel - level); if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { - return this.tileSizePerScaleFactor[scaleFactor]; + return this.tileSizePerScaleFactor[scaleFactor].width; } return this._tileWidth; }, @@ -204,7 +209,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea var scaleFactor = Math.pow(2, this.maxLevel - level); if (this.tileSizePerScaleFactor && this.tileSizePerScaleFactor[scaleFactor]) { - return this.tileSizePerScaleFactor[scaleFactor]; + return this.tileSizePerScaleFactor[scaleFactor].height; } return this._tileHeight; }, From e1e345a4bc753ea80f4d761c223cdc893ed81975 Mon Sep 17 00:00:00 2001 From: Conner Wingard Date: Tue, 14 Jul 2015 14:49:52 -0400 Subject: [PATCH 082/104] Clean up TileSource object when provided tileWidth/tileHeight for clarity. Add basic TileSource tests. --- src/tilesource.js | 27 +++++++++++++++----- test/modules/tilesource.js | 51 ++++++++++++++++++++++++++++++++++++++ test/test.html | 1 + 3 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 test/modules/tilesource.js diff --git a/src/tilesource.js b/src/tilesource.js index fa51915b..48d5bf10 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -191,11 +191,26 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve ( options.width / options.height ) : 1; this.dimensions = new $.Point( options.width, options.height ); - if ( options.tileSize ){ - this._tileWidth = this._tileHeight = options.tileSize; + if ( this.tileSize ){ + this._tileWidth = this._tileHeight = this.tileSize; + delete this.tileSize; } else { - this._tileWidth = options.tileWidth ? options.tileWidth : 0; - this._tileHeight = options.tileHeight ? options.tileHeight: 0; + if( this.tileWidth ){ + // We were passed tileWidth in options, but we want to rename it + // with a leading underscore to make clear that it is not safe to directly modify it + this._tileWidth = this.tileWidth; + delete this.tileWidth; + } else { + this._tileWidth = 0; + } + + if( this.tileHeight ){ + // See note above about renaming this.tileWidth + this._tileHeight = this.tileHeight; + delete this.tileHeight; + } else { + this._tileHeight = 0; + } } this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; @@ -230,7 +245,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * Return the tileWidth for a given level. * Subclasses should override this if tileWidth can be different at different levels * such as in IIIFTileSource. Code should use this function rather than reading - * from .tileWidth directly. + * from ._tileWidth directly. * @function * @param {Number} level */ @@ -245,7 +260,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ * Return the tileHeight for a given level. * Subclasses should override this if tileHeight can be different at different levels * such as in IIIFTileSource. Code should use this function rather than reading - * from .tileHeight directly. + * from ._tileHeight directly. * @function * @param {Number} level */ diff --git a/test/modules/tilesource.js b/test/modules/tilesource.js new file mode 100644 index 00000000..59d8b77f --- /dev/null +++ b/test/modules/tilesource.js @@ -0,0 +1,51 @@ +/* global module, ok, equal, start, test, testLog, Util */ +(function() { + + module('TileSource', { + setup: function() { + testLog.reset(); + } + }); + + + test("should set sane tile size defaults", function() { + var source = new OpenSeadragon.TileSource(); + + equal(source.getTileWidth(), 0, "getTileWidth() should return 0 if not provided a size"); + equal(source.getTileHeight(), 0, "getTileHeight() should return 0 if not provided a size"); + }); + + test("providing tileSize", function(){ + var tileSize = 256, + source = new OpenSeadragon.TileSource({ + tileSize: tileSize + }); + + equal(source.tileSize, undefined, "tileSize should not be set on the tileSource"); + equal(source.getTileWidth(), tileSize, "getTileWidth() should equal tileSize"); + equal(source.getTileHeight(), tileSize, "getTileHeight() should equal tileSize"); + }); + + + test("providing tileWidth and tileHeight", function(){ + var tileWidth = 256, + tileHeight = 512, + source = new OpenSeadragon.TileSource({ + tileWidth: tileWidth, + tileHeight: tileHeight + }); + + equal(source._tileWidth, tileWidth, "tileWidth option should set _tileWidth"); + equal(source._tileHeight, tileHeight, "tileHeight option should set _tileHeight"); + equal(source.tileWidth, undefined, "tileWidth should be renamed _tileWidth"); + equal(source.tileHeight, undefined, "tileHeight should be renamed _tileHeight"); + equal(source.getTileWidth(), tileWidth, "getTileWidth() should equal tileWidth"); + equal(source.getTileHeight(), tileHeight, "getTileHeight() should equal tileHeight"); + }); + + test('getTileSize() deprecation', function() { + var source = new OpenSeadragon.TileSource(); + Util.testDeprecation(source, 'getTileSize'); + }); + +}()); diff --git a/test/test.html b/test/test.html index ae4863e8..e52eb66a 100644 --- a/test/test.html +++ b/test/test.html @@ -36,6 +36,7 @@ +