diff --git a/build.properties b/build.properties index fdaf90fb..7229889d 100644 --- a/build.properties +++ b/build.properties @@ -6,7 +6,7 @@ PROJECT: openseadragon BUILD_MAJOR: 0 BUILD_MINOR: 9 -BUILD_ID: 21 +BUILD_ID: 25 BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID} VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID} diff --git a/build.xml b/build.xml index 0ec07da6..6ee6610a 100644 --- a/build.xml +++ b/build.xml @@ -23,6 +23,7 @@ + diff --git a/openseadragon.js b/openseadragon.js index 31c3099e..3b0278d5 100644 --- a/openseadragon.js +++ b/openseadragon.js @@ -1,5 +1,5 @@ /** - * @version OpenSeadragon 0.9.21 + * @version OpenSeadragon 0.9.25 * * @fileOverview *

@@ -455,6 +455,7 @@ OpenSeadragon = window.OpenSeadragon || function( options ){ navigatorHeight: null, navigatorWidth: null, navigatorPosition: null, + navigatorSizeRatio: 0.25, //These two were referenced but never defined controlsFadeDelay: 2000, @@ -2781,7 +2782,190 @@ $.Control.prototype = { }; }( OpenSeadragon )); +(function( $ ){ + //id hash for private properties; + var THIS = {}; + + /** + * @class + */ + $.ControlDock = function( options ){ + var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'], + layout, + i; + + $.extend( true, this, { + id: 'controldock-'+(+new Date())+'-'+Math.floor(Math.random()*1000000), + container: $.makeNeutralElement('div'), + controls: [] + }, options ); + + if( this.element ){ + this.element = $.getElement( this.element ); + this.element.appendChild( this.container ); + this.element.style.position = 'relative'; + this.container.style.width = '100%'; + this.container.style.height = '100%'; + } + + for( i = 0; i < layouts.length; i++ ){ + layout = layouts[ i ]; + this.controls[ layout ] = $.makeNeutralElement( "div" ); + this.controls[ layout ].style.position = 'absolute'; + if ( layout.match( 'left' ) ){ + this.controls[ layout ].style.left = '0px'; + } + if ( layout.match( 'right' ) ){ + this.controls[ layout ].style.right = '0px'; + } + if ( layout.match( 'top' ) ){ + this.controls[ layout ].style.top = '0px'; + } + if ( layout.match( 'bottom' ) ){ + this.controls[ layout ].style.bottom = '0px'; + } + } + + this.container.appendChild( this.controls.topleft ); + this.container.appendChild( this.controls.topright ); + this.container.appendChild( this.controls.bottomright ); + this.container.appendChild( this.controls.bottomleft ); + }; + + $.ControlDock.prototype = { + + /** + * @function + */ + addControl: function ( element, anchor ) { + var element = $.getElement( element ), + div = null; + + if ( getControlIndex( this, element ) >= 0 ) { + return; // they're trying to add a duplicate control + } + + switch ( anchor ) { + case $.ControlAnchor.TOP_RIGHT: + div = this.controls.topright; + element.style.position = "relative"; + element.style.marginRight = "4px"; + element.style.marginTop = "4px"; + break; + case $.ControlAnchor.BOTTOM_RIGHT: + div = this.controls.bottomright; + element.style.position = "relative"; + element.style.marginRight = "4px"; + element.style.marginBottom = "4px"; + break; + case $.ControlAnchor.BOTTOM_LEFT: + div = this.controls.bottomleft; + element.style.position = "relative"; + element.style.marginLeft = "4px"; + element.style.marginBottom = "4px"; + break; + case $.ControlAnchor.TOP_LEFT: + div = this.controls.topleft; + element.style.position = "relative"; + element.style.marginLeft = "4px"; + element.style.marginTop = "4px"; + break; + case $.ControlAnchor.NONE: + default: + div = this.container; + element.style.margin = "0px"; + element.style.padding = "0px"; + break; + } + + this.controls.push( + new $.Control( element, anchor, div ) + ); + element.style.display = "inline-block"; + }, + + + /** + * @function + * @return {OpenSeadragon.Viewer} Chainable. + */ + removeControl: function ( element ) { + var element = $.getElement( element ), + i = getControlIndex( this, element ); + + if ( i >= 0 ) { + this.controls[ i ].destroy(); + this.controls.splice( i, 1 ); + } + + return this; + }, + + /** + * @function + * @return {OpenSeadragon.Viewer} Chainable. + */ + clearControls: function () { + while ( this.controls.length > 0 ) { + this.controls.pop().destroy(); + } + + return this; + }, + + + /** + * @function + * @return {Boolean} + */ + areControlsEnabled: function () { + var i; + + for ( i = this.controls.length - 1; i >= 0; i-- ) { + if ( this.controls[ i ].isVisible() ) { + return true; + } + } + + return false; + }, + + + /** + * @function + * @return {OpenSeadragon.Viewer} Chainable. + */ + setControlsEnabled: function( enabled ) { + var i; + + for ( i = this.controls.length - 1; i >= 0; i-- ) { + this.controls[ i ].setVisible( enabled ); + } + + return this; + } + + }; + + + /////////////////////////////////////////////////////////////////////////////// + // Utility methods + /////////////////////////////////////////////////////////////////////////////// + function getControlIndex( dock, element ) { + var controls = dock.controls, + i; + + for ( i = controls.length - 1; i >= 0; i-- ) { + if ( controls[ i ].element == element ) { + return i; + } + } + + return -1; + }; + +}( OpenSeadragon )); (function( $ ){ // dictionary from hash to private properties @@ -2799,6 +2983,7 @@ var THIS = {}; * * @class * @extends OpenSeadragon.EventHandler + * @extends OpenSeadragon.ControlDock * @param {Object} options * @param {String} options.element Id of Element to attach to, * @param {String} options.xmlPath Xpath ( TODO: not sure! ), @@ -2816,7 +3001,6 @@ $.Viewer = function( options ) { _this = this, i; - $.EventHandler.call( this ); //backward compatibility for positional args while prefering more //idiomatic javascript options object as the only argument @@ -2844,7 +3028,6 @@ $.Viewer = function( options ) { id: options.id, hash: options.id, - controls: [], overlays: [], overlayControls: [], @@ -2869,10 +3052,33 @@ $.Viewer = function( options ) { }, $.DEFAULT_SETTINGS, options ); + $.EventHandler.call( this ); + $.ControlDock.call( this, options ); + this.element = this.element || document.getElementById( this.id ); - this.container = $.makeNeutralElement( "div" ); this.canvas = $.makeNeutralElement( "div" ); + (function( canvas ){ + canvas.width = "100%"; + canvas.height = "100%"; + canvas.overflow = "hidden"; + canvas.position = "absolute"; + canvas.top = "0px"; + canvas.left = "0px"; + }( this.canvas.style )); + + (function( container ){ + container.width = "100%"; + container.height = "100%"; + container.position = "relative"; + container.left = "0px"; + container.top = "0px"; + container.textAlign = "left"; // needed to protect against + }( this.container.style )); + + this.container.insertBefore( this.canvas, this.container.firstChild); + this.element.appendChild( this.container ); + //Used for toggling between fullscreen and default container size //TODO: these can be closure private and shared across Viewer // instances. @@ -2910,45 +3116,6 @@ $.Viewer = function( options ) { releaseHandler: $.delegate( this, onContainerRelease ) }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking - (function( canvas ){ - canvas.width = "100%"; - canvas.height = "100%"; - canvas.overflow = "hidden"; - canvas.position = "absolute"; - canvas.top = "0px"; - canvas.left = "0px"; - }( this.canvas.style )); - - - (function( container ){ - container.width = "100%"; - container.height = "100%"; - container.position = "relative"; - container.left = "0px"; - container.top = "0px"; - container.textAlign = "left"; // needed to protect against - }( this.container.style )); - - var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'], - layout; - - for( i = 0; i < layouts.length; i++ ){ - layout = layouts[ i ] - this.controls[ layout ] = $.makeNeutralElement( "div" ); - this.controls[ layout ].style.position = 'absolute'; - if ( layout.match( 'left' ) ){ - this.controls[ layout ].style.left = '0px'; - } - if ( layout.match( 'right' ) ){ - this.controls[ layout ].style.right = '0px'; - } - if ( layout.match( 'top' ) ){ - this.controls[ layout ].style.top = '0px'; - } - if ( layout.match( 'bottom' ) ){ - this.controls[ layout ].style.bottom = '0px'; - } - } //private state properties $.extend( THIS[ this.hash ], { @@ -2971,7 +3138,14 @@ $.Viewer = function( options ) { onHomeHandler = $.delegate( this, onHome ), onFullPageHandler = $.delegate( this, onFullPage ), navImages = this.navImages, - zoomIn = new $.Button({ + zoomIn, + zoomOut, + goHome, + fullPage; + + if( this.showNavigationControl ){ + + zoomIn = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, tooltip: $.getString( "Tooltips.ZoomIn" ), @@ -2984,7 +3158,8 @@ $.Viewer = function( options ) { onClick: doSingleZoomInHandler, onEnter: beginZoomingInHandler, onExit: endZoomingHandler - }), + }); + zoomOut = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -2998,7 +3173,8 @@ $.Viewer = function( options ) { onClick: doSingleZoomOutHandler, onEnter: beginZoomingOutHandler, onExit: endZoomingHandler - }), + }); + goHome = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -3008,7 +3184,8 @@ $.Viewer = function( options ) { srcHover: resolveUrl( this.prefixUrl, navImages.home.HOVER ), srcDown: resolveUrl( this.prefixUrl, navImages.home.DOWN ), onRelease: onHomeHandler - }), + }); + fullPage = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -3020,25 +3197,27 @@ $.Viewer = function( options ) { onRelease: onFullPageHandler }); - this.buttons = new $.ButtonGroup({ - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - buttons: [ - zoomIn, - zoomOut, - goHome, - fullPage - ] - }); + this.buttons = new $.ButtonGroup({ + clickTimeThreshold: this.clickTimeThreshold, + clickDistThreshold: this.clickDistThreshold, + buttons: [ + zoomIn, + zoomOut, + goHome, + fullPage + ] + }); - this.navControl = this.buttons.element; - this.navControl[ $.SIGNAL ] = true; // hack to get our controls to fade - this.addHandler( 'open', $.delegate( this, lightUp ) ); - - if ( this.showNavigationControl ) { - this.navControl.style.marginRight = "4px"; - this.navControl.style.marginBottom = "4px"; - this.addControl( this.navControl, $.ControlAnchor.BOTTOM_RIGHT ); + this.navControl = this.buttons.element; + this.navControl[ $.SIGNAL ] = true; // hack to get our controls to fade + this.addHandler( 'open', $.delegate( this, lightUp ) ); + + if( this.toolbar ){ + this.toolbar = new $.ControlDock({ element: this.toolbar }); + this.toolbar.addControl( this.navControl ); + }else{ + this.addControl( this.navControl, $.ControlAnchor.BOTTOM_RIGHT ); + } } if ( this.showNavigator ){ @@ -3065,13 +3244,6 @@ $.Viewer = function( options ) { ); } - this.container.appendChild( this.canvas ); - this.container.appendChild( this.controls.topleft ); - this.container.appendChild( this.controls.topright ); - this.container.appendChild( this.controls.bottomright ); - this.container.appendChild( this.controls.bottomleft ); - this.element.appendChild( this.container ); - window.setTimeout( function(){ beginControlsAutoHide( _this ); }, 1 ); // initial fade out @@ -3119,49 +3291,8 @@ $.Viewer = function( options ) { } }; -$.extend( $.Viewer.prototype, $.EventHandler.prototype, { +$.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, { - /** - * @function - * @name OpenSeadragon.Viewer.prototype.addControl - */ - addControl: function ( element, anchor ) { - var element = $.getElement( element ), - div = null; - - if ( getControlIndex( this, element ) >= 0 ) { - return; // they're trying to add a duplicate control - } - - switch ( anchor ) { - case $.ControlAnchor.TOP_RIGHT: - div = this.controls.topright; - element.style.position = "relative"; - break; - case $.ControlAnchor.BOTTOM_RIGHT: - div = this.controls.bottomright; - element.style.position = "relative"; - break; - case $.ControlAnchor.BOTTOM_LEFT: - div = this.controls.bottomleft; - element.style.position = "relative"; - break; - case $.ControlAnchor.TOP_LEFT: - div = this.controls.topleft; - element.style.position = "relative"; - break; - case $.ControlAnchor.NONE: - default: - div = this.container; - element.style.position = "absolute"; - break; - } - - this.controls.push( - new $.Control( element, anchor, div ) - ); - element.style.display = "inline-block"; - }, /** * @function @@ -3317,61 +3448,6 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { return this; }, - /** - * @function - * @name OpenSeadragon.Viewer.prototype.removeControl - * @return {OpenSeadragon.Viewer} Chainable. - */ - removeControl: function ( element ) { - - var element = $.getElement( element ), - i = getControlIndex( this, element ); - - if ( i >= 0 ) { - this.controls[ i ].destroy(); - this.controls.splice( i, 1 ); - } - - return this; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.clearControls - * @return {OpenSeadragon.Viewer} Chainable. - */ - clearControls: function () { - while ( this.controls.length > 0 ) { - this.controls.pop().destroy(); - } - return this; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isDashboardEnabled - * @return {Boolean} - */ - isDashboardEnabled: function () { - var i; - - for ( i = this.controls.length - 1; i >= 0; i-- ) { - if ( this.controls[ i ].isVisible() ) { - return true; - } - } - - return false; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isFullPage - * @return {Boolean} - */ - isFullPage: function () { - return this.container.parentNode == document.body; - }, /** * @function @@ -3384,26 +3460,45 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { /** * @function - * @name OpenSeadragon.Viewer.prototype.isVisible + * @name OpenSeadragon.Viewer.prototype.setMouseNavEnabled + * @return {OpenSeadragon.Viewer} Chainable. + */ + setMouseNavEnabled: function( enabled ){ + this.innerTracker.setTracking( enabled ); + return this; + }, + + + /** + * @function + * @name OpenSeadragon.Viewer.prototype.isDashboardEnabled * @return {Boolean} */ - isVisible: function () { - return this.container.style.visibility != "hidden"; + isDashboardEnabled: function () { + return this.areControlsEnabled( enabled ); }, + /** * @function * @name OpenSeadragon.Viewer.prototype.setDashboardEnabled * @return {OpenSeadragon.Viewer} Chainable. */ setDashboardEnabled: function( enabled ) { - var i; - for ( i = this.controls.length - 1; i >= 0; i-- ) { - this.controls[ i ].setVisible( enabled ); - } - return this; + return this.setControlsEnabled( enabled ); }, + + /** + * @function + * @name OpenSeadragon.Viewer.prototype.isFullPage + * @return {Boolean} + */ + isFullPage: function () { + return this.container.parentNode == document.body; + }, + + /** * Toggle full page mode. * @function @@ -3445,7 +3540,6 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { canvasStyle.color = "white"; containerStyle.position = "fixed"; - containerStyle.zIndex = "99999999"; //when entering full screen on the ipad it wasnt sufficient to leave //the body intact as only only the top half of the screen would @@ -3453,11 +3547,29 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { //them as touch events on the document body. Thus we remove and store //the bodies elements and replace them when we leave full screen. this.previousBody = []; - nodes = document.body.childNodes.length; + nodes = body.childNodes.length; for ( i = 0; i < nodes; i ++ ){ - this.previousBody.push( document.body.childNodes[ 0 ] ); - document.body.removeChild( document.body.childNodes[ 0 ] ); + this.previousBody.push( body.childNodes[ 0 ] ); + body.removeChild( body.childNodes[ 0 ] ); } + + //If we've got a toolbar, we need to enable the user to use css to + //preserve it in fullpage mode + if( this.toolbar && this.toolbar.element ){ + //save a reference to the parent so we can put it back + //in the long run we need a better strategy + this.toolbar.parentNode = this.toolbar.element.parentNode; + this.toolbar.previousSibling = this.toolbar.element.previousSibling; + body.appendChild( this.toolbar.element ); + + //Make sure the user has some ability to style the toolbar based + //on the mode + this.toolbar.element.setAttribute( + 'class', + this.toolbar.element.className +" fullpage" + ); + } + body.appendChild( this.container ); THIS[ this.hash ].prevContainerSize = $.getWindowSize(); @@ -3479,11 +3591,30 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { containerStyle.position = "relative"; containerStyle.zIndex = ""; - document.body.removeChild( this.container ); + //If we've got a toolbar, we need to enable the user to use css to + //reset it to its original state + if( this.toolbar && this.toolbar.element ){ + body.removeChild( this.toolbar.element ); + + //Make sure the user has some ability to style the toolbar based + //on the mode + this.toolbar.element.setAttribute( + 'class', + this.toolbar.element.className.replace('fullpage','') + ); + } + + body.removeChild( this.container ); nodes = this.previousBody.length; for ( i = 0; i < nodes; i++ ){ - document.body.appendChild( this.previousBody.shift() ); + body.appendChild( this.previousBody.shift() ); } + this.toolbar.parentNode.insertBefore( + this.toolbar.element, + this.toolbar.previousSibling + ); + delete this.toolbar.parentNode; + delete this.toolbar.previousSibling; this.element.appendChild( this.container ); THIS[ this.hash ].prevContainerSize = $.getElementSize( this.element ); @@ -3521,16 +3652,17 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { return this; }, + /** * @function - * @name OpenSeadragon.Viewer.prototype.setMouseNavEnabled - * @return {OpenSeadragon.Viewer} Chainable. + * @name OpenSeadragon.Viewer.prototype.isVisible + * @return {Boolean} */ - setMouseNavEnabled: function( enabled ){ - this.innerTracker.setTracking( enabled ); - return this; + isVisible: function () { + return this.container.style.visibility != "hidden"; }, + /** * @function * @name OpenSeadragon.Viewer.prototype.setVisible @@ -3695,18 +3827,6 @@ function onContainerEnter( tracker, position, buttonDownElement, buttonDownAny ) abortControlsAutoHide( this ); }; -/////////////////////////////////////////////////////////////////////////////// -// Utility methods -/////////////////////////////////////////////////////////////////////////////// -function getControlIndex( viewer, element ) { - for ( i = viewer.controls.length - 1; i >= 0; i-- ) { - if ( viewer.controls[ i ].element == element ) { - return i; - } - } - return -1; -}; - /////////////////////////////////////////////////////////////////////////////// // Page update routines ( aka Views - for future reference ) @@ -3886,13 +4006,15 @@ $.Navigator = function( options ){ this.element.id = options.id; } - options = $.extend( true, options, { + options = $.extend( true, { + navigatorSizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio + }, 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 + showNavigationControl: false }); (function( style ){ @@ -3906,33 +4028,29 @@ $.Navigator = function( options ){ style.overflow = 'hidden'; }( this.element.style )); - this.displayRegion = $.makeNeutralElement( "div" ); + 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.border = '2px 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 ){ + if( options.width && options.height ){ + this.element.style.width = options.width + 'px'; this.element.style.height = options.height + 'px'; } else { - this.element.style.height = ( viewerSize.y / 4 ) + 'px'; + this.element.style.width = ( viewerSize.x * options.navigatorSizeRatio ) + 'px'; + this.element.style.height = ( viewerSize.y * options.navigatorSizeRatio ) + 'px'; } }; @@ -3951,8 +4069,8 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { 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'; + style.width = ( Math.abs( topleft.x - bottomright.x ) - 3 ) + 'px'; + style.height = ( Math.abs( topleft.y - bottomright.y ) - 3 ) + 'px'; }( this.displayRegion.style )); @@ -4896,7 +5014,7 @@ function outTo( button, newState ) { $.ButtonGroup = function( options ) { $.extend( true, this, { - buttons: null, + buttons: [], clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold, clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold }, options ); diff --git a/src/buttongroup.js b/src/buttongroup.js index 11e10143..e7c288d9 100644 --- a/src/buttongroup.js +++ b/src/buttongroup.js @@ -24,7 +24,7 @@ $.ButtonGroup = function( options ) { $.extend( true, this, { - buttons: null, + buttons: [], clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold, clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold }, options ); diff --git a/src/controldock.js b/src/controldock.js new file mode 100644 index 00000000..0415bbe9 --- /dev/null +++ b/src/controldock.js @@ -0,0 +1,184 @@ +(function( $ ){ + + //id hash for private properties; + var THIS = {}; + + /** + * @class + */ + $.ControlDock = function( options ){ + var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'], + layout, + i; + + $.extend( true, this, { + id: 'controldock-'+(+new Date())+'-'+Math.floor(Math.random()*1000000), + container: $.makeNeutralElement('div'), + controls: [] + }, options ); + + if( this.element ){ + this.element = $.getElement( this.element ); + this.element.appendChild( this.container ); + this.element.style.position = 'relative'; + this.container.style.width = '100%'; + this.container.style.height = '100%'; + } + + for( i = 0; i < layouts.length; i++ ){ + layout = layouts[ i ]; + this.controls[ layout ] = $.makeNeutralElement( "div" ); + this.controls[ layout ].style.position = 'absolute'; + if ( layout.match( 'left' ) ){ + this.controls[ layout ].style.left = '0px'; + } + if ( layout.match( 'right' ) ){ + this.controls[ layout ].style.right = '0px'; + } + if ( layout.match( 'top' ) ){ + this.controls[ layout ].style.top = '0px'; + } + if ( layout.match( 'bottom' ) ){ + this.controls[ layout ].style.bottom = '0px'; + } + } + + this.container.appendChild( this.controls.topleft ); + this.container.appendChild( this.controls.topright ); + this.container.appendChild( this.controls.bottomright ); + this.container.appendChild( this.controls.bottomleft ); + }; + + $.ControlDock.prototype = { + + /** + * @function + */ + addControl: function ( element, anchor ) { + var element = $.getElement( element ), + div = null; + + if ( getControlIndex( this, element ) >= 0 ) { + return; // they're trying to add a duplicate control + } + + switch ( anchor ) { + case $.ControlAnchor.TOP_RIGHT: + div = this.controls.topright; + element.style.position = "relative"; + element.style.marginRight = "4px"; + element.style.marginTop = "4px"; + break; + case $.ControlAnchor.BOTTOM_RIGHT: + div = this.controls.bottomright; + element.style.position = "relative"; + element.style.marginRight = "4px"; + element.style.marginBottom = "4px"; + break; + case $.ControlAnchor.BOTTOM_LEFT: + div = this.controls.bottomleft; + element.style.position = "relative"; + element.style.marginLeft = "4px"; + element.style.marginBottom = "4px"; + break; + case $.ControlAnchor.TOP_LEFT: + div = this.controls.topleft; + element.style.position = "relative"; + element.style.marginLeft = "4px"; + element.style.marginTop = "4px"; + break; + case $.ControlAnchor.NONE: + default: + div = this.container; + element.style.margin = "0px"; + element.style.padding = "0px"; + break; + } + + this.controls.push( + new $.Control( element, anchor, div ) + ); + element.style.display = "inline-block"; + }, + + + /** + * @function + * @return {OpenSeadragon.Viewer} Chainable. + */ + removeControl: function ( element ) { + var element = $.getElement( element ), + i = getControlIndex( this, element ); + + if ( i >= 0 ) { + this.controls[ i ].destroy(); + this.controls.splice( i, 1 ); + } + + return this; + }, + + /** + * @function + * @return {OpenSeadragon.Viewer} Chainable. + */ + clearControls: function () { + while ( this.controls.length > 0 ) { + this.controls.pop().destroy(); + } + + return this; + }, + + + /** + * @function + * @return {Boolean} + */ + areControlsEnabled: function () { + var i; + + for ( i = this.controls.length - 1; i >= 0; i-- ) { + if ( this.controls[ i ].isVisible() ) { + return true; + } + } + + return false; + }, + + + /** + * @function + * @return {OpenSeadragon.Viewer} Chainable. + */ + setControlsEnabled: function( enabled ) { + var i; + + for ( i = this.controls.length - 1; i >= 0; i-- ) { + this.controls[ i ].setVisible( enabled ); + } + + return this; + } + + }; + + + /////////////////////////////////////////////////////////////////////////////// + // Utility methods + /////////////////////////////////////////////////////////////////////////////// + function getControlIndex( dock, element ) { + var controls = dock.controls, + i; + + for ( i = controls.length - 1; i >= 0; i-- ) { + if ( controls[ i ].element == element ) { + return i; + } + } + + return -1; + }; + +}( OpenSeadragon )); \ No newline at end of file diff --git a/src/navigator.js b/src/navigator.js index e75f0675..a1ecdcf8 100644 --- a/src/navigator.js +++ b/src/navigator.js @@ -18,13 +18,15 @@ $.Navigator = function( options ){ this.element.id = options.id; } - options = $.extend( true, options, { + options = $.extend( true, { + navigatorSizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio + }, 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 + showNavigationControl: false }); (function( style ){ @@ -38,33 +40,29 @@ $.Navigator = function( options ){ style.overflow = 'hidden'; }( this.element.style )); - this.displayRegion = $.makeNeutralElement( "div" ); + 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.border = '2px 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 ){ + if( options.width && options.height ){ + this.element.style.width = options.width + 'px'; this.element.style.height = options.height + 'px'; } else { - this.element.style.height = ( viewerSize.y / 4 ) + 'px'; + this.element.style.width = ( viewerSize.x * options.navigatorSizeRatio ) + 'px'; + this.element.style.height = ( viewerSize.y * options.navigatorSizeRatio ) + 'px'; } }; @@ -83,8 +81,8 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { 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'; + style.width = ( Math.abs( topleft.x - bottomright.x ) - 3 ) + 'px'; + style.height = ( Math.abs( topleft.y - bottomright.y ) - 3 ) + 'px'; }( this.displayRegion.style )); diff --git a/src/openseadragon.js b/src/openseadragon.js index 0c8f585c..c81a3737 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -455,6 +455,7 @@ OpenSeadragon = window.OpenSeadragon || function( options ){ navigatorHeight: null, navigatorWidth: null, navigatorPosition: null, + navigatorSizeRatio: 0.25, //These two were referenced but never defined controlsFadeDelay: 2000, diff --git a/src/viewer.js b/src/viewer.js index 8d989b5d..60d8245f 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -16,6 +16,7 @@ var THIS = {}; * * @class * @extends OpenSeadragon.EventHandler + * @extends OpenSeadragon.ControlDock * @param {Object} options * @param {String} options.element Id of Element to attach to, * @param {String} options.xmlPath Xpath ( TODO: not sure! ), @@ -33,7 +34,6 @@ $.Viewer = function( options ) { _this = this, i; - $.EventHandler.call( this ); //backward compatibility for positional args while prefering more //idiomatic javascript options object as the only argument @@ -61,7 +61,6 @@ $.Viewer = function( options ) { id: options.id, hash: options.id, - controls: [], overlays: [], overlayControls: [], @@ -86,10 +85,33 @@ $.Viewer = function( options ) { }, $.DEFAULT_SETTINGS, options ); + $.EventHandler.call( this ); + $.ControlDock.call( this, options ); + this.element = this.element || document.getElementById( this.id ); - this.container = $.makeNeutralElement( "div" ); this.canvas = $.makeNeutralElement( "div" ); + (function( canvas ){ + canvas.width = "100%"; + canvas.height = "100%"; + canvas.overflow = "hidden"; + canvas.position = "absolute"; + canvas.top = "0px"; + canvas.left = "0px"; + }( this.canvas.style )); + + (function( container ){ + container.width = "100%"; + container.height = "100%"; + container.position = "relative"; + container.left = "0px"; + container.top = "0px"; + container.textAlign = "left"; // needed to protect against + }( this.container.style )); + + this.container.insertBefore( this.canvas, this.container.firstChild); + this.element.appendChild( this.container ); + //Used for toggling between fullscreen and default container size //TODO: these can be closure private and shared across Viewer // instances. @@ -127,45 +149,6 @@ $.Viewer = function( options ) { releaseHandler: $.delegate( this, onContainerRelease ) }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking - (function( canvas ){ - canvas.width = "100%"; - canvas.height = "100%"; - canvas.overflow = "hidden"; - canvas.position = "absolute"; - canvas.top = "0px"; - canvas.left = "0px"; - }( this.canvas.style )); - - - (function( container ){ - container.width = "100%"; - container.height = "100%"; - container.position = "relative"; - container.left = "0px"; - container.top = "0px"; - container.textAlign = "left"; // needed to protect against - }( this.container.style )); - - var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'], - layout; - - for( i = 0; i < layouts.length; i++ ){ - layout = layouts[ i ] - this.controls[ layout ] = $.makeNeutralElement( "div" ); - this.controls[ layout ].style.position = 'absolute'; - if ( layout.match( 'left' ) ){ - this.controls[ layout ].style.left = '0px'; - } - if ( layout.match( 'right' ) ){ - this.controls[ layout ].style.right = '0px'; - } - if ( layout.match( 'top' ) ){ - this.controls[ layout ].style.top = '0px'; - } - if ( layout.match( 'bottom' ) ){ - this.controls[ layout ].style.bottom = '0px'; - } - } //private state properties $.extend( THIS[ this.hash ], { @@ -188,7 +171,14 @@ $.Viewer = function( options ) { onHomeHandler = $.delegate( this, onHome ), onFullPageHandler = $.delegate( this, onFullPage ), navImages = this.navImages, - zoomIn = new $.Button({ + zoomIn, + zoomOut, + goHome, + fullPage; + + if( this.showNavigationControl ){ + + zoomIn = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, tooltip: $.getString( "Tooltips.ZoomIn" ), @@ -201,7 +191,8 @@ $.Viewer = function( options ) { onClick: doSingleZoomInHandler, onEnter: beginZoomingInHandler, onExit: endZoomingHandler - }), + }); + zoomOut = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -215,7 +206,8 @@ $.Viewer = function( options ) { onClick: doSingleZoomOutHandler, onEnter: beginZoomingOutHandler, onExit: endZoomingHandler - }), + }); + goHome = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -225,7 +217,8 @@ $.Viewer = function( options ) { srcHover: resolveUrl( this.prefixUrl, navImages.home.HOVER ), srcDown: resolveUrl( this.prefixUrl, navImages.home.DOWN ), onRelease: onHomeHandler - }), + }); + fullPage = new $.Button({ clickTimeThreshold: this.clickTimeThreshold, clickDistThreshold: this.clickDistThreshold, @@ -237,25 +230,27 @@ $.Viewer = function( options ) { onRelease: onFullPageHandler }); - this.buttons = new $.ButtonGroup({ - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - buttons: [ - zoomIn, - zoomOut, - goHome, - fullPage - ] - }); + this.buttons = new $.ButtonGroup({ + clickTimeThreshold: this.clickTimeThreshold, + clickDistThreshold: this.clickDistThreshold, + buttons: [ + zoomIn, + zoomOut, + goHome, + fullPage + ] + }); - this.navControl = this.buttons.element; - this.navControl[ $.SIGNAL ] = true; // hack to get our controls to fade - this.addHandler( 'open', $.delegate( this, lightUp ) ); - - if ( this.showNavigationControl ) { - this.navControl.style.marginRight = "4px"; - this.navControl.style.marginBottom = "4px"; - this.addControl( this.navControl, $.ControlAnchor.BOTTOM_RIGHT ); + this.navControl = this.buttons.element; + this.navControl[ $.SIGNAL ] = true; // hack to get our controls to fade + this.addHandler( 'open', $.delegate( this, lightUp ) ); + + if( this.toolbar ){ + this.toolbar = new $.ControlDock({ element: this.toolbar }); + this.toolbar.addControl( this.navControl ); + }else{ + this.addControl( this.navControl, $.ControlAnchor.BOTTOM_RIGHT ); + } } if ( this.showNavigator ){ @@ -282,13 +277,6 @@ $.Viewer = function( options ) { ); } - this.container.appendChild( this.canvas ); - this.container.appendChild( this.controls.topleft ); - this.container.appendChild( this.controls.topright ); - this.container.appendChild( this.controls.bottomright ); - this.container.appendChild( this.controls.bottomleft ); - this.element.appendChild( this.container ); - window.setTimeout( function(){ beginControlsAutoHide( _this ); }, 1 ); // initial fade out @@ -336,49 +324,8 @@ $.Viewer = function( options ) { } }; -$.extend( $.Viewer.prototype, $.EventHandler.prototype, { +$.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, { - /** - * @function - * @name OpenSeadragon.Viewer.prototype.addControl - */ - addControl: function ( element, anchor ) { - var element = $.getElement( element ), - div = null; - - if ( getControlIndex( this, element ) >= 0 ) { - return; // they're trying to add a duplicate control - } - - switch ( anchor ) { - case $.ControlAnchor.TOP_RIGHT: - div = this.controls.topright; - element.style.position = "relative"; - break; - case $.ControlAnchor.BOTTOM_RIGHT: - div = this.controls.bottomright; - element.style.position = "relative"; - break; - case $.ControlAnchor.BOTTOM_LEFT: - div = this.controls.bottomleft; - element.style.position = "relative"; - break; - case $.ControlAnchor.TOP_LEFT: - div = this.controls.topleft; - element.style.position = "relative"; - break; - case $.ControlAnchor.NONE: - default: - div = this.container; - element.style.position = "absolute"; - break; - } - - this.controls.push( - new $.Control( element, anchor, div ) - ); - element.style.display = "inline-block"; - }, /** * @function @@ -534,61 +481,6 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { return this; }, - /** - * @function - * @name OpenSeadragon.Viewer.prototype.removeControl - * @return {OpenSeadragon.Viewer} Chainable. - */ - removeControl: function ( element ) { - - var element = $.getElement( element ), - i = getControlIndex( this, element ); - - if ( i >= 0 ) { - this.controls[ i ].destroy(); - this.controls.splice( i, 1 ); - } - - return this; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.clearControls - * @return {OpenSeadragon.Viewer} Chainable. - */ - clearControls: function () { - while ( this.controls.length > 0 ) { - this.controls.pop().destroy(); - } - return this; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isDashboardEnabled - * @return {Boolean} - */ - isDashboardEnabled: function () { - var i; - - for ( i = this.controls.length - 1; i >= 0; i-- ) { - if ( this.controls[ i ].isVisible() ) { - return true; - } - } - - return false; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isFullPage - * @return {Boolean} - */ - isFullPage: function () { - return this.container.parentNode == document.body; - }, /** * @function @@ -601,26 +493,45 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { /** * @function - * @name OpenSeadragon.Viewer.prototype.isVisible + * @name OpenSeadragon.Viewer.prototype.setMouseNavEnabled + * @return {OpenSeadragon.Viewer} Chainable. + */ + setMouseNavEnabled: function( enabled ){ + this.innerTracker.setTracking( enabled ); + return this; + }, + + + /** + * @function + * @name OpenSeadragon.Viewer.prototype.isDashboardEnabled * @return {Boolean} */ - isVisible: function () { - return this.container.style.visibility != "hidden"; + isDashboardEnabled: function () { + return this.areControlsEnabled( enabled ); }, + /** * @function * @name OpenSeadragon.Viewer.prototype.setDashboardEnabled * @return {OpenSeadragon.Viewer} Chainable. */ setDashboardEnabled: function( enabled ) { - var i; - for ( i = this.controls.length - 1; i >= 0; i-- ) { - this.controls[ i ].setVisible( enabled ); - } - return this; + return this.setControlsEnabled( enabled ); }, + + /** + * @function + * @name OpenSeadragon.Viewer.prototype.isFullPage + * @return {Boolean} + */ + isFullPage: function () { + return this.container.parentNode == document.body; + }, + + /** * Toggle full page mode. * @function @@ -662,7 +573,6 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { canvasStyle.color = "white"; containerStyle.position = "fixed"; - containerStyle.zIndex = "99999999"; //when entering full screen on the ipad it wasnt sufficient to leave //the body intact as only only the top half of the screen would @@ -670,11 +580,29 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { //them as touch events on the document body. Thus we remove and store //the bodies elements and replace them when we leave full screen. this.previousBody = []; - nodes = document.body.childNodes.length; + nodes = body.childNodes.length; for ( i = 0; i < nodes; i ++ ){ - this.previousBody.push( document.body.childNodes[ 0 ] ); - document.body.removeChild( document.body.childNodes[ 0 ] ); + this.previousBody.push( body.childNodes[ 0 ] ); + body.removeChild( body.childNodes[ 0 ] ); } + + //If we've got a toolbar, we need to enable the user to use css to + //preserve it in fullpage mode + if( this.toolbar && this.toolbar.element ){ + //save a reference to the parent so we can put it back + //in the long run we need a better strategy + this.toolbar.parentNode = this.toolbar.element.parentNode; + this.toolbar.previousSibling = this.toolbar.element.previousSibling; + body.appendChild( this.toolbar.element ); + + //Make sure the user has some ability to style the toolbar based + //on the mode + this.toolbar.element.setAttribute( + 'class', + this.toolbar.element.className +" fullpage" + ); + } + body.appendChild( this.container ); THIS[ this.hash ].prevContainerSize = $.getWindowSize(); @@ -696,11 +624,30 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { containerStyle.position = "relative"; containerStyle.zIndex = ""; - document.body.removeChild( this.container ); + //If we've got a toolbar, we need to enable the user to use css to + //reset it to its original state + if( this.toolbar && this.toolbar.element ){ + body.removeChild( this.toolbar.element ); + + //Make sure the user has some ability to style the toolbar based + //on the mode + this.toolbar.element.setAttribute( + 'class', + this.toolbar.element.className.replace('fullpage','') + ); + } + + body.removeChild( this.container ); nodes = this.previousBody.length; for ( i = 0; i < nodes; i++ ){ - document.body.appendChild( this.previousBody.shift() ); + body.appendChild( this.previousBody.shift() ); } + this.toolbar.parentNode.insertBefore( + this.toolbar.element, + this.toolbar.previousSibling + ); + delete this.toolbar.parentNode; + delete this.toolbar.previousSibling; this.element.appendChild( this.container ); THIS[ this.hash ].prevContainerSize = $.getElementSize( this.element ); @@ -738,16 +685,17 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, { return this; }, + /** * @function - * @name OpenSeadragon.Viewer.prototype.setMouseNavEnabled - * @return {OpenSeadragon.Viewer} Chainable. + * @name OpenSeadragon.Viewer.prototype.isVisible + * @return {Boolean} */ - setMouseNavEnabled: function( enabled ){ - this.innerTracker.setTracking( enabled ); - return this; + isVisible: function () { + return this.container.style.visibility != "hidden"; }, + /** * @function * @name OpenSeadragon.Viewer.prototype.setVisible @@ -912,18 +860,6 @@ function onContainerEnter( tracker, position, buttonDownElement, buttonDownAny ) abortControlsAutoHide( this ); }; -/////////////////////////////////////////////////////////////////////////////// -// Utility methods -/////////////////////////////////////////////////////////////////////////////// -function getControlIndex( viewer, element ) { - for ( i = viewer.controls.length - 1; i >= 0; i-- ) { - if ( viewer.controls[ i ].element == element ) { - return i; - } - } - return -1; -}; - /////////////////////////////////////////////////////////////////////////////// // Page update routines ( aka Views - for future reference )