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)
|
||||
* 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)
|
||||
* 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:
|
||||
|
||||
|
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,
|
||||
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-- ) {
|
||||
tile = lastDrawn[ i ];
|
||||
|
||||
@ -1088,10 +1107,10 @@ function drawTiles( drawer, lastDrawn ){
|
||||
// specifically, don't save,rotate,restore every time we draw a tile
|
||||
if( drawer.viewport.degrees !== 0 ) {
|
||||
offsetForRotation( tile, drawer.canvas, drawer.context, drawer.viewport.degrees );
|
||||
tile.drawCanvas( drawer.context );
|
||||
tile.drawCanvas( drawer.context, drawingHandler );
|
||||
restoreRotationChanges( tile, drawer.canvas, drawer.context );
|
||||
} else {
|
||||
tile.drawCanvas( drawer.context );
|
||||
tile.drawCanvas( drawer.context, drawingHandler );
|
||||
}
|
||||
} else {
|
||||
tile.drawHTML( drawer.canvas );
|
||||
|
@ -358,6 +358,11 @@
|
||||
* image and if the 'next' button will wrap to the first image when viewing
|
||||
* 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]
|
||||
* If the viewer has been configured with a sequence of tile sources, then
|
||||
* provide buttons for navigating forward and backward through the images.
|
||||
@ -815,6 +820,18 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
HOVER: 'fullpage_hover.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: {
|
||||
REST: 'previous_rest.png',
|
||||
GROUP: 'previous_grouphover.png',
|
||||
@ -829,6 +846,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
||||
}
|
||||
},
|
||||
navPrevNextWrap: false,
|
||||
showRotationControl: false,
|
||||
|
||||
//DEVELOPER SETTINGS
|
||||
debugMode: false,
|
||||
|
@ -55,7 +55,9 @@ var I18N = {
|
||||
ZoomIn: "Zoom in",
|
||||
ZoomOut: "Zoom out",
|
||||
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.
|
||||
* @function
|
||||
* @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,
|
||||
size = this.size,
|
||||
@ -280,6 +282,9 @@ $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
|
||||
|
||||
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();
|
||||
context.drawImage(
|
||||
rendered.canvas,
|
||||
|
@ -1150,6 +1150,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ),
|
||||
onHomeHandler = $.delegate( this, onHome ),
|
||||
onFullScreenHandler = $.delegate( this, onFullScreen ),
|
||||
onRotateLeftHandler = $.delegate( this, onRotateLeft ),
|
||||
onRotateRightHandler = $.delegate( this, onRotateRight ),
|
||||
onFocusHandler = $.delegate( this, onFocus ),
|
||||
onBlurHandler = $.delegate( this, onBlur ),
|
||||
navImages = this.navImages,
|
||||
@ -1229,6 +1231,37 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
||||
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 ){
|
||||
this.buttons = new $.ButtonGroup({
|
||||
buttons: buttons,
|
||||
@ -1550,7 +1583,8 @@ function _getSafeElemSize (oElement) {
|
||||
* @private
|
||||
*/
|
||||
function openTileSource( viewer, source ) {
|
||||
var _this = viewer;
|
||||
var i,
|
||||
_this = viewer;
|
||||
|
||||
if ( _this.source ) {
|
||||
_this.close( );
|
||||
@ -1578,7 +1612,8 @@ function openTileSource( viewer, source ) {
|
||||
showNavigator: false,
|
||||
minZoomImageRatio: 1,
|
||||
maxZoomPixelRatio: 1,
|
||||
viewer: _this //,
|
||||
viewer: _this,
|
||||
degrees: _this.degrees //,
|
||||
//TODO: figure out how to support these in a way that makes sense
|
||||
//minZoomLevel: this.minZoomLevel,
|
||||
//maxZoomLevel: this.maxZoomLevel
|
||||
@ -1600,7 +1635,8 @@ function openTileSource( viewer, source ) {
|
||||
defaultZoomLevel: _this.defaultZoomLevel,
|
||||
minZoomLevel: _this.minZoomLevel,
|
||||
maxZoomLevel: _this.maxZoomLevel,
|
||||
viewer: _this
|
||||
viewer: _this,
|
||||
degrees: _this.degrees
|
||||
});
|
||||
}
|
||||
|
||||
@ -1629,6 +1665,21 @@ function openTileSource( viewer, source ) {
|
||||
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
|
||||
if ( _this.showNavigator && !_this.collectionMode ){
|
||||
// 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(){
|
||||
var previous = THIS[ this.hash ].sequence - 1;
|
||||
|
@ -280,4 +280,23 @@
|
||||
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/units.js"></script>
|
||||
<script src="/test/overlays.js"></script>
|
||||
<script src="/test/rotate.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|