From 58da998d7561f1105af274e565ddc4347beafe23 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 30 Nov 2013 17:28:17 -0500 Subject: [PATCH 01/15] Add basic layers support --- src/viewer.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index 7d9e755b..3037877c 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -578,6 +578,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, this.source = null; this.drawer = null; + this.drawers = []; this.viewport = this.preserveViewport ? this.viewport : null; //this.profiler = null; @@ -1045,6 +1046,20 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, return this; }, + /** + * @function + * @name OpenSeadragon.Viewer.prototype.addLayer + */ + addLayer: function( tileSource ) { + var drawer = new $.Drawer( + tileSource, + this.viewport, + this.canvas + ); + this.drawers.push( drawer ); + updateOnce( this ); + return drawer.canvas; + }, /** * @function @@ -1460,6 +1475,7 @@ function openTileSource( viewer, source ) { debugMode: _this.debugMode, debugGridColor: _this.debugGridColor }); + _this.drawers.push( _this.drawer ); //Instantiate a navigator if configured if ( _this.showNavigator && !_this.collectionMode ){ @@ -1929,7 +1945,7 @@ function updateOnce( viewer ) { } if ( animated ) { - viewer.drawer.update(); + updateDrawers( viewer ); if( viewer.navigator ){ viewer.navigator.update( viewer.viewport ); } @@ -1943,8 +1959,8 @@ function updateOnce( viewer ) { * @property {?Object} userData - Arbitrary subscriber-defined object. */ viewer.raiseEvent( "animation" ); - } else if ( THIS[ viewer.hash ].forceRedraw || viewer.drawer.needsUpdate() ) { - viewer.drawer.update(); + } else if ( THIS[ viewer.hash ].forceRedraw || drawersNeedUpdate( viewer ) ) { + updateDrawers( viewer ); if( viewer.navigator ){ viewer.navigator.update( viewer.viewport ); } @@ -1998,6 +2014,18 @@ function resizeViewportAndRecenter( viewer, containerSize, oldBounds, oldCenter viewport.fitBounds( newBounds, true ); } +function updateDrawers( viewer ) { + viewer.drawers.forEach( function( drawer ) { + drawer.update(); + }); +} + +function drawersNeedUpdate( viewer ) { + return viewer.drawers.some( function( drawer ) { + return drawer.needsUpdate(); + }); +} + /////////////////////////////////////////////////////////////////////////////// // Navigation Controls /////////////////////////////////////////////////////////////////////////////// From 0c2af6550aa1150145a7f0f08e93278fbb8b701c Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Mon, 9 Dec 2013 09:26:36 -0500 Subject: [PATCH 02/15] Add support to add/remove layers and change their levels. --- src/drawer.js | 22 +++ src/openseadragon.js | 6 + src/viewer.js | 319 +++++++++++++++++++++++++++++++------------ src/viewport.js | 2 +- 4 files changed, 259 insertions(+), 90 deletions(-) diff --git a/src/drawer.js b/src/drawer.js index a25e0aee..43b49035 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -91,6 +91,7 @@ $.Drawer = function( options ) { collectionOverlays: {}, //configurable settings + opacity: $.DEFAULT_SETTINGS.opacity, maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount, imageLoaderLimit: $.DEFAULT_SETTINGS.imageLoaderLimit, minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, @@ -143,6 +144,7 @@ $.Drawer = function( options ) { this.canvas.style.width = "100%"; this.canvas.style.height = "100%"; this.canvas.style.position = "absolute"; + $.setElementOpacity( this.canvas, this.opacity, true ); // explicit left-align this.container.style.textAlign = "left"; @@ -340,6 +342,26 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ return this; }, + /** + * Set the opacity of the drawer. + * @method + * @param {Number} opacity + * @return {OpenSeadragon.Drawer} Chainable. + */ + setOpacity: function( opacity ) { + this.opacity = opacity; + $.setElementOpacity( this.canvas, this.opacity, true ); + return this; + }, + + /** + * Get the opacity of the drawer. + * @method + * @returns {Number} + */ + getOpacity: function() { + return this.opacity; + }, /** * Returns whether the Drawer is scheduled for an update at the diff --git a/src/openseadragon.js b/src/openseadragon.js index e9c0f388..2392b185 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -190,6 +190,9 @@ * Zoom level to use when image is first opened or the home button is clicked. * If 0, adjusts to fit viewer. * + * @property {Number} [opacity=1] + * Opacity of the drawer (1=opaque, 0=transparent) + * * @property {Number} [degrees=0] * Initial rotation. * @@ -715,6 +718,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ // INITIAL ROTATION degrees: 0, + // APPEARANCE + opacity: 1, + //REFERENCE STRIP SETTINGS showReferenceStrip: false, referenceStripScroll: 'horizontal', diff --git a/src/viewer.js b/src/viewer.js index 3037877c..99b7bbb4 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -477,81 +477,26 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @fires OpenSeadragon.Viewer.event:open-failed */ open: function ( tileSource ) { - var _this = this, - customTileSource, - readySource, - $TileSource, - options; + var _this = this; _this._hideMessage(); - //allow plain xml strings or json strings to be parsed here - if( $.type( tileSource ) == 'string' ){ - if( tileSource.match(/\s*<.*/) ){ - tileSource = $.parseXml( tileSource ); - }else if( tileSource.match(/\s*[\{\[].*/) ){ - /*jshint evil:true*/ - tileSource = eval( '('+tileSource+')' ); - } - } - - setTimeout(function(){ - if ( $.type( tileSource ) == 'string') { - //If its still a string it means it must be a url at this point - tileSource = new $.TileSource( tileSource, function( event ){ - openTileSource( _this, event.tileSource ); - }); - tileSource.addHandler( 'open-failed', function ( event ) { - /** - * Raised when an error occurs loading a TileSource. - * - * @event open-failed - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {String} message - * @property {String} source - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - _this.raiseEvent( 'open-failed', event ); - }); - - } else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ){ - if( $.isFunction( tileSource.getTileUrl ) ){ - //Custom tile source - customTileSource = new $.TileSource(tileSource); - customTileSource.getTileUrl = tileSource.getTileUrl; - openTileSource( _this, customTileSource ); - } else { - //inline configuration - $TileSource = $.TileSource.determineType( _this, tileSource ); - if ( !$TileSource ) { - /*** - * Raised when an error occurs loading a TileSource. - * - * @event open-failed - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {String} message - * @property {String} source - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - _this.raiseEvent( 'open-failed', { - message: "Unable to load TileSource", - source: tileSource - }); - return; - } - options = $TileSource.prototype.configure.apply( _this, [ tileSource ]); - readySource = new $TileSource( options ); - openTileSource( _this, readySource ); - } - } else { - //can assume it's already a tile source implementation - openTileSource( _this, tileSource ); - } - }, 1); + getTileSourceImplementation( _this, tileSource, function( tileSource ) { + openTileSource( _this, tileSource ); + }, function( event ) { + /** + * Raised when an error occurs loading a TileSource. + * + * @event open-failed + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {String} message + * @property {String} source + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + _this.raiseEvent( 'open-failed', event ); + }); return this; }, @@ -1047,18 +992,155 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }, /** + * Add a layer. + * options.tileSource can be anything that {@link OpenSeadragon.Viewer#open} + * supports. * @function - * @name OpenSeadragon.Viewer.prototype.addLayer + * @param {Object} options + * @param {String|Object|Function} [options.tileSource] The TileSource of the layer. + * @param {Number} [options.opacity=1] The opacity of the layer. + * @param {Number} [options.level] The level of the layer. + * @returns {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:add-layer + * @fires OpenSeadragon.Viewer.event:add-layer-failed */ - addLayer: function( tileSource ) { - var drawer = new $.Drawer( - tileSource, - this.viewport, - this.canvas - ); - this.drawers.push( drawer ); - updateOnce( this ); - return drawer.canvas; + addLayer: function( options ) { + var _this = this, + tileSource = options.tileSource; + + if ( !tileSource ) { + throw new Error( "No tile source provided as new layer." ); + } + + getTileSourceImplementation( this, tileSource, function( tileSource ) { + var drawer = new $.Drawer({ + viewer: _this, + source: tileSource, + viewport: _this.viewport, + element: _this.canvas, + opacity: options.opacity !== undefined ? + options.opacity : _this.opacity, + maxImageCacheCount: _this.maxImageCacheCount, + imageLoaderLimit: _this.imageLoaderLimit, + minZoomImageRatio: _this.minZoomImageRatio, + wrapHorizontal: _this.wrapHorizontal, + wrapVertical: _this.wrapVertical, + immediateRender: _this.immediateRender, + blendTime: _this.blendTime, + alwaysBlend: _this.alwaysBlend, + minPixelRatio: _this.minPixelRatio, + timeout: _this.timeout, + debugMode: _this.debugMode, + debugGridColor: _this.debugGridColor + }); + _this.drawers.push( drawer ); + if ( options.level ) { + _this.setLayerLevel( drawer, options.level ); + } + THIS[ _this.hash ].forceRedraw = true; + /** + * Raised when a layer is successfully added. + * @event add-layer + * @memberOf OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {OpenSeadragon.Drawer} drawer The layer's underlying drawer. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + _this.raiseEvent( 'add-layer', { drawer: drawer } ); + }, function( event ) { + /** + * Raised when an error occurs while adding a layer. + * @event add-layer-failed + * @memberOf OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {String} message + * @property {String} source + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + _this.raiseEvent( 'add-layer-failed', event ); + } ); + + return this; + }, + + /** + * Get the level of the layer associated with the given drawer or -1 if not + * present. + * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer. + * @returns {Number} The level of the layer or -1 if not present. + */ + getLayerLevel: function( drawer ) { + return this.drawers.indexOf( drawer ); + }, + + /** + * Change the level of a layer so that it appears over or under others. + * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the changing + * level layer. + * @param {Number} level The new level + * @returns {OpenSeadragon.Viewer} Chainable. + */ + setLayerLevel: function( drawer, level ) { + var oldLevel = this.drawers.indexOf( drawer ); + if ( level === 0 || oldLevel === 0 ) { + throw new Error( "Cannot reassign base level." ); + } + if ( level > this.drawers.length - 1 ) { + throw new Error( "Level bigger than number of layers." ); + } + if ( level === oldLevel || oldLevel === -1 ) { + return this; + } + this.drawers.splice( oldLevel, 1 ); + this.drawers.splice( level, 0, drawer ); + this.canvas.removeChild( drawer.canvas ); + // Insert right after layer at level - 1 + var prevLevelCanvas = this.drawers[level - 1].canvas; + prevLevelCanvas.parentNode.insertBefore( drawer.canvas, + prevLevelCanvas.nextSibling ); + return this; + }, + + /** + * @function + * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer + * to remove + * @returns {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:remove-layer + */ + removeLayer: function( drawer ) { + var index = this.drawers.indexOf( drawer ); + if ( index === -1 ) { + return this; + } + if ( index === 0 ) { + throw new Error( "Cannot remove base layer." ); + } + + this.drawers.splice( index, 1 ); + this.canvas.removeChild( drawer.canvas ); + /** + * Raised when a layer is removed. + * @event remove-layer + * @memberOf OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {OpenSeadragon.Drawer} drawer The layer's underlying drawer. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'remove-layer', { drawer: drawer } ); + return this; + }, + + /** + * Force the viewer to redraw its drawers. + * @returns {OpenSeadragon.Viewer} Chainable. + */ + forceRedraw: function() { + THIS[ this.hash ].forceRedraw = true; + return this; }, /** @@ -1389,6 +1471,61 @@ function _getSafeElemSize (oElement) { ); } +/** + * @function + * @private + */ +function getTileSourceImplementation( viewer, tileSource, successCallback, + failCallback) { + var _this = viewer; + + //allow plain xml strings or json strings to be parsed here + if ( $.type( tileSource ) == 'string' ) { + if ( tileSource.match( /\s*<.*/ ) ) { + tileSource = $.parseXml( tileSource ); + } else if ( tileSource.match( /\s*[\{\[].*/ ) ) { + /*jshint evil:true*/ + tileSource = eval( '(' + tileSource + ')' ); + } + } + + setTimeout( function() { + if ( $.type( tileSource ) == 'string' ) { + //If its still a string it means it must be a url at this point + tileSource = new $.TileSource( tileSource, function( event ) { + successCallback( event.tileSource ); + }); + tileSource.addHandler( 'open-failed', function( event ) { + failCallback( event ); + } ); + + } else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ) { + if ( $.isFunction( tileSource.getTileUrl ) ) { + //Custom tile source + var customTileSource = new $.TileSource( tileSource ); + customTileSource.getTileUrl = tileSource.getTileUrl; + successCallback( customTileSource ); + } else { + //inline configuration + var $TileSource = $.TileSource.determineType( _this, tileSource ); + if ( !$TileSource ) { + failCallback( { + message: "Unable to load TileSource", + source: tileSource + }); + return; + } + var options = $TileSource.prototype.configure.apply( _this, [ tileSource ] ); + var readySource = new $TileSource( options ); + successCallback( readySource ); + } + } else { + //can assume it's already a tile source implementation + successCallback( tileSource ); + } + }, 1 ); +} + /** * @function * @private @@ -1462,6 +1599,7 @@ function openTileSource( viewer, source ) { viewport: _this.viewport, element: _this.canvas, overlays: [].concat( _this.overlays ).concat( _this.source.overlays ), + opacity: _this.opacity, maxImageCacheCount: _this.maxImageCacheCount, imageLoaderLimit: _this.imageLoaderLimit, minZoomImageRatio: _this.minZoomImageRatio, @@ -1475,7 +1613,7 @@ function openTileSource( viewer, source ) { debugMode: _this.debugMode, debugGridColor: _this.debugGridColor }); - _this.drawers.push( _this.drawer ); + _this.drawers = [_this.drawer]; //Instantiate a navigator if configured if ( _this.showNavigator && !_this.collectionMode ){ @@ -2015,15 +2153,18 @@ function resizeViewportAndRecenter( viewer, containerSize, oldBounds, oldCenter } function updateDrawers( viewer ) { - viewer.drawers.forEach( function( drawer ) { - drawer.update(); - }); + for (var i = 0; i < viewer.drawers.length; i++ ) { + viewer.drawers[i].update(); + } } function drawersNeedUpdate( viewer ) { - return viewer.drawers.some( function( drawer ) { - return drawer.needsUpdate(); - }); + for (var i = 0; i < viewer.drawers.length; i++ ) { + if (viewer.drawers[i].needsUpdate()) { + return true; + } + } + return false; } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/viewport.js b/src/viewport.js index 65230f4a..8625ff60 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -652,7 +652,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ throw new Error('Currently only 0, 90, 180, and 270 degrees are supported.'); } this.degrees = degrees; - this.viewer.drawer.update(); + this.viewer.forceRedraw(); return this; }, From c90a1edfb5b7c98f2cddf545e53064c13da28ec3 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 10 Dec 2013 19:22:15 -0500 Subject: [PATCH 03/15] Add layer-level-changed event and add $.indexOf polyfill --- src/openseadragon.js | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/viewer.js | 26 +++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index 2392b185..bc6e5e59 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -1332,6 +1332,50 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ } }, + /** + * Find the first index at which an element is found in an array or -1 + * if not present. + * + * Code taken and adapted from + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility + * + * @function + * @param {Array} array The array from which to find the element + * @param {Object} searchElement The element to find + * @param {Number} [fromIndex=0] Index to start research. + * @returns {Number} The index of the element in the array. + */ + indexOf: function( array, searchElement, fromIndex ) { + if ( Array.prototype.indexOf ) { + this.indexOf = Array.prototype.indexOf; + } else { + this.indexOf = function( array, searchElement, fromIndex ) { + var i, + pivot = ( fromIndex ) ? fromIndex : 0, + length; + if ( !array ) { + throw new TypeError( ); + } + + length = array.length; + if ( length === 0 || pivot >= length ) { + return -1; + } + + if ( pivot < 0 ) { + pivot = length - Math.abs( pivot ); + } + + for ( i = pivot; i < length; i++ ) { + if ( array[i] === searchElement ) { + return i; + } + } + return -1; + }; + } + return this.indexOf( array, searchElement, fromIndex ); + }, /** * Remove the specified CSS class from the element. diff --git a/src/viewer.js b/src/viewer.js index 99b7bbb4..2724a5ae 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1072,7 +1072,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @returns {Number} The level of the layer or -1 if not present. */ getLayerLevel: function( drawer ) { - return this.drawers.indexOf( drawer ); + return $.indexOf( this.drawers, drawer ); }, /** @@ -1081,13 +1081,14 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * level layer. * @param {Number} level The new level * @returns {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:layer-level-changed */ setLayerLevel: function( drawer, level ) { - var oldLevel = this.drawers.indexOf( drawer ); + var oldLevel = this.getLayerLevel( drawer ); if ( level === 0 || oldLevel === 0 ) { throw new Error( "Cannot reassign base level." ); } - if ( level > this.drawers.length - 1 ) { + if ( level >= this.drawers.length ) { throw new Error( "Level bigger than number of layers." ); } if ( level === oldLevel || oldLevel === -1 ) { @@ -1100,6 +1101,25 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, var prevLevelCanvas = this.drawers[level - 1].canvas; prevLevelCanvas.parentNode.insertBefore( drawer.canvas, prevLevelCanvas.nextSibling ); + + /** + * Raised when the order of the layers has been changed. + * @event layer-level-changed + * @memberOf OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {OpenSeadragon.Drawer} drawer - The drawer which level has + * been changed + * @property {Number} previousLevel - The previous level of the drawer + * @property {Number} newLevel - The new level of the drawer + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'layer-level-changed', { + drawer: drawer, + previousLevel: oldLevel, + newLevel: level + } ); + return this; }, From b542b905902ad08ac71cb51fc4668b8862d8b70c Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 5 Jan 2014 19:20:45 -0500 Subject: [PATCH 04/15] Fix $.indexOf pollyfill Add the original options in addLayer events Add layers demo page --- src/openseadragon.js | 12 ++-- src/viewer.js | 10 ++- test/data/A.png | Bin 0 -> 16312 bytes test/data/BBlue.png | Bin 0 -> 19128 bytes test/data/CCyan.png | Bin 0 -> 49573 bytes test/data/DDandelion.png | Bin 0 -> 9751 bytes test/demo/layers.html | 131 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 7 deletions(-) create mode 100644 test/data/A.png create mode 100644 test/data/BBlue.png create mode 100644 test/data/CCyan.png create mode 100644 test/data/DDandelion.png create mode 100644 test/demo/layers.html diff --git a/src/openseadragon.js b/src/openseadragon.js index bc6e5e59..d7f63116 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -433,10 +433,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ */ /* jshint ignore:start */ $.version = { - versionStr: '<%= osdVersion.versionStr %>', - major: <%= osdVersion.major %>, - minor: <%= osdVersion.minor %>, - revision: <%= osdVersion.revision %> +// versionStr: '<%= osdVersion.versionStr %>', +// major: <%= osdVersion.major %>, +// minor: <%= osdVersion.minor %>, +// revision: <%= osdVersion.revision %> }; /* jshint ignore:end */ @@ -1347,7 +1347,9 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ */ indexOf: function( array, searchElement, fromIndex ) { if ( Array.prototype.indexOf ) { - this.indexOf = Array.prototype.indexOf; + this.indexOf = function( array, searchElement, fromIndex ) { + return array.indexOf( searchElement, fromIndex ); + }; } else { this.indexOf = function( array, searchElement, fromIndex ) { var i, diff --git a/src/viewer.js b/src/viewer.js index 2724a5ae..62de5ccc 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1044,11 +1044,16 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @memberOf OpenSeadragon.Viewer * @type {object} * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {Object} options The options passed to the addLayer method. * @property {OpenSeadragon.Drawer} drawer The layer's underlying drawer. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( 'add-layer', { drawer: drawer } ); + _this.raiseEvent( 'add-layer', { + options: options, + drawer: drawer + }); }, function( event ) { + event.options = options; /** * Raised when an error occurs while adding a layer. * @event add-layer-failed @@ -1057,6 +1062,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. * @property {String} message * @property {String} source + * @property {Object} options The options passed to the addLayer method. * @property {?Object} userData - Arbitrary subscriber-defined object. */ _this.raiseEvent( 'add-layer-failed', event ); @@ -1496,7 +1502,7 @@ function _getSafeElemSize (oElement) { * @private */ function getTileSourceImplementation( viewer, tileSource, successCallback, - failCallback) { + failCallback ) { var _this = viewer; //allow plain xml strings or json strings to be parsed here diff --git a/test/data/A.png b/test/data/A.png new file mode 100644 index 0000000000000000000000000000000000000000..dde0744330f1a4975b34e3a4834a64e828aafd5a GIT binary patch literal 16312 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 literal 0 HcmV?d00001 diff --git a/test/data/BBlue.png b/test/data/BBlue.png new file mode 100644 index 0000000000000000000000000000000000000000..055d7ff463fa8ca6bd8afbfcdd9bc22a2c71686a GIT binary patch literal 19128 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 literal 0 HcmV?d00001 diff --git a/test/data/CCyan.png b/test/data/CCyan.png new file mode 100644 index 0000000000000000000000000000000000000000..40e56de2c7f017fb733f0bea36df8e3ef6b389ef GIT binary patch literal 49573 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 literal 0 HcmV?d00001 diff --git a/test/data/DDandelion.png b/test/data/DDandelion.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc849f2ade5dbf7feddc2f482d78af2369e004b GIT binary patch literal 9751 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 literal 0 HcmV?d00001 diff --git a/test/demo/layers.html b/test/demo/layers.html new file mode 100644 index 00000000..b1053b96 --- /dev/null +++ b/test/demo/layers.html @@ -0,0 +1,131 @@ + + + + OpenSeadragon Layers Demo + + + + + +
+ Simple demo page to show an OpenSeadragon viewer with layers. +
+
+ +
+
+ Availables layers
+ +
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ Used layers
+ +
+
+ + + + From 63af1e75170023a50a9d43fb161843573f328b9b Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sun, 5 Jan 2014 19:30:52 -0500 Subject: [PATCH 05/15] Uncomment version --- src/openseadragon.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openseadragon.js b/src/openseadragon.js index d7f63116..9ad73a17 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -433,10 +433,10 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ */ /* jshint ignore:start */ $.version = { -// versionStr: '<%= osdVersion.versionStr %>', -// major: <%= osdVersion.major %>, -// minor: <%= osdVersion.minor %>, -// revision: <%= osdVersion.revision %> + versionStr: '<%= osdVersion.versionStr %>', + major: <%= osdVersion.major %>, + minor: <%= osdVersion.minor %>, + revision: <%= osdVersion.revision %> }; /* jshint ignore:end */ From 56707bd4f7d52396b379375c40ae75448bb8685c Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 11 Jan 2014 12:52:44 -0500 Subject: [PATCH 06/15] Add viewer.getNumberOfLayers method layers.html test file: -display 2 layers after opening -fix order -add opacity demo -add rotation demo --- src/viewer.js | 8 +++ test/demo/layers.html | 117 ++++++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 27 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index 62de5ccc..e6a6b54c 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1081,6 +1081,14 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, return $.indexOf( this.drawers, drawer ); }, + /** + * Get the number of layers used. + * @returns {Number} The number of layers used. + */ + getNumberOfLayers: function() { + return this.drawers.length - 1; + }, + /** * Change the level of a layer so that it appears over or under others. * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the changing diff --git a/test/demo/layers.html b/test/demo/layers.html index b1053b96..1803d2f6 100644 --- a/test/demo/layers.html +++ b/test/demo/layers.html @@ -12,10 +12,12 @@ float: left; } - #availables, #used, #arrows, #left, #updown, #right { + #availables, #arrows, #left, #updown, #right { float: left; } + + @@ -28,63 +30,95 @@
Availables layers
- +
- +
- +
Used layers
+ +



+ +
+ Opacity:
+ +
+ +
+ +
+ +
+
+ +



+ +
+ +
From f0f76b847e0202954176f45c763191de5dfee886 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 11 Jan 2014 18:08:04 -0500 Subject: [PATCH 07/15] Add viewer.getLayerAtLevel method and rename getLayerLevel to getLevelOfLayer. Add layers unit test. --- src/viewer.js | 16 +++++- test/layers.js | 129 +++++++++++++++++++++++++++++++++++++++++++++++++ test/test.css | 5 ++ test/test.html | 1 + 4 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 test/layers.js diff --git a/src/viewer.js b/src/viewer.js index e6a6b54c..5e536336 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1071,13 +1071,25 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, return this; }, + /** + * Get the layer at the specified level. + * @param {Number} level The layer to retrieve level. + * @returns {OpenSeadragon.Drawer} The layer at the specified level. + */ + getLayerAtLevel: function( level ) { + if ( level >= this.drawers.length ) { + throw new Error( "Level bigger than number of layers." ); + } + return this.drawers[ level ]; + }, + /** * Get the level of the layer associated with the given drawer or -1 if not * present. * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer. * @returns {Number} The level of the layer or -1 if not present. */ - getLayerLevel: function( drawer ) { + getLevelOfLayer: function( drawer ) { return $.indexOf( this.drawers, drawer ); }, @@ -1098,7 +1110,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * @fires OpenSeadragon.Viewer.event:layer-level-changed */ setLayerLevel: function( drawer, level ) { - var oldLevel = this.getLayerLevel( drawer ); + var oldLevel = this.getLevelOfLayer( drawer ); if ( level === 0 || oldLevel === 0 ) { throw new Error( "Cannot reassign base level." ); } diff --git a/test/layers.js b/test/layers.js new file mode 100644 index 00000000..a0fe4883 --- /dev/null +++ b/test/layers.js @@ -0,0 +1,129 @@ +/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */ + +( function() { + var viewer; + + module( 'Layers', { + setup: function() { + $( '
' ).appendTo( "#qunit-fixture" ); + + testLog.reset(); + + viewer = OpenSeadragon( { + id: 'layersexample', + prefixUrl: '/build/openseadragon/images/', + springStiffness: 100 // Faster animation = faster tests + }); + }, + teardown: function() { + if ( viewer && viewer.close ) { + viewer.close(); + } + + viewer = null; + $("#layersexample").remove(); + } + } ); + + // ---------- + asyncTest( 'Layers operations', function() { + expect( 22 ); + + viewer.addHandler( "open", function() { + equal( 0, viewer.getNumberOfLayers(), + "No layer should be present after opening." ); + + var options = { + tileSource: { + type: 'legacy-image-pyramid', + levels: [ { + url: "test/data/A.png", + width: 1000, + height: 1000 + } ] + } + }; + viewer.addLayer( options ); + viewer.addHandler( "add-layer", function addFirstLayerHandler( event ) { + viewer.removeHandler( "add-layer", addFirstLayerHandler ); + var layer1 = event.drawer; + + equal( viewer.getNumberOfLayers(), 1, + "1 layer should be present after adding a layer." ); + equal( options, event.options, + "The options should be transmitted via the event." ); + equal( viewer.getLevelOfLayer( layer1 ), 1, + "The first added layer should have a level of 1" ); + equal( viewer.getLayerAtLevel( 1 ), layer1, + "The layer at level 1 should be the first added layer." ); + + viewer.addLayer( options ); + viewer.addHandler( "add-layer", function addSecondLayerHandler( event ) { + viewer.removeHandler( "add-layer", addSecondLayerHandler ); + var layer2 = event.drawer; + + equal( viewer.getNumberOfLayers(), 2, + "2 layers should be present after adding a second layer." ); + equal( viewer.getLevelOfLayer( layer2 ), 2, + "If not specified, a layer should be added with the highest level." ); + equal( viewer.getLayerAtLevel( 2 ), layer2, + "The layer at level 2 should be the second added layer." ); + + viewer.addHandler( "layer-level-changed", + function layerLevelChangedHandler( event ) { + viewer.removeHandler( "layer-level-changed", + layerLevelChangedHandler ); + equal( event.drawer, layer2, + "The layer which changed level should be layer2" ); + equal( event.previousLevel, 2, "Previous level should be 2." ); + equal( event.newLevel, 1, "New level should be 1." ); + }); + viewer.setLayerLevel( layer2, 1 ); + equal( viewer.getLevelOfLayer( layer2 ), 1, + "Layer2 level should be 1 after setLayerLevel." ); + equal( viewer.getLevelOfLayer( layer1 ), 2, + "Layer1 level should be 2 after setLayerLevel." ); + equal( viewer.getLayerAtLevel( 1 ), layer2, + "The layer at level 1 should be layer2." ); + equal( viewer.getLayerAtLevel( 2 ), layer1, + "The layer at level 2 should be layer1." ); + + options.level = 2; + options.tileSource.levels[0].url = "test/data/CCyan.png"; + options.opacity = 0.5; + viewer.addLayer( options ); + viewer.addHandler( "add-layer", function addThirdLayerHandler( event ) { + viewer.removeHandler( "add-layer", addThirdLayerHandler ); + var layer3 = event.drawer; + + equal( viewer.getNumberOfLayers(), 3, + "3 layers should be present after adding a third layer." ); + equal( viewer.getLevelOfLayer( layer3 ), 2, + "Layer 3 should be added with level 2." ); + equal( viewer.getLevelOfLayer( layer2 ), 1, + "Layer 2 should stay at level 1." ); + + viewer.addHandler( "remove-layer", function removeLayerHandler( event ) { + viewer.removeHandler( "remove-layer", removeLayerHandler ); + + equal( layer2, event.drawer, "Removed layer should be layer2." ); + + equal( viewer.getLevelOfLayer( layer1 ), 2, + "Layer 1 should be at level 2." ); + equal( viewer.getLevelOfLayer( layer2 ), -1, + "Layer 2 should be at level -1." ); + equal( viewer.getLevelOfLayer( layer3 ), 1, + "Layer 3 should be at level 1." ); + + }); + viewer.removeLayer( layer2 ); + start(); + }); + }); + }); + }); + viewer.open( '/test/data/testpattern.dzi' ); + }); + + +})(); diff --git a/test/test.css b/test/test.css index eb651b16..4eb71894 100644 --- a/test/test.css +++ b/test/test.css @@ -17,3 +17,8 @@ height: 500px; width: 500px; } + +#layersexample { + height: 500px; + width: 500px; +} diff --git a/test/test.html b/test/test.html index 0f46fa30..bfe9e345 100644 --- a/test/test.html +++ b/test/test.html @@ -27,5 +27,6 @@ + From cd62ba7eca66769d9e1062fadf78ea42ad62b89a Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 11 Jan 2014 19:15:28 -0500 Subject: [PATCH 08/15] Prevent adding collections as layers. --- src/viewer.js | 38 ++++++++++++++++++++++++++------------ test/layers.js | 39 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index 5e536336..6d57d945 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1012,7 +1012,32 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, throw new Error( "No tile source provided as new layer." ); } + function raiseAddLayerFailed( event ) { + /** + * Raised when an error occurs while adding a layer. + * @event add-layer-failed + * @memberOf OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {String} message + * @property {String} source + * @property {Object} options The options passed to the addLayer method. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + _this.raiseEvent( 'add-layer-failed', event ); + } + getTileSourceImplementation( this, tileSource, function( tileSource ) { + + if ( tileSource instanceof Array ) { + raiseAddLayerFailed({ + message: "Collections can not be added as layers.", + source: tileSource, + options: options + }); + return; + } + var drawer = new $.Drawer({ viewer: _this, source: tileSource, @@ -1054,18 +1079,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }); }, function( event ) { event.options = options; - /** - * Raised when an error occurs while adding a layer. - * @event add-layer-failed - * @memberOf OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {String} message - * @property {String} source - * @property {Object} options The options passed to the addLayer method. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - _this.raiseEvent( 'add-layer-failed', event ); + raiseAddLayerFailed(event); } ); return this; diff --git a/test/layers.js b/test/layers.js index a0fe4883..50aff801 100644 --- a/test/layers.js +++ b/test/layers.js @@ -37,7 +37,7 @@ tileSource: { type: 'legacy-image-pyramid', levels: [ { - url: "test/data/A.png", + url: "data/A.png", width: 1000, height: 1000 } ] @@ -89,7 +89,7 @@ "The layer at level 2 should be layer1." ); options.level = 2; - options.tileSource.levels[0].url = "test/data/CCyan.png"; + options.tileSource.levels[0].url = "data/CCyan.png"; options.opacity = 0.5; viewer.addLayer( options ); viewer.addHandler( "add-layer", function addThirdLayerHandler( event ) { @@ -125,5 +125,40 @@ viewer.open( '/test/data/testpattern.dzi' ); }); + asyncTest( 'Collections as layers', function() { + var options = { + tileSource: [{ + type: 'legacy-image-pyramid', + levels: [{ + url: "data/A.png", + width: 1000, + height: 1000 + }] + }, { + type: 'legacy-image-pyramid', + levels: [{ + url: "data/BBlue.png", + width: 1000, + height: 1000 + }] + }] + }; + + viewer.addHandler( "open", function openHandler() { + viewer.removeHandler( "open", openHandler ); + + viewer.addHandler( "add-layer-failed", + function addLayerFailedHandler( event ) { + viewer.removeHandler( "add-layer-failed", addLayerFailedHandler ); + + equal( event.message, "Collections can not be added as layers." ); + equal( event.options, options, "Layer failed event should give the options." ); + start(); + } ); + viewer.addLayer( options ); + + }); + viewer.open( '/test/data/testpattern.dzi' ); + }); })(); From facccf7b455362b22b62f844bc5e2b20b01fe922 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Sat, 11 Jan 2014 19:25:27 -0500 Subject: [PATCH 09/15] Throw an error when trying to add a layer in collection mode. --- src/viewer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/viewer.js b/src/viewer.js index 6d57d945..bc40160a 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1011,6 +1011,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, if ( !tileSource ) { throw new Error( "No tile source provided as new layer." ); } + if ( this.collectionMode ) { + throw new Error( "Layers not supported in collection mode." ); + } function raiseAddLayerFailed( event ) { /** From 1c1cd0dc4df7f12754603f50735ed90e00973b22 Mon Sep 17 00:00:00 2001 From: Antoine Vandecreme Date: Tue, 21 Jan 2014 19:24:47 -0500 Subject: [PATCH 10/15] Allow base layer reassignment if not in sequence mode. --- src/viewer.js | 38 ++++++++---- test/demo/layers.html | 2 +- test/layers.js | 134 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 143 insertions(+), 31 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index bc40160a..d3847f95 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -994,7 +994,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, /** * Add a layer. * options.tileSource can be anything that {@link OpenSeadragon.Viewer#open} - * supports. + * supports except arrays of images as layers cannot be sequences. * @function * @param {Object} options * @param {String|Object|Function} [options.tileSource] The TileSource of the layer. @@ -1008,6 +1008,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, var _this = this, tileSource = options.tileSource; + if ( !this.isOpen() ) { + throw new Error( "An image must be loaded before adding layers." ); + } if ( !tileSource ) { throw new Error( "No tile source provided as new layer." ); } @@ -1034,7 +1037,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, if ( tileSource instanceof Array ) { raiseAddLayerFailed({ - message: "Collections can not be added as layers.", + message: "Sequences can not be added as layers.", source: tileSource, options: options }); @@ -1062,7 +1065,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, debugGridColor: _this.debugGridColor }); _this.drawers.push( drawer ); - if ( options.level ) { + if ( options.level !== undefined ) { _this.setLayerLevel( drawer, options.level ); } THIS[ _this.hash ].forceRedraw = true; @@ -1114,8 +1117,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, * Get the number of layers used. * @returns {Number} The number of layers used. */ - getNumberOfLayers: function() { - return this.drawers.length - 1; + getLayersCount: function() { + return this.drawers.length; }, /** @@ -1128,22 +1131,33 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, */ setLayerLevel: function( drawer, level ) { var oldLevel = this.getLevelOfLayer( drawer ); - if ( level === 0 || oldLevel === 0 ) { - throw new Error( "Cannot reassign base level." ); - } + if ( level >= this.drawers.length ) { throw new Error( "Level bigger than number of layers." ); } if ( level === oldLevel || oldLevel === -1 ) { return this; } + if ( level === 0 || oldLevel === 0 ) { + if ( THIS[ this.hash ].sequenced ) { + throw new Error( "Cannot reassign base level when in sequence mode." ); + } + // We need to re-assign the base drawer + this.drawer = level === 0 ? drawer : this.getLayerAtLevel( level ); + } this.drawers.splice( oldLevel, 1 ); this.drawers.splice( level, 0, drawer ); this.canvas.removeChild( drawer.canvas ); - // Insert right after layer at level - 1 - var prevLevelCanvas = this.drawers[level - 1].canvas; - prevLevelCanvas.parentNode.insertBefore( drawer.canvas, - prevLevelCanvas.nextSibling ); + if ( level === 0 ) { + var nextLevelCanvas = this.drawers[ 1 ].canvas; + nextLevelCanvas.parentNode.insertBefore( drawer.canvas, + nextLevelCanvas ); + } else { + // Insert right after layer at level - 1 + var prevLevelCanvas = this.drawers[level - 1].canvas; + prevLevelCanvas.parentNode.insertBefore( drawer.canvas, + prevLevelCanvas.nextSibling ); + } /** * Raised when the order of the layers has been changed. diff --git a/test/demo/layers.html b/test/demo/layers.html index 1803d2f6..87467480 100644 --- a/test/demo/layers.html +++ b/test/demo/layers.html @@ -28,7 +28,7 @@
- Availables layers
+ Available layers