diff --git a/changelog.txt b/changelog.txt index 73dc4c76..f66f80c8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,26 @@ OPENSEADRAGON CHANGELOG ======================= -1.0.0: (in progress) +1.0.1: (in progress) + +* DEPRECATION: overlay functions have been moved from Drawer to Viewer (#331) +* Improved overlay functions (#331) +* Fixed: Nav button highlight states aren't quite aligned on Firefox (#303) +* Added ControlAnchor options for default controls (#304) +* Enabled basic cross-domain tile loading without tainting canvas (works in Chrome and Firefox) (#308) +* Added a ControlAnchor.ABSOLUTE enumeration. Enables absolute positioning of control elements in the viewer (#310) +* Added a 'navigator-scroll' event to Navigator. Fired when mousewheel/pinch events occur in the navigator (#310) +* Added a navigatorMaintainSizeRatio option. If set to true, the navigator minimap resizes when the viewer element is resized (#310) +* Added 'ABSOLUTE' as a navigatorPosition option, along with corresponding navigatorTop, navigatorLeft options. Allows the navigator minimap to be placed anywhere in the viewer (#310) +* Enhanced the navigatorTop, navigatorLeft, navigatorHeight, and navigatorWidth options to allow a number for pixel units or a string for other element units (%, em, etc.) (#310) +* Additional enhancements for IIIF support (#315) +* Fixed: Setting degrees in Viewer constructor has no effect (#336) +* Added pre-draw event for tiles to allow applications to alter the image (#348) +* Added optional Rotate Left/Right buttons to standard controls (#341) + +1.0.0: + +NOTE: This version has a number of breaking changes to the API, mostly in event handling. See below. * BREAKING CHANGE: All EventSource and MouseTracker event handler method signatures changed to 'handlerMethod(event)' where event == { eventSource, userData, ... } (#251) (Also fixes #23, #224, #239) * The new eventSource property in the event object replaces the old eventSource parameter that was passed to handler methods. diff --git a/images/rotateleft_grouphover.png b/images/rotateleft_grouphover.png new file mode 100644 index 00000000..9aec7ac9 Binary files /dev/null and b/images/rotateleft_grouphover.png differ diff --git a/images/rotateleft_hover.png b/images/rotateleft_hover.png new file mode 100644 index 00000000..ba32c5a4 Binary files /dev/null and b/images/rotateleft_hover.png differ diff --git a/images/rotateleft_pressed.png b/images/rotateleft_pressed.png new file mode 100644 index 00000000..b968ebf8 Binary files /dev/null and b/images/rotateleft_pressed.png differ diff --git a/images/rotateleft_rest.png b/images/rotateleft_rest.png new file mode 100644 index 00000000..ebbf081b Binary files /dev/null and b/images/rotateleft_rest.png differ diff --git a/images/rotateright_grouphover.png b/images/rotateright_grouphover.png new file mode 100644 index 00000000..86e8689c Binary files /dev/null and b/images/rotateright_grouphover.png differ diff --git a/images/rotateright_hover.png b/images/rotateright_hover.png new file mode 100644 index 00000000..d22a728f Binary files /dev/null and b/images/rotateright_hover.png differ diff --git a/images/rotateright_pressed.png b/images/rotateright_pressed.png new file mode 100644 index 00000000..fc2ead64 Binary files /dev/null and b/images/rotateright_pressed.png differ diff --git a/images/rotateright_rest.png b/images/rotateright_rest.png new file mode 100644 index 00000000..07219678 Binary files /dev/null and b/images/rotateright_rest.png differ diff --git a/src/button.js b/src/button.js index abadaff4..23b51c17 100644 --- a/src/button.js +++ b/src/button.js @@ -129,11 +129,9 @@ $.Button = function( options ) { this.imgGroup = $.makeTransparentImage( this.srcGroup ); this.imgHover = $.makeTransparentImage( this.srcHover ); this.imgDown = $.makeTransparentImage( this.srcDown ); + this.imgDiv = $.makeNeutralElement( "div" ); - this.element.appendChild( this.imgRest ); - this.element.appendChild( this.imgGroup ); - this.element.appendChild( this.imgHover ); - this.element.appendChild( this.imgDown ); + this.imgDiv.style.position = "relative"; this.imgGroup.style.position = this.imgHover.style.position = @@ -160,6 +158,12 @@ $.Button = function( options ) { this.imgDown.style.top = ""; } + + this.imgDiv.appendChild( this.imgRest ); + this.imgDiv.appendChild( this.imgGroup ); + this.imgDiv.appendChild( this.imgHover ); + this.imgDiv.appendChild( this.imgDown ); + this.element.appendChild( this.imgDiv ); } diff --git a/src/control.js b/src/control.js index d35776b9..c6010399 100644 --- a/src/control.js +++ b/src/control.js @@ -46,13 +46,15 @@ * @property {Number} TOP_RIGHT * @property {Number} BOTTOM_LEFT * @property {Number} BOTTOM_RIGHT + * @property {Number} ABSOLUTE */ $.ControlAnchor = { NONE: 0, TOP_LEFT: 1, TOP_RIGHT: 2, BOTTOM_RIGHT: 3, - BOTTOM_LEFT: 4 + BOTTOM_LEFT: 4, + ABSOLUTE: 5 }; /** @@ -110,14 +112,30 @@ $.Control = function ( element, options, container ) { * @member {Element} wrapper * @memberof OpenSeadragon.Control# */ - this.wrapper = $.makeNeutralElement( "span" ); - this.wrapper.style.display = "inline-block"; - this.wrapper.appendChild( this.element ); + if ( this.anchor == $.ControlAnchor.ABSOLUTE ) { + this.wrapper = $.makeNeutralElement( "div" ); + this.wrapper.style.position = "absolute"; + this.wrapper.style.top = typeof ( options.top ) == "number" ? ( options.top + 'px' ) : options.top; + this.wrapper.style.left = typeof ( options.left ) == "number" ? (options.left + 'px' ) : options.left; + this.wrapper.style.height = typeof ( options.height ) == "number" ? ( options.height + 'px' ) : options.height; + this.wrapper.style.width = typeof ( options.width ) == "number" ? ( options.width + 'px' ) : options.width; + this.wrapper.style.margin = "0px"; + this.wrapper.style.padding = "0px"; - if ( this.anchor == $.ControlAnchor.NONE ) { - // IE6 fix - this.wrapper.style.width = this.wrapper.style.height = "100%"; + this.element.style.position = "relative"; + this.element.style.top = "0px"; + this.element.style.left = "0px"; + this.element.style.height = "100%"; + this.element.style.width = "100%"; + } else { + this.wrapper = $.makeNeutralElement( "span" ); + this.wrapper.style.display = "inline-block"; + if ( this.anchor == $.ControlAnchor.NONE ) { + // IE6 fix + this.wrapper.style.width = this.wrapper.style.height = "100%"; + } } + this.wrapper.appendChild( this.element ); if (options.attachToViewer ) { if ( this.anchor == $.ControlAnchor.TOP_RIGHT || @@ -161,7 +179,7 @@ $.Control.prototype = /** @lends OpenSeadragon.Control.prototype */{ */ setVisible: function( visible ) { this.wrapper.style.display = visible ? - "inline-block" : + ( this.anchor == $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) : "none"; }, diff --git a/src/controldock.js b/src/controldock.js index 685418d4..183890c7 100644 --- a/src/controldock.js +++ b/src/controldock.js @@ -126,6 +126,11 @@ element.style.paddingLeft = "0px"; element.style.paddingTop = "0px"; break; + case $.ControlAnchor.ABSOLUTE: + div = this.container; + element.style.margin = "0px"; + element.style.padding = "0px"; + break; default: case $.ControlAnchor.NONE: div = this.container; diff --git a/src/drawer.js b/src/drawer.js index 43b49035..3a4d3047 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 opacity: $.DEFAULT_SETTINGS.opacity, @@ -150,18 +149,6 @@ $.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(); }; @@ -179,57 +166,15 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @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 + * @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 + * @deprecated - use {@link OpenSeadragon.Viewer#addOverlay} instead. */ 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 - }); - } + $.console.error("drawer.addOverlay is deprecated. Use viewer.addOverlay instead."); + this.viewer.addOverlay( element, location, placement, onDraw ); return this; }, @@ -244,36 +189,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * to. * @return {OpenSeadragon.Drawer} Chainable. * @fires OpenSeadragon.Viewer.event:update-overlay + * @deprecated - use {@link OpenSeadragon.Viewer#updateOverlay} instead. */ 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 - }); - } + $.console.error("drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead."); + this.viewer.updateOverlay( element, location, placement ); return this; }, @@ -285,33 +205,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * element id which represent the ovelay content to be removed. * @return {OpenSeadragon.Drawer} Chainable. * @fires OpenSeadragon.Viewer.event:remove-overlay + * @deprecated - use {@link OpenSeadragon.Viewer#removeOverlay} instead. */ 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 - }); - } + $.console.error("drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead."); + this.viewer.updateOverlay( element ); return this; }, @@ -321,24 +219,11 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ * @method * @return {OpenSeadragon.Drawer} Chainable. * @fires OpenSeadragon.Viewer.event:clear-overlay + * @deprecated - use {@link OpenSeadragon.Viewer#clearOverlays} instead. */ 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', {} ); - } + $.console.error("drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead."); + this.viewer.clearOverlays(); return this; }, @@ -362,7 +247,6 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ getOpacity: function() { return this.opacity; }, - /** * Returns whether the Drawer is scheduled for an update at the * soonest possible opportunity. @@ -441,6 +325,7 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{ this.downloading++; image = new Image(); + image.crossOrigin = 'Anonymous'; complete = function( imagesrc, resultingImage ){ _this.downloading--; @@ -488,61 +373,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 @@ -716,7 +546,6 @@ function updateViewport( drawer ) { //TODO drawTiles( drawer, drawer.lastDrawn ); - drawOverlays( drawer.viewport, drawer.overlays, drawer.container ); //TODO if ( best ) { @@ -1163,23 +992,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 @@ -1217,28 +1029,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, @@ -1249,6 +1039,25 @@ function drawTiles( drawer, lastDrawn ){ tileSource, collectionTileSource; + // We need a callback to give image manipulation a chance to happen + var drawingHandler = function(args) { + if (drawer.viewer) { + /** + * This event is fired just before the tile is drawn giving the application a chance to alter the image. + * + * NOTE: This event is only fired when the drawer is using a . + * + * @event tile-drawing + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event. + * @property {OpenSeadragon.Tile} tile + * @property {?Object} userData - 'context', 'tile' and 'rendered'. + */ + drawer.viewer.raiseEvent('tile-drawing', args); + } + }; + for ( i = lastDrawn.length - 1; i >= 0; i-- ) { tile = lastDrawn[ i ]; @@ -1299,7 +1108,7 @@ function drawTiles( drawer, lastDrawn ){ ')'; } - drawer.addOverlay( + drawer.viewer.addOverlay( viewer.element, tile.bounds ); @@ -1320,10 +1129,10 @@ function drawTiles( drawer, lastDrawn ){ // specifically, don't save,rotate,restore every time we draw a tile if( drawer.viewport.degrees !== 0 ) { offsetForRotation( tile, drawer.canvas, drawer.context, drawer.viewport.degrees ); - tile.drawCanvas( drawer.context ); + tile.drawCanvas( drawer.context, drawingHandler ); restoreRotationChanges( tile, drawer.canvas, drawer.context ); } else { - tile.drawCanvas( drawer.context ); + tile.drawCanvas( drawer.context, drawingHandler ); } } else { tile.drawHTML( drawer.canvas ); diff --git a/src/iiif1_1tilesource.js b/src/iiif1_1tilesource.js index cfddcb8c..6997ba20 100644 --- a/src/iiif1_1tilesource.js +++ b/src/iiif1_1tilesource.js @@ -45,20 +45,49 @@ */ $.IIIF1_1TileSource = function( options ){ + $.extend( true, this, options ); - if( !(this.height && this.width && this['@id'] ) ){ - throw new Error('IIIF required parameters not provided.'); + + if ( !( this.height && this.width && this['@id'] ) ){ + throw new Error( 'IIIF required parameters not provided.' ); } - if ( !(this.tile_width && this.tile_height) ) { - // use the short dimension if there aren't tile sizes provided. - options.tileSize = Math.min(this.height, this.width); - } else { + if ( ( this.profile && + this.profile == "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0" ) ){ + // what if not reporting a profile? + throw new Error( 'IIIF Image API 1.1 compliance level 1 or greater is required.' ); + } + + if ( this.tile_width ) { options.tileSize = this.tile_width; + } else if ( this.tile_height ) { + options.tileSize = this.tile_height; + } else { + // use the largest of tileOptions that is smaller than the short + // dimension + + var shortDim = Math.min( this.height, this.width ), + tileOptions = [256,512,1024], + smallerTiles = []; + + for ( var c = 0; c < tileOptions.length; c++ ) { + if ( tileOptions[c] <= shortDim ) { + smallerTiles.push( tileOptions[c] ); + } + } + + if ( smallerTiles.length > 0 ) { + options.tileSize = Math.max.apply( null, smallerTiles ); + } else { + // If we're smaller than 256, just use the short side. + options.tileSize = shortDim; + } + this.tile_width = options.tileSize; // So that 'full' gets used for + this.tile_height = options.tileSize; // the region below } - if (! options.maxLevel ) { + if ( !options.maxLevel ) { var mf = -1; var scfs = this.scale_factors || this.scale_factor; if ( scfs instanceof Array ) { @@ -67,7 +96,7 @@ $.IIIF1_1TileSource = function( options ){ if ( !isNaN( cf ) && cf > mf ) { mf = cf; } } } - if ( mf < 0 ) { options.maxLevel = Number(Math.ceil(Math.log(Math.max(this.width, this.height), 2))); } + if ( mf < 0 ) { options.maxLevel = Number( Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) ) ); } else { options.maxLevel = mf; } } @@ -82,33 +111,29 @@ $.extend( $.IIIF1_1TileSource.prototype, $.TileSource.prototype, /** @lends Open * @param {Object|Array} data * @param {String} optional - url */ - supports: function( data, url ){ - return data.profile && ( - "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0" == data.profile || - "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1" == data.profile || - "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level2" == data.profile || - "http://library.stanford.edu/iiif/image-api/1.1/compliance.html" == data.profile - ); + supports: function( data, url ) { + return ( data['@context'] && + data['@context'] == "http://library.stanford.edu/iiif/image-api/1.1/context.json" ); }, /** * * @function * @param {Object} data - the raw configuration + * @example IIIF 1.1 Info Looks like this (XML syntax is no more) + * { + * "@context" : "http://library.stanford.edu/iiif/image-api/1.1/context.json", + * "@id" : "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C", + * "width" : 6000, + * "height" : 4000, + * "scale_factors" : [ 1, 2, 4 ], + * "tile_width" : 1024, + * "tile_height" : 1024, + * "formats" : [ "jpg", "png" ], + * "qualities" : [ "native", "grey" ], + * "profile" : "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0" + * } */ - // IIIF 1.1 Info Looks like this (XML syntax is no more): - // { - // "@context" : "http://library.stanford.edu/iiif/image-api/1.1/context.json", - // "@id" : "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C", - // "width" : 6000, - // "height" : 4000, - // "scale_factors" : [ 1, 2, 4 ], - // "tile_width" : 1024, - // "tile_height" : 1024, - // "formats" : [ "jpg", "png" ], - // "qualities" : [ "native", "grey" ] - // "profile" : "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0" - // } configure: function( data ){ return data; }, @@ -124,6 +149,7 @@ $.extend( $.IIIF1_1TileSource.prototype, $.TileSource.prototype, /** @lends Open getTileUrl: function( level, x, y ){ //# constants + var IIIF_ROTATION = '0', IIIF_QUALITY = 'native.jpg', @@ -131,32 +157,34 @@ $.extend( $.IIIF1_1TileSource.prototype, $.TileSource.prototype, /** @lends Open scale = Math.pow( 0.5, this.maxLevel - level ), //# image dimensions at this level - level_width = Math.ceil( this.width * scale ), - level_height = Math.ceil( this.height * scale ), + levelWidth = Math.ceil( this.width * scale ), + levelHeight = Math.ceil( this.height * scale ), //## iiif region - iiif_tile_size_width = Math.ceil( this.tileSize / scale ), - iiif_tile_size_height = Math.ceil( this.tileSize / scale ), - iiif_region, - iiif_tile_x, - iiif_tile_y, - iiif_tile_w, - iiif_tile_h, - iiif_size, + iiifTileSizeWidth = Math.ceil( this.tileSize / scale ), + iiifTileSizeHeight = Math.ceil( this.tileSize / scale ), + iiifRegion, + iiifTileX, + iiifTileY, + iiifTileW, + iiifTileH, + iiifSize, uri; - if ( level_width < this.tile_width && level_height < this.tile_height ){ - iiif_size = level_width + "," + level_height; - iiif_region = 'full'; + if ( levelWidth < this.tile_width && levelHeight < this.tile_height ){ + iiifSize = levelWidth + ","; + iiifRegion = 'full'; } else { - iiif_tile_x = x * iiif_tile_size_width; - iiif_tile_y = y * iiif_tile_size_height; - iiif_tile_w = Math.min( iiif_tile_size_width, this.width - iiif_tile_x ); - iiif_tile_h = Math.min( iiif_tile_size_height, this.height - iiif_tile_y ); - iiif_size = Math.ceil(iiif_tile_w * scale) + "," + Math.ceil(iiif_tile_h * scale); - iiif_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(','); + iiifTileX = x * iiifTileSizeWidth; + iiifTileY = y * iiifTileSizeHeight; + iiifTileW = Math.min( iiifTileSizeWidth, this.width - iiifTileX ); + iiifTileH = Math.min( iiifTileSizeHeight, this.height - iiifTileY ); + + iiifSize = Math.ceil( iiifTileW * scale ) + ","; + + iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' ); } - uri = [ this['@id'], iiif_region, iiif_size, IIIF_ROTATION, IIIF_QUALITY ].join('/'); + uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, IIIF_QUALITY ].join( '/' ); return uri; } }); diff --git a/src/iiiftilesource.js b/src/iiiftilesource.js index a9b79cd1..2c8814ef 100644 --- a/src/iiiftilesource.js +++ b/src/iiiftilesource.js @@ -114,7 +114,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea * @param {Object|XMLDocument} data - the raw configuration * @param {String} url - the url the data was retreived from if any. * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile source via it's constructor. + * to configure this tile source via its constructor. */ configure: function( data, url ){ var service, diff --git a/src/navigator.js b/src/navigator.js index f06944f9..ffb7102f 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -50,7 +50,8 @@ $.Navigator = function( options ){ var viewer = options.viewer, - viewerSize = $.getElementSize( viewer.element), + viewerSize, + navigatorSize, unneededElement; //We may need to create a new element and id if they did not @@ -73,6 +74,12 @@ $.Navigator = function( options ){ options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT; } else if( 'TOP_LEFT' == options.position ){ options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT; + } else if( 'ABSOLUTE' == options.position ){ + options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE; + options.controlOptions.top = options.top; + options.controlOptions.left = options.left; + options.controlOptions.height = options.height; + options.controlOptions.width = options.width; } } @@ -99,12 +106,12 @@ $.Navigator = function( options ){ showSequenceControl: false, immediateRender: true, blendTime: 0, - animationTime: 0 + animationTime: 0, + autoResize: options.autoResize }); options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio; - this.viewerSizeInPoints = viewer.viewport.deltaPointsFromPixels(viewerSize); this.borderWidth = 2; //At some browser magnification levels the display regions lines up correctly, but at some there appears to //be a one pixel gap. @@ -112,14 +119,16 @@ $.Navigator = function( options ){ this.totalBorderWidths = new $.Point(this.borderWidth*2, this.borderWidth*2).minus(this.fudge); - (function( style, borderWidth ){ - style.margin = '0px'; - style.border = borderWidth + 'px solid #555'; - style.padding = '0px'; - style.background = '#000'; - style.opacity = 0.8; - style.overflow = 'hidden'; - }( this.element.style, this.borderWidth)); + if ( options.controlOptions.anchor != $.ControlAnchor.NONE ) { + (function( style, borderWidth ){ + style.margin = '0px'; + style.border = borderWidth + 'px solid #555'; + style.padding = '0px'; + style.background = '#000'; + style.opacity = 0.8; + style.overflow = 'hidden'; + }( this.element.style, this.borderWidth)); + } this.displayRegion = $.makeNeutralElement( "div" ); this.displayRegion.id = this.element.id + '-displayregion'; @@ -152,15 +161,11 @@ $.Navigator = function( options ){ this.element.innerTracker = new $.MouseTracker({ - element: this.element, - dragHandler: $.delegate( this, onCanvasDrag ), - clickHandler: $.delegate( this, onCanvasClick ), - releaseHandler: $.delegate( this, onCanvasRelease ), - scrollHandler: function(){ - //dont scroll the page up and down if the user is scrolling - //in the navigator - return false; - } + element: this.element, + dragHandler: $.delegate( this, onCanvasDrag ), + clickHandler: $.delegate( this, onCanvasClick ), + releaseHandler: $.delegate( this, onCanvasRelease ), + scrollHandler: $.delegate( this, onCanvasScroll ) }).setTracking( true ); /*this.displayRegion.outerTracker = new $.MouseTracker({ @@ -178,14 +183,22 @@ $.Navigator = function( options ){ options.controlOptions ); - if( options.width && options.height ){ - this.element.style.width = options.width + 'px'; - this.element.style.height = options.height + 'px'; - } else { - this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px'; - this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px'; + if ( options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE && options.controlOptions.anchor != $.ControlAnchor.NONE ) { + if ( options.width && options.height ) { + this.element.style.height = typeof ( options.height ) == "number" ? ( options.height + 'px' ) : options.height; + this.element.style.width = typeof ( options.width ) == "number" ? ( options.width + 'px' ) : options.width; + } else { + viewerSize = $.getElementSize( viewer.element ); + this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px'; + this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px'; + this.oldViewerSize = viewerSize; + } + navigatorSize = $.getElementSize( this.element ); + this.elementArea = navigatorSize.x * navigatorSize.y; } + this.oldContainerSize = new $.Point( 0, 0 ); + $.Viewer.apply( this, [ options ] ); this.element.getElementsByTagName('form')[0].appendChild( this.displayRegion ); @@ -199,18 +212,71 @@ $.Navigator = function( options ){ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.Navigator.prototype */{ /** + * Used to notify the navigator when its size has changed. + * Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable. * @function */ - update: function( viewport ){ + updateSize: function () { + if ( this.viewport ) { + var containerSize = new $.Point( + (this.container.clientWidth === 0 ? 1 : this.container.clientWidth), + (this.container.clientHeight === 0 ? 1 : this.container.clientHeight) + ); + if ( !containerSize.equals( this.oldContainerSize ) ) { + var oldBounds = this.viewport.getBounds(); + var oldCenter = this.viewport.getCenter(); + this.viewport.resize( containerSize, true ); + var imageHeight = 1 / this.source.aspectRatio; + var newWidth = oldBounds.width <= 1 ? oldBounds.width : 1; + var newHeight = oldBounds.height <= imageHeight ? + oldBounds.height : imageHeight; + var newBounds = new $.Rect( + oldCenter.x - ( newWidth / 2.0 ), + oldCenter.y - ( newHeight / 2.0 ), + newWidth, + newHeight + ); + this.viewport.fitBounds( newBounds, true ); + this.oldContainerSize = containerSize; + this.drawer.update(); + } + } + }, - var bounds, + /** + * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs. + * @function + * @param {OpenSeadragon.Viewport} The viewport this navigator is tracking. + */ + update: function( viewport ) { + + var viewerSize, + newWidth, + newHeight, + bounds, topleft, bottomright; - if( viewport && this.viewport ){ + viewerSize = $.getElementSize( this.viewer.element ); + if ( !viewerSize.equals( this.oldViewerSize ) ) { + this.oldViewerSize = viewerSize; + if ( this.maintainSizeRatio ) { + newWidth = viewerSize.x * this.sizeRatio; + newHeight = viewerSize.y * this.sizeRatio; + } + else { + newWidth = Math.sqrt(this.elementArea * (viewerSize.x / viewerSize.y)); + newHeight = this.elementArea / newWidth; + } + this.element.style.width = newWidth + 'px'; + this.element.style.height = newHeight + 'px'; + this.updateSize(); + } + + if( viewport && this.viewport ) { bounds = viewport.getBounds( true ); - topleft = this.viewport.pixelFromPoint( bounds.getTopLeft()); - bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight()).minus(this.totalBorderWidths); + topleft = this.viewport.pixelFromPoint( bounds.getTopLeft(), false ); + bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight(), false ).minus( this.totalBorderWidths ); //update style for navigator-box (function(style) { @@ -229,7 +295,8 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /* }, - open: function( source ){ + open: function( source ) { + this.updateSize(); var containerSize = this.viewer.viewport.containerSize.times( this.sizeRatio ); if( source.tileSize > containerSize.x || source.tileSize > containerSize.y ){ @@ -256,21 +323,7 @@ function onCanvasClick( event ) { dimensions; if (! this.drag) { if ( this.viewer.viewport ) { - viewerPosition = this.viewport.deltaPointsFromPixels( event.position ); - dimensions = this.viewer.viewport.getBounds().getSize(); - newBounds = new $.Rect( - viewerPosition.x - dimensions.x/2, - viewerPosition.y - dimensions.y/2, - dimensions.x, - dimensions.y - ); - if (this.viewer.source.aspectRatio > this.viewer.viewport.getAspectRatio()) { - newBounds.y = newBounds.y - ((this.viewerSizeInPoints.y - (1/this.viewer.source.aspectRatio)) /2 ); - } - else { - newBounds.x = newBounds.x - ((this.viewerSizeInPoints.x -1) /2 ); - } - this.viewer.viewport.fitBounds(newBounds); + this.viewer.viewport.panTo( this.viewport.pointFromPixel( event.position ) ); this.viewer.viewport.applyConstraints(); } } @@ -320,16 +373,30 @@ function onCanvasRelease( event ) { * @function */ function onCanvasScroll( event ) { - var factor; - if ( this.viewer.viewport ) { - factor = Math.pow( this.zoomPerScroll, event.scroll ); - this.viewer.viewport.zoomBy( - factor, - this.viewport.getCenter() - ); - this.viewer.viewport.applyConstraints(); - } - //cancels event + /** + * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.). + * + * @event navigator-scroll + * @memberof OpenSeadragon.Viewer + * @type {object} + * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event. + * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event. + * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element. + * @property {Number} scroll - The scroll delta for the event. + * @property {Boolean} shift - True if the shift key was pressed during this event. + * @property {Object} originalEvent - The original DOM event. + * @property {?Object} userData - Arbitrary subscriber-defined object. + */ + this.viewer.raiseEvent( 'navigator-scroll', { + tracker: event.eventSource, + position: event.position, + scroll: event.scroll, + shift: event.shift, + originalEvent: event.originalEvent + }); + + //dont scroll the page up and down if the user is scrolling + //in the navigator return false; } diff --git a/src/openseadragon.js b/src/openseadragon.js index 4e85c905..b7fa0867 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -102,11 +102,11 @@ */ /** - * The root namespace for OpenSeadragon. All utility methods - * and classes are defined on or below this namespace. - * * @namespace OpenSeadragon * + * @classdesc The root namespace for OpenSeadragon. All utility methods + * and classes are defined on or below this namespace. + * */ @@ -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 option and later removed with + * {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new + * image is opened. + * To add overlays which can be definitively 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 stays 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. @@ -270,24 +292,45 @@ * @property {Boolean} [showNavigationControl=true] * Set to false to prevent the appearance of the default navigation controls. * + * @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT] + * Placement of the default navigation controls. + * * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] - * Set the ID of a div to hold the navigator minimap. If one is not specified, - * one will be generated and placed on top of the main image + * The ID of a div to hold the navigator minimap. + * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. + * If an ID is not specified, a div element will be generated and placed on top of the main image. * - * @property {Number} [navigatorHeight=null] - * TODO: Implement this. Currently not used. - * - * @property {Number} [navigatorWidth=null] - * TODO: Implement this. Currently not used. - * - * @property {Number} [navigatorPosition=null] - * TODO: Implement this. Currently not used. + * @property {String} [navigatorPosition='TOP_RIGHT'] + * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.
+ * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
+ * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] - * Ratio of navigator size to viewer size. + * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. + * + * @property {Boolean} [navigatorMaintainSizeRatio=false] + * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. + * + * @property {Number|String} [navigatorTop=null] + * Specifies the location of the navigator minimap (see navigatorPosition). + * + * @property {Number|String} [navigatorLeft=null] + * Specifies the location of the navigator minimap (see navigatorPosition). + * + * @property {Number|String} [navigatorHeight=null] + * Specifies the size of the navigator minimap (see navigatorPosition). + * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. + * + * @property {Number|String} [navigatorWidth=null] + * Specifies the size of the navigator minimap (see navigatorPosition). + * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. + * + * @property {Boolean} [navigatorAutoResize=true] + * Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior. + * Setting to false can also improve performance when the navigator is configured to a fixed size. * * @property {Number} [controlsFadeDelay=2000] * The number of milliseconds to wait once the user has stopped interacting @@ -321,10 +364,18 @@ * image and if the 'next' button will wrap to the first image when viewing * the last image. * + * @property {Boolean} [showRotationControl=false] + * If true then the rotate left/right controls will be displayed as part of the + * standard controls. This is also subject to the browser support for rotate + * (e.g. viewer.drawer.canRotate()). + * * @property {Boolean} [showSequenceControl=true] * If the viewer has been configured with a sequence of tile sources, then * provide buttons for navigating forward and backward through the images. * + * @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT] + * Placement of the default sequence controls. + * * @property {Number} [initialPage=0] * If the viewer has been configured with a sequence of tile sources, display this page initially. * @@ -703,20 +754,26 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ autoResize: true, //DEFAULT CONTROL SETTINGS - showSequenceControl: true, //SEQUENCE - preserveViewport: false, //SEQUENCE - showNavigationControl: true, //ZOOM/HOME/FULL/SEQUENCE - controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE - controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE - mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY + showSequenceControl: true, //SEQUENCE + sequenceControlAnchor: null, //SEQUENCE + preserveViewport: false, //SEQUENCE + showNavigationControl: true, //ZOOM/HOME/FULL/SEQUENCE + navigationControlAnchor: null, //ZOOM/HOME/FULL + controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE + controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE + mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY //VIEWPORT NAVIGATOR SETTINGS - showNavigator: false, - navigatorId: null, - navigatorHeight: null, - navigatorWidth: null, - navigatorPosition: null, - navigatorSizeRatio: 0.2, + showNavigator: false, + navigatorId: null, + navigatorPosition: null, + navigatorSizeRatio: 0.2, + navigatorMaintainSizeRatio: false, + navigatorTop: null, + navigatorLeft: null, + navigatorHeight: null, + navigatorWidth: null, + navigatorAutoResize: true, // INITIAL ROTATION degrees: 0, @@ -775,6 +832,18 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ HOVER: 'fullpage_hover.png', DOWN: 'fullpage_pressed.png' }, + rotateleft: { + REST: 'rotateleft_rest.png', + GROUP: 'rotateleft_grouphover.png', + HOVER: 'rotateleft_hover.png', + DOWN: 'rotateleft_pressed.png' + }, + rotateright: { + REST: 'rotateright_rest.png', + GROUP: 'rotateright_grouphover.png', + HOVER: 'rotateright_hover.png', + DOWN: 'rotateright_pressed.png' + }, previous: { REST: 'previous_rest.png', GROUP: 'previous_grouphover.png', @@ -789,6 +858,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ } }, navPrevNextWrap: false, + showRotationControl: false, //DEVELOPER SETTINGS debugMode: false, diff --git a/src/overlay.js b/src/overlay.js index 29445389..accb6ea4 100644 --- a/src/overlay.js +++ b/src/overlay.js @@ -35,7 +35,8 @@ (function( $ ){ /** - * An enumeration of positions that an overlay may be assigned relative to the viewport. + * An enumeration of positions that an overlay may be assigned relative to + * the viewport. * @member OverlayPlacement * @memberof OpenSeadragon * @static @@ -69,8 +70,14 @@ * @memberof OpenSeadragon * @param {Object} options * @param {Element} options.element - * @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - * @param {OpenSeadragon.OverlayPlacement} options.placement - Only used if location is an {@link OpenSeadragon.Point}. + * @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. 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. + * Only used if location is a {@link OpenSeadragon.Point}. * @param {OpenSeadragon.Overlay.OnDrawCallback} options.onDraw */ $.Overlay = function( element, location, placement ) { @@ -86,9 +93,9 @@ */ var options; - if( $.isPlainObject( element ) ){ + if ( $.isPlainObject( element ) ) { options = element; - } else{ + } else { options = { element: element, location: location, @@ -174,7 +181,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, @@ -209,9 +216,15 @@ 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 +238,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/point.js b/src/point.js index b3cd928a..38aad1f1 100644 --- a/src/point.js +++ b/src/point.js @@ -76,10 +76,10 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Substract another Point to this point and return a new Point. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the + * @param {OpenSeadragon.Point} point The point to substract vector components. + * @returns {OpenSeadragon.Point} A new point representing the substraction of the * vector components */ minus: function( point ) { @@ -90,11 +90,11 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Multiply this point by a factor and return a new Point. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components + * @param {Number} factor The factor to multiply vector components. + * @returns {OpenSeadragon.Point} A new point representing the multiplication + * of the vector components by the factor */ times: function( factor ) { return new $.Point( @@ -104,11 +104,11 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Divide this point by a factor and return a new Point. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components + * @param {Number} factor The factor to divide vector components. + * @returns {OpenSeadragon.Point} A new point representing the division of the + * vector components by the factor */ divide: function( factor ) { return new $.Point( @@ -118,10 +118,9 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Compute the opposite of this point and return a new Point. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the + * @returns {OpenSeadragon.Point} A new point representing the opposite of the * vector components */ negate: function() { @@ -129,11 +128,10 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Compute the distance between this point and another point. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components + * @param {OpenSeadragon.Point} point The point to compute the distance with. + * @returns {Number} The distance between the 2 points */ distanceTo: function( point ) { return Math.sqrt( @@ -143,22 +141,21 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Apply a function to each coordinate of this point and return a new point. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components + * @param {function} func The function to apply to each coordinate. + * @returns {OpenSeadragon.Point} A new point with the coordinates computed + * by the specified function */ apply: function( func ) { return new $.Point( func( this.x ), func( this.y ) ); }, /** - * Add another Point to this point and return a new Point. + * Check if this point is equal to another one. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components + * @param {OpenSeadragon.Point} point The point to compare this point with. + * @returns {Boolean} true if they are equal, false otherwise. */ equals: function( point ) { return ( @@ -186,11 +183,10 @@ $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{ }, /** - * Add another Point to this point and return a new Point. + * Convert this point to a string in the format (x,y) where x and y are + * rounded to the nearest integer. * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components + * @returns {String} A string representation of this point. */ toString: function() { return "(" + Math.round(this.x) + "," + Math.round(this.y) + ")"; diff --git a/src/strings.js b/src/strings.js index e6cf8352..03f4ed42 100644 --- a/src/strings.js +++ b/src/strings.js @@ -55,7 +55,9 @@ var I18N = { ZoomIn: "Zoom in", ZoomOut: "Zoom out", NextPage: "Next page", - PreviousPage: "Previous page" + PreviousPage: "Previous page", + RotateLeft: "Rotate left", + RotateRight: "Rotate right" } }; diff --git a/src/tile.js b/src/tile.js index 820f856b..9aa4bde4 100644 --- a/src/tile.js +++ b/src/tile.js @@ -231,8 +231,10 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ * Renders the tile in a canvas-based context. * @function * @param {Canvas} context + * @param {Function} method for firing the drawing event. drawingHandler({context, tile, rendered}) + * where rendered is the context with the pre-drawn image. */ - drawCanvas: function( context ) { + drawCanvas: function( context, drawingHandler ) { var position = this.position, size = this.size, @@ -280,6 +282,9 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ rendered = TILE_CACHE[ this.url ]; + // This gives the application a chance to make image manipulation changes as we are rendering the image + drawingHandler({context: context, tile: this, rendered: rendered}); + //rendered.save(); context.drawImage( rendered.canvas, @@ -298,7 +303,7 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{ }, /** - * Removes tile from it's contianer. + * Removes tile from its container. * @function */ unload: function() { diff --git a/src/tilesource.js b/src/tilesource.js index e6dff844..90a80f7c 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -55,7 +55,7 @@ * @param {Number|Object|Array|String} width * If more than a single argument is supplied, the traditional use of * positional parameters is supplied and width is expected to be the width - * source image at it's max resolution in pixels. If a single argument is supplied and + * source image at its max resolution in pixels. If a single argument is supplied and * it is an Object or Array, the construction is assumed to occur through * the extending classes implementation of 'configure'. Finally if only a * single argument is supplied and it is a String, the extending class is @@ -93,7 +93,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve } //Tile sources supply some events, namely 'ready' when they must be configured - //by asyncronously fetching their configuration data. + //by asynchronously fetching their configuration data. $.EventSource.call( this ); //we allow options to override anything we dont treat as @@ -131,7 +131,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve * @memberof OpenSeadragon.TileSource# */ /** - * The overlap in pixels each tile shares with it's adjacent neighbors. + * The overlap in pixels each tile shares with its adjacent neighbors. * @member {Number} tileOverlap * @memberof OpenSeadragon.TileSource# */ @@ -366,7 +366,7 @@ $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{ callback: callback }); } else { - // request info via xhr asyncronously. + // request info via xhr asynchronously. $.makeAjaxRequest( url, function( xhr ) { var data = processResponse( xhr ); callback( data ); diff --git a/src/viewer.js b/src/viewer.js index d978fbed..e56a979d 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -74,8 +74,7 @@ $.Viewer = function( options ) { xmlPath: args.length > 1 ? args[ 1 ] : undefined, prefixUrl: args.length > 2 ? args[ 2 ] : undefined, controls: args.length > 3 ? args[ 3 ] : undefined, - overlays: args.length > 4 ? args[ 4 ] : undefined, - overlayControls: args.length > 5 ? args[ 5 ] : undefined + overlays: args.length > 4 ? args[ 4 ] : undefined }; } @@ -127,9 +126,8 @@ $.Viewer = function( options ) { */ canvas: null, - //TODO: not sure how to best describe these - overlays: [], - overlayControls:[], + // Overlays list. An overlay allows to add html on top of the viewer. + overlays: [], //private state properties previousBody: [], @@ -211,6 +209,7 @@ $.Viewer = function( options ) { }; this._updateRequestId = null; + this.currentOverlays = []; //Inherit some behaviors and properties $.EventSource.call( this ); @@ -517,9 +516,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; @@ -856,6 +853,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, } + if ( this.navigator && this.viewport ) { + this.navigator.update( this.viewport ); + } + /** * Raised when the viewer has changed to/from full-page mode (see {@link OpenSeadragon.Viewer#setFullPage}). * @@ -937,6 +938,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, _this.element.style.height = _this.fullPageStyleHeight; } } + if ( _this.navigator && _this.viewport ) { + _this.navigator.update( _this.viewport ); + } /** * Raised when the viewer has changed to/from full-screen mode (see {@link OpenSeadragon.Viewer#setFullScreen}). * @@ -1318,7 +1322,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }else{ this.addControl( this.pagingControl, - {anchor: $.ControlAnchor.TOP_LEFT} + {anchor: this.sequenceControlAnchor || $.ControlAnchor.TOP_LEFT} ); } } @@ -1342,6 +1346,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ), onHomeHandler = $.delegate( this, onHome ), onFullScreenHandler = $.delegate( this, onFullScreen ), + onRotateLeftHandler = $.delegate( this, onRotateLeft ), + onRotateRightHandler = $.delegate( this, onRotateRight ), onFocusHandler = $.delegate( this, onFocus ), onBlurHandler = $.delegate( this, onBlur ), navImages = this.navImages, @@ -1421,6 +1427,37 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, onBlur: onBlurHandler })); + if (this.showRotationControl) { + buttons.push( this.rotateLeft = new $.Button({ + element: this.rotateLeftButton ? $.getElement( this.rotateLeftButton ) : null, + clickTimeThreshold: this.clickTimeThreshold, + clickDistThreshold: this.clickDistThreshold, + tooltip: $.getString( "Tooltips.RotateLeft" ), + srcRest: resolveUrl( this.prefixUrl, navImages.rotateleft.REST ), + srcGroup: resolveUrl( this.prefixUrl, navImages.rotateleft.GROUP ), + srcHover: resolveUrl( this.prefixUrl, navImages.rotateleft.HOVER ), + srcDown: resolveUrl( this.prefixUrl, navImages.rotateleft.DOWN ), + onRelease: onRotateLeftHandler, + onFocus: onFocusHandler, + onBlur: onBlurHandler + })); + + buttons.push( this.rotateRight = new $.Button({ + element: this.rotateRightButton ? $.getElement( this.rotateRightButton ) : null, + clickTimeThreshold: this.clickTimeThreshold, + clickDistThreshold: this.clickDistThreshold, + tooltip: $.getString( "Tooltips.RotateRight" ), + srcRest: resolveUrl( this.prefixUrl, navImages.rotateright.REST ), + srcGroup: resolveUrl( this.prefixUrl, navImages.rotateright.GROUP ), + srcHover: resolveUrl( this.prefixUrl, navImages.rotateright.HOVER ), + srcDown: resolveUrl( this.prefixUrl, navImages.rotateright.DOWN ), + onRelease: onRotateRightHandler, + onFocus: onFocusHandler, + onBlur: onBlurHandler + })); + + } + if( useGroup ){ this.buttons = new $.ButtonGroup({ buttons: buttons, @@ -1439,7 +1476,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, }else{ this.addControl( this.navControl, - {anchor: $.ControlAnchor.TOP_LEFT} + {anchor: this.navigationControlAnchor || $.ControlAnchor.TOP_LEFT} ); } } @@ -1490,6 +1527,173 @@ $.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. 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 + * @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( getOverlayObject( this, options ) ); + 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 @@ -1630,9 +1834,8 @@ function getTileSourceImplementation( viewer, tileSource, successCallback, * @private */ function openTileSource( viewer, source ) { - var _this = viewer, - overlay, - i; + var i, + _this = viewer; if ( _this.source ) { _this.close( ); @@ -1660,12 +1863,13 @@ function openTileSource( viewer, source ) { showNavigator: false, minZoomImageRatio: 1, maxZoomPixelRatio: 1, - viewer: _this //, + viewer: _this, + degrees: _this.degrees //, //TODO: figure out how to support these in a way that makes sense //minZoomLevel: this.minZoomLevel, //maxZoomLevel: this.maxZoomLevel }); - }else{ + } else { if( source ){ _this.source = source; } @@ -1682,7 +1886,8 @@ function openTileSource( viewer, source ) { defaultZoomLevel: _this.defaultZoomLevel, minZoomLevel: _this.minZoomLevel, maxZoomLevel: _this.maxZoomLevel, - viewer: _this + viewer: _this, + degrees: _this.degrees }); } @@ -1697,7 +1902,6 @@ function openTileSource( viewer, source ) { source: _this.source, viewport: _this.viewport, element: _this.canvas, - overlays: [].concat( _this.overlays ).concat( _this.source.overlays ), opacity: _this.opacity, maxImageCacheCount: _this.maxImageCacheCount, imageLoaderLimit: _this.imageLoaderLimit, @@ -1714,6 +1918,21 @@ function openTileSource( viewer, source ) { }); _this.drawers = [_this.drawer]; + // Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons + if (!_this.drawer.canRotate()) { + // Disable/remove the rotate left/right buttons since they aren't supported + if (_this.rotateLeft) { + i = _this.buttons.buttons.indexOf(_this.rotateLeft); + _this.buttons.buttons.splice(i, 1); + _this.buttons.element.removeChild(_this.rotateLeft.element); + } + if (_this.rotateRight) { + i = _this.buttons.buttons.indexOf(_this.rotateRight); + _this.buttons.buttons.splice(i, 1); + _this.buttons.element.removeChild(_this.rotateRight.element); + } + } + //Instantiate a navigator if configured if ( _this.showNavigator && !_this.collectionMode ){ // Note: By passing the fully parsed source, the navigator doesn't @@ -1722,16 +1941,19 @@ function openTileSource( viewer, source ) { _this.navigator.open( source ); } else { _this.navigator = new $.Navigator({ - id: _this.navigatorId, - position: _this.navigatorPosition, - sizeRatio: _this.navigatorSizeRatio, - height: _this.navigatorHeight, - width: _this.navigatorWidth, - tileSources: source, - tileHost: _this.tileHost, - prefixUrl: _this.prefixUrl, - overlays: _this.overlays, - viewer: _this + id: _this.navigatorId, + position: _this.navigatorPosition, + sizeRatio: _this.navigatorSizeRatio, + maintainSizeRatio: _this.navigatorMaintainSizeRatio, + top: _this.navigatorTop, + left: _this.navigatorLeft, + width: _this.navigatorWidth, + height: _this.navigatorHeight, + autoResize: _this.navigatorAutoResize, + tileSources: source, + tileHost: _this.tileHost, + prefixUrl: _this.prefixUrl, + viewer: _this }); } } @@ -1748,7 +1970,6 @@ function openTileSource( viewer, source ) { tileSources: _this.tileSources, tileHost: _this.tileHost, prefixUrl: _this.prefixUrl, - overlays: _this.overlays, viewer: _this }); } @@ -1759,40 +1980,10 @@ function openTileSource( viewer, source ) { THIS[ _this.hash ].forceRedraw = true; _this._updateRequestId = scheduleUpdate( _this, updateMulti ); - //Assuming you had programatically created a bunch of overlays - //and added them via configuration - for ( i = 0; i < _this.overlayControls.length; i++ ) { - - overlay = _this.overlayControls[ i ]; - - if ( overlay.point ) { - - _this.drawer.addOverlay( - overlay.id, - new $.Point( - overlay.point.X, - overlay.point.Y - ), - $.OverlayPlacement.TOP_LEFT - ); - - } else { - - _this.drawer.addOverlay( - overlay.id, - new $.Rect( - overlay.rect.Point.X, - overlay.rect.Point.Y, - overlay.rect.Width, - overlay.rect.Height - ), - overlay.placement - ); - - } - } VIEWERS[ _this.hash ] = _this; + loadOverlays( _this ); + /** * Raised when the viewer has opened and loaded one or more TileSources. * @@ -1808,8 +1999,98 @@ 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 ( overlay instanceof $.Overlay ) { + return overlay; + } + var element = null; + if ( overlay.element ) { + element = $.getElement( overlay.element ); + } else { + 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 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 ) { + 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 @@ -2183,6 +2464,7 @@ function updateOnce( viewer ) { if ( animated ) { updateDrawers( viewer ); + drawOverlays( viewer.viewport, viewer.currentOverlays, viewer.canvas ); if( viewer.navigator ){ viewer.navigator.update( viewer.viewport ); } @@ -2198,6 +2480,7 @@ function updateOnce( viewer ) { viewer.raiseEvent( "animation" ); } else if ( THIS[ viewer.hash ].forceRedraw || drawersNeedUpdate( viewer ) ) { updateDrawers( viewer ); + drawOverlays( viewer.viewport, viewer.currentOverlays, viewer.canvas ); if( viewer.navigator ){ viewer.navigator.update( viewer.viewport ); } @@ -2371,6 +2654,38 @@ function onFullScreen() { } } +/** + * Note: The current rotation feature is limited to 90 degree turns. + */ +function onRotateLeft() { + if ( this.viewport ) { + var currRotation = this.viewport.getRotation(); + if (currRotation === 0) { + currRotation = 270; + } + else { + currRotation -= 90; + } + this.viewport.setRotation(currRotation); + } +} + +/** + * Note: The current rotation feature is limited to 90 degree turns. + */ +function onRotateRight() { + if ( this.viewport ) { + var currRotation = this.viewport.getRotation(); + if (currRotation === 270) { + currRotation = 0; + } + else { + currRotation += 90; + } + this.viewport.setRotation(currRotation); + } +} + function onPrevious(){ var previous = THIS[ this.hash ].sequence - 1; diff --git a/src/viewport.js b/src/viewport.js index 8625ff60..aadb81e9 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -920,12 +920,12 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{ /** * Convert pixel coordinates relative to the image to * viewer element coordinates. - * @param {OpenSeadragon.Point} point + * @param {OpenSeadragon.Point} pixel * @returns {OpenSeadragon.Point} */ - imageToViewerElementCoordinates: function( point ) { - var pixel = this.pixelFromPoint( point, true ); - return this.imageToViewportCoordinates( pixel ); + imageToViewerElementCoordinates: function( pixel ) { + var point = this.imageToViewportCoordinates( pixel ); + return this.pixelFromPoint( point, true ); }, /** diff --git a/test/data/iiif_1_1_no_tiles_1048.json b/test/data/iiif_1_1_no_tiles_1048.json new file mode 100644 index 00000000..e5789846 --- /dev/null +++ b/test/data/iiif_1_1_no_tiles_1048.json @@ -0,0 +1,18 @@ +{ + "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", + "height": 870, + "width": 1048, + "qualities": [ + "native", + "color", + "grey", + "bitonal" + ], + "formats": [ + "jpg", + "png", + "gif" + ], + "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", + "@id": "http://localhost:8000/test/data/iiif_1_1_no_tiles_1048" +} diff --git a/test/data/iiif_1_1_no_tiles_1048/0,0,1024,870/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/0,0,1024,870/512,/0/native.jpg new file mode 100644 index 00000000..2a103534 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/0,0,1024,870/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/0,0,512,512/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/0,0,512,512/512,/0/native.jpg new file mode 100644 index 00000000..abe1eab4 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/0,0,512,512/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/0,512,512,358/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/0,512,512,358/512,/0/native.jpg new file mode 100644 index 00000000..2e3a12c9 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/0,512,512,358/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/1024,0,24,512/24,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/1024,0,24,512/24,/0/native.jpg new file mode 100644 index 00000000..55fffb23 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/1024,0,24,512/24,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/1024,0,24,870/12,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/1024,0,24,870/12,/0/native.jpg new file mode 100644 index 00000000..ed7a45c1 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/1024,0,24,870/12,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/1024,512,24,358/24,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/1024,512,24,358/24,/0/native.jpg new file mode 100644 index 00000000..2ad1ab7a Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/1024,512,24,358/24,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/512,0,512,512/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/512,0,512,512/512,/0/native.jpg new file mode 100644 index 00000000..ec687491 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/512,0,512,512/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/512,512,512,358/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/512,512,512,358/512,/0/native.jpg new file mode 100644 index 00000000..9609e3e0 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/512,512,512,358/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/full/131,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/full/131,/0/native.jpg new file mode 100644 index 00000000..b2d34820 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/full/131,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/full/17,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/full/17,/0/native.jpg new file mode 100644 index 00000000..e66b6ef7 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/full/17,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/full/262,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/full/262,/0/native.jpg new file mode 100644 index 00000000..b6a4288d Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/full/262,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/full/33,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/full/33,/0/native.jpg new file mode 100644 index 00000000..c8dd168c Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/full/33,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_1048/full/66,/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/full/66,/0/native.jpg new file mode 100644 index 00000000..65d0a09d Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_1048/full/66,/0/native.jpg differ diff --git a/test/data/iiif_no_tiles/0,0,1024,850/16,14/0/native.jpg b/test/data/iiif_1_1_no_tiles_1048/full/9,/0/native.jpg similarity index 66% rename from test/data/iiif_no_tiles/0,0,1024,850/16,14/0/native.jpg rename to test/data/iiif_1_1_no_tiles_1048/full/9,/0/native.jpg index 4cee30fa..f51d6103 100644 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/16,14/0/native.jpg and b/test/data/iiif_1_1_no_tiles_1048/full/9,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255.json b/test/data/iiif_1_1_no_tiles_255.json new file mode 100644 index 00000000..12a09460 --- /dev/null +++ b/test/data/iiif_1_1_no_tiles_255.json @@ -0,0 +1,18 @@ +{ + "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", + "height": 212, + "width": 255, + "qualities": [ + "native", + "color", + "grey", + "bitonal" + ], + "formats": [ + "jpg", + "png", + "gif" + ], + "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", + "@id": "http://localhost:8000/test/data/iiif_1_1_no_tiles_255" +} diff --git a/test/data/iiif_1_1_no_tiles_255/0,0,212,212/212,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/0,0,212,212/212,/0/native.jpg new file mode 100644 index 00000000..b41466da Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/0,0,212,212/212,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255/212,0,43,212/43,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/212,0,43,212/43,/0/native.jpg new file mode 100644 index 00000000..03cc9619 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/212,0,43,212/43,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255/full/128,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/full/128,/0/native.jpg new file mode 100644 index 00000000..69c602b8 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/full/128,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255/full/16,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/full/16,/0/native.jpg new file mode 100644 index 00000000..42b54ec6 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/full/16,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255/full/32,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/full/32,/0/native.jpg new file mode 100644 index 00000000..c341f30b Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/full/32,/0/native.jpg differ diff --git a/test/data/iiif_no_tiles/0,0,1024,850/8,7/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/full/4,/0/native.jpg similarity index 79% rename from test/data/iiif_no_tiles/0,0,1024,850/8,7/0/native.jpg rename to test/data/iiif_1_1_no_tiles_255/full/4,/0/native.jpg index 893409b0..62fdf391 100644 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/8,7/0/native.jpg and b/test/data/iiif_1_1_no_tiles_255/full/4,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255/full/64,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/full/64,/0/native.jpg new file mode 100644 index 00000000..83629b8e Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/full/64,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_255/full/8,/0/native.jpg b/test/data/iiif_1_1_no_tiles_255/full/8,/0/native.jpg new file mode 100644 index 00000000..087db0a1 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_255/full/8,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384.json b/test/data/iiif_1_1_no_tiles_384.json new file mode 100644 index 00000000..112a0be6 --- /dev/null +++ b/test/data/iiif_1_1_no_tiles_384.json @@ -0,0 +1,18 @@ +{ + "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", + "height": 319, + "width": 384, + "qualities": [ + "native", + "color", + "grey", + "bitonal" + ], + "formats": [ + "jpg", + "png", + "gif" + ], + "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", + "@id": "http://localhost:8000/test/data/iiif_1_1_no_tiles_384" +} diff --git a/test/data/iiif_1_1_no_tiles_384/0,0,256,256/256,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/0,0,256,256/256,/0/native.jpg new file mode 100644 index 00000000..f2affd34 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/0,0,256,256/256,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/0,256,256,63/256,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/0,256,256,63/256,/0/native.jpg new file mode 100644 index 00000000..a44db124 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/0,256,256,63/256,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/256,0,128,256/128,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/256,0,128,256/128,/0/native.jpg new file mode 100644 index 00000000..61f8d677 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/256,0,128,256/128,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/256,256,128,63/128,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/256,256,128,63/128,/0/native.jpg new file mode 100644 index 00000000..a1002c86 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/256,256,128,63/128,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/full/12,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/full/12,/0/native.jpg new file mode 100644 index 00000000..2677d6c1 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/full/12,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/full/192,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/full/192,/0/native.jpg new file mode 100644 index 00000000..00593f8a Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/full/192,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/full/24,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/full/24,/0/native.jpg new file mode 100644 index 00000000..ee21015d Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/full/24,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/full/48,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/full/48,/0/native.jpg new file mode 100644 index 00000000..5597681c Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/full/48,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/full/6,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/full/6,/0/native.jpg new file mode 100644 index 00000000..8c4134df Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/full/6,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_384/full/96,/0/native.jpg b/test/data/iiif_1_1_no_tiles_384/full/96,/0/native.jpg new file mode 100644 index 00000000..38a07820 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_384/full/96,/0/native.jpg differ diff --git a/test/data/iiif_no_tiles.json b/test/data/iiif_1_1_no_tiles_768.json similarity index 75% rename from test/data/iiif_no_tiles.json rename to test/data/iiif_1_1_no_tiles_768.json index 7f677b32..743d6128 100644 --- a/test/data/iiif_no_tiles.json +++ b/test/data/iiif_1_1_no_tiles_768.json @@ -1,7 +1,7 @@ { "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level2", - "height": 850, - "width": 1024, + "height": 637, + "width": 768, "qualities": [ "native", "color", @@ -14,6 +14,5 @@ "gif" ], "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", - "@id": "http://localhost:8000/test/data/iiif_no_tiles" + "@id": "http://localhost:8000/test/data/iiif_1_1_no_tiles_768" } - diff --git a/test/data/iiif_1_1_no_tiles_768/0,0,512,512/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/0,0,512,512/512,/0/native.jpg new file mode 100644 index 00000000..c547135f Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/0,0,512,512/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/0,512,512,125/512,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/0,512,512,125/512,/0/native.jpg new file mode 100644 index 00000000..88717a4d Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/0,512,512,125/512,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/512,0,256,512/256,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/512,0,256,512/256,/0/native.jpg new file mode 100644 index 00000000..3423bc3a Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/512,0,256,512/256,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/512,512,256,125/256,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/512,512,256,125/256,/0/native.jpg new file mode 100644 index 00000000..8fee2e2e Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/512,512,256,125/256,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/12,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/12,/0/native.jpg new file mode 100644 index 00000000..97ddd626 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/12,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/192,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/192,/0/native.jpg new file mode 100644 index 00000000..0f786ee9 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/192,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/24,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/24,/0/native.jpg new file mode 100644 index 00000000..151eed0f Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/24,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/384,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/384,/0/native.jpg new file mode 100644 index 00000000..9dcd340f Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/384,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/48,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/48,/0/native.jpg new file mode 100644 index 00000000..6ceb6842 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/48,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/6,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/6,/0/native.jpg new file mode 100644 index 00000000..20139e5e Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/6,/0/native.jpg differ diff --git a/test/data/iiif_1_1_no_tiles_768/full/96,/0/native.jpg b/test/data/iiif_1_1_no_tiles_768/full/96,/0/native.jpg new file mode 100644 index 00000000..299589b6 Binary files /dev/null and b/test/data/iiif_1_1_no_tiles_768/full/96,/0/native.jpg differ diff --git a/test/data/iiif1_1.json b/test/data/iiif_1_1_tiled.json similarity index 88% rename from test/data/iiif1_1.json rename to test/data/iiif_1_1_tiled.json index 1ad7a6fa..641d8511 100644 --- a/test/data/iiif1_1.json +++ b/test/data/iiif_1_1_tiled.json @@ -24,5 +24,5 @@ "gif" ], "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", - "@id": "http://localhost:8000/test/data/iiif_1_1_files" + "@id": "http://localhost:8000/test/data/iiif_1_1_tiled" } diff --git a/test/data/iiif_1_1_files/0,0,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,0,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,0,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,0,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/0,0,512,512/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,0,512,512/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,0,512,512/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,0,512,512/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/0,0,775,1024/194,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,0,775,1024/194,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,0,775,1024/194,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,0,775,1024/194,/0/native.jpg diff --git a/test/data/iiif_1_1_files/0,256,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,256,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,256,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,256,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/0,512,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,512,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,512,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,512,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/0,512,512,512/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,512,512,512/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,512,512,512/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,512,512,512/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/0,768,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/0,768,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/0,768,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/0,768,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/256,0,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/256,0,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/256,0,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/256,0,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/256,256,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/256,256,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/256,256,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/256,256,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/256,512,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/256,512,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/256,512,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/256,512,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/256,768,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/256,768,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/256,768,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/256,768,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/512,0,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/512,0,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/512,0,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/512,0,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/512,0,263,512/132,256/0/native.jpg b/test/data/iiif_1_1_tiled/512,0,263,512/132,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/512,0,263,512/132,256/0/native.jpg rename to test/data/iiif_1_1_tiled/512,0,263,512/132,/0/native.jpg diff --git a/test/data/iiif_1_1_files/512,256,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/512,256,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/512,256,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/512,256,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/512,512,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/512,512,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/512,512,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/512,512,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/512,512,263,512/132,256/0/native.jpg b/test/data/iiif_1_1_tiled/512,512,263,512/132,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/512,512,263,512/132,256/0/native.jpg rename to test/data/iiif_1_1_tiled/512,512,263,512/132,/0/native.jpg diff --git a/test/data/iiif_1_1_files/512,768,256,256/256,256/0/native.jpg b/test/data/iiif_1_1_tiled/512,768,256,256/256,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/512,768,256,256/256,256/0/native.jpg rename to test/data/iiif_1_1_tiled/512,768,256,256/256,/0/native.jpg diff --git a/test/data/iiif_1_1_files/768,0,7,256/7,256/0/native.jpg b/test/data/iiif_1_1_tiled/768,0,7,256/7,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/768,0,7,256/7,256/0/native.jpg rename to test/data/iiif_1_1_tiled/768,0,7,256/7,/0/native.jpg diff --git a/test/data/iiif_1_1_files/768,256,7,256/7,256/0/native.jpg b/test/data/iiif_1_1_tiled/768,256,7,256/7,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/768,256,7,256/7,256/0/native.jpg rename to test/data/iiif_1_1_tiled/768,256,7,256/7,/0/native.jpg diff --git a/test/data/iiif_1_1_files/768,512,7,256/7,256/0/native.jpg b/test/data/iiif_1_1_tiled/768,512,7,256/7,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/768,512,7,256/7,256/0/native.jpg rename to test/data/iiif_1_1_tiled/768,512,7,256/7,/0/native.jpg diff --git a/test/data/iiif_1_1_files/768,768,7,256/7,256/0/native.jpg b/test/data/iiif_1_1_tiled/768,768,7,256/7,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/768,768,7,256/7,256/0/native.jpg rename to test/data/iiif_1_1_tiled/768,768,7,256/7,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/1,1/0/native.jpg b/test/data/iiif_1_1_tiled/full/1,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/1,1/0/native.jpg rename to test/data/iiif_1_1_tiled/full/1,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/13,16/0/native.jpg b/test/data/iiif_1_1_tiled/full/13,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/13,16/0/native.jpg rename to test/data/iiif_1_1_tiled/full/13,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/2,2/0/native.jpg b/test/data/iiif_1_1_tiled/full/2,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/2,2/0/native.jpg rename to test/data/iiif_1_1_tiled/full/2,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/25,32/0/native.jpg b/test/data/iiif_1_1_tiled/full/25,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/25,32/0/native.jpg rename to test/data/iiif_1_1_tiled/full/25,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/4,4/0/native.jpg b/test/data/iiif_1_1_tiled/full/4,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/4,4/0/native.jpg rename to test/data/iiif_1_1_tiled/full/4,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/49,64/0/native.jpg b/test/data/iiif_1_1_tiled/full/49,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/49,64/0/native.jpg rename to test/data/iiif_1_1_tiled/full/49,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/7,8/0/native.jpg b/test/data/iiif_1_1_tiled/full/7,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/7,8/0/native.jpg rename to test/data/iiif_1_1_tiled/full/7,/0/native.jpg diff --git a/test/data/iiif_1_1_files/full/97,128/0/native.jpg b/test/data/iiif_1_1_tiled/full/97,/0/native.jpg similarity index 100% rename from test/data/iiif_1_1_files/full/97,128/0/native.jpg rename to test/data/iiif_1_1_tiled/full/97,/0/native.jpg diff --git a/test/data/iiif_no_tiles/0,0,1024,850/128,107/0/native.jpg b/test/data/iiif_no_tiles/0,0,1024,850/128,107/0/native.jpg deleted file mode 100644 index e0bacd02..00000000 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/128,107/0/native.jpg and /dev/null differ diff --git a/test/data/iiif_no_tiles/0,0,1024,850/256,213/0/native.jpg b/test/data/iiif_no_tiles/0,0,1024,850/256,213/0/native.jpg deleted file mode 100644 index 6c8c5441..00000000 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/256,213/0/native.jpg and /dev/null differ diff --git a/test/data/iiif_no_tiles/0,0,1024,850/32,27/0/native.jpg b/test/data/iiif_no_tiles/0,0,1024,850/32,27/0/native.jpg deleted file mode 100644 index f97752d8..00000000 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/32,27/0/native.jpg and /dev/null differ diff --git a/test/data/iiif_no_tiles/0,0,1024,850/512,425/0/native.jpg b/test/data/iiif_no_tiles/0,0,1024,850/512,425/0/native.jpg deleted file mode 100644 index c3300449..00000000 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/512,425/0/native.jpg and /dev/null differ diff --git a/test/data/iiif_no_tiles/0,0,1024,850/64,54/0/native.jpg b/test/data/iiif_no_tiles/0,0,1024,850/64,54/0/native.jpg deleted file mode 100644 index ad7f30c8..00000000 Binary files a/test/data/iiif_no_tiles/0,0,1024,850/64,54/0/native.jpg and /dev/null differ diff --git a/test/data/iiif_no_tiles/full/full/0/native.jpg b/test/data/iiif_no_tiles/full/full/0/native.jpg deleted file mode 100644 index dcc1d22b..00000000 Binary files a/test/data/iiif_no_tiles/full/full/0/native.jpg and /dev/null differ diff --git a/test/events.js b/test/events.js index 6fb7f23c..86f007c1 100644 --- a/test/events.js +++ b/test/events.js @@ -280,4 +280,23 @@ viewer.open( '/test/data/testpattern.dzi' ); } ); + // ---------- + asyncTest( 'tile-drawing event', function () { + var tileDrawing = function ( event ) { + viewer.removeHandler( 'tile-drawing', tileDrawing ); + ok( event, 'Event handler should be invoked' ); + if ( event ) { + // Make sure we have the expected elements set + ok(event.context, "Context should be set"); + ok(event.tile, "Tile should be set"); + ok(event.rendered, "Rendered should be set"); + } + viewer.close(); + start(); + }; + + viewer.addHandler( 'tile-drawing', tileDrawing ); + viewer.open( '/test/data/testpattern.dzi' ); + } ); + } )(); diff --git a/test/formats.js b/test/formats.js index 9cabf816..7a54048f 100644 --- a/test/formats.js +++ b/test/formats.js @@ -77,12 +77,27 @@ // ---------- asyncTest('IIIF 1.1 JSON', function() { - testOpen('iiif1_1.json'); + testOpen('iiif_1_1_tiled.json'); }); - // ---------- - asyncTest('IIIF No Tiles', function() { - testOpen('iiif_no_tiles.json'); + // ---------- + asyncTest('IIIF No Tiles, Less than 256', function() { + testOpen('iiif_1_1_no_tiles_255.json'); + }); + + // ---------- + asyncTest('IIIF No Tiles, Bet. 256 and 512', function() { + testOpen('iiif_1_1_no_tiles_384.json'); + }); + + // ---------- + asyncTest('IIIF No Tiles, Bet. 512 and 1024', function() { + testOpen('iiif_1_1_no_tiles_768.json'); + }); + + // ---------- + asyncTest('IIIF No Tiles, Larger than 1024', function() { + testOpen('iiif_1_1_no_tiles_1048.json'); }); })(); diff --git a/test/navigator.js b/test/navigator.js index f63c8cd0..95631a91 100644 --- a/test/navigator.js +++ b/test/navigator.js @@ -3,12 +3,11 @@ QUnit.config.autostart = false; (function () { - var viewer, + var debug = false, + viewer, displayRegion, navigator, - navigatorAspectRatio, - leftScalingFactor, - maxHeightFactor, + navigatorScaleFactor, contentStartFromLeft, contentStartFromTop, displayRegionWidth, @@ -42,9 +41,7 @@ QUnit.config.autostart = false; } displayRegion = null; navigator = null; - navigatorAspectRatio = null; - leftScalingFactor = null; - maxHeightFactor = null; + navigatorScaleFactor = null; contentStartFromLeft = null; contentStartFromTop = null; displayRegionWidth = null; @@ -55,8 +52,27 @@ QUnit.config.autostart = false; var assessNavigatorLocation = function (expectedX, expectedY) { var navigator = $(".navigator"); - Util.assessNumericValue(expectedX, navigator.offset().left, 4, ' Navigator x position'); - Util.assessNumericValue(expectedY, navigator.offset().top, 4, ' Navigator y position'); + Util.assessNumericValue(expectedX, navigator.offset().left, 10, ' Navigator x Position'); + Util.assessNumericValue(expectedY, navigator.offset().top, 10, ' Navigator y Position'); + }; + + var assessNavigatorSize = function (expectedWidth, expectedHeight, msg) { + var navigator = $(".navigator"); + + Util.assessNumericValue(expectedWidth, navigator.width(), 2, ' Navigator Width ' + (msg ? msg : '')); + Util.assessNumericValue(expectedHeight, navigator.height(), 2, ' Navigator Height ' + (msg ? msg : '')); + }; + + var assessNavigatorAspectRatio = function (expectedAspectRatio, variance, msg) { + var navigator = $(".navigator"); + + Util.assessNumericValue(expectedAspectRatio, navigator.width() / navigator.height(), variance, ' Navigator Aspect Ratio ' + (msg ? msg : '')); + }; + + var assessNavigatorArea = function (expectedArea, msg) { + var navigator = $(".navigator"); + + Util.assessNumericValue(expectedArea, navigator.width() * navigator.height(), Math.max(navigator.width(), navigator.height()), ' Navigator Area ' + (msg ? msg : '')); }; var navigatorRegionBoundsInPoints = function () { @@ -65,46 +81,47 @@ QUnit.config.autostart = false; expectedDisplayRegionHeight, expectedDisplayRegionXLocation, expectedDisplayRegionYLocation; - if (navigator === null) { - maxHeightFactor = 1; - navigator = $(".navigator"); - navigatorAspectRatio = navigator.height() / navigator.width(); - leftScalingFactor = navigatorAspectRatio * viewer.source.aspectRatio; - if (viewer.source.aspectRatio < 1) { - if (viewer.source.aspectRatio < navigatorAspectRatio) { - maxHeightFactor = viewer.source.aspectRatio * navigatorAspectRatio; - } - else { - maxHeightFactor = viewer.source.aspectRatio; - } - contentStartFromLeft = ((1 / maxHeightFactor) - 1) / 2 * maxHeightFactor * navigator.width(); - contentStartFromTop = 0; - } - else { - if (viewer.source.aspectRatio < navigatorAspectRatio) { - contentStartFromTop = (navigatorAspectRatio - (1 / viewer.source.aspectRatio)) / 2 / navigatorAspectRatio * navigator.height(); - } - else { - contentStartFromTop = (navigatorAspectRatio - (1 / viewer.source.aspectRatio)) / 2 / navigatorAspectRatio * navigator.height(); - leftScalingFactor = 1; - } - } - displayRegionWidth = navigator.width() - 2 * contentStartFromLeft; - displayRegionHeight = navigator.height() - 2 * contentStartFromTop; - } - expectedDisplayRegionWidth = navigator.width() / viewer.viewport.getZoom() * maxHeightFactor; - expectedDisplayRegionHeight = navigator.height() / viewer.viewport.getZoom() * maxHeightFactor; - expectedDisplayRegionXLocation = viewer.viewport.getBounds().x * maxHeightFactor * navigator.width() + contentStartFromLeft; - expectedDisplayRegionYLocation = viewer.viewport.getBounds().y * leftScalingFactor * navigator.width() + contentStartFromTop; + if (navigator === null) { + navigator = $(".navigator"); + navigatorScaleFactor = Math.min(navigator.width() / viewer.viewport.contentSize.x, navigator.height() / viewer.viewport.contentSize.y); + displayRegionWidth = viewer.viewport.contentSize.x * navigatorScaleFactor; + displayRegionHeight = viewer.viewport.contentSize.y * navigatorScaleFactor; + contentStartFromLeft = (navigator.width() - displayRegionWidth) / 2; + contentStartFromTop = (navigator.height() - displayRegionHeight) / 2; + } + expectedDisplayRegionWidth = viewer.viewport.getBounds().width * displayRegionWidth; + expectedDisplayRegionHeight = viewer.viewport.getBounds().height * displayRegionHeight * viewer.source.aspectRatio; + expectedDisplayRegionXLocation = viewer.viewport.getBounds().x * displayRegionWidth + contentStartFromLeft; + expectedDisplayRegionYLocation = viewer.viewport.getBounds().y * displayRegionHeight * viewer.source.aspectRatio + contentStartFromTop; regionBoundsInPoints = new OpenSeadragon.Rect(expectedDisplayRegionXLocation, expectedDisplayRegionYLocation, expectedDisplayRegionWidth, expectedDisplayRegionHeight); + if (debug) { + console.log('Image width: ' + viewer.viewport.contentSize.x + '\n' + + 'Image height: ' + viewer.viewport.contentSize.y + '\n' + + 'navigator.width(): ' + navigator.width() + '\n' + + 'navigator.height(): ' + navigator.height() + '\n' + + 'navigatorScaleFactor: ' + navigatorScaleFactor + '\n' + + 'contentStartFromLeft: ' + contentStartFromLeft + '\n' + + 'contentStartFromTop: ' + contentStartFromTop + '\n' + + 'displayRegionWidth: ' + displayRegionWidth + '\n' + + 'displayRegionHeight: ' + displayRegionHeight + '\n' + + 'expectedDisplayRegionXLocation: ' + expectedDisplayRegionXLocation + '\n' + + 'expectedDisplayRegionYLocation: ' + expectedDisplayRegionYLocation + '\n' + + 'expectedDisplayRegionWidth: ' + expectedDisplayRegionWidth + '\n' + + 'expectedDisplayRegionHeight: ' + expectedDisplayRegionHeight + '\n' + ); + } + return regionBoundsInPoints; }; var assessDisplayRegion = function (status) { + if (debug) { + console.log(status); + } var expectedBounds = navigatorRegionBoundsInPoints(); Util.assessNumericValue(expectedBounds.width, displayRegion.width() + viewer.navigator.totalBorderWidths.x, 2, status + ' Width synchronization'); Util.assessNumericValue(expectedBounds.height, displayRegion.height() + viewer.navigator.totalBorderWidths.y, 2, status + ' Height synchronization'); @@ -259,34 +276,53 @@ QUnit.config.autostart = false; simulateNavigatorDrag(viewer.navigator, delta.x * displayRegionWidth, delta.y * displayRegionHeight); }; + var resizeElement = function ($element, width, height) { + $element.width(width); + $element.height(height); + }; + var assessNavigatorViewerPlacement = function (seadragonProperties, testProperties) { var navigatorOperationScenarios = [ - {interactionOperation:clickOnNavigator("TOPRIGHT"), - assessmentOperation:assessViewerInCorner("TOPRIGHT"), - assessmentMessage:"After click on navigator on top right" }, - {interactionOperation:dragNavigatorBackToCenter, - assessmentOperation:assessViewerInCenter, - assessmentMessage:"After drag on navigator from top right" }, - {interactionOperation:clickOnNavigator("BOTTOMLEFT"), - assessmentOperation:assessViewerInCorner("BOTTOMLEFT"), - assessmentMessage:"After click on navigator on bottom left" }, - {interactionOperation:dragNavigatorBackToCenter, - assessmentOperation:assessViewerInCenter, - assessmentMessage:"After drag on navigator from bottom left" }, - {interactionOperation:clickOnNavigator("BOTTOMRIGHT"), - assessmentOperation:assessViewerInCorner("BOTTOMRIGHT"), - assessmentMessage:"After click on navigator on bottom right" }, - {interactionOperation:dragNavigatorBackToCenter, - assessmentOperation:assessViewerInCenter, - assessmentMessage:"After drag on navigator from bottom right" }, - {interactionOperation:clickOnNavigator("TOPLEFT"), - assessmentOperation:assessViewerInCorner("TOPLEFT"), - assessmentMessage:"After click on navigator on top left" }, - {interactionOperation:dragNavigatorBackToCenter, - assessmentOperation:assessViewerInCenter, - assessmentMessage:"After drag on navigator from top left" } - ], - autoFadeWaitTime = 100; + {interactionOperation:clickOnNavigator("TOPRIGHT"), + assessmentOperation:assessViewerInCorner("TOPRIGHT"), + assessmentMessage:"After click on navigator on top right" }, + {interactionOperation:dragNavigatorBackToCenter, + assessmentOperation:assessViewerInCenter, + assessmentMessage:"After drag on navigator from top right" }, + {interactionOperation:clickOnNavigator("BOTTOMLEFT"), + assessmentOperation:assessViewerInCorner("BOTTOMLEFT"), + assessmentMessage:"After click on navigator on bottom left" }, + {interactionOperation:dragNavigatorBackToCenter, + assessmentOperation:assessViewerInCenter, + assessmentMessage:"After drag on navigator from bottom left" }, + {interactionOperation:clickOnNavigator("BOTTOMRIGHT"), + assessmentOperation:assessViewerInCorner("BOTTOMRIGHT"), + assessmentMessage:"After click on navigator on bottom right" }, + {interactionOperation:dragNavigatorBackToCenter, + assessmentOperation:assessViewerInCenter, + assessmentMessage:"After drag on navigator from bottom right" }, + {interactionOperation:clickOnNavigator("TOPLEFT"), + assessmentOperation:assessViewerInCorner("TOPLEFT"), + assessmentMessage:"After click on navigator on top left" }, + {interactionOperation:dragNavigatorBackToCenter, + assessmentOperation:assessViewerInCenter, + assessmentMessage:"After drag on navigator from top left" } + ], + viewerResizeScenarios = [ + {resizeFactorX:0.5, resizeFactorY:1.0, assessmentMessage:"After Viewer Resize (50%, 100%)"}, + {resizeFactorX:1.0, resizeFactorY:0.5, assessmentMessage:"After Viewer Resize (100%, 50%)"}, + {resizeFactorX:1.0, resizeFactorY:1.0, assessmentMessage:"After Viewer Resize (100%, 100%)"} + ], + navigatorResizeScenarios = [ + {resizeFactorX:0.75, resizeFactorY:1.0, assessmentMessage:"After Navigator Resize (75%, 100%)"}, + {resizeFactorX:1.0, resizeFactorY:0.75, assessmentMessage:"After Navigator Resize (100%, 75%)"}, + {resizeFactorX:1.0, resizeFactorY:1.0, assessmentMessage:"After Navigator Resize (100%, 100%)"} + ], + autoFadeWaitTime = 100, + navigatorElement = null, + viewerElement = null, + viewerOriginalSize = null, + navigatorOriginalSize = null; seadragonProperties.visibilityRatio = 1; viewer = OpenSeadragon(seadragonProperties); @@ -330,38 +366,112 @@ QUnit.config.autostart = false; waitForViewer(assessAfterDragOnViewer); }; + var assessAfterResizeNavigator = function () { + viewer.viewport.zoomTo(viewer.viewport.getZoom() * 2); + waitForViewer(assessAfterZoomOnViewer); + }; + + var assessNavigatorResizeAndTakeNextStep = function (step) { + return function () { + var nextStep = step + 1; + assessNavigatorSize(navigatorOriginalSize.x * navigatorResizeScenarios[step].resizeFactorX, navigatorOriginalSize.y * navigatorResizeScenarios[step].resizeFactorY, navigatorResizeScenarios[step].assessmentMessage); + assessDisplayRegion(navigatorResizeScenarios[step].assessmentMessage); + if (step === viewerResizeScenarios.length - 1) { + assessAfterResizeNavigator(); + } + else { + resizeElement(navigatorElement, navigatorOriginalSize.x * navigatorResizeScenarios[nextStep].resizeFactorX, navigatorOriginalSize.y * navigatorResizeScenarios[nextStep].resizeFactorY); + waitForViewer(assessNavigatorResizeAndTakeNextStep(nextStep)); + } + }; + }; + + var assessViewerResizeAndTakeNextStep = function (step) { + return function () { + var nextStep = step + 1; + if (seadragonProperties.navigatorId) { + // Navigator hosted in outside element...size shouldn't change + assessNavigatorSize(navigatorOriginalSize.x, navigatorOriginalSize.y, viewerResizeScenarios[step].assessmentMessage); + } + else { + // Navigator hosted in viewer + if (seadragonProperties.navigatorPosition && seadragonProperties.navigatorPosition == 'ABSOLUTE') { + // Navigator positioned 'ABSOLUTE'...size shouldn't change + assessNavigatorSize(navigatorOriginalSize.x, navigatorOriginalSize.y, viewerResizeScenarios[step].assessmentMessage); + } + else { + // Navigator positioned 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', or 'BOTTOM_RIGHT' + if (seadragonProperties.navigatorMaintainSizeRatio) { + // Navigator should maintain aspect ratio and size proportioned to viewer size + assessNavigatorAspectRatio(viewerElement.width() / viewerElement.height(), 0.0001, viewerResizeScenarios[step].assessmentMessage); + assessNavigatorSize(viewerElement.width() * seadragonProperties.navigatorSizeRatio, viewerElement.height() * seadragonProperties.navigatorSizeRatio, viewerResizeScenarios[step].assessmentMessage); + } + else { + // Navigator should maintain aspect ratio and area + // Variances are loosened up here, since 1 pixel rounding difference in resizing to maintain area + // can cause a relatively large difference in area and aspect ratio. + assessNavigatorAspectRatio(viewerElement.width() / viewerElement.height(), 0.1, viewerResizeScenarios[step].assessmentMessage); + assessNavigatorArea(navigatorOriginalSize.x * navigatorOriginalSize.y, viewerResizeScenarios[step].assessmentMessage); + } + } + } + + if (step === viewerResizeScenarios.length - 1) { + if (seadragonProperties.navigatorId) { + // Navigator hosted in outside element...run navigator resize tests + resizeElement(navigatorElement, navigatorOriginalSize.x * navigatorResizeScenarios[0].resizeFactorX, navigatorOriginalSize.y * navigatorResizeScenarios[0].resizeFactorY); + waitForViewer(assessNavigatorResizeAndTakeNextStep(0)); + } + else { + // Navigator hosted in viewer...skip navigator resize tests + assessAfterResizeNavigator(); + } + } + else { + resizeElement(viewerElement, viewerOriginalSize.x * viewerResizeScenarios[nextStep].resizeFactorX, viewerOriginalSize.y * viewerResizeScenarios[nextStep].resizeFactorY); + waitForViewer(assessViewerResizeAndTakeNextStep(nextStep)); + } + }; + }; + var captureInitialStateThenAct = function () { assessDisplayRegion("After image load"); testProperties.determineExpectationsAndAssessNavigatorLocation(seadragonProperties, testProperties); - viewer.viewport.zoomTo(viewer.viewport.getZoom() * 2); - waitForViewer(assessAfterZoomOnViewer); + viewerOriginalSize = new OpenSeadragon.Point(viewerElement.width(), viewerElement.height()); + navigatorOriginalSize = new OpenSeadragon.Point(navigatorElement.width(), navigatorElement.height()); + + resizeElement(viewerElement, viewerOriginalSize.x * viewerResizeScenarios[0].resizeFactorX, viewerOriginalSize.y * viewerResizeScenarios[0].resizeFactorY); + waitForViewer(assessViewerResizeAndTakeNextStep(0)); }; var assessAutoFadeTriggered = function () { - ok($(testProperties.navigatorLocator).parent().css("opacity") < 1, "Expecting navigator to be autofade when in the default location"); + ok(navigatorElement.parent().css("opacity") < 1, "Expecting navigator to be autofade when in the default location"); waitForViewer(captureInitialStateThenAct); }; var assessAutoFadeDisabled = function () { - ok($(testProperties.navigatorLocator).parent().css("opacity") > 0, "Expecting navigator to be always visible when in a custom location"); + ok(navigatorElement.parent().css("opacity") > 0, "Expecting navigator to be always visible when in a custom location"); waitForViewer(captureInitialStateThenAct); }; var openHandler = function () { viewer.removeHandler('open', openHandler); + navigatorElement = $(testProperties.navigatorLocator); + viewerElement = $("#" + seadragonProperties.id); + //TODO This should be testProperties.testAutoFade, but test hangs. Fix this! if (!testProperties.testAutohide) { waitForViewer(captureInitialStateThenAct); } else { - ok($(testProperties.navigatorLocator).parent().css("opacity") > 0, "Expecting navigator to be visible initially"); + ok(navigatorElement.parent().css("opacity") > 0, "Expecting navigator to be visible initially"); var event = { clientX:1, clientY:1 }; - $("#" + seadragonProperties.id).simulate('blur', event); + viewerElement.simulate('blur', event); if (testProperties.expectedAutoFade) { setTimeout(assessAutoFadeTriggered,autoFadeWaitTime); @@ -379,6 +489,8 @@ QUnit.config.autostart = false; prefixUrl:'/build/openseadragon/images/', tileSources:'/test/data/wide.dzi', showNavigator:true, + navigatorSizeRatio:0.2, + navigatorMaintainSizeRatio: false, animationTime:0 }, { @@ -390,11 +502,192 @@ QUnit.config.autostart = false; navigatorElement = $(testProperties.navigatorLocator); assessNavigatorLocation(mainViewerElement.offset().left + mainViewerElement.width() - navigatorElement.width(), mainViewerElement.offset().top); + assessNavigatorSize(mainViewerElement.width() * seadragonProperties.navigatorSizeRatio, mainViewerElement.height() * seadragonProperties.navigatorSizeRatio); + assessNavigatorAspectRatio(mainViewerElement.width() / mainViewerElement.height(), 0.0001); } }); }); - asyncTest('CustomNavigatorLocationWithWideImageWideViewer', function () { + asyncTest('DefaultNavigatorLocationWithTallImageWideViewer', function () { + assessNavigatorViewerPlacement({ + id:'wideexample', + prefixUrl:'/build/openseadragon/images/', + tileSources:'/test/data/tall.dzi', + showNavigator:true, + navigatorSizeRatio:0.2, + navigatorMaintainSizeRatio: false, + animationTime:0, + controlsFadeDelay:0, + controlsFadeLength:1 + }, + { + displayRegionLocator:'.navigator .displayregion', + navigatorLocator:'.navigator', + testAutoFade: true, + expectedAutoFade: true, + determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { + var mainViewerElement = $("#" + seadragonProperties.id), + navigatorElement = $(testProperties.navigatorLocator); + assessNavigatorLocation(mainViewerElement.offset().left + mainViewerElement.width() - navigatorElement.width(), + mainViewerElement.offset().top); + assessNavigatorSize(mainViewerElement.width() * seadragonProperties.navigatorSizeRatio, mainViewerElement.height() * seadragonProperties.navigatorSizeRatio); + assessNavigatorAspectRatio(mainViewerElement.width() / mainViewerElement.height(), 0.0001); + } + }); + }); + + asyncTest('TopLeftNavigatorLocation', function () { + assessNavigatorViewerPlacement({ + id:'example', + prefixUrl:'/build/openseadragon/images/', + tileSources:'/test/data/testpattern.dzi', + showNavigationControl: false, + showNavigator:true, + navigatorSizeRatio:0.2, + navigatorMaintainSizeRatio: false, + navigatorPosition: 'TOP_LEFT', + animationTime:0, + controlsFadeDelay:0, + controlsFadeLength:1 + }, + { + displayRegionLocator:'.navigator .displayregion', + navigatorLocator:'.navigator', + testAutoFade: true, + expectedAutoFade: true, + determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { + var mainViewerElement = $("#" + seadragonProperties.id), + navigatorElement = $(testProperties.navigatorLocator); + assessNavigatorLocation(mainViewerElement.offset().left, + mainViewerElement.offset().top); + assessNavigatorSize(mainViewerElement.width() * seadragonProperties.navigatorSizeRatio, mainViewerElement.height() * seadragonProperties.navigatorSizeRatio); + assessNavigatorAspectRatio(mainViewerElement.width() / mainViewerElement.height(), 0.0001); + } + }); + }); + + asyncTest('TopRightNavigatorLocation', function () { + assessNavigatorViewerPlacement({ + id:'example', + prefixUrl:'/build/openseadragon/images/', + tileSources:'/test/data/testpattern.dzi', + showNavigationControl: false, + showNavigator:true, + navigatorSizeRatio:0.2, + navigatorMaintainSizeRatio: true, + navigatorPosition: 'TOP_RIGHT', + animationTime:0, + controlsFadeDelay:0, + controlsFadeLength:1 + }, + { + displayRegionLocator:'.navigator .displayregion', + navigatorLocator:'.navigator', + testAutoFade: true, + expectedAutoFade: true, + determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { + var mainViewerElement = $("#" + seadragonProperties.id), + navigatorElement = $(testProperties.navigatorLocator); + assessNavigatorLocation(mainViewerElement.offset().left + mainViewerElement.width() - navigatorElement.width(), + mainViewerElement.offset().top); + assessNavigatorSize(mainViewerElement.width() * seadragonProperties.navigatorSizeRatio, mainViewerElement.height() * seadragonProperties.navigatorSizeRatio); + assessNavigatorAspectRatio(mainViewerElement.width() / mainViewerElement.height(), 0.0001); + } + }); + }); + + asyncTest('BottomLeftNavigatorLocation', function () { + assessNavigatorViewerPlacement({ + id:'example', + prefixUrl:'/build/openseadragon/images/', + tileSources:'/test/data/testpattern.dzi', + showNavigationControl: false, + showNavigator:true, + navigatorSizeRatio:0.2, + navigatorMaintainSizeRatio: false, + navigatorPosition: 'BOTTOM_LEFT', + animationTime:0, + controlsFadeDelay:0, + controlsFadeLength:1 + }, + { + displayRegionLocator:'.navigator .displayregion', + navigatorLocator:'.navigator', + testAutoFade: true, + expectedAutoFade: true, + determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { + var mainViewerElement = $("#" + seadragonProperties.id), + navigatorElement = $(testProperties.navigatorLocator); + assessNavigatorLocation(mainViewerElement.offset().left, + mainViewerElement.offset().top + mainViewerElement.height() - navigatorElement.height()); + assessNavigatorSize(mainViewerElement.width() * seadragonProperties.navigatorSizeRatio, mainViewerElement.height() * seadragonProperties.navigatorSizeRatio); + assessNavigatorAspectRatio(mainViewerElement.width() / mainViewerElement.height(), 0.0001); + } + }); + }); + + asyncTest('BottomRightNavigatorLocation', function () { + assessNavigatorViewerPlacement({ + id:'example', + prefixUrl:'/build/openseadragon/images/', + tileSources:'/test/data/testpattern.dzi', + showNavigationControl: false, + showNavigator:true, + navigatorSizeRatio:0.2, + navigatorMaintainSizeRatio: false, + navigatorPosition: 'BOTTOM_RIGHT', + animationTime:0, + controlsFadeDelay:0, + controlsFadeLength:1 + }, + { + displayRegionLocator:'.navigator .displayregion', + navigatorLocator:'.navigator', + testAutoFade: true, + expectedAutoFade: true, + determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { + var mainViewerElement = $("#" + seadragonProperties.id), + navigatorElement = $(testProperties.navigatorLocator); + assessNavigatorLocation(mainViewerElement.offset().left + mainViewerElement.width() - navigatorElement.width(), + mainViewerElement.offset().top + mainViewerElement.height() - navigatorElement.height()); + assessNavigatorSize(mainViewerElement.width() * seadragonProperties.navigatorSizeRatio, mainViewerElement.height() * seadragonProperties.navigatorSizeRatio); + assessNavigatorAspectRatio(mainViewerElement.width() / mainViewerElement.height(), 0.0001); + } + }); + }); + + asyncTest('AbsoluteNavigatorLocation', function () { + assessNavigatorViewerPlacement({ + id:'example', + prefixUrl:'/build/openseadragon/images/', + tileSources:'/test/data/testpattern.dzi', + showNavigationControl: false, + showNavigator:true, + navigatorPosition: 'ABSOLUTE', + navigatorTop: 10, + navigatorLeft: 10, + navigatorHeight: 150, + navigatorWidth: 175, + animationTime:0, + controlsFadeDelay:0, + controlsFadeLength:1 + }, + { + displayRegionLocator:'.navigator .displayregion', + navigatorLocator:'.navigator', + testAutoFade: true, + expectedAutoFade: true, + determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { + var mainViewerElement = $("#" + seadragonProperties.id), + navigatorElement = $(testProperties.navigatorLocator); + assessNavigatorLocation(mainViewerElement.offset().left + seadragonProperties.navigatorLeft, + mainViewerElement.offset().top + seadragonProperties.navigatorTop); + assessNavigatorSize(seadragonProperties.navigatorWidth, seadragonProperties.navigatorHeight); + } + }); + }); + + asyncTest('CustomNavigatorElementWithWideImageWideViewer', function () { assessNavigatorViewerPlacement({ id:'wideexample', navigatorId:'exampleNavigator', @@ -416,8 +709,21 @@ QUnit.config.autostart = false; }); }); - asyncTest('CustomDialogNavigatorLocationWithTallImageTallViewer', function () { - $('#exampleNavigator').dialog(); + asyncTest('CustomDialogNavigatorElementWithTallImageTallViewer', function () { + $('#exampleNavigator').dialog({ width: 150, + height:100, + open: function (event, ui) { + $('#exampleNavigator').width(150); + $('#exampleNavigator').height(100); + } + //TODO Use this in testing resizable navigator + //resize: function (event, ui) { + // //ui.size.width + // //ui.size.height + // //$('#exampleNavigator').dialog("option", "width", 200); + // //$('#exampleNavigator').dialog("option", "width", 200); + //} + }); assessNavigatorViewerPlacement({ id:'tallexample', navigatorId:'exampleNavigator', @@ -440,30 +746,7 @@ QUnit.config.autostart = false; var jqueryDialog = $(testProperties.navigatorLocator); assessNavigatorLocation(jqueryDialog.offset().left, jqueryDialog.offset().top); - } - }); - }); - - asyncTest('DefaultNavigatorLocationWithTallImageWideViewer', function () { - assessNavigatorViewerPlacement({ - id:'wideexample', - prefixUrl:'/build/openseadragon/images/', - tileSources:'/test/data/tall.dzi', - showNavigator:true, - animationTime:0, - controlsFadeDelay:0, - controlsFadeLength:1 - }, - { - displayRegionLocator:'.navigator .displayregion', - navigatorLocator:'.navigator', - testAutoFade: true, - expectedAutoFade: true, - determineExpectationsAndAssessNavigatorLocation:function (seadragonProperties, testProperties) { - var mainViewerElement = $("#" + seadragonProperties.id), - navigatorElement = $(testProperties.navigatorLocator); - assessNavigatorLocation(mainViewerElement.offset().left + mainViewerElement.width() - navigatorElement.width(), - mainViewerElement.offset().top); + assessNavigatorSize(jqueryDialog.width(), jqueryDialog.height()); } }); }); diff --git a/test/overlays.js b/test/overlays.js new file mode 100644 index 00000000..47e0af89 --- /dev/null +++ b/test/overlays.js @@ -0,0 +1,344 @@ +/* global QUnit, module, Util, $, console, test, asyncTest, start, ok, equal */ + +( function() { + var viewer; + + module( "Overlays", { + setup: function() { + var example = $( '
' ).appendTo( "#qunit-fixture" ); + var fixedOverlay = $( '
' ).appendTo(example); + fixedOverlay.width(70); + fixedOverlay.height(60); + + testLog.reset(); + }, + teardown: function() { + resetTestVariables(); + } + } ); + + var resetTestVariables = function() { + if ( viewer ) { + viewer.close(); + } + }; + + function waitForViewer( handler, count ) { + if ( typeof count !== "number" ) { + count = 0; + } + var ready = viewer.isOpen() && + viewer.drawer !== null && + !viewer.drawer.needsUpdate() && + Util.equalsWithVariance( viewer.viewport.getBounds( true ).x, + viewer.viewport.getBounds().x, 0.000 ) && + Util.equalsWithVariance( viewer.viewport.getBounds( true ).y, + viewer.viewport.getBounds().y, 0.000 ) && + Util.equalsWithVariance( viewer.viewport.getBounds( true ).width, + viewer.viewport.getBounds().width, 0.000 ); + + if ( ready ) { + handler(); + } else if ( count < 50 ) { + count++; + setTimeout( function() { + waitForViewer( handler, count ); + }, 100 ); + } else { + console.log( "waitForViewer:" + viewer.isOpen( ) + ":" + viewer.drawer + + ":" + viewer.drawer.needsUpdate() ); + handler(); + } + } + + asyncTest( 'Overlays via viewer options', function() { + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: [ '/test/data/testpattern.dzi', '/test/data/testpattern.dzi' ], + springStiffness: 100, // Faster animation = faster tests + overlays: [ { + x: 0.1, + y: 0.4, + width: 0.09, + height: 0.09, + id: "overlay" + } ] + } ); + viewer.addHandler( 'open', openHandler ); + + function openHandler() { + viewer.removeHandler( 'open', openHandler ); + + 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 ); + } + + function openPageHandler() { + viewer.removeHandler( 'open', openPageHandler ); + + 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(); + } + + function closeHandler() { + viewer.removeHandler( 'close', closeHandler ); + + 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( 'Page Overlays via viewer options', function() { + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: [ { + Image: { + xmlns: "http://schemas.microsoft.com/deepzoom/2008", + Url: "/test/data/testpattern_files/", + Format: "jpg", + Overlap: "1", + TileSize: "254", + Size: { + Width: 1000, + Height: 1000 + } + }, + overlays: [ { + x: 0.1, + y: 0.4, + width: 0.09, + height: 0.09, + id: "overlay" + } ] + }, { + Image: { + xmlns: "http://schemas.microsoft.com/deepzoom/2008", + Url: "/test/data/testpattern_files/", + Format: "jpg", + Overlap: "1", + TileSize: "254", + Size: { + Width: 1000, + Height: 1000 + } + } + } ], + springStiffness: 100 // Faster animation = faster tests + } ); + viewer.addHandler( 'open', openHandler ); + + function openHandler() { + viewer.removeHandler( 'open', openHandler ); + + equal( viewer.overlays.length, 0, "No global overlay should be added." ); + equal( viewer.currentOverlays.length, 1, "Page overlay should be open." ); + + viewer.addHandler( 'open', openPageHandler ); + viewer.goToPage( 1 ); + } + + function openPageHandler() { + viewer.removeHandler( 'open', openPageHandler ); + + 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(); + } + + function closeHandler() { + viewer.removeHandler( 'close', closeHandler ); + + 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(); + } + } ); + + asyncTest( 'Overlays via addOverlay method', function() { + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: [ '/test/data/testpattern.dzi', '/test/data/testpattern.dzi' ], + springStiffness: 100 // Faster animation = faster tests + } ); + viewer.addHandler( 'open', openHandler ); + + function openHandler() { + viewer.removeHandler( 'open', openHandler ); + + 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 = $( "
" ).prop("id", "overlay").get( 0 ); + viewer.addOverlay( overlay, rect ); + 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 ); + } + + function openPageHandler() { + viewer.removeHandler( 'open', openPageHandler ); + + 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(); + } + + function closeHandler() { + viewer.removeHandler( 'close', closeHandler ); + + 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(); + } + + } ); + + asyncTest( 'Overlays size in pixels', function() { + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: [ '/test/data/testpattern.dzi', '/test/data/testpattern.dzi' ], + springStiffness: 100, // Faster animation = faster tests + overlays: [ { + px: 13, + py: 120, + width: 124, + height: 132, + id: "overlay" + }, { + px: 400, + py: 500, + id: "fixed-overlay" + }] + } ); + + function checkOverlayPosition( contextMessage ) { + var viewport = viewer.viewport; + + var expPosition = viewport.imageToViewerElementCoordinates( + new OpenSeadragon.Point( 13, 120 ) ).apply( Math.floor ); + var actPosition = $( "#overlay" ).position(); + equal( actPosition.left, expPosition.x, "X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); + + var zoom = viewport.viewportToImageZoom( viewport.getZoom( true ) ); + var expectedWidth = Math.ceil( 124 * zoom ); + var expectedHeight = Math.ceil( 132 * zoom ); + equal( $( "#overlay" ).width(), expectedWidth, "Width mismatch " + contextMessage ); + equal( $( "#overlay" ).height( ), expectedHeight, "Height mismatch " + contextMessage ); + + + expPosition = viewport.imageToViewerElementCoordinates( + new OpenSeadragon.Point( 400, 500 ) ).apply( Math.floor ); + actPosition = $( "#fixed-overlay" ).position(); + equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); + + equal( $( "#fixed-overlay" ).width(), 70, "Fixed overlay width mismatch " + contextMessage ); + equal( $( "#fixed-overlay" ).height( ), 60, "Fixed overlay height mismatch " + contextMessage ); + } + + waitForViewer( function() { + checkOverlayPosition( "after opening using image coordinates" ); + + viewer.viewport.zoomBy( 1.1 ).panBy( new OpenSeadragon.Point( 0.1, 0.2 ) ); + waitForViewer( function() { + checkOverlayPosition( "after zoom and pan using image coordinates" ); + + viewer.viewport.goHome(); + waitForViewer( function() { + checkOverlayPosition( "after goHome using image coordinates" ); + start(); + } ); + } ); + + } ); + } ); + + asyncTest( 'Overlays size in points', function() { + + viewer = OpenSeadragon( { + id: 'example-overlays', + prefixUrl: '/build/openseadragon/images/', + tileSources: [ '/test/data/testpattern.dzi', '/test/data/testpattern.dzi' ], + springStiffness: 100, // Faster animation = faster tests + overlays: [ { + x: 0.2, + y: 0.1, + width: 0.5, + height: 0.1, + id: "overlay" + },{ + x: 0.5, + y: 0.6, + id: "fixed-overlay" + } ] + } ); + + function checkOverlayPosition( contextMessage ) { + var viewport = viewer.viewport; + + var expPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point( 0.2, 0.1 ) ).apply( Math.floor ); + var actPosition = $( "#overlay" ).position(); + equal( actPosition.left, expPosition.x, "X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Y position mismatch " + contextMessage ); + + var expectedSize = viewport.deltaPixelsFromPoints( + new OpenSeadragon.Point(0.5, 0.1)); + equal( $( "#overlay" ).width(), expectedSize.x, "Width mismatch " + contextMessage ); + equal( $( "#overlay" ).height( ), expectedSize.y, "Height mismatch " + contextMessage ); + + + expPosition = viewport.viewportToViewerElementCoordinates( + new OpenSeadragon.Point( 0.5, 0.6 ) ).apply( Math.floor ); + actPosition = $( "#fixed-overlay" ).position(); + equal( actPosition.left, expPosition.x, "Fixed overlay X position mismatch " + contextMessage ); + equal( actPosition.top, expPosition.y, "Fixed overlay Y position mismatch " + contextMessage ); + + equal( $( "#fixed-overlay" ).width(), 70, "Fixed overlay width mismatch " + contextMessage ); + equal( $( "#fixed-overlay" ).height( ), 60, "Fixed overlay height mismatch " + contextMessage ); + } + + waitForViewer( function() { + checkOverlayPosition( "after opening using viewport coordinates" ); + + viewer.viewport.zoomBy( 1.1 ).panBy( new OpenSeadragon.Point( 0.1, 0.2 ) ); + waitForViewer( function() { + checkOverlayPosition( "after zoom and pan using viewport coordinates" ); + + viewer.viewport.goHome(); + waitForViewer( function() { + checkOverlayPosition( "after goHome using viewport coordinates" ); + start(); + } ); + } ); + + } ); + } ); + +} )(); diff --git a/test/rotate.js b/test/rotate.js new file mode 100644 index 00000000..9128d7fa --- /dev/null +++ b/test/rotate.js @@ -0,0 +1,77 @@ +/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */ + +(function () { + var viewer; + + module('Basic', { + setup: function () { + var example = $('
').appendTo("#qunit-fixture"); + + testLog.reset(); + + }, + teardown: function () { + if (viewer && viewer.close) { + viewer.close(); + } + + viewer = null; + } + }); + + asyncTest('RotateControlOff', function () { + + var openHandler = function (event) { + viewer.removeHandler('open', openHandler); + ok(true, 'Open event was sent'); + ok(viewer.drawer, 'Drawer exists'); + ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true'); + ok(!viewer.showRotationControl, 'showRotationControl should be off'); + ok(!viewer.rotateLeft, "rotateLeft button should be null"); + ok(!viewer.rotateRight, "rotateRight button should be null"); + start(); + }; + + viewer = OpenSeadragon({ + id: 'rotateTests', + prefixUrl: '/build/openseadragon/images/', + springStiffness: 100, // Faster animation = faster tests + showRotationControl: false + }); + viewer.addHandler('open', openHandler); + viewer.open('/test/data/testpattern.dzi'); + }); + + asyncTest('RotateControlOn', function () { + + var openHandler = function (event) { + viewer.removeHandler('open', openHandler); + ok(true, 'Open event was sent'); + ok(viewer.drawer, 'Drawer exists'); + ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true'); + ok(viewer.showRotationControl, 'showRotationControl should be true'); + ok(-1 != viewer.buttons.buttons.indexOf(viewer.rotateLeft), "rotateLeft should be found"); + ok(-1 != viewer.buttons.buttons.indexOf(viewer.rotateRight), "rotateRight should be found"); + + // Now simulate the left/right button clicks. + // TODO: re-factor simulateViewerClickWithDrag so it'll accept any element, and use that. + ok(viewer.viewport.degrees === 0, "Image should start at 0 degrees rotation"); + viewer.rotateLeft.onRelease(); + ok(viewer.viewport.degrees === 270, "Image should be 270 degrees rotation (left)"); + viewer.rotateRight.onRelease(); + ok(viewer.viewport.degrees === 0, "Image should be 270 degrees rotation (right)"); + + start(); + }; + + viewer = OpenSeadragon({ + id: 'rotateTests', + prefixUrl: '/build/openseadragon/images/', + springStiffness: 100, // Faster animation = faster tests + showRotationControl: true + }); + viewer.addHandler('open', openHandler); + viewer.open('/test/data/testpattern.dzi'); + }); + +})(); diff --git a/test/test.css b/test/test.css index 4eb71894..c8e85fab 100644 --- a/test/test.css +++ b/test/test.css @@ -3,6 +3,11 @@ width: 500px; } +#exampleNavigator { + height: 100px; + width: 150px; +} + #wideexample { height: 300px; width: 700px; @@ -13,7 +18,7 @@ width: 300px; } -#unitsexample { +#unitsexample, #example-overlays { height: 500px; width: 500px; } diff --git a/test/test.html b/test/test.html index bfe9e345..94602d88 100644 --- a/test/test.html +++ b/test/test.html @@ -28,5 +28,7 @@ + + diff --git a/test/units.js b/test/units.js index 348835b3..1ce10a09 100644 --- a/test/units.js +++ b/test/units.js @@ -26,12 +26,34 @@ function pointEqual(a, b, message) { - ok(a.x === b.x && a.y === b.y, message); + Util.assessNumericValue(a.x, b.x, 0.00000001, message); + Util.assessNumericValue(a.y, b.y, 0.00000001, message); } // ---------- asyncTest('Coordinates conversions', function() { + function checkPoint(context) { + var viewport = viewer.viewport; + + var point = new OpenSeadragon.Point(15, 12); + var result = viewport.viewerElementToImageCoordinates( + viewport.imageToViewerElementCoordinates(point)); + pointEqual(result, point, 'viewerElement and image ' + context); + + var result = viewport.windowToImageCoordinates( + viewport.imageToWindowCoordinates(point)); + pointEqual(result, point, 'window and image ' + context); + + var result = viewport.viewerElementToViewportCoordinates( + viewport.viewportToViewerElementCoordinates(point)); + pointEqual(result, point, 'viewerElement and viewport ' + context); + + var result = viewport.windowToViewportCoordinates( + viewport.viewportToWindowCoordinates(point)); + pointEqual(result, point, 'window and viewport ' + context); + } + viewer.addHandler("open", function () { var viewport = viewer.viewport; @@ -52,24 +74,13 @@ var pixel = viewport.viewerElementToImageCoordinates(viewerTopRight); pointEqual(pixel, imageTopRight, 'Viewer top right has viewport coordinates imageWidth,0.'); - var point = new OpenSeadragon.Point(15, 12); - var result = viewport.viewerElementToImageCoordinates( - viewport.imageToViewerElementCoordinates(point)); - pointEqual(result, point, 'viewerElement and image'); - - var result = viewport.windowToImageCoordinates( - viewport.imageToWindowCoordinates(point)); - pointEqual(result, point, 'window and image'); - - var result = viewport.viewerElementToViewportCoordinates( - viewport.viewportToViewerElementCoordinates(point)); - pointEqual(result, point, 'viewerElement and viewport'); - - var result = viewport.windowToViewportCoordinates( - viewport.viewportToWindowCoordinates(point)); - pointEqual(result, point, 'window and viewport'); - - start(); + checkPoint('after opening'); + viewer.addHandler('animation-finish', function animationHandler() { + viewer.removeHandler('animation-finish', animationHandler); + checkPoint('after zoom and pan'); + start(); + }); + viewer.viewport.zoomTo(0.8).panTo(new OpenSeadragon.Point(0.1, 0.2)); }); viewer.open('/test/data/testpattern.dzi'); }); @@ -104,14 +115,13 @@ checkZoom(); var zoomHandler = function() { - viewer.removeHandler('animationfinish', zoomHandler); + viewer.removeHandler('animation-finish', zoomHandler); checkZoom(); start(); }; - viewer.addHandler('animationfinish', zoomHandler); + viewer.addHandler('animation-finish', zoomHandler); viewport.zoomTo(2); - start(); }); viewer.open('/test/data/testpattern.dzi');