Merge from master
@ -12,6 +12,9 @@ OPENSEADRAGON CHANGELOG
|
|||||||
* Added 'ABSOLUTE' as a navigatorPosition option, along with corresponding navigatorTop, navigatorLeft options. Allows the navigator minimap to be placed anywhere in the viewer (#310)
|
* Added 'ABSOLUTE' as a navigatorPosition option, along with corresponding navigatorTop, navigatorLeft options. Allows the navigator minimap to be placed anywhere in the viewer (#310)
|
||||||
* Enhanced the navigatorTop, navigatorLeft, navigatorHeight, and navigatorWidth options to allow a number for pixel units or a string for other element units (%, em, etc.) (#310)
|
* Enhanced the navigatorTop, navigatorLeft, navigatorHeight, and navigatorWidth options to allow a number for pixel units or a string for other element units (%, em, etc.) (#310)
|
||||||
* Additional enhancements for IIIF support (#315)
|
* Additional enhancements for IIIF support (#315)
|
||||||
|
* Fixed: Setting degrees in Viewer constructor has no effect (#336)
|
||||||
|
* Added pre-draw event for tiles to allow applications to alter the image (#348)
|
||||||
|
* Added optional Rotate Left/Right buttons to standard controls (#341)
|
||||||
|
|
||||||
1.0.0:
|
1.0.0:
|
||||||
|
|
||||||
|
BIN
images/rotateleft_grouphover.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/rotateleft_hover.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
images/rotateleft_pressed.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
images/rotateleft_rest.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/rotateright_grouphover.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
images/rotateright_hover.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
images/rotateright_pressed.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
images/rotateright_rest.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
@ -1017,6 +1017,25 @@ function drawTiles( drawer, lastDrawn ){
|
|||||||
tileSource,
|
tileSource,
|
||||||
collectionTileSource;
|
collectionTileSource;
|
||||||
|
|
||||||
|
// We need a callback to give image manipulation a chance to happen
|
||||||
|
var drawingHandler = function(args) {
|
||||||
|
if (drawer.viewer) {
|
||||||
|
/**
|
||||||
|
* This event is fired just before the tile is drawn giving the application a chance to alter the image.
|
||||||
|
*
|
||||||
|
* NOTE: This event is only fired when the drawer is using a <canvas>.
|
||||||
|
*
|
||||||
|
* @event tile-drawing
|
||||||
|
* @memberof OpenSeadragon.Viewer
|
||||||
|
* @type {object}
|
||||||
|
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||||
|
* @property {OpenSeadragon.Tile} tile
|
||||||
|
* @property {?Object} userData - 'context', 'tile' and 'rendered'.
|
||||||
|
*/
|
||||||
|
drawer.viewer.raiseEvent('tile-drawing', args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for ( i = lastDrawn.length - 1; i >= 0; i-- ) {
|
for ( i = lastDrawn.length - 1; i >= 0; i-- ) {
|
||||||
tile = lastDrawn[ i ];
|
tile = lastDrawn[ i ];
|
||||||
|
|
||||||
@ -1088,10 +1107,10 @@ function drawTiles( drawer, lastDrawn ){
|
|||||||
// specifically, don't save,rotate,restore every time we draw a tile
|
// specifically, don't save,rotate,restore every time we draw a tile
|
||||||
if( drawer.viewport.degrees !== 0 ) {
|
if( drawer.viewport.degrees !== 0 ) {
|
||||||
offsetForRotation( tile, drawer.canvas, drawer.context, drawer.viewport.degrees );
|
offsetForRotation( tile, drawer.canvas, drawer.context, drawer.viewport.degrees );
|
||||||
tile.drawCanvas( drawer.context );
|
tile.drawCanvas( drawer.context, drawingHandler );
|
||||||
restoreRotationChanges( tile, drawer.canvas, drawer.context );
|
restoreRotationChanges( tile, drawer.canvas, drawer.context );
|
||||||
} else {
|
} else {
|
||||||
tile.drawCanvas( drawer.context );
|
tile.drawCanvas( drawer.context, drawingHandler );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tile.drawHTML( drawer.canvas );
|
tile.drawHTML( drawer.canvas );
|
||||||
|
@ -358,6 +358,11 @@
|
|||||||
* image and if the 'next' button will wrap to the first image when viewing
|
* image and if the 'next' button will wrap to the first image when viewing
|
||||||
* the last image.
|
* the last image.
|
||||||
*
|
*
|
||||||
|
* @property {Boolean} [showRotationControl=false]
|
||||||
|
* If true then the rotate left/right controls will be displayed as part of the
|
||||||
|
* standard controls. This is also subject to the browser support for rotate
|
||||||
|
* (e.g. viewer.drawer.canRotate()).
|
||||||
|
*
|
||||||
* @property {Boolean} [showSequenceControl=true]
|
* @property {Boolean} [showSequenceControl=true]
|
||||||
* If the viewer has been configured with a sequence of tile sources, then
|
* If the viewer has been configured with a sequence of tile sources, then
|
||||||
* provide buttons for navigating forward and backward through the images.
|
* provide buttons for navigating forward and backward through the images.
|
||||||
@ -815,6 +820,18 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
|||||||
HOVER: 'fullpage_hover.png',
|
HOVER: 'fullpage_hover.png',
|
||||||
DOWN: 'fullpage_pressed.png'
|
DOWN: 'fullpage_pressed.png'
|
||||||
},
|
},
|
||||||
|
rotateleft: {
|
||||||
|
REST: 'rotateleft_rest.png',
|
||||||
|
GROUP: 'rotateleft_grouphover.png',
|
||||||
|
HOVER: 'rotateleft_hover.png',
|
||||||
|
DOWN: 'rotateleft_pressed.png'
|
||||||
|
},
|
||||||
|
rotateright: {
|
||||||
|
REST: 'rotateright_rest.png',
|
||||||
|
GROUP: 'rotateright_grouphover.png',
|
||||||
|
HOVER: 'rotateright_hover.png',
|
||||||
|
DOWN: 'rotateright_pressed.png'
|
||||||
|
},
|
||||||
previous: {
|
previous: {
|
||||||
REST: 'previous_rest.png',
|
REST: 'previous_rest.png',
|
||||||
GROUP: 'previous_grouphover.png',
|
GROUP: 'previous_grouphover.png',
|
||||||
@ -829,6 +846,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
navPrevNextWrap: false,
|
navPrevNextWrap: false,
|
||||||
|
showRotationControl: false,
|
||||||
|
|
||||||
//DEVELOPER SETTINGS
|
//DEVELOPER SETTINGS
|
||||||
debugMode: false,
|
debugMode: false,
|
||||||
|
@ -55,7 +55,9 @@ var I18N = {
|
|||||||
ZoomIn: "Zoom in",
|
ZoomIn: "Zoom in",
|
||||||
ZoomOut: "Zoom out",
|
ZoomOut: "Zoom out",
|
||||||
NextPage: "Next page",
|
NextPage: "Next page",
|
||||||
PreviousPage: "Previous page"
|
PreviousPage: "Previous page",
|
||||||
|
RotateLeft: "Rotate left",
|
||||||
|
RotateRight: "Rotate right"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -231,8 +231,10 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
|||||||
* Renders the tile in a canvas-based context.
|
* Renders the tile in a canvas-based context.
|
||||||
* @function
|
* @function
|
||||||
* @param {Canvas} context
|
* @param {Canvas} context
|
||||||
|
* @param {Function} method for firing the drawing event. drawingHandler({context, tile, rendered})
|
||||||
|
* where <code>rendered</code> is the context with the pre-drawn image.
|
||||||
*/
|
*/
|
||||||
drawCanvas: function( context ) {
|
drawCanvas: function( context, drawingHandler ) {
|
||||||
|
|
||||||
var position = this.position,
|
var position = this.position,
|
||||||
size = this.size,
|
size = this.size,
|
||||||
@ -280,6 +282,9 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
|||||||
|
|
||||||
rendered = TILE_CACHE[ this.url ];
|
rendered = TILE_CACHE[ this.url ];
|
||||||
|
|
||||||
|
// This gives the application a chance to make image manipulation changes as we are rendering the image
|
||||||
|
drawingHandler({context: context, tile: this, rendered: rendered});
|
||||||
|
|
||||||
//rendered.save();
|
//rendered.save();
|
||||||
context.drawImage(
|
context.drawImage(
|
||||||
rendered.canvas,
|
rendered.canvas,
|
||||||
|
@ -1150,6 +1150,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ),
|
doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ),
|
||||||
onHomeHandler = $.delegate( this, onHome ),
|
onHomeHandler = $.delegate( this, onHome ),
|
||||||
onFullScreenHandler = $.delegate( this, onFullScreen ),
|
onFullScreenHandler = $.delegate( this, onFullScreen ),
|
||||||
|
onRotateLeftHandler = $.delegate( this, onRotateLeft ),
|
||||||
|
onRotateRightHandler = $.delegate( this, onRotateRight ),
|
||||||
onFocusHandler = $.delegate( this, onFocus ),
|
onFocusHandler = $.delegate( this, onFocus ),
|
||||||
onBlurHandler = $.delegate( this, onBlur ),
|
onBlurHandler = $.delegate( this, onBlur ),
|
||||||
navImages = this.navImages,
|
navImages = this.navImages,
|
||||||
@ -1229,6 +1231,37 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
onBlur: onBlurHandler
|
onBlur: onBlurHandler
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
if (this.showRotationControl) {
|
||||||
|
buttons.push( this.rotateLeft = new $.Button({
|
||||||
|
element: this.rotateLeftButton ? $.getElement( this.rotateLeftButton ) : null,
|
||||||
|
clickTimeThreshold: this.clickTimeThreshold,
|
||||||
|
clickDistThreshold: this.clickDistThreshold,
|
||||||
|
tooltip: $.getString( "Tooltips.RotateLeft" ),
|
||||||
|
srcRest: resolveUrl( this.prefixUrl, navImages.rotateleft.REST ),
|
||||||
|
srcGroup: resolveUrl( this.prefixUrl, navImages.rotateleft.GROUP ),
|
||||||
|
srcHover: resolveUrl( this.prefixUrl, navImages.rotateleft.HOVER ),
|
||||||
|
srcDown: resolveUrl( this.prefixUrl, navImages.rotateleft.DOWN ),
|
||||||
|
onRelease: onRotateLeftHandler,
|
||||||
|
onFocus: onFocusHandler,
|
||||||
|
onBlur: onBlurHandler
|
||||||
|
}));
|
||||||
|
|
||||||
|
buttons.push( this.rotateRight = new $.Button({
|
||||||
|
element: this.rotateRightButton ? $.getElement( this.rotateRightButton ) : null,
|
||||||
|
clickTimeThreshold: this.clickTimeThreshold,
|
||||||
|
clickDistThreshold: this.clickDistThreshold,
|
||||||
|
tooltip: $.getString( "Tooltips.RotateRight" ),
|
||||||
|
srcRest: resolveUrl( this.prefixUrl, navImages.rotateright.REST ),
|
||||||
|
srcGroup: resolveUrl( this.prefixUrl, navImages.rotateright.GROUP ),
|
||||||
|
srcHover: resolveUrl( this.prefixUrl, navImages.rotateright.HOVER ),
|
||||||
|
srcDown: resolveUrl( this.prefixUrl, navImages.rotateright.DOWN ),
|
||||||
|
onRelease: onRotateRightHandler,
|
||||||
|
onFocus: onFocusHandler,
|
||||||
|
onBlur: onBlurHandler
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if( useGroup ){
|
if( useGroup ){
|
||||||
this.buttons = new $.ButtonGroup({
|
this.buttons = new $.ButtonGroup({
|
||||||
buttons: buttons,
|
buttons: buttons,
|
||||||
@ -1550,7 +1583,8 @@ function _getSafeElemSize (oElement) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function openTileSource( viewer, source ) {
|
function openTileSource( viewer, source ) {
|
||||||
var _this = viewer;
|
var i,
|
||||||
|
_this = viewer;
|
||||||
|
|
||||||
if ( _this.source ) {
|
if ( _this.source ) {
|
||||||
_this.close( );
|
_this.close( );
|
||||||
@ -1578,7 +1612,8 @@ function openTileSource( viewer, source ) {
|
|||||||
showNavigator: false,
|
showNavigator: false,
|
||||||
minZoomImageRatio: 1,
|
minZoomImageRatio: 1,
|
||||||
maxZoomPixelRatio: 1,
|
maxZoomPixelRatio: 1,
|
||||||
viewer: _this //,
|
viewer: _this,
|
||||||
|
degrees: _this.degrees //,
|
||||||
//TODO: figure out how to support these in a way that makes sense
|
//TODO: figure out how to support these in a way that makes sense
|
||||||
//minZoomLevel: this.minZoomLevel,
|
//minZoomLevel: this.minZoomLevel,
|
||||||
//maxZoomLevel: this.maxZoomLevel
|
//maxZoomLevel: this.maxZoomLevel
|
||||||
@ -1600,7 +1635,8 @@ function openTileSource( viewer, source ) {
|
|||||||
defaultZoomLevel: _this.defaultZoomLevel,
|
defaultZoomLevel: _this.defaultZoomLevel,
|
||||||
minZoomLevel: _this.minZoomLevel,
|
minZoomLevel: _this.minZoomLevel,
|
||||||
maxZoomLevel: _this.maxZoomLevel,
|
maxZoomLevel: _this.maxZoomLevel,
|
||||||
viewer: _this
|
viewer: _this,
|
||||||
|
degrees: _this.degrees
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1629,6 +1665,21 @@ function openTileSource( viewer, source ) {
|
|||||||
debugGridColor: _this.debugGridColor
|
debugGridColor: _this.debugGridColor
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons
|
||||||
|
if (!_this.drawer.canRotate()) {
|
||||||
|
// Disable/remove the rotate left/right buttons since they aren't supported
|
||||||
|
if (_this.rotateLeft) {
|
||||||
|
i = _this.buttons.buttons.indexOf(_this.rotateLeft);
|
||||||
|
_this.buttons.buttons.splice(i, 1);
|
||||||
|
_this.buttons.element.removeChild(_this.rotateLeft.element);
|
||||||
|
}
|
||||||
|
if (_this.rotateRight) {
|
||||||
|
i = _this.buttons.buttons.indexOf(_this.rotateRight);
|
||||||
|
_this.buttons.buttons.splice(i, 1);
|
||||||
|
_this.buttons.element.removeChild(_this.rotateRight.element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Instantiate a navigator if configured
|
//Instantiate a navigator if configured
|
||||||
if ( _this.showNavigator && !_this.collectionMode ){
|
if ( _this.showNavigator && !_this.collectionMode ){
|
||||||
// Note: By passing the fully parsed source, the navigator doesn't
|
// Note: By passing the fully parsed source, the navigator doesn't
|
||||||
@ -2335,6 +2386,38 @@ function onFullScreen() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: The current rotation feature is limited to 90 degree turns.
|
||||||
|
*/
|
||||||
|
function onRotateLeft() {
|
||||||
|
if ( this.viewport ) {
|
||||||
|
var currRotation = this.viewport.getRotation();
|
||||||
|
if (currRotation === 0) {
|
||||||
|
currRotation = 270;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currRotation -= 90;
|
||||||
|
}
|
||||||
|
this.viewport.setRotation(currRotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note: The current rotation feature is limited to 90 degree turns.
|
||||||
|
*/
|
||||||
|
function onRotateRight() {
|
||||||
|
if ( this.viewport ) {
|
||||||
|
var currRotation = this.viewport.getRotation();
|
||||||
|
if (currRotation === 270) {
|
||||||
|
currRotation = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currRotation += 90;
|
||||||
|
}
|
||||||
|
this.viewport.setRotation(currRotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function onPrevious(){
|
function onPrevious(){
|
||||||
var previous = THIS[ this.hash ].sequence - 1;
|
var previous = THIS[ this.hash ].sequence - 1;
|
||||||
|
@ -280,4 +280,23 @@
|
|||||||
viewer.open( '/test/data/testpattern.dzi' );
|
viewer.open( '/test/data/testpattern.dzi' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
asyncTest( 'tile-drawing event', function () {
|
||||||
|
var tileDrawing = function ( event ) {
|
||||||
|
viewer.removeHandler( 'tile-drawing', tileDrawing );
|
||||||
|
ok( event, 'Event handler should be invoked' );
|
||||||
|
if ( event ) {
|
||||||
|
// Make sure we have the expected elements set
|
||||||
|
ok(event.context, "Context should be set");
|
||||||
|
ok(event.tile, "Tile should be set");
|
||||||
|
ok(event.rendered, "Rendered should be set");
|
||||||
|
}
|
||||||
|
viewer.close();
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
|
||||||
|
viewer.addHandler( 'tile-drawing', tileDrawing );
|
||||||
|
viewer.open( '/test/data/testpattern.dzi' );
|
||||||
|
} );
|
||||||
|
|
||||||
} )();
|
} )();
|
||||||
|
77
test/rotate.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var viewer;
|
||||||
|
|
||||||
|
module('Basic', {
|
||||||
|
setup: function () {
|
||||||
|
var example = $('<div id="rotateTests"></div>').appendTo("#qunit-fixture");
|
||||||
|
|
||||||
|
testLog.reset();
|
||||||
|
|
||||||
|
},
|
||||||
|
teardown: function () {
|
||||||
|
if (viewer && viewer.close) {
|
||||||
|
viewer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('RotateControlOff', function () {
|
||||||
|
|
||||||
|
var openHandler = function (event) {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
ok(true, 'Open event was sent');
|
||||||
|
ok(viewer.drawer, 'Drawer exists');
|
||||||
|
ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true');
|
||||||
|
ok(!viewer.showRotationControl, 'showRotationControl should be off');
|
||||||
|
ok(!viewer.rotateLeft, "rotateLeft button should be null");
|
||||||
|
ok(!viewer.rotateRight, "rotateRight button should be null");
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
|
||||||
|
viewer = OpenSeadragon({
|
||||||
|
id: 'rotateTests',
|
||||||
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
|
springStiffness: 100, // Faster animation = faster tests
|
||||||
|
showRotationControl: false
|
||||||
|
});
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open('/test/data/testpattern.dzi');
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('RotateControlOn', function () {
|
||||||
|
|
||||||
|
var openHandler = function (event) {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
ok(true, 'Open event was sent');
|
||||||
|
ok(viewer.drawer, 'Drawer exists');
|
||||||
|
ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true');
|
||||||
|
ok(viewer.showRotationControl, 'showRotationControl should be true');
|
||||||
|
ok(-1 != viewer.buttons.buttons.indexOf(viewer.rotateLeft), "rotateLeft should be found");
|
||||||
|
ok(-1 != viewer.buttons.buttons.indexOf(viewer.rotateRight), "rotateRight should be found");
|
||||||
|
|
||||||
|
// Now simulate the left/right button clicks.
|
||||||
|
// TODO: re-factor simulateViewerClickWithDrag so it'll accept any element, and use that.
|
||||||
|
ok(viewer.viewport.degrees === 0, "Image should start at 0 degrees rotation");
|
||||||
|
viewer.rotateLeft.onRelease();
|
||||||
|
ok(viewer.viewport.degrees === 270, "Image should be 270 degrees rotation (left)");
|
||||||
|
viewer.rotateRight.onRelease();
|
||||||
|
ok(viewer.viewport.degrees === 0, "Image should be 270 degrees rotation (right)");
|
||||||
|
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
|
||||||
|
viewer = OpenSeadragon({
|
||||||
|
id: 'rotateTests',
|
||||||
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
|
springStiffness: 100, // Faster animation = faster tests
|
||||||
|
showRotationControl: true
|
||||||
|
});
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open('/test/data/testpattern.dzi');
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
@ -28,5 +28,6 @@
|
|||||||
<script src="/test/events.js"></script>
|
<script src="/test/events.js"></script>
|
||||||
<script src="/test/units.js"></script>
|
<script src="/test/units.js"></script>
|
||||||
<script src="/test/overlays.js"></script>
|
<script src="/test/overlays.js"></script>
|
||||||
|
<script src="/test/rotate.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|