World cleanup; viewer layer function deprecation

This commit is contained in:
Ian Gilman 2014-08-19 15:02:04 -07:00
parent 796588ace2
commit 33f0fa1e4b
2 changed files with 200 additions and 135 deletions

View File

@ -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} * 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. * 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 * @function
* @param {Object} options * @param {Object} options
* @param {String|Object|Function} options.tileSource The TileSource of the layer. * @param {String|Object|Function} options.tileSource - The TileSource of the item.
* @param {Number} [options.opacity=1] The opacity of the layer. * @param {Number} [options.index] The index of the item. Added on top of
* @param {Number} [options.level] The level of the layer. Added on top of * all other items if not specified.
* all other layers if not specified.
* @param {Number} [options.x=0] The X position for the image in world coordinates. * @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.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.width=1] The width for the image in world coordinates.
* @param {Number} [options.height] The height for the image in world coordinates. * @param {Number} [options.height] The height for the image in world coordinates.
* @returns {OpenSeadragon.Viewer} Chainable. * @fires OpenSeadragon.World.event:add-item
* @fires OpenSeadragon.Viewer.event:add-layer * @fires OpenSeadragon.Viewer.event:add-item-failed
* @fires OpenSeadragon.Viewer.event:add-layer-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, var _this = this,
tileSource = options.tileSource; tileSource = options.tileSource;
if ( !this.isOpen() ) { if ( !this.isOpen() ) {
throw new Error( "An image must be loaded before adding layers." ); throw new Error( "An image must be loaded before adding additional images." );
}
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 ) { function raiseAddItemFailed( event ) {
/** /**
* Raised when an error occurs while adding a layer. * Raised when an error occurs while adding a item.
* @event add-layer-failed * @event add-item-failed
* @memberOf OpenSeadragon.Viewer * @memberOf OpenSeadragon.Viewer
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
* @property {String} message * @property {String} message
* @property {String} source * @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. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
_this.raiseEvent( 'add-layer-failed', event ); _this.raiseEvent( 'add-item-failed', event );
} }
getTileSourceImplementation( this, tileSource, function( tileSource ) { getTileSourceImplementation( this, tileSource, function( tileSource ) {
if ( tileSource instanceof Array ) { if ( tileSource instanceof Array ) {
raiseAddLayerFailed({ raiseAddItemFailed({
message: "Sequences can not be added as layers.", message: "[Viewer.addTiledImage] Sequences can not be added.",
source: tileSource, source: tileSource,
options: options options: options
}); });
@ -1113,88 +1108,87 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
debugMode: _this.debugMode, debugMode: _this.debugMode,
debugGridColor: _this.debugGridColor debugGridColor: _this.debugGridColor
}); });
_this.world.addItem( tiledImage );
_this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor()); _this.world.addItem( tiledImage, {
if ( options.level !== undefined ) { index: options.index
_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
}); });
}, function( event ) { }, function( event ) {
event.options = options; 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; return this;
}, },
/** /**
* Get the layer at the specified level. * @function
* @param {Number} level The layer to retrieve level. * @private
* @returns {OpenSeadragon.Drawer} The layer at the specified level.
*/ */
getLayerAtLevel: function( level ) { getLayerAtLevel: function( level ) {
$.console.error( "[Viewer.getLayerAtLevel] this function is deprecated." ); $.console.error( "[Viewer.getLayerAtLevel] this function is deprecated; use World.getItemAt() instead." );
return null; return this.world.getItemAt(level);
}, },
/** /**
* Get the level of the layer associated with the given drawer or -1 if not * @function
* present. * @private
* @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer.
* @returns {Number} The level of the layer or -1 if not present.
*/ */
getLevelOfLayer: function( drawer ) { getLevelOfLayer: function( drawer ) {
$.console.error( "[Viewer.getLevelOfLayer] this function is deprecated." ); $.console.error( "[Viewer.getLevelOfLayer] this function is deprecated; use World.getIndexOfItem() instead." );
return -1; return this.world.getIndexOfItem(drawer);
}, },
/** /**
* Get the number of layers used. * @function
* @returns {Number} The number of layers used. * @private
*/ */
getLayersCount: function() { getLayersCount: function() {
$.console.error( "[Viewer.getLayersCount] this function is deprecated." ); $.console.error( "[Viewer.getLayersCount] this function is deprecated; use World.getItemCount() instead." );
return 0; return this.world.getItemCount();
}, },
/** /**
* Change the level of a layer so that it appears over or under others. * @function
* @param {OpenSeadragon.Drawer} drawer The underlying drawer of the changing * @private
* level layer.
* @param {Number} level The new level
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:layer-level-changed
*/ */
setLayerLevel: function( drawer, level ) { setLayerLevel: function( drawer, level ) {
$.console.error( "[Viewer.setLayerLevel] this function is deprecated." ); $.console.error( "[Viewer.setLayerLevel] this function is deprecated; use World.setItemIndex() instead." );
return this; return this.world.setItemIndex(drawer, level);
}, },
/** /**
* Remove a layer. If there is only one layer, close the viewer.
* @function * @function
* @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer * @private
* to remove
* @returns {OpenSeadragon.Viewer} Chainable.
* @fires OpenSeadragon.Viewer.event:remove-layer
*/ */
removeLayer: function( drawer ) { removeLayer: function( drawer ) {
$.console.error( "[Viewer.removeLayer] this function is deprecated." ); $.console.error( "[Viewer.removeLayer] this function is deprecated; use World.removeItem() instead." );
return this; return this.world.removeItem(drawer);
}, },
/** /**
@ -1896,6 +1890,16 @@ function openTileSource( viewer, source, options ) {
viewer: _this 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({ _this.drawer = new $.Drawer({
viewer: _this, viewer: _this,
viewport: _this.viewport, viewport: _this.viewport,
@ -1929,7 +1933,6 @@ function openTileSource( viewer, source, options ) {
}); });
_this.world.addItem( tiledImage ); _this.world.addItem( tiledImage );
_this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor());
_this.viewport.goHome( true ); _this.viewport.goHome( true );
// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons // Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons

View File

@ -35,41 +35,77 @@
(function( $ ){ (function( $ ){
/** /**
* Keeps track of all of the tiled images in the scene.
*
* @class World * @class World
* @classdesc * @classdesc
*/ *
* @memberof OpenSeadragon
* @extends OpenSeadragon.EventSource
* @param {OpenSeadragon.Options} options - World options.
**/
$.World = function( options ) { $.World = function( options ) {
$.console.assert( options.viewer, "[World] options.viewer is required" ); $.console.assert( options.viewer, "[World] options.viewer is required" );
$.EventSource.call( this );
this.viewer = options.viewer; this.viewer = options.viewer;
this._items = []; this._items = [];
this._figureSizes(); this._figureSizes();
}; };
$.World.prototype = /** @lends OpenSeadragon.World.prototype */{ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.World.prototype */{
addItem: function( item ) { /**
* 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
*/
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 ); this._items.push( item );
this._figureSizes();
},
/**
* Get the item at the specified level.
* @param {Number} level The item to retrieve level.
* @returns {OpenSeadragon.TiledImage} The item at the specified level.
*/
getItemAt: function( level ) {
if ( level >= this._items.length ) {
throw new Error( "Level bigger than number of items." );
} }
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. * Get the item at the specified index.
* @param {OpenSeadragon.TiledImage} item The item. * @param {Number} index - The item's index.
* @returns {Number} The level of the item or -1 if not present. * @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 ); 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. * Change the index of a item so that it appears over or under others.
* @param {OpenSeadragon.Drawer} drawer The underlying drawer of the changing * @param {OpenSeadragon.TiledImage} item - The item to move.
* level layer. * @param {Number} index - The new index.
* @param {Number} level The new level * @fires OpenSeadragon.World.event:item-index-changed
* @fires OpenSeadragon.Viewer.event:layer-level-changed
*/ */
setItemLevel: function( item, level ) { setItemIndex: function( item, index ) {
var oldLevel = this.getLevelOfItem( item ); $.console.assert(item, "[World.setItemIndex] item is required");
$.console.assert(index !== 'undefined', "[World.setItemIndex] index is required");
if ( level >= this._items.length ) { var oldIndex = this.getIndexOfItem( item );
throw new Error( "Level bigger than number of layers." );
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; 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. * Raised when the order of the indexes has been changed.
* @event layer-level-changed * @event item-index-changed
* @memberOf OpenSeadragon.Viewer * @memberOf OpenSeadragon.World
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
* @property {OpenSeadragon.Drawer} drawer - The drawer which level has * @property {OpenSeadragon.TiledImage} item - The item whose index has
* been changed * been changed
* @property {Number} previousLevel - The previous level of the drawer * @property {Number} previousIndex - The previous index of the item
* @property {Number} newLevel - The new level of the drawer * @property {Number} newIndex - The new index of the item
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
// TODO: deprecate this.raiseEvent( 'item-index-changed', {
this.viewer.raiseEvent( 'layer-level-changed', { item: item,
drawer: item, previousIndex: oldIndex,
previousLevel: oldLevel, newIndex: index
newLevel: level
} ); } );
}, },
/** /**
* Remove a layer. If there is only one layer, close the viewer. * Remove an item.
* @function * @function
* @param {OpenSeadragon.Drawer} drawer The underlying drawer of the layer * @param {OpenSeadragon.TiledImage} item - The item to remove.
* to remove * @fires OpenSeadragon.World.event:remove-item
* @fires OpenSeadragon.Viewer.event:remove-layer
*/ */
removeItem: function( item ) { removeItem: function( item ) {
$.console.assert(item, "[World.removeItem] item is required");
var index = this._items.indexOf( item ); var index = this._items.indexOf( item );
if ( index === -1 ) { if ( index === -1 ) {
return; return;
@ -137,30 +177,41 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{
this._figureSizes(); this._figureSizes();
/** /**
* Raised when a layer is removed. * Raised when a item is removed.
* @event remove-layer * @event remove-item
* @memberOf OpenSeadragon.Viewer * @memberOf OpenSeadragon.World
* @type {object} * @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
* @property {OpenSeadragon.Drawer} drawer The layer's underlying drawer. * @property {OpenSeadragon.TiledImage} item - The item's underlying item.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
// TODO: deprecate this.raiseEvent( 'remove-item', { item: item } );
this.raiseEvent( 'remove-layer', { drawer: item } );
}, },
resetTiles: function() { /**
* Clears all tiles and triggers updates for all items.
* @function
*/
resetItems: function() {
for ( var i = 0; i < this._items.length; i++ ) { for ( var i = 0; i < this._items.length; i++ ) {
this._items[i].reset(); this._items[i].reset();
} }
}, },
/**
* Updates (i.e. draws) all items.
* @function
*/
update: function() { update: function() {
for ( var i = 0; i < this._items.length; i++ ) { for ( var i = 0; i < this._items.length; i++ ) {
this._items[i].update(); this._items[i].update();
} }
}, },
/**
* @function
* @returns {Boolean} true if any items need updating.
*/
needsUpdate: function() { needsUpdate: function() {
for ( var i = 0; i < this._items.length; i++ ) { for ( var i = 0; i < this._items.length; i++ ) {
if ( this._items[i].needsUpdate() ) { if ( this._items[i].needsUpdate() ) {
@ -170,18 +221,29 @@ $.World.prototype = /** @lends OpenSeadragon.World.prototype */{
return false; return false;
}, },
/**
* @function
* @returns {OpenSeadragon.Rect} the smallest rectangle that encloses all items, in world coordinates.
*/
getHomeBounds: function() { getHomeBounds: function() {
return this._homeBounds.clone(); 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() { getContentFactor: function() {
return this._contentFactor; return this._contentFactor;
}, },
/**
* @function
* @private
*/
_figureSizes: function() { _figureSizes: function() {
if ( !this._items.length ) { if ( !this._items.length ) {
this._homeBounds = new $.Rect(0, 0, 1, 1); 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._contentSize = new $.Point(this._homeBounds.width * this._contentFactor,
this._homeBounds.height * this._contentFactor); this._homeBounds.height * this._contentFactor);
} }
}; });
}( OpenSeadragon )); }( OpenSeadragon ));