diff --git a/src/drawer.js b/src/drawer.js index a53d0d3b..27f0cdd7 100644 --- a/src/drawer.js +++ b/src/drawer.js @@ -87,8 +87,7 @@ $.Drawer = function( options ) { //internal state / configurable settings - overlays: [], // An unordered list of Overlays added. - collectionOverlays: {}, + collectionOverlays: {}, // For collection mode. Here an overlay is actually a viewer. //configurable settings maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount, @@ -148,199 +147,11 @@ $.Drawer = function( options ) { this.container.style.textAlign = "left"; this.container.appendChild( this.canvas ); - //create the correct type of overlay by convention if the overlays - //are not already OpenSeadragon.Overlays - for( i = 0; i < this.overlays.length; i++ ){ - if( $.isPlainObject( this.overlays[ i ] ) ){ - - this.overlays[ i ] = addOverlayFromConfiguration( this, this.overlays[ i ]); - - } else if ( $.isFunction( this.overlays[ i ] ) ){ - //TODO - } - } - //this.profiler = new $.Profiler(); }; $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.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. - * @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 - * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or - * rectangle which will be overlayed. - * @param {OpenSeadragon.OverlayPlacement} placement - The position of the - * viewport which the location coordinates will be treated as relative - * to. - * @param {function} onDraw - If supplied the callback is called when the overlay - * needs to be drawn. It it the responsibility of the callback to do any drawing/positioning. - * It is passed position, size and element. - * @fires OpenSeadragon.Viewer.event:add-overlay - */ - addOverlay: function( element, location, placement, onDraw ) { - var options; - if( $.isPlainObject( element ) ){ - options = element; - } else { - options = { - element: element, - location: location, - placement: placement, - onDraw: onDraw - }; - } - - element = $.getElement(options.element); - - if ( getOverlayIndex( this.overlays, element ) >= 0 ) { - // they're trying to add a duplicate overlay - return; - } - - this.overlays.push( new $.Overlay({ - element: element, - location: options.location, - placement: options.placement, - onDraw: options.onDraw - }) ); - this.updateAgain = true; - if( this.viewer ){ - /** - * Raised when an overlay is added to the viewer (see {@link OpenSeadragon.Drawer#addOverlay}). - * - * @event add-overlay - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {Element} element - The overlay element. - * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location - * @property {OpenSeadragon.OverlayPlacement} placement - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.viewer.raiseEvent( 'add-overlay', { - element: element, - location: options.location, - placement: options.placement - }); - } - return this; - }, - - /** - * Updates the overlay represented by the reference to the element or - * element id moving it to the new location, relative to the new placement. - * @method - * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or - * rectangle which will be overlayed. - * @param {OpenSeadragon.OverlayPlacement} placement - The position of the - * viewport which the location coordinates will be treated as relative - * to. - * @return {OpenSeadragon.Drawer} Chainable. - * @fires OpenSeadragon.Viewer.event:update-overlay - */ - updateOverlay: function( element, location, placement ) { - var i; - - element = $.getElement( element ); - i = getOverlayIndex( this.overlays, element ); - - if ( i >= 0 ) { - this.overlays[ i ].update( location, placement ); - this.updateAgain = true; - } - if( this.viewer ){ - /** - * Raised when an overlay's location or placement changes (see {@link OpenSeadragon.Drawer#updateOverlay}). - * - * @event update-overlay - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {Element} element - * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location - * @property {OpenSeadragon.OverlayPlacement} placement - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.viewer.raiseEvent( 'update-overlay', { - element: element, - location: location, - placement: placement - }); - } - return this; - }, - - /** - * Removes and overlay identified by the reference element or element id - * and schedules and update. - * @method - * @param {Element|String} element - A reference to the element or an - * element id which represent the ovelay content to be removed. - * @return {OpenSeadragon.Drawer} Chainable. - * @fires OpenSeadragon.Viewer.event:remove-overlay - */ - removeOverlay: function( element ) { - var i; - - element = $.getElement( element ); - i = getOverlayIndex( this.overlays, element ); - - if ( i >= 0 ) { - this.overlays[ i ].destroy(); - this.overlays.splice( i, 1 ); - this.updateAgain = true; - } - if( this.viewer ){ - /** - * Raised when an overlay is removed from the viewer (see {@link OpenSeadragon.Drawer#removeOverlay}). - * - * @event remove-overlay - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {Element} element - The overlay element. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.viewer.raiseEvent( 'remove-overlay', { - element: element - }); - } - return this; - }, - - /** - * Removes all currently configured Overlays from this Drawer and schedules - * and update. - * @method - * @return {OpenSeadragon.Drawer} Chainable. - * @fires OpenSeadragon.Viewer.event:clear-overlay - */ - clearOverlays: function() { - while ( this.overlays.length > 0 ) { - this.overlays.pop().destroy(); - this.updateAgain = true; - } - if( this.viewer ){ - /** - * Raised when all overlays are removed from the viewer (see {@link OpenSeadragon.Drawer#clearOverlays}). - * - * @event clear-overlay - * @memberof OpenSeadragon.Viewer - * @type {object} - * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. - * @property {?Object} userData - Arbitrary subscriber-defined object. - */ - this.viewer.raiseEvent( 'clear-overlay', {} ); - } - return this; - }, - - /** * Returns whether the Drawer is scheduled for an update at the * soonest possible opportunity. @@ -467,61 +278,6 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ } }; -/** - * @private - * @inner - */ - function addOverlayFromConfiguration( drawer, 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 = drawer.viewport.imageToViewportRectangle( rect ); - } - - if( overlay.placement ){ - return new $.Overlay({ - element: element, - location: drawer.viewport.pointFromPixel(rect), - placement: $.OverlayPlacement[overlay.placement.toUpperCase()], - onDraw: overlay.onDraw - }); - }else{ - return new $.Overlay({ - element: element, - location: rect, - onDraw: overlay.onDraw - }); - } - -} - /** * @private * @inner @@ -695,7 +451,6 @@ function updateViewport( drawer ) { //TODO drawTiles( drawer, drawer.lastDrawn ); - drawOverlays( drawer.viewport, drawer.overlays, drawer.container ); //TODO if ( best ) { @@ -1142,23 +897,6 @@ function resetCoverage( coverage, level ) { coverage[ level ] = {}; } -/** - * @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. - */ -function getOverlayIndex( overlays, element ) { - var i; - for ( i = overlays.length - 1; i >= 0; i-- ) { - if ( overlays[ i ].element == element ) { - return i; - } - } - - return -1; -} - /** * @private * @inner @@ -1196,28 +934,6 @@ function finishLoadingImage( image, callback, successful, jobid ){ } - -function drawOverlays( viewport, overlays, container ){ - var i, - length = overlays.length; - for ( i = 0; i < length; i++ ) { - drawOverlay( viewport, overlays[ i ], container ); - } -} - -function drawOverlay( viewport, overlay, container ){ - - overlay.position = viewport.pixelFromPoint( - overlay.bounds.getTopLeft(), - true - ); - overlay.size = viewport.deltaPixelsFromPoints( - overlay.bounds.getSize(), - true - ); - overlay.drawHTML( container, viewport ); -} - function drawTiles( drawer, lastDrawn ){ var i, tile, diff --git a/src/overlay.js b/src/overlay.js index 29445389..c87c84e1 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -174,7 +174,7 @@ element.parentNode.removeChild( element ); //this should allow us to preserve overlays when required between //pages - if( element.prevElementParent ){ + if ( element.prevElementParent ) { style.display = 'none'; //element.prevElementParent.insertBefore( // element, @@ -208,10 +208,16 @@ drawerCenter = new $.Point( viewport.viewer.drawer.canvas.width / 2, viewport.viewer.drawer.canvas.height / 2 - ), - degrees = viewport.degrees, - position, - size, + ), + degrees = viewport.degrees, + position = viewport.pixelFromPoint( + this.bounds.getTopLeft(), + true + ), + size = viewport.deltaPixelsFromPoints( + this.bounds.getSize(), + true + ), overlayCenter; if ( element.parentNode != container ) { @@ -225,8 +231,8 @@ this.size = $.getElementSize( element ); } - position = this.position; - size = this.size; + this.position = position; + this.size = size; this.adjust( position, size ); diff --git a/src/viewer.js b/src/viewer.js index 11a96691..f9eb1242 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -126,7 +126,7 @@ $.Viewer = function( options ) { */ canvas: null, - //TODO: not sure how to best describe these + // Overlays list. An overlay allows to add html on top of the viewer. overlays: [], //private state properties @@ -570,9 +570,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, this.navigator.close(); } - if ( this.drawer ) { - this.drawer.clearOverlays(); - } + this.clearOverlays(); this.source = null; this.drawer = null; @@ -1299,6 +1297,178 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, return this; }, + /** + * 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. + * @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 + * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or + * rectangle which will be overlayed. + * @param {OpenSeadragon.OverlayPlacement} placement - The position of the + * viewport which the location coordinates will be treated as relative + * to. + * @param {function} onDraw - If supplied the callback is called when the overlay + * needs to be drawn. It it the responsibility of the callback to do any drawing/positioning. + * It is passed position, size and element. + * @return {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:add-overlay + */ + addOverlay: function( element, location, placement, onDraw ) { + var options; + if( $.isPlainObject( element ) ){ + options = element; + } else { + options = { + element: element, + location: location, + placement: placement, + onDraw: onDraw + }; + } + + 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[ this.hash ].forceRedraw = true; + /** + * Raised when an overlay is added to the viewer (see {@link OpenSeadragon.Viewer#addOverlay}). + * + * @event add-overlay + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {Element} element - The overlay element. + * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location + * @property {OpenSeadragon.OverlayPlacement} placement + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'add-overlay', { + element: element, + location: options.location, + placement: options.placement + }); + return this; + }, + + /** + * Updates the overlay represented by the reference to the element or + * element id moving it to the new location, relative to the new placement. + * @method + * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or + * rectangle which will be overlayed. + * @param {OpenSeadragon.OverlayPlacement} placement - The position of the + * viewport which the location coordinates will be treated as relative + * to. + * @return {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:update-overlay + */ + updateOverlay: function( element, location, placement ) { + var i; + + element = $.getElement( element ); + i = getOverlayIndex( this.currentOverlays, element ); + + if ( i >= 0 ) { + this.currentOverlays[ i ].update( location, placement ); + THIS[ this.hash ].forceRedraw = true; + /** + * Raised when an overlay's location or placement changes + * (see {@link OpenSeadragon.Viewer#updateOverlay}). + * + * @event update-overlay + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the + * Viewer which raised the event. + * @property {Element} element + * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location + * @property {OpenSeadragon.OverlayPlacement} placement + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'update-overlay', { + element: element, + location: location, + placement: placement + }); + } + return this; + }, + + /** + * Removes an overlay identified by the reference element or element id + * and schedules an update. + * @method + * @param {Element|String} element - A reference to the element or an + * element id which represent the ovelay content to be removed. + * @return {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:remove-overlay + */ + removeOverlay: function( element ) { + var i; + + element = $.getElement( element ); + i = getOverlayIndex( this.currentOverlays, element ); + + if ( i >= 0 ) { + this.currentOverlays[ i ].destroy(); + this.currentOverlays.splice( i, 1 ); + THIS[ this.hash ].forceRedraw = true; + /** + * Raised when an overlay is removed from the viewer + * (see {@link OpenSeadragon.Viewer#removeOverlay}). + * + * @event remove-overlay + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the + * Viewer which raised the event. + * @property {Element} element - The overlay element. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'remove-overlay', { + element: element + }); + } + return this; + }, + + /** + * Removes all currently configured Overlays from this Viewer and schedules + * an update. + * @method + * @return {OpenSeadragon.Viewer} Chainable. + * @fires OpenSeadragon.Viewer.event:clear-overlay + */ + clearOverlays: function() { + while ( this.currentOverlays.length > 0 ) { + this.currentOverlays.pop().destroy(); + } + THIS[ this.hash ].forceRedraw = true; + /** + * Raised when all overlays are removed from the viewer (see {@link OpenSeadragon.Drawer#clearOverlays}). + * + * @event clear-overlay + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.raiseEvent( 'clear-overlay', {} ); + return this; + }, + /** * Updates the sequence buttons. * @function OpenSeadragon.Viewer.prototype._updateSequenceButtons @@ -1417,7 +1587,7 @@ function openTileSource( viewer, source ) { //minZoomLevel: this.minZoomLevel, //maxZoomLevel: this.maxZoomLevel }); - }else{ + } else { if( source ){ _this.source = source; } @@ -1515,6 +1685,16 @@ 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 ] ); + } + /** * Raised when the viewer has opened and loaded one or more TileSources. * @@ -1530,8 +1710,82 @@ function openTileSource( viewer, source ) { return _this; } +function getOverlayObject( viewer, overlay ) { + if ( !$.isPlainObject( 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 + }); + } else { + return new $.Overlay({ + element: element, + location: rect, + 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. + */ +function getOverlayIndex( overlays, element ) { + var i; + for ( i = overlays.length - 1; i >= 0; i-- ) { + if ( overlays[ i ].element == element ) { + return i; + } + } + + return -1; +} + +function drawOverlays( viewport, overlays, container ){ + var i, + length = overlays.length; + for ( i = 0; i < length; i++ ) { + overlays[ i ].drawHTML( container, viewport ); + } +} /////////////////////////////////////////////////////////////////////////////// // Schedulers provide the general engine for animation @@ -1905,6 +2159,7 @@ function updateOnce( viewer ) { if ( animated ) { viewer.drawer.update(); + drawOverlays( viewer.viewport, viewer.currentOverlays, viewer.canvas ); if( viewer.navigator ){ viewer.navigator.update( viewer.viewport ); } @@ -1920,6 +2175,7 @@ function updateOnce( viewer ) { viewer.raiseEvent( "animation" ); } else if ( THIS[ viewer.hash ].forceRedraw || viewer.drawer.needsUpdate() ) { viewer.drawer.update(); + drawOverlays( viewer.viewport, viewer.currentOverlays, viewer.canvas ); if( viewer.navigator ){ viewer.navigator.update( viewer.viewport ); }