diff --git a/README.md b/README.md
index 1ddabf86..71fafeca 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# OpenSeadragon
+[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
An open-source, web-based viewer for zoomable images, implemented in pure JavaScript.
diff --git a/src/drawer.js b/src/drawer.js
index fbb8db0f..fd1cd730 100644
--- a/src/drawer.js
+++ b/src/drawer.js
@@ -324,6 +324,9 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
}
},
+ /**
+ * @private
+ */
drawDebugInfo: function( tile, count, i ){
if ( this.useCanvas ) {
this.context.save();
@@ -396,6 +399,30 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
}
},
+ /**
+ * @private
+ */
+ debugRect: function(rect) {
+ if ( this.useCanvas ) {
+ this.context.save();
+ this.context.lineWidth = 2;
+ this.context.strokeStyle = this.debugGridColor;
+ this.context.fillStyle = this.debugGridColor;
+
+ this.context.strokeRect(
+ rect.x,
+ rect.y,
+ rect.width,
+ rect.height
+ );
+
+ this.context.restore();
+ }
+ },
+
+ /**
+ * @private
+ */
_offsetForRotation: function( tile, degrees ){
var cx = this.canvas.width / 2,
cy = this.canvas.height / 2,
@@ -410,6 +437,9 @@ $.Drawer.prototype = /** @lends OpenSeadragon.Drawer.prototype */{
tile.position.y = py;
},
+ /**
+ * @private
+ */
_restoreRotationChanges: function( tile ){
var cx = this.canvas.width / 2,
cy = this.canvas.height / 2,
diff --git a/src/openseadragon.js b/src/openseadragon.js
index 49cd03d9..91a7231d 100644
--- a/src/openseadragon.js
+++ b/src/openseadragon.js
@@ -256,6 +256,10 @@
* achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to
* true will provide the effect of an infinitely scrolling viewport.
*
+ * @property {Object} [viewportMargins={}]
+ * Pushes the "home" region in from the sides by the specified amounts.
+ * Possible subproperties (Numbers, in screen coordinates): left, top, right, bottom.
+ *
* @property {Number} [imageLoaderLimit=0]
* The maximum number of image requests to make concurrently. By default
* it is set to 0 allowing the browser to make the maximum number of
diff --git a/src/rectangle.js b/src/rectangle.js
index a546a2e4..217b5126 100644
--- a/src/rectangle.js
+++ b/src/rectangle.js
@@ -185,6 +185,22 @@ $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
( this.height === other.height );
},
+ /**
+ * Multiply all dimensions in this Rect by a factor and return a new Rect.
+ * @function
+ * @param {Number} factor The factor to multiply vector components.
+ * @returns {OpenSeadragon.Rect} A new rect representing the multiplication
+ * of the vector components by the factor
+ */
+ times: function( factor ) {
+ return new OpenSeadragon.Rect(
+ this.x * factor,
+ this.y * factor,
+ this.width * factor,
+ this.height * factor
+ );
+ },
+
/**
* Rotates a rectangle around a point. Currently only 90, 180, and 270
* degrees are supported.
diff --git a/src/tiledimage.js b/src/tiledimage.js
index 0134897a..e2afbed5 100644
--- a/src/tiledimage.js
+++ b/src/tiledimage.js
@@ -178,8 +178,7 @@ function updateViewport( tiledImage ) {
best = null,
haveDrawn = false,
currentTime = $.now(),
- viewportSize = tiledImage.viewport.getContainerSize(),
- viewportBounds = tiledImage.viewport.getBounds( true ),
+ viewportBounds = tiledImage.viewport.getBoundsWithMargins( true ),
viewportTL = viewportBounds.getTopLeft(),
viewportBR = viewportBounds.getBottomRight(),
zeroRatioC = tiledImage.viewport.deltaPixelsFromPoints(
diff --git a/src/viewer.js b/src/viewer.js
index d522c077..c0a6d900 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -434,7 +434,8 @@ $.Viewer = function( options ) {
viewer: this,
degrees: this.degrees,
navigatorRotate: this.navigatorRotate,
- homeFillsViewer: this.homeFillsViewer
+ homeFillsViewer: this.homeFillsViewer,
+ margins: this.viewportMargins
});
// Create the image loader
diff --git a/src/viewport.js b/src/viewport.js
index 0c3744b0..fab50455 100644
--- a/src/viewport.js
+++ b/src/viewport.js
@@ -63,6 +63,15 @@ $.Viewport = function( options ) {
delete options.config;
}
+ this._margins = $.extend({
+ left: 0,
+ top: 0,
+ right: 0,
+ bottom: 0
+ }, options.margins || {});
+
+ delete options.margins;
+
$.extend( true, this, {
//required settings
@@ -89,6 +98,11 @@ $.Viewport = function( options ) {
}, options );
+ this._containerInnerSize = new $.Point(
+ Math.max(1, this.containerSize.x - (this._margins.left + this._margins.right)),
+ Math.max(1, this.containerSize.y - (this._margins.top + this._margins.bottom))
+ );
+
this.centerSpringX = new $.Spring({
initial: 0,
springStiffness: this.springStiffness,
@@ -257,7 +271,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
getMaxZoom: function() {
var zoom = this.maxZoomLevel;
if (!zoom) {
- zoom = this.contentSize.x * this.maxZoomPixelRatio / this.containerSize.x;
+ zoom = this.contentSize.x * this.maxZoomPixelRatio / this._containerInnerSize.x;
zoom /= this.homeBounds.width;
}
@@ -268,11 +282,12 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
* @function
*/
getAspectRatio: function() {
- return this.containerSize.x / this.containerSize.y;
+ return this._containerInnerSize.x / this._containerInnerSize.y;
},
/**
* @function
+ * @returns {OpenSeadragon.Point} The size of the container, in screen coordinates.
*/
getContainerSize: function() {
return new $.Point(
@@ -284,6 +299,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
/**
* @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
+ * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in world coordinates.
*/
getBounds: function( current ) {
var center = this.getCenter( current ),
@@ -298,6 +314,22 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
);
},
+ /**
+ * @function
+ * @param {Boolean} current - Pass true for the current location; defaults to false (target location).
+ * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,
+ * including the space taken by margins, in world coordinates.
+ */
+ getBoundsWithMargins: function( current ) {
+ var bounds = this.getBounds(current);
+ var factor = this._containerInnerSize.x * this.getZoom(current);
+ bounds.x -= this._margins.left / factor;
+ bounds.y -= this._margins.top / factor;
+ bounds.width += (this._margins.left + this._margins.right) / factor;
+ bounds.height += (this._margins.top + this._margins.bottom) / factor;
+ return bounds;
+ },
+
/**
* @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
@@ -338,13 +370,9 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
height
);
- newZoomPixel = this.zoomPoint.minus(
- bounds.getTopLeft()
- ).times(
- this.containerSize.x / bounds.width
- );
+ newZoomPixel = this._pixelFromPoint(this.zoomPoint, bounds);
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel );
- deltaZoomPoints = deltaZoomPixels.divide( this.containerSize.x * zoom );
+ deltaZoomPoints = deltaZoomPixels.divide( this._containerInnerSize.x * zoom );
return centerTarget.plus( deltaZoomPoints );
},
@@ -550,14 +578,14 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
}
referencePoint = oldBounds.getTopLeft().times(
- this.containerSize.x / oldBounds.width
+ this._containerInnerSize.x / oldBounds.width
).minus(
newBounds.getTopLeft().times(
- this.containerSize.x / newBounds.width
+ this._containerInnerSize.x / newBounds.width
)
).divide(
- this.containerSize.x / oldBounds.width -
- this.containerSize.x / newBounds.width
+ this._containerInnerSize.x / oldBounds.width -
+ this._containerInnerSize.x / newBounds.width
);
return this.zoomTo( newZoom, referencePoint, immediately );
@@ -800,12 +828,16 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
newBounds = oldBounds,
widthDeltaFactor;
- this.containerSize = new $.Point(
- newContainerSize.x,
- newContainerSize.y
+ this.containerSize.x = newContainerSize.x;
+ this.containerSize.y = newContainerSize.y;
+
+ this._containerInnerSize = new $.Point(
+ Math.max(1, newContainerSize.x - (this._margins.left + this._margins.right)),
+ Math.max(1, newContainerSize.y - (this._margins.top + this._margins.bottom))
);
if ( maintain ) {
+ // TODO: widthDeltaFactor will always be 1; probably not what's intended
widthDeltaFactor = newContainerSize.x / this.containerSize.x;
newBounds.width = oldBounds.width * widthDeltaFactor;
newBounds.height = newBounds.width / this.getAspectRatio();
@@ -877,7 +909,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
*/
deltaPixelsFromPoints: function( deltaPoints, current ) {
return deltaPoints.times(
- this.containerSize.x * this.getZoom( current )
+ this._containerInnerSize.x * this.getZoom( current )
);
},
@@ -888,7 +920,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
*/
deltaPointsFromPixels: function( deltaPixels, current ) {
return deltaPixels.divide(
- this.containerSize.x * this.getZoom( current )
+ this._containerInnerSize.x * this.getZoom( current )
);
},
@@ -898,11 +930,19 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/
pixelFromPoint: function( point, current ) {
- var bounds = this.getBounds( current );
+ return this._pixelFromPoint(point, this.getBounds( current ));
+ },
+
+ /**
+ * @private
+ */
+ _pixelFromPoint: function( point, bounds ) {
return point.minus(
bounds.getTopLeft()
).times(
- this.containerSize.x / bounds.width
+ this._containerInnerSize.x / bounds.width
+ ).plus(
+ new $.Point(this._margins.left, this._margins.top)
);
},
@@ -913,8 +953,10 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
*/
pointFromPixel: function( pixel, current ) {
var bounds = this.getBounds( current );
- return pixel.divide(
- this.containerSize.x / bounds.width
+ return pixel.minus(
+ new $.Point(this._margins.left, this._margins.top)
+ ).divide(
+ this._containerInnerSize.x / bounds.width
).plus(
bounds.getTopLeft()
);
@@ -1129,7 +1171,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
*/
viewportToImageZoom: function( viewportZoom ) {
var imageWidth = this.viewer.source.dimensions.x;
- var containerWidth = this.getContainerSize().x;
+ var containerWidth = this._containerInnerSize.x;
var viewportToImageZoomRatio = containerWidth / imageWidth;
return viewportZoom * viewportToImageZoomRatio;
},
@@ -1147,7 +1189,7 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
*/
imageToViewportZoom: function( imageZoom ) {
var imageWidth = this.viewer.source.dimensions.x;
- var containerWidth = this.getContainerSize().x;
+ var containerWidth = this._containerInnerSize.x;
var viewportToImageZoomRatio = imageWidth / containerWidth;
return imageZoom * viewportToImageZoomRatio;
}
diff --git a/test/demo/collections/main.js b/test/demo/collections/main.js
index 30992342..f558fa66 100644
--- a/test/demo/collections/main.js
+++ b/test/demo/collections/main.js
@@ -6,49 +6,94 @@
init: function() {
var self = this;
- var tileSources = [
- {
- tileSource: "../../data/tall.dzi",
- x: 1.5,
- y: 0,
- width: 1
- }, {
- tileSource: '../../data/wide.dzi',
- opacity: 1,
- x: 0,
- y: 1.5,
- height: 1
- }
- ];
+ var testInitialOpen = false;
+ var testOverlays = false;
+ var testMargins = false;
+ var margins;
- this.viewer = OpenSeadragon( {
- // debugMode: true,
+ var config = {
+ debugMode: true,
zoomPerScroll: 1.02,
showNavigator: true,
id: "contentDiv",
- tileSources: tileSources,
- prefixUrl: "../../../build/openseadragon/images/",
- overlays: [ {
- px: 13,
- py: 120,
- width: 124,
- height: 132,
- id: "overlay"
- }, {
- px: 400,
- py: 500,
- width: 400,
- height: 400,
- id: "fixed-overlay",
- placement: "TOP_LEFT"
- } ]
- } );
+ prefixUrl: "../../../build/openseadragon/images/"
+ };
- this.viewer.addHandler( "open", function() {
- // console.log(self.viewer.viewport.contentSize);
- });
+ if (testInitialOpen) {
+ config.tileSources = [
+ {
+ tileSource: "../../data/tall.dzi",
+ x: 1.5,
+ y: 0,
+ width: 1
+ }, {
+ tileSource: '../../data/wide.dzi',
+ opacity: 1,
+ x: 0,
+ y: 1.5,
+ height: 1
+ }
+ ];
+ }
+
+ if (testOverlays) {
+ config.overlays = [ {
+ px: 13,
+ py: 120,
+ width: 124,
+ height: 132,
+ id: "overlay"
+ }, {
+ px: 400,
+ py: 500,
+ width: 400,
+ height: 400,
+ id: "fixed-overlay",
+ placement: "TOP_LEFT"
+ } ];
+ }
+
+ if (testMargins) {
+ margins = {
+ top: 250,
+ left: 250,
+ right: 250,
+ bottom: 250
+ };
+
+ config.viewportMargins = margins;
+ }
+
+ this.viewer = OpenSeadragon(config);
+
+ if (testInitialOpen) {
+ this.viewer.addHandler( "open", function() {
+ // console.log(self.viewer.viewport.contentSize);
+ });
+ }
+
+ if (testMargins) {
+ this.viewer.addHandler('animation', function() {
+ var box = new OpenSeadragon.Rect(margins.left, margins.top,
+ $('#contentDiv').width() - (margins.left + margins.right),
+ $('#contentDiv').height() - (margins.top + margins.bottom));
+
+ self.viewer.drawer.debugRect(box);
+ });
+ }
// this.crossTest3();
+ this.basicTest();
+ },
+
+ // ----------
+ basicTest: function() {
+ var self = this;
+
+ this.viewer.addHandler('open', function() {
+ });
+
+ this.viewer.open("../../data/testpattern.dzi");
},
// ----------
@@ -177,6 +222,40 @@
y: -2,
width: 6
});
+ },
+
+ // ----------
+ cjTest: function() {
+ var imageKey = "e-pluribus-unum";
+ var imageXML = '