diff --git a/src/openseadragon.js b/src/openseadragon.js index 9ee18112..13e96eb1 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -136,6 +136,28 @@ * is an Array of objects, it is used to create a * {@link OpenSeadragon.LegacyTileSource}. * + * @property {Array} overlays Array of objects defining permanent overlays of + * the viewer. The overlays added via this options and later removed with + * {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new + * image is opened. + * To add overlays which can be definitly removed, one must use + * {@link OpenSeadragon.Viewer#addOverlay} + * If displaying a sequence of images, the overlays can be associated + * with a specific page by passing the overlays array to the page's + * tile source configuration. + * Expected properties: + * * x, y, (or px, py for pixel coordinates) to define the location. + * * width, height in point if using x,y or in pixels if using px,py. If width + * and height are specified, the overlay size is adjusted when zooming, + * otherwise the size stay the size of the content (or the size defined by CSS). + * * className to associate a class to the overlay + * * id to set the overlay element. If an element with this id already exists, + * it is reused, otherwise it is created. If not specified, a new element is + * created. + * * placement a string to define the relative position to the viewport. + * Only used if no width and height are specified. Default: TOP_LEFT. + * See {@link OpenSeadragon.OverlayPlacement} for possible values. + * * @property {String} [xmlPath=null] * DEPRECATED. A relative path to load a DZI file from the server. * Prefer the newer Options.tileSources. diff --git a/src/overlay.js b/src/overlay.js index 5ae11efb..3d4a4e6d 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -73,7 +73,7 @@ * @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The * location of the overlay on the image. If a {@link OpenSeadragon.Point} * is specified, the overlay will keep a constant size independently of the - * zoom. It a {@link OpenSeadragon.Rect} is specified, the overlay size will + * zoom. If a {@link OpenSeadragon.Rect} is specified, the overlay size will * be adjusted when the zoom changes. * @param {OpenSeadragon.OverlayPlacement} [options.placement=OpenSeadragon.OverlayPlacement.TOP_LEFT] * Relative position to the viewport. diff --git a/src/viewer.js b/src/viewer.js index 085eb73f..b0c28b68 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -127,10 +127,7 @@ $.Viewer = function( options ) { canvas: null, // Overlays list. An overlay allows to add html on top of the viewer. - // Configured overlays via viewer's options overlays: [], - // Currently opened overlays - currentOverlays: [], //private state properties previousBody: [], @@ -212,6 +209,7 @@ $.Viewer = function( options ) { }; this._updateRequestId = null; + this.currentOverlays = []; //Inherit some behaviors and properties $.EventSource.call( this ); @@ -1303,7 +1301,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, /** * Adds an html element as an overlay to the current viewport. Useful for * highlighting words or areas of interest on an image or other zoomable - * interface. + * interface. The overlays added via this method are removed when the viewport + * is closed which include when changing page. * @method * @param {Element|String|Object} element - A reference to an element or an id for * the element which will overlayed. Or an Object specifying the configuration for the overlay @@ -1331,19 +1330,13 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }; } - element = $.getElement(options.element); + element = $.getElement( options.element ); if ( getOverlayIndex( this.currentOverlays, element ) >= 0 ) { // they're trying to add a duplicate overlay return this; } - - this.currentOverlays.push( new $.Overlay({ - element: element, - location: options.location, - placement: options.placement, - onDraw: options.onDraw - }) ); + this.currentOverlays.push( getOverlayObject( this, options ) ); THIS[ this.hash ].forceRedraw = true; /** * Raised when an overlay is added to the viewer (see {@link OpenSeadragon.Viewer#addOverlay}). @@ -1622,7 +1615,6 @@ function openTileSource( viewer, source ) { source: _this.source, viewport: _this.viewport, element: _this.canvas, - overlays: [].concat( _this.overlays ).concat( _this.source.overlays ), maxImageCacheCount: _this.maxImageCacheCount, imageLoaderLimit: _this.imageLoaderLimit, minZoomImageRatio: _this.minZoomImageRatio, @@ -1657,7 +1649,6 @@ function openTileSource( viewer, source ) { tileSources: source, tileHost: _this.tileHost, prefixUrl: _this.prefixUrl, - overlays: _this.overlays, viewer: _this }); } @@ -1675,7 +1666,6 @@ function openTileSource( viewer, source ) { tileSources: _this.tileSources, tileHost: _this.tileHost, prefixUrl: _this.prefixUrl, - overlays: _this.overlays, viewer: _this }); } @@ -1688,15 +1678,7 @@ function openTileSource( viewer, source ) { VIEWERS[ _this.hash ] = _this; - _this.currentOverlays = []; - var i; - for ( i = 0; i < _this.overlays.length; i++ ) { - _this.currentOverlays[ i ] = getOverlayObject( _this, _this.overlays[ i ] ); - } - for ( var j = 0; j < _this.source.overlays.length; j++ ) { - _this.currentOverlays[ i + j ] = - getOverlayObject( _this, _this.source.overlays[ j ] ); - } + loadOverlays( _this ); /** * Raised when the viewer has opened and loaded one or more TileSources. @@ -1713,68 +1695,84 @@ function openTileSource( viewer, source ) { return _this; } +function loadOverlays( _this ) { + _this.currentOverlays = []; + for ( var i = 0; i < _this.overlays.length; i++ ) { + _this.currentOverlays[ i ] = getOverlayObject( _this, _this.overlays[ i ] ); + } + for ( var j = 0; j < _this.source.overlays.length; j++ ) { + _this.currentOverlays[ i + j ] = + getOverlayObject( _this, _this.source.overlays[ j ] ); + } +} + function getOverlayObject( viewer, overlay ) { - if ( !$.isPlainObject( overlay ) ) { + if ( overlay instanceof $.Overlay ) { return overlay; } - var element = null, - rect = ( overlay.height && overlay.width ) ? new $.Rect( - overlay.x || overlay.px, - overlay.y || overlay.py, - overlay.width, - overlay.height - ) : new $.Point( - overlay.x || overlay.px, - overlay.y || overlay.py - ), - id = overlay.id ? - overlay.id : - "openseadragon-overlay-" + Math.floor( Math.random() * 10000000 ); - - element = $.getElement(overlay.id); - if ( !element ) { - element = document.createElement("a"); - element.href = "#/overlay/" + id; - } - element.id = id; - $.addClass( element, overlay.className ? - overlay.className : - "openseadragon-overlay" - ); - - if( overlay.px !== undefined ) { - //if they specified 'px' so it's in pixel coordinates so - //we need to translate to viewport coordinates - rect = viewer.viewport.imageToViewportRectangle( rect ); - } - - if( overlay.placement ){ - return new $.Overlay({ - element: element, - location: viewer.viewport.pointFromPixel( rect ), - placement: $.OverlayPlacement[ overlay.placement.toUpperCase() ], - onDraw: overlay.onDraw - }); + var element = null; + if ( overlay.element ) { + element = $.getElement( overlay.element ); } else { - return new $.Overlay({ - element: element, - location: rect, - onDraw: overlay.onDraw - }); + var id = overlay.id ? + overlay.id : + "openseadragon-overlay-" + Math.floor( Math.random() * 10000000 ); + + element = $.getElement( overlay.id ); + if ( !element ) { + element = document.createElement( "a" ); + element.href = "#/overlay/" + id; + } + element.id = id; + $.addClass( element, overlay.className ? + overlay.className : + "openseadragon-overlay" + ); } + + var location = overlay.location; + if ( !location ) { + var rect = ( overlay.height && overlay.width ) ? new $.Rect( + overlay.x || overlay.px, + overlay.y || overlay.py, + overlay.width, + overlay.height + ) : new $.Point( + overlay.x || overlay.px, + overlay.y || overlay.py + ); + if( overlay.px !== undefined ) { + //if they specified 'px' so it's in pixel coordinates so + //we need to translate to viewport coordinates + rect = viewer.viewport.imageToViewportRectangle( rect ); + } + location = overlay.placement ? viewer.viewport.pointFromPixel( rect ) : + rect; + } + + var placement = overlay.placement; + if ( placement && ( $.type( placement ) === "string" ) ) { + placement = $.OverlayPlacement[ overlay.placement.toUpperCase() ]; + } + + return new $.Overlay({ + element: element, + location: location, + placement: placement, + onDraw: overlay.onDraw + }); } /** * @private * @inner - * Determines the 'z-index' of the given overlay. Overlays are ordered in - * a z-index based on the order they are added to the Drawer. + * Determines the index of the given overlay in the given overlays array. */ function getOverlayIndex( overlays, element ) { var i; for ( i = overlays.length - 1; i >= 0; i-- ) { - if ( overlays[ i ].element == element ) { + if ( overlays[ i ].element === element ) { return i; } } @@ -1782,7 +1780,7 @@ function getOverlayIndex( overlays, element ) { return -1; } -function drawOverlays( viewport, overlays, container ){ +function drawOverlays( viewport, overlays, container ) { var i, length = overlays.length; for ( i = 0; i < length; i++ ) { diff --git a/test/overlays.js b/test/overlays.js index 60f931dc..47e0af89 100644 --- a/test/overlays.js +++ b/test/overlays.js @@ -71,8 +71,8 @@ function openHandler() { viewer.removeHandler( 'open', openHandler ); - equal( viewer.overlays.length, 1, "Global overlay not added." ); - equal( viewer.currentOverlays.length, 1, "Global overlay not opened." ); + equal( viewer.overlays.length, 1, "Global overlay should be added." ); + equal( viewer.currentOverlays.length, 1, "Global overlay should be open." ); viewer.addHandler( 'open', openPageHandler ); viewer.goToPage( 1 ); @@ -81,8 +81,8 @@ function openPageHandler() { viewer.removeHandler( 'open', openPageHandler ); - equal( viewer.overlays.length, 1, "Global overlay removed after page switch." ); - equal( viewer.currentOverlays.length, 1, "Global overlay not re-opened after page switch." ); + equal( viewer.overlays.length, 1, "Global overlay should stay after page switch." ); + equal( viewer.currentOverlays.length, 1, "Global overlay should re-open after page switch." ); viewer.addHandler( 'close', closeHandler ); viewer.close(); @@ -91,14 +91,14 @@ function closeHandler() { viewer.removeHandler( 'close', closeHandler ); - equal( viewer.overlays.length, 1, "Global overlay removed on close." ); - equal( viewer.currentOverlays.length, 0, "Global overlay not removed on close." ); + equal( viewer.overlays.length, 1, "Global overlay should not be removed on close." ); + equal( viewer.currentOverlays.length, 0, "Global overlay should be closed on close." ); start(); } } ); - asyncTest( 'Overlays via addOverlay ', function() { + asyncTest( 'Page Overlays via viewer options', function() { viewer = OpenSeadragon( { id: 'example-overlays', @@ -143,7 +143,7 @@ viewer.removeHandler( 'open', openHandler ); equal( viewer.overlays.length, 0, "No global overlay should be added." ); - equal( viewer.currentOverlays.length, 1, "Tile overlay not opened." ); + equal( viewer.currentOverlays.length, 1, "Page overlay should be open." ); viewer.addHandler( 'open', openPageHandler ); viewer.goToPage( 1 ); @@ -152,8 +152,8 @@ function openPageHandler() { viewer.removeHandler( 'open', openPageHandler ); - equal( viewer.overlays.length, 0, "Global overlay added after page switch." ); - equal( viewer.currentOverlays.length, 0, "Tile overlay re-opened after page switch." ); + equal( viewer.overlays.length, 0, "No global overlay should be added after page switch." ); + equal( viewer.currentOverlays.length, 0, "No page overlay should be opened after page switch." ); viewer.addHandler( 'close', closeHandler ); viewer.close(); @@ -162,8 +162,8 @@ function closeHandler() { viewer.removeHandler( 'close', closeHandler ); - equal( viewer.overlays.length, 0, "Global overlay added on close." ); - equal( viewer.currentOverlays.length, 0, "Tile overlay not removed on close." ); + equal( viewer.overlays.length, 0, "No global overlay should be added on close." ); + equal( viewer.currentOverlays.length, 0, "Page overlay should be closed on close." ); start(); } @@ -182,14 +182,14 @@ function openHandler() { viewer.removeHandler( 'open', openHandler ); - equal( viewer.overlays.length, 0, "Global overlay added." ); - equal( viewer.currentOverlays.length, 0, "Overlay opened." ); + equal( viewer.overlays.length, 0, "No global overlay should be added." ); + equal( viewer.currentOverlays.length, 0, "No overlay should be open." ); var rect = new OpenSeadragon.Rect( 0.1, 0.1, 0.1, 0.1 ); - var overlay = $( "
" ).get( 0 ); + var overlay = $( "" ).prop("id", "overlay").get( 0 ); viewer.addOverlay( overlay, rect ); - equal( viewer.overlays.length, 0, "Manual overlay added as global overlay." ); - equal( viewer.currentOverlays.length, 1, "Manual overlay not opened." ); + equal( viewer.overlays.length, 0, "No manual overlay should be added as global overlay." ); + equal( viewer.currentOverlays.length, 1, "A manual overlay should be open." ); viewer.addHandler( 'open', openPageHandler ); viewer.goToPage( 1 ); @@ -198,8 +198,8 @@ function openPageHandler() { viewer.removeHandler( 'open', openPageHandler ); - equal( viewer.overlays.length, 0, "Global overlay added after page switch." ); - equal( viewer.currentOverlays.length, 0, "Manual overlay not removed after page switch." ); + equal( viewer.overlays.length, 0, "No global overlay should be added after page switch." ); + equal( viewer.currentOverlays.length, 0, "Manual overlay should be removed after page switch." ); viewer.addHandler( 'close', closeHandler ); viewer.close(); @@ -208,8 +208,8 @@ function closeHandler() { viewer.removeHandler( 'close', closeHandler ); - equal( viewer.overlays.length, 0, "Global overlay added on close." ); - equal( viewer.currentOverlays.length, 0, "Overlay not removed on close." ); + equal( viewer.overlays.length, 0, "No global overlay should be added on close." ); + equal( viewer.currentOverlays.length, 0, "Manual overlay should be removed on close." ); start(); }