diff --git a/index.html b/index.html
new file mode 100644
index 00000000..940b7151
--- /dev/null
+++ b/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+ OpenSeadragon Navigator Test
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index a1bb9b6d..3a2ecdfc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "openseadragon",
- "version": "5.0.0",
+ "version": "5.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openseadragon",
- "version": "5.0.0",
+ "version": "5.0.1",
"license": "BSD-3-Clause",
"devDependencies": {
"eslint-plugin-compat": "^4.1.2",
@@ -668,9 +668,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001636",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz",
- "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==",
+ "version": "1.0.30001690",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
+ "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
"dev": true,
"funding": [
{
@@ -5217,4 +5217,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/navigator.js b/src/navigator.js
index 0a984ab3..1b7fef6d 100644
--- a/src/navigator.js
+++ b/src/navigator.js
@@ -32,641 +32,712 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-(function( $ ){
+(function ($) {
+ /**
+ * @class Navigator
+ * @classdesc The Navigator provides a small view of the current image as fixed
+ * while representing the viewport as a moving box serving as a frame
+ * of reference in the larger viewport as to which portion of the image
+ * is currently being examined. The navigator's viewport can be interacted
+ * with using the keyboard or the mouse.
+ *
+ * @memberof OpenSeadragon
+ * @extends OpenSeadragon.Viewer
+ * @extends OpenSeadragon.EventSource
+ * @param {Object} options - Navigator options
+ * @param {Element} [options.element] - An element to use for the navigator.
+ * @param {String} [options.id] - Id of the element to use for the navigator. However, this is ignored if {@link options.element} is provided.
+ */
+ $.Navigator = function (options) {
+ var viewer = options.viewer,
+ _this = this,
+ viewerSize,
+ navigatorSize;
-/**
- * @class Navigator
- * @classdesc The Navigator provides a small view of the current image as fixed
- * while representing the viewport as a moving box serving as a frame
- * of reference in the larger viewport as to which portion of the image
- * is currently being examined. The navigator's viewport can be interacted
- * with using the keyboard or the mouse.
- *
- * @memberof OpenSeadragon
- * @extends OpenSeadragon.Viewer
- * @extends OpenSeadragon.EventSource
- * @param {Object} options - Navigator options
- * @param {Element} [options.element] - An element to use for the navigator.
- * @param {String} [options.id] - Id of the element to use for the navigator. However, this is ignored if {@link options.element} is provided.
- */
-$.Navigator = function( options ){
+ //We may need to create a new element and id if they did not
+ //provide the id for the existing element or the element itself
+ if (options.element || options.id) {
+ if (options.element) {
+ if (options.id) {
+ $.console.warn(
+ "Given option.id for Navigator was ignored since option.element was provided and is being used instead."
+ );
+ }
- var viewer = options.viewer,
- _this = this,
- viewerSize,
- navigatorSize;
+ // Don't overwrite the element's id if it has one already
+ if (options.element.id) {
+ options.id = options.element.id;
+ } else {
+ options.id = "navigator-" + $.now();
+ }
- //We may need to create a new element and id if they did not
- //provide the id for the existing element or the element itself
- if( options.element || options.id ){
- if ( options.element ) {
- if ( options.id ){
- $.console.warn("Given option.id for Navigator was ignored since option.element was provided and is being used instead.");
- }
-
- // Don't overwrite the element's id if it has one already
- if ( options.element.id ) {
- options.id = options.element.id;
+ this.element = options.element;
} else {
- options.id = 'navigator-' + $.now();
+ this.element = document.getElementById(options.id);
}
- this.element = options.element;
+ options.controlOptions = {
+ anchor: $.ControlAnchor.NONE,
+ attachToViewer: false,
+ autoFade: false,
+ };
} else {
- this.element = document.getElementById( options.id );
- }
+ options.id = "navigator-" + $.now();
+ this.element = $.makeNeutralElement("div");
+ options.controlOptions = {
+ anchor: $.ControlAnchor.TOP_RIGHT,
+ attachToViewer: true,
+ autoFade: options.autoFade,
+ };
- options.controlOptions = {
- anchor: $.ControlAnchor.NONE,
- attachToViewer: false,
- autoFade: false
- };
- } else {
- options.id = 'navigator-' + $.now();
- this.element = $.makeNeutralElement( "div" );
- options.controlOptions = {
- anchor: $.ControlAnchor.TOP_RIGHT,
- attachToViewer: true,
- autoFade: options.autoFade
- };
-
- if( options.position ){
- if( 'BOTTOM_RIGHT' === options.position ){
- options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT;
- } else if( 'BOTTOM_LEFT' === options.position ){
- options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
- } else if( 'TOP_RIGHT' === options.position ){
- options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
- } else if( 'TOP_LEFT' === options.position ){
- options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
- } else if( 'ABSOLUTE' === options.position ){
- options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
- options.controlOptions.top = options.top;
- options.controlOptions.left = options.left;
- options.controlOptions.height = options.height;
- options.controlOptions.width = options.width;
+ if (options.position) {
+ if ("BOTTOM_RIGHT" === options.position) {
+ options.controlOptions.anchor =
+ $.ControlAnchor.BOTTOM_RIGHT;
+ } else if ("BOTTOM_LEFT" === options.position) {
+ options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
+ } else if ("TOP_RIGHT" === options.position) {
+ options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
+ } else if ("TOP_LEFT" === options.position) {
+ options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
+ } else if ("ABSOLUTE" === options.position) {
+ options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
+ options.controlOptions.top = options.top;
+ options.controlOptions.left = options.left;
+ options.controlOptions.height = options.height;
+ options.controlOptions.width = options.width;
+ }
}
}
- }
- this.element.id = options.id;
- this.element.className += ' navigator';
+ this.element.id = options.id;
+ this.element.className += " navigator";
- options = $.extend( true, {
- sizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio
- }, options, {
- element: this.element,
- tabIndex: -1, // No keyboard navigation, omit from tab order
- //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,
- showSequenceControl: false,
- immediateRender: true,
- blendTime: 0,
- animationTime: options.animationTime,
- // disable autoResize since resize behavior is implemented differently by the navigator
- autoResize: false,
- // prevent resizing the navigator from adding unwanted space around the image
- minZoomImageRatio: 1.0,
- background: options.background,
- opacity: options.opacity,
- borderColor: options.borderColor,
- displayRegionColor: options.displayRegionColor
- });
+ options = $.extend(
+ true,
+ {
+ sizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio,
+ },
+ options,
+ {
+ element: this.element,
+ tabIndex: -1, // No keyboard navigation, omit from tab order
+ //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,
+ showSequenceControl: false,
+ immediateRender: true,
+ blendTime: 0,
+ animationTime: options.animationTime,
+ // disable autoResize since resize behavior is implemented differently by the navigator
+ autoResize: false,
+ // prevent resizing the navigator from adding unwanted space around the image
+ minZoomImageRatio: 1.0,
+ background: options.background,
+ opacity: options.opacity,
+ borderColor: options.borderColor,
+ displayRegionColor: options.displayRegionColor,
+ }
+ );
- options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio;
+ options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio;
- $.setElementTouchActionNone( this.element );
+ $.setElementTouchActionNone(this.element);
- this.borderWidth = 2;
- //At some browser magnification levels the display regions lines up correctly, but at some there appears to
- //be a one pixel gap.
- this.fudge = new $.Point(1, 1);
- this.totalBorderWidths = new $.Point(this.borderWidth * 2, this.borderWidth * 2).minus(this.fudge);
+ this.borderWidth = 2;
+ //At some browser magnification levels the display regions lines up correctly, but at some there appears to
+ //be a one pixel gap.
+ this.fudge = new $.Point(1, 1);
+ this.totalBorderWidths = new $.Point(
+ this.borderWidth * 2,
+ this.borderWidth * 2
+ ).minus(this.fudge);
+ if (options.controlOptions.anchor !== $.ControlAnchor.NONE) {
+ (function (style, borderWidth) {
+ style.margin = "0px";
+ style.border = borderWidth + "px solid " + options.borderColor;
+ style.padding = "0px";
+ style.background = options.background;
+ style.opacity = options.opacity;
+ style.overflow = "hidden";
+ })(this.element.style, this.borderWidth);
+ }
- if ( options.controlOptions.anchor !== $.ControlAnchor.NONE ) {
- (function( style, borderWidth ){
- style.margin = '0px';
- style.border = borderWidth + 'px solid ' + options.borderColor;
- style.padding = '0px';
- style.background = options.background;
- style.opacity = options.opacity;
- style.overflow = 'hidden';
- }( this.element.style, this.borderWidth));
- }
+ this.displayRegion = $.makeNeutralElement("div");
+ this.displayRegion.id = this.element.id + "-displayregion";
+ this.displayRegion.className = "displayregion";
- this.displayRegion = $.makeNeutralElement( "div" );
- this.displayRegion.id = this.element.id + '-displayregion';
- this.displayRegion.className = 'displayregion';
+ (function (style, borderWidth) {
+ style.position = "relative";
+ style.top = "0px";
+ style.left = "0px";
+ style.fontSize = "0px";
+ style.overflow = "hidden";
+ style.border =
+ borderWidth + "px solid " + options.displayRegionColor;
+ style.margin = "0px";
+ style.padding = "0px";
+ style.background = "transparent";
- (function( style, borderWidth ){
- style.position = 'relative';
- style.top = '0px';
- style.left = '0px';
- style.fontSize = '0px';
- style.overflow = 'hidden';
- style.border = borderWidth + 'px solid ' + options.displayRegionColor;
- style.margin = '0px';
- style.padding = '0px';
- style.background = 'transparent';
+ // We use square bracket notation on the statement below, because float is a keyword.
+ // This is important for the Google Closure compiler, if nothing else.
+ /*jshint sub:true */
+ style["float"] = "left"; //Webkit
- // We use square bracket notation on the statement below, because float is a keyword.
- // This is important for the Google Closure compiler, if nothing else.
- /*jshint sub:true */
- style['float'] = 'left'; //Webkit
+ style.cssFloat = "left"; //Firefox
+ style.zIndex = 999999999;
+ style.cursor = "default";
+ style.boxSizing = "content-box";
+ })(this.displayRegion.style, this.borderWidth);
+ $.setElementPointerEventsNone(this.displayRegion);
+ $.setElementTouchActionNone(this.displayRegion);
- style.cssFloat = 'left'; //Firefox
- style.zIndex = 999999999;
- style.cursor = 'default';
- style.boxSizing = 'content-box';
- }( this.displayRegion.style, this.borderWidth ));
- $.setElementPointerEventsNone( this.displayRegion );
- $.setElementTouchActionNone( this.displayRegion );
+ this.displayRegionContainer = $.makeNeutralElement("div");
+ this.displayRegionContainer.id =
+ this.element.id + "-displayregioncontainer";
+ this.displayRegionContainer.className = "displayregioncontainer";
+ this.displayRegionContainer.style.width = "100%";
+ this.displayRegionContainer.style.height = "100%";
+ $.setElementPointerEventsNone(this.displayRegionContainer);
+ $.setElementTouchActionNone(this.displayRegionContainer);
- this.displayRegionContainer = $.makeNeutralElement("div");
- this.displayRegionContainer.id = this.element.id + '-displayregioncontainer';
- this.displayRegionContainer.className = "displayregioncontainer";
- this.displayRegionContainer.style.width = "100%";
- this.displayRegionContainer.style.height = "100%";
- $.setElementPointerEventsNone( this.displayRegionContainer );
- $.setElementTouchActionNone( this.displayRegionContainer );
+ viewer.addControl(this.element, options.controlOptions);
- viewer.addControl(
- this.element,
- options.controlOptions
+ this._resizeWithViewer =
+ options.controlOptions.anchor !== $.ControlAnchor.ABSOLUTE &&
+ options.controlOptions.anchor !== $.ControlAnchor.NONE;
+
+ if (options.width && options.height) {
+ this.setWidth(options.width);
+ this.setHeight(options.height);
+ } else if (this._resizeWithViewer) {
+ viewerSize = $.getElementSize(viewer.element);
+ this.element.style.height =
+ Math.round(viewerSize.y * options.sizeRatio) + "px";
+ this.element.style.width =
+ Math.round(viewerSize.x * options.sizeRatio) + "px";
+ this.oldViewerSize = viewerSize;
+ navigatorSize = $.getElementSize(this.element);
+ this.elementArea = navigatorSize.x * navigatorSize.y;
+ }
+
+ this.oldContainerSize = new $.Point(0, 0);
+
+ $.Viewer.apply(this, [options]);
+
+ this.displayRegionContainer.appendChild(this.displayRegion);
+ this.element
+ .getElementsByTagName("div")[0]
+ .appendChild(this.displayRegionContainer);
+
+ function rotate(degrees, immediately) {
+ _setTransformRotate(_this.displayRegionContainer, degrees);
+ _setTransformRotate(_this.displayRegion, -degrees);
+ _this.viewport.setRotation(degrees, immediately);
+ }
+ if (options.navigatorRotate) {
+ var degrees = options.viewer.viewport ?
+ options.viewer.viewport.getRotation() :
+ options.viewer.degrees || 0;
+
+ rotate(degrees, true);
+ options.viewer.addHandler("rotate", function (args) {
+ rotate(args.degrees, args.immediately);
+ });
+ }
+
+ // Remove the base class' (Viewer's) innerTracker and replace it with our own
+ this.innerTracker.destroy();
+ this.innerTracker = new $.MouseTracker({
+ userData: "Navigator.innerTracker",
+ element: this.element, //this.canvas,
+ dragHandler: $.delegate(this, onCanvasDrag),
+ clickHandler: $.delegate(this, onCanvasClick),
+ releaseHandler: $.delegate(this, onCanvasRelease),
+ scrollHandler: $.delegate(this, onCanvasScroll),
+ preProcessEventHandler: function (eventInfo) {
+ if (eventInfo.eventType === "wheel") {
+ //don't scroll the page up and down if the user is scrolling
+ //in the navigator
+ eventInfo.preventDefault = true;
+ }
+ },
+ });
+ this.outerTracker.userData = "Navigator.outerTracker";
+
+ // this.innerTracker is attached to this.element...we need to allow pointer
+ // events to pass through this Viewer's canvas/container elements so implicit
+ // pointer capture works on touch devices
+ //TODO an alternative is to attach the new MouseTracker to this.canvas...not
+ // sure why it isn't already (see MouseTracker constructor call above)
+ $.setElementPointerEventsNone(this.canvas);
+ $.setElementPointerEventsNone(this.container);
+
+ this.addHandler("reset-size", function () {
+ if (_this.viewport) {
+ _this.viewport.goHome(true);
+ }
+ });
+
+ viewer.world.addHandler("item-index-change", function (event) {
+ window.setTimeout(function () {
+ var item = _this.world.getItemAt(event.previousIndex);
+ _this.world.setItemIndex(item, event.newIndex);
+ }, 1);
+ });
+
+ viewer.world.addHandler("remove-item", function (event) {
+ var theirItem = event.item;
+ var myItem = _this._getMatchingItem(theirItem);
+ if (myItem) {
+ _this.world.removeItem(myItem);
+ }
+ });
+
+ this.update(viewer.viewport);
+ };
+
+ $.extend(
+ $.Navigator.prototype,
+ $.EventSource.prototype,
+ $.Viewer.prototype,
+ /** @lends OpenSeadragon.Navigator.prototype */ {
+ /**
+ * Used to notify the navigator when its size has changed.
+ * Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable.
+ * @function
+ */
+ updateSize: function () {
+ if (this.viewport) {
+ var containerSize = new $.Point(
+ this.container.clientWidth === 0 ?
+ 1 :
+ this.container.clientWidth,
+ this.container.clientHeight === 0 ?
+ 1 :
+ this.container.clientHeight
+ );
+
+ if (!containerSize.equals(this.oldContainerSize)) {
+ this.viewport.resize(containerSize, true);
+ this.viewport.goHome(true);
+ this.oldContainerSize = containerSize;
+ this.world.update();
+ this.world.draw();
+ this.update(this.viewer.viewport);
+ }
+ }
+ },
+
+ /**
+ * Controls the visibility of the navigator element.
+ * This method hides or shows the entire navigator, including its background and borders.
+ * @function
+ * @param {Boolean} visible - True to show the navigator, false to hide it.
+ */
+ setVisible: function (visible) {
+ if (this.element) {
+ // Only proceed if there's a valid element to control
+ if (visible) {
+ // Show the navigator element
+ this.element.style.display = "block";
+ if (this.viewport) {
+ // Update the viewport and ensure constraints are applied
+ this.viewport.goHome(true);
+ this.world.update();
+ this.world.draw();
+ this.update(this.viewer.viewport);
+ }
+ } else {
+ // Hide the navigator element
+ this.element.style.display = "none";
+ }
+ } else {
+ // Log a warning if the navigator element is missing
+ $.console.warn(
+ "Navigator element not found. Unable to set visibility."
+ );
+ }
+ },
+
+ /**
+ * Explicitly sets the width of the navigator, in web coordinates. Disables automatic resizing.
+ * @param {Number|String} width - the new width, either a number of pixels or a CSS string, such as "100%"
+ */
+ setWidth: function (width) {
+ this.width = width;
+ this.element.style.width =
+ typeof width === "number" ? width + "px" : width;
+ this._resizeWithViewer = false;
+ this.updateSize();
+ },
+
+ /**
+ * Explicitly sets the height of the navigator, in web coordinates. Disables automatic resizing.
+ * @param {Number|String} height - the new height, either a number of pixels or a CSS string, such as "100%"
+ */
+ setHeight: function (height) {
+ this.height = height;
+ this.element.style.height =
+ typeof height === "number" ? height + "px" : height;
+ this._resizeWithViewer = false;
+ this.updateSize();
+ },
+
+ /**
+ * Flip navigator element
+ * @param {Boolean} state - Flip state to set.
+ */
+ setFlip: function (state) {
+ this.viewport.setFlip(state);
+
+ this.setDisplayTransform(
+ this.viewer.viewport.getFlip() ?
+ "scale(-1,1)" :
+ "scale(1,1)"
+ );
+ return this;
+ },
+
+ setDisplayTransform: function (rule) {
+ setElementTransform(this.canvas, rule);
+ setElementTransform(this.element, rule);
+ },
+
+ /**
+ * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
+ * @function
+ * @param {OpenSeadragon.Viewport} [viewport] The viewport to display. Default: the viewport this navigator is tracking.
+ */
+ update: function (viewport) {
+ var viewerSize,
+ newWidth,
+ newHeight,
+ bounds,
+ topleft,
+ bottomright;
+
+ if (!viewport) {
+ viewport = this.viewer.viewport;
+ }
+
+ viewerSize = $.getElementSize(this.viewer.element);
+ if (
+ this._resizeWithViewer &&
+ viewerSize.x &&
+ viewerSize.y &&
+ !viewerSize.equals(this.oldViewerSize)
+ ) {
+ this.oldViewerSize = viewerSize;
+
+ if (this.maintainSizeRatio || !this.elementArea) {
+ newWidth = viewerSize.x * this.sizeRatio;
+ newHeight = viewerSize.y * this.sizeRatio;
+ } else {
+ newWidth = Math.sqrt(
+ this.elementArea * (viewerSize.x / viewerSize.y)
+ );
+ newHeight = this.elementArea / newWidth;
+ }
+
+ this.element.style.width = Math.round(newWidth) + "px";
+ this.element.style.height = Math.round(newHeight) + "px";
+
+ if (!this.elementArea) {
+ this.elementArea = newWidth * newHeight;
+ }
+
+ this.updateSize();
+ }
+
+ if (viewport && this.viewport) {
+ bounds = viewport.getBoundsNoRotate(true);
+ topleft = this.viewport.pixelFromPointNoRotate(
+ bounds.getTopLeft(),
+ false
+ );
+ bottomright = this.viewport
+ .pixelFromPointNoRotate(bounds.getBottomRight(), false)
+ .minus(this.totalBorderWidths);
+
+ if (!this.navigatorRotate) {
+ var degrees = viewport.getRotation(true);
+ _setTransformRotate(this.displayRegion, -degrees);
+ }
+
+ //update style for navigator-box
+ var style = this.displayRegion.style;
+ style.display = this.world.getItemCount() ?
+ "block" :
+ "none";
+
+ style.top = topleft.y.toFixed(2) + "px";
+ style.left = topleft.x.toFixed(2) + "px";
+
+ var width = bottomright.x - topleft.x;
+ var height = bottomright.y - topleft.y;
+ // make sure width and height are non-negative so IE doesn't throw
+ style.width = Math.round(Math.max(width, 0)) + "px";
+ style.height = Math.round(Math.max(height, 0)) + "px";
+ }
+ },
+
+ // overrides Viewer.addTiledImage
+ addTiledImage: function (options) {
+ var _this = this;
+
+ var original = options.originalTiledImage;
+ delete options.original;
+
+ var optionsClone = $.extend({}, options, {
+ success: function (event) {
+ var myItem = event.item;
+ myItem._originalForNavigator = original;
+ _this._matchBounds(myItem, original, true);
+ _this._matchOpacity(myItem, original);
+ _this._matchCompositeOperation(myItem, original);
+
+ function matchBounds() {
+ _this._matchBounds(myItem, original);
+ }
+
+ function matchOpacity() {
+ _this._matchOpacity(myItem, original);
+ }
+
+ function matchCompositeOperation() {
+ _this._matchCompositeOperation(myItem, original);
+ }
+
+ original.addHandler("bounds-change", matchBounds);
+ original.addHandler("clip-change", matchBounds);
+ original.addHandler("opacity-change", matchOpacity);
+ original.addHandler(
+ "composite-operation-change",
+ matchCompositeOperation
+ );
+ },
+ });
+
+ return $.Viewer.prototype.addTiledImage.apply(this, [
+ optionsClone,
+ ]);
+ },
+
+ destroy: function () {
+ return $.Viewer.prototype.destroy.apply(this);
+ },
+
+ // private
+ _getMatchingItem: function (theirItem) {
+ var count = this.world.getItemCount();
+ var item;
+ for (var i = 0; i < count; i++) {
+ item = this.world.getItemAt(i);
+ if (item._originalForNavigator === theirItem) {
+ return item;
+ }
+ }
+
+ return null;
+ },
+
+ // private
+ _matchBounds: function (myItem, theirItem, immediately) {
+ var bounds = theirItem.getBoundsNoRotate();
+ myItem.setPosition(bounds.getTopLeft(), immediately);
+ myItem.setWidth(bounds.width, immediately);
+ myItem.setRotation(theirItem.getRotation(), immediately);
+ myItem.setClip(theirItem.getClip());
+ myItem.setFlip(theirItem.getFlip());
+ },
+
+ // private
+ _matchOpacity: function (myItem, theirItem) {
+ myItem.setOpacity(theirItem.opacity);
+ },
+
+ // private
+ _matchCompositeOperation: function (myItem, theirItem) {
+ myItem.setCompositeOperation(theirItem.compositeOperation);
+ },
+ }
);
- this._resizeWithViewer = options.controlOptions.anchor !== $.ControlAnchor.ABSOLUTE &&
- options.controlOptions.anchor !== $.ControlAnchor.NONE;
-
- if (options.width && options.height) {
- this.setWidth(options.width);
- this.setHeight(options.height);
- } else if ( this._resizeWithViewer ) {
- viewerSize = $.getElementSize( viewer.element );
- this.element.style.height = Math.round( viewerSize.y * options.sizeRatio ) + 'px';
- this.element.style.width = Math.round( viewerSize.x * options.sizeRatio ) + 'px';
- this.oldViewerSize = viewerSize;
- navigatorSize = $.getElementSize( this.element );
- this.elementArea = navigatorSize.x * navigatorSize.y;
- }
-
- this.oldContainerSize = new $.Point( 0, 0 );
-
- $.Viewer.apply( this, [ options ] );
-
- this.displayRegionContainer.appendChild(this.displayRegion);
- this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
-
- function rotate(degrees, immediately) {
- _setTransformRotate(_this.displayRegionContainer, degrees);
- _setTransformRotate(_this.displayRegion, -degrees);
- _this.viewport.setRotation(degrees, immediately);
- }
- if (options.navigatorRotate) {
- var degrees = options.viewer.viewport ?
- options.viewer.viewport.getRotation() :
- options.viewer.degrees || 0;
-
- rotate(degrees, true);
- options.viewer.addHandler("rotate", function (args) {
- rotate(args.degrees, args.immediately);
- });
- }
-
-
- // Remove the base class' (Viewer's) innerTracker and replace it with our own
- this.innerTracker.destroy();
- this.innerTracker = new $.MouseTracker({
- userData: 'Navigator.innerTracker',
- element: this.element, //this.canvas,
- dragHandler: $.delegate( this, onCanvasDrag ),
- clickHandler: $.delegate( this, onCanvasClick ),
- releaseHandler: $.delegate( this, onCanvasRelease ),
- scrollHandler: $.delegate( this, onCanvasScroll ),
- preProcessEventHandler: function (eventInfo) {
- if (eventInfo.eventType === 'wheel') {
- //don't scroll the page up and down if the user is scrolling
- //in the navigator
- eventInfo.preventDefault = true;
- }
- }
- });
- this.outerTracker.userData = 'Navigator.outerTracker';
-
- // this.innerTracker is attached to this.element...we need to allow pointer
- // events to pass through this Viewer's canvas/container elements so implicit
- // pointer capture works on touch devices
- //TODO an alternative is to attach the new MouseTracker to this.canvas...not
- // sure why it isn't already (see MouseTracker constructor call above)
- $.setElementPointerEventsNone( this.canvas );
- $.setElementPointerEventsNone( this.container );
-
- this.addHandler("reset-size", function() {
- if (_this.viewport) {
- _this.viewport.goHome(true);
- }
- });
-
- viewer.world.addHandler("item-index-change", function(event) {
- window.setTimeout(function(){
- var item = _this.world.getItemAt(event.previousIndex);
- _this.world.setItemIndex(item, event.newIndex);
- }, 1);
- });
-
- viewer.world.addHandler("remove-item", function(event) {
- var theirItem = event.item;
- var myItem = _this._getMatchingItem(theirItem);
- if (myItem) {
- _this.world.removeItem(myItem);
- }
- });
-
- this.update(viewer.viewport);
-};
-
-$.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.Navigator.prototype */{
-
/**
- * Used to notify the navigator when its size has changed.
- * Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable.
+ * @private
+ * @inner
* @function
*/
- updateSize: function () {
- if ( this.viewport ) {
- var containerSize = new $.Point(
- (this.container.clientWidth === 0 ? 1 : this.container.clientWidth),
- (this.container.clientHeight === 0 ? 1 : this.container.clientHeight)
- );
+ function onCanvasClick(event) {
+ var canvasClickEventArgs = {
+ tracker: event.eventSource,
+ position: event.position,
+ quick: event.quick,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefaultAction: false,
+ };
+ /**
+ * Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
+ *
+ * @event navigator-click
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
+ * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
+ * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
+ * @property {Boolean} shift - True if the shift key was pressed during this event.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
+ */
- if ( !containerSize.equals( this.oldContainerSize ) ) {
- this.viewport.resize( containerSize, true );
- this.viewport.goHome(true);
- this.oldContainerSize = containerSize;
- this.world.update();
- this.world.draw();
- this.update(this.viewer.viewport);
+ this.viewer.raiseEvent("navigator-click", canvasClickEventArgs);
+
+ if (
+ !canvasClickEventArgs.preventDefaultAction &&
+ event.quick &&
+ this.viewer.viewport &&
+ (this.panVertical || this.panHorizontal)
+ ) {
+ if (this.viewer.viewport.flipped) {
+ event.position.x =
+ this.viewport.getContainerSize().x - event.position.x;
}
- }
- },
-
- /**
- * Explicitly sets the width of the navigator, in web coordinates. Disables automatic resizing.
- * @param {Number|String} width - the new width, either a number of pixels or a CSS string, such as "100%"
- */
- setWidth: function(width) {
- this.width = width;
- this.element.style.width = typeof (width) === "number" ? (width + 'px') : width;
- this._resizeWithViewer = false;
- this.updateSize();
- },
-
- /**
- * Explicitly sets the height of the navigator, in web coordinates. Disables automatic resizing.
- * @param {Number|String} height - the new height, either a number of pixels or a CSS string, such as "100%"
- */
- setHeight: function(height) {
- this.height = height;
- this.element.style.height = typeof (height) === "number" ? (height + 'px') : height;
- this._resizeWithViewer = false;
- this.updateSize();
- },
-
- /**
- * Flip navigator element
- * @param {Boolean} state - Flip state to set.
- */
- setFlip: function(state) {
- this.viewport.setFlip(state);
-
- this.setDisplayTransform(this.viewer.viewport.getFlip() ? "scale(-1,1)" : "scale(1,1)");
- return this;
- },
-
- setDisplayTransform: function(rule) {
- setElementTransform(this.canvas, rule);
- setElementTransform(this.element, rule);
- },
-
- /**
- * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
- * @function
- * @param {OpenSeadragon.Viewport} [viewport] The viewport to display. Default: the viewport this navigator is tracking.
- */
- update: function( viewport ) {
-
- var viewerSize,
- newWidth,
- newHeight,
- bounds,
- topleft,
- bottomright;
-
- if(!viewport){
- viewport = this.viewer.viewport;
- }
-
- viewerSize = $.getElementSize( this.viewer.element );
- if ( this._resizeWithViewer && viewerSize.x && viewerSize.y && !viewerSize.equals( this.oldViewerSize ) ) {
- this.oldViewerSize = viewerSize;
-
- if ( this.maintainSizeRatio || !this.elementArea) {
- newWidth = viewerSize.x * this.sizeRatio;
- newHeight = viewerSize.y * this.sizeRatio;
- } else {
- newWidth = Math.sqrt(this.elementArea * (viewerSize.x / viewerSize.y));
- newHeight = this.elementArea / newWidth;
+ var target = this.viewport.pointFromPixel(event.position);
+ if (!this.panVertical) {
+ // perform only horizonal pan
+ target.y = this.viewer.viewport.getCenter(true).y;
+ } else if (!this.panHorizontal) {
+ // perform only vertical pan
+ target.x = this.viewer.viewport.getCenter(true).x;
}
-
- this.element.style.width = Math.round( newWidth ) + 'px';
- this.element.style.height = Math.round( newHeight ) + 'px';
-
- if (!this.elementArea) {
- this.elementArea = newWidth * newHeight;
- }
-
- this.updateSize();
- }
-
- if (viewport && this.viewport) {
- bounds = viewport.getBoundsNoRotate(true);
- topleft = this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(), false);
- bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false)
- .minus( this.totalBorderWidths );
-
- if (!this.navigatorRotate) {
- var degrees = viewport.getRotation(true);
- _setTransformRotate(this.displayRegion, -degrees);
- }
-
- //update style for navigator-box
- var style = this.displayRegion.style;
- style.display = this.world.getItemCount() ? 'block' : 'none';
-
- style.top = topleft.y.toFixed(2) + "px";
- style.left = topleft.x.toFixed(2) + "px";
-
- var width = bottomright.x - topleft.x;
- var height = bottomright.y - topleft.y;
- // make sure width and height are non-negative so IE doesn't throw
- style.width = Math.round( Math.max( width, 0 ) ) + 'px';
- style.height = Math.round( Math.max( height, 0 ) ) + 'px';
- }
-
- },
-
- // overrides Viewer.addTiledImage
- addTiledImage: function(options) {
- var _this = this;
-
- var original = options.originalTiledImage;
- delete options.original;
-
- var optionsClone = $.extend({}, options, {
- success: function(event) {
- var myItem = event.item;
- myItem._originalForNavigator = original;
- _this._matchBounds(myItem, original, true);
- _this._matchOpacity(myItem, original);
- _this._matchCompositeOperation(myItem, original);
-
- function matchBounds() {
- _this._matchBounds(myItem, original);
- }
-
- function matchOpacity() {
- _this._matchOpacity(myItem, original);
- }
-
- function matchCompositeOperation() {
- _this._matchCompositeOperation(myItem, original);
- }
-
- original.addHandler('bounds-change', matchBounds);
- original.addHandler('clip-change', matchBounds);
- original.addHandler('opacity-change', matchOpacity);
- original.addHandler('composite-operation-change', matchCompositeOperation);
- }
- });
-
- return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
- },
-
- destroy: function() {
- return $.Viewer.prototype.destroy.apply(this);
- },
-
- // private
- _getMatchingItem: function(theirItem) {
- var count = this.world.getItemCount();
- var item;
- for (var i = 0; i < count; i++) {
- item = this.world.getItemAt(i);
- if (item._originalForNavigator === theirItem) {
- return item;
- }
- }
-
- return null;
- },
-
- // private
- _matchBounds: function(myItem, theirItem, immediately) {
- var bounds = theirItem.getBoundsNoRotate();
- myItem.setPosition(bounds.getTopLeft(), immediately);
- myItem.setWidth(bounds.width, immediately);
- myItem.setRotation(theirItem.getRotation(), immediately);
- myItem.setClip(theirItem.getClip());
- myItem.setFlip(theirItem.getFlip());
- },
-
- // private
- _matchOpacity: function(myItem, theirItem) {
- myItem.setOpacity(theirItem.opacity);
- },
-
- // private
- _matchCompositeOperation: function(myItem, theirItem) {
- myItem.setCompositeOperation(theirItem.compositeOperation);
- }
-});
-
-
-/**
- * @private
- * @inner
- * @function
- */
-function onCanvasClick( event ) {
- var canvasClickEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- quick: event.quick,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: false
- };
- /**
- * Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
- *
- * @event navigator-click
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
- */
-
- this.viewer.raiseEvent('navigator-click', canvasClickEventArgs);
-
- if ( !canvasClickEventArgs.preventDefaultAction && event.quick && this.viewer.viewport && (this.panVertical || this.panHorizontal)) {
- if(this.viewer.viewport.flipped) {
- event.position.x = this.viewport.getContainerSize().x - event.position.x;
- }
- var target = this.viewport.pointFromPixel(event.position);
- if (!this.panVertical) {
- // perform only horizonal pan
- target.y = this.viewer.viewport.getCenter(true).y;
- } else if (!this.panHorizontal) {
- // perform only vertical pan
- target.x = this.viewer.viewport.getCenter(true).x;
- }
- this.viewer.viewport.panTo(target);
- this.viewer.viewport.applyConstraints();
- }
-
-}
-
-/**
- * @private
- * @inner
- * @function
- */
-function onCanvasDrag( event ) {
- var canvasDragEventArgs = {
- tracker: event.eventSource,
- position: event.position,
- delta: event.delta,
- speed: event.speed,
- direction: event.direction,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefaultAction: false
- };
- /**
- * Raised when a drag event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
- *
- * @event navigator-drag
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
- * @property {Number} speed - Current computed speed, in pixels per second.
- * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
- */
- this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs);
-
- if ( !canvasDragEventArgs.preventDefaultAction && this.viewer.viewport ) {
- if( !this.panHorizontal ){
- event.delta.x = 0;
- }
- if( !this.panVertical ){
- event.delta.y = 0;
- }
-
- if(this.viewer.viewport.flipped){
- event.delta.x = -event.delta.x;
- }
-
- this.viewer.viewport.panBy(
- this.viewport.deltaPointsFromPixels(
- event.delta
- )
- );
- if( this.viewer.constrainDuringPan ){
+ this.viewer.viewport.panTo(target);
this.viewer.viewport.applyConstraints();
}
}
-}
-
-
-/**
- * @private
- * @inner
- * @function
- */
-function onCanvasRelease( event ) {
- if ( event.insideElementPressed && this.viewer.viewport ) {
- this.viewer.viewport.applyConstraints();
- }
-}
-
-
-/**
- * @private
- * @inner
- * @function
- */
-function onCanvasScroll( event ) {
- var eventArgs = {
- tracker: event.eventSource,
- position: event.position,
- scroll: event.scroll,
- shift: event.shift,
- originalEvent: event.originalEvent,
- preventDefault: event.preventDefault
- };
/**
- * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.).
- *
- * @event navigator-scroll
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} scroll - The scroll delta for the event.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {Boolean} preventDefault - Set to true to prevent the default user-agent's handling of the wheel event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
+ * @private
+ * @inner
+ * @function
*/
- this.viewer.raiseEvent( 'navigator-scroll', eventArgs );
+ function onCanvasDrag(event) {
+ var canvasDragEventArgs = {
+ tracker: event.eventSource,
+ position: event.position,
+ delta: event.delta,
+ speed: event.speed,
+ direction: event.direction,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefaultAction: false,
+ };
+ /**
+ * Raised when a drag event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
+ *
+ * @event navigator-drag
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
+ * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
+ * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
+ * @property {Number} speed - Current computed speed, in pixels per second.
+ * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
+ * @property {Boolean} shift - True if the shift key was pressed during this event.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ * @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
+ */
+ this.viewer.raiseEvent("navigator-drag", canvasDragEventArgs);
- event.preventDefault = eventArgs.preventDefault;
-}
+ if (!canvasDragEventArgs.preventDefaultAction && this.viewer.viewport) {
+ if (!this.panHorizontal) {
+ event.delta.x = 0;
+ }
+ if (!this.panVertical) {
+ event.delta.y = 0;
+ }
-/**
- * @function
- * @private
- * @param {Object} element
- * @param {Number} degrees
- */
-function _setTransformRotate( element, degrees ) {
- setElementTransform(element, "rotate(" + degrees + "deg)");
-}
+ if (this.viewer.viewport.flipped) {
+ event.delta.x = -event.delta.x;
+ }
-function setElementTransform( element, rule ) {
- element.style.webkitTransform = rule;
- element.style.mozTransform = rule;
- element.style.msTransform = rule;
- element.style.oTransform = rule;
- element.style.transform = rule;
-}
+ this.viewer.viewport.panBy(
+ this.viewport.deltaPointsFromPixels(event.delta)
+ );
+ if (this.viewer.constrainDuringPan) {
+ this.viewer.viewport.applyConstraints();
+ }
+ }
+ }
-}( OpenSeadragon ));
+ /**
+ * @private
+ * @inner
+ * @function
+ */
+ function onCanvasRelease(event) {
+ if (event.insideElementPressed && this.viewer.viewport) {
+ this.viewer.viewport.applyConstraints();
+ }
+ }
+
+ /**
+ * @private
+ * @inner
+ * @function
+ */
+ function onCanvasScroll(event) {
+ var eventArgs = {
+ tracker: event.eventSource,
+ position: event.position,
+ scroll: event.scroll,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefault: event.preventDefault,
+ };
+
+ /**
+ * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.).
+ *
+ * @event navigator-scroll
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
+ * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
+ * @property {Number} scroll - The scroll delta for the event.
+ * @property {Boolean} shift - True if the shift key was pressed during this event.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {Boolean} preventDefault - Set to true to prevent the default user-agent's handling of the wheel event.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.viewer.raiseEvent("navigator-scroll", eventArgs);
+
+ event.preventDefault = eventArgs.preventDefault;
+ }
+
+ /**
+ * @function
+ * @private
+ * @param {Object} element
+ * @param {Number} degrees
+ */
+ function _setTransformRotate(element, degrees) {
+ setElementTransform(element, "rotate(" + degrees + "deg)");
+ }
+
+ function setElementTransform(element, rule) {
+ element.style.webkitTransform = rule;
+ element.style.mozTransform = rule;
+ element.style.msTransform = rule;
+ element.style.oTransform = rule;
+ element.style.transform = rule;
+ }
+})(OpenSeadragon);