diff --git a/Gruntfile.js b/Gruntfile.js index c9a35b11..b52657c4 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -14,6 +14,7 @@ module.exports = function(grunt) { // ---------- var distribution = "build/openseadragon/openseadragon.js", minified = "build/openseadragon/openseadragon.min.js", + releaseRoot = "../site-build/built-openseadragon/", sources = [ "src/openseadragon.js", "src/fullscreen.js", @@ -52,11 +53,7 @@ module.exports = function(grunt) { clean: { build: ["build"], release: { - src: [ - "../site-build/openseadragon", - "../site-build/openseadragon.zip", - "../site-build/openseadragon.tar" - ], + src: [releaseRoot], options: { force: true } @@ -150,14 +147,16 @@ module.exports = function(grunt) { grunt.file.recurse("images", function(abspath, rootdir, subdir, filename) { grunt.file.copy(abspath, "build/openseadragon/images/" + (subdir || "") + filename); }); + + grunt.file.copy("changelog.txt", "build/changelog.txt"); }); // ---------- // Copy:release task. - // Copies the contents of the build folder into ../site-build. + // Copies the contents of the build folder into the release folder. grunt.registerTask("copy:release", function() { grunt.file.recurse("build", function(abspath, rootdir, subdir, filename) { - var dest = "../site-build/" + var dest = releaseRoot + (subdir ? subdir + "/" : '/') + filename; @@ -184,7 +183,7 @@ module.exports = function(grunt) { // ---------- // Publish task. - // Cleans the built files out of ../site-build and copies newly built ones over. + // Cleans the built files out of the release folder and copies newly built ones over. grunt.registerTask("publish", ["package", "clean:release", "copy:release"]); // ---------- diff --git a/README.md b/README.md index ae259f02..3c194552 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,12 @@ If you wish to work interactively with the tests or test your changes: and open `http://localhost:8000/` in your browser +## Contributing + +OpenSeadragon is truly a community project; we welcome your involvement! + +When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For more thoughts on code style, see https://github.com/rwldrn/idiomatic.js/. + ## Licenses OpenSeadragon was initially released with a New BSD License ( preserved below ), while work done by Chris Thatcher is additionally licensed under the MIT License. diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 00000000..871d7f84 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,29 @@ +OPENSEADRAGON CHANGELOG +======================= + +0.9.125: In Progress + +* Fully deprecated OpenSeadragon.createFromDZI, safely deprecated Viewer.openTileSource and + Viewer.openDZI to use Viewer.open internally. (#53 & #54). +* Full page bug fix for when viewer is child of document body (#43). +* Overlays for DZI bug fix (#45). + +0.9.124: + +* Performance enhancements. + + +0.9.123: + +* Real fullscreen support. + + +0.9.122: + +* Performance enhancements. + + +0.9.121: + +* Touch pan now works on Android. +* Pinch zoom is better on all devices. diff --git a/package.json b/package.json index 49bcd9af..fe3dff04 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "OpenSeadragon", - "version": "0.9.123", + "version": "0.9.124", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "devDependencies": { "grunt": "~0.4.0", @@ -8,7 +8,7 @@ "grunt-contrib-concat": "~0.1.2", "grunt-contrib-jshint": "~0.1.1", "grunt-contrib-uglify": "~0.1.1", - "grunt-contrib-qunit": "~0.1.1", + "grunt-contrib-qunit": "~0.2.0", "grunt-contrib-connect": "~0.1.2", "grunt-contrib-watch": "~0.2.0", "grunt-contrib-clean": "~0.4.0", diff --git a/src/dzitilesource.js b/src/dzitilesource.js index 7e37caa7..2a8150c4 100644 --- a/src/dzitilesource.js +++ b/src/dzitilesource.js @@ -108,19 +108,8 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, { options = configureFromObject( this, data ); } - if( url && !options.tilesUrl ){ - if( 'http' !== url.substring( 0, 4 ) ){ - host = location.protocol + '//' + location.host; - } - dziPath = url.split('/'); - dziName = dziPath.pop(); - dziName = dziName.substring(0, dziName.lastIndexOf('.')); - dziPath = '/' + dziPath.join('/') + '/' + dziName + '_files/'; - tilesUrl = dziPath; - if( host ){ - tilesUrl = host + tilesUrl; - } - options.tilesUrl = tilesUrl; + if (url && !options.tilesUrl) { + options.tilesUrl = url.replace(/([^\/]+)\.dzi$/, '$1_files/'); } return options; @@ -216,6 +205,7 @@ function configureFromXML( tileSource, xmlDoc ){ configuration = { Image: { xmlns: "http://schemas.microsoft.com/deepzoom/2008", + Url: root.getAttribute( "Url" ), Format: root.getAttribute( "Format" ), DisplayRect: null, Overlap: parseInt( root.getAttribute( "Overlap" ), 10 ), @@ -315,8 +305,7 @@ function configureFromObject( tileSource, configuration ){ )); } - - return { + return $.extend(true, { width: width, /* width *required */ height: height, /* height *required */ tileSize: tileSize, /* tileSize *required */ @@ -326,7 +315,7 @@ function configureFromObject( tileSource, configuration ){ tilesUrl: tilesUrl, /* tilesUrl */ fileFormat: fileFormat, /* fileFormat */ displayRects: displayRects /* displayRects */ - }; + }, configuration ); } diff --git a/src/navigator.js b/src/navigator.js index d4540231..348b1a11 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -17,7 +17,8 @@ $.Navigator = function( options ){ var _this = this, viewer = options.viewer, - viewerSize = $.getElementSize( viewer.element ); + viewerSize = $.getElementSize( viewer.element), + unneededElement; //We may need to create a new element and id if they did not //provide the id for the existing element @@ -148,6 +149,10 @@ $.Navigator = function( options ){ $.Viewer.apply( this, [ options ] ); this.element.getElementsByTagName('form')[0].appendChild( this.displayRegion ); + unneededElement = this.element.getElementsByTagName('textarea')[0]; + if (unneededElement) { + unneededElement.parentNode.removeChild(unneededElement); + } }; @@ -194,7 +199,7 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { containerSize.y ) / source.tileSize; } else { - this.minPixelRatio = thie.viewer.minPixelRatio; + this.minPixelRatio = this.viewer.minPixelRatio; } return $.Viewer.prototype.open.apply( this, [ source ] ); } diff --git a/src/openseadragon.js b/src/openseadragon.js index 7f43faf9..b6975284 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -1385,108 +1385,16 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ /** - * Loads a Deep Zoom Image description from a url, XML string or JSON string - * and provides a callback hook for the resulting Document + * Fully deprecated. Will throw an error. * @function * @name OpenSeadragon.createFromDZI * @param {String} xmlUrl * @param {String} xmlString * @param {Function} callback - * @deprecated + * @deprecated - use OpenSeadragon.Viewer.prototype.open */ createFromDZI: function( dzi, callback, tileHost ) { - var async = typeof ( callback ) == "function", - dziUrl = ( - dzi.substring(0,1) != '<' && - dzi.substring(0,1) != '{' - ) ? dzi : null, - dziString = dziUrl ? null : dzi, - error = null, - urlParts, - filename, - lastDot, - tilesUrl, - callbackName; - - - if( tileHost ){ - - tilesUrl = tileHost + "/_files/"; - - } else if( dziUrl ) { - - urlParts = dziUrl.split( '/' ); - filename = urlParts[ urlParts.length - 1 ]; - if( filename.match(/_dzi\.js$/) ){ - //for jsonp dzi specification, the '_dzi' needs to be removed - //from the filename to be consistent with the spec - filename = filename.replace('_dzi.js', '.js'); - } - - lastDot = filename.lastIndexOf( '.' ); - - if ( lastDot > -1 ) { - urlParts[ urlParts.length - 1 ] = filename.slice( 0, lastDot ); - } - - - tilesUrl = urlParts.join( '/' ) + "_files/"; - - } - - function finish( func, obj ) { - try { - return func( obj, tilesUrl ); - } catch ( e ) { - if ( async ) { - return null; - } else { - throw e; - } - } - } - - if ( async ) { - if ( dziString ) { - window.setTimeout( function() { - var source = finish( processDZIXml, $.parseXml( xmlString ) ); - // call after finish sets error - callback( source, error ); - }, 1); - } else { - if( dziUrl.match(/_dzi\.js$/) ){ - callbackName = dziUrl.split( '/' ).pop().replace('.js',''); - $.jsonp({ - url: dziUrl, - callbackName: callbackName, - callback: function( imageData ){ - var source = finish( processDZIJSON, imageData.Image ); - callback( source ); - } - }); - } else { - $.makeAjaxRequest( dziUrl, function( xhr ) { - var source = finish( processDZIResponse, xhr ); - // call after finish sets error - callback( source, error ); - }); - } - } - - return null; - } - - if ( dziString ) { - return finish( - processDZIXml, - $.parseXml( dziString ) - ); - } else { - return finish( - processDZIResponse, - $.makeAjaxRequest( dziUrl ) - ); - } + throw "OpenSeadragon.createFromDZI is deprecated, use Viewer.open."; }, /** diff --git a/src/tilesource.js b/src/tilesource.js index 11cc499a..650a9477 100644 --- a/src/tilesource.js +++ b/src/tilesource.js @@ -281,9 +281,7 @@ $.TileSource.prototype = { callback: callback }); } else { - //TODO: struggling a little with TileSource rewrite to make info - // requests work asyncronously. For now I'm opting to make - // all xhr info request syncronous. + // request info via xhr asyncronously. $.makeAjaxRequest( url, function( xhr ) { var data = processResponse( xhr ); callback( data ); @@ -415,7 +413,7 @@ function processResponse( xhr ){ * @eprivate * @inner * @function - * @param {Object|Array} data - the tile source configuration object + * @param {Object|Array|Document} data - the tile source configuration object * @param {String} url - the url where the tile source configuration object was * loaded from, if any. */ diff --git a/src/viewer.js b/src/viewer.js index cb43d420..3070304c 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -129,6 +129,7 @@ $.Viewer = function( options ) { // did we decide this viewer has a sequence of tile sources "sequenced": false, "sequence": 0, + "fullPage": false, "onfullscreenchange": null }; @@ -163,12 +164,12 @@ $.Viewer = function( options ) { initialTileSource = this.tileSources; } - this.openTileSource( initialTileSource ); + this.open( initialTileSource ); } - this.element = this.element || document.getElementById( this.id ); - this.canvas = $.makeNeutralElement( "div" ); - this.textAreaToReceiveKeyboardCommands = $.makeNeutralElement( "textarea" ); + this.element = this.element || document.getElementById( this.id ); + this.canvas = $.makeNeutralElement( "div" ); + this.keyboardCommandArea = $.makeNeutralElement( "textarea" ); this.canvas.className = "openseadragon-canvas"; (function( style ){ @@ -180,17 +181,6 @@ $.Viewer = function( options ) { style.left = "0px"; }( this.canvas.style )); - this.textAreaToReceiveKeyboardCommands.className = "findmetoo"; - (function( style ){ - style.width = "100%"; - style.height = "100%"; - style.overflow = "hidden"; - style.position = "absolute"; - style.top = "0px"; - style.left = "0px"; - }( this.textAreaToReceiveKeyboardCommands.style )); - - //the container is created through applying the ControlDock constructor above this.container.className = "openseadragon-container"; (function( style ){ @@ -203,8 +193,18 @@ $.Viewer = function( options ) { style.textAlign = "left"; // needed to protect against }( this.container.style )); + this.keyboardCommandArea.className = "keyboard-command-area"; + (function( style ){ + style.width = "100%"; + style.height = "100%"; + style.overflow = "hidden"; + style.position = "absolute"; + style.top = "0px"; + style.left = "0px"; + }( this.keyboardCommandArea.style )); + this.container.insertBefore( this.canvas, this.container.firstChild ); - this.container.insertBefore( this.textAreaToReceiveKeyboardCommands, this.container.firstChild ); + this.container.insertBefore( this.keyboardCommandArea, this.container.firstChild ); this.element.appendChild( this.container ); //Used for toggling between fullscreen and default container size @@ -215,62 +215,63 @@ $.Viewer = function( options ) { this.bodyOverflow = document.body.style.overflow; this.docOverflow = document.documentElement.style.overflow; - this.textAreaToReceiveKeyboardCommands.innerTracker = new $.MouseTracker({ - _this : this, - element: this.textAreaToReceiveKeyboardCommands, - focusHandler: function(){ - var point = $.getElementPosition( this.element ); - window.scrollTo( 0, point.y ); - }, + this.keyboardCommandArea.innerTracker = new $.MouseTracker({ + _this : this, + element: this.keyboardCommandArea, + focusHandler: function(){ + var point = $.getElementPosition( this.element ); + window.scrollTo( 0, point.y ); + }, - keyHandler: function(tracker, keyCode, shiftKey){ - switch( keyCode ){ - case 61://=|+ - _this.viewport.zoomBy(1.1); - _this.viewport.applyConstraints(); - return false; - case 45://-|_ - _this.viewport.zoomBy(0.9); - _this.viewport.applyConstraints(); - return false; - case 48://0|) - _this.viewport.goHome(); - _this.viewport.applyConstraints(); - return false; - case 119://w - case 87://W - case 38://up arrow - if (shiftKey) + keyHandler: function(tracker, keyCode, shiftKey){ + switch( keyCode ){ + case 61://=|+ _this.viewport.zoomBy(1.1); - else - _this.viewport.panBy(new $.Point(0, -0.05)); - _this.viewport.applyConstraints(); - return false; - case 115://s - case 83://S - case 40://down arrow - if (shiftKey) + _this.viewport.applyConstraints(); + return false; + case 45://-|_ _this.viewport.zoomBy(0.9); - else - _this.viewport.panBy(new $.Point(0, 0.05)); - _this.viewport.applyConstraints(); - return false; - case 97://a - case 37://left arrow - _this.viewport.panBy(new $.Point(-0.05, 0)); - _this.viewport.applyConstraints(); - return false; - case 100://d - case 39://right arrow - _this.viewport.panBy(new $.Point(0.05, 0)); - _this.viewport.applyConstraints(); - return false; - default: - //console.log( 'navigator keycode %s', keyCode ); - return true; + _this.viewport.applyConstraints(); + return false; + case 48://0|) + _this.viewport.goHome(); + _this.viewport.applyConstraints(); + return false; + case 119://w + case 87://W + case 38://up arrow + if (shiftKey) + _this.viewport.zoomBy(1.1); + else + _this.viewport.panBy(new $.Point(0, -0.05)); + _this.viewport.applyConstraints(); + return false; + case 115://s + case 83://S + case 40://down arrow + if (shiftKey) + _this.viewport.zoomBy(0.9); + else + _this.viewport.panBy(new $.Point(0, 0.05)); + _this.viewport.applyConstraints(); + return false; + case 97://a + case 37://left arrow + _this.viewport.panBy(new $.Point(-0.05, 0)); + _this.viewport.applyConstraints(); + return false; + case 100://d + case 39://right arrow + _this.viewport.panBy(new $.Point(0.05, 0)); + _this.viewport.applyConstraints(); + return false; + default: + //console.log( 'navigator keycode %s', keyCode ); + return true; + } } - } - }).setTracking( true ); // default state + }).setTracking( true ); // default state + this.innerTracker = new $.MouseTracker({ element: this.canvas, @@ -324,29 +325,36 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, }, /** - * If the string is xml is simply parsed and opened, otherwise the string - * is treated as an URL and an xml document is requested via ajax, parsed - * and then opened in the viewer. + * A deprecated function, renamed to 'open' to match event name and + * match current 'close' method. * @function * @name OpenSeadragon.Viewer.prototype.openDzi - * @param {String} dzi and xml string or the url to a DZI xml document. + * @param {String} dzi xml string or the url to a DZI xml document. * @return {OpenSeadragon.Viewer} Chainable. * * @deprecated - use 'open' instead. */ openDzi: function ( dzi ) { - var _this = this; - $.createFromDZI( - dzi, - function( source ){ - _this.open( source ); - }, - this.tileHost - ); - return this; + return this.open( dzi ); }, /** + * A deprecated function, renamed to 'open' to match event name and + * match current 'close' method. + * @function + * @name OpenSeadragon.Viewer.prototype.openTileSource + * @param {String|Object|Function} See OpenSeadragon.Viewer.prototype.open + * @return {OpenSeadragon.Viewer} Chainable. + * + * @deprecated - use 'open' instead. + */ + openTileSource: function ( tileSource ) { + return this.open( tileSource ); + }, + + /** + * Open a TileSource object into the viewer. + * * tileSources is a complex option... * * It can be a string, object, function, or an array of any of these: @@ -361,227 +369,56 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, * named 'getTileUrl', it is treated as a custom TileSource. * @function * @name OpenSeadragon.Viewer.prototype.openTileSource + * @param {String|Object|Function} * @return {OpenSeadragon.Viewer} Chainable. */ - openTileSource: function ( tileSource ) { + open: function ( tileSource ) { var _this = this, customTileSource, readySource, $TileSource, options; + //allow plain xml strings or json strings to be parsed here + if( $.type( tileSource ) == 'string' ){ + if( tileSource.match(/\s*<.*/) ){ + tileSource = $.parseXml( tileSource ); + }else if( tileSource.match(/\s*[\{\[].*/) ){ + /*jshint evil:true*/ + tileSource = eval( '('+tileSource+')' ); + } + } + setTimeout(function(){ if ( $.type( tileSource ) == 'string') { - //TODO: We cant assume a string implies a dzi since all - //complete TileSource implementations should have a getInfo - //which allows them to be configured via AJAX. Im not sure - //if its better to use file extension or url pattern, or to - //inspect the resulting info object. + //If its still a string it means it must be a url at this point tileSource = new $.TileSource( tileSource, function( readySource ){ - _this.open( readySource ); + openTileSource( _this, readySource ); }); - } else if ( $.isPlainObject( tileSource ) ){ + } else if ( $.isPlainObject( tileSource ) || tileSource.nodeType ){ if( $.isFunction( tileSource.getTileUrl ) ){ //Custom tile source customTileSource = new $.TileSource(tileSource); customTileSource.getTileUrl = tileSource.getTileUrl; - _this.open( customTileSource ); + openTileSource( _this, customTileSource ); } else { //inline configuration $TileSource = $.TileSource.determineType( _this, tileSource ); options = $TileSource.prototype.configure.apply( _this, [ tileSource ]); readySource = new $TileSource( options ); - _this.open( readySource ); + openTileSource( _this, readySource ); } } else { //can assume it's already a tile source implementation - _this.open( tileSource ); + openTileSource( _this, tileSource ); } }, 1); return this; }, - /** - * @function - * @name OpenSeadragon.Viewer.prototype.open - * @return {OpenSeadragon.Viewer} Chainable. - */ - open: function( source ) { - var _this = this, - overlay, - i; - - if ( this.source ) { - this.close( ); - } - - // to ignore earlier opens - THIS[ this.hash ].lastOpenStartTime = +new Date(); - - window.setTimeout( function () { - if ( THIS[ _this.hash ].lastOpenStartTime > THIS[ _this.hash ].lastOpenEndTime ) { - THIS[ _this.hash ].setMessage( $.getString( "Messages.Loading" ) ); - } - }, 2000); - - THIS[ this.hash ].lastOpenEndTime = +new Date(); - this.canvas.innerHTML = ""; - THIS[ this.hash ].prevContainerSize = $.getElementSize( this.container ); - - - if( this.collectionMode ){ - this.source = new $.TileSourceCollection({ - rows: this.collectionRows, - layout: this.collectionLayout, - tileSize: this.collectionTileSize, - tileSources: this.tileSources, - tileMargin: this.collectionTileMargin - }); - this.viewport = this.viewport ? this.viewport : new $.Viewport({ - collectionMode: true, - collectionTileSource: this.source, - containerSize: THIS[ this.hash ].prevContainerSize, - contentSize: this.source.dimensions, - springStiffness: this.springStiffness, - animationTime: this.animationTime, - showNavigator: false, - minZoomImageRatio: 1, - maxZoomPixelRatio: 1, - viewer: this //, - //TODO: figure out how to support these in a way that makes sense - //minZoomLevel: this.minZoomLevel, - //maxZoomLevel: this.maxZoomLevel - }); - }else{ - if( source ){ - this.source = source; - } - this.viewport = this.viewport ? this.viewport : new $.Viewport({ - containerSize: THIS[ this.hash ].prevContainerSize, - contentSize: this.source.dimensions, - springStiffness: this.springStiffness, - animationTime: this.animationTime, - minZoomImageRatio: this.minZoomImageRatio, - maxZoomPixelRatio: this.maxZoomPixelRatio, - visibilityRatio: this.visibilityRatio, - wrapHorizontal: this.wrapHorizontal, - wrapVertical: this.wrapVertical, - defaultZoomLevel: this.defaultZoomLevel, - minZoomLevel: this.minZoomLevel, - maxZoomLevel: this.maxZoomLevel, - viewer: this - }); - } - - if( this.preserveVewport ){ - this.viewport.resetContentSize( this.source.dimensions ); - } - - this.source.overlays = this.source.overlays || []; - - this.drawer = new $.Drawer({ - source: this.source, - viewport: this.viewport, - element: this.canvas, - overlays: [].concat( this.overlays ).concat( this.source.overlays ), - maxImageCacheCount: this.maxImageCacheCount, - imageLoaderLimit: this.imageLoaderLimit, - minZoomImageRatio: this.minZoomImageRatio, - wrapHorizontal: this.wrapHorizontal, - wrapVertical: this.wrapVertical, - immediateRender: this.immediateRender, - blendTime: this.blendTime, - alwaysBlend: this.alwaysBlend, - minPixelRatio: this.collectionMode ? 0 : this.minPixelRatio, - timeout: this.timeout, - debugMode: this.debugMode, - debugGridColor: this.debugGridColor - }); - - //Instantiate a navigator if configured - if ( this.showNavigator && ! this.navigator && !this.collectionMode ){ - this.navigator = new $.Navigator({ - id: this.navigatorId, - position: this.navigatorPosition, - sizeRatio: this.navigatorSizeRatio, - height: this.navigatorHeight, - width: this.navigatorWidth, - tileSources: this.tileSources, - tileHost: this.tileHost, - prefixUrl: this.prefixUrl, - overlays: this.overlays, - viewer: this - }); - } - - //Instantiate a referencestrip if configured - if ( this.showReferenceStrip && ! this.referenceStrip ){ - this.referenceStrip = new $.ReferenceStrip({ - id: this.referenceStripElement, - position: this.referenceStripPosition, - sizeRatio: this.referenceStripSizeRatio, - scroll: this.referenceStripScroll, - height: this.referenceStripHeight, - width: this.referenceStripWidth, - tileSources: this.tileSources, - tileHost: this.tileHost, - prefixUrl: this.prefixUrl, - overlays: this.overlays, - viewer: this - }); - } - - //this.profiler = new $.Profiler(); - - THIS[ this.hash ].animating = false; - THIS[ this.hash ].forceRedraw = true; - 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; - - if( this.navigator ){ - this.navigator.open( source ); - } - - this.raiseEvent( 'open', { source: source, viewer: this } ); - - return this; - }, - + /** * @function * @name OpenSeadragon.Viewer.prototype.close @@ -667,7 +504,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, * @return {Boolean} */ isFullPage: function () { - return this.element.parentNode == document.body; + return THIS[ this.hash ].fullPage; }, @@ -724,7 +561,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, //the bodies elements and replace them when we leave full screen. this.previousBody = []; THIS[ this.hash ].prevElementParent = this.element.parentNode; - THIS[ this.hash ].prevNextSibling = this.element.prevNextSibling; + THIS[ this.hash ].prevNextSibling = this.element.nextSibling; THIS[ this.hash ].prevElementSize = $.getElementSize( this.element ); nodes = body.childNodes.length; for ( i = 0; i < nodes; i ++ ){ @@ -785,6 +622,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, ) + 'px'; } + THIS[ this.hash ].fullPage = true; + // mouse will be inside container now $.delegate( this, onContainerEnter )(); @@ -846,6 +685,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, this.element.style.height = THIS[ this.hash ].prevElementSize.y + 'px'; this.element.style.width = THIS[ this.hash ].prevElementSize.x + 'px'; + THIS[ this.hash ].fullPage = false; + // mouse will likely be outside now $.delegate( this, onContainerExit )(); @@ -1155,7 +996,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, } } - this.openTileSource( this.tileSources[ page ] ); + this.open( this.tileSources[ page ] ); } if( $.isFunction( this.onPageChange ) ){ @@ -1172,6 +1013,185 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, }); +/** + * @function + * @private + */ +function openTileSource( viewer, source ) { + var _this = viewer, + overlay, + i; + + if ( _this.source ) { + _this.close( ); + } + + // to ignore earlier opens + THIS[ _this.hash ].lastOpenStartTime = +new Date(); + + window.setTimeout( function () { + if ( THIS[ _this.hash ].lastOpenStartTime > THIS[ _this.hash ].lastOpenEndTime ) { + THIS[ _this.hash ].setMessage( $.getString( "Messages.Loading" ) ); + } + }, 2000); + + THIS[ _this.hash ].lastOpenEndTime = +new Date(); + _this.canvas.innerHTML = ""; + THIS[ _this.hash ].prevContainerSize = $.getElementSize( _this.container ); + + + if( _this.collectionMode ){ + _this.source = new $.TileSourceCollection({ + rows: _this.collectionRows, + layout: _this.collectionLayout, + tileSize: _this.collectionTileSize, + tileSources: _this.tileSources, + tileMargin: _this.collectionTileMargin + }); + _this.viewport = _this.viewport ? _this.viewport : new $.Viewport({ + collectionMode: true, + collectionTileSource: _this.source, + containerSize: THIS[ _this.hash ].prevContainerSize, + contentSize: _this.source.dimensions, + springStiffness: _this.springStiffness, + animationTime: _this.animationTime, + showNavigator: false, + minZoomImageRatio: 1, + maxZoomPixelRatio: 1, + viewer: _this //, + //TODO: figure out how to support these in a way that makes sense + //minZoomLevel: this.minZoomLevel, + //maxZoomLevel: this.maxZoomLevel + }); + }else{ + if( source ){ + _this.source = source; + } + _this.viewport = _this.viewport ? _this.viewport : new $.Viewport({ + containerSize: THIS[ _this.hash ].prevContainerSize, + contentSize: _this.source.dimensions, + springStiffness: _this.springStiffness, + animationTime: _this.animationTime, + minZoomImageRatio: _this.minZoomImageRatio, + maxZoomPixelRatio: _this.maxZoomPixelRatio, + visibilityRatio: _this.visibilityRatio, + wrapHorizontal: _this.wrapHorizontal, + wrapVertical: _this.wrapVertical, + defaultZoomLevel: _this.defaultZoomLevel, + minZoomLevel: _this.minZoomLevel, + maxZoomLevel: _this.maxZoomLevel, + viewer: _this + }); + } + + if( _this.preserveVewport ){ + _this.viewport.resetContentSize( _this.source.dimensions ); + } + + _this.source.overlays = _this.source.overlays || []; + + _this.drawer = new $.Drawer({ + source: _this.source, + viewport: _this.viewport, + element: _this.canvas, + overlays: [].concat( _this.overlays ).concat( _this.source.overlays ), + maxImageCacheCount: _this.maxImageCacheCount, + imageLoaderLimit: _this.imageLoaderLimit, + minZoomImageRatio: _this.minZoomImageRatio, + wrapHorizontal: _this.wrapHorizontal, + wrapVertical: _this.wrapVertical, + immediateRender: _this.immediateRender, + blendTime: _this.blendTime, + alwaysBlend: _this.alwaysBlend, + minPixelRatio: _this.collectionMode ? 0 : _this.minPixelRatio, + timeout: _this.timeout, + debugMode: _this.debugMode, + debugGridColor: _this.debugGridColor + }); + + //Instantiate a navigator if configured + if ( _this.showNavigator && ! _this.navigator && !_this.collectionMode ){ + _this.navigator = new $.Navigator({ + id: _this.navigatorId, + position: _this.navigatorPosition, + sizeRatio: _this.navigatorSizeRatio, + height: _this.navigatorHeight, + width: _this.navigatorWidth, + tileSources: _this.tileSources, + tileHost: _this.tileHost, + prefixUrl: _this.prefixUrl, + overlays: _this.overlays, + viewer: _this + }); + } + + //Instantiate a referencestrip if configured + if ( _this.showReferenceStrip && !_this.referenceStrip ){ + _this.referenceStrip = new $.ReferenceStrip({ + id: _this.referenceStripElement, + position: _this.referenceStripPosition, + sizeRatio: _this.referenceStripSizeRatio, + scroll: _this.referenceStripScroll, + height: _this.referenceStripHeight, + width: _this.referenceStripWidth, + tileSources: _this.tileSources, + tileHost: _this.tileHost, + prefixUrl: _this.prefixUrl, + overlays: _this.overlays, + viewer: _this + }); + } + + //this.profiler = new $.Profiler(); + + THIS[ _this.hash ].animating = false; + THIS[ _this.hash ].forceRedraw = true; + 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; + + if( _this.navigator ){ + _this.navigator.open( source ); + } + + _this.raiseEvent( 'open', { source: source, viewer: _this } ); + + return _this; +} + + /////////////////////////////////////////////////////////////////////////////// @@ -1275,7 +1295,7 @@ function onBlur(){ } -function onCanvasClick( tracker, position, quick, shift, event ) { +function onCanvasClick( tracker, position, quick, shift ) { var zoomPreClick, factor; if ( this.viewport && quick ) { // ignore clicks where mouse moved @@ -1287,16 +1307,15 @@ function onCanvasClick( tracker, position, quick, shift, event ) { ); this.viewport.applyConstraints(); } - this.raiseEvent( 'click', { + this.raiseEvent( 'canvas-click', { tracker: tracker, position: position, quick: quick, - shift: shift, - originalEvent: event + shift: shift }); } -function onCanvasDrag( tracker, position, delta, shift, event ) { +function onCanvasDrag( tracker, position, delta, shift ) { if ( this.viewport ) { if( !this.panHorizontal ){ delta.x = 0; @@ -1313,29 +1332,27 @@ function onCanvasDrag( tracker, position, delta, shift, event ) { this.viewport.applyConstraints(); } } - this.raiseEvent( 'drag', { + this.raiseEvent( 'canvas-click', { tracker: tracker, position: position, delta: delta, - shift: shift, - originalEvent: event + shift: shift }); } -function onCanvasRelease( tracker, position, insideElementPress, insideElementRelease, event ) { +function onCanvasRelease( tracker, position, insideElementPress, insideElementRelease ) { if ( insideElementPress && this.viewport ) { this.viewport.applyConstraints(); } - this.raiseEvent( 'release', { + this.raiseEvent( 'canvas-release', { tracker: tracker, position: position, insideElementPress: insideElementPress, - insideElementRelease: insideElementRelease, - originalEvent: event + insideElementRelease: insideElementRelease }); } -function onCanvasScroll( tracker, position, scroll, shift, event ) { +function onCanvasScroll( tracker, position, scroll, shift ) { var factor; if ( this.viewport ) { factor = Math.pow( this.zoomPerScroll, scroll ); @@ -1345,58 +1362,54 @@ function onCanvasScroll( tracker, position, scroll, shift, event ) { ); this.viewport.applyConstraints(); } - this.raiseEvent( 'scroll', { + this.raiseEvent( 'canvas-scroll', { tracker: tracker, position: position, scroll: scroll, - shift: shift, - originalEvent: event + shift: shift }); //cancels event return false; } -function onContainerExit( tracker, position, buttonDownElement, buttonDownAny, event ) { +function onContainerExit( tracker, position, buttonDownElement, buttonDownAny ) { if ( !buttonDownElement ) { THIS[ this.hash ].mouseInside = false; if ( !THIS[ this.hash ].animating ) { beginControlsAutoHide( this ); } } - this.raiseEvent( 'exit', { + this.raiseEvent( 'container-exit', { tracker: tracker, position: position, buttonDownElement: buttonDownElement, - buttonDownAny: buttonDownAny, - originalEvent: event + buttonDownAny: buttonDownAny }); } -function onContainerRelease( tracker, position, insideElementPress, insideElementRelease, event ) { +function onContainerRelease( tracker, position, insideElementPress, insideElementRelease ) { if ( !insideElementRelease ) { THIS[ this.hash ].mouseInside = false; if ( !THIS[ this.hash ].animating ) { beginControlsAutoHide( this ); } } - this.raiseEvent( 'release', { + this.raiseEvent( 'container-release', { tracker: tracker, position: position, insideElementPress: insideElementPress, - insideElementRelease: insideElementRelease, - originalEvent: event + insideElementRelease: insideElementRelease }); } -function onContainerEnter( tracker, position, buttonDownElement, buttonDownAny, event ) { +function onContainerEnter( tracker, position, buttonDownElement, buttonDownAny ) { THIS[ this.hash ].mouseInside = true; abortControlsAutoHide( this ); - this.raiseEvent( 'enter', { + this.raiseEvent( 'container-enter', { tracker: tracker, position: position, buttonDownElement: buttonDownElement, - buttonDownAny: buttonDownAny, - originalEvent: event + buttonDownAny: buttonDownAny }); } @@ -1589,4 +1602,4 @@ function onNext(){ } -}( OpenSeadragon )); +}( OpenSeadragon )); \ No newline at end of file diff --git a/test/navigator.js b/test/navigator.js index 8e5fa262..e876950a 100644 --- a/test/navigator.js +++ b/test/navigator.js @@ -14,7 +14,7 @@ QUnit.config.autostart = false; module("navigator", { setup:function () { - resetDom(); + Util.resetDom(); resetTestVariables(); } }); @@ -38,32 +38,11 @@ QUnit.config.autostart = false; displayRegionHeight = null; }; - var resetDom = function () { - if ($('#exampleNavigator').is(':ui-dialog')) { - $('#exampleNavigator').dialog('destroy'); - } - $("#exampleNavigator").remove(); - $(".navigator").remove(); - $("#example").empty(); - $("#tallexample").empty(); - $("#wideexample").empty(); - $("#example").parent().append('
'); - }; - - var equalsWithVariance = function (value1, value2, variance) { - return Math.abs(value1 - value2) <= variance; - }; - - - var assessNumericValue = function (value1, value2, variance, message) { - ok(equalsWithVariance(value1, value2, variance), message + " Expected:" + value1 + " Found: " + value2 + " Variance: " + variance); - }; - var assessNavigatorLocation = function (expectedX, expectedY) { var navigator = $(".navigator"); - assessNumericValue(expectedX, navigator.offset().left, 4, ' Navigator x position'); - assessNumericValue(expectedY, navigator.offset().top, 4, ' Navigator y position'); + Util.assessNumericValue(expectedX, navigator.offset().left, 4, ' Navigator x position'); + Util.assessNumericValue(expectedY, navigator.offset().top, 4, ' Navigator y position'); }; var navigatorRegionBoundsInPoints = function () { @@ -113,10 +92,10 @@ QUnit.config.autostart = false; var assessDisplayRegion = function (status) { var expectedBounds = navigatorRegionBoundsInPoints(); - assessNumericValue(expectedBounds.width, displayRegion.width() + viewer.navigator.totalBorderWidths.x, 2, status + ' Width synchronization'); - assessNumericValue(expectedBounds.height, displayRegion.height() + viewer.navigator.totalBorderWidths.y, 2, status + ' Height synchronization'); - assessNumericValue(expectedBounds.x, displayRegion.position().left, 2, status + ' Left synchronization'); - assessNumericValue(expectedBounds.y, displayRegion.position().top, 2, status + ' Top synchronization'); + 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'); + Util.assessNumericValue(expectedBounds.x, displayRegion.position().left, 2, status + ' Left synchronization'); + Util.assessNumericValue(expectedBounds.y, displayRegion.position().top, 2, status + ' Top synchronization'); }; var waitForViewer = function () { @@ -138,11 +117,11 @@ QUnit.config.autostart = false; viewerAndNavigatorDisplayReady = viewer.drawer !== null && !viewer.drawer.needsUpdate() && currentDisplayWidth > 0 && - equalsWithVariance(lastDisplayRegionLeft, currentDisplayRegionLeft, .0001) && - equalsWithVariance(lastDisplayWidth, currentDisplayWidth, .0001) && - equalsWithVariance(viewer.viewport.getBounds(true).x, viewer.viewport.getBounds().x, .0001) && - equalsWithVariance(viewer.viewport.getBounds(true).y, viewer.viewport.getBounds().y, .0001) && - equalsWithVariance(viewer.viewport.getBounds(true).width, viewer.viewport.getBounds().width, .0001); + Util.equalsWithVariance(lastDisplayRegionLeft, currentDisplayRegionLeft, .0001) && + Util.equalsWithVariance(lastDisplayWidth, currentDisplayWidth, .0001) && + Util.equalsWithVariance(viewer.viewport.getBounds(true).x, viewer.viewport.getBounds().x, .0001) && + Util.equalsWithVariance(viewer.viewport.getBounds(true).y, viewer.viewport.getBounds().y, .0001) && + Util.equalsWithVariance(viewer.viewport.getBounds(true).width, viewer.viewport.getBounds().width, .0001); } catch (err) { //Ignore. Subsequent code will try again shortly @@ -213,10 +192,10 @@ QUnit.config.autostart = false; expecteYCoordinate = 1 / viewer.source.aspectRatio - viewer.viewport.getBounds().height; } if (viewer.viewport.getBounds().width < 1) { - assessNumericValue(expectedXCoordinate, viewer.viewport.getBounds().x, .04, ' Viewer at ' + theContentCorner + ', x coord'); + Util.assessNumericValue(expectedXCoordinate, viewer.viewport.getBounds().x, .04, ' Viewer at ' + theContentCorner + ', x coord'); } if (viewer.viewport.getBounds().height < 1 / viewer.source.aspectRatio) { - assessNumericValue(expecteYCoordinate, viewer.viewport.getBounds().y, .04, ' Viewer at ' + theContentCorner + ', y coord'); + Util.assessNumericValue(expecteYCoordinate, viewer.viewport.getBounds().y, .04, ' Viewer at ' + theContentCorner + ', y coord'); } } }; @@ -226,8 +205,8 @@ QUnit.config.autostart = false; if (viewer.source.aspectRatio < 1) { yPositionVariance = yPositionVariance / viewer.source.aspectRatio; } - assessNumericValue(1 / viewer.source.aspectRatio / 2, viewer.viewport.getCenter().y, yPositionVariance, ' Viewer at center, y coord'); - assessNumericValue(.5, viewer.viewport.getCenter().x, .4, ' Viewer at center, x coord'); + Util.assessNumericValue(1 / viewer.source.aspectRatio / 2, viewer.viewport.getCenter().y, yPositionVariance, ' Viewer at center, y coord'); + Util.assessNumericValue(.5, viewer.viewport.getCenter().x, .4, ' Viewer at center, x coord'); }; var clickOnNavigator = function (theContentCorner) { diff --git a/test/util.js b/test/util.js index af88f36a..e5fb8894 100644 --- a/test/util.js +++ b/test/util.js @@ -28,8 +28,30 @@ .simulate('mouseover', event) .simulate('mousedown', event) .simulate('mouseup', event); + }, + + resetDom: function () { + if ($('#exampleNavigator').is(':ui-dialog')) { + $('#exampleNavigator').dialog('destroy'); + } + $("#exampleNavigator").remove(); + $(".navigator").remove(); + $("#example").empty(); + $("#tallexample").empty(); + $("#wideexample").empty(); + $("#example").parent().append(''); + }, + + equalsWithVariance: function (value1, value2, variance) { + return Math.abs(value1 - value2) <= variance; + }, + + + assessNumericValue: function (value1, value2, variance, message) { + ok(Util.equalsWithVariance(value1, value2, variance), message + " Expected:" + value1 + " Found: " + value2 + " Variance: " + variance); } + }; })();