From 33f0fa1e4b9083bdb1d27ca05ab3fad4d71deb8c Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 19 Aug 2014 15:02:04 -0700 Subject: [PATCH] World cleanup; viewer layer function deprecation --- src/viewer.js | 155 ++++++++++++++++++++++--------------------- src/world.js | 180 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 200 insertions(+), 135 deletions(-) diff --git a/src/viewer.js b/src/viewer.js index d8c99b52..367aeb53 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1032,59 +1032,54 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }, /** - * Add a layer. + * Add a tiled image to the viewer. * options.tileSource can be anything that {@link OpenSeadragon.Viewer#open} - * supports except arrays of images as layers cannot be sequences. + * supports except arrays of images. * Note that you can specify options.width or options.height, but not both. - * The other dimension will be calculated according to the layer's aspect ratio. + * The other dimension will be calculated according to the item's aspect ratio. * @function * @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. Added on top of - * all other layers if not specified. + * @param {String|Object|Function} options.tileSource - The TileSource of the item. + * @param {Number} [options.index] The index of the item. Added on top of + * all other items if not specified. * @param {Number} [options.x=0] The X position for the image in world coordinates. * @param {Number} [options.y=0] The Y position for the image in world coordinates. * @param {Number} [options.width=1] The width for the image in world coordinates. * @param {Number} [options.height] The height for the image in world coordinates. - * @returns {OpenSeadragon.Viewer} Chainable. - * @fires OpenSeadragon.Viewer.event:add-layer - * @fires OpenSeadragon.Viewer.event:add-layer-failed + * @fires OpenSeadragon.World.event:add-item + * @fires OpenSeadragon.Viewer.event:add-item-failed */ - addLayer: function( options ) { + addTiledImage: function( options ) { + $.console.assert(options, "[Viewer.addTiledImage] options is required"); + $.console.assert(options.tileSource, "[Viewer.addTiledImage] options.tileSource is required"); + 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." ); - } - if ( this.collectionMode ) { - throw new Error( "Layers not supported in collection mode." ); + throw new Error( "An image must be loaded before adding additional images." ); } - function raiseAddLayerFailed( event ) { + function raiseAddItemFailed( event ) { /** - * Raised when an error occurs while adding a layer. - * @event add-layer-failed + * Raised when an error occurs while adding a item. + * @event add-item-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} options The options passed to the addTiledImage method. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - _this.raiseEvent( 'add-layer-failed', event ); + _this.raiseEvent( 'add-item-failed', event ); } getTileSourceImplementation( this, tileSource, function( tileSource ) { if ( tileSource instanceof Array ) { - raiseAddLayerFailed({ - message: "Sequences can not be added as layers.", + raiseAddItemFailed({ + message: "[Viewer.addTiledImage] Sequences can not be added.", source: tileSource, options: options }); @@ -1113,88 +1108,87 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, debugMode: _this.debugMode, debugGridColor: _this.debugGridColor }); - _this.world.addItem( tiledImage ); - _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); - if ( options.level !== undefined ) { - _this.world.setItemLevel( tiledImage, 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 {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', { - options: options, - drawer: tiledImage + + _this.world.addItem( tiledImage, { + index: options.index }); }, function( event ) { event.options = options; - raiseAddLayerFailed(event); + raiseAddItemFailed(event); } ); + }, + /** + * @function + * @private + */ + addLayer: function( options ) { + var self = this; + + $.console.error( "[Viewer.addLayer] this function is deprecated; use Viewer.addTiledImage() instead." ); + + var addItemHandler = function(event) { + self.world.removeHandler("add-item", addItemHandler); + self.raiseEvent("add-layer", { + options: options, + drawer: event.item + }); + }; + + var failureHandler = function(event) { + self.removeHandler("add-item-failed", failureHandler); + self.raiseEvent("add-layer-failed", event); + }; + + this.world.addHandler("add-item", addItemHandler); + this.addHandler("add-item-failed", failureHandler); + this.addTiledImage(options); 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. + * @function + * @private */ getLayerAtLevel: function( level ) { - $.console.error( "[Viewer.getLayerAtLevel] this function is deprecated." ); - return null; + $.console.error( "[Viewer.getLayerAtLevel] this function is deprecated; use World.getItemAt() instead." ); + return this.world.getItemAt(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. + * @function + * @private */ getLevelOfLayer: function( drawer ) { - $.console.error( "[Viewer.getLevelOfLayer] this function is deprecated." ); - return -1; + $.console.error( "[Viewer.getLevelOfLayer] this function is deprecated; use World.getIndexOfItem() instead." ); + return this.world.getIndexOfItem(drawer); }, /** - * Get the number of layers used. - * @returns {Number} The number of layers used. + * @function + * @private */ getLayersCount: function() { - $.console.error( "[Viewer.getLayersCount] this function is deprecated." ); - return 0; + $.console.error( "[Viewer.getLayersCount] this function is deprecated; use World.getItemCount() instead." ); + return this.world.getItemCount(); }, /** - * 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. - * @fires OpenSeadragon.Viewer.event:layer-level-changed + * @function + * @private */ setLayerLevel: function( drawer, level ) { - $.console.error( "[Viewer.setLayerLevel] this function is deprecated." ); - return this; + $.console.error( "[Viewer.setLayerLevel] this function is deprecated; use World.setItemIndex() instead." ); + return this.world.setItemIndex(drawer, level); }, /** - * Remove a layer. If there is only one layer, close the viewer. * @function - * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer - * to remove - * @returns {OpenSeadragon.Viewer} Chainable. - * @fires OpenSeadragon.Viewer.event:remove-layer + * @private */ removeLayer: function( drawer ) { - $.console.error( "[Viewer.removeLayer] this function is deprecated." ); - return this; + $.console.error( "[Viewer.removeLayer] this function is deprecated; use World.removeItem() instead." ); + return this.world.removeItem(drawer); }, /** @@ -1896,6 +1890,16 @@ function openTileSource( viewer, source, options ) { viewer: _this }); + _this.world.addHandler('add-item', function(event) { + _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); + THIS[ _this.hash ].forceRedraw = true; + }); + + _this.world.addHandler('remove-item', function(event) { + _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); + THIS[ _this.hash ].forceRedraw = true; + }); + _this.drawer = new $.Drawer({ viewer: _this, viewport: _this.viewport, @@ -1929,7 +1933,6 @@ function openTileSource( viewer, source, options ) { }); _this.world.addItem( tiledImage ); - _this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); _this.viewport.goHome( true ); // Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons diff --git a/src/world.js b/src/world.js index 1b2245b3..a9299edb 100644 --- a/src/world.js +++ b/src/world.js @@ -35,41 +35,77 @@ (function( $ ){ /** + * Keeps track of all of the tiled images in the scene. + * * @class World * @classdesc - */ + * + * @memberof OpenSeadragon + * @extends OpenSeadragon.EventSource + * @param {OpenSeadragon.Options} options - World options. + **/ $.World = function( options ) { $.console.assert( options.viewer, "[World] options.viewer is required" ); + $.EventSource.call( this ); + this.viewer = options.viewer; this._items = []; this._figureSizes(); }; -$.World.prototype = /** @lends OpenSeadragon.World.prototype */{ - addItem: function( item ) { - this._items.push( item ); - this._figureSizes(); - }, - +$.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.World.prototype */{ /** - * Get the item at the specified level. - * @param {Number} level The item to retrieve level. - * @returns {OpenSeadragon.TiledImage} The item at the specified level. + * Add the specified item. + * @param {OpenSeadragon.TiledImage} item - The item to add. + * @param {Number} options.index - index for the item (optional). + * @fires OpenSeadragon.World.event:add-item */ - getItemAt: function( level ) { - if ( level >= this._items.length ) { - throw new Error( "Level bigger than number of items." ); + addItem: function( item, options ) { + $.console.assert(item, "[World.addItem] item is required"); + $.console.assert(item instanceof $.TiledImage, "[World.addItem] only TiledImages supported at this time"); + + options = options || {}; + if (options.index !== undefined) { + var index = Math.max(0, Math.min(this._items.length, options.index)); + this._items.splice(index, 0, item); + } else { + this._items.push( item ); } - return this._items[ level ]; + + this._figureSizes(); + + /** + * Raised when an item is added to the World. + * @event add-item + * @memberOf OpenSeadragon.World + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the World which raised the event. + * @property {OpenSeadragon.Drawer} item - The item that has been added + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'add-item', { + item: item + } ); }, /** - * Get the level of the given item or -1 if not present. - * @param {OpenSeadragon.TiledImage} item The item. - * @returns {Number} The level of the item or -1 if not present. + * Get the item at the specified index. + * @param {Number} index - The item's index. + * @returns {OpenSeadragon.TiledImage} The item at the specified index. */ - getLevelOfItem: function( item ) { + getItemAt: function( index ) { + $.console.assert(index !== 'undefined', "[World.getItemAt] index is required"); + return this._items[ index ]; + }, + + /** + * Get the index of the given item or -1 if not present. + * @param {OpenSeadragon.TiledImage} item - The item. + * @returns {Number} The index of the item or -1 if not present. + */ + getIndexOfItem: function( item ) { + $.console.assert(item, "[World.getIndexOfItem] item is required"); return $.indexOf( this._items, item ); }, @@ -82,52 +118,56 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ }, /** - * 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 - * @fires OpenSeadragon.Viewer.event:layer-level-changed + * Change the index of a item so that it appears over or under others. + * @param {OpenSeadragon.TiledImage} item - The item to move. + * @param {Number} index - The new index. + * @fires OpenSeadragon.World.event:item-index-changed */ - setItemLevel: function( item, level ) { - var oldLevel = this.getLevelOfItem( item ); + setItemIndex: function( item, index ) { + $.console.assert(item, "[World.setItemIndex] item is required"); + $.console.assert(index !== 'undefined', "[World.setItemIndex] index is required"); - if ( level >= this._items.length ) { - throw new Error( "Level bigger than number of layers." ); + var oldIndex = this.getIndexOfItem( item ); + + if ( index >= this._items.length ) { + throw new Error( "Index bigger than number of layers." ); } - if ( level === oldLevel || oldLevel === -1 ) { + + if ( index === oldIndex || oldIndex === -1 ) { return; } - this._items.splice( oldLevel, 1 ); - this._items.splice( level, 0, item ); + + this._items.splice( oldIndex, 1 ); + this._items.splice( index, 0, item ); /** - * Raised when the order of the layers has been changed. - * @event layer-level-changed - * @memberOf OpenSeadragon.Viewer + * Raised when the order of the indexes has been changed. + * @event item-index-changed + * @memberOf OpenSeadragon.World * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {OpenSeadragon.Drawer} drawer - The drawer which level has + * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event. + * @property {OpenSeadragon.TiledImage} item - The item whose index has * been changed - * @property {Number} previousLevel - The previous level of the drawer - * @property {Number} newLevel - The new level of the drawer + * @property {Number} previousIndex - The previous index of the item + * @property {Number} newIndex - The new index of the item * @property {?Object} userData - Arbitrary subscriber-defined object. */ - // TODO: deprecate - this.viewer.raiseEvent( 'layer-level-changed', { - drawer: item, - previousLevel: oldLevel, - newLevel: level + this.raiseEvent( 'item-index-changed', { + item: item, + previousIndex: oldIndex, + newIndex: index } ); }, /** - * Remove a layer. If there is only one layer, close the viewer. + * Remove an item. * @function - * @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer - * to remove - * @fires OpenSeadragon.Viewer.event:remove-layer + * @param {OpenSeadragon.TiledImage} item - The item to remove. + * @fires OpenSeadragon.World.event:remove-item */ removeItem: function( item ) { + $.console.assert(item, "[World.removeItem] item is required"); + var index = this._items.indexOf( item ); if ( index === -1 ) { return; @@ -137,30 +177,41 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ this._figureSizes(); /** - * Raised when a layer is removed. - * @event remove-layer - * @memberOf OpenSeadragon.Viewer + * Raised when a item is removed. + * @event remove-item + * @memberOf OpenSeadragon.World * @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 {OpenSeadragon.World} eventSource - A reference to the World which raised the event. + * @property {OpenSeadragon.TiledImage} item - The item's underlying item. * @property {?Object} userData - Arbitrary subscriber-defined object. */ - // TODO: deprecate - this.raiseEvent( 'remove-layer', { drawer: item } ); + this.raiseEvent( 'remove-item', { item: item } ); }, - resetTiles: function() { + /** + * Clears all tiles and triggers updates for all items. + * @function + */ + resetItems: function() { for ( var i = 0; i < this._items.length; i++ ) { this._items[i].reset(); } }, + /** + * Updates (i.e. draws) all items. + * @function + */ update: function() { for ( var i = 0; i < this._items.length; i++ ) { this._items[i].update(); } }, + /** + * @function + * @returns {Boolean} true if any items need updating. + */ needsUpdate: function() { for ( var i = 0; i < this._items.length; i++ ) { if ( this._items[i].needsUpdate() ) { @@ -170,18 +221,29 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ return false; }, + /** + * @function + * @returns {OpenSeadragon.Rect} the smallest rectangle that encloses all items, in world coordinates. + */ getHomeBounds: function() { return this._homeBounds.clone(); }, - getContentSize: function() { - return this._contentSize.clone(); - }, - + /** + * To facilitate zoom constraints, we keep track of the pixel density of the + * densest item in the World (i.e. the item whose content size to world size + * ratio is the highest) and save it as this "content factor". + * @function + * @returns {Number} the number of content units per world unit. + */ getContentFactor: function() { return this._contentFactor; }, + /** + * @function + * @private + */ _figureSizes: function() { if ( !this._items.length ) { this._homeBounds = new $.Rect(0, 0, 1, 1); @@ -209,6 +271,6 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{ this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor, this._homeBounds.height * this._contentFactor); } -}; +}); }( OpenSeadragon ));