diff --git a/images/rotateleft_grouphover.png b/images/rotateleft_grouphover.png new file mode 100644 index 00000000..9aec7ac9 Binary files /dev/null and b/images/rotateleft_grouphover.png differ diff --git a/images/rotateleft_hover.png b/images/rotateleft_hover.png new file mode 100644 index 00000000..ba32c5a4 Binary files /dev/null and b/images/rotateleft_hover.png differ diff --git a/images/rotateleft_pressed.png b/images/rotateleft_pressed.png new file mode 100644 index 00000000..b968ebf8 Binary files /dev/null and b/images/rotateleft_pressed.png differ diff --git a/images/rotateleft_rest.png b/images/rotateleft_rest.png new file mode 100644 index 00000000..ebbf081b Binary files /dev/null and b/images/rotateleft_rest.png differ diff --git a/images/rotateright_grouphover.png b/images/rotateright_grouphover.png new file mode 100644 index 00000000..86e8689c Binary files /dev/null and b/images/rotateright_grouphover.png differ diff --git a/images/rotateright_hover.png b/images/rotateright_hover.png new file mode 100644 index 00000000..d22a728f Binary files /dev/null and b/images/rotateright_hover.png differ diff --git a/images/rotateright_pressed.png b/images/rotateright_pressed.png new file mode 100644 index 00000000..fc2ead64 Binary files /dev/null and b/images/rotateright_pressed.png differ diff --git a/images/rotateright_rest.png b/images/rotateright_rest.png new file mode 100644 index 00000000..07219678 Binary files /dev/null and b/images/rotateright_rest.png differ diff --git a/src/openseadragon.js b/src/openseadragon.js index 9ee18112..611d1cd4 100644 --- a/src/openseadragon.js +++ b/src/openseadragon.js @@ -336,6 +336,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. @@ -793,6 +798,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', @@ -807,6 +824,7 @@ window.OpenSeadragon = window.OpenSeadragon || function( options ){ } }, navPrevNextWrap: false, + showRotationControl: false, //DEVELOPER SETTINGS debugMode: false, diff --git a/src/strings.js b/src/strings.js index e6cf8352..03f4ed42 100644 --- a/src/strings.js +++ b/src/strings.js @@ -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" } }; diff --git a/src/viewer.js b/src/viewer.js index e7fc0a1f..cd4b5614 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -1153,6 +1153,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, @@ -1232,6 +1234,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, @@ -1470,6 +1503,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 @@ -2116,6 +2164,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; diff --git a/test/rotate.js b/test/rotate.js new file mode 100644 index 00000000..c59bb91e --- /dev/null +++ b/test/rotate.js @@ -0,0 +1,77 @@ +/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */ + +(function () { + var viewer; + + module('Basic', { + setup: function () { + var example = $('
').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. Security prevents us from simulating a click, + // so we will call the 'onRelease' handler for the button + 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'); + }); + +})(); diff --git a/test/test.html b/test/test.html index 0f46fa30..1d5c1ce6 100644 --- a/test/test.html +++ b/test/test.html @@ -27,5 +27,6 @@ +