diff --git a/build.properties b/build.properties
index 6716fb70..4aea5bd6 100644
--- a/build.properties
+++ b/build.properties
@@ -6,7 +6,7 @@
PROJECT: openseadragon
BUILD_MAJOR: 0
BUILD_MINOR: 9
-BUILD_ID: 26
+BUILD_ID: 27
BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
diff --git a/openseadragon.js b/openseadragon.js
index bfddd738..e855578a 100644
--- a/openseadragon.js
+++ b/openseadragon.js
@@ -1,5 +1,5 @@
/**
- * @version OpenSeadragon 0.9.26
+ * @version OpenSeadragon 0.9.27
*
* @fileOverview
*
@@ -1758,6 +1758,9 @@ $.EventHandler.prototype = {
this.scrollHandler = options.scrollHandler || null;
this.clickHandler = options.clickHandler || null;
this.dragHandler = options.dragHandler || null;
+ this.keyHandler = options.keyHandler || null;
+ this.focusHandler = options.focusHandler || null;
+ this.blurHandler = options.blurHandler || null;
//Store private properties in a scope sealed hash map
var _this = this;
@@ -1795,6 +1798,9 @@ $.EventHandler.prototype = {
"touchstart": function( event ){ onTouchStart( _this, event ); },
"touchmove": function( event ){ onTouchMove( _this, event ); },
"touchend": function( event ){ onTouchEnd( _this, event ); },
+ "keypress": function( event ){ onKeyPress( _this, event ); },
+ "focus": function( event ){ onFocus( _this, event ); },
+ "blur": function( event ){ onBlur( _this, event ); },
tracking: false,
capturing: false,
buttonDown: false,
@@ -1940,8 +1946,24 @@ $.EventHandler.prototype = {
* @param {Boolean} shift
* Was the shift key being pressed during this event?
*/
- dragHandler: function(){}
+ dragHandler: function(){},
+ /**
+ * Implement or assign implmentation to these handlers during or after
+ * calling the constructor.
+ * @function
+ * @param {OpenSeadragon.MouseTracker} tracker
+ * A reference to the tracker instance.
+ * @param {Number} keyCode
+ * The key code that was pressed.
+ * @param {Boolean} shift
+ * Was the shift key being pressed during this event?
+ */
+ keyHandler: function(){},
+
+ focusHandler: function(){},
+
+ blurHandler: function(){}
};
/**
@@ -1954,7 +1976,9 @@ $.EventHandler.prototype = {
"mouseover", "mouseout", "mousedown", "mouseup",
"click",
"DOMMouseScroll", "mousewheel",
- "touchstart", "touchmove", "touchend"
+ "touchstart", "touchmove", "touchend",
+ "keypress",
+ "focus", "blur"
],
delegate = THIS[ tracker.hash ],
event,
@@ -1985,7 +2009,9 @@ $.EventHandler.prototype = {
"mouseover", "mouseout", "mousedown", "mouseup",
"click",
"DOMMouseScroll", "mousewheel",
- "touchstart", "touchmove", "touchend"
+ "touchstart", "touchmove", "touchend",
+ "keypress",
+ "focus", "blur"
],
delegate = THIS[ tracker.hash ],
event,
@@ -2124,6 +2150,81 @@ $.EventHandler.prototype = {
};
+ /**
+ * @private
+ * @inner
+ */
+ function onFocus( tracker, event ){
+ console.log( "focus %s", event );
+ if ( tracker.focusHandler ) {
+ try {
+ tracker.focusHandler(
+ tracker,
+ event
+ );
+ $.cancelEvent( event );
+ } catch ( e ) {
+ $.console.error(
+ "%s while executing key handler: %s",
+ e.name,
+ e.message,
+ e
+ );
+ }
+ }
+ };
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function onBlur( tracker, event ){
+ console.log( "blur %s", event );
+ if ( tracker.blurHandler ) {
+ try {
+ tracker.blurHandler(
+ tracker,
+ event
+ );
+ $.cancelEvent( event );
+ } catch ( e ) {
+ $.console.error(
+ "%s while executing key handler: %s",
+ e.name,
+ e.message,
+ e
+ );
+ }
+ }
+ };
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function onKeyPress( tracker, event ){
+ //console.log( "keypress %s", event.keyCode );
+ if ( tracker.keyHandler ) {
+ try {
+ tracker.keyHandler(
+ tracker,
+ event.keyCode
+ );
+ $.cancelEvent( event );
+ } catch ( e ) {
+ $.console.error(
+ "%s while executing key handler: %s",
+ e.name,
+ e.message,
+ e
+ );
+ }
+ }
+ };
+
+
/**
* @private
* @inner
@@ -2797,7 +2898,7 @@ $.Control.prototype = {
$.extend( true, this, {
id: 'controldock-'+(+new Date())+'-'+Math.floor(Math.random()*1000000),
- container: $.makeNeutralElement('div'),
+ container: $.makeNeutralElement('form'),
controls: []
}, options );
@@ -2969,7 +3070,10 @@ $.Control.prototype = {
(function( $ ){
// dictionary from hash to private properties
-var THIS = {};
+var THIS = {},
+// We keep a list of viewers so we can 'wake-up' each viewer on
+// a page after toggling between fullpage modes
+ VIEWERS = {};
/**
*
@@ -3137,6 +3241,8 @@ $.Viewer = function( options ) {
doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ),
onHomeHandler = $.delegate( this, onHome ),
onFullPageHandler = $.delegate( this, onFullPage ),
+ onFocusHandler = $.delegate( this, onFocus ),
+ onBlurHandler = $.delegate( this, onBlur ),
navImages = this.navImages,
zoomIn,
zoomOut,
@@ -3157,7 +3263,9 @@ $.Viewer = function( options ) {
onRelease: endZoomingHandler,
onClick: doSingleZoomInHandler,
onEnter: beginZoomingInHandler,
- onExit: endZoomingHandler
+ onExit: endZoomingHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
zoomOut = new $.Button({
@@ -3172,7 +3280,9 @@ $.Viewer = function( options ) {
onRelease: endZoomingHandler,
onClick: doSingleZoomOutHandler,
onEnter: beginZoomingOutHandler,
- onExit: endZoomingHandler
+ onExit: endZoomingHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
goHome = new $.Button({
@@ -3183,7 +3293,9 @@ $.Viewer = function( options ) {
srcGroup: resolveUrl( this.prefixUrl, navImages.home.GROUP ),
srcHover: resolveUrl( this.prefixUrl, navImages.home.HOVER ),
srcDown: resolveUrl( this.prefixUrl, navImages.home.DOWN ),
- onRelease: onHomeHandler
+ onRelease: onHomeHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
fullPage = new $.Button({
@@ -3194,7 +3306,9 @@ $.Viewer = function( options ) {
srcGroup: resolveUrl( this.prefixUrl, navImages.fullpage.GROUP ),
srcHover: resolveUrl( this.prefixUrl, navImages.fullpage.HOVER ),
srcDown: resolveUrl( this.prefixUrl, navImages.fullpage.DOWN ),
- onRelease: onFullPageHandler
+ onRelease: onFullPageHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
this.buttons = new $.ButtonGroup({
@@ -3214,29 +3328,12 @@ $.Viewer = function( options ) {
if( this.toolbar ){
this.toolbar = new $.ControlDock({ element: this.toolbar });
- this.toolbar.addControl( this.navControl );
+ this.toolbar.addControl( this.navControl, $.ControlAnchor.TOP_LEFT );
}else{
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,
@@ -3244,6 +3341,21 @@ $.Viewer = function( options ) {
);
}
+ //Instantiate a navigator if configured
+ if ( this.showNavigator ){
+ this.navigator = new $.Navigator({
+ id: this.navigatorElement,
+ position: this.navigatorPosition,
+ sizeRatio: this.navigatorSizeRatio,
+ height: this.navigatorHeight,
+ width: this.navigatorWidth,
+ tileSources: this.tileSources,
+ prefixUrl: this.prefixUrl,
+ overlays: this.overlays,
+ viewer: this
+ });
+ }
+
window.setTimeout( function(){
beginControlsAutoHide( _this );
}, 1 ); // initial fade out
@@ -3282,9 +3394,16 @@ $.Viewer = function( options ) {
} else if ( $.isArray( initialTileSource ) ){
//Legacy image pyramid
this.open( new $.LegacyTileSource( initialTileSource ) );
- } else if ( $.isFunction( initialTileSource ) ){
+ } else if ( $.isPlainObject( initialTileSource ) && $.isFunction( initialTileSource.getTileUrl ) ){
//Custom tile source
- customTileSource = new TileSource();
+ customTileSource = new $.TileSource(
+ initialTileSource.width,
+ initialTileSource.height,
+ initialTileSource.tileSize,
+ initialTileSource.tileOverlap,
+ initialTileSource.minLevel,
+ initialTileSource.maxLevel
+ );
customTileSource.getTileUrl = initialTileSource;
this.open( customTileSource );
}
@@ -3383,6 +3502,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
source: this.source,
viewport: this.viewport,
element: this.canvas,
+ overlays: this.overlays,
maxImageCacheCount: this.maxImageCacheCount,
imageLoaderLimit: this.imageLoaderLimit,
minZoomImageRatio: this.minZoomImageRatio,
@@ -3400,6 +3520,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
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 ];
@@ -3430,6 +3552,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
}
}
+ VIEWERS[ this.hash ] = this;
this.raiseEvent( "open" );
return this;
},
@@ -3445,6 +3568,10 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
this.drawer = null;
//this.profiler = null;
this.canvas.innerHTML = "";
+
+ VIEWERS[ this.hash ] = null;
+ delete VIEWERS[ this.hash ];
+
return this;
},
@@ -3475,7 +3602,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
* @return {Boolean}
*/
isDashboardEnabled: function () {
- return this.areControlsEnabled( enabled );
+ //TODO: why this indirection? these methods arent even implemented
+ return this.areControlsEnabled();
},
@@ -3485,6 +3613,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
* @return {OpenSeadragon.Viewer} Chainable.
*/
setDashboardEnabled: function( enabled ) {
+ //TODO: why this indirection? these methods arent even implemented
return this.setControlsEnabled( enabled );
},
@@ -3516,6 +3645,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
canvasStyle = this.canvas.style,
oldBounds,
newBounds,
+ viewer,
+ hash,
nodes,
i;
@@ -3559,7 +3690,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
//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;
+ this.toolbar.nextSibling = this.toolbar.element.nextSibling;
body.appendChild( this.toolbar.element );
//Make sure the user has some ability to style the toolbar based
@@ -3568,8 +3699,12 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
'class',
this.toolbar.element.className +" fullpage"
);
- }
+ this.toolbar.element.style.position = 'fixed';
+ this.container.style.top = $.getElementSize(
+ this.toolbar.element
+ ).y + 'px';
+ }
body.appendChild( this.container );
THIS[ this.hash ].prevContainerSize = $.getWindowSize();
@@ -3602,12 +3737,15 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
'class',
this.toolbar.element.className.replace('fullpage','')
);
+ this.toolbar.element.style.position = 'relative';
this.toolbar.parentNode.insertBefore(
this.toolbar.element,
- this.toolbar.previousSibling
+ this.toolbar.nextSibling
);
delete this.toolbar.parentNode;
- delete this.toolbar.previousSibling;
+ delete this.toolbar.nextSibling;
+
+ this.container.style.top = 'auto';
}
body.removeChild( this.container );
@@ -3643,11 +3781,20 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
null,
true
);
+ //Ensures that if multiple viewers are on a page, the viewers that
+ //were hidden during fullpage are 'reopened'
+ for( hash in VIEWERS ){
+ viewer = VIEWERS[ hash ];
+ if( viewer !== this && viewer != this.navigator ){
+ viewer.open( viewer.source );
+ }
+ }
}
THIS[ this.hash ].forceRedraw = true;
this.raiseEvent( "resize", this );
updateOnce( this );
+
}
return this;
},
@@ -3762,6 +3909,15 @@ function abortControlsAutoHide( viewer ) {
///////////////////////////////////////////////////////////////////////////////
// Default view event handlers.
///////////////////////////////////////////////////////////////////////////////
+function onFocus(){
+ abortControlsAutoHide( this );
+};
+
+function onBlur(){
+ beginControlsAutoHide( this );
+
+};
+
function onCanvasClick( tracker, position, quick, shift ) {
var zoomPreClick,
factor;
@@ -3995,23 +4151,25 @@ function onFullPage() {
$.Navigator = function( options ){
var _this = this,
- viewer = $.getElement( options.viewerId ),
- viewerSize = $.getElementSize( viewer );
+ viewer = options.viewer,
+ viewerSize = $.getElementSize( viewer.element );
//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.id = 'navigator-' + (+new Date());
+ this.element = $.makeNeutralElement( "div" );
+ this.element.id = options.id;
+ this.element.className = 'navigator';
}
options = $.extend( true, {
- navigatorSizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio
+ sizeRatio: $.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
+ minPixelRatio: 0,
showNavigator: false,
mouseNavEnabled: false,
showNavigationControl: false
@@ -4035,7 +4193,8 @@ $.Navigator = function( options ){
style.position = 'relative';
style.top = '0px';
style.left = '0px';
- style.border = '2px solid red';
+ style.border = '1px solid #900';
+ style.outline = '2px auto #900';
style.background = 'transparent';
style.float = 'left';
style.zIndex = 999999999;
@@ -4043,16 +4202,21 @@ $.Navigator = function( options ){
this.element.appendChild( this.displayRegion );
- $.Viewer.apply( this, [ options ] );
+ viewer.addControl(
+ this.element,
+ $.ControlAnchor.TOP_RIGHT
+ );
if( options.width && options.height ){
this.element.style.width = options.width + 'px';
this.element.style.height = options.height + 'px';
} else {
- this.element.style.width = ( viewerSize.x * options.navigatorSizeRatio ) + 'px';
- this.element.style.height = ( viewerSize.y * options.navigatorSizeRatio ) + 'px';
+ this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px';
+ this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px';
}
+ $.Viewer.apply( this, [ options ] );
+
};
$.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, {
@@ -4771,14 +4935,16 @@ $.Button = function( options ) {
onRelease: null,
onClick: null,
onEnter: null,
- onExit: null
+ onExit: null,
+ onFocus: null,
+ onBlur: null
}, options );
//TODO: make button elements accessible by making them a-tags
// maybe even consider basing them on the element and adding
// methods jquery-style.
- this.element = options.element || $.makeNeutralElement( "a" );
+ this.element = options.element || $.makeNeutralElement( "button" );
this.element.href = '#';
this.addHandler( "onPress", this.onPress );
@@ -4786,6 +4952,8 @@ $.Button = function( options ) {
this.addHandler( "onClick", this.onClick );
this.addHandler( "onEnter", this.onEnter );
this.addHandler( "onExit", this.onExit );
+ this.addHandler( "onFocus", this.onFocus );
+ this.addHandler( "onBlur", this.onBlur );
this.currentState = $.ButtonState.GROUP;
this.imgRest = $.makeTransparentImage( this.srcRest );
@@ -4846,6 +5014,11 @@ $.Button = function( options ) {
}
},
+ focusHandler: function( tracker, position, buttonDownElement, buttonDownAny ) {
+ this.enterHandler( tracker, position, buttonDownElement, buttonDownAny );
+ _this.raiseEvent( "onFocus", _this );
+ },
+
exitHandler: function( tracker, position, buttonDownElement, buttonDownAny ) {
outTo( _this, $.ButtonState.GROUP );
if ( buttonDownElement ) {
@@ -4853,6 +5026,11 @@ $.Button = function( options ) {
}
},
+ blurHandler: function( tracker, position, buttonDownElement, buttonDownAny ) {
+ this.exitHandler( tracker, position, buttonDownElement, buttonDownAny );
+ _this.raiseEvent( "onBlur", _this );
+ },
+
pressHandler: function( tracker, position ) {
inTo( _this, $.ButtonState.DOWN );
_this.raiseEvent( "onPress", _this );
@@ -4873,6 +5051,12 @@ $.Button = function( options ) {
if ( quick ) {
_this.raiseEvent("onClick", _this);
}
+ },
+
+ keyHandler: function( tracker, key ){
+ //console.log( "%s : handling key!", _this.tooltip);
+ _this.raiseEvent( "onClick", _this );
+ _this.raiseEvent( "onRelease", _this );
}
}).setTracking( true );
@@ -4922,7 +5106,7 @@ function updateFade( button ) {
opacity = 1.0 - deltaTime / button.fadeLength;
opacity = Math.min( 1.0, opacity );
opacity = Math.max( 0.0, opacity );
-
+
$.setElementOpacity( button.imgGroup, opacity, true );
if ( opacity > 0 ) {
// fade again
@@ -5024,13 +5208,17 @@ $.ButtonGroup = function( options ) {
_this = this,
i;
- this.element = options.group || $.makeNeutralElement( "span" );
+ this.element = options.group || $.makeNeutralElement( "fieldgroup" );
+ this.label = $.makeNeutralElement( "label" );
+ //TODO: support labels for ButtonGroups
+ //this.label.innerHTML = "test";
+
this.element.style.display = "inline-block";
+ this.element.appendChild( this.label );
for ( i = 0; i < buttons.length; i++ ) {
this.element.appendChild( buttons[ i ].element );
}
-
this.tracker = new $.MouseTracker({
element: this.element,
clickTimeThreshold: this.clickTimeThreshold,
@@ -5533,7 +5721,7 @@ $.Tile.prototype = {
* @class
*/
$.Overlay = function( element, location, placement ) {
- this.element = element;
+ this.element = element;
this.scales = location instanceof $.Rect;
this.bounds = new $.Rect(
location.x,
@@ -5726,7 +5914,9 @@ $.Drawer = function( options ) {
//backward compatibility for positional args while prefering more
//idiomatic javascript options object as the only argument
- var args = arguments;
+ var args = arguments,
+ i;
+
if( !$.isPlainObject( options ) ){
options = {
source: args[ 0 ],
@@ -5736,21 +5926,20 @@ $.Drawer = function( options ) {
}
$.extend( true, this, {
- //references to closely related openseadragon objects
- //viewport: null,
- //source: null,
//internal state properties
downloading: 0,
tilesMatrix: {},
tilesLoaded: [],
coverage: {},
- overlays: [],
lastDrawn: [],
lastResetTime: 0,
midUpdate: false,
updateAgain: true,
+ //internal state / configurable settings
+ overlays: [],
+
//configurable settings
maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount,
imageLoaderLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
@@ -5779,6 +5968,37 @@ $.Drawer = function( options ) {
this.container.style.textAlign = "left";
this.container.appendChild( this.canvas );
+ //create the correct type of overlay by convention if the overlays
+ //are not already OpenSeadragon.Overlays
+ for( i = 0; i < this.overlays.length; i++ ){
+ if( $.isPlainObject( this.overlays[ i ] ) ){
+
+ (function( _this, overlay ){
+
+ var link = document.createElement("a"),
+ rect = new $.Rect(
+ overlay.x,
+ overlay.y,
+ overlay.width,
+ overlay.height
+ ),
+ id = Math.floor(Math.random()*10000000);
+
+ link.href = "#/overlay/"+id;
+ link.id = id;
+ link.className = overlay.class ?
+ overlay.class :
+ "openseadragon-overlay";
+
+ _this.overlays[ i ] = new $.Overlay( link, rect );
+
+ }( this, this.overlays[ i ] ));
+
+ } else if ( $.isFunction( this.overlays[ i ] ) ){
+
+ }
+ }
+
//this.profiler = new $.Profiler();
};
diff --git a/src/button.js b/src/button.js
index 340302fd..9e42c32d 100644
--- a/src/button.js
+++ b/src/button.js
@@ -70,14 +70,16 @@ $.Button = function( options ) {
onRelease: null,
onClick: null,
onEnter: null,
- onExit: null
+ onExit: null,
+ onFocus: null,
+ onBlur: null
}, options );
//TODO: make button elements accessible by making them a-tags
// maybe even consider basing them on the element and adding
// methods jquery-style.
- this.element = options.element || $.makeNeutralElement( "a" );
+ this.element = options.element || $.makeNeutralElement( "button" );
this.element.href = '#';
this.addHandler( "onPress", this.onPress );
@@ -85,6 +87,8 @@ $.Button = function( options ) {
this.addHandler( "onClick", this.onClick );
this.addHandler( "onEnter", this.onEnter );
this.addHandler( "onExit", this.onExit );
+ this.addHandler( "onFocus", this.onFocus );
+ this.addHandler( "onBlur", this.onBlur );
this.currentState = $.ButtonState.GROUP;
this.imgRest = $.makeTransparentImage( this.srcRest );
@@ -145,6 +149,11 @@ $.Button = function( options ) {
}
},
+ focusHandler: function( tracker, position, buttonDownElement, buttonDownAny ) {
+ this.enterHandler( tracker, position, buttonDownElement, buttonDownAny );
+ _this.raiseEvent( "onFocus", _this );
+ },
+
exitHandler: function( tracker, position, buttonDownElement, buttonDownAny ) {
outTo( _this, $.ButtonState.GROUP );
if ( buttonDownElement ) {
@@ -152,6 +161,11 @@ $.Button = function( options ) {
}
},
+ blurHandler: function( tracker, position, buttonDownElement, buttonDownAny ) {
+ this.exitHandler( tracker, position, buttonDownElement, buttonDownAny );
+ _this.raiseEvent( "onBlur", _this );
+ },
+
pressHandler: function( tracker, position ) {
inTo( _this, $.ButtonState.DOWN );
_this.raiseEvent( "onPress", _this );
@@ -172,6 +186,12 @@ $.Button = function( options ) {
if ( quick ) {
_this.raiseEvent("onClick", _this);
}
+ },
+
+ keyHandler: function( tracker, key ){
+ //console.log( "%s : handling key!", _this.tooltip);
+ _this.raiseEvent( "onClick", _this );
+ _this.raiseEvent( "onRelease", _this );
}
}).setTracking( true );
@@ -221,7 +241,7 @@ function updateFade( button ) {
opacity = 1.0 - deltaTime / button.fadeLength;
opacity = Math.min( 1.0, opacity );
opacity = Math.max( 0.0, opacity );
-
+
$.setElementOpacity( button.imgGroup, opacity, true );
if ( opacity > 0 ) {
// fade again
diff --git a/src/buttongroup.js b/src/buttongroup.js
index e7c288d9..b35b671d 100644
--- a/src/buttongroup.js
+++ b/src/buttongroup.js
@@ -34,13 +34,17 @@ $.ButtonGroup = function( options ) {
_this = this,
i;
- this.element = options.group || $.makeNeutralElement( "span" );
+ this.element = options.group || $.makeNeutralElement( "fieldgroup" );
+ this.label = $.makeNeutralElement( "label" );
+ //TODO: support labels for ButtonGroups
+ //this.label.innerHTML = "test";
+
this.element.style.display = "inline-block";
+ this.element.appendChild( this.label );
for ( i = 0; i < buttons.length; i++ ) {
this.element.appendChild( buttons[ i ].element );
}
-
this.tracker = new $.MouseTracker({
element: this.element,
clickTimeThreshold: this.clickTimeThreshold,
diff --git a/src/controldock.js b/src/controldock.js
index 0415bbe9..d02709ed 100644
--- a/src/controldock.js
+++ b/src/controldock.js
@@ -13,7 +13,7 @@
$.extend( true, this, {
id: 'controldock-'+(+new Date())+'-'+Math.floor(Math.random()*1000000),
- container: $.makeNeutralElement('div'),
+ container: $.makeNeutralElement('form'),
controls: []
}, options );
diff --git a/src/drawer.js b/src/drawer.js
index 62a78e3c..fd6605ff 100644
--- a/src/drawer.js
+++ b/src/drawer.js
@@ -45,7 +45,9 @@ $.Drawer = function( options ) {
//backward compatibility for positional args while prefering more
//idiomatic javascript options object as the only argument
- var args = arguments;
+ var args = arguments,
+ i;
+
if( !$.isPlainObject( options ) ){
options = {
source: args[ 0 ],
@@ -55,21 +57,20 @@ $.Drawer = function( options ) {
}
$.extend( true, this, {
- //references to closely related openseadragon objects
- //viewport: null,
- //source: null,
//internal state properties
downloading: 0,
tilesMatrix: {},
tilesLoaded: [],
coverage: {},
- overlays: [],
lastDrawn: [],
lastResetTime: 0,
midUpdate: false,
updateAgain: true,
+ //internal state / configurable settings
+ overlays: [],
+
//configurable settings
maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount,
imageLoaderLimit: $.DEFAULT_SETTINGS.imageLoaderLimit,
@@ -98,6 +99,37 @@ $.Drawer = function( options ) {
this.container.style.textAlign = "left";
this.container.appendChild( this.canvas );
+ //create the correct type of overlay by convention if the overlays
+ //are not already OpenSeadragon.Overlays
+ for( i = 0; i < this.overlays.length; i++ ){
+ if( $.isPlainObject( this.overlays[ i ] ) ){
+
+ (function( _this, overlay ){
+
+ var link = document.createElement("a"),
+ rect = new $.Rect(
+ overlay.x,
+ overlay.y,
+ overlay.width,
+ overlay.height
+ ),
+ id = Math.floor(Math.random()*10000000);
+
+ link.href = "#/overlay/"+id;
+ link.id = id;
+ link.className = overlay.class ?
+ overlay.class :
+ "openseadragon-overlay";
+
+ _this.overlays[ i ] = new $.Overlay( link, rect );
+
+ }( this, this.overlays[ i ] ));
+
+ } else if ( $.isFunction( this.overlays[ i ] ) ){
+
+ }
+ }
+
//this.profiler = new $.Profiler();
};
diff --git a/src/mousetracker.js b/src/mousetracker.js
index b85438b4..9f931bbf 100644
--- a/src/mousetracker.js
+++ b/src/mousetracker.js
@@ -81,6 +81,9 @@
this.scrollHandler = options.scrollHandler || null;
this.clickHandler = options.clickHandler || null;
this.dragHandler = options.dragHandler || null;
+ this.keyHandler = options.keyHandler || null;
+ this.focusHandler = options.focusHandler || null;
+ this.blurHandler = options.blurHandler || null;
//Store private properties in a scope sealed hash map
var _this = this;
@@ -118,6 +121,9 @@
"touchstart": function( event ){ onTouchStart( _this, event ); },
"touchmove": function( event ){ onTouchMove( _this, event ); },
"touchend": function( event ){ onTouchEnd( _this, event ); },
+ "keypress": function( event ){ onKeyPress( _this, event ); },
+ "focus": function( event ){ onFocus( _this, event ); },
+ "blur": function( event ){ onBlur( _this, event ); },
tracking: false,
capturing: false,
buttonDown: false,
@@ -263,8 +269,24 @@
* @param {Boolean} shift
* Was the shift key being pressed during this event?
*/
- dragHandler: function(){}
+ dragHandler: function(){},
+ /**
+ * Implement or assign implmentation to these handlers during or after
+ * calling the constructor.
+ * @function
+ * @param {OpenSeadragon.MouseTracker} tracker
+ * A reference to the tracker instance.
+ * @param {Number} keyCode
+ * The key code that was pressed.
+ * @param {Boolean} shift
+ * Was the shift key being pressed during this event?
+ */
+ keyHandler: function(){},
+
+ focusHandler: function(){},
+
+ blurHandler: function(){}
};
/**
@@ -277,7 +299,9 @@
"mouseover", "mouseout", "mousedown", "mouseup",
"click",
"DOMMouseScroll", "mousewheel",
- "touchstart", "touchmove", "touchend"
+ "touchstart", "touchmove", "touchend",
+ "keypress",
+ "focus", "blur"
],
delegate = THIS[ tracker.hash ],
event,
@@ -308,7 +332,9 @@
"mouseover", "mouseout", "mousedown", "mouseup",
"click",
"DOMMouseScroll", "mousewheel",
- "touchstart", "touchmove", "touchend"
+ "touchstart", "touchmove", "touchend",
+ "keypress",
+ "focus", "blur"
],
delegate = THIS[ tracker.hash ],
event,
@@ -447,6 +473,81 @@
};
+ /**
+ * @private
+ * @inner
+ */
+ function onFocus( tracker, event ){
+ console.log( "focus %s", event );
+ if ( tracker.focusHandler ) {
+ try {
+ tracker.focusHandler(
+ tracker,
+ event
+ );
+ $.cancelEvent( event );
+ } catch ( e ) {
+ $.console.error(
+ "%s while executing key handler: %s",
+ e.name,
+ e.message,
+ e
+ );
+ }
+ }
+ };
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function onBlur( tracker, event ){
+ console.log( "blur %s", event );
+ if ( tracker.blurHandler ) {
+ try {
+ tracker.blurHandler(
+ tracker,
+ event
+ );
+ $.cancelEvent( event );
+ } catch ( e ) {
+ $.console.error(
+ "%s while executing key handler: %s",
+ e.name,
+ e.message,
+ e
+ );
+ }
+ }
+ };
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function onKeyPress( tracker, event ){
+ //console.log( "keypress %s", event.keyCode );
+ if ( tracker.keyHandler ) {
+ try {
+ tracker.keyHandler(
+ tracker,
+ event.keyCode
+ );
+ $.cancelEvent( event );
+ } catch ( e ) {
+ $.console.error(
+ "%s while executing key handler: %s",
+ e.name,
+ e.message,
+ e
+ );
+ }
+ }
+ };
+
+
/**
* @private
* @inner
diff --git a/src/navigator.js b/src/navigator.js
index a1ecdcf8..58ba8e80 100644
--- a/src/navigator.js
+++ b/src/navigator.js
@@ -7,23 +7,25 @@
$.Navigator = function( options ){
var _this = this,
- viewer = $.getElement( options.viewerId ),
- viewerSize = $.getElementSize( viewer );
+ viewer = options.viewer,
+ viewerSize = $.getElementSize( viewer.element );
//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.id = 'navigator-' + (+new Date());
+ this.element = $.makeNeutralElement( "div" );
+ this.element.id = options.id;
+ this.element.className = 'navigator';
}
options = $.extend( true, {
- navigatorSizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio
+ sizeRatio: $.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
+ minPixelRatio: 0,
showNavigator: false,
mouseNavEnabled: false,
showNavigationControl: false
@@ -47,7 +49,8 @@ $.Navigator = function( options ){
style.position = 'relative';
style.top = '0px';
style.left = '0px';
- style.border = '2px solid red';
+ style.border = '1px solid #900';
+ style.outline = '2px auto #900';
style.background = 'transparent';
style.float = 'left';
style.zIndex = 999999999;
@@ -55,16 +58,21 @@ $.Navigator = function( options ){
this.element.appendChild( this.displayRegion );
- $.Viewer.apply( this, [ options ] );
+ viewer.addControl(
+ this.element,
+ $.ControlAnchor.TOP_RIGHT
+ );
if( options.width && options.height ){
this.element.style.width = options.width + 'px';
this.element.style.height = options.height + 'px';
} else {
- this.element.style.width = ( viewerSize.x * options.navigatorSizeRatio ) + 'px';
- this.element.style.height = ( viewerSize.y * options.navigatorSizeRatio ) + 'px';
+ this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px';
+ this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px';
}
+ $.Viewer.apply( this, [ options ] );
+
};
$.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, {
diff --git a/src/overlay.js b/src/overlay.js
index 0a276524..11557bcb 100644
--- a/src/overlay.js
+++ b/src/overlay.js
@@ -24,7 +24,7 @@
* @class
*/
$.Overlay = function( element, location, placement ) {
- this.element = element;
+ this.element = element;
this.scales = location instanceof $.Rect;
this.bounds = new $.Rect(
location.x,
diff --git a/src/viewer.js b/src/viewer.js
index 04b24480..9b4e7157 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -2,7 +2,10 @@
(function( $ ){
// dictionary from hash to private properties
-var THIS = {};
+var THIS = {},
+// We keep a list of viewers so we can 'wake-up' each viewer on
+// a page after toggling between fullpage modes
+ VIEWERS = {};
/**
*
@@ -170,6 +173,8 @@ $.Viewer = function( options ) {
doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ),
onHomeHandler = $.delegate( this, onHome ),
onFullPageHandler = $.delegate( this, onFullPage ),
+ onFocusHandler = $.delegate( this, onFocus ),
+ onBlurHandler = $.delegate( this, onBlur ),
navImages = this.navImages,
zoomIn,
zoomOut,
@@ -190,7 +195,9 @@ $.Viewer = function( options ) {
onRelease: endZoomingHandler,
onClick: doSingleZoomInHandler,
onEnter: beginZoomingInHandler,
- onExit: endZoomingHandler
+ onExit: endZoomingHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
zoomOut = new $.Button({
@@ -205,7 +212,9 @@ $.Viewer = function( options ) {
onRelease: endZoomingHandler,
onClick: doSingleZoomOutHandler,
onEnter: beginZoomingOutHandler,
- onExit: endZoomingHandler
+ onExit: endZoomingHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
goHome = new $.Button({
@@ -216,7 +225,9 @@ $.Viewer = function( options ) {
srcGroup: resolveUrl( this.prefixUrl, navImages.home.GROUP ),
srcHover: resolveUrl( this.prefixUrl, navImages.home.HOVER ),
srcDown: resolveUrl( this.prefixUrl, navImages.home.DOWN ),
- onRelease: onHomeHandler
+ onRelease: onHomeHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
fullPage = new $.Button({
@@ -227,7 +238,9 @@ $.Viewer = function( options ) {
srcGroup: resolveUrl( this.prefixUrl, navImages.fullpage.GROUP ),
srcHover: resolveUrl( this.prefixUrl, navImages.fullpage.HOVER ),
srcDown: resolveUrl( this.prefixUrl, navImages.fullpage.DOWN ),
- onRelease: onFullPageHandler
+ onRelease: onFullPageHandler,
+ onFocus: onFocusHandler,
+ onBlur: onBlurHandler
});
this.buttons = new $.ButtonGroup({
@@ -247,29 +260,12 @@ $.Viewer = function( options ) {
if( this.toolbar ){
this.toolbar = new $.ControlDock({ element: this.toolbar });
- this.toolbar.addControl( this.navControl );
+ this.toolbar.addControl( this.navControl, $.ControlAnchor.TOP_LEFT );
}else{
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,
@@ -277,6 +273,21 @@ $.Viewer = function( options ) {
);
}
+ //Instantiate a navigator if configured
+ if ( this.showNavigator ){
+ this.navigator = new $.Navigator({
+ id: this.navigatorElement,
+ position: this.navigatorPosition,
+ sizeRatio: this.navigatorSizeRatio,
+ height: this.navigatorHeight,
+ width: this.navigatorWidth,
+ tileSources: this.tileSources,
+ prefixUrl: this.prefixUrl,
+ overlays: this.overlays,
+ viewer: this
+ });
+ }
+
window.setTimeout( function(){
beginControlsAutoHide( _this );
}, 1 ); // initial fade out
@@ -315,9 +326,16 @@ $.Viewer = function( options ) {
} else if ( $.isArray( initialTileSource ) ){
//Legacy image pyramid
this.open( new $.LegacyTileSource( initialTileSource ) );
- } else if ( $.isFunction( initialTileSource ) ){
+ } else if ( $.isPlainObject( initialTileSource ) && $.isFunction( initialTileSource.getTileUrl ) ){
//Custom tile source
- customTileSource = new TileSource();
+ customTileSource = new $.TileSource(
+ initialTileSource.width,
+ initialTileSource.height,
+ initialTileSource.tileSize,
+ initialTileSource.tileOverlap,
+ initialTileSource.minLevel,
+ initialTileSource.maxLevel
+ );
customTileSource.getTileUrl = initialTileSource;
this.open( customTileSource );
}
@@ -416,6 +434,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
source: this.source,
viewport: this.viewport,
element: this.canvas,
+ overlays: this.overlays,
maxImageCacheCount: this.maxImageCacheCount,
imageLoaderLimit: this.imageLoaderLimit,
minZoomImageRatio: this.minZoomImageRatio,
@@ -433,6 +452,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
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 ];
@@ -463,6 +484,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
}
}
+ VIEWERS[ this.hash ] = this;
this.raiseEvent( "open" );
return this;
},
@@ -478,6 +500,10 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
this.drawer = null;
//this.profiler = null;
this.canvas.innerHTML = "";
+
+ VIEWERS[ this.hash ] = null;
+ delete VIEWERS[ this.hash ];
+
return this;
},
@@ -508,7 +534,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
* @return {Boolean}
*/
isDashboardEnabled: function () {
- return this.areControlsEnabled( enabled );
+ //TODO: why this indirection? these methods arent even implemented
+ return this.areControlsEnabled();
},
@@ -518,6 +545,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
* @return {OpenSeadragon.Viewer} Chainable.
*/
setDashboardEnabled: function( enabled ) {
+ //TODO: why this indirection? these methods arent even implemented
return this.setControlsEnabled( enabled );
},
@@ -549,6 +577,8 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
canvasStyle = this.canvas.style,
oldBounds,
newBounds,
+ viewer,
+ hash,
nodes,
i;
@@ -592,7 +622,7 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
//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;
+ this.toolbar.nextSibling = this.toolbar.element.nextSibling;
body.appendChild( this.toolbar.element );
//Make sure the user has some ability to style the toolbar based
@@ -601,8 +631,12 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
'class',
this.toolbar.element.className +" fullpage"
);
- }
+ this.toolbar.element.style.position = 'fixed';
+ this.container.style.top = $.getElementSize(
+ this.toolbar.element
+ ).y + 'px';
+ }
body.appendChild( this.container );
THIS[ this.hash ].prevContainerSize = $.getWindowSize();
@@ -635,12 +669,15 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
'class',
this.toolbar.element.className.replace('fullpage','')
);
+ this.toolbar.element.style.position = 'relative';
this.toolbar.parentNode.insertBefore(
this.toolbar.element,
- this.toolbar.previousSibling
+ this.toolbar.nextSibling
);
delete this.toolbar.parentNode;
- delete this.toolbar.previousSibling;
+ delete this.toolbar.nextSibling;
+
+ this.container.style.top = 'auto';
}
body.removeChild( this.container );
@@ -676,11 +713,20 @@ $.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype,
null,
true
);
+ //Ensures that if multiple viewers are on a page, the viewers that
+ //were hidden during fullpage are 'reopened'
+ for( hash in VIEWERS ){
+ viewer = VIEWERS[ hash ];
+ if( viewer !== this && viewer != this.navigator ){
+ viewer.open( viewer.source );
+ }
+ }
}
THIS[ this.hash ].forceRedraw = true;
this.raiseEvent( "resize", this );
updateOnce( this );
+
}
return this;
},
@@ -795,6 +841,15 @@ function abortControlsAutoHide( viewer ) {
///////////////////////////////////////////////////////////////////////////////
// Default view event handlers.
///////////////////////////////////////////////////////////////////////////////
+function onFocus(){
+ abortControlsAutoHide( this );
+};
+
+function onBlur(){
+ beginControlsAutoHide( this );
+
+};
+
function onCanvasClick( tracker, position, quick, shift ) {
var zoomPreClick,
factor;