2014-08-07 00:48:18 +04:00
|
|
|
/*
|
|
|
|
* OpenSeadragon - World
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 CodePlex Foundation
|
|
|
|
* Copyright (C) 2010-2013 OpenSeadragon contributors
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are
|
|
|
|
* met:
|
|
|
|
*
|
|
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* - Neither the name of CodePlex Foundation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
|
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
(function( $ ){
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @class World
|
2014-08-20 02:02:04 +04:00
|
|
|
* @memberof OpenSeadragon
|
|
|
|
* @extends OpenSeadragon.EventSource
|
2014-11-04 22:53:39 +03:00
|
|
|
* @classdesc Keeps track of all of the tiled images in the scene.
|
|
|
|
* @param {Object} options - World options.
|
|
|
|
* @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this World.
|
2014-08-20 02:02:04 +04:00
|
|
|
**/
|
2014-08-07 00:48:18 +04:00
|
|
|
$.World = function( options ) {
|
|
|
|
$.console.assert( options.viewer, "[World] options.viewer is required" );
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
$.EventSource.call( this );
|
|
|
|
|
2014-08-07 00:48:18 +04:00
|
|
|
this.viewer = options.viewer;
|
|
|
|
this._items = [];
|
2014-10-25 02:18:32 +04:00
|
|
|
this._needsUpdate = false;
|
2014-08-19 03:04:49 +04:00
|
|
|
this._figureSizes();
|
2014-08-07 00:48:18 +04:00
|
|
|
};
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
$.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.World.prototype */{
|
|
|
|
/**
|
|
|
|
* Add the specified item.
|
|
|
|
* @param {OpenSeadragon.TiledImage} item - The item to add.
|
2014-11-04 22:53:39 +03:00
|
|
|
* @param {Number} [options.index] - Index for the item. If not specified, goes at the top.
|
2014-08-20 02:02:04 +04:00
|
|
|
* @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 );
|
|
|
|
}
|
|
|
|
|
2014-08-19 03:04:49 +04:00
|
|
|
this._figureSizes();
|
2014-10-25 02:18:32 +04:00
|
|
|
this._needsUpdate = true;
|
2014-08-20 02:02:04 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
2014-11-04 22:53:39 +03:00
|
|
|
* @property {OpenSeadragon.TiledImage} item - The item that has been added.
|
2014-08-20 02:02:04 +04:00
|
|
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
|
|
|
*/
|
|
|
|
this.raiseEvent( 'add-item', {
|
|
|
|
item: item
|
|
|
|
} );
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-08-20 02:02:04 +04:00
|
|
|
* Get the item at the specified index.
|
|
|
|
* @param {Number} index - The item's index.
|
|
|
|
* @returns {OpenSeadragon.TiledImage} The item at the specified index.
|
2014-08-07 00:48:18 +04:00
|
|
|
*/
|
2014-08-20 02:02:04 +04:00
|
|
|
getItemAt: function( index ) {
|
|
|
|
$.console.assert(index !== 'undefined', "[World.getItemAt] index is required");
|
|
|
|
return this._items[ index ];
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-08-20 02:02:04 +04:00
|
|
|
* 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.
|
2014-08-07 00:48:18 +04:00
|
|
|
*/
|
2014-08-20 02:02:04 +04:00
|
|
|
getIndexOfItem: function( item ) {
|
|
|
|
$.console.assert(item, "[World.getIndexOfItem] item is required");
|
2014-08-07 00:48:18 +04:00
|
|
|
return $.indexOf( this._items, item );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @returns {Number} The number of items used.
|
|
|
|
*/
|
|
|
|
getItemCount: function() {
|
|
|
|
return this._items.length;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-08-20 02:02:04 +04:00
|
|
|
* 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
|
2014-08-07 00:48:18 +04:00
|
|
|
*/
|
2014-08-20 02:02:04 +04:00
|
|
|
setItemIndex: function( item, index ) {
|
|
|
|
$.console.assert(item, "[World.setItemIndex] item is required");
|
|
|
|
$.console.assert(index !== 'undefined', "[World.setItemIndex] index is required");
|
2014-08-07 00:48:18 +04:00
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
var oldIndex = this.getIndexOfItem( item );
|
|
|
|
|
|
|
|
if ( index >= this._items.length ) {
|
|
|
|
throw new Error( "Index bigger than number of layers." );
|
2014-08-07 00:48:18 +04:00
|
|
|
}
|
2014-08-20 02:02:04 +04:00
|
|
|
|
|
|
|
if ( index === oldIndex || oldIndex === -1 ) {
|
2014-08-07 00:48:18 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-08-20 02:02:04 +04:00
|
|
|
|
|
|
|
this._items.splice( oldIndex, 1 );
|
|
|
|
this._items.splice( index, 0, item );
|
2014-10-25 02:18:32 +04:00
|
|
|
this._needsUpdate = true;
|
2014-08-07 00:48:18 +04:00
|
|
|
|
|
|
|
/**
|
2014-08-20 02:02:04 +04:00
|
|
|
* Raised when the order of the indexes has been changed.
|
|
|
|
* @event item-index-changed
|
|
|
|
* @memberOf OpenSeadragon.World
|
2014-08-07 00:48:18 +04:00
|
|
|
* @type {object}
|
2014-08-20 02:02:04 +04:00
|
|
|
* @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.
|
|
|
|
* @property {OpenSeadragon.TiledImage} item - The item whose index has
|
2014-08-07 00:48:18 +04:00
|
|
|
* been changed
|
2014-08-20 02:02:04 +04:00
|
|
|
* @property {Number} previousIndex - The previous index of the item
|
|
|
|
* @property {Number} newIndex - The new index of the item
|
2014-08-07 00:48:18 +04:00
|
|
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
|
|
|
*/
|
2014-08-20 02:02:04 +04:00
|
|
|
this.raiseEvent( 'item-index-changed', {
|
|
|
|
item: item,
|
|
|
|
previousIndex: oldIndex,
|
|
|
|
newIndex: index
|
2014-08-07 00:48:18 +04:00
|
|
|
} );
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2014-08-20 02:02:04 +04:00
|
|
|
* Remove an item.
|
|
|
|
* @param {OpenSeadragon.TiledImage} item - The item to remove.
|
|
|
|
* @fires OpenSeadragon.World.event:remove-item
|
2014-08-07 00:48:18 +04:00
|
|
|
*/
|
|
|
|
removeItem: function( item ) {
|
2014-08-20 02:02:04 +04:00
|
|
|
$.console.assert(item, "[World.removeItem] item is required");
|
|
|
|
|
2014-08-07 00:48:18 +04:00
|
|
|
var index = this._items.indexOf( item );
|
|
|
|
if ( index === -1 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._items.splice( index, 1 );
|
2014-08-19 03:04:49 +04:00
|
|
|
this._figureSizes();
|
2014-10-25 02:18:32 +04:00
|
|
|
this._needsUpdate = true;
|
2014-09-25 00:58:09 +04:00
|
|
|
this._raiseRemoveItem(item);
|
|
|
|
},
|
2014-08-19 03:04:49 +04:00
|
|
|
|
2014-09-25 00:58:09 +04:00
|
|
|
/**
|
|
|
|
* Remove all items.
|
|
|
|
* @fires OpenSeadragon.World.event:remove-item
|
|
|
|
*/
|
|
|
|
removeAll: function() {
|
|
|
|
var removedItems = this._items;
|
|
|
|
this._items = [];
|
|
|
|
this._figureSizes();
|
2014-10-25 02:18:32 +04:00
|
|
|
this._needsUpdate = true;
|
2014-09-25 00:58:09 +04:00
|
|
|
|
|
|
|
for (var i = 0; i < removedItems.length; i++) {
|
|
|
|
this._raiseRemoveItem(removedItems[i]);
|
|
|
|
}
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
/**
|
|
|
|
* Clears all tiles and triggers updates for all items.
|
|
|
|
*/
|
|
|
|
resetItems: function() {
|
2014-08-13 03:04:55 +04:00
|
|
|
for ( var i = 0; i < this._items.length; i++ ) {
|
2014-08-12 04:04:20 +04:00
|
|
|
this._items[i].reset();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
/**
|
|
|
|
* Updates (i.e. draws) all items.
|
|
|
|
*/
|
2014-08-07 00:48:18 +04:00
|
|
|
update: function() {
|
2014-08-13 03:04:55 +04:00
|
|
|
for ( var i = 0; i < this._items.length; i++ ) {
|
2014-08-07 00:48:18 +04:00
|
|
|
this._items[i].update();
|
|
|
|
}
|
2014-10-25 02:18:32 +04:00
|
|
|
|
|
|
|
this._needsUpdate = false;
|
2014-08-07 00:48:18 +04:00
|
|
|
},
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
/**
|
|
|
|
* @returns {Boolean} true if any items need updating.
|
|
|
|
*/
|
2014-08-07 00:48:18 +04:00
|
|
|
needsUpdate: function() {
|
2014-08-13 03:04:55 +04:00
|
|
|
for ( var i = 0; i < this._items.length; i++ ) {
|
|
|
|
if ( this._items[i].needsUpdate() ) {
|
2014-08-07 00:48:18 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2014-10-25 02:18:32 +04:00
|
|
|
return this._needsUpdate;
|
2014-08-13 03:04:55 +04:00
|
|
|
},
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
/**
|
2014-11-04 22:53:39 +03:00
|
|
|
* @returns {OpenSeadragon.Rect} The smallest rectangle that encloses all items, in world coordinates.
|
2014-08-20 02:02:04 +04:00
|
|
|
*/
|
2014-08-13 03:04:55 +04:00
|
|
|
getHomeBounds: function() {
|
2014-08-19 03:04:49 +04:00
|
|
|
return this._homeBounds.clone();
|
|
|
|
},
|
|
|
|
|
2014-08-20 02:02:04 +04:00
|
|
|
/**
|
|
|
|
* 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".
|
|
|
|
* @returns {Number} the number of content units per world unit.
|
|
|
|
*/
|
2014-08-19 03:04:49 +04:00
|
|
|
getContentFactor: function() {
|
|
|
|
return this._contentFactor;
|
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// private
|
2014-08-19 03:04:49 +04:00
|
|
|
_figureSizes: function() {
|
2014-08-13 03:04:55 +04:00
|
|
|
if ( !this._items.length ) {
|
2014-08-19 03:04:49 +04:00
|
|
|
this._homeBounds = new $.Rect(0, 0, 1, 1);
|
|
|
|
this._contentSize = new $.Point(1, 1);
|
|
|
|
return;
|
2014-08-13 03:04:55 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
var bounds = this._items[0].getWorldBounds();
|
2014-08-19 03:04:49 +04:00
|
|
|
this._contentFactor = this._items[0].getContentSize().x / bounds.width;
|
2014-08-13 03:04:55 +04:00
|
|
|
var left = bounds.x;
|
|
|
|
var top = bounds.y;
|
|
|
|
var right = bounds.x + bounds.width;
|
|
|
|
var bottom = bounds.y + bounds.height;
|
|
|
|
var box;
|
|
|
|
for ( var i = 1; i < this._items.length; i++ ) {
|
|
|
|
box = this._items[i].getWorldBounds();
|
2014-11-04 04:14:17 +03:00
|
|
|
this._contentFactor = Math.max(this._contentFactor, this._items[i].getContentSize().x / box.width);
|
2014-08-13 03:04:55 +04:00
|
|
|
left = Math.min( left, box.x );
|
|
|
|
top = Math.min( top, box.y );
|
|
|
|
right = Math.max( right, box.x + box.width );
|
|
|
|
bottom = Math.max( bottom, box.y + box.height );
|
|
|
|
}
|
|
|
|
|
2014-08-19 03:04:49 +04:00
|
|
|
this._homeBounds = new $.Rect( left, top, right - left, bottom - top );
|
|
|
|
this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor,
|
|
|
|
this._homeBounds.height * this._contentFactor);
|
2014-09-25 00:58:09 +04:00
|
|
|
},
|
|
|
|
|
2014-11-04 22:53:39 +03:00
|
|
|
// private
|
2014-09-25 00:58:09 +04:00
|
|
|
_raiseRemoveItem: function(item) {
|
|
|
|
/**
|
|
|
|
* Raised when a item is removed.
|
|
|
|
* @event remove-item
|
|
|
|
* @memberOf OpenSeadragon.World
|
|
|
|
* @type {object}
|
|
|
|
* @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.
|
|
|
|
*/
|
|
|
|
this.raiseEvent( 'remove-item', { item: item } );
|
2014-08-07 00:48:18 +04:00
|
|
|
}
|
2014-08-20 02:02:04 +04:00
|
|
|
});
|
2014-08-07 00:48:18 +04:00
|
|
|
|
|
|
|
}( OpenSeadragon ));
|