diff --git a/build.properties b/build.properties index af817a35..fdaf90fb 100644 --- a/build.properties +++ b/build.properties @@ -1,12 +1,12 @@ # OpenSeadragon build.properties # TODO: how do you auto-increment build_id's with every commit? -# TRY: continuos integration +# TRY: continuous integration # TRY: git-hooks PROJECT: openseadragon BUILD_MAJOR: 0 BUILD_MINOR: 9 -BUILD_ID: 18 +BUILD_ID: 21 BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID} VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID} diff --git a/build.xml b/build.xml index c7ae2277..0ec07da6 100644 --- a/build.xml +++ b/build.xml @@ -24,6 +24,7 @@ + diff --git a/openseadragon.js b/openseadragon.js index 7ec55431..31c3099e 100644 --- a/openseadragon.js +++ b/openseadragon.js @@ -1,5 +1,5 @@ /** - * @version OpenSeadragon 0.9.18 + * @version OpenSeadragon 0.9.21 * * @fileOverview *

@@ -109,17 +109,28 @@ * floated on top of the image the user is viewing. * * @param {Boolean} [options.immediateRender=false] + * Render the best closest level first, ignoring the lowering levels which + * provide the effect of very blurry to sharp. It is recommended to change + * setting to true for mobile devices. * * @param {Boolean} [options.wrapHorizontal=false] - * Should the image wrap horizontally within the viewport. Useful for - * maps or images representing the surface of a sphere or cylinder. + * Set to true to force the image to wrap horizontally within the viewport. + * Useful for maps or images representing the surface of a sphere or cylinder. * * @param {Boolean} [options.wrapVertical=false] - * Should the image wrap vertically within the viewport. Useful for - * maps or images representing the surface of a sphere or cylinder. + * Set to true to force the image to wrap vertically within the viewport. + * Useful for maps or images representing the surface of a sphere or cylinder. * * @param {Number} [options.minZoomImageRatio=0.8] + * The minimum percentage ( expressed as a number between 0 and 1 ) of + * the viewport height or width at which the zoom out will be constrained. + * Setting it to 0, for example will allow you to zoom out infinitly. + * * @param {Number} [options.maxZoomPixelRatio=2] + * The maximum ratio to allow a zoom-in to affect the highest level pixel + * ratio. This can be set to Infinity to allow 'infinite' zooming into the + * image though it is less effective visually if the HTML5 Canvas is not + * availble on the viewing device. * * @param {Number} [options.visibilityRatio=0.5] * The percentage ( as a number from 0 to 1 ) of the source image which @@ -129,14 +140,31 @@ * true will provide the effect of an infinitely scrolling viewport. * * @param {Number} [options.springStiffness=5.0] + * * @param {Number} [options.imageLoaderLimit=0] + * The maximum number of image requests to make concurrently. By default + * it is set to 0 allowing the browser to make the maximum number of + * image requests in parallel as allowed by the browsers policy. + * * @param {Number} [options.clickTimeThreshold=200] + * If multiple mouse clicks occurs within less than this number of + * milliseconds, treat them as a single click. + * * @param {Number} [options.clickDistThreshold=5] + * If a mouse or touch drag occurs and the distance to the starting drag + * point is less than this many pixels, ignore the drag event. + * * @param {Number} [options.zoomPerClick=2.0] + * The "zoom distance" per mouse click or touch tap. + * * @param {Number} [options.zoomPerScroll=1.2] + * The "zoom distance" per mouse scroll or touch pinch. + * * @param {Number} [options.zoomPerSecond=2.0] + * The number of seconds to animate a single zoom event over. * * @param {Boolean} [options.showNavigationControl=true] + * Set to false to prevent the appearance of the default navigation controls. * * @param {Number} [options.controlsFadeDelay=2000] * The number of milliseconds to wait once the user has stopped interacting @@ -421,6 +449,12 @@ OpenSeadragon = window.OpenSeadragon || function( options ){ zoomPerScroll: 1.2, zoomPerSecond: 2.0, showNavigationControl: true, + + showNavigator: false, + navigatorElement: null, + navigatorHeight: null, + navigatorWidth: null, + navigatorPosition: null, //These two were referenced but never defined controlsFadeDelay: 2000, @@ -1076,6 +1110,78 @@ OpenSeadragon = window.OpenSeadragon || function( options ){ }, + /** + * Taken from jQuery 1.6.1 + * @param {Object} options + * @param {String} options.url + * @param {Function} options.callback + * @param {String} [options.param='callback'] The name of the url parameter + * to request the jsonp provider with. + * @param {String} [options.callbackName=] The name of the callback to + * request the jsonp provider with. + */ + jsonp: function( options ){ + var script, + url = options.url, + head = document.head || + document.getElementsByTagName( "head" )[ 0 ] || + document.documentElement, + jsonpCallback = options.callbackName || 'openseadragon' + (+new Date()), + previous = window[ jsonpCallback ], + replace = "$1" + jsonpCallback + "$2", + callbackParam = options.param || 'callback', + callback = options.callback; + + url = url.replace( /(\=)\?(&|$)|\?\?/i, replace ); + // Add callback manually + url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback; + + // Install callback + window[ jsonpCallback ] = function( response ) { + if ( !previous ){ + delete window[ jsonpCallback ]; + } else { + window[ jsonpCallback ] = previous; + } + if( callback && $.isFunction( callback ) ){ + callback( response ); + } + }; + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( options.scriptCharset ) { + script.charset = options.scriptCharset; + } + + script.src = url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + + }, + + /** * Loads a Deep Zoom Image description from a url or XML string and * provides a callback hook for the resulting Document @@ -2758,11 +2864,12 @@ $.Viewer = function( options ) { source: null, drawer: null, viewport: null, + navigator: null, profiler: null }, $.DEFAULT_SETTINGS, options ); - this.element = document.getElementById( this.id ); + this.element = this.element || document.getElementById( this.id ); this.container = $.makeNeutralElement( "div" ); this.canvas = $.makeNeutralElement( "div" ); @@ -2792,7 +2899,7 @@ $.Viewer = function( options ) { dragHandler: $.delegate( this, onCanvasDrag ), releaseHandler: $.delegate( this, onCanvasRelease ), scrollHandler: $.delegate( this, onCanvasScroll ) - }).setTracking( true ); // default state + }).setTracking( this.mouseNavEnabled ? true : false ); // default state this.outerTracker = new $.MouseTracker({ element: this.container, @@ -2801,7 +2908,7 @@ $.Viewer = function( options ) { enterHandler: $.delegate( this, onContainerEnter ), exitHandler: $.delegate( this, onContainerExit ), releaseHandler: $.delegate( this, onContainerRelease ) - }).setTracking( true ); // always tracking + }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking (function( canvas ){ canvas.width = "100%"; @@ -2934,6 +3041,23 @@ $.Viewer = function( options ) { this.addControl( this.navControl, $.ControlAnchor.BOTTOM_RIGHT ); } + if ( this.showNavigator ){ + this.navigator = new $.Navigator({ + viewerId: this.id, + id: this.navigatorElement, + position: this.navigatorPosition, + height: this.navigatorHeight, + width: this.navigatorWidth, + tileSources: this.tileSources, + prefixUrl: this.prefixUrl + }); + this.addControl( + this.navigator.element, + $.ControlAnchor.TOP_RIGHT + ); + } + + for ( i = 0; i < this.customControls.length; i++ ) { this.addControl( this.customControls[ i ].id, @@ -3628,9 +3752,15 @@ function updateOnce( viewer ) { if ( animated ) { viewer.drawer.update(); + if( viewer.navigator ){ + viewer.navigator.update( viewer.viewport ); + } viewer.raiseEvent( "animation" ); } else if ( THIS[ viewer.hash ].forceRedraw || viewer.drawer.needsUpdate() ) { viewer.drawer.update(); + if( viewer.navigator ){ + viewer.navigator.update( viewer.viewport ); + } THIS[ viewer.hash ].forceRedraw = false; } @@ -3736,7 +3866,102 @@ function onFullPage() { }; }( OpenSeadragon )); +(function( $ ){ + +/** + * @param {Object} options + * @param {String} options.viewerId + */ +$.Navigator = function( options ){ + var _this = this, + viewer = $.getElement( options.viewerId ), + viewerSize = $.getElementSize( viewer ); + + //We may need to create a new element and id if they did not + //provide the id for the existing element + if( !options.id ){ + options.id = 'navigator-' + (+new Date()); + this.element = $.makeNeutralElement( "div" ); + this.element.id = options.id; + } + + options = $.extend( true, options, { + element: this.element, + //These need to be overridden to prevent recursion since + //the navigator is a viewer and a viewer has a navigator + showNavigator: false, + mouseNavEnabled: false, + showNavigationControl: false + }); + + (function( style ){ + style.marginTop = '0px'; + style.marginRight = '0px'; + style.marginBottom = '0px'; + style.marginLeft = '0px'; + style.border = '2px solid #555'; + style.background = '#000'; + style.opacity = 0.8; + style.overflow = 'hidden'; + }( this.element.style )); + + this.displayRegion = $.makeNeutralElement( "div" ); + this.displayRegion.id = this.element.id + '-displayregion'; + + (function( style ){ + style.position = 'relative'; + style.top = '0px'; + style.left = '0px'; + style.border = '1px solid red'; + style.background = 'transparent'; + style.float = 'left'; + style.zIndex = 999999999; + style.opacity = 0.8; + }( this.displayRegion.style )); + + this.element.appendChild( this.displayRegion ); + + $.Viewer.apply( this, [ options ] ); + + if( options.width ){ + this.element.style.width = options.width + 'px'; + } else { + this.element.style.width = ( viewerSize.x / 4 ) + 'px'; + } + if( options.height ){ + this.element.style.height = options.height + 'px'; + } else { + this.element.style.height = ( viewerSize.y / 4 ) + 'px'; + } + +}; + +$.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { + + update: function( viewport ){ + + + var bounds = viewport.getBounds( true ), + topleft = this.viewport.pixelFromPoint( bounds.getTopLeft() ) + bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight() ); + + //update style for navigator-box + (function(style){ + + style.top = topleft.y + 'px'; + style.left = topleft.x + 'px'; + style.width = ( Math.abs( topleft.x - bottomright.x ) - 2 ) + 'px'; + style.height = ( Math.abs( topleft.y - bottomright.y ) - 2 ) + 'px'; + + }( this.displayRegion.style )); + + } + +}); + + +}( OpenSeadragon )); (function( $ ){ //TODO: I guess this is where the i18n needs to be reimplemented. I'll look @@ -6390,8 +6615,8 @@ $.Viewport.prototype = { height = width / this.getAspectRatio(); return new $.Rect( - center.x - width / 2.0, - center.y - height / 2.0, + center.x - ( width / 2.0 ), + center.y - ( height / 2.0 ), width, height ); diff --git a/src/navigator.js b/src/navigator.js new file mode 100644 index 00000000..e75f0675 --- /dev/null +++ b/src/navigator.js @@ -0,0 +1,96 @@ +(function( $ ){ + +/** + * @param {Object} options + * @param {String} options.viewerId + */ +$.Navigator = function( options ){ + + var _this = this, + viewer = $.getElement( options.viewerId ), + viewerSize = $.getElementSize( viewer ); + + //We may need to create a new element and id if they did not + //provide the id for the existing element + if( !options.id ){ + options.id = 'navigator-' + (+new Date()); + this.element = $.makeNeutralElement( "div" ); + this.element.id = options.id; + } + + options = $.extend( true, options, { + element: this.element, + //These need to be overridden to prevent recursion since + //the navigator is a viewer and a viewer has a navigator + showNavigator: false, + mouseNavEnabled: false, + showNavigationControl: false + }); + + (function( style ){ + style.marginTop = '0px'; + style.marginRight = '0px'; + style.marginBottom = '0px'; + style.marginLeft = '0px'; + style.border = '2px solid #555'; + style.background = '#000'; + style.opacity = 0.8; + style.overflow = 'hidden'; + }( this.element.style )); + + this.displayRegion = $.makeNeutralElement( "div" ); + this.displayRegion.id = this.element.id + '-displayregion'; + + (function( style ){ + style.position = 'relative'; + style.top = '0px'; + style.left = '0px'; + style.border = '1px solid red'; + style.background = 'transparent'; + style.float = 'left'; + style.zIndex = 999999999; + style.opacity = 0.8; + }( this.displayRegion.style )); + + this.element.appendChild( this.displayRegion ); + + $.Viewer.apply( this, [ options ] ); + + if( options.width ){ + this.element.style.width = options.width + 'px'; + } else { + this.element.style.width = ( viewerSize.x / 4 ) + 'px'; + } + if( options.height ){ + this.element.style.height = options.height + 'px'; + } else { + this.element.style.height = ( viewerSize.y / 4 ) + 'px'; + } + +}; + +$.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { + + update: function( viewport ){ + + + var bounds = viewport.getBounds( true ), + topleft = this.viewport.pixelFromPoint( bounds.getTopLeft() ) + bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight() ); + + //update style for navigator-box + (function(style){ + + style.top = topleft.y + 'px'; + style.left = topleft.x + 'px'; + style.width = ( Math.abs( topleft.x - bottomright.x ) - 2 ) + 'px'; + style.height = ( Math.abs( topleft.y - bottomright.y ) - 2 ) + 'px'; + + }( this.displayRegion.style )); + + } + +}); + + +}( OpenSeadragon )); \ No newline at end of file diff --git a/src/openseadragon.js b/src/openseadragon.js index 99c51611..0c8f585c 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -109,17 +109,28 @@ * floated on top of the image the user is viewing. * * @param {Boolean} [options.immediateRender=false] + * Render the best closest level first, ignoring the lowering levels which + * provide the effect of very blurry to sharp. It is recommended to change + * setting to true for mobile devices. * * @param {Boolean} [options.wrapHorizontal=false] - * Should the image wrap horizontally within the viewport. Useful for - * maps or images representing the surface of a sphere or cylinder. + * Set to true to force the image to wrap horizontally within the viewport. + * Useful for maps or images representing the surface of a sphere or cylinder. * * @param {Boolean} [options.wrapVertical=false] - * Should the image wrap vertically within the viewport. Useful for - * maps or images representing the surface of a sphere or cylinder. + * Set to true to force the image to wrap vertically within the viewport. + * Useful for maps or images representing the surface of a sphere or cylinder. * * @param {Number} [options.minZoomImageRatio=0.8] + * The minimum percentage ( expressed as a number between 0 and 1 ) of + * the viewport height or width at which the zoom out will be constrained. + * Setting it to 0, for example will allow you to zoom out infinitly. + * * @param {Number} [options.maxZoomPixelRatio=2] + * The maximum ratio to allow a zoom-in to affect the highest level pixel + * ratio. This can be set to Infinity to allow 'infinite' zooming into the + * image though it is less effective visually if the HTML5 Canvas is not + * availble on the viewing device. * * @param {Number} [options.visibilityRatio=0.5] * The percentage ( as a number from 0 to 1 ) of the source image which @@ -129,14 +140,31 @@ * true will provide the effect of an infinitely scrolling viewport. * * @param {Number} [options.springStiffness=5.0] + * * @param {Number} [options.imageLoaderLimit=0] + * The maximum number of image requests to make concurrently. By default + * it is set to 0 allowing the browser to make the maximum number of + * image requests in parallel as allowed by the browsers policy. + * * @param {Number} [options.clickTimeThreshold=200] + * If multiple mouse clicks occurs within less than this number of + * milliseconds, treat them as a single click. + * * @param {Number} [options.clickDistThreshold=5] + * If a mouse or touch drag occurs and the distance to the starting drag + * point is less than this many pixels, ignore the drag event. + * * @param {Number} [options.zoomPerClick=2.0] + * The "zoom distance" per mouse click or touch tap. + * * @param {Number} [options.zoomPerScroll=1.2] + * The "zoom distance" per mouse scroll or touch pinch. + * * @param {Number} [options.zoomPerSecond=2.0] + * The number of seconds to animate a single zoom event over. * * @param {Boolean} [options.showNavigationControl=true] + * Set to false to prevent the appearance of the default navigation controls. * * @param {Number} [options.controlsFadeDelay=2000] * The number of milliseconds to wait once the user has stopped interacting @@ -421,6 +449,12 @@ OpenSeadragon = window.OpenSeadragon || function( options ){ zoomPerScroll: 1.2, zoomPerSecond: 2.0, showNavigationControl: true, + + showNavigator: false, + navigatorElement: null, + navigatorHeight: null, + navigatorWidth: null, + navigatorPosition: null, //These two were referenced but never defined controlsFadeDelay: 2000, @@ -1076,6 +1110,78 @@ OpenSeadragon = window.OpenSeadragon || function( options ){ }, + /** + * Taken from jQuery 1.6.1 + * @param {Object} options + * @param {String} options.url + * @param {Function} options.callback + * @param {String} [options.param='callback'] The name of the url parameter + * to request the jsonp provider with. + * @param {String} [options.callbackName=] The name of the callback to + * request the jsonp provider with. + */ + jsonp: function( options ){ + var script, + url = options.url, + head = document.head || + document.getElementsByTagName( "head" )[ 0 ] || + document.documentElement, + jsonpCallback = options.callbackName || 'openseadragon' + (+new Date()), + previous = window[ jsonpCallback ], + replace = "$1" + jsonpCallback + "$2", + callbackParam = options.param || 'callback', + callback = options.callback; + + url = url.replace( /(\=)\?(&|$)|\?\?/i, replace ); + // Add callback manually + url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback; + + // Install callback + window[ jsonpCallback ] = function( response ) { + if ( !previous ){ + delete window[ jsonpCallback ]; + } else { + window[ jsonpCallback ] = previous; + } + if( callback && $.isFunction( callback ) ){ + callback( response ); + } + }; + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( options.scriptCharset ) { + script.charset = options.scriptCharset; + } + + script.src = url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + + }, + + /** * Loads a Deep Zoom Image description from a url or XML string and * provides a callback hook for the resulting Document diff --git a/src/viewer.js b/src/viewer.js index 61b0494f..8d989b5d 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -81,11 +81,12 @@ $.Viewer = function( options ) { source: null, drawer: null, viewport: null, + navigator: null, profiler: null }, $.DEFAULT_SETTINGS, options ); - this.element = document.getElementById( this.id ); + this.element = this.element || document.getElementById( this.id ); this.container = $.makeNeutralElement( "div" ); this.canvas = $.makeNeutralElement( "div" ); @@ -115,7 +116,7 @@ $.Viewer = function( options ) { dragHandler: $.delegate( this, onCanvasDrag ), releaseHandler: $.delegate( this, onCanvasRelease ), scrollHandler: $.delegate( this, onCanvasScroll ) - }).setTracking( true ); // default state + }).setTracking( this.mouseNavEnabled ? true : false ); // default state this.outerTracker = new $.MouseTracker({ element: this.container, @@ -124,7 +125,7 @@ $.Viewer = function( options ) { enterHandler: $.delegate( this, onContainerEnter ), exitHandler: $.delegate( this, onContainerExit ), releaseHandler: $.delegate( this, onContainerRelease ) - }).setTracking( true ); // always tracking + }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking (function( canvas ){ canvas.width = "100%"; @@ -257,6 +258,23 @@ $.Viewer = function( options ) { this.addControl( this.navControl, $.ControlAnchor.BOTTOM_RIGHT ); } + if ( this.showNavigator ){ + this.navigator = new $.Navigator({ + viewerId: this.id, + id: this.navigatorElement, + position: this.navigatorPosition, + height: this.navigatorHeight, + width: this.navigatorWidth, + tileSources: this.tileSources, + prefixUrl: this.prefixUrl + }); + this.addControl( + this.navigator.element, + $.ControlAnchor.TOP_RIGHT + ); + } + + for ( i = 0; i < this.customControls.length; i++ ) { this.addControl( this.customControls[ i ].id, @@ -951,9 +969,15 @@ function updateOnce( viewer ) { if ( animated ) { viewer.drawer.update(); + if( viewer.navigator ){ + viewer.navigator.update( viewer.viewport ); + } viewer.raiseEvent( "animation" ); } else if ( THIS[ viewer.hash ].forceRedraw || viewer.drawer.needsUpdate() ) { viewer.drawer.update(); + if( viewer.navigator ){ + viewer.navigator.update( viewer.viewport ); + } THIS[ viewer.hash ].forceRedraw = false; } diff --git a/src/viewport.js b/src/viewport.js index b00e1093..b6807edd 100644 --- a/src/viewport.js +++ b/src/viewport.js @@ -129,8 +129,8 @@ $.Viewport.prototype = { height = width / this.getAspectRatio(); return new $.Rect( - center.x - width / 2.0, - center.y - height / 2.0, + center.x - ( width / 2.0 ), + center.y - ( height / 2.0 ), width, height );