mirror of
https://github.com/openseadragon/openseadragon.git
synced 2024-11-25 06:36:11 +03:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
96a4de7b87
@ -28,6 +28,7 @@ module.exports = function(grunt) {
|
|||||||
"src/mousetracker.js",
|
"src/mousetracker.js",
|
||||||
"src/control.js",
|
"src/control.js",
|
||||||
"src/controldock.js",
|
"src/controldock.js",
|
||||||
|
"src/placement.js",
|
||||||
"src/viewer.js",
|
"src/viewer.js",
|
||||||
"src/navigator.js",
|
"src/navigator.js",
|
||||||
"src/strings.js",
|
"src/strings.js",
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
OPENSEADRAGON CHANGELOG
|
OPENSEADRAGON CHANGELOG
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
2.1.1: (in progress)
|
2.2.0: (in progress)
|
||||||
|
|
||||||
* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and
|
* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and
|
||||||
Viewport.contentAspectY have been removed.
|
Viewport.contentAspectY have been removed. (#846)
|
||||||
* DEPRECATION: Viewport.setHomeBounds has been deprecated
|
* BREAKING CHANGE: The Overlay.getBounds method now takes the viewport as parameter. (#896)
|
||||||
* DEPRECATION: the Viewport constructor is now ignoring the contentSize option
|
* DEPRECATION: Overlay.scales, Overlay.bounds and Overlay.position have been deprecated. (#896)
|
||||||
|
* Overlay.width !== null should be used to test whether the overlay scales horizontally
|
||||||
|
* Overlay.height !== null should be used to test whether the overlay scales vertically
|
||||||
|
* The Overlay.getBounds method should be used to get the bounds of the overlay in viewport coordinates
|
||||||
|
* Overlay.location replaces Overlay.position
|
||||||
|
* DEPRECATION: Viewport.setHomeBounds has been deprecated (#846)
|
||||||
|
* DEPRECATION: the Viewport constructor is now ignoring the contentSize option (#846)
|
||||||
* Tile edge smoothing at high zoom (#764)
|
* Tile edge smoothing at high zoom (#764)
|
||||||
* Fixed issue with reference strip popping up virtual keyboard on mobile devices (#779)
|
* Fixed issue with reference strip popping up virtual keyboard on mobile devices (#779)
|
||||||
* Now supporting rotation in the Rect class (#782)
|
* Now supporting rotation in the Rect class (#782)
|
||||||
@ -25,6 +31,22 @@ Viewport.contentAspectY have been removed.
|
|||||||
* Fixed: with scrollToZoom disabled, the viewer caused page scrolling to slow down (#858)
|
* Fixed: with scrollToZoom disabled, the viewer caused page scrolling to slow down (#858)
|
||||||
* Added Viewer.getOverlayById and Overlay.getBounds functions (#853)
|
* Added Viewer.getOverlayById and Overlay.getBounds functions (#853)
|
||||||
* Tiled images with 0 opacity no longer load their tiles or do drawing calculations (#859)
|
* Tiled images with 0 opacity no longer load their tiles or do drawing calculations (#859)
|
||||||
|
* Fixed issue with edge smoothing with PNG tiles at high zoom (#860)
|
||||||
|
* Fixed: Images with transparency were clearing images layered below them (#861)
|
||||||
|
* Fixed issue causing HTML pages to jump unwantedly to the reference strip upon loading (#872)
|
||||||
|
* Added addOnceHandler method to EventSource (#887)
|
||||||
|
* Added TiledImage.fitBounds method (#888)
|
||||||
|
* Overlays can now be scaled in a single dimension by providing a point location and either width or height (#896)
|
||||||
|
* Added full rotation support to overlays (#729, #193)
|
||||||
|
* Viewport.goHome() now takes clipping into account (#910)
|
||||||
|
* Improved zoom to point (#923)
|
||||||
|
* Optimized sketch canvas clearing and blending for images with opacity or transfer modes (#927)
|
||||||
|
* Now taking rotation into account in viewport getBounds and fitBounds methods (#934)
|
||||||
|
* Added option to disable navigator auto-fade (#935)
|
||||||
|
* Fixed issue with maintaining viewport position with full screen (#940)
|
||||||
|
* Fixed an issue with simultaneous touch events (#930)
|
||||||
|
* Avoid loading clipped out tiles (#939)
|
||||||
|
* Improved precision for subtle moves with fitBounds (#939)
|
||||||
|
|
||||||
2.1.0:
|
2.1.0:
|
||||||
|
|
||||||
|
@ -259,13 +259,17 @@ $.Drawer.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_clear: function ( useSketch ) {
|
_clear: function (useSketch, bounds) {
|
||||||
if ( !this.useCanvas ) {
|
if (!this.useCanvas) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var context = this._getContext( useSketch );
|
var context = this._getContext(useSketch);
|
||||||
|
if (bounds) {
|
||||||
|
context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||||
|
} else {
|
||||||
var canvas = context.canvas;
|
var canvas = context.canvas;
|
||||||
context.clearRect( 0, 0, canvas.width, canvas.height );
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -382,21 +386,59 @@ $.Drawer.prototype = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Blends the sketch canvas in the main canvas.
|
* Blends the sketch canvas in the main canvas.
|
||||||
* @param {Float} opacity The opacity of the blending.
|
* @param {Object} options The options
|
||||||
* @param {Float} [scale=1] The scale at which tiles were drawn on the sketch. Default is 1.
|
* @param {Float} options.opacity The opacity of the blending.
|
||||||
|
* @param {Float} [options.scale=1] The scale at which tiles were drawn on
|
||||||
|
* the sketch. Default is 1.
|
||||||
* Use scale to draw at a lower scale and then enlarge onto the main canvas.
|
* Use scale to draw at a lower scale and then enlarge onto the main canvas.
|
||||||
* @param {OpenSeadragon.Point} [translate] A translation vector that was used to draw the tiles
|
* @param {OpenSeadragon.Point} [options.translate] A translation vector
|
||||||
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
|
* that was used to draw the tiles
|
||||||
* @returns {undefined}
|
* @param {String} [options.compositeOperation] - How the image is
|
||||||
|
* composited onto other images; see compositeOperation in
|
||||||
|
* {@link OpenSeadragon.Options} for possible values.
|
||||||
|
* @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch
|
||||||
|
* canvas to blend in the main canvas. If specified, options.scale and
|
||||||
|
* options.translate get ignored.
|
||||||
*/
|
*/
|
||||||
blendSketch: function(opacity, scale, translate, compositeOperation) {
|
blendSketch: function(opacity, scale, translate, compositeOperation) {
|
||||||
|
var options = opacity;
|
||||||
|
if (!$.isPlainObject(options)) {
|
||||||
|
options = {
|
||||||
|
opacity: opacity,
|
||||||
|
scale: scale,
|
||||||
|
translate: translate,
|
||||||
|
compositeOperation: compositeOperation
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!this.useCanvas || !this.sketchCanvas) {
|
if (!this.useCanvas || !this.sketchCanvas) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scale = scale || 1;
|
opacity = options.opacity;
|
||||||
|
compositeOperation = options.compositeOperation;
|
||||||
|
var bounds = options.bounds;
|
||||||
|
|
||||||
|
this.context.save();
|
||||||
|
this.context.globalAlpha = opacity;
|
||||||
|
if (compositeOperation) {
|
||||||
|
this.context.globalCompositeOperation = compositeOperation;
|
||||||
|
}
|
||||||
|
if (bounds) {
|
||||||
|
this.context.drawImage(
|
||||||
|
this.sketchCanvas,
|
||||||
|
bounds.x,
|
||||||
|
bounds.y,
|
||||||
|
bounds.width,
|
||||||
|
bounds.height,
|
||||||
|
bounds.x,
|
||||||
|
bounds.y,
|
||||||
|
bounds.width,
|
||||||
|
bounds.height
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
scale = options.scale || 1;
|
||||||
|
translate = options.translate;
|
||||||
var position = translate instanceof $.Point ?
|
var position = translate instanceof $.Point ?
|
||||||
translate :
|
translate : new $.Point(0, 0);
|
||||||
new $.Point(0, 0);
|
|
||||||
|
|
||||||
var widthExt = 0;
|
var widthExt = 0;
|
||||||
var heightExt = 0;
|
var heightExt = 0;
|
||||||
@ -406,12 +448,6 @@ $.Drawer.prototype = {
|
|||||||
widthExt = Math.round(widthDiff / 2);
|
widthExt = Math.round(widthDiff / 2);
|
||||||
heightExt = Math.round(heightDiff / 2);
|
heightExt = Math.round(heightDiff / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.context.save();
|
|
||||||
this.context.globalAlpha = opacity;
|
|
||||||
if (compositeOperation) {
|
|
||||||
this.context.globalCompositeOperation = compositeOperation;
|
|
||||||
}
|
|
||||||
this.context.drawImage(
|
this.context.drawImage(
|
||||||
this.sketchCanvas,
|
this.sketchCanvas,
|
||||||
position.x - widthExt * scale,
|
position.x - widthExt * scale,
|
||||||
@ -423,6 +459,7 @@ $.Drawer.prototype = {
|
|||||||
this.canvas.width + 2 * widthExt,
|
this.canvas.width + 2 * widthExt,
|
||||||
this.canvas.height + 2 * heightExt
|
this.canvas.height + 2 * heightExt
|
||||||
);
|
);
|
||||||
|
}
|
||||||
this.context.restore();
|
this.context.restore();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -301,7 +301,9 @@ function configureFromXML( tileSource, xmlDoc ){
|
|||||||
} else if ( rootName == "Collection" ) {
|
} else if ( rootName == "Collection" ) {
|
||||||
throw new Error( $.getString( "Errors.Dzc" ) );
|
throw new Error( $.getString( "Errors.Dzc" ) );
|
||||||
} else if ( rootName == "Error" ) {
|
} else if ( rootName == "Error" ) {
|
||||||
return $._processDZIError( root );
|
var messageNode = root.getElementsByTagName("Message")[0];
|
||||||
|
var message = messageNode.firstChild.nodeValue;
|
||||||
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error( $.getString( "Errors.Dzi" ) );
|
throw new Error( $.getString( "Errors.Dzi" ) );
|
||||||
|
@ -56,7 +56,31 @@ $.EventSource = function() {
|
|||||||
/** @lends OpenSeadragon.EventSource.prototype */
|
/** @lends OpenSeadragon.EventSource.prototype */
|
||||||
$.EventSource.prototype = {
|
$.EventSource.prototype = {
|
||||||
|
|
||||||
// TODO: Add a method 'one' which automatically unbinds a listener after the first triggered event that matches.
|
/**
|
||||||
|
* Add an event handler to be triggered only once (or a given number of times)
|
||||||
|
* for a given event.
|
||||||
|
* @function
|
||||||
|
* @param {String} eventName - Name of event to register.
|
||||||
|
* @param {OpenSeadragon.EventHandler} handler - Function to call when event
|
||||||
|
* is triggered.
|
||||||
|
* @param {Object} [userData=null] - Arbitrary object to be passed unchanged
|
||||||
|
* to the handler.
|
||||||
|
* @param {Number} [times=1] - The number of times to handle the event
|
||||||
|
* before removing it.
|
||||||
|
*/
|
||||||
|
addOnceHandler: function(eventName, handler, userData, times) {
|
||||||
|
var self = this;
|
||||||
|
times = times || 1;
|
||||||
|
var count = 0;
|
||||||
|
var onceHandler = function(event) {
|
||||||
|
count++;
|
||||||
|
if (count === times) {
|
||||||
|
self.removeHandler(eventName, onceHandler);
|
||||||
|
}
|
||||||
|
handler(event);
|
||||||
|
};
|
||||||
|
this.addHandler(eventName, onceHandler, userData);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an event handler for a given event.
|
* Add an event handler for a given event.
|
||||||
|
@ -1357,11 +1357,11 @@
|
|||||||
* @private
|
* @private
|
||||||
* @inner
|
* @inner
|
||||||
*/
|
*/
|
||||||
function capturePointer( tracker, pointerType ) {
|
function capturePointer( tracker, pointerType, pointerCount ) {
|
||||||
var pointsList = tracker.getActivePointersListByType( pointerType ),
|
var pointsList = tracker.getActivePointersListByType( pointerType ),
|
||||||
eventParams;
|
eventParams;
|
||||||
|
|
||||||
pointsList.captureCount++;
|
pointsList.captureCount += (pointerCount || 1);
|
||||||
|
|
||||||
if ( pointsList.captureCount === 1 ) {
|
if ( pointsList.captureCount === 1 ) {
|
||||||
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
||||||
@ -1400,11 +1400,11 @@
|
|||||||
* @private
|
* @private
|
||||||
* @inner
|
* @inner
|
||||||
*/
|
*/
|
||||||
function releasePointer( tracker, pointerType ) {
|
function releasePointer( tracker, pointerType, pointerCount ) {
|
||||||
var pointsList = tracker.getActivePointersListByType( pointerType ),
|
var pointsList = tracker.getActivePointersListByType( pointerType ),
|
||||||
eventParams;
|
eventParams;
|
||||||
|
|
||||||
pointsList.captureCount--;
|
pointsList.captureCount -= (pointerCount || 1);
|
||||||
|
|
||||||
if ( pointsList.captureCount === 0 ) {
|
if ( pointsList.captureCount === 0 ) {
|
||||||
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
|
||||||
@ -2074,7 +2074,7 @@
|
|||||||
|
|
||||||
if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
|
if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
|
||||||
$.stopEvent( event );
|
$.stopEvent( event );
|
||||||
capturePointer( tracker, 'touch' );
|
capturePointer( tracker, 'touch', touchCount );
|
||||||
}
|
}
|
||||||
|
|
||||||
$.cancelEvent( event );
|
$.cancelEvent( event );
|
||||||
@ -2128,7 +2128,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( updatePointersUp( tracker, event, gPoints, 0 ) ) {
|
if ( updatePointersUp( tracker, event, gPoints, 0 ) ) {
|
||||||
releasePointer( tracker, 'touch' );
|
releasePointer( tracker, 'touch', touchCount );
|
||||||
}
|
}
|
||||||
|
|
||||||
// simulate touchleave on our tracked element
|
// simulate touchleave on our tracked element
|
||||||
|
@ -62,7 +62,7 @@ $.Navigator = function( options ){
|
|||||||
options.controlOptions = {
|
options.controlOptions = {
|
||||||
anchor: $.ControlAnchor.TOP_RIGHT,
|
anchor: $.ControlAnchor.TOP_RIGHT,
|
||||||
attachToViewer: true,
|
attachToViewer: true,
|
||||||
autoFade: true
|
autoFade: options.autoFade
|
||||||
};
|
};
|
||||||
|
|
||||||
if( options.position ){
|
if( options.position ){
|
||||||
@ -306,8 +306,8 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
|
|||||||
this.updateSize();
|
this.updateSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if( viewport && this.viewport ) {
|
if (viewport && this.viewport) {
|
||||||
bounds = viewport.getBounds( true );
|
bounds = viewport.getBoundsNoRotate(true);
|
||||||
topleft = this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(), false);
|
topleft = this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(), false);
|
||||||
bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false)
|
bottomright = this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(), false)
|
||||||
.minus( this.totalBorderWidths );
|
.minus( this.totalBorderWidths );
|
||||||
|
@ -82,28 +82,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @version <%= pkg.name %> <%= pkg.version %>
|
|
||||||
*
|
|
||||||
* @file
|
|
||||||
* <h2><strong>OpenSeadragon - Javascript Deep Zooming</strong></h2>
|
|
||||||
* <p>
|
|
||||||
* OpenSeadragon provides an html interface for creating
|
|
||||||
* deep zoom user interfaces. The simplest examples include deep
|
|
||||||
* zoom for large resolution images, and complex examples include
|
|
||||||
* zoomable map interfaces driven by SVG files.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @module OpenSeadragon
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @namespace OpenSeadragon
|
* @namespace OpenSeadragon
|
||||||
*
|
* @version <%= pkg.name %> <%= pkg.version %>
|
||||||
* @classdesc The root namespace for OpenSeadragon. All utility methods
|
* @classdesc The root namespace for OpenSeadragon. All utility methods
|
||||||
* and classes are defined on or below this namespace.
|
* and classes are defined on or below this namespace.
|
||||||
*
|
*
|
||||||
@ -154,7 +135,7 @@
|
|||||||
* created.
|
* created.
|
||||||
* * placement a string to define the relative position to the viewport.
|
* * placement a string to define the relative position to the viewport.
|
||||||
* Only used if no width and height are specified. Default: 'TOP_LEFT'.
|
* Only used if no width and height are specified. Default: 'TOP_LEFT'.
|
||||||
* See {@link OpenSeadragon.OverlayPlacement} for possible values.
|
* See {@link OpenSeadragon.Placement} for possible values.
|
||||||
*
|
*
|
||||||
* @property {String} [xmlPath=null]
|
* @property {String} [xmlPath=null]
|
||||||
* <strong>DEPRECATED</strong>. A relative path to load a DZI file from the server.
|
* <strong>DEPRECATED</strong>. A relative path to load a DZI file from the server.
|
||||||
@ -373,16 +354,16 @@
|
|||||||
*
|
*
|
||||||
* @property {String} [navigatorId=navigator-GENERATED DATE]
|
* @property {String} [navigatorId=navigator-GENERATED DATE]
|
||||||
* The ID of a div to hold the navigator minimap.
|
* The ID of a div to hold the navigator minimap.
|
||||||
* If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored.
|
* If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, navigator[Top|Left|Height|Width] and navigatorAutoFade options will be ignored.
|
||||||
* If an ID is not specified, a div element will be generated and placed on top of the main image.
|
* If an ID is not specified, a div element will be generated and placed on top of the main image.
|
||||||
*
|
*
|
||||||
* @property {String} [navigatorPosition='TOP_RIGHT']
|
* @property {String} [navigatorPosition='TOP_RIGHT']
|
||||||
* Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br>
|
* Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br>
|
||||||
* If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br>
|
* If 'ABSOLUTE' is specified, then navigator[Top|Left|Height|Width] determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br>
|
||||||
* For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap.
|
* For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigator[Height|Width] values determine the size of the navigator minimap.
|
||||||
*
|
*
|
||||||
* @property {Number} [navigatorSizeRatio=0.2]
|
* @property {Number} [navigatorSizeRatio=0.2]
|
||||||
* Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified.
|
* Ratio of navigator size to viewer size. Ignored if navigator[Height|Width] are specified.
|
||||||
*
|
*
|
||||||
* @property {Boolean} [navigatorMaintainSizeRatio=false]
|
* @property {Boolean} [navigatorMaintainSizeRatio=false]
|
||||||
* If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.
|
* If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.
|
||||||
@ -405,6 +386,10 @@
|
|||||||
* Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.
|
* Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.
|
||||||
* Setting to false can also improve performance when the navigator is configured to a fixed size.
|
* Setting to false can also improve performance when the navigator is configured to a fixed size.
|
||||||
*
|
*
|
||||||
|
* @property {Boolean} [navigatorAutoFade=true]
|
||||||
|
* If the user stops interacting with the viewport, fade the navigator minimap.
|
||||||
|
* Setting to false will make the navigator minimap always visible.
|
||||||
|
*
|
||||||
* @property {Boolean} [navigatorRotate=true]
|
* @property {Boolean} [navigatorRotate=true]
|
||||||
* If true, the navigator will be rotated together with the viewer.
|
* If true, the navigator will be rotated together with the viewer.
|
||||||
*
|
*
|
||||||
@ -691,8 +676,6 @@
|
|||||||
* This function serves as a single point of instantiation for an {@link OpenSeadragon.Viewer}, including all
|
* This function serves as a single point of instantiation for an {@link OpenSeadragon.Viewer}, including all
|
||||||
* combinations of out-of-the-box configurable features.
|
* combinations of out-of-the-box configurable features.
|
||||||
*
|
*
|
||||||
* @function OpenSeadragon
|
|
||||||
* @memberof module:OpenSeadragon
|
|
||||||
* @param {OpenSeadragon.Options} options - Viewer options.
|
* @param {OpenSeadragon.Options} options - Viewer options.
|
||||||
* @returns {OpenSeadragon.Viewer}
|
* @returns {OpenSeadragon.Viewer}
|
||||||
*/
|
*/
|
||||||
@ -842,6 +825,21 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shim around Object.freeze. Does nothing if Object.freeze is not supported.
|
||||||
|
* @param {Object} obj The object to freeze.
|
||||||
|
* @return {Object} obj The frozen object.
|
||||||
|
*/
|
||||||
|
$.freezeObject = function(obj) {
|
||||||
|
if (Object.freeze) {
|
||||||
|
$.freezeObject = Object.freeze;
|
||||||
|
} else {
|
||||||
|
$.freezeObject = function(obj) {
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return $.freezeObject(obj);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the browser supports the HTML5 canvas element
|
* True if the browser supports the HTML5 canvas element
|
||||||
@ -1065,6 +1063,7 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
navigatorHeight: null,
|
navigatorHeight: null,
|
||||||
navigatorWidth: null,
|
navigatorWidth: null,
|
||||||
navigatorAutoResize: true,
|
navigatorAutoResize: true,
|
||||||
|
navigatorAutoFade: true,
|
||||||
navigatorRotate: true,
|
navigatorRotate: true,
|
||||||
|
|
||||||
// INITIAL ROTATION
|
// INITIAL ROTATION
|
||||||
@ -1322,6 +1321,49 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
return window.getComputedStyle( element, "" );
|
return window.getComputedStyle( element, "" );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the property with the correct vendor prefix appended.
|
||||||
|
* @param {String} property the property name
|
||||||
|
* @returns {String} the property with the correct prefix or null if not
|
||||||
|
* supported.
|
||||||
|
*/
|
||||||
|
getCssPropertyWithVendorPrefix: function(property) {
|
||||||
|
var memo = {};
|
||||||
|
|
||||||
|
$.getCssPropertyWithVendorPrefix = function(property) {
|
||||||
|
if (memo[property] !== undefined) {
|
||||||
|
return memo[property];
|
||||||
|
}
|
||||||
|
var style = document.createElement('div').style;
|
||||||
|
var result = null;
|
||||||
|
if (style[property] !== undefined) {
|
||||||
|
result = property;
|
||||||
|
} else {
|
||||||
|
var prefixes = ['Webkit', 'Moz', 'MS', 'O',
|
||||||
|
'webkit', 'moz', 'ms', 'o'];
|
||||||
|
var suffix = $.capitalizeFirstLetter(property);
|
||||||
|
for (var i = 0; i < prefixes.length; i++) {
|
||||||
|
var prop = prefixes[i] + suffix;
|
||||||
|
if (style[prop] !== undefined) {
|
||||||
|
result = prop;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memo[property] = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
return $.getCssPropertyWithVendorPrefix(property);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capitalizes the first letter of a string
|
||||||
|
* @param {String} string
|
||||||
|
* @returns {String} The string with the first letter capitalized
|
||||||
|
*/
|
||||||
|
capitalizeFirstLetter: function(string) {
|
||||||
|
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a point is within the bounding rectangle of the given element (hit-test).
|
* Determines if a point is within the bounding rectangle of the given element (hit-test).
|
||||||
@ -2533,185 +2575,4 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
}(OpenSeadragon));
|
||||||
* @private
|
|
||||||
* @inner
|
|
||||||
* @function
|
|
||||||
* @param {XMLHttpRequest} xhr
|
|
||||||
* @param {String} tilesUrl
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
function processDZIResponse( xhr, tilesUrl ) {
|
|
||||||
var status,
|
|
||||||
statusText,
|
|
||||||
doc = null;
|
|
||||||
|
|
||||||
if ( !xhr ) {
|
|
||||||
throw new Error( $.getString( "Errors.Security" ) );
|
|
||||||
} else if ( xhr.status !== 200 && xhr.status !== 0 ) {
|
|
||||||
status = xhr.status;
|
|
||||||
statusText = ( status == 404 ) ?
|
|
||||||
"Not Found" :
|
|
||||||
xhr.statusText;
|
|
||||||
throw new Error( $.getString( "Errors.Status", status, statusText ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( xhr.responseXML && xhr.responseXML.documentElement ) {
|
|
||||||
doc = xhr.responseXML;
|
|
||||||
} else if ( xhr.responseText ) {
|
|
||||||
doc = $.parseXml( xhr.responseText );
|
|
||||||
}
|
|
||||||
|
|
||||||
return processDZIXml( doc, tilesUrl );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @inner
|
|
||||||
* @function
|
|
||||||
* @param {Document} xmlDoc
|
|
||||||
* @param {String} tilesUrl
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
function processDZIXml( xmlDoc, tilesUrl ) {
|
|
||||||
|
|
||||||
if ( !xmlDoc || !xmlDoc.documentElement ) {
|
|
||||||
throw new Error( $.getString( "Errors.Xml" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = xmlDoc.documentElement,
|
|
||||||
rootName = root.tagName;
|
|
||||||
|
|
||||||
if ( rootName == "Image" ) {
|
|
||||||
try {
|
|
||||||
return processDZI( root, tilesUrl );
|
|
||||||
} catch ( e ) {
|
|
||||||
throw (e instanceof Error) ?
|
|
||||||
e :
|
|
||||||
new Error( $.getString("Errors.Dzi") );
|
|
||||||
}
|
|
||||||
} else if ( rootName == "Collection" ) {
|
|
||||||
throw new Error( $.getString( "Errors.Dzc" ) );
|
|
||||||
} else if ( rootName == "Error" ) {
|
|
||||||
return $._processDZIError( root );
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error( $.getString( "Errors.Dzi" ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @inner
|
|
||||||
* @function
|
|
||||||
* @param {Element} imageNode
|
|
||||||
* @param {String} tilesUrl
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
function processDZI( imageNode, tilesUrl ) {
|
|
||||||
var fileFormat = imageNode.getAttribute( "Format" ),
|
|
||||||
sizeNode = imageNode.getElementsByTagName( "Size" )[ 0 ],
|
|
||||||
dispRectNodes = imageNode.getElementsByTagName( "DisplayRect" ),
|
|
||||||
width = parseInt( sizeNode.getAttribute( "Width" ), 10 ),
|
|
||||||
height = parseInt( sizeNode.getAttribute( "Height" ), 10 ),
|
|
||||||
tileSize = parseInt( imageNode.getAttribute( "TileSize" ), 10 ),
|
|
||||||
tileOverlap = parseInt( imageNode.getAttribute( "Overlap" ), 10 ),
|
|
||||||
dispRects = [],
|
|
||||||
dispRectNode,
|
|
||||||
rectNode,
|
|
||||||
i;
|
|
||||||
|
|
||||||
if ( !$.imageFormatSupported( fileFormat ) ) {
|
|
||||||
throw new Error(
|
|
||||||
$.getString( "Errors.ImageFormat", fileFormat.toUpperCase() )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( i = 0; i < dispRectNodes.length; i++ ) {
|
|
||||||
dispRectNode = dispRectNodes[ i ];
|
|
||||||
rectNode = dispRectNode.getElementsByTagName( "Rect" )[ 0 ];
|
|
||||||
|
|
||||||
dispRects.push( new $.DisplayRect(
|
|
||||||
parseInt( rectNode.getAttribute( "X" ), 10 ),
|
|
||||||
parseInt( rectNode.getAttribute( "Y" ), 10 ),
|
|
||||||
parseInt( rectNode.getAttribute( "Width" ), 10 ),
|
|
||||||
parseInt( rectNode.getAttribute( "Height" ), 10 ),
|
|
||||||
0, // ignore MinLevel attribute, bug in Deep Zoom Composer
|
|
||||||
parseInt( dispRectNode.getAttribute( "MaxLevel" ), 10 )
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return new $.DziTileSource(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
tileSize,
|
|
||||||
tileOverlap,
|
|
||||||
tilesUrl,
|
|
||||||
fileFormat,
|
|
||||||
dispRects
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @inner
|
|
||||||
* @function
|
|
||||||
* @param {Element} imageNode
|
|
||||||
* @param {String} tilesUrl
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
function processDZIJSON( imageData, tilesUrl ) {
|
|
||||||
var fileFormat = imageData.Format,
|
|
||||||
sizeData = imageData.Size,
|
|
||||||
dispRectData = imageData.DisplayRect || [],
|
|
||||||
width = parseInt( sizeData.Width, 10 ),
|
|
||||||
height = parseInt( sizeData.Height, 10 ),
|
|
||||||
tileSize = parseInt( imageData.TileSize, 10 ),
|
|
||||||
tileOverlap = parseInt( imageData.Overlap, 10 ),
|
|
||||||
dispRects = [],
|
|
||||||
rectData,
|
|
||||||
i;
|
|
||||||
|
|
||||||
if ( !$.imageFormatSupported( fileFormat ) ) {
|
|
||||||
throw new Error(
|
|
||||||
$.getString( "Errors.ImageFormat", fileFormat.toUpperCase() )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( i = 0; i < dispRectData.length; i++ ) {
|
|
||||||
rectData = dispRectData[ i ].Rect;
|
|
||||||
|
|
||||||
dispRects.push( new $.DisplayRect(
|
|
||||||
parseInt( rectData.X, 10 ),
|
|
||||||
parseInt( rectData.Y, 10 ),
|
|
||||||
parseInt( rectData.Width, 10 ),
|
|
||||||
parseInt( rectData.Height, 10 ),
|
|
||||||
0, // ignore MinLevel attribute, bug in Deep Zoom Composer
|
|
||||||
parseInt( rectData.MaxLevel, 10 )
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return new $.DziTileSource(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
tileSize,
|
|
||||||
tileOverlap,
|
|
||||||
tilesUrl,
|
|
||||||
fileFormat,
|
|
||||||
dispRects
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @private
|
|
||||||
* @inner
|
|
||||||
* @function
|
|
||||||
* @param {Document} errorNode
|
|
||||||
* @throws {Error}
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
$._processDZIError = function ( errorNode ) {
|
|
||||||
var messageNode = errorNode.getElementsByTagName( "Message" )[ 0 ],
|
|
||||||
message = messageNode.firstChild.nodeValue;
|
|
||||||
|
|
||||||
throw new Error(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
}( OpenSeadragon ));
|
|
||||||
|
478
src/overlay.js
478
src/overlay.js
@ -32,14 +32,17 @@
|
|||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function( $ ){
|
(function($) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An enumeration of positions that an overlay may be assigned relative to
|
* An enumeration of positions that an overlay may be assigned relative to
|
||||||
* the viewport.
|
* the viewport.
|
||||||
|
* It is identical to OpenSeadragon.Placement but is kept for backward
|
||||||
|
* compatibility.
|
||||||
* @member OverlayPlacement
|
* @member OverlayPlacement
|
||||||
* @memberof OpenSeadragon
|
* @memberof OpenSeadragon
|
||||||
* @static
|
* @static
|
||||||
|
* @readonly
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @property {Number} CENTER
|
* @property {Number} CENTER
|
||||||
* @property {Number} TOP_LEFT
|
* @property {Number} TOP_LEFT
|
||||||
@ -51,17 +54,26 @@
|
|||||||
* @property {Number} BOTTOM_LEFT
|
* @property {Number} BOTTOM_LEFT
|
||||||
* @property {Number} LEFT
|
* @property {Number} LEFT
|
||||||
*/
|
*/
|
||||||
$.OverlayPlacement = {
|
$.OverlayPlacement = $.Placement;
|
||||||
CENTER: 0,
|
|
||||||
TOP_LEFT: 1,
|
/**
|
||||||
TOP: 2,
|
* An enumeration of possible ways to handle overlays rotation
|
||||||
TOP_RIGHT: 3,
|
* @member OverlayRotationMode
|
||||||
RIGHT: 4,
|
* @memberOf OpenSeadragon
|
||||||
BOTTOM_RIGHT: 5,
|
* @static
|
||||||
BOTTOM: 6,
|
* @readonly
|
||||||
BOTTOM_LEFT: 7,
|
* @property {Number} NO_ROTATION The overlay ignore the viewport rotation.
|
||||||
LEFT: 8
|
* @property {Number} EXACT The overlay use CSS 3 transforms to rotate with
|
||||||
};
|
* the viewport. If the overlay contains text, it will get rotated as well.
|
||||||
|
* @property {Number} BOUNDING_BOX The overlay adjusts for rotation by
|
||||||
|
* taking the size of the bounding box of the rotated bounds.
|
||||||
|
* Only valid for overlays with Rect location and scalable in both directions.
|
||||||
|
*/
|
||||||
|
$.OverlayRotationMode = $.freezeObject({
|
||||||
|
NO_ROTATION: 1,
|
||||||
|
EXACT: 2,
|
||||||
|
BOUNDING_BOX: 3
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Overlay
|
* @class Overlay
|
||||||
@ -72,19 +84,27 @@
|
|||||||
* @param {Element} options.element
|
* @param {Element} options.element
|
||||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The
|
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The
|
||||||
* location of the overlay on the image. If a {@link OpenSeadragon.Point}
|
* location of the overlay on the image. If a {@link OpenSeadragon.Point}
|
||||||
* is specified, the overlay will keep a constant size independently of the
|
* is specified, the overlay will be located at this location with respect
|
||||||
* zoom. If a {@link OpenSeadragon.Rect} is specified, the overlay size will
|
* to the placement option. If a {@link OpenSeadragon.Rect} is specified,
|
||||||
* be adjusted when the zoom changes.
|
* the overlay will be placed at this location with the corresponding width
|
||||||
* @param {OpenSeadragon.OverlayPlacement} [options.placement=OpenSeadragon.OverlayPlacement.TOP_LEFT]
|
* and height and placement TOP_LEFT.
|
||||||
* Relative position to the viewport.
|
* @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT]
|
||||||
* Only used if location is a {@link OpenSeadragon.Point}.
|
* Defines what part of the overlay should be at the specified options.location
|
||||||
* @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]
|
* @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]
|
||||||
* @param {Boolean} [options.checkResize=true] Set to false to avoid to
|
* @param {Boolean} [options.checkResize=true] Set to false to avoid to
|
||||||
* check the size of the overlay everytime it is drawn when using a
|
* check the size of the overlay everytime it is drawn in the directions
|
||||||
* {@link OpenSeadragon.Point} as options.location. It will improve
|
* which are not scaled. It will improve performances but will cause a
|
||||||
* performances but will cause a misalignment if the overlay size changes.
|
* misalignment if the overlay size changes.
|
||||||
|
* @param {Number} [options.width] The width of the overlay in viewport
|
||||||
|
* coordinates. If specified, the width of the overlay will be adjusted when
|
||||||
|
* the zoom changes.
|
||||||
|
* @param {Number} [options.height] The height of the overlay in viewport
|
||||||
|
* coordinates. If specified, the height of the overlay will be adjusted when
|
||||||
|
* the zoom changes.
|
||||||
|
* @param {Boolean} [options.rotationMode=OpenSeadragon.OverlayRotationMode.EXACT]
|
||||||
|
* How to handle the rotation of the viewport.
|
||||||
*/
|
*/
|
||||||
$.Overlay = function( element, location, placement ) {
|
$.Overlay = function(element, location, placement) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onDraw callback signature used by {@link OpenSeadragon.Overlay}.
|
* onDraw callback signature used by {@link OpenSeadragon.Overlay}.
|
||||||
@ -97,7 +117,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
var options;
|
var options;
|
||||||
if ( $.isPlainObject( element ) ) {
|
if ($.isPlainObject(element)) {
|
||||||
options = element;
|
options = element;
|
||||||
} else {
|
} else {
|
||||||
options = {
|
options = {
|
||||||
@ -108,72 +128,66 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.element = options.element;
|
this.element = options.element;
|
||||||
this.scales = options.location instanceof $.Rect;
|
|
||||||
this.bounds = new $.Rect(
|
|
||||||
options.location.x,
|
|
||||||
options.location.y,
|
|
||||||
options.location.width,
|
|
||||||
options.location.height
|
|
||||||
);
|
|
||||||
this.position = new $.Point(
|
|
||||||
options.location.x,
|
|
||||||
options.location.y
|
|
||||||
);
|
|
||||||
this.size = new $.Point(
|
|
||||||
options.location.width,
|
|
||||||
options.location.height
|
|
||||||
);
|
|
||||||
this.style = options.element.style;
|
this.style = options.element.style;
|
||||||
// rects are always top-left
|
this._init(options);
|
||||||
this.placement = options.location instanceof $.Point ?
|
|
||||||
options.placement :
|
|
||||||
$.OverlayPlacement.TOP_LEFT;
|
|
||||||
this.onDraw = options.onDraw;
|
|
||||||
this.checkResize = options.checkResize === undefined ?
|
|
||||||
true : options.checkResize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @lends OpenSeadragon.Overlay.prototype */
|
/** @lends OpenSeadragon.Overlay.prototype */
|
||||||
$.Overlay.prototype = {
|
$.Overlay.prototype = {
|
||||||
|
|
||||||
|
// private
|
||||||
|
_init: function(options) {
|
||||||
|
this.location = options.location;
|
||||||
|
this.placement = options.placement === undefined ?
|
||||||
|
$.Placement.TOP_LEFT : options.placement;
|
||||||
|
this.onDraw = options.onDraw;
|
||||||
|
this.checkResize = options.checkResize === undefined ?
|
||||||
|
true : options.checkResize;
|
||||||
|
|
||||||
|
// When this.width is not null, the overlay get scaled horizontally
|
||||||
|
this.width = options.width === undefined ? null : options.width;
|
||||||
|
|
||||||
|
// When this.height is not null, the overlay get scaled vertically
|
||||||
|
this.height = options.height === undefined ? null : options.height;
|
||||||
|
|
||||||
|
this.rotationMode = options.rotationMode || $.OverlayRotationMode.EXACT;
|
||||||
|
|
||||||
|
// Having a rect as location is a syntactic sugar
|
||||||
|
if (this.location instanceof $.Rect) {
|
||||||
|
this.width = this.location.width;
|
||||||
|
this.height = this.location.height;
|
||||||
|
this.location = this.location.getTopLeft();
|
||||||
|
this.placement = $.Placement.TOP_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated properties kept for backward compatibility.
|
||||||
|
this.scales = this.width !== null && this.height !== null;
|
||||||
|
this.bounds = new $.Rect(
|
||||||
|
this.location.x, this.location.y, this.width, this.height);
|
||||||
|
this.position = this.location;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Internal function to adjust the position of an overlay
|
||||||
|
* depending on it size and placement.
|
||||||
* @function
|
* @function
|
||||||
* @param {OpenSeadragon.OverlayPlacement} position
|
* @param {OpenSeadragon.Point} position
|
||||||
* @param {OpenSeadragon.Point} size
|
* @param {OpenSeadragon.Point} size
|
||||||
*/
|
*/
|
||||||
adjust: function( position, size ) {
|
adjust: function(position, size) {
|
||||||
switch ( this.placement ) {
|
var properties = $.Placement.properties[this.placement];
|
||||||
case $.OverlayPlacement.TOP_LEFT:
|
if (!properties) {
|
||||||
break;
|
return;
|
||||||
case $.OverlayPlacement.TOP:
|
}
|
||||||
|
if (properties.isHorizontallyCentered) {
|
||||||
position.x -= size.x / 2;
|
position.x -= size.x / 2;
|
||||||
break;
|
} else if (properties.isRight) {
|
||||||
case $.OverlayPlacement.TOP_RIGHT:
|
|
||||||
position.x -= size.x;
|
|
||||||
break;
|
|
||||||
case $.OverlayPlacement.RIGHT:
|
|
||||||
position.x -= size.x;
|
position.x -= size.x;
|
||||||
|
}
|
||||||
|
if (properties.isVerticallyCentered) {
|
||||||
position.y -= size.y / 2;
|
position.y -= size.y / 2;
|
||||||
break;
|
} else if (properties.isBottom) {
|
||||||
case $.OverlayPlacement.BOTTOM_RIGHT:
|
|
||||||
position.x -= size.x;
|
|
||||||
position.y -= size.y;
|
position.y -= size.y;
|
||||||
break;
|
|
||||||
case $.OverlayPlacement.BOTTOM:
|
|
||||||
position.x -= size.x / 2;
|
|
||||||
position.y -= size.y;
|
|
||||||
break;
|
|
||||||
case $.OverlayPlacement.BOTTOM_LEFT:
|
|
||||||
position.y -= size.y;
|
|
||||||
break;
|
|
||||||
case $.OverlayPlacement.LEFT:
|
|
||||||
position.y -= size.y / 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case $.OverlayPlacement.CENTER:
|
|
||||||
position.x -= size.x / 2;
|
|
||||||
position.y -= size.y / 2;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -181,20 +195,20 @@
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
var element = this.element,
|
var element = this.element;
|
||||||
style = this.style;
|
var style = this.style;
|
||||||
|
|
||||||
if ( element.parentNode ) {
|
if (element.parentNode) {
|
||||||
element.parentNode.removeChild( element );
|
element.parentNode.removeChild(element);
|
||||||
//this should allow us to preserve overlays when required between
|
//this should allow us to preserve overlays when required between
|
||||||
//pages
|
//pages
|
||||||
if ( element.prevElementParent ) {
|
if (element.prevElementParent) {
|
||||||
style.display = 'none';
|
style.display = 'none';
|
||||||
//element.prevElementParent.insertBefore(
|
//element.prevElementParent.insertBefore(
|
||||||
// element,
|
// element,
|
||||||
// element.prevNextSibling
|
// element.prevNextSibling
|
||||||
//);
|
//);
|
||||||
document.body.appendChild( element );
|
document.body.appendChild(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,122 +219,258 @@
|
|||||||
style.left = "";
|
style.left = "";
|
||||||
style.position = "";
|
style.position = "";
|
||||||
|
|
||||||
if ( this.scales ) {
|
if (this.width !== null) {
|
||||||
style.width = "";
|
style.width = "";
|
||||||
|
}
|
||||||
|
if (this.height !== null) {
|
||||||
style.height = "";
|
style.height = "";
|
||||||
}
|
}
|
||||||
|
var transformOriginProp = $.getCssPropertyWithVendorPrefix(
|
||||||
|
'transformOrigin');
|
||||||
|
var transformProp = $.getCssPropertyWithVendorPrefix(
|
||||||
|
'transform');
|
||||||
|
if (transformOriginProp && transformProp) {
|
||||||
|
style[transformOriginProp] = "";
|
||||||
|
style[transformProp] = "";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
* @param {Element} container
|
* @param {Element} container
|
||||||
*/
|
*/
|
||||||
drawHTML: function( container, viewport ) {
|
drawHTML: function(container, viewport) {
|
||||||
var element = this.element,
|
var element = this.element;
|
||||||
style = this.style,
|
if (element.parentNode !== container) {
|
||||||
scales = this.scales,
|
|
||||||
degrees = viewport.degrees,
|
|
||||||
position = viewport.pixelFromPoint(
|
|
||||||
this.bounds.getTopLeft(),
|
|
||||||
true
|
|
||||||
),
|
|
||||||
size,
|
|
||||||
overlayCenter;
|
|
||||||
|
|
||||||
if ( element.parentNode != container ) {
|
|
||||||
//save the source parent for later if we need it
|
//save the source parent for later if we need it
|
||||||
element.prevElementParent = element.parentNode;
|
element.prevElementParent = element.parentNode;
|
||||||
element.prevNextSibling = element.nextSibling;
|
element.prevNextSibling = element.nextSibling;
|
||||||
container.appendChild( element );
|
container.appendChild(element);
|
||||||
this.size = $.getElementSize( element );
|
|
||||||
|
// this.size is used by overlays which don't get scaled in at
|
||||||
|
// least one direction when this.checkResize is set to false.
|
||||||
|
this.size = $.getElementSize(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( scales ) {
|
var positionAndSize = this._getOverlayPositionAndSize(viewport);
|
||||||
size = viewport.deltaPixelsFromPoints(
|
|
||||||
this.bounds.getSize(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} else if ( this.checkResize ) {
|
|
||||||
size = $.getElementSize( element );
|
|
||||||
} else {
|
|
||||||
size = this.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.position = position;
|
var position = positionAndSize.position;
|
||||||
this.size = size;
|
var size = this.size = positionAndSize.size;
|
||||||
|
var rotate = positionAndSize.rotate;
|
||||||
this.adjust( position, size );
|
|
||||||
|
|
||||||
position = position.apply( Math.round );
|
|
||||||
size = size.apply( Math.round );
|
|
||||||
|
|
||||||
// rotate the position of the overlay
|
|
||||||
// TODO only rotate overlays if in canvas mode
|
|
||||||
// TODO replace the size rotation with CSS3 transforms
|
|
||||||
// TODO add an option to overlays to not rotate with the image
|
|
||||||
// Currently only rotates position and size
|
|
||||||
if( degrees !== 0 && this.scales ) {
|
|
||||||
overlayCenter = new $.Point( size.x / 2, size.y / 2 );
|
|
||||||
|
|
||||||
var drawerCenter = new $.Point(
|
|
||||||
viewport.viewer.drawer.canvas.width / 2,
|
|
||||||
viewport.viewer.drawer.canvas.height / 2
|
|
||||||
);
|
|
||||||
position = position.plus( overlayCenter ).rotate(
|
|
||||||
degrees,
|
|
||||||
drawerCenter
|
|
||||||
).minus( overlayCenter );
|
|
||||||
|
|
||||||
size = size.rotate( degrees, new $.Point( 0, 0 ) );
|
|
||||||
size = new $.Point( Math.abs( size.x ), Math.abs( size.y ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// call the onDraw callback if it exists to allow one to overwrite
|
// call the onDraw callback if it exists to allow one to overwrite
|
||||||
// the drawing/positioning/sizing of the overlay
|
// the drawing/positioning/sizing of the overlay
|
||||||
if ( this.onDraw ) {
|
if (this.onDraw) {
|
||||||
this.onDraw( position, size, element );
|
this.onDraw(position, size, this.element);
|
||||||
} else {
|
} else {
|
||||||
|
var style = this.style;
|
||||||
style.left = position.x + "px";
|
style.left = position.x + "px";
|
||||||
style.top = position.y + "px";
|
style.top = position.y + "px";
|
||||||
style.position = "absolute";
|
if (this.width !== null) {
|
||||||
|
|
||||||
if (style.display != 'none') {
|
|
||||||
style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( scales ) {
|
|
||||||
style.width = size.x + "px";
|
style.width = size.x + "px";
|
||||||
|
}
|
||||||
|
if (this.height !== null) {
|
||||||
style.height = size.y + "px";
|
style.height = size.y + "px";
|
||||||
}
|
}
|
||||||
|
var transformOriginProp = $.getCssPropertyWithVendorPrefix(
|
||||||
|
'transformOrigin');
|
||||||
|
var transformProp = $.getCssPropertyWithVendorPrefix(
|
||||||
|
'transform');
|
||||||
|
if (transformOriginProp && transformProp) {
|
||||||
|
if (rotate) {
|
||||||
|
style[transformOriginProp] = this._getTransformOrigin();
|
||||||
|
style[transformProp] = "rotate(" + rotate + "deg)";
|
||||||
|
} else {
|
||||||
|
style[transformOriginProp] = "";
|
||||||
|
style[transformProp] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
style.position = "absolute";
|
||||||
|
|
||||||
|
if (style.display !== 'none') {
|
||||||
|
style.display = 'block';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
// private
|
||||||
* @function
|
_getOverlayPositionAndSize: function(viewport) {
|
||||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location
|
var position = viewport.pixelFromPoint(this.location, true);
|
||||||
* @param {OpenSeadragon.OverlayPlacement} position
|
var size = this._getSizeInPixels(viewport);
|
||||||
*/
|
this.adjust(position, size);
|
||||||
update: function( location, placement ) {
|
|
||||||
this.scales = location instanceof $.Rect;
|
var rotate = 0;
|
||||||
this.bounds = new $.Rect(
|
if (viewport.degrees &&
|
||||||
location.x,
|
this.rotationMode !== $.OverlayRotationMode.NO_ROTATION) {
|
||||||
location.y,
|
// BOUNDING_BOX is only valid if both directions get scaled.
|
||||||
location.width,
|
// Get replaced by EXACT otherwise.
|
||||||
location.height
|
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX &&
|
||||||
);
|
this.width !== null && this.height !== null) {
|
||||||
// rects are always top-left
|
var rect = new $.Rect(position.x, position.y, size.x, size.y);
|
||||||
this.placement = location instanceof $.Point ?
|
var boundingBox = this._getBoundingBox(rect, viewport.degrees);
|
||||||
placement :
|
position = boundingBox.getTopLeft();
|
||||||
$.OverlayPlacement.TOP_LEFT;
|
size = boundingBox.getSize();
|
||||||
|
} else {
|
||||||
|
rotate = viewport.degrees;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
position: position,
|
||||||
|
size: size,
|
||||||
|
rotate: rotate
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_getSizeInPixels: function(viewport) {
|
||||||
|
var width = this.size.x;
|
||||||
|
var height = this.size.y;
|
||||||
|
if (this.width !== null || this.height !== null) {
|
||||||
|
var scaledSize = viewport.deltaPixelsFromPointsNoRotate(
|
||||||
|
new $.Point(this.width || 0, this.height || 0), true);
|
||||||
|
if (this.width !== null) {
|
||||||
|
width = scaledSize.x;
|
||||||
|
}
|
||||||
|
if (this.height !== null) {
|
||||||
|
height = scaledSize.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.checkResize &&
|
||||||
|
(this.width === null || this.height === null)) {
|
||||||
|
var eltSize = this.size = $.getElementSize(this.element);
|
||||||
|
if (this.width === null) {
|
||||||
|
width = eltSize.x;
|
||||||
|
}
|
||||||
|
if (this.height === null) {
|
||||||
|
height = eltSize.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new $.Point(width, height);
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_getBoundingBox: function(rect, degrees) {
|
||||||
|
var refPoint = this._getPlacementPoint(rect);
|
||||||
|
return rect.rotate(degrees, refPoint).getBoundingBox();
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_getPlacementPoint: function(rect) {
|
||||||
|
var result = new $.Point(rect.x, rect.y);
|
||||||
|
var properties = $.Placement.properties[this.placement];
|
||||||
|
if (properties) {
|
||||||
|
if (properties.isHorizontallyCentered) {
|
||||||
|
result.x += rect.width / 2;
|
||||||
|
} else if (properties.isRight) {
|
||||||
|
result.x += rect.width;
|
||||||
|
}
|
||||||
|
if (properties.isVerticallyCentered) {
|
||||||
|
result.y += rect.height / 2;
|
||||||
|
} else if (properties.isBottom) {
|
||||||
|
result.y += rect.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_getTransformOrigin: function() {
|
||||||
|
var result = "";
|
||||||
|
var properties = $.Placement.properties[this.placement];
|
||||||
|
if (!properties) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (properties.isLeft) {
|
||||||
|
result = "left";
|
||||||
|
} else if (properties.isRight) {
|
||||||
|
result = "right";
|
||||||
|
}
|
||||||
|
if (properties.isTop) {
|
||||||
|
result += " top";
|
||||||
|
} else if (properties.isBottom) {
|
||||||
|
result += " bottom";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Changes the overlay settings.
|
||||||
* @function
|
* @function
|
||||||
|
* @param {OpenSeadragon.Point|OpenSeadragon.Rect|Object} location
|
||||||
|
* If an object is specified, the options are the same than the constructor
|
||||||
|
* except for the element which can not be changed.
|
||||||
|
* @param {OpenSeadragon.Placement} position
|
||||||
|
*/
|
||||||
|
update: function(location, placement) {
|
||||||
|
var options = $.isPlainObject(location) ? location : {
|
||||||
|
location: location,
|
||||||
|
placement: placement
|
||||||
|
};
|
||||||
|
this._init({
|
||||||
|
location: options.location || this.location,
|
||||||
|
placement: options.placement !== undefined ?
|
||||||
|
options.placement : this.placement,
|
||||||
|
onDraw: options.onDraw || this.onDraw,
|
||||||
|
checkResize: options.checkResize || this.checkResize,
|
||||||
|
width: options.width !== undefined ? options.width : this.width,
|
||||||
|
height: options.height !== undefined ? options.height : this.height,
|
||||||
|
rotationMode: options.rotationMode || this.rotationMode
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current bounds of the overlay in viewport coordinates
|
||||||
|
* @function
|
||||||
|
* @param {OpenSeadragon.Viewport} viewport the viewport
|
||||||
* @returns {OpenSeadragon.Rect} overlay bounds
|
* @returns {OpenSeadragon.Rect} overlay bounds
|
||||||
*/
|
*/
|
||||||
getBounds: function() {
|
getBounds: function(viewport) {
|
||||||
return this.bounds.clone();
|
$.console.assert(viewport,
|
||||||
|
'A viewport must now be passed to Overlay.getBounds.');
|
||||||
|
var width = this.width;
|
||||||
|
var height = this.height;
|
||||||
|
if (width === null || height === null) {
|
||||||
|
var size = viewport.deltaPointsFromPixelsNoRotate(this.size, true);
|
||||||
|
if (width === null) {
|
||||||
|
width = size.x;
|
||||||
|
}
|
||||||
|
if (height === null) {
|
||||||
|
height = size.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var location = this.location.clone();
|
||||||
|
this.adjust(location, new $.Point(width, height));
|
||||||
|
return this._adjustBoundsForRotation(
|
||||||
|
viewport, new $.Rect(location.x, location.y, width, height));
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_adjustBoundsForRotation: function(viewport, bounds) {
|
||||||
|
if (!viewport ||
|
||||||
|
viewport.degrees === 0 ||
|
||||||
|
this.rotationMode === $.OverlayRotationMode.EXACT) {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX) {
|
||||||
|
// If overlay not fully scalable, BOUNDING_BOX falls back to EXACT
|
||||||
|
if (this.width === null || this.height === null) {
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
// It is easier to just compute the position and size and
|
||||||
|
// convert to viewport coordinates.
|
||||||
|
var positionAndSize = this._getOverlayPositionAndSize(viewport);
|
||||||
|
return viewport.viewerElementToViewportRectangle(new $.Rect(
|
||||||
|
positionAndSize.position.x,
|
||||||
|
positionAndSize.position.y,
|
||||||
|
positionAndSize.size.x,
|
||||||
|
positionAndSize.size.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// NO_ROTATION case
|
||||||
|
return bounds.rotate(-viewport.degrees,
|
||||||
|
this._getPlacementPoint(bounds));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}( OpenSeadragon ));
|
}(OpenSeadragon));
|
||||||
|
138
src/placement.js
Normal file
138
src/placement.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* OpenSeadragon - Placement
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010-2016 OpenSeadragon contributors
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of CodePlex Foundation nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from
|
||||||
|
* this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An enumeration of positions to anchor an element.
|
||||||
|
* @member Placement
|
||||||
|
* @memberOf OpenSeadragon
|
||||||
|
* @static
|
||||||
|
* @readonly
|
||||||
|
* @property {OpenSeadragon.Placement} CENTER
|
||||||
|
* @property {OpenSeadragon.Placement} TOP_LEFT
|
||||||
|
* @property {OpenSeadragon.Placement} TOP
|
||||||
|
* @property {OpenSeadragon.Placement} TOP_RIGHT
|
||||||
|
* @property {OpenSeadragon.Placement} RIGHT
|
||||||
|
* @property {OpenSeadragon.Placement} BOTTOM_RIGHT
|
||||||
|
* @property {OpenSeadragon.Placement} BOTTOM
|
||||||
|
* @property {OpenSeadragon.Placement} BOTTOM_LEFT
|
||||||
|
* @property {OpenSeadragon.Placement} LEFT
|
||||||
|
*/
|
||||||
|
$.Placement = $.freezeObject({
|
||||||
|
CENTER: 0,
|
||||||
|
TOP_LEFT: 1,
|
||||||
|
TOP: 2,
|
||||||
|
TOP_RIGHT: 3,
|
||||||
|
RIGHT: 4,
|
||||||
|
BOTTOM_RIGHT: 5,
|
||||||
|
BOTTOM: 6,
|
||||||
|
BOTTOM_LEFT: 7,
|
||||||
|
LEFT: 8,
|
||||||
|
properties: {
|
||||||
|
0: {
|
||||||
|
isLeft: false,
|
||||||
|
isHorizontallyCentered: true,
|
||||||
|
isRight: false,
|
||||||
|
isTop: false,
|
||||||
|
isVerticallyCentered: true,
|
||||||
|
isBottom: false
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
isLeft: true,
|
||||||
|
isHorizontallyCentered: false,
|
||||||
|
isRight: false,
|
||||||
|
isTop: true,
|
||||||
|
isVerticallyCentered: false,
|
||||||
|
isBottom: false
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
isLeft: false,
|
||||||
|
isHorizontallyCentered: true,
|
||||||
|
isRight: false,
|
||||||
|
isTop: true,
|
||||||
|
isVerticallyCentered: false,
|
||||||
|
isBottom: false
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
isLeft: false,
|
||||||
|
isHorizontallyCentered: false,
|
||||||
|
isRight: true,
|
||||||
|
isTop: true,
|
||||||
|
isVerticallyCentered: false,
|
||||||
|
isBottom: false
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
isLeft: false,
|
||||||
|
isHorizontallyCentered: false,
|
||||||
|
isRight: true,
|
||||||
|
isTop: false,
|
||||||
|
isVerticallyCentered: true,
|
||||||
|
isBottom: false
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
isLeft: false,
|
||||||
|
isHorizontallyCentered: false,
|
||||||
|
isRight: true,
|
||||||
|
isTop: false,
|
||||||
|
isVerticallyCentered: false,
|
||||||
|
isBottom: true
|
||||||
|
},
|
||||||
|
6: {
|
||||||
|
isLeft: false,
|
||||||
|
isHorizontallyCentered: true,
|
||||||
|
isRight: false,
|
||||||
|
isTop: false,
|
||||||
|
isVerticallyCentered: false,
|
||||||
|
isBottom: true
|
||||||
|
},
|
||||||
|
7: {
|
||||||
|
isLeft: true,
|
||||||
|
isHorizontallyCentered: false,
|
||||||
|
isRight: false,
|
||||||
|
isTop: false,
|
||||||
|
isVerticallyCentered: false,
|
||||||
|
isBottom: true
|
||||||
|
},
|
||||||
|
8: {
|
||||||
|
isLeft: true,
|
||||||
|
isHorizontallyCentered: false,
|
||||||
|
isRight: false,
|
||||||
|
isTop: false,
|
||||||
|
isVerticallyCentered: true,
|
||||||
|
isBottom: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}(OpenSeadragon));
|
206
src/rectangle.js
206
src/rectangle.js
@ -110,6 +110,33 @@ $.Rect = function(x, y, width, height, degrees) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a rectangle having the 3 specified points as summits.
|
||||||
|
* @static
|
||||||
|
* @memberof OpenSeadragon.Rect
|
||||||
|
* @param {OpenSeadragon.Point} topLeft
|
||||||
|
* @param {OpenSeadragon.Point} topRight
|
||||||
|
* @param {OpenSeadragon.Point} bottomLeft
|
||||||
|
* @returns {OpenSeadragon.Rect}
|
||||||
|
*/
|
||||||
|
$.Rect.fromSummits = function(topLeft, topRight, bottomLeft) {
|
||||||
|
var width = topLeft.distanceTo(topRight);
|
||||||
|
var height = topLeft.distanceTo(bottomLeft);
|
||||||
|
var diff = topRight.minus(topLeft);
|
||||||
|
var radians = Math.atan(diff.y / diff.x);
|
||||||
|
if (diff.x < 0) {
|
||||||
|
radians += Math.PI;
|
||||||
|
} else if (diff.y < 0) {
|
||||||
|
radians += 2 * Math.PI;
|
||||||
|
}
|
||||||
|
return new $.Rect(
|
||||||
|
topLeft.x,
|
||||||
|
topLeft.y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
radians / Math.PI * 180);
|
||||||
|
};
|
||||||
|
|
||||||
/** @lends OpenSeadragon.Rect.prototype */
|
/** @lends OpenSeadragon.Rect.prototype */
|
||||||
$.Rect.prototype = {
|
$.Rect.prototype = {
|
||||||
/**
|
/**
|
||||||
@ -280,11 +307,137 @@ $.Rect.prototype = {
|
|||||||
bottom - top);
|
bottom - top);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bounding box of the intersection of this rectangle with the
|
||||||
|
* given rectangle.
|
||||||
|
* @param {OpenSeadragon.Rect} rect
|
||||||
|
* @return {OpenSeadragon.Rect} the bounding box of the intersection
|
||||||
|
* or null if the rectangles don't intersect.
|
||||||
|
*/
|
||||||
|
intersection: function(rect) {
|
||||||
|
// Simplified version of Weiler Atherton clipping algorithm
|
||||||
|
// https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm
|
||||||
|
// Because we just want the bounding box of the intersection,
|
||||||
|
// we can just compute the bounding box of:
|
||||||
|
// 1. all the summits of this which are inside rect
|
||||||
|
// 2. all the summits of rect which are inside this
|
||||||
|
// 3. all the intersections of rect and this
|
||||||
|
var EPSILON = 0.0000000001;
|
||||||
|
|
||||||
|
var intersectionPoints = [];
|
||||||
|
|
||||||
|
var thisTopLeft = this.getTopLeft();
|
||||||
|
if (rect.containsPoint(thisTopLeft, EPSILON)) {
|
||||||
|
intersectionPoints.push(thisTopLeft);
|
||||||
|
}
|
||||||
|
var thisTopRight = this.getTopRight();
|
||||||
|
if (rect.containsPoint(thisTopRight, EPSILON)) {
|
||||||
|
intersectionPoints.push(thisTopRight);
|
||||||
|
}
|
||||||
|
var thisBottomLeft = this.getBottomLeft();
|
||||||
|
if (rect.containsPoint(thisBottomLeft, EPSILON)) {
|
||||||
|
intersectionPoints.push(thisBottomLeft);
|
||||||
|
}
|
||||||
|
var thisBottomRight = this.getBottomRight();
|
||||||
|
if (rect.containsPoint(thisBottomRight, EPSILON)) {
|
||||||
|
intersectionPoints.push(thisBottomRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rectTopLeft = rect.getTopLeft();
|
||||||
|
if (this.containsPoint(rectTopLeft, EPSILON)) {
|
||||||
|
intersectionPoints.push(rectTopLeft);
|
||||||
|
}
|
||||||
|
var rectTopRight = rect.getTopRight();
|
||||||
|
if (this.containsPoint(rectTopRight, EPSILON)) {
|
||||||
|
intersectionPoints.push(rectTopRight);
|
||||||
|
}
|
||||||
|
var rectBottomLeft = rect.getBottomLeft();
|
||||||
|
if (this.containsPoint(rectBottomLeft, EPSILON)) {
|
||||||
|
intersectionPoints.push(rectBottomLeft);
|
||||||
|
}
|
||||||
|
var rectBottomRight = rect.getBottomRight();
|
||||||
|
if (this.containsPoint(rectBottomRight, EPSILON)) {
|
||||||
|
intersectionPoints.push(rectBottomRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
var thisSegments = this._getSegments();
|
||||||
|
var rectSegments = rect._getSegments();
|
||||||
|
for (var i = 0; i < thisSegments.length; i++) {
|
||||||
|
var thisSegment = thisSegments[i];
|
||||||
|
for (var j = 0; j < rectSegments.length; j++) {
|
||||||
|
var rectSegment = rectSegments[j];
|
||||||
|
var intersect = getIntersection(thisSegment[0], thisSegment[1],
|
||||||
|
rectSegment[0], rectSegment[1]);
|
||||||
|
if (intersect) {
|
||||||
|
intersectionPoints.push(intersect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get intersection point of segments [a,b] and [c,d]
|
||||||
|
function getIntersection(a, b, c, d) {
|
||||||
|
// http://stackoverflow.com/a/1968345/1440403
|
||||||
|
var abVector = b.minus(a);
|
||||||
|
var cdVector = d.minus(c);
|
||||||
|
|
||||||
|
var denom = -cdVector.x * abVector.y + abVector.x * cdVector.y;
|
||||||
|
if (denom === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = (abVector.x * (a.y - c.y) - abVector.y * (a.x - c.x)) / denom;
|
||||||
|
var t = (cdVector.x * (a.y - c.y) - cdVector.y * (a.x - c.x)) / denom;
|
||||||
|
|
||||||
|
if (-EPSILON <= s && s <= 1 - EPSILON &&
|
||||||
|
-EPSILON <= t && t <= 1 - EPSILON) {
|
||||||
|
return new $.Point(a.x + t * abVector.x, a.y + t * abVector.y);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intersectionPoints.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var minX = intersectionPoints[0].x;
|
||||||
|
var maxX = intersectionPoints[0].x;
|
||||||
|
var minY = intersectionPoints[0].y;
|
||||||
|
var maxY = intersectionPoints[0].y;
|
||||||
|
for (var k = 1; k < intersectionPoints.length; k++) {
|
||||||
|
var point = intersectionPoints[k];
|
||||||
|
if (point.x < minX) {
|
||||||
|
minX = point.x;
|
||||||
|
}
|
||||||
|
if (point.x > maxX) {
|
||||||
|
maxX = point.x;
|
||||||
|
}
|
||||||
|
if (point.y < minY) {
|
||||||
|
minY = point.y;
|
||||||
|
}
|
||||||
|
if (point.y > maxY) {
|
||||||
|
maxY = point.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new $.Rect(minX, minY, maxX - minX, maxY - minY);
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_getSegments: function() {
|
||||||
|
var topLeft = this.getTopLeft();
|
||||||
|
var topRight = this.getTopRight();
|
||||||
|
var bottomLeft = this.getBottomLeft();
|
||||||
|
var bottomRight = this.getBottomRight();
|
||||||
|
return [[topLeft, topRight],
|
||||||
|
[topRight, bottomRight],
|
||||||
|
[bottomRight, bottomLeft],
|
||||||
|
[bottomLeft, topLeft]];
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates a rectangle around a point.
|
* Rotates a rectangle around a point.
|
||||||
* @function
|
* @function
|
||||||
* @param {Number} degrees The angle in degrees to rotate.
|
* @param {Number} degrees The angle in degrees to rotate.
|
||||||
* @param {OpenSeadragon.Point} pivot The point about which to rotate.
|
* @param {OpenSeadragon.Point} [pivot] The point about which to rotate.
|
||||||
* Defaults to the center of the rectangle.
|
* Defaults to the center of the rectangle.
|
||||||
* @return {OpenSeadragon.Rect}
|
* @return {OpenSeadragon.Rect}
|
||||||
*/
|
*/
|
||||||
@ -340,6 +493,51 @@ $.Rect.prototype = {
|
|||||||
maxY - minY);
|
maxY - minY);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the smallest horizontal (degrees=0) rectangle which contains
|
||||||
|
* this rectangle and has integers x, y, width and height
|
||||||
|
* @returns {OpenSeadragon.Rect}
|
||||||
|
*/
|
||||||
|
getIntegerBoundingBox: function() {
|
||||||
|
var boundingBox = this.getBoundingBox();
|
||||||
|
var x = Math.floor(boundingBox.x);
|
||||||
|
var y = Math.floor(boundingBox.y);
|
||||||
|
var width = Math.ceil(boundingBox.width + boundingBox.x - x);
|
||||||
|
var height = Math.ceil(boundingBox.height + boundingBox.y - y);
|
||||||
|
return new $.Rect(x, y, width, height);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether a point is inside this rectangle (edge included).
|
||||||
|
* @function
|
||||||
|
* @param {OpenSeadragon.Point} point
|
||||||
|
* @param {Number} [epsilon=0] the margin of error allowed
|
||||||
|
* @returns {Boolean} true if the point is inside this rectangle, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
containsPoint: function(point, epsilon) {
|
||||||
|
epsilon = epsilon || 0;
|
||||||
|
|
||||||
|
// See http://stackoverflow.com/a/2752754/1440403 for explanation
|
||||||
|
var topLeft = this.getTopLeft();
|
||||||
|
var topRight = this.getTopRight();
|
||||||
|
var bottomLeft = this.getBottomLeft();
|
||||||
|
var topDiff = topRight.minus(topLeft);
|
||||||
|
var leftDiff = bottomLeft.minus(topLeft);
|
||||||
|
|
||||||
|
return ((point.x - topLeft.x) * topDiff.x +
|
||||||
|
(point.y - topLeft.y) * topDiff.y >= -epsilon) &&
|
||||||
|
|
||||||
|
((point.x - topRight.x) * topDiff.x +
|
||||||
|
(point.y - topRight.y) * topDiff.y <= epsilon) &&
|
||||||
|
|
||||||
|
((point.x - topLeft.x) * leftDiff.x +
|
||||||
|
(point.y - topLeft.y) * leftDiff.y >= -epsilon) &&
|
||||||
|
|
||||||
|
((point.x - bottomLeft.x) * leftDiff.x +
|
||||||
|
(point.y - bottomLeft.y) * leftDiff.y <= epsilon);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a string representation of the rectangle which is useful for
|
* Provides a string representation of the rectangle which is useful for
|
||||||
* debugging.
|
* debugging.
|
||||||
@ -348,10 +546,10 @@ $.Rect.prototype = {
|
|||||||
*/
|
*/
|
||||||
toString: function() {
|
toString: function() {
|
||||||
return "[" +
|
return "[" +
|
||||||
(Math.round(this.x * 100) / 100) + "," +
|
(Math.round(this.x * 100) / 100) + ", " +
|
||||||
(Math.round(this.y * 100) / 100) + "," +
|
(Math.round(this.y * 100) / 100) + ", " +
|
||||||
(Math.round(this.width * 100) / 100) + "x" +
|
(Math.round(this.width * 100) / 100) + "x" +
|
||||||
(Math.round(this.height * 100) / 100) + "," +
|
(Math.round(this.height * 100) / 100) + ", " +
|
||||||
(Math.round(this.degrees * 100) / 100) + "deg" +
|
(Math.round(this.degrees * 100) / 100) + "deg" +
|
||||||
"]";
|
"]";
|
||||||
}
|
}
|
||||||
|
@ -276,7 +276,6 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.currentPage = page;
|
this.currentPage = page;
|
||||||
$.getElement( element.id + '-displayregion' ).focus();
|
|
||||||
onStripEnter.call( this, { eventSource: this.innerTracker } );
|
onStripEnter.call( this, { eventSource: this.innerTracker } );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -234,6 +234,15 @@ $.Spring.prototype = {
|
|||||||
} else {
|
} else {
|
||||||
this.current.value = currentValue;
|
this.current.value = currentValue;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the spring is at the target value
|
||||||
|
* @function
|
||||||
|
* @returns {Boolean} True if at target value, false otherwise
|
||||||
|
*/
|
||||||
|
isAtTargetValue: function() {
|
||||||
|
return this.current.value === this.target.value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
56
src/tile.js
56
src/tile.js
@ -193,6 +193,11 @@ $.Tile.prototype = {
|
|||||||
return this.level + "/" + this.x + "_" + this.y;
|
return this.level + "/" + this.x + "_" + this.y;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_hasTransparencyChannel: function() {
|
||||||
|
return !!this.context2D || this.url.match('.png');
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the tile in an html container.
|
* Renders the tile in an html container.
|
||||||
* @function
|
* @function
|
||||||
@ -280,27 +285,6 @@ $.Tile.prototype = {
|
|||||||
|
|
||||||
context.globalAlpha = this.opacity;
|
context.globalAlpha = this.opacity;
|
||||||
|
|
||||||
//if we are supposed to be rendering fully opaque rectangle,
|
|
||||||
//ie its done fading or fading is turned off, and if we are drawing
|
|
||||||
//an image with an alpha channel, then the only way
|
|
||||||
//to avoid seeing the tile underneath is to clear the rectangle
|
|
||||||
if (context.globalAlpha === 1 &&
|
|
||||||
(this.context2D || this.url.match('.png'))) {
|
|
||||||
//clearing only the inside of the rectangle occupied
|
|
||||||
//by the png prevents edge flikering
|
|
||||||
context.clearRect(
|
|
||||||
position.x + 1,
|
|
||||||
position.y + 1,
|
|
||||||
size.x - 2,
|
|
||||||
size.y - 2
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// This gives the application a chance to make image manipulation
|
|
||||||
// changes as we are rendering the image
|
|
||||||
drawingHandler({context: context, tile: this, rendered: rendered});
|
|
||||||
|
|
||||||
if (typeof scale === 'number' && scale !== 1) {
|
if (typeof scale === 'number' && scale !== 1) {
|
||||||
// draw tile at a different scale
|
// draw tile at a different scale
|
||||||
position = position.times(scale);
|
position = position.times(scale);
|
||||||
@ -312,6 +296,25 @@ $.Tile.prototype = {
|
|||||||
position = position.plus(translate);
|
position = position.plus(translate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if we are supposed to be rendering fully opaque rectangle,
|
||||||
|
//ie its done fading or fading is turned off, and if we are drawing
|
||||||
|
//an image with an alpha channel, then the only way
|
||||||
|
//to avoid seeing the tile underneath is to clear the rectangle
|
||||||
|
if (context.globalAlpha === 1 && this._hasTransparencyChannel()) {
|
||||||
|
//clearing only the inside of the rectangle occupied
|
||||||
|
//by the png prevents edge flikering
|
||||||
|
context.clearRect(
|
||||||
|
position.x + 1,
|
||||||
|
position.y + 1,
|
||||||
|
size.x - 2,
|
||||||
|
size.y - 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gives the application a chance to make image manipulation
|
||||||
|
// changes as we are rendering the image
|
||||||
|
drawingHandler({context: context, tile: this, rendered: rendered});
|
||||||
|
|
||||||
context.drawImage(
|
context.drawImage(
|
||||||
rendered.canvas,
|
rendered.canvas,
|
||||||
0,
|
0,
|
||||||
@ -333,15 +336,18 @@ $.Tile.prototype = {
|
|||||||
* @return {Float}
|
* @return {Float}
|
||||||
*/
|
*/
|
||||||
getScaleForEdgeSmoothing: function() {
|
getScaleForEdgeSmoothing: function() {
|
||||||
if (!this.cacheImageRecord) {
|
var context;
|
||||||
|
if (this.cacheImageRecord) {
|
||||||
|
context = this.cacheImageRecord.getRenderedContext();
|
||||||
|
} else if (this.context2D) {
|
||||||
|
context = this.context2D;
|
||||||
|
} else {
|
||||||
$.console.warn(
|
$.console.warn(
|
||||||
'[Tile.drawCanvas] attempting to get tile scale %s when tile\'s not cached',
|
'[Tile.drawCanvas] attempting to get tile scale %s when tile\'s not cached',
|
||||||
this.toString());
|
this.toString());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
return context.canvas.width / (this.size.x * $.pixelDensityRatio);
|
||||||
var rendered = this.cacheImageRecord.getRenderedContext();
|
|
||||||
return rendered.canvas.width / this.size.times($.pixelDensityRatio).x;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +52,10 @@
|
|||||||
* @param {Number} [options.y=0] - Top position, in viewport coordinates.
|
* @param {Number} [options.y=0] - Top position, in viewport coordinates.
|
||||||
* @param {Number} [options.width=1] - Width, in viewport coordinates.
|
* @param {Number} [options.width=1] - Width, in viewport coordinates.
|
||||||
* @param {Number} [options.height] - Height, in viewport coordinates.
|
* @param {Number} [options.height] - Height, in viewport coordinates.
|
||||||
|
* @param {OpenSeadragon.Rect} [options.fitBounds] The bounds in viewport coordinates
|
||||||
|
* to fit the image into. If specified, x, y, width and height get ignored.
|
||||||
|
* @param {OpenSeadragon.Placement} [options.fitBoundsPlacement=OpenSeadragon.Placement.CENTER]
|
||||||
|
* How to anchor the image in the bounds if options.fitBounds is set.
|
||||||
* @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to
|
* @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to
|
||||||
* (portions of the image outside of this area will not be visible). Only works on
|
* (portions of the image outside of this area will not be visible). Only works on
|
||||||
* browsers that support the HTML5 canvas.
|
* browsers that support the HTML5 canvas.
|
||||||
@ -122,6 +126,11 @@ $.TiledImage = function( options ) {
|
|||||||
delete options.height;
|
delete options.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fitBounds = options.fitBounds;
|
||||||
|
delete options.fitBounds;
|
||||||
|
var fitBoundsPlacement = options.fitBoundsPlacement || OpenSeadragon.Placement.CENTER;
|
||||||
|
delete options.fitBoundsPlacement;
|
||||||
|
|
||||||
$.extend( true, this, {
|
$.extend( true, this, {
|
||||||
|
|
||||||
//internal state properties
|
//internal state properties
|
||||||
@ -172,6 +181,10 @@ $.TiledImage = function( options ) {
|
|||||||
|
|
||||||
this._updateForScale();
|
this._updateForScale();
|
||||||
|
|
||||||
|
if (fitBounds) {
|
||||||
|
this.fitBounds(fitBounds, fitBoundsPlacement, true);
|
||||||
|
}
|
||||||
|
|
||||||
// We need a callback to give image manipulation a chance to happen
|
// We need a callback to give image manipulation a chance to happen
|
||||||
this._drawingHandler = function(args) {
|
this._drawingHandler = function(args) {
|
||||||
/**
|
/**
|
||||||
@ -274,6 +287,26 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
return this.getBounds();
|
return this.getBounds();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bounds of the displayed part of the tiled image.
|
||||||
|
* @param {Boolean} [current=false] Pass true for the current location,
|
||||||
|
* false for the target location.
|
||||||
|
* @returns {$.Rect} The clipped bounds in viewport coordinates.
|
||||||
|
*/
|
||||||
|
getClippedBounds: function(current) {
|
||||||
|
var bounds = this.getBounds(current);
|
||||||
|
if (this._clip) {
|
||||||
|
var ratio = this._worldWidthCurrent / this.source.dimensions.x;
|
||||||
|
var clip = this._clip.times(ratio);
|
||||||
|
bounds = new $.Rect(
|
||||||
|
bounds.x + clip.x,
|
||||||
|
bounds.y + clip.y,
|
||||||
|
clip.width,
|
||||||
|
clip.height);
|
||||||
|
}
|
||||||
|
return bounds;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
|
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
|
||||||
*/
|
*/
|
||||||
@ -543,6 +576,67 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
this._setScale(height / this.normHeight, immediately);
|
this._setScale(height / this.normHeight, immediately);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Positions and scales the TiledImage to fit in the specified bounds.
|
||||||
|
* Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
|
||||||
|
* twice
|
||||||
|
* @param {OpenSeadragon.Rect} bounds The bounds to fit the image into.
|
||||||
|
* @param {OpenSeadragon.Placement} [anchor=OpenSeadragon.Placement.CENTER]
|
||||||
|
* How to anchor the image in the bounds.
|
||||||
|
* @param {Boolean} [immediately=false] Whether to animate to the new size
|
||||||
|
* or snap immediately.
|
||||||
|
* @fires OpenSeadragon.TiledImage.event:bounds-change
|
||||||
|
*/
|
||||||
|
fitBounds: function(bounds, anchor, immediately) {
|
||||||
|
anchor = anchor || $.Placement.CENTER;
|
||||||
|
var anchorProperties = $.Placement.properties[anchor];
|
||||||
|
var aspectRatio = this.contentAspectX;
|
||||||
|
var xOffset = 0;
|
||||||
|
var yOffset = 0;
|
||||||
|
var displayedWidthRatio = 1;
|
||||||
|
var displayedHeightRatio = 1;
|
||||||
|
if (this._clip) {
|
||||||
|
aspectRatio = this._clip.getAspectRatio();
|
||||||
|
displayedWidthRatio = this._clip.width / this.source.dimensions.x;
|
||||||
|
displayedHeightRatio = this._clip.height / this.source.dimensions.y;
|
||||||
|
if (bounds.getAspectRatio() > aspectRatio) {
|
||||||
|
xOffset = this._clip.x / this._clip.height * bounds.height;
|
||||||
|
yOffset = this._clip.y / this._clip.height * bounds.height;
|
||||||
|
} else {
|
||||||
|
xOffset = this._clip.x / this._clip.width * bounds.width;
|
||||||
|
yOffset = this._clip.y / this._clip.width * bounds.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bounds.getAspectRatio() > aspectRatio) {
|
||||||
|
// We will have margins on the X axis
|
||||||
|
var height = bounds.height / displayedHeightRatio;
|
||||||
|
var marginLeft = 0;
|
||||||
|
if (anchorProperties.isHorizontallyCentered) {
|
||||||
|
marginLeft = (bounds.width - bounds.height * aspectRatio) / 2;
|
||||||
|
} else if (anchorProperties.isRight) {
|
||||||
|
marginLeft = bounds.width - bounds.height * aspectRatio;
|
||||||
|
}
|
||||||
|
this.setPosition(
|
||||||
|
new $.Point(bounds.x - xOffset + marginLeft, bounds.y - yOffset),
|
||||||
|
immediately);
|
||||||
|
this.setHeight(height, immediately);
|
||||||
|
} else {
|
||||||
|
// We will have margins on the Y axis
|
||||||
|
var width = bounds.width / displayedWidthRatio;
|
||||||
|
var marginTop = 0;
|
||||||
|
if (anchorProperties.isVerticallyCentered) {
|
||||||
|
marginTop = (bounds.height - bounds.width / aspectRatio) / 2;
|
||||||
|
} else if (anchorProperties.isBottom) {
|
||||||
|
marginTop = bounds.height - bounds.width / aspectRatio;
|
||||||
|
}
|
||||||
|
this.setPosition(
|
||||||
|
new $.Point(bounds.x - xOffset, bounds.y - yOffset + marginTop),
|
||||||
|
immediately);
|
||||||
|
this.setWidth(width, immediately);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle,
|
* @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle,
|
||||||
* in image pixels, or null if none.
|
* in image pixels, or null if none.
|
||||||
@ -650,6 +744,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
this.raiseEvent('bounds-change');
|
this.raiseEvent('bounds-change');
|
||||||
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_isBottomItem: function() {
|
||||||
|
return this.viewer.world.getItemAt(0) === this;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -688,7 +787,6 @@ function updateViewport( tiledImage ) {
|
|||||||
Math.log( 2 )
|
Math.log( 2 )
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
degrees = tiledImage.viewport.degrees,
|
|
||||||
renderPixelRatioC,
|
renderPixelRatioC,
|
||||||
renderPixelRatioT,
|
renderPixelRatioT,
|
||||||
zeroRatioT,
|
zeroRatioT,
|
||||||
@ -696,26 +794,23 @@ function updateViewport( tiledImage ) {
|
|||||||
levelOpacity,
|
levelOpacity,
|
||||||
levelVisibility;
|
levelVisibility;
|
||||||
|
|
||||||
viewportBounds.x -= tiledImage._xSpring.current.value;
|
|
||||||
viewportBounds.y -= tiledImage._ySpring.current.value;
|
|
||||||
|
|
||||||
// Reset tile's internal drawn state
|
// Reset tile's internal drawn state
|
||||||
while ( tiledImage.lastDrawn.length > 0 ) {
|
while (tiledImage.lastDrawn.length > 0) {
|
||||||
tile = tiledImage.lastDrawn.pop();
|
tile = tiledImage.lastDrawn.pop();
|
||||||
tile.beingDrawn = false;
|
tile.beingDrawn = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Change bounds for rotation
|
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
|
||||||
if (degrees === 90 || degrees === 270) {
|
var tiledImageBounds = tiledImage.getClippedBounds(true);
|
||||||
viewportBounds = viewportBounds.rotate( degrees );
|
var intersection = viewportBounds.intersection(tiledImageBounds);
|
||||||
} else if (degrees !== 0 && degrees !== 180) {
|
if (intersection === null) {
|
||||||
// This is just an approximation.
|
return;
|
||||||
var orthBounds = viewportBounds.rotate(90);
|
|
||||||
viewportBounds.x -= orthBounds.width / 2;
|
|
||||||
viewportBounds.y -= orthBounds.height / 2;
|
|
||||||
viewportBounds.width += orthBounds.width;
|
|
||||||
viewportBounds.height += orthBounds.height;
|
|
||||||
}
|
}
|
||||||
|
viewportBounds = intersection;
|
||||||
|
}
|
||||||
|
viewportBounds = viewportBounds.getBoundingBox();
|
||||||
|
viewportBounds.x -= tiledImage._xSpring.current.value;
|
||||||
|
viewportBounds.y -= tiledImage._ySpring.current.value;
|
||||||
|
|
||||||
var viewportTL = viewportBounds.getTopLeft();
|
var viewportTL = viewportBounds.getTopLeft();
|
||||||
var viewportBR = viewportBounds.getBottomRight();
|
var viewportBR = viewportBounds.getBottomRight();
|
||||||
@ -1324,22 +1419,22 @@ function compareTiles( previousBest, tile ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawTiles( tiledImage, lastDrawn ) {
|
function drawTiles( tiledImage, lastDrawn ) {
|
||||||
var i,
|
if (lastDrawn.length === 0) {
|
||||||
tile = lastDrawn[0];
|
|
||||||
|
|
||||||
if ( tiledImage.opacity <= 0 ) {
|
|
||||||
drawDebugInfo( tiledImage, lastDrawn );
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var tile = lastDrawn[0];
|
||||||
|
|
||||||
var useSketch = tiledImage.opacity < 1 ||
|
var useSketch = tiledImage.opacity < 1 ||
|
||||||
(tiledImage.compositeOperation && tiledImage.compositeOperation !== 'source-over');
|
(tiledImage.compositeOperation &&
|
||||||
|
tiledImage.compositeOperation !== 'source-over') ||
|
||||||
|
(!tiledImage._isBottomItem() && tile._hasTransparencyChannel());
|
||||||
|
|
||||||
var sketchScale;
|
var sketchScale;
|
||||||
var sketchTranslate;
|
var sketchTranslate;
|
||||||
|
|
||||||
var zoom = tiledImage.viewport.getZoom(true);
|
var zoom = tiledImage.viewport.getZoom(true);
|
||||||
var imageZoom = tiledImage.viewportToImageZoom(zoom);
|
var imageZoom = tiledImage.viewportToImageZoom(zoom);
|
||||||
if (imageZoom > tiledImage.smoothTileEdgesMinZoom && tile) {
|
if (imageZoom > tiledImage.smoothTileEdgesMinZoom) {
|
||||||
// When zoomed in a lot (>100%) the tile edges are visible.
|
// When zoomed in a lot (>100%) the tile edges are visible.
|
||||||
// So we have to composite them at ~100% and scale them up together.
|
// So we have to composite them at ~100% and scale them up together.
|
||||||
useSketch = true;
|
useSketch = true;
|
||||||
@ -1349,8 +1444,17 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
tiledImage._drawer.getCanvasSize(true));
|
tiledImage._drawer.getCanvasSize(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( useSketch ) {
|
var bounds;
|
||||||
tiledImage._drawer._clear( true );
|
if (useSketch) {
|
||||||
|
if (!sketchScale) {
|
||||||
|
// Except when edge smoothing, we only clean the part of the
|
||||||
|
// sketch canvas we are going to use for performance reasons.
|
||||||
|
bounds = tiledImage.viewport.viewportToViewerElementRectangle(
|
||||||
|
tiledImage.getClippedBounds(true))
|
||||||
|
.getIntegerBoundingBox()
|
||||||
|
.times($.pixelDensityRatio);
|
||||||
|
}
|
||||||
|
tiledImage._drawer._clear(true, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When scaling, we must rotate only when blending the sketch canvas to avoid
|
// When scaling, we must rotate only when blending the sketch canvas to avoid
|
||||||
@ -1396,7 +1500,7 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch);
|
tiledImage._drawer.drawRectangle(placeholderRect, fillStyle, useSketch);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( i = lastDrawn.length - 1; i >= 0; i-- ) {
|
for (var i = lastDrawn.length - 1; i >= 0; i--) {
|
||||||
tile = lastDrawn[ i ];
|
tile = lastDrawn[ i ];
|
||||||
tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate );
|
tiledImage._drawer.drawTile( tile, tiledImage._drawingHandler, useSketch, sketchScale, sketchTranslate );
|
||||||
tile.beingDrawn = true;
|
tile.beingDrawn = true;
|
||||||
@ -1433,7 +1537,13 @@ function drawTiles( tiledImage, lastDrawn ) {
|
|||||||
if (offsetForRotation) {
|
if (offsetForRotation) {
|
||||||
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false);
|
tiledImage._drawer._offsetForRotation(tiledImage.viewport.degrees, false);
|
||||||
}
|
}
|
||||||
tiledImage._drawer.blendSketch(tiledImage.opacity, sketchScale, sketchTranslate, tiledImage.compositeOperation);
|
tiledImage._drawer.blendSketch({
|
||||||
|
opacity: tiledImage.opacity,
|
||||||
|
scale: sketchScale,
|
||||||
|
translate: sketchTranslate,
|
||||||
|
compositeOperation: tiledImage.compositeOperation,
|
||||||
|
bounds: bounds
|
||||||
|
});
|
||||||
if (offsetForRotation) {
|
if (offsetForRotation) {
|
||||||
tiledImage._drawer._restoreRotationChanges(false);
|
tiledImage._drawer._restoreRotationChanges(false);
|
||||||
}
|
}
|
||||||
|
@ -544,7 +544,7 @@ $.TileSource.prototype = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for retriving the url which will return an image for the
|
* Responsible for retriving the url which will return an image for the
|
||||||
* region speified by the given x, y, and level components.
|
* region specified by the given x, y, and level components.
|
||||||
* This method is not implemented by this class other than to throw an Error
|
* This method is not implemented by this class other than to throw an Error
|
||||||
* announcing you have to implement it. Because of the variety of tile
|
* announcing you have to implement it. Because of the variety of tile
|
||||||
* server technologies, and various specifications for building image
|
* server technologies, and various specifications for building image
|
||||||
|
195
src/viewer.js
195
src/viewer.js
@ -40,14 +40,19 @@ var nextHash = 1;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* The main point of entry into creating a zoomable image on the page.
|
* The main point of entry into creating a zoomable image on the page.<br>
|
||||||
*
|
* <br>
|
||||||
* We have provided an idiomatic javascript constructor which takes
|
* We have provided an idiomatic javascript constructor which takes
|
||||||
* a single object, but still support the legacy positional arguments.
|
* a single object, but still support the legacy positional arguments.<br>
|
||||||
*
|
* <br>
|
||||||
* The options below are given in order that they appeared in the constructor
|
* The options below are given in order that they appeared in the constructor
|
||||||
* as arguments and we translate a positional call into an idiomatic call.
|
* as arguments and we translate a positional call into an idiomatic call.<br>
|
||||||
*
|
* <br>
|
||||||
|
* To create a viewer, you can use either of this methods:<br>
|
||||||
|
* <ul>
|
||||||
|
* <li><code>var viewer = new OpenSeadragon.Viewer(options);</code></li>
|
||||||
|
* <li><code>var viewer = OpenSeadragon(options);</code></li>
|
||||||
|
* </ul>
|
||||||
* @class Viewer
|
* @class Viewer
|
||||||
* @classdesc The main OpenSeadragon viewer class.
|
* @classdesc The main OpenSeadragon viewer class.
|
||||||
*
|
*
|
||||||
@ -234,7 +239,9 @@ $.Viewer = function( options ) {
|
|||||||
style.left = "0px";
|
style.left = "0px";
|
||||||
}(this.canvas.style));
|
}(this.canvas.style));
|
||||||
$.setElementTouchActionNone( this.canvas );
|
$.setElementTouchActionNone( this.canvas );
|
||||||
|
if (options.tabIndex !== "") {
|
||||||
this.canvas.tabIndex = (options.tabIndex === undefined ? 0 : options.tabIndex);
|
this.canvas.tabIndex = (options.tabIndex === undefined ? 0 : options.tabIndex);
|
||||||
|
}
|
||||||
|
|
||||||
//the container is created through applying the ControlDock constructor above
|
//the container is created through applying the ControlDock constructor above
|
||||||
this.container.className = "openseadragon-container";
|
this.container.className = "openseadragon-container";
|
||||||
@ -410,6 +417,7 @@ $.Viewer = function( options ) {
|
|||||||
width: this.navigatorWidth,
|
width: this.navigatorWidth,
|
||||||
height: this.navigatorHeight,
|
height: this.navigatorHeight,
|
||||||
autoResize: this.navigatorAutoResize,
|
autoResize: this.navigatorAutoResize,
|
||||||
|
autoFade: this.navigatorAutoFade,
|
||||||
prefixUrl: this.prefixUrl,
|
prefixUrl: this.prefixUrl,
|
||||||
viewer: this,
|
viewer: this,
|
||||||
navigatorRotate: this.navigatorRotate,
|
navigatorRotate: this.navigatorRotate,
|
||||||
@ -911,9 +919,14 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
docStyle.padding = "0";
|
docStyle.padding = "0";
|
||||||
|
|
||||||
this.bodyWidth = bodyStyle.width;
|
this.bodyWidth = bodyStyle.width;
|
||||||
this.bodyHeight = bodyStyle.height;
|
this.docWidth = docStyle.width;
|
||||||
bodyStyle.width = "100%";
|
bodyStyle.width = "100%";
|
||||||
|
docStyle.width = "100%";
|
||||||
|
|
||||||
|
this.bodyHeight = bodyStyle.height;
|
||||||
|
this.docHeight = docStyle.height;
|
||||||
bodyStyle.height = "100%";
|
bodyStyle.height = "100%";
|
||||||
|
docStyle.height = "100%";
|
||||||
|
|
||||||
//when entering full screen on the ipad it wasnt sufficient to leave
|
//when entering full screen on the ipad it wasnt sufficient to leave
|
||||||
//the body intact as only only the top half of the screen would
|
//the body intact as only only the top half of the screen would
|
||||||
@ -974,7 +987,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
docStyle.padding = this.docPadding;
|
docStyle.padding = this.docPadding;
|
||||||
|
|
||||||
bodyStyle.width = this.bodyWidth;
|
bodyStyle.width = this.bodyWidth;
|
||||||
|
docStyle.width = this.docWidth;
|
||||||
|
|
||||||
bodyStyle.height = this.bodyHeight;
|
bodyStyle.height = this.bodyHeight;
|
||||||
|
docStyle.height = this.docHeight;
|
||||||
|
|
||||||
body.removeChild( this.element );
|
body.removeChild( this.element );
|
||||||
nodes = this.previousBody.length;
|
nodes = this.previousBody.length;
|
||||||
@ -1204,6 +1220,10 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* @param {Number} [options.y=0] The Y position for the image in viewport coordinates.
|
* @param {Number} [options.y=0] The Y position for the image in viewport coordinates.
|
||||||
* @param {Number} [options.width=1] The width for the image in viewport coordinates.
|
* @param {Number} [options.width=1] The width for the image in viewport coordinates.
|
||||||
* @param {Number} [options.height] The height for the image in viewport coordinates.
|
* @param {Number} [options.height] The height for the image in viewport coordinates.
|
||||||
|
* @param {OpenSeadragon.Rect} [options.fitBounds] The bounds in viewport coordinates
|
||||||
|
* to fit the image into. If specified, x, y, width and height get ignored.
|
||||||
|
* @param {OpenSeadragon.Placement} [options.fitBoundsPlacement=OpenSeadragon.Placement.CENTER]
|
||||||
|
* How to anchor the image in the bounds if options.fitBounds is set.
|
||||||
* @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to
|
* @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to
|
||||||
* (portions of the image outside of this area will not be visible). Only works on
|
* (portions of the image outside of this area will not be visible). Only works on
|
||||||
* browsers that support the HTML5 canvas.
|
* browsers that support the HTML5 canvas.
|
||||||
@ -1339,6 +1359,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
y: queueItem.options.y,
|
y: queueItem.options.y,
|
||||||
width: queueItem.options.width,
|
width: queueItem.options.width,
|
||||||
height: queueItem.options.height,
|
height: queueItem.options.height,
|
||||||
|
fitBounds: queueItem.options.fitBounds,
|
||||||
|
fitBoundsPlacement: queueItem.options.fitBoundsPlacement,
|
||||||
clip: queueItem.options.clip,
|
clip: queueItem.options.clip,
|
||||||
placeholderFillStyle: queueItem.options.placeholderFillStyle,
|
placeholderFillStyle: queueItem.options.placeholderFillStyle,
|
||||||
opacity: queueItem.options.opacity,
|
opacity: queueItem.options.opacity,
|
||||||
@ -1780,10 +1802,12 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* is closed which include when changing page.
|
* is closed which include when changing page.
|
||||||
* @method
|
* @method
|
||||||
* @param {Element|String|Object} element - A reference to an element or an id for
|
* @param {Element|String|Object} element - A reference to an element or an id for
|
||||||
* the element which will be overlayed. Or an Object specifying the configuration for the overlay
|
* the element which will be overlayed. Or an Object specifying the configuration for the overlay.
|
||||||
|
* If using an object, see {@link OpenSeadragon.Overlay} for a list of
|
||||||
|
* all available options.
|
||||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
|
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
|
||||||
* rectangle which will be overlayed.
|
* rectangle which will be overlayed. This is a viewport relative location.
|
||||||
* @param {OpenSeadragon.OverlayPlacement} placement - The position of the
|
* @param {OpenSeadragon.Placement} placement - The position of the
|
||||||
* viewport which the location coordinates will be treated as relative
|
* viewport which the location coordinates will be treated as relative
|
||||||
* to.
|
* to.
|
||||||
* @param {function} onDraw - If supplied the callback is called when the overlay
|
* @param {function} onDraw - If supplied the callback is called when the overlay
|
||||||
@ -1825,7 +1849,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.
|
||||||
* @property {Element} element - The overlay element.
|
* @property {Element} element - The overlay element.
|
||||||
* @property {OpenSeadragon.Point|OpenSeadragon.Rect} location
|
* @property {OpenSeadragon.Point|OpenSeadragon.Rect} location
|
||||||
* @property {OpenSeadragon.OverlayPlacement} placement
|
* @property {OpenSeadragon.Placement} placement
|
||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
this.raiseEvent( 'add-overlay', {
|
this.raiseEvent( 'add-overlay', {
|
||||||
@ -1843,8 +1867,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* @param {Element|String} element - A reference to an element or an id for
|
* @param {Element|String} element - A reference to an element or an id for
|
||||||
* the element which is overlayed.
|
* the element which is overlayed.
|
||||||
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
|
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or
|
||||||
* rectangle which will be overlayed.
|
* rectangle which will be overlayed. This is a viewport relative location.
|
||||||
* @param {OpenSeadragon.OverlayPlacement} placement - The position of the
|
* @param {OpenSeadragon.Placement} placement - The position of the
|
||||||
* viewport which the location coordinates will be treated as relative
|
* viewport which the location coordinates will be treated as relative
|
||||||
* to.
|
* to.
|
||||||
* @return {OpenSeadragon.Viewer} Chainable.
|
* @return {OpenSeadragon.Viewer} Chainable.
|
||||||
@ -1870,7 +1894,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
* Viewer which raised the event.
|
* Viewer which raised the event.
|
||||||
* @property {Element} element
|
* @property {Element} element
|
||||||
* @property {OpenSeadragon.Point|OpenSeadragon.Rect} location
|
* @property {OpenSeadragon.Point|OpenSeadragon.Rect} location
|
||||||
* @property {OpenSeadragon.OverlayPlacement} placement
|
* @property {OpenSeadragon.Placement} placement
|
||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
this.raiseEvent( 'update-overlay', {
|
this.raiseEvent( 'update-overlay', {
|
||||||
@ -2191,37 +2215,28 @@ function getOverlayObject( viewer, overlay ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var location = overlay.location;
|
var location = overlay.location;
|
||||||
if ( !location ) {
|
var width = overlay.width;
|
||||||
if ( overlay.width && overlay.height ) {
|
var height = overlay.height;
|
||||||
location = overlay.px !== undefined ?
|
if (!location) {
|
||||||
viewer.viewport.imageToViewportRectangle( new $.Rect(
|
var x = overlay.x;
|
||||||
|
var y = overlay.y;
|
||||||
|
if (overlay.px !== undefined) {
|
||||||
|
var rect = viewer.viewport.imageToViewportRectangle(new $.Rect(
|
||||||
overlay.px,
|
overlay.px,
|
||||||
overlay.py,
|
overlay.py,
|
||||||
overlay.width,
|
width || 0,
|
||||||
overlay.height
|
height || 0));
|
||||||
) ) :
|
x = rect.x;
|
||||||
new $.Rect(
|
y = rect.y;
|
||||||
overlay.x,
|
width = width !== undefined ? rect.width : undefined;
|
||||||
overlay.y,
|
height = height !== undefined ? rect.height : undefined;
|
||||||
overlay.width,
|
|
||||||
overlay.height
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
location = overlay.px !== undefined ?
|
|
||||||
viewer.viewport.imageToViewportCoordinates( new $.Point(
|
|
||||||
overlay.px,
|
|
||||||
overlay.py
|
|
||||||
) ) :
|
|
||||||
new $.Point(
|
|
||||||
overlay.x,
|
|
||||||
overlay.y
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
location = new $.Point(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
var placement = overlay.placement;
|
var placement = overlay.placement;
|
||||||
if ( placement && ( $.type( placement ) === "string" ) ) {
|
if (placement && $.type(placement) === "string") {
|
||||||
placement = $.OverlayPlacement[ overlay.placement.toUpperCase() ];
|
placement = $.Placement[overlay.placement.toUpperCase()];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new $.Overlay({
|
return new $.Overlay({
|
||||||
@ -2229,7 +2244,10 @@ function getOverlayObject( viewer, overlay ) {
|
|||||||
location: location,
|
location: location,
|
||||||
placement: placement,
|
placement: placement,
|
||||||
onDraw: overlay.onDraw,
|
onDraw: overlay.onDraw,
|
||||||
checkResize: overlay.checkResize
|
checkResize: overlay.checkResize,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
rotationMode: overlay.rotationMode
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2542,22 +2560,25 @@ function onCanvasDrag( event ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCanvasDragEnd( event ) {
|
function onCanvasDragEnd( event ) {
|
||||||
var gestureSettings;
|
if (!event.preventDefaultAction && this.viewport) {
|
||||||
|
var gestureSettings = this.gestureSettingsByDeviceType(event.pointerType);
|
||||||
if ( !event.preventDefaultAction && this.viewport ) {
|
if (gestureSettings.flickEnabled &&
|
||||||
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
|
event.speed >= gestureSettings.flickMinSpeed) {
|
||||||
if ( gestureSettings.flickEnabled && event.speed >= gestureSettings.flickMinSpeed ) {
|
var amplitudeX = 0;
|
||||||
var amplitudeX = gestureSettings.flickMomentum * ( event.speed * Math.cos( event.direction - (Math.PI / 180 * this.viewport.degrees) ) ),
|
if (this.panHorizontal) {
|
||||||
amplitudeY = gestureSettings.flickMomentum * ( event.speed * Math.sin( event.direction - (Math.PI / 180 * this.viewport.degrees) ) ),
|
amplitudeX = gestureSettings.flickMomentum * event.speed *
|
||||||
center = this.viewport.pixelFromPoint( this.viewport.getCenter( true ) ),
|
Math.cos(event.direction);
|
||||||
target = this.viewport.pointFromPixel( new $.Point( center.x - amplitudeX, center.y - amplitudeY ) );
|
|
||||||
if( !this.panHorizontal ) {
|
|
||||||
target.x = center.x;
|
|
||||||
}
|
}
|
||||||
if( !this.panVertical ) {
|
var amplitudeY = 0;
|
||||||
target.y = center.y;
|
if (this.panVertical) {
|
||||||
|
amplitudeY = gestureSettings.flickMomentum * event.speed *
|
||||||
|
Math.sin(event.direction);
|
||||||
}
|
}
|
||||||
this.viewport.panTo( target, false );
|
var center = this.viewport.pixelFromPoint(
|
||||||
|
this.viewport.getCenter(true));
|
||||||
|
var target = this.viewport.pointFromPixel(
|
||||||
|
new $.Point(center.x - amplitudeX, center.y - amplitudeY));
|
||||||
|
this.viewport.panTo(target, false);
|
||||||
}
|
}
|
||||||
this.viewport.applyConstraints();
|
this.viewport.applyConstraints();
|
||||||
}
|
}
|
||||||
@ -2576,7 +2597,7 @@ function onCanvasDragEnd( event ) {
|
|||||||
* @property {Object} originalEvent - The original DOM event.
|
* @property {Object} originalEvent - The original DOM event.
|
||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
this.raiseEvent( 'canvas-drag-end', {
|
this.raiseEvent('canvas-drag-end', {
|
||||||
tracker: event.eventSource,
|
tracker: event.eventSource,
|
||||||
position: event.position,
|
position: event.position,
|
||||||
speed: event.speed,
|
speed: event.speed,
|
||||||
@ -2963,33 +2984,26 @@ function updateOnce( viewer ) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var containerSize;
|
if (viewer.autoResize) {
|
||||||
if ( viewer.autoResize ) {
|
var containerSize = _getSafeElemSize(viewer.container);
|
||||||
containerSize = _getSafeElemSize( viewer.container );
|
var prevContainerSize = THIS[viewer.hash].prevContainerSize;
|
||||||
if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) {
|
if (!containerSize.equals(prevContainerSize)) {
|
||||||
if ( viewer.preserveImageSizeOnResize ) {
|
var viewport = viewer.viewport;
|
||||||
var prevContainerSize = THIS[ viewer.hash ].prevContainerSize;
|
if (viewer.preserveImageSizeOnResize) {
|
||||||
var bounds = viewer.viewport.getBounds(true);
|
var resizeRatio = prevContainerSize.x / containerSize.x;
|
||||||
var deltaX = (containerSize.x - prevContainerSize.x);
|
var zoom = viewport.getZoom() * resizeRatio;
|
||||||
var deltaY = (containerSize.y - prevContainerSize.y);
|
var center = viewport.getCenter();
|
||||||
var viewportDiff = viewer.viewport.deltaPointsFromPixels(new OpenSeadragon.Point(deltaX, deltaY), true);
|
viewport.resize(containerSize, false);
|
||||||
viewer.viewport.resize(new OpenSeadragon.Point(containerSize.x, containerSize.y), false);
|
viewport.zoomTo(zoom, null, true);
|
||||||
|
viewport.panTo(center, true);
|
||||||
// Keep the center of the image in the center and just adjust the amount of image shown
|
} else {
|
||||||
bounds.width += viewportDiff.x;
|
|
||||||
bounds.height += viewportDiff.y;
|
|
||||||
bounds.x -= (viewportDiff.x / 2);
|
|
||||||
bounds.y -= (viewportDiff.y / 2);
|
|
||||||
viewer.viewport.fitBoundsWithConstraints(bounds, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// maintain image position
|
// maintain image position
|
||||||
var oldBounds = viewer.viewport.getBounds();
|
var oldBounds = viewport.getBounds();
|
||||||
var oldCenter = viewer.viewport.getCenter();
|
viewport.resize(containerSize, true);
|
||||||
resizeViewportAndRecenter(viewer, containerSize, oldBounds, oldCenter);
|
viewport.fitBoundsWithConstraints(oldBounds, true);
|
||||||
}
|
}
|
||||||
THIS[ viewer.hash ].prevContainerSize = containerSize;
|
THIS[viewer.hash].prevContainerSize = containerSize;
|
||||||
THIS[ viewer.hash ].forceRedraw = true;
|
THIS[viewer.hash].forceRedraw = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3074,27 +3088,6 @@ function updateOnce( viewer ) {
|
|||||||
//viewer.profiler.endUpdate();
|
//viewer.profiler.endUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function resizes the viewport and recenters the image
|
|
||||||
// as it was before resizing.
|
|
||||||
// TODO: better adjust width and height. The new width and height
|
|
||||||
// should depend on the image dimensions and on the dimensions
|
|
||||||
// of the viewport before and after switching mode.
|
|
||||||
function resizeViewportAndRecenter( viewer, containerSize, oldBounds, oldCenter ) {
|
|
||||||
var viewport = viewer.viewport;
|
|
||||||
|
|
||||||
viewport.resize( containerSize, true );
|
|
||||||
|
|
||||||
var newBounds = new $.Rect(
|
|
||||||
oldCenter.x - ( oldBounds.width / 2.0 ),
|
|
||||||
oldCenter.y - ( oldBounds.height / 2.0 ),
|
|
||||||
oldBounds.width,
|
|
||||||
oldBounds.height
|
|
||||||
);
|
|
||||||
|
|
||||||
// let the viewport decide if the bounds are too big or too small
|
|
||||||
viewport.fitBoundsWithConstraints( newBounds, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawWorld( viewer ) {
|
function drawWorld( viewer ) {
|
||||||
viewer.imageLoader.clear();
|
viewer.imageLoader.clear();
|
||||||
viewer.drawer.clear();
|
viewer.drawer.clear();
|
||||||
|
401
src/viewport.js
401
src/viewport.js
@ -237,6 +237,17 @@ $.Viewport.prototype = {
|
|||||||
* @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.
|
* @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.
|
||||||
*/
|
*/
|
||||||
getHomeBounds: function() {
|
getHomeBounds: function() {
|
||||||
|
return this.getHomeBoundsNoRotate().rotate(-this.getRotation());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the home bounds in viewport coordinates.
|
||||||
|
* This method ignores the viewport rotation. Use
|
||||||
|
* {@link OpenSeadragon.Viewport#getHomeBounds} to take it into account.
|
||||||
|
* @function
|
||||||
|
* @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.
|
||||||
|
*/
|
||||||
|
getHomeBoundsNoRotate: function() {
|
||||||
var center = this._contentBounds.getCenter();
|
var center = this._contentBounds.getCenter();
|
||||||
var width = 1.0 / this.getHomeZoom();
|
var width = 1.0 / this.getHomeZoom();
|
||||||
var height = width / this.getAspectRatio();
|
var height = width / this.getAspectRatio();
|
||||||
@ -254,8 +265,8 @@ $.Viewport.prototype = {
|
|||||||
* @param {Boolean} immediately
|
* @param {Boolean} immediately
|
||||||
* @fires OpenSeadragon.Viewer.event:home
|
* @fires OpenSeadragon.Viewer.event:home
|
||||||
*/
|
*/
|
||||||
goHome: function( immediately ) {
|
goHome: function(immediately) {
|
||||||
if( this.viewer ){
|
if (this.viewer) {
|
||||||
/**
|
/**
|
||||||
* Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}).
|
* Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}).
|
||||||
*
|
*
|
||||||
@ -266,11 +277,11 @@ $.Viewport.prototype = {
|
|||||||
* @property {Boolean} immediately
|
* @property {Boolean} immediately
|
||||||
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
* @property {?Object} userData - Arbitrary subscriber-defined object.
|
||||||
*/
|
*/
|
||||||
this.viewer.raiseEvent( 'home', {
|
this.viewer.raiseEvent('home', {
|
||||||
immediately: immediately
|
immediately: immediately
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this.fitBounds( this.getHomeBounds(), immediately );
|
return this.fitBounds(this.getHomeBounds(), immediately);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -317,8 +328,8 @@ $.Viewport.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
|
||||||
* The margins push the "home" region in from the sides by the specified amounts.
|
* The margins push the "home" region in from the sides by the specified amounts.
|
||||||
|
* @function
|
||||||
* @returns {Object} Properties (Numbers, in screen coordinates): left, top, right, bottom.
|
* @returns {Object} Properties (Numbers, in screen coordinates): left, top, right, bottom.
|
||||||
*/
|
*/
|
||||||
getMargins: function() {
|
getMargins: function() {
|
||||||
@ -326,8 +337,8 @@ $.Viewport.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
|
||||||
* The margins push the "home" region in from the sides by the specified amounts.
|
* The margins push the "home" region in from the sides by the specified amounts.
|
||||||
|
* @function
|
||||||
* @param {Object} margins - Properties (Numbers, in screen coordinates): left, top, right, bottom.
|
* @param {Object} margins - Properties (Numbers, in screen coordinates): left, top, right, bottom.
|
||||||
*/
|
*/
|
||||||
setMargins: function(margins) {
|
setMargins: function(margins) {
|
||||||
@ -341,7 +352,9 @@ $.Viewport.prototype = {
|
|||||||
}, margins);
|
}, margins);
|
||||||
|
|
||||||
this._updateContainerInnerSize();
|
this._updateContainerInnerSize();
|
||||||
|
if (this.viewer) {
|
||||||
this.viewer.forceRedraw();
|
this.viewer.forceRedraw();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -350,14 +363,26 @@ $.Viewport.prototype = {
|
|||||||
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
|
* @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 viewport coordinates.
|
* @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.
|
||||||
*/
|
*/
|
||||||
getBounds: function( current ) {
|
getBounds: function(current) {
|
||||||
var center = this.getCenter( current ),
|
return this.getBoundsNoRotate(current).rotate(-this.getRotation());
|
||||||
width = 1.0 / this.getZoom( current ),
|
},
|
||||||
height = width / this.getAspectRatio();
|
|
||||||
|
/**
|
||||||
|
* Returns the bounds of the visible area in viewport coordinates.
|
||||||
|
* This method ignores the viewport rotation. Use
|
||||||
|
* {@link OpenSeadragon.Viewport#getBounds} to take it into account.
|
||||||
|
* @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 viewport coordinates.
|
||||||
|
*/
|
||||||
|
getBoundsNoRotate: function(current) {
|
||||||
|
var center = this.getCenter(current);
|
||||||
|
var width = 1.0 / this.getZoom(current);
|
||||||
|
var height = width / this.getAspectRatio();
|
||||||
|
|
||||||
return new $.Rect(
|
return new $.Rect(
|
||||||
center.x - ( width / 2.0 ),
|
center.x - (width / 2.0),
|
||||||
center.y - ( height / 2.0 ),
|
center.y - (height / 2.0),
|
||||||
width,
|
width,
|
||||||
height
|
height
|
||||||
);
|
);
|
||||||
@ -369,8 +394,19 @@ $.Viewport.prototype = {
|
|||||||
* @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,
|
* @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,
|
||||||
* including the space taken by margins, in viewport coordinates.
|
* including the space taken by margins, in viewport coordinates.
|
||||||
*/
|
*/
|
||||||
getBoundsWithMargins: function( current ) {
|
getBoundsWithMargins: function(current) {
|
||||||
var bounds = this.getBounds(current);
|
return this.getBoundsNoRotateWithMargins(current).rotate(
|
||||||
|
-this.getRotation(), this.getCenter(current));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 viewport coordinates.
|
||||||
|
*/
|
||||||
|
getBoundsNoRotateWithMargins: function(current) {
|
||||||
|
var bounds = this.getBoundsNoRotate(current);
|
||||||
var factor = this._containerInnerSize.x * this.getZoom(current);
|
var factor = this._containerInnerSize.x * this.getZoom(current);
|
||||||
bounds.x -= this._margins.left / factor;
|
bounds.x -= this._margins.left / factor;
|
||||||
bounds.y -= this._margins.top / factor;
|
bounds.y -= this._margins.top / factor;
|
||||||
@ -438,6 +474,13 @@ $.Viewport.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// private
|
||||||
|
_applyZoomConstraints: function(zoom) {
|
||||||
|
return Math.max(
|
||||||
|
Math.min(zoom, this.getMaxZoom()),
|
||||||
|
this.getMinZoom());
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function
|
* @function
|
||||||
* @private
|
* @private
|
||||||
@ -519,40 +562,44 @@ $.Viewport.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Enforces the minZoom, maxZoom and visibilityRatio constraints by
|
||||||
|
* zooming and panning to the closest acceptable zoom and location.
|
||||||
* @function
|
* @function
|
||||||
|
* @param {Boolean} [immediately=false]
|
||||||
* @return {OpenSeadragon.Viewport} Chainable.
|
* @return {OpenSeadragon.Viewport} Chainable.
|
||||||
* @fires OpenSeadragon.Viewer.event:constrain
|
* @fires OpenSeadragon.Viewer.event:constrain
|
||||||
*/
|
*/
|
||||||
applyConstraints: function( immediately ) {
|
applyConstraints: function(immediately) {
|
||||||
var actualZoom = this.getZoom(),
|
var actualZoom = this.getZoom();
|
||||||
constrainedZoom = Math.max(
|
var constrainedZoom = this._applyZoomConstraints(actualZoom);
|
||||||
Math.min( actualZoom, this.getMaxZoom() ),
|
|
||||||
this.getMinZoom()
|
|
||||||
),
|
|
||||||
bounds,
|
|
||||||
constrainedBounds;
|
|
||||||
|
|
||||||
if ( actualZoom != constrainedZoom ) {
|
if (actualZoom !== constrainedZoom) {
|
||||||
this.zoomTo( constrainedZoom, this.zoomPoint, immediately );
|
this.zoomTo(constrainedZoom, this.zoomPoint, immediately);
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds = this.getBounds();
|
var bounds = this.getBoundsNoRotate();
|
||||||
|
var constrainedBounds = this._applyBoundaryConstraints(
|
||||||
|
bounds, immediately);
|
||||||
|
|
||||||
constrainedBounds = this._applyBoundaryConstraints( bounds, immediately );
|
if (bounds.x !== constrainedBounds.x ||
|
||||||
|
bounds.y !== constrainedBounds.y ||
|
||||||
if ( bounds.x !== constrainedBounds.x || bounds.y !== constrainedBounds.y || immediately ){
|
immediately) {
|
||||||
this.fitBounds( constrainedBounds, immediately );
|
this.fitBounds(
|
||||||
|
constrainedBounds.rotate(-this.getRotation()),
|
||||||
|
immediately);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Equivalent to {@link OpenSeadragon.Viewport#applyConstraints}
|
||||||
* @function
|
* @function
|
||||||
* @param {Boolean} immediately
|
* @param {Boolean} [immediately=false]
|
||||||
|
* @return {OpenSeadragon.Viewport} Chainable.
|
||||||
|
* @fires OpenSeadragon.Viewer.event:constrain
|
||||||
*/
|
*/
|
||||||
ensureVisible: function( immediately ) {
|
ensureVisible: function(immediately) {
|
||||||
return this.applyConstraints( immediately );
|
return this.applyConstraints(immediately);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -562,41 +609,37 @@ $.Viewport.prototype = {
|
|||||||
* @param {Object} options (immediately=false, constraints=false)
|
* @param {Object} options (immediately=false, constraints=false)
|
||||||
* @return {OpenSeadragon.Viewport} Chainable.
|
* @return {OpenSeadragon.Viewport} Chainable.
|
||||||
*/
|
*/
|
||||||
_fitBounds: function( bounds, options ) {
|
_fitBounds: function(bounds, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var immediately = options.immediately || false;
|
var immediately = options.immediately || false;
|
||||||
var constraints = options.constraints || false;
|
var constraints = options.constraints || false;
|
||||||
|
|
||||||
var aspect = this.getAspectRatio();
|
var aspect = this.getAspectRatio();
|
||||||
var center = bounds.getCenter();
|
var center = bounds.getCenter();
|
||||||
|
|
||||||
|
// Compute width and height of bounding box.
|
||||||
var newBounds = new $.Rect(
|
var newBounds = new $.Rect(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
bounds.y,
|
bounds.y,
|
||||||
bounds.width,
|
bounds.width,
|
||||||
bounds.height
|
bounds.height,
|
||||||
);
|
bounds.degrees + this.getRotation())
|
||||||
|
.getBoundingBox();
|
||||||
|
|
||||||
if ( newBounds.getAspectRatio() >= aspect ) {
|
if (newBounds.getAspectRatio() >= aspect) {
|
||||||
newBounds.height = bounds.width / aspect;
|
newBounds.height = newBounds.width / aspect;
|
||||||
newBounds.y = center.y - newBounds.height / 2;
|
|
||||||
} else {
|
} else {
|
||||||
newBounds.width = bounds.height * aspect;
|
newBounds.width = newBounds.height * aspect;
|
||||||
newBounds.x = center.x - newBounds.width / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.panTo( this.getCenter( true ), true );
|
// Compute x and y from width, height and center position
|
||||||
this.zoomTo( this.getZoom( true ), null, true );
|
newBounds.x = center.x - newBounds.width / 2;
|
||||||
|
newBounds.y = center.y - newBounds.height / 2;
|
||||||
var oldBounds = this.getBounds();
|
|
||||||
var oldZoom = this.getZoom();
|
|
||||||
var newZoom = 1.0 / newBounds.width;
|
var newZoom = 1.0 / newBounds.width;
|
||||||
|
|
||||||
if (constraints) {
|
if (constraints) {
|
||||||
var newBoundsAspectRatio = newBounds.getAspectRatio();
|
var newBoundsAspectRatio = newBounds.getAspectRatio();
|
||||||
var newConstrainedZoom = Math.max(
|
var newConstrainedZoom = this._applyZoomConstraints(newZoom);
|
||||||
Math.min(newZoom, this.getMaxZoom() ),
|
|
||||||
this.getMinZoom()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (newZoom !== newConstrainedZoom) {
|
if (newZoom !== newConstrainedZoom) {
|
||||||
newZoom = newConstrainedZoom;
|
newZoom = newConstrainedZoom;
|
||||||
@ -606,58 +649,70 @@ $.Viewport.prototype = {
|
|||||||
newBounds.y = center.y - newBounds.height / 2;
|
newBounds.y = center.y - newBounds.height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
newBounds = this._applyBoundaryConstraints( newBounds, immediately );
|
newBounds = this._applyBoundaryConstraints(newBounds, immediately);
|
||||||
center = newBounds.getCenter();
|
center = newBounds.getCenter();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (immediately) {
|
if (immediately) {
|
||||||
this.panTo( center, true );
|
this.panTo(center, true);
|
||||||
return this.zoomTo(newZoom, null, true);
|
return this.zoomTo(newZoom, null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.abs(newZoom - oldZoom) < 0.00000001 ||
|
this.panTo(this.getCenter(true), true);
|
||||||
Math.abs(newBounds.width - oldBounds.width) < 0.00000001) {
|
this.zoomTo(this.getZoom(true), null, true);
|
||||||
return this.panTo( center, immediately );
|
|
||||||
|
var oldBounds = this.getBounds();
|
||||||
|
var oldZoom = this.getZoom();
|
||||||
|
|
||||||
|
if (oldZoom === 0 || Math.abs(newZoom / oldZoom - 1) < 0.00000001) {
|
||||||
|
this.zoomTo(newZoom, true);
|
||||||
|
return this.panTo(center, immediately);
|
||||||
}
|
}
|
||||||
|
|
||||||
var referencePoint = oldBounds.getTopLeft().times(
|
newBounds = newBounds.rotate(-this.getRotation());
|
||||||
this._containerInnerSize.x / oldBounds.width
|
var referencePoint = newBounds.getTopLeft().times(newZoom)
|
||||||
).minus(
|
.minus(oldBounds.getTopLeft().times(oldZoom))
|
||||||
newBounds.getTopLeft().times(
|
.divide(newZoom - oldZoom);
|
||||||
this._containerInnerSize.x / newBounds.width
|
|
||||||
)
|
|
||||||
).divide(
|
|
||||||
this._containerInnerSize.x / oldBounds.width -
|
|
||||||
this._containerInnerSize.x / newBounds.width
|
|
||||||
);
|
|
||||||
|
|
||||||
return this.zoomTo( newZoom, referencePoint, immediately );
|
return this.zoomTo(newZoom, referencePoint, immediately);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Makes the viewport zoom and pan so that the specified bounds take
|
||||||
|
* as much space as possible in the viewport.
|
||||||
|
* Note: this method ignores the constraints (minZoom, maxZoom and
|
||||||
|
* visibilityRatio).
|
||||||
|
* Use {@link OpenSeadragon.Viewport#fitBoundsWithConstraints} to enforce
|
||||||
|
* them.
|
||||||
* @function
|
* @function
|
||||||
* @param {OpenSeadragon.Rect} bounds
|
* @param {OpenSeadragon.Rect} bounds
|
||||||
* @param {Boolean} immediately
|
* @param {Boolean} [immediately=false]
|
||||||
* @return {OpenSeadragon.Viewport} Chainable.
|
* @return {OpenSeadragon.Viewport} Chainable.
|
||||||
*/
|
*/
|
||||||
fitBounds: function( bounds, immediately ) {
|
fitBounds: function(bounds, immediately) {
|
||||||
return this._fitBounds( bounds, {
|
return this._fitBounds(bounds, {
|
||||||
immediately: immediately,
|
immediately: immediately,
|
||||||
constraints: false
|
constraints: false
|
||||||
} );
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Makes the viewport zoom and pan so that the specified bounds take
|
||||||
|
* as much space as possible in the viewport while enforcing the constraints
|
||||||
|
* (minZoom, maxZoom and visibilityRatio).
|
||||||
|
* Note: because this method enforces the constraints, part of the
|
||||||
|
* provided bounds may end up outside of the viewport.
|
||||||
|
* Use {@link OpenSeadragon.Viewport#fitBounds} to ignore them.
|
||||||
* @function
|
* @function
|
||||||
* @param {OpenSeadragon.Rect} bounds
|
* @param {OpenSeadragon.Rect} bounds
|
||||||
* @param {Boolean} immediately
|
* @param {Boolean} [immediately=false]
|
||||||
* @return {OpenSeadragon.Viewport} Chainable.
|
* @return {OpenSeadragon.Viewport} Chainable.
|
||||||
*/
|
*/
|
||||||
fitBoundsWithConstraints: function( bounds, immediately ) {
|
fitBoundsWithConstraints: function(bounds, immediately) {
|
||||||
return this._fitBounds( bounds, {
|
return this._fitBounds(bounds, {
|
||||||
immediately: immediately,
|
immediately: immediately,
|
||||||
constraints: true
|
constraints: true
|
||||||
} );
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -752,7 +807,12 @@ $.Viewport.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Zooms to the specified zoom level
|
||||||
* @function
|
* @function
|
||||||
|
* @param {Number} zoom The zoom level to zoom to.
|
||||||
|
* @param {OpenSeadragon.Point} [refPoint] The point which will stay at
|
||||||
|
* the same screen location. Defaults to the viewport center.
|
||||||
|
* @param {Boolean} [immediately=false]
|
||||||
* @return {OpenSeadragon.Viewport} Chainable.
|
* @return {OpenSeadragon.Viewport} Chainable.
|
||||||
* @fires OpenSeadragon.Viewer.event:zoom
|
* @fires OpenSeadragon.Viewer.event:zoom
|
||||||
*/
|
*/
|
||||||
@ -842,7 +902,7 @@ $.Viewport.prototype = {
|
|||||||
* @fires OpenSeadragon.Viewer.event:resize
|
* @fires OpenSeadragon.Viewer.event:resize
|
||||||
*/
|
*/
|
||||||
resize: function( newContainerSize, maintain ) {
|
resize: function( newContainerSize, maintain ) {
|
||||||
var oldBounds = this.getBounds(),
|
var oldBounds = this.getBoundsNoRotate(),
|
||||||
newBounds = oldBounds,
|
newBounds = oldBounds,
|
||||||
widthDeltaFactor;
|
widthDeltaFactor;
|
||||||
|
|
||||||
@ -888,37 +948,37 @@ $.Viewport.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Update the zoom and center (X and Y) springs.
|
||||||
* @function
|
* @function
|
||||||
|
* @returns {Boolean} True if any change has been made, false otherwise.
|
||||||
*/
|
*/
|
||||||
update: function() {
|
update: function() {
|
||||||
var oldZoomPixel,
|
|
||||||
newZoomPixel,
|
|
||||||
deltaZoomPixels,
|
|
||||||
deltaZoomPoints;
|
|
||||||
|
|
||||||
if (this.zoomPoint) {
|
if (this.zoomPoint) {
|
||||||
oldZoomPixel = this.pixelFromPoint( this.zoomPoint, true );
|
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
||||||
}
|
|
||||||
|
|
||||||
this.zoomSpring.update();
|
this.zoomSpring.update();
|
||||||
|
var newZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
|
||||||
|
|
||||||
if (this.zoomPoint && this.zoomSpring.current.value != this._oldZoom) {
|
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
|
||||||
newZoomPixel = this.pixelFromPoint( this.zoomPoint, true );
|
var deltaZoomPoints = this.deltaPointsFromPixels(
|
||||||
deltaZoomPixels = newZoomPixel.minus( oldZoomPixel );
|
deltaZoomPixels, true);
|
||||||
deltaZoomPoints = this.deltaPointsFromPixels( deltaZoomPixels, true );
|
|
||||||
|
|
||||||
this.centerSpringX.shiftBy( deltaZoomPoints.x );
|
this.centerSpringX.shiftBy(deltaZoomPoints.x);
|
||||||
this.centerSpringY.shiftBy( deltaZoomPoints.y );
|
this.centerSpringY.shiftBy(deltaZoomPoints.y);
|
||||||
} else {
|
|
||||||
|
if (this.zoomSpring.isAtTargetValue()) {
|
||||||
this.zoomPoint = null;
|
this.zoomPoint = null;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.zoomSpring.update();
|
||||||
|
}
|
||||||
|
|
||||||
this.centerSpringX.update();
|
this.centerSpringX.update();
|
||||||
this.centerSpringY.update();
|
this.centerSpringY.update();
|
||||||
|
|
||||||
var changed = this.centerSpringX.current.value != this._oldCenterX ||
|
var changed = this.centerSpringX.current.value !== this._oldCenterX ||
|
||||||
this.centerSpringY.current.value != this._oldCenterY ||
|
this.centerSpringY.current.value !== this._oldCenterY ||
|
||||||
this.zoomSpring.current.value != this._oldZoom;
|
this.zoomSpring.current.value !== this._oldZoom;
|
||||||
|
|
||||||
this._oldCenterX = this.centerSpringX.current.value;
|
this._oldCenterX = this.centerSpringX.current.value;
|
||||||
this._oldCenterY = this.centerSpringY.current.value;
|
this._oldCenterY = this.centerSpringY.current.value;
|
||||||
@ -994,7 +1054,8 @@ $.Viewport.prototype = {
|
|||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
pixelFromPointNoRotate: function(point, current) {
|
pixelFromPointNoRotate: function(point, current) {
|
||||||
return this._pixelFromPointNoRotate(point, this.getBounds(current));
|
return this._pixelFromPointNoRotate(
|
||||||
|
point, this.getBoundsNoRotate(current));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1005,7 +1066,7 @@ $.Viewport.prototype = {
|
|||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
pixelFromPoint: function(point, current) {
|
pixelFromPoint: function(point, current) {
|
||||||
return this._pixelFromPoint(point, this.getBounds(current));
|
return this._pixelFromPoint(point, this.getBoundsNoRotate(current));
|
||||||
},
|
},
|
||||||
|
|
||||||
// private
|
// private
|
||||||
@ -1036,7 +1097,7 @@ $.Viewport.prototype = {
|
|||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
pointFromPixelNoRotate: function(pixel, current) {
|
pointFromPixelNoRotate: function(pixel, current) {
|
||||||
var bounds = this.getBounds( current );
|
var bounds = this.getBoundsNoRotate(current);
|
||||||
return pixel.minus(
|
return pixel.minus(
|
||||||
new $.Point(this._margins.left, this._margins.top)
|
new $.Point(this._margins.left, this._margins.top)
|
||||||
).divide(
|
).divide(
|
||||||
@ -1085,8 +1146,18 @@ $.Viewport.prototype = {
|
|||||||
return this.viewportToImageCoordinates(viewerX.x, viewerX.y);
|
return this.viewportToImageCoordinates(viewerX.x, viewerX.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.viewportToImageCoordinates] is not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.viewportToImageCoordinates] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.viewportToImageCoordinates instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageCoordinates
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.viewportToImageCoordinates(viewerX, viewerY, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._viewportToImageDelta(
|
return this._viewportToImageDelta(
|
||||||
@ -1119,8 +1190,18 @@ $.Viewport.prototype = {
|
|||||||
return this.imageToViewportCoordinates(imageX.x, imageX.y);
|
return this.imageToViewportCoordinates(imageX.x, imageX.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.imageToViewportCoordinates] is not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.imageToViewportCoordinates] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.imageToViewportCoordinates instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageCoordinates
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.imageToViewportCoordinates(imageX, imageY, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var point = this._imageToViewportDelta(imageX, imageY);
|
var point = this._imageToViewportDelta(imageX, imageY);
|
||||||
@ -1150,6 +1231,21 @@ $.Viewport.prototype = {
|
|||||||
rect = new $.Rect(imageX, imageY, pixelWidth, pixelHeight);
|
rect = new $.Rect(imageX, imageY, pixelWidth, pixelHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.viewer) {
|
||||||
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.imageToViewportRectangle] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.imageToViewportRectangle instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.imageToViewportRectangle
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.imageToViewportRectangle(
|
||||||
|
imageX, imageY, pixelWidth, pixelHeight, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var coordA = this.imageToViewportCoordinates(rect.x, rect.y);
|
var coordA = this.imageToViewportCoordinates(rect.x, rect.y);
|
||||||
var coordB = this._imageToViewportDelta(rect.width, rect.height);
|
var coordB = this._imageToViewportDelta(rect.width, rect.height);
|
||||||
return new $.Rect(
|
return new $.Rect(
|
||||||
@ -1183,6 +1279,21 @@ $.Viewport.prototype = {
|
|||||||
rect = new $.Rect(viewerX, viewerY, pointWidth, pointHeight);
|
rect = new $.Rect(viewerX, viewerY, pointWidth, pointHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.viewer) {
|
||||||
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.viewportToImageRectangle] is not accurate ' +
|
||||||
|
'with multi-image; use TiledImage.viewportToImageRectangle instead.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageCoordinates
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.viewportToImageRectangle(
|
||||||
|
viewerX, viewerY, pointWidth, pointHeight, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var coordA = this.viewportToImageCoordinates(rect.x, rect.y);
|
var coordA = this.viewportToImageCoordinates(rect.x, rect.y);
|
||||||
var coordB = this._viewportToImageDelta(rect.width, rect.height);
|
var coordB = this._viewportToImageDelta(rect.width, rect.height);
|
||||||
return new $.Rect(
|
return new $.Rect(
|
||||||
@ -1224,10 +1335,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} pixel
|
* @param {OpenSeadragon.Point} pixel
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
windowToImageCoordinates: function( pixel ) {
|
windowToImageCoordinates: function(pixel) {
|
||||||
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.windowToImageCoordinates] the viewport must have a viewer.");
|
||||||
var viewerCoordinates = pixel.minus(
|
var viewerCoordinates = pixel.minus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
return this.viewerElementToImageCoordinates( viewerCoordinates );
|
return this.viewerElementToImageCoordinates(viewerCoordinates);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1236,10 +1349,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} pixel
|
* @param {OpenSeadragon.Point} pixel
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
imageToWindowCoordinates: function( pixel ) {
|
imageToWindowCoordinates: function(pixel) {
|
||||||
var viewerCoordinates = this.imageToViewerElementCoordinates( pixel );
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.imageToWindowCoordinates] the viewport must have a viewer.");
|
||||||
|
var viewerCoordinates = this.imageToViewerElementCoordinates(pixel);
|
||||||
return viewerCoordinates.plus(
|
return viewerCoordinates.plus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1262,15 +1377,45 @@ $.Viewport.prototype = {
|
|||||||
return this.pixelFromPoint( point, true );
|
return this.pixelFromPoint( point, true );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a rectangle in pixel coordinates relative to the viewer element
|
||||||
|
* to viewport coordinates.
|
||||||
|
* @param {OpenSeadragon.Rect} rectangle the rectangle to convert
|
||||||
|
* @returns {OpenSeadragon.Rect} the converted rectangle
|
||||||
|
*/
|
||||||
|
viewerElementToViewportRectangle: function(rectangle) {
|
||||||
|
return $.Rect.fromSummits(
|
||||||
|
this.pointFromPixel(rectangle.getTopLeft(), true),
|
||||||
|
this.pointFromPixel(rectangle.getTopRight(), true),
|
||||||
|
this.pointFromPixel(rectangle.getBottomLeft(), true)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a rectangle in viewport coordinates to pixel coordinates relative
|
||||||
|
* to the viewer element.
|
||||||
|
* @param {OpenSeadragon.Rect} rectangle the rectangle to convert
|
||||||
|
* @returns {OpenSeadragon.Rect} the converted rectangle
|
||||||
|
*/
|
||||||
|
viewportToViewerElementRectangle: function(rectangle) {
|
||||||
|
return $.Rect.fromSummits(
|
||||||
|
this.pixelFromPoint(rectangle.getTopLeft(), true),
|
||||||
|
this.pixelFromPoint(rectangle.getTopRight(), true),
|
||||||
|
this.pixelFromPoint(rectangle.getBottomLeft(), true)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert pixel coordinates relative to the window to viewport coordinates.
|
* Convert pixel coordinates relative to the window to viewport coordinates.
|
||||||
* @param {OpenSeadragon.Point} pixel
|
* @param {OpenSeadragon.Point} pixel
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
windowToViewportCoordinates: function( pixel ) {
|
windowToViewportCoordinates: function(pixel) {
|
||||||
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.windowToViewportCoordinates] the viewport must have a viewer.");
|
||||||
var viewerCoordinates = pixel.minus(
|
var viewerCoordinates = pixel.minus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
return this.viewerElementToViewportCoordinates( viewerCoordinates );
|
return this.viewerElementToViewportCoordinates(viewerCoordinates);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1278,10 +1423,12 @@ $.Viewport.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} point
|
* @param {OpenSeadragon.Point} point
|
||||||
* @returns {OpenSeadragon.Point}
|
* @returns {OpenSeadragon.Point}
|
||||||
*/
|
*/
|
||||||
viewportToWindowCoordinates: function( point ) {
|
viewportToWindowCoordinates: function(point) {
|
||||||
var viewerCoordinates = this.viewportToViewerElementCoordinates( point );
|
$.console.assert(this.viewer,
|
||||||
|
"[Viewport.viewportToWindowCoordinates] the viewport must have a viewer.");
|
||||||
|
var viewerCoordinates = this.viewportToViewerElementCoordinates(point);
|
||||||
return viewerCoordinates.plus(
|
return viewerCoordinates.plus(
|
||||||
OpenSeadragon.getElementPosition( this.viewer.element ));
|
$.getElementPosition(this.viewer.element));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1296,9 +1443,19 @@ $.Viewport.prototype = {
|
|||||||
* target zoom.
|
* target zoom.
|
||||||
* @returns {Number} imageZoom The image zoom
|
* @returns {Number} imageZoom The image zoom
|
||||||
*/
|
*/
|
||||||
viewportToImageZoom: function( viewportZoom ) {
|
viewportToImageZoom: function(viewportZoom) {
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.viewportToImageZoom] is not accurate with multi-image.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.viewportToImageZoom] is not ' +
|
||||||
|
'accurate with multi-image.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.viewportToImageZoom
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.viewportToImageZoom(viewportZoom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageWidth = this._contentSizeNoRotate.x;
|
var imageWidth = this._contentSizeNoRotate.x;
|
||||||
@ -1320,9 +1477,19 @@ $.Viewport.prototype = {
|
|||||||
* target zoom.
|
* target zoom.
|
||||||
* @returns {Number} viewportZoom The viewport zoom
|
* @returns {Number} viewportZoom The viewport zoom
|
||||||
*/
|
*/
|
||||||
imageToViewportZoom: function( imageZoom ) {
|
imageToViewportZoom: function(imageZoom) {
|
||||||
if (this.viewer && this.viewer.world.getItemCount() > 1) {
|
if (this.viewer) {
|
||||||
$.console.error('[Viewport.imageToViewportZoom] is not accurate with multi-image.');
|
var count = this.viewer.world.getItemCount();
|
||||||
|
if (count > 1) {
|
||||||
|
$.console.error('[Viewport.imageToViewportZoom] is not accurate ' +
|
||||||
|
'with multi-image.');
|
||||||
|
} else if (count === 1) {
|
||||||
|
// It is better to use TiledImage.imageToViewportZoom
|
||||||
|
// because this._contentBoundsNoRotate can not be relied on
|
||||||
|
// with clipping.
|
||||||
|
var item = this.viewer.world.getItemAt(0);
|
||||||
|
return item.imageToViewportZoom(imageZoom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageWidth = this._contentSizeNoRotate.x;
|
var imageWidth = this._contentSizeNoRotate.x;
|
||||||
|
42
src/world.js
42
src/world.js
@ -375,33 +375,39 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
|
|||||||
var oldContentSize = this._contentSize ? this._contentSize.clone() : null;
|
var oldContentSize = this._contentSize ? this._contentSize.clone() : null;
|
||||||
var oldContentFactor = this._contentFactor || 0;
|
var oldContentFactor = this._contentFactor || 0;
|
||||||
|
|
||||||
if ( !this._items.length ) {
|
if (!this._items.length) {
|
||||||
this._homeBounds = new $.Rect(0, 0, 1, 1);
|
this._homeBounds = new $.Rect(0, 0, 1, 1);
|
||||||
this._contentSize = new $.Point(1, 1);
|
this._contentSize = new $.Point(1, 1);
|
||||||
this._contentFactor = 1;
|
this._contentFactor = 1;
|
||||||
} else {
|
} else {
|
||||||
var bounds = this._items[0].getBounds();
|
var item = this._items[0];
|
||||||
this._contentFactor = this._items[0].getContentSize().x / bounds.width;
|
var bounds = item.getBounds();
|
||||||
var left = bounds.x;
|
this._contentFactor = item.getContentSize().x / bounds.width;
|
||||||
var top = bounds.y;
|
var clippedBounds = item.getClippedBounds();
|
||||||
var right = bounds.x + bounds.width;
|
var left = clippedBounds.x;
|
||||||
var bottom = bounds.y + bounds.height;
|
var top = clippedBounds.y;
|
||||||
var box;
|
var right = clippedBounds.x + clippedBounds.width;
|
||||||
for ( var i = 1; i < this._items.length; i++ ) {
|
var bottom = clippedBounds.y + clippedBounds.height;
|
||||||
box = this._items[i].getBounds();
|
for (var i = 1; i < this._items.length; i++) {
|
||||||
this._contentFactor = Math.max(this._contentFactor, this._items[i].getContentSize().x / box.width);
|
item = this._items[i];
|
||||||
left = Math.min( left, box.x );
|
bounds = item.getBounds();
|
||||||
top = Math.min( top, box.y );
|
this._contentFactor = Math.max(this._contentFactor,
|
||||||
right = Math.max( right, box.x + box.width );
|
item.getContentSize().x / bounds.width);
|
||||||
bottom = Math.max( bottom, box.y + box.height );
|
clippedBounds = item.getClippedBounds();
|
||||||
|
left = Math.min(left, clippedBounds.x);
|
||||||
|
top = Math.min(top, clippedBounds.y);
|
||||||
|
right = Math.max(right, clippedBounds.x + clippedBounds.width);
|
||||||
|
bottom = Math.max(bottom, clippedBounds.y + clippedBounds.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._homeBounds = new $.Rect( left, top, right - left, bottom - top );
|
this._homeBounds = new $.Rect(left, top, right - left, bottom - top);
|
||||||
this._contentSize = new $.Point(this._homeBounds.width * this._contentFactor,
|
this._contentSize = new $.Point(
|
||||||
|
this._homeBounds.width * this._contentFactor,
|
||||||
this._homeBounds.height * this._contentFactor);
|
this._homeBounds.height * this._contentFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._contentFactor !== oldContentFactor || !this._homeBounds.equals(oldHomeBounds) ||
|
if (this._contentFactor !== oldContentFactor ||
|
||||||
|
!this._homeBounds.equals(oldHomeBounds) ||
|
||||||
!this._contentSize.equals(oldContentSize)) {
|
!this._contentSize.equals(oldContentSize)) {
|
||||||
/**
|
/**
|
||||||
* Raised when the home bounds or content factor change.
|
* Raised when the home bounds or content factor change.
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<script src="/src/mousetracker.js"></script>
|
<script src="/src/mousetracker.js"></script>
|
||||||
<script src="/src/control.js"></script>
|
<script src="/src/control.js"></script>
|
||||||
<script src="/src/controldock.js"></script>
|
<script src="/src/controldock.js"></script>
|
||||||
|
<script src="/src/placement.js"></script>
|
||||||
<script src="/src/viewer.js"></script>
|
<script src="/src/viewer.js"></script>
|
||||||
<script src="/src/navigator.js"></script>
|
<script src="/src/navigator.js"></script>
|
||||||
<script src="/src/strings.js"></script>
|
<script src="/src/strings.js"></script>
|
||||||
@ -52,6 +53,7 @@
|
|||||||
<!-- Helpers -->
|
<!-- Helpers -->
|
||||||
<script src="/test/helpers/legacy.mouse.shim.js"></script>
|
<script src="/test/helpers/legacy.mouse.shim.js"></script>
|
||||||
<script src="/test/helpers/test.js"></script>
|
<script src="/test/helpers/test.js"></script>
|
||||||
|
<script src="/test/helpers/touch.js"></script>
|
||||||
|
|
||||||
<!-- Modules -->
|
<!-- Modules -->
|
||||||
<!-- Polyfill must be inserted first because it is testing functions
|
<!-- Polyfill must be inserted first because it is testing functions
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="contentDiv" class="openseadragon1"></div>
|
<div id="contentDiv" class="openseadragon1"></div>
|
||||||
<div id="annotation-div">
|
<div id="annotation-div">
|
||||||
<input type="button" value="Hide Overlay" id="hideOverlay">
|
<input type="button" value="Hide Overlays" id="hideOverlays">
|
||||||
|
<input type="button" value="Rotate" id="rotate">
|
||||||
|
<span id="degrees">0deg</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
@ -22,19 +24,74 @@
|
|||||||
var viewer = OpenSeadragon({
|
var viewer = OpenSeadragon({
|
||||||
id: "contentDiv",
|
id: "contentDiv",
|
||||||
prefixUrl: "../../build/openseadragon/images/",
|
prefixUrl: "../../build/openseadragon/images/",
|
||||||
tileSources: "../data/testpattern.dzi"
|
tileSources: "../data/testpattern.dzi",
|
||||||
|
minZoomImageRatio: 0,
|
||||||
|
maxZoomPixelRatio: 10
|
||||||
});
|
});
|
||||||
|
|
||||||
viewer.addHandler("open", function(event) {
|
viewer.addHandler("open", function(event) {
|
||||||
var elt = document.createElement("div");
|
var elt = document.createElement("div");
|
||||||
|
elt.className = "runtime-overlay";
|
||||||
elt.style.background = "green";
|
elt.style.background = "green";
|
||||||
elt.id = "runtime-overlay";
|
elt.style.outline = "3px solid red";
|
||||||
elt.style.border = "1px solid red";
|
elt.style.opacity = "0.7";
|
||||||
viewer.addOverlay( elt, new OpenSeadragon.Rect(0.2, 0.2, 0.75, 0.75) );
|
elt.textContent = "Scaled overlay";
|
||||||
|
viewer.addOverlay({
|
||||||
|
element: elt,
|
||||||
|
location: new OpenSeadragon.Rect(0.21, 0.21, 0.099, 0.099),
|
||||||
|
rotationMode: OpenSeadragon.OverlayRotationMode.BOUNDING_BOX
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#hideOverlay").click(function(){
|
elt = document.createElement("div");
|
||||||
$("#runtime-overlay").toggle();
|
elt.className = "runtime-overlay";
|
||||||
|
elt.style.background = "white";
|
||||||
|
elt.style.outline = "3px solid red";
|
||||||
|
elt.style.width = "100px";
|
||||||
|
elt.textContent = "Scaled vertically";
|
||||||
|
viewer.addOverlay({
|
||||||
|
element: elt,
|
||||||
|
location: new OpenSeadragon.Point(0.6, 0.6),
|
||||||
|
height: 0.1,
|
||||||
|
placement: OpenSeadragon.Placement.TOP_LEFT,
|
||||||
|
rotationMode: OpenSeadragon.OverlayRotationMode.NO_ROTATION
|
||||||
|
});
|
||||||
|
|
||||||
|
elt = document.createElement("div");
|
||||||
|
elt.className = "runtime-overlay";
|
||||||
|
elt.style.background = "white";
|
||||||
|
elt.style.opacity = "0.5";
|
||||||
|
elt.style.outline = "1px solid blue";
|
||||||
|
elt.style.height = "100px";
|
||||||
|
elt.textContent = "Scaled horizontally";
|
||||||
|
viewer.addOverlay({
|
||||||
|
element: elt,
|
||||||
|
location: new OpenSeadragon.Point(0.1, 0.5),
|
||||||
|
width: 0.1
|
||||||
|
});
|
||||||
|
|
||||||
|
elt = document.createElement("div");
|
||||||
|
elt.className = "runtime-overlay";
|
||||||
|
elt.style.background = "white";
|
||||||
|
elt.style.opacity = "0.5";
|
||||||
|
elt.style.outline = "5px solid pink";
|
||||||
|
elt.style.width = "100px";
|
||||||
|
elt.style.height = "100px";
|
||||||
|
elt.textContent = "Not scaled, centered in the middle";
|
||||||
|
viewer.addOverlay({
|
||||||
|
element: elt,
|
||||||
|
location: new OpenSeadragon.Point(0.5, 0.5),
|
||||||
|
placement: OpenSeadragon.Placement.CENTER,
|
||||||
|
checkResize: false,
|
||||||
|
rotationMode: OpenSeadragon.OverlayRotationMode.EXACT
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
$("#hideOverlays").click(function(){
|
||||||
|
$(".runtime-overlay").toggle();
|
||||||
|
});
|
||||||
|
$("#rotate").click(function() {
|
||||||
|
viewer.viewport.setRotation(viewer.viewport.getRotation() + 22.5);
|
||||||
|
$("#degrees").text(viewer.viewport.getRotation() + "deg");
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
134
test/helpers/touch.js
Normal file
134
test/helpers/touch.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/* global TouchUtil, $ */
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
var touches,
|
||||||
|
identifier,
|
||||||
|
target;
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
window.TouchUtil = {
|
||||||
|
reset: function () {
|
||||||
|
touches = [];
|
||||||
|
identifier = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
initTracker: function ( tracker ) {
|
||||||
|
// for testing in other touch-enabled browsers
|
||||||
|
if ( !('ontouchstart' in window) ) {
|
||||||
|
tracker.setTracking( false );
|
||||||
|
OpenSeadragon.MouseTracker.subscribeEvents.push( 'touchstart', 'touchend' );
|
||||||
|
tracker.setTracking( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
target = tracker.element;
|
||||||
|
},
|
||||||
|
|
||||||
|
resetTracker: function ( tracker ) {
|
||||||
|
// for testing in other touch-enabled browsers
|
||||||
|
if ( !('ontouchstart' in window) ) {
|
||||||
|
tracker.setTracking( false );
|
||||||
|
['touchstart', 'touchend'].forEach(function ( type ) {
|
||||||
|
var index = OpenSeadragon.MouseTracker.subscribeEvents.indexOf( type );
|
||||||
|
if ( index > -1 ) {
|
||||||
|
OpenSeadragon.MouseTracker.subscribeEvents.splice( index, 1 );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tracker.setTracking( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
target = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function () {
|
||||||
|
var touch,
|
||||||
|
event,
|
||||||
|
newTouches = [];
|
||||||
|
|
||||||
|
for ( var i = 0; i < arguments.length; i++ ) {
|
||||||
|
touch = createTouch(
|
||||||
|
target.offsetLeft + arguments[ i ][ 0 ],
|
||||||
|
target.offsetTop + arguments[ i ][ 1 ]
|
||||||
|
);
|
||||||
|
|
||||||
|
touches.push( touch );
|
||||||
|
newTouches.push( touch );
|
||||||
|
}
|
||||||
|
|
||||||
|
event = createTouchEvent( 'touchstart', newTouches );
|
||||||
|
target.dispatchEvent( event );
|
||||||
|
return newTouches.length === 1 ? newTouches[ 0 ] : newTouches;
|
||||||
|
},
|
||||||
|
|
||||||
|
end: function ( changedTouches ) {
|
||||||
|
if ( !$.isArray( changedTouches ) ) {
|
||||||
|
changedTouches = [ changedTouches ];
|
||||||
|
}
|
||||||
|
|
||||||
|
var event;
|
||||||
|
touches = touches.filter(function ( touch ) {
|
||||||
|
return changedTouches.indexOf( touch ) === -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
event = createTouchEvent( 'touchend', changedTouches );
|
||||||
|
target.dispatchEvent( event );
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
function createTouch( x, y ) {
|
||||||
|
try {
|
||||||
|
// new spec
|
||||||
|
return new Touch({
|
||||||
|
identifier: identifier++,
|
||||||
|
target: target,
|
||||||
|
pageX: target.offsetLeft + x,
|
||||||
|
pageY: target.offsetTop + y
|
||||||
|
} );
|
||||||
|
} catch (e) {
|
||||||
|
// legacy
|
||||||
|
return document.createTouch( window, target, identifier++, x, y, x, y );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTouchList( touches ) {
|
||||||
|
// legacy
|
||||||
|
return document.createTouchList.apply( document, touches );
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTouchEvent( type, changedTouches ) {
|
||||||
|
try {
|
||||||
|
// new spec
|
||||||
|
return new TouchEvent( type, {
|
||||||
|
view: window,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
touches: touches,
|
||||||
|
targetTouches: touches,
|
||||||
|
changedTouches: changedTouches
|
||||||
|
} );
|
||||||
|
} catch (e) {
|
||||||
|
// legacy
|
||||||
|
var touchEvent = document.createEvent( 'TouchEvent' );
|
||||||
|
var touch1 = changedTouches[ 0 ];
|
||||||
|
touchEvent.initTouchEvent(
|
||||||
|
createTouchList( touches ), // touches
|
||||||
|
createTouchList( touches ), // targetTouches
|
||||||
|
createTouchList( changedTouches ), // changedTouches
|
||||||
|
type, // type
|
||||||
|
window, // view
|
||||||
|
touch1.screenX, // screenX
|
||||||
|
touch1.screenY, // screenY
|
||||||
|
touch1.clientX, // clientX
|
||||||
|
touch1.clientY, // clientY
|
||||||
|
false, // ctrlKey
|
||||||
|
false, // altKey
|
||||||
|
false, // shiftKey
|
||||||
|
false // metaKey
|
||||||
|
);
|
||||||
|
return touchEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
@ -1,4 +1,4 @@
|
|||||||
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
|
/* global module, asyncTest, $, ok, equal, notEqual, start, test, TouchUtil, Util, testLog */
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var viewer;
|
var viewer;
|
||||||
@ -678,45 +678,96 @@
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest( 'Viewer: preventDefaultAction', function () {
|
if ('TouchEvent' in window) {
|
||||||
|
asyncTest( 'MouseTracker: touch events', function () {
|
||||||
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
|
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
|
||||||
tracker = viewer.innerTracker,
|
tracker = viewer.innerTracker,
|
||||||
origClickHandler,
|
touches;
|
||||||
origDragHandler,
|
|
||||||
dragCount = 10,
|
var reset = function () {
|
||||||
originalZoom = 0,
|
touches = [];
|
||||||
originalBounds = null;
|
TouchUtil.reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
var assessTouchExpectations = function ( expected ) {
|
||||||
|
var pointersList = tracker.getActivePointersListByType( 'touch' );
|
||||||
|
if ('captureCount' in expected) {
|
||||||
|
equal( pointersList.captureCount, expected.captureCount, expected.description + 'Pointer capture count matches expected (' + expected.captureCount + ')' );
|
||||||
|
}
|
||||||
|
if ('contacts' in expected) {
|
||||||
|
equal( pointersList.contacts, expected.contacts, expected.description + 'Pointer contact count matches expected (' + expected.contacts + ')' );
|
||||||
|
}
|
||||||
|
if ('trackedPointers' in expected) {
|
||||||
|
equal( pointersList.getLength(), expected.trackedPointers, expected.description + 'Tracked pointer count matches expected (' + expected.trackedPointers + ')' );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var onOpen = function ( event ) {
|
var onOpen = function ( event ) {
|
||||||
viewer.removeHandler( 'open', onOpen );
|
viewer.removeHandler( 'open', onOpen );
|
||||||
|
|
||||||
// Hook viewer events to set preventDefaultAction
|
TouchUtil.initTracker( tracker );
|
||||||
origClickHandler = tracker.clickHandler;
|
|
||||||
tracker.clickHandler = function ( event ) {
|
// start-end-end (multi-touch start event)
|
||||||
event.preventDefaultAction = true;
|
reset();
|
||||||
return origClickHandler( event );
|
touches = TouchUtil.start( [0,0], [20,20] );
|
||||||
};
|
assessTouchExpectations({
|
||||||
origDragHandler = tracker.dragHandler;
|
description: 'start-end-end (multi-touch start event) [capture]: ',
|
||||||
tracker.dragHandler = function ( event ) {
|
captureCount: 2,
|
||||||
event.preventDefaultAction = true;
|
contacts: 2,
|
||||||
return origDragHandler( event );
|
trackedPointers: 2
|
||||||
|
});
|
||||||
|
TouchUtil.end( touches[1] );
|
||||||
|
TouchUtil.end( touches[0] );
|
||||||
|
assessTouchExpectations({
|
||||||
|
description: 'start-end-end (multi-touch start event) [release]: ',
|
||||||
|
captureCount: 0,
|
||||||
|
contacts: 0,
|
||||||
|
trackedPointers: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
// start-start-end (multi-touch end event)
|
||||||
|
reset();
|
||||||
|
touches.push( TouchUtil.start([0, 0]) );
|
||||||
|
touches.push( TouchUtil.start([20, 20]) );
|
||||||
|
assessTouchExpectations({
|
||||||
|
description: 'start-start-end (multi-touch end event) [capture]: ',
|
||||||
|
captureCount: 2,
|
||||||
|
contacts: 2,
|
||||||
|
trackedPointers: 2
|
||||||
|
});
|
||||||
|
TouchUtil.end( touches );
|
||||||
|
assessTouchExpectations({
|
||||||
|
description: 'start-start-end (multi-touch end event) [release]: ',
|
||||||
|
captureCount: 0,
|
||||||
|
contacts: 0,
|
||||||
|
trackedPointers: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
TouchUtil.resetTracker( tracker );
|
||||||
|
viewer.close();
|
||||||
|
start();
|
||||||
};
|
};
|
||||||
|
|
||||||
originalZoom = viewer.viewport.getZoom();
|
viewer.addHandler( 'open', onOpen );
|
||||||
originalBounds = viewer.viewport.getBounds();
|
viewer.open( '/test/data/testpattern.dzi' );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
var event = {
|
// ----------
|
||||||
clientX:1,
|
asyncTest('Viewer: preventDefaultAction', function() {
|
||||||
clientY:1
|
var $canvas = $(viewer.element).find('.openseadragon-canvas')
|
||||||
};
|
.not('.navigator .openseadragon-canvas');
|
||||||
|
var tracker = viewer.innerTracker;
|
||||||
|
var epsilon = 0.0000001;
|
||||||
|
|
||||||
|
function simulateClickAndDrag() {
|
||||||
$canvas.simulate( 'focus', event );
|
$canvas.simulate( 'focus', event );
|
||||||
// Drag to pan
|
// Drag to pan
|
||||||
Util.simulateViewerClickWithDrag( {
|
Util.simulateViewerClickWithDrag( {
|
||||||
viewer: viewer,
|
viewer: viewer,
|
||||||
widthFactor: 0.25,
|
widthFactor: 0.25,
|
||||||
heightFactor: 0.25,
|
heightFactor: 0.25,
|
||||||
dragCount: dragCount,
|
dragCount: 10,
|
||||||
dragDx: 1,
|
dragDx: 1,
|
||||||
dragDy: 1
|
dragDy: 1
|
||||||
} );
|
} );
|
||||||
@ -730,20 +781,57 @@
|
|||||||
dragDy: 0
|
dragDy: 0
|
||||||
} );
|
} );
|
||||||
$canvas.simulate( 'blur', event );
|
$canvas.simulate( 'blur', event );
|
||||||
|
}
|
||||||
|
|
||||||
var zoom = viewer.viewport.getZoom(),
|
var onOpen = function() {
|
||||||
bounds = viewer.viewport.getBounds();
|
viewer.removeHandler('open', onOpen);
|
||||||
|
|
||||||
equal( zoom, originalZoom, "Zoom prevented" );
|
// Hook viewer events to set preventDefaultAction
|
||||||
ok( bounds.x == originalBounds.x && bounds.y == originalBounds.y, 'Pan prevented' );
|
var origClickHandler = tracker.clickHandler;
|
||||||
|
tracker.clickHandler = function(event) {
|
||||||
|
event.preventDefaultAction = true;
|
||||||
|
return origClickHandler(event);
|
||||||
|
};
|
||||||
|
var origDragHandler = tracker.dragHandler;
|
||||||
|
tracker.dragHandler = function(event) {
|
||||||
|
event.preventDefaultAction = true;
|
||||||
|
return origDragHandler(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
var originalZoom = viewer.viewport.getZoom();
|
||||||
|
var originalBounds = viewer.viewport.getBounds();
|
||||||
|
|
||||||
|
simulateClickAndDrag();
|
||||||
|
|
||||||
|
var zoom = viewer.viewport.getZoom();
|
||||||
|
var bounds = viewer.viewport.getBounds();
|
||||||
|
Util.assessNumericValue(zoom, originalZoom, epsilon,
|
||||||
|
"Zoom should be prevented");
|
||||||
|
Util.assertRectangleEquals(bounds, originalBounds, epsilon,
|
||||||
|
'Pan should be prevented');
|
||||||
|
|
||||||
|
tracker.clickHandler = origClickHandler;
|
||||||
|
tracker.dragHandler = origDragHandler;
|
||||||
|
|
||||||
|
simulateClickAndDrag();
|
||||||
|
|
||||||
|
var zoom = viewer.viewport.getZoom();
|
||||||
|
var bounds = viewer.viewport.getBounds();
|
||||||
|
Util.assessNumericValue(zoom, 0.002, epsilon,
|
||||||
|
"Zoom should not be prevented");
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
bounds,
|
||||||
|
new OpenSeadragon.Rect(-250, -0.25, 500, 0.5),
|
||||||
|
epsilon,
|
||||||
|
'Pan should not be prevented');
|
||||||
|
|
||||||
viewer.close();
|
viewer.close();
|
||||||
start();
|
start();
|
||||||
};
|
};
|
||||||
|
|
||||||
viewer.addHandler( 'open', onOpen );
|
viewer.addHandler('open', onOpen);
|
||||||
viewer.open( '/test/data/testpattern.dzi' );
|
viewer.open('/test/data/testpattern.dzi');
|
||||||
} );
|
});
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest( 'EventSource/MouseTracker/Viewer: event.originalEvent event.userData canvas-drag canvas-drag-end canvas-release canvas-click', function () {
|
asyncTest( 'EventSource/MouseTracker/Viewer: event.originalEvent event.userData canvas-drag canvas-drag-end canvas-release canvas-click', function () {
|
||||||
@ -949,6 +1037,59 @@
|
|||||||
viewer.open( '/test/data/testpattern.dzi' );
|
viewer.open( '/test/data/testpattern.dzi' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
test('EventSource: addOnceHandler', function() {
|
||||||
|
var eventSource = new OpenSeadragon.EventSource();
|
||||||
|
var userData = 'data';
|
||||||
|
var eventData = {
|
||||||
|
foo: 1
|
||||||
|
};
|
||||||
|
var handlerCalledCount = 0;
|
||||||
|
eventSource.addOnceHandler('test-event', function(event) {
|
||||||
|
handlerCalledCount++;
|
||||||
|
strictEqual(event.foo, eventData.foo,
|
||||||
|
'Event data should be transmitted to the event.');
|
||||||
|
strictEqual(event.userData, userData,
|
||||||
|
'User data should be transmitted to the event.');
|
||||||
|
}, userData);
|
||||||
|
strictEqual(0, handlerCalledCount,
|
||||||
|
'Handler should not have been called yet.');
|
||||||
|
eventSource.raiseEvent('test-event', eventData);
|
||||||
|
strictEqual(1, handlerCalledCount,
|
||||||
|
'Handler should have been called once.');
|
||||||
|
eventSource.raiseEvent('test-event', eventData);
|
||||||
|
strictEqual(1, handlerCalledCount,
|
||||||
|
'Handler should still have been called once.');
|
||||||
|
});
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
test('EventSource: addOnceHandler 2 times', function() {
|
||||||
|
var eventSource = new OpenSeadragon.EventSource();
|
||||||
|
var userData = 'data';
|
||||||
|
var eventData = {
|
||||||
|
foo: 1
|
||||||
|
};
|
||||||
|
var handlerCalledCount = 0;
|
||||||
|
eventSource.addOnceHandler('test-event', function(event) {
|
||||||
|
handlerCalledCount++;
|
||||||
|
strictEqual(event.foo, eventData.foo,
|
||||||
|
'Event data should be transmitted to the event.');
|
||||||
|
strictEqual(event.userData, userData,
|
||||||
|
'User data should be transmitted to the event.');
|
||||||
|
}, userData, 2);
|
||||||
|
strictEqual(0, handlerCalledCount,
|
||||||
|
'Handler should not have been called yet.');
|
||||||
|
eventSource.raiseEvent('test-event', eventData);
|
||||||
|
strictEqual(1, handlerCalledCount,
|
||||||
|
'Handler should have been called once.');
|
||||||
|
eventSource.raiseEvent('test-event', eventData);
|
||||||
|
strictEqual(2, handlerCalledCount,
|
||||||
|
'Handler should have been called twice.');
|
||||||
|
eventSource.raiseEvent('test-event', eventData);
|
||||||
|
strictEqual(2, handlerCalledCount,
|
||||||
|
'Handler should still have been called twice.');
|
||||||
|
});
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest( 'Viewer: tile-drawing event', function () {
|
asyncTest( 'Viewer: tile-drawing event', function () {
|
||||||
var tileDrawing = function ( event ) {
|
var tileDrawing = function ( event ) {
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
module( 'Multi-Image', {
|
module( 'Multi-Image', {
|
||||||
setup: function() {
|
setup: function() {
|
||||||
$( '<div id="itemsexample"></div>' ).appendTo( "#qunit-fixture" );
|
$( '<div id="example"></div>' ).appendTo( "#qunit-fixture" );
|
||||||
|
|
||||||
testLog.reset();
|
testLog.reset();
|
||||||
|
|
||||||
viewer = OpenSeadragon( {
|
viewer = OpenSeadragon( {
|
||||||
id: 'itemsexample',
|
id: 'example',
|
||||||
prefixUrl: '/build/openseadragon/images/',
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
springStiffness: 100 // Faster animation = faster tests
|
springStiffness: 100 // Faster animation = faster tests
|
||||||
});
|
});
|
||||||
@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewer = null;
|
viewer = null;
|
||||||
$( "#itemsexample" ).remove();
|
$("#example").remove();
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@ -208,4 +208,57 @@
|
|||||||
viewer.open('/test/data/testpattern.dzi');
|
viewer.open('/test/data/testpattern.dzi');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('Transparent image on top of others', function() {
|
||||||
|
viewer.open('/test/data/testpattern.dzi');
|
||||||
|
|
||||||
|
// TODO: replace with fully-loaded event listener when available.
|
||||||
|
setTimeout(function() {
|
||||||
|
var imageData = viewer.drawer.context.getImageData(0, 0, 500, 500);
|
||||||
|
// Pixel 250,250 will be in the hole of the A
|
||||||
|
var expectedVal = getPixelValue(imageData, 250, 250);
|
||||||
|
|
||||||
|
notEqual(expectedVal.r, 0, 'Red channel should not be 0');
|
||||||
|
notEqual(expectedVal.g, 0, 'Green channel should not be 0');
|
||||||
|
notEqual(expectedVal.b, 0, 'Blue channel should not be 0');
|
||||||
|
notEqual(expectedVal.a, 0, 'Alpha channel should not be 0');
|
||||||
|
|
||||||
|
viewer.addSimpleImage({
|
||||||
|
url: '/test/data/A.png'
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: replace with fully-loaded event listener when available.
|
||||||
|
setTimeout(function() {
|
||||||
|
var imageData = viewer.drawer.context.getImageData(0, 0, 500, 500);
|
||||||
|
var actualVal = getPixelValue(imageData, 250, 250);
|
||||||
|
|
||||||
|
equal(actualVal.r, expectedVal.r,
|
||||||
|
'Red channel should not change in transparent part of the A');
|
||||||
|
equal(actualVal.g, expectedVal.g,
|
||||||
|
'Green channel should not change in transparent part of the A');
|
||||||
|
equal(actualVal.b, expectedVal.b,
|
||||||
|
'Blue channel should not change in transparent part of the A');
|
||||||
|
equal(actualVal.a, expectedVal.a,
|
||||||
|
'Alpha channel should not change in transparent part of the A');
|
||||||
|
|
||||||
|
var onAVal = getPixelValue(imageData, 333, 250);
|
||||||
|
equal(onAVal.r, 0, 'Red channel should be null on the A');
|
||||||
|
equal(onAVal.g, 0, 'Green channel should be null on the A');
|
||||||
|
equal(onAVal.b, 0, 'Blue channel should be null on the A');
|
||||||
|
equal(onAVal.a, 255, 'Alpha channel should be 255 on the A');
|
||||||
|
|
||||||
|
start();
|
||||||
|
}, 500);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
function getPixelValue(imageData, x, y) {
|
||||||
|
var offset = 4 * (y * imageData.width + x);
|
||||||
|
return {
|
||||||
|
r: imageData.data[offset],
|
||||||
|
g: imageData.data[offset + 1],
|
||||||
|
b: imageData.data[offset + 2],
|
||||||
|
a: imageData.data[offset + 3]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -161,6 +161,65 @@
|
|||||||
"Incorrect union with non horizontal rectangles.");
|
"Incorrect union with non horizontal rectangles.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('intersection', function() {
|
||||||
|
var rect1 = new OpenSeadragon.Rect(2, 2, 2, 3);
|
||||||
|
var rect2 = new OpenSeadragon.Rect(0, 1, 1, 1);
|
||||||
|
var expected = null;
|
||||||
|
var actual = rect1.intersection(rect2);
|
||||||
|
equal(expected, actual,
|
||||||
|
"Rectangle " + rect2 + " should not intersect " + rect1);
|
||||||
|
actual = rect2.intersection(rect1);
|
||||||
|
equal(expected, actual,
|
||||||
|
"Rectangle " + rect1 + " should not intersect " + rect2);
|
||||||
|
|
||||||
|
rect1 = new OpenSeadragon.Rect(0, 0, 2, 1);
|
||||||
|
rect2 = new OpenSeadragon.Rect(1, 0, 2, 2);
|
||||||
|
expected = new OpenSeadragon.Rect(1, 0, 1, 1);
|
||||||
|
actual = rect1.intersection(rect2);
|
||||||
|
Util.assertRectangleEquals(expected, actual, precision,
|
||||||
|
"Intersection of " + rect2 + " with " + rect1 + " should be " +
|
||||||
|
expected);
|
||||||
|
actual = rect2.intersection(rect1);
|
||||||
|
Util.assertRectangleEquals(expected, actual, precision,
|
||||||
|
"Intersection of " + rect1 + " with " + rect2 + " should be " +
|
||||||
|
expected);
|
||||||
|
|
||||||
|
rect1 = new OpenSeadragon.Rect(0, 0, 3, 3);
|
||||||
|
rect2 = new OpenSeadragon.Rect(1, 1, 1, 1);
|
||||||
|
expected = new OpenSeadragon.Rect(1, 1, 1, 1);
|
||||||
|
actual = rect1.intersection(rect2);
|
||||||
|
Util.assertRectangleEquals(expected, actual, precision,
|
||||||
|
"Intersection of " + rect2 + " with " + rect1 + " should be " +
|
||||||
|
expected);
|
||||||
|
actual = rect2.intersection(rect1);
|
||||||
|
Util.assertRectangleEquals(expected, actual, precision,
|
||||||
|
"Intersection of " + rect1 + " with " + rect2 + " should be " +
|
||||||
|
expected);
|
||||||
|
|
||||||
|
|
||||||
|
rect1 = new OpenSeadragon.Rect(2, 2, 2, 3, 45);
|
||||||
|
rect2 = new OpenSeadragon.Rect(0, 1, 1, 1);
|
||||||
|
expected = null;
|
||||||
|
actual = rect1.intersection(rect2);
|
||||||
|
equal(expected, actual,
|
||||||
|
"Rectangle " + rect2 + " should not intersect " + rect1);
|
||||||
|
actual = rect2.intersection(rect1);
|
||||||
|
equal(expected, actual,
|
||||||
|
"Rectangle " + rect1 + " should not intersect " + rect2);
|
||||||
|
|
||||||
|
rect1 = new OpenSeadragon.Rect(2, 0, 2, 3, 45);
|
||||||
|
rect2 = new OpenSeadragon.Rect(0, 1, 1, 1);
|
||||||
|
expected = new OpenSeadragon.Rect(0, 1, 1, 1);
|
||||||
|
actual = rect1.intersection(rect2);
|
||||||
|
Util.assertRectangleEquals(expected, actual, precision,
|
||||||
|
"Intersection of " + rect2 + " with " + rect1 + " should be " +
|
||||||
|
expected);
|
||||||
|
actual = rect2.intersection(rect1);
|
||||||
|
Util.assertRectangleEquals(expected, actual, precision,
|
||||||
|
"Intersection of " + rect1 + " with " + rect2 + " should be " +
|
||||||
|
expected);
|
||||||
|
});
|
||||||
|
|
||||||
test('rotate', function() {
|
test('rotate', function() {
|
||||||
var rect = new OpenSeadragon.Rect(0, 0, 2, 1);
|
var rect = new OpenSeadragon.Rect(0, 0, 2, 1);
|
||||||
|
|
||||||
@ -218,4 +277,27 @@
|
|||||||
"Bounding box of rect rotated 270deg.");
|
"Bounding box of rect rotated 270deg.");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('containsPoint', function() {
|
||||||
|
var rect = new OpenSeadragon.Rect(0, 0, 1, 1, 45);
|
||||||
|
|
||||||
|
ok(rect.containsPoint(new OpenSeadragon.Point(0, 0)),
|
||||||
|
'Point 0,0 should be inside ' + rect);
|
||||||
|
ok(rect.containsPoint(rect.getTopRight()),
|
||||||
|
'Top right vertex should be inside ' + rect);
|
||||||
|
ok(rect.containsPoint(rect.getBottomRight()),
|
||||||
|
'Bottom right vertex should be inside ' + rect);
|
||||||
|
ok(rect.containsPoint(rect.getBottomLeft()),
|
||||||
|
'Bottom left vertex should be inside ' + rect);
|
||||||
|
ok(rect.containsPoint(rect.getCenter()),
|
||||||
|
'Center should be inside ' + rect);
|
||||||
|
notOk(rect.containsPoint(new OpenSeadragon.Point(1, 0)),
|
||||||
|
'Point 1,0 should not be inside ' + rect);
|
||||||
|
ok(rect.containsPoint(new OpenSeadragon.Point(0.5, 0.5)),
|
||||||
|
'Point 0.5,0.5 should be inside ' + rect);
|
||||||
|
ok(rect.containsPoint(new OpenSeadragon.Point(0.4, 0.5)),
|
||||||
|
'Point 0.4,0.5 should be inside ' + rect);
|
||||||
|
notOk(rect.containsPoint(new OpenSeadragon.Point(0.6, 0.5)),
|
||||||
|
'Point 0.6,0.5 should not be inside ' + rect);
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
var viewer;
|
var viewer;
|
||||||
|
|
||||||
module('TiledImage', {
|
module('TiledImage', {
|
||||||
setup: function () {
|
setup: function() {
|
||||||
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
|
||||||
|
|
||||||
testLog.reset();
|
testLog.reset();
|
||||||
@ -15,7 +15,7 @@
|
|||||||
springStiffness: 100 // Faster animation = faster tests
|
springStiffness: 100 // Faster animation = faster tests
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
teardown: function () {
|
teardown: function() {
|
||||||
if (viewer && viewer.close) {
|
if (viewer && viewer.close) {
|
||||||
viewer.close();
|
viewer.close();
|
||||||
}
|
}
|
||||||
@ -87,7 +87,7 @@
|
|||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest('animation', function() {
|
asyncTest('animation', function() {
|
||||||
viewer.addHandler("open", function () {
|
viewer.addHandler("open", function() {
|
||||||
var image = viewer.world.getItemAt(0);
|
var image = viewer.world.getItemAt(0);
|
||||||
propEqual(image.getBounds(), new OpenSeadragon.Rect(0, 0, 1, 1), 'target bounds on open');
|
propEqual(image.getBounds(), new OpenSeadragon.Rect(0, 0, 1, 1), 'target bounds on open');
|
||||||
propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds on open');
|
propEqual(image.getBounds(true), new OpenSeadragon.Rect(0, 0, 1, 1), 'current bounds on open');
|
||||||
@ -206,10 +206,12 @@
|
|||||||
propEqual(image.getClip(), clip, 'clip is set correctly');
|
propEqual(image.getClip(), clip, 'clip is set correctly');
|
||||||
|
|
||||||
Util.spyOnce(viewer.drawer, 'setClip', function(rect) {
|
Util.spyOnce(viewer.drawer, 'setClip', function(rect) {
|
||||||
ok(true, 'drawer.setClip is called');
|
var homeBounds = viewer.viewport.getHomeBounds();
|
||||||
var pixelRatio = viewer.viewport.getContainerSize().x / image.getContentSize().x;
|
var canvasClip = viewer.viewport
|
||||||
var canvasClip = clip.times(pixelRatio * OpenSeadragon.pixelDensityRatio);
|
.viewportToViewerElementRectangle(homeBounds);
|
||||||
propEqual(rect, canvasClip, 'clipping to correct rect');
|
var precision = 0.00000001;
|
||||||
|
Util.assertRectangleEquals(rect, canvasClip, precision,
|
||||||
|
'clipping should be ' + canvasClip);
|
||||||
start();
|
start();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -220,6 +222,39 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('getClipBounds', function() {
|
||||||
|
var clip = new OpenSeadragon.Rect(100, 200, 800, 500);
|
||||||
|
|
||||||
|
viewer.addHandler('open', function() {
|
||||||
|
var image = viewer.world.getItemAt(0);
|
||||||
|
var bounds = image.getClippedBounds();
|
||||||
|
var expectedBounds = new OpenSeadragon.Rect(1.2, 1.4, 1.6, 1);
|
||||||
|
propEqual(bounds, expectedBounds,
|
||||||
|
'getClipBounds should take clipping into account.');
|
||||||
|
|
||||||
|
image = viewer.world.getItemAt(1);
|
||||||
|
bounds = image.getClippedBounds();
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(1, 2, 2, 2);
|
||||||
|
propEqual(bounds, expectedBounds,
|
||||||
|
'getClipBounds should work when no clipping set.');
|
||||||
|
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
clip: clip,
|
||||||
|
x: 1,
|
||||||
|
y: 1,
|
||||||
|
width: 2
|
||||||
|
}, {
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
x: 1,
|
||||||
|
y: 2,
|
||||||
|
width: 2
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
// ----------
|
// ----------
|
||||||
asyncTest('opacity', function() {
|
asyncTest('opacity', function() {
|
||||||
|
|
||||||
@ -257,4 +292,138 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('fitBounds', function() {
|
||||||
|
|
||||||
|
function assertRectEquals(actual, expected, message) {
|
||||||
|
ok(actual.equals(expected), message + ' should be ' +
|
||||||
|
expected.toString() + ', found ' + actual.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer.addHandler('open', function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
|
||||||
|
var squareImage = viewer.world.getItemAt(0);
|
||||||
|
squareImage.fitBounds(
|
||||||
|
new OpenSeadragon.Rect(0, 0, 1, 2),
|
||||||
|
OpenSeadragon.Placement.CENTER,
|
||||||
|
true);
|
||||||
|
var actualBounds = squareImage.getBounds(true);
|
||||||
|
var expectedBounds = new OpenSeadragon.Rect(0, 0.5, 1, 1);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Square image bounds');
|
||||||
|
|
||||||
|
var tallImage = viewer.world.getItemAt(1);
|
||||||
|
tallImage.fitBounds(
|
||||||
|
new OpenSeadragon.Rect(0, 0, 1, 2),
|
||||||
|
OpenSeadragon.Placement.TOP_LEFT,
|
||||||
|
true);
|
||||||
|
actualBounds = tallImage.getBounds(true);
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(0, 0, 0.5, 2);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Tall image bounds');
|
||||||
|
|
||||||
|
var wideImage = viewer.world.getItemAt(2);
|
||||||
|
wideImage.fitBounds(
|
||||||
|
new OpenSeadragon.Rect(0, 0, 1, 2),
|
||||||
|
OpenSeadragon.Placement.BOTTOM_RIGHT,
|
||||||
|
true);
|
||||||
|
actualBounds = wideImage.getBounds(true);
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(0, 1.75, 1, 0.25);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Wide image bounds');
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.open([
|
||||||
|
'/test/data/testpattern.dzi',
|
||||||
|
'/test/data/tall.dzi',
|
||||||
|
'/test/data/wide.dzi'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('fitBounds in constructor', function() {
|
||||||
|
|
||||||
|
function assertRectEquals(actual, expected, message) {
|
||||||
|
ok(actual.equals(expected), message + ' should be ' +
|
||||||
|
expected.toString() + ', found ' + actual.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer.addHandler('open', function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
|
||||||
|
var squareImage = viewer.world.getItemAt(0);
|
||||||
|
var actualBounds = squareImage.getBounds(true);
|
||||||
|
var expectedBounds = new OpenSeadragon.Rect(0, 0.5, 1, 1);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Square image bounds');
|
||||||
|
|
||||||
|
var tallImage = viewer.world.getItemAt(1);
|
||||||
|
actualBounds = tallImage.getBounds(true);
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(0, 0, 0.5, 2);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Tall image bounds');
|
||||||
|
|
||||||
|
var wideImage = viewer.world.getItemAt(2);
|
||||||
|
actualBounds = wideImage.getBounds(true);
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(0, 1.75, 1, 0.25);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Wide image bounds');
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
x: 1, // should be ignored
|
||||||
|
y: 1, // should be ignored
|
||||||
|
width: 2, // should be ignored
|
||||||
|
fitBounds: new OpenSeadragon.Rect(0, 0, 1, 2)
|
||||||
|
// No placement specified, should default to CENTER
|
||||||
|
}, {
|
||||||
|
tileSource: '/test/data/tall.dzi',
|
||||||
|
fitBounds: new OpenSeadragon.Rect(0, 0, 1, 2),
|
||||||
|
fitBoundsPlacement: OpenSeadragon.Placement.TOP_LEFT
|
||||||
|
}, {
|
||||||
|
tileSource: '/test/data/wide.dzi',
|
||||||
|
fitBounds: new OpenSeadragon.Rect(0, 0, 1, 2),
|
||||||
|
fitBoundsPlacement: OpenSeadragon.Placement.BOTTOM_RIGHT
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('fitBounds with clipping', function() {
|
||||||
|
|
||||||
|
function assertRectEquals(actual, expected, message) {
|
||||||
|
ok(actual.equals(expected), message + ' should be ' +
|
||||||
|
expected.toString() + ', found ' + actual.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewer.addHandler('open', function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
|
||||||
|
var squareImage = viewer.world.getItemAt(0);
|
||||||
|
var actualBounds = squareImage.getBounds(true);
|
||||||
|
var expectedBounds = new OpenSeadragon.Rect(-1, -1, 2, 2);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Square image bounds');
|
||||||
|
|
||||||
|
var tallImage = viewer.world.getItemAt(1);
|
||||||
|
actualBounds = tallImage.getBounds(true);
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(1, 1, 2, 8);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Tall image bounds');
|
||||||
|
|
||||||
|
var wideImage = viewer.world.getItemAt(2);
|
||||||
|
actualBounds = wideImage.getBounds(true);
|
||||||
|
expectedBounds = new OpenSeadragon.Rect(1, 1, 16, 4);
|
||||||
|
assertRectEquals(actualBounds, expectedBounds, 'Wide image bounds');
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: '/test/data/testpattern.dzi',
|
||||||
|
clip: new OpenSeadragon.Rect(500, 500, 500, 500),
|
||||||
|
fitBounds: new OpenSeadragon.Rect(0, 0, 1, 1)
|
||||||
|
}, {
|
||||||
|
tileSource: '/test/data/tall.dzi',
|
||||||
|
clip: new OpenSeadragon.Rect(0, 0, 250, 100),
|
||||||
|
fitBounds: new OpenSeadragon.Rect(1, 1, 1, 2),
|
||||||
|
fitBoundsPlacement: OpenSeadragon.Placement.TOP
|
||||||
|
}, {
|
||||||
|
tileSource: '/test/data/wide.dzi',
|
||||||
|
clip: new OpenSeadragon.Rect(0, 0, 100, 250),
|
||||||
|
fitBounds: new OpenSeadragon.Rect(1, 1, 1, 2),
|
||||||
|
fitBoundsPlacement: OpenSeadragon.Placement.TOP_LEFT
|
||||||
|
}]);
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
var VIEWER_ID = "example";
|
var VIEWER_ID = "example";
|
||||||
var PREFIX_URL = "/build/openseadragon/images/";
|
var PREFIX_URL = "/build/openseadragon/images/";
|
||||||
var SPRING_STIFFNESS = 100; // Faster animation = faster tests
|
var SPRING_STIFFNESS = 100; // Faster animation = faster tests
|
||||||
|
var EPSILON = 0.0000000001;
|
||||||
|
|
||||||
module("viewport", {
|
module("viewport", {
|
||||||
setup: function () {
|
setup: function () {
|
||||||
@ -218,7 +219,27 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
asyncTest('getHomeBoundsWithRotation', function() {
|
asyncTest('getHomeBoundsNoRotate with rotation', function() {
|
||||||
|
function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
viewport.setRotation(-675);
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
viewport.getHomeBoundsNoRotate(),
|
||||||
|
new OpenSeadragon.Rect(
|
||||||
|
(1 - Math.sqrt(2)) / 2,
|
||||||
|
(1 - Math.sqrt(2)) / 2,
|
||||||
|
Math.sqrt(2),
|
||||||
|
Math.sqrt(2)),
|
||||||
|
0.00000001,
|
||||||
|
"Test getHomeBoundsNoRotate with degrees = -675");
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('getHomeBounds with rotation', function() {
|
||||||
function openHandler() {
|
function openHandler() {
|
||||||
viewer.removeHandler('open', openHandler);
|
viewer.removeHandler('open', openHandler);
|
||||||
var viewport = viewer.viewport;
|
var viewport = viewer.viewport;
|
||||||
@ -226,10 +247,11 @@
|
|||||||
Util.assertRectangleEquals(
|
Util.assertRectangleEquals(
|
||||||
viewport.getHomeBounds(),
|
viewport.getHomeBounds(),
|
||||||
new OpenSeadragon.Rect(
|
new OpenSeadragon.Rect(
|
||||||
(1 - Math.sqrt(2)) / 2,
|
0.5,
|
||||||
(1 - Math.sqrt(2)) / 2,
|
-0.5,
|
||||||
Math.sqrt(2),
|
Math.sqrt(2),
|
||||||
Math.sqrt(2)),
|
Math.sqrt(2),
|
||||||
|
45),
|
||||||
0.00000001,
|
0.00000001,
|
||||||
"Test getHomeBounds with degrees = -675");
|
"Test getHomeBounds with degrees = -675");
|
||||||
start();
|
start();
|
||||||
@ -238,6 +260,57 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('getHomeBoundsWithMultiImages', function() {
|
||||||
|
function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
new OpenSeadragon.Rect(0, 0, 4, 4),
|
||||||
|
viewport.getHomeBounds(),
|
||||||
|
0.00000001,
|
||||||
|
"Test getHomeBoundsWithMultiImages");
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 2
|
||||||
|
}, {
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 3,
|
||||||
|
y: 3,
|
||||||
|
width: 1
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('getHomeBoundsWithMultiImagesAndClipping', function() {
|
||||||
|
function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
new OpenSeadragon.Rect(1, 1, 4, 4),
|
||||||
|
viewport.getHomeBounds(),
|
||||||
|
0.00000001,
|
||||||
|
"Test getHomeBoundsWithMultiImagesAndClipping");
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open([{
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 2,
|
||||||
|
clip: new OpenSeadragon.Rect(500, 500, 500, 500)
|
||||||
|
}, {
|
||||||
|
tileSource: DZI_PATH,
|
||||||
|
x: 4,
|
||||||
|
y: 4,
|
||||||
|
width: 1
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
|
||||||
asyncTest('getHomeZoom', function() {
|
asyncTest('getHomeZoom', function() {
|
||||||
reopenViewerHelper({
|
reopenViewerHelper({
|
||||||
property: 'defaultZoomLevel',
|
property: 'defaultZoomLevel',
|
||||||
@ -336,7 +409,7 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
asyncTest('ensureVisible', function(){
|
asyncTest('ensureVisible', function() {
|
||||||
var openHandler = function(event) {
|
var openHandler = function(event) {
|
||||||
viewer.removeHandler('open', openHandler);
|
viewer.removeHandler('open', openHandler);
|
||||||
var viewport = viewer.viewport;
|
var viewport = viewer.viewport;
|
||||||
@ -354,17 +427,114 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('applyConstraints', function() {
|
||||||
|
var openHandler = function() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
|
||||||
|
viewport.fitBounds(new OpenSeadragon.Rect(1, 1, 1, 1), true);
|
||||||
|
viewport.visibilityRatio = 0.3;
|
||||||
|
viewport.applyConstraints(true);
|
||||||
|
var bounds = viewport.getBounds();
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
bounds,
|
||||||
|
new OpenSeadragon.Rect(0.7, 0.7, 1, 1),
|
||||||
|
EPSILON,
|
||||||
|
"Viewport.applyConstraints should move viewport.");
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('applyConstraints with rotation', function() {
|
||||||
|
var openHandler = function() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
viewport.setRotation(45);
|
||||||
|
viewport.fitBounds(new OpenSeadragon.Rect(1, 1, 1, 1), true);
|
||||||
|
viewport.applyConstraints(true);
|
||||||
|
var bounds = viewport.getBounds();
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
bounds,
|
||||||
|
new OpenSeadragon.Rect(1, 0, Math.sqrt(2), Math.sqrt(2), 45),
|
||||||
|
EPSILON,
|
||||||
|
"Viewport.applyConstraints with rotation should move viewport.");
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fit bounds tests
|
||||||
|
var testRectsFitBounds = [
|
||||||
|
new OpenSeadragon.Rect(0, -0.75, 0.5, 1),
|
||||||
|
new OpenSeadragon.Rect(0.5, 0, 0.5, 0.8),
|
||||||
|
new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5),
|
||||||
|
new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5),
|
||||||
|
new OpenSeadragon.Rect(0.5, 0.25, Math.sqrt(0.125), Math.sqrt(0.125), 45)
|
||||||
|
];
|
||||||
|
|
||||||
|
var expectedRectsFitBounds = [
|
||||||
|
new OpenSeadragon.Rect(-0.25, -0.75, 1, 1),
|
||||||
|
new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8),
|
||||||
|
new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5),
|
||||||
|
new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5),
|
||||||
|
new OpenSeadragon.Rect(0.25, 0.25, 0.5, 0.5)
|
||||||
|
];
|
||||||
|
|
||||||
|
var expectedRectsFitBoundsWithRotation = [
|
||||||
|
new OpenSeadragon.Rect(
|
||||||
|
0.25,
|
||||||
|
-1,
|
||||||
|
Math.sqrt(0.125) + Math.sqrt(0.5),
|
||||||
|
Math.sqrt(0.125) + Math.sqrt(0.5),
|
||||||
|
45),
|
||||||
|
new OpenSeadragon.Rect(
|
||||||
|
0.75,
|
||||||
|
-0.25,
|
||||||
|
Math.sqrt(0.125) + Math.sqrt(8 / 25),
|
||||||
|
Math.sqrt(0.125) + Math.sqrt(8 / 25),
|
||||||
|
45),
|
||||||
|
new OpenSeadragon.Rect(
|
||||||
|
1,
|
||||||
|
0.5,
|
||||||
|
Math.sqrt(0.125) * 2,
|
||||||
|
Math.sqrt(0.125) * 2,
|
||||||
|
45),
|
||||||
|
new OpenSeadragon.Rect(
|
||||||
|
-0.05,
|
||||||
|
-0.55,
|
||||||
|
Math.sqrt(0.125) * 2,
|
||||||
|
Math.sqrt(0.125) * 2,
|
||||||
|
45),
|
||||||
|
new OpenSeadragon.Rect(
|
||||||
|
0.5,
|
||||||
|
0.25,
|
||||||
|
Math.sqrt(0.125),
|
||||||
|
Math.sqrt(0.125),
|
||||||
|
45)
|
||||||
|
];
|
||||||
|
|
||||||
|
var expectedRectsFitBoundsWithConstraints = [
|
||||||
|
new OpenSeadragon.Rect(-0.25, -0.5, 1, 1),
|
||||||
|
new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8),
|
||||||
|
new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5),
|
||||||
|
new OpenSeadragon.Rect(-0.25, -0.25, 0.5, 0.5),
|
||||||
|
new OpenSeadragon.Rect(0.25, 0.25, 0.5, 0.5)
|
||||||
|
];
|
||||||
|
|
||||||
asyncTest('fitBounds', function(){
|
asyncTest('fitBounds', function(){
|
||||||
var openHandler = function(event) {
|
var openHandler = function(event) {
|
||||||
viewer.removeHandler('open', openHandler);
|
viewer.removeHandler('open', openHandler);
|
||||||
var viewport = viewer.viewport;
|
var viewport = viewer.viewport;
|
||||||
|
|
||||||
for(var i = 0; i < testRects.length; i++){
|
for(var i = 0; i < testRectsFitBounds.length; i++){
|
||||||
var rect = testRects[i].times(viewport.getContainerSize());
|
var rect = testRectsFitBounds[i];
|
||||||
viewport.fitBounds(rect, true);
|
viewport.fitBounds(rect, true);
|
||||||
propEqual(
|
propEqual(
|
||||||
viewport.getBounds(),
|
viewport.getBounds(),
|
||||||
rect,
|
expectedRectsFitBounds[i],
|
||||||
"Fit bounds correctly."
|
"Fit bounds correctly."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -374,19 +544,27 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
var testRectsFitBounds = [
|
asyncTest('fitBounds with viewport rotation', function(){
|
||||||
new OpenSeadragon.Rect(0, -0.75, 0.5, 1),
|
var openHandler = function(event) {
|
||||||
new OpenSeadragon.Rect(0.5, 0, 0.5, 0.8),
|
viewer.removeHandler('open', openHandler);
|
||||||
new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5),
|
var viewport = viewer.viewport;
|
||||||
new OpenSeadragon.Rect(-0.3, -0.3, 0.5, 0.5)
|
viewport.setRotation(45);
|
||||||
];
|
|
||||||
|
|
||||||
var expectedRectsFitBounds = [
|
for(var i = 0; i < testRectsFitBounds.length; i++){
|
||||||
new OpenSeadragon.Rect(-0.25, -0.5, 1, 1),
|
var rect = testRectsFitBounds[i];
|
||||||
new OpenSeadragon.Rect(0.35, 0, 0.8, 0.8),
|
viewport.fitBounds(rect, true);
|
||||||
new OpenSeadragon.Rect(0.75, 0.75, 0.5, 0.5),
|
Util.assertRectangleEquals(
|
||||||
new OpenSeadragon.Rect(-0.25, -0.25, 0.5, 0.5)
|
viewport.getBounds(),
|
||||||
];
|
expectedRectsFitBoundsWithRotation[i],
|
||||||
|
EPSILON,
|
||||||
|
"Fit bounds correctly."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
asyncTest('fitBoundsWithConstraints', function(){
|
asyncTest('fitBoundsWithConstraints', function(){
|
||||||
var openHandler = function(event) {
|
var openHandler = function(event) {
|
||||||
@ -399,7 +577,7 @@
|
|||||||
viewport.fitBoundsWithConstraints(rect, true);
|
viewport.fitBoundsWithConstraints(rect, true);
|
||||||
propEqual(
|
propEqual(
|
||||||
viewport.getBounds(),
|
viewport.getBounds(),
|
||||||
expectedRectsFitBounds[i],
|
expectedRectsFitBoundsWithConstraints[i],
|
||||||
"Fit bounds correctly."
|
"Fit bounds correctly."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -409,6 +587,44 @@
|
|||||||
viewer.open(DZI_PATH);
|
viewer.open(DZI_PATH);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('fitBounds with almost same zoom', function() {
|
||||||
|
var openHandler = function() {
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
var rect1 = new OpenSeadragon.Rect(0, 0, 1, 1);
|
||||||
|
viewport.fitBounds(rect1, true);
|
||||||
|
Util.assertRectangleEquals(rect1, viewport.getBounds(), 1e-6,
|
||||||
|
'Bounds should be ' + rect1);
|
||||||
|
|
||||||
|
// Zoom and pan
|
||||||
|
var rect2 = new OpenSeadragon.Rect(1, 1, 1 + 1e-8, 1 + 1e-8);
|
||||||
|
viewport.fitBounds(rect2);
|
||||||
|
Util.assertRectangleEquals(rect2, viewport.getBounds(), 1e-6,
|
||||||
|
'Bounds should be ' + rect2);
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addOnceHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('fitBounds with big rectangle', function() {
|
||||||
|
var openHandler = function() {
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
var rect1 = new OpenSeadragon.Rect(0, 0, 1e9, 1e9);
|
||||||
|
viewport.fitBounds(rect1, true);
|
||||||
|
Util.assertRectangleEquals(rect1, viewport.getBounds(), 1e-6,
|
||||||
|
'Bounds should be ' + rect1);
|
||||||
|
|
||||||
|
// Zoom and pan
|
||||||
|
var rect2 = new OpenSeadragon.Rect(1, 1, 2e9, 2e9);
|
||||||
|
viewport.fitBounds(rect2);
|
||||||
|
Util.assertRectangleEquals(rect2, viewport.getBounds(), 1e-6,
|
||||||
|
'Bounds should be ' + rect2);
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addOnceHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
asyncTest('fitHorizontally', function(){
|
asyncTest('fitHorizontally', function(){
|
||||||
var openHandler = function(event) {
|
var openHandler = function(event) {
|
||||||
viewer.removeHandler('open', openHandler);
|
viewer.removeHandler('open', openHandler);
|
||||||
@ -440,6 +656,7 @@
|
|||||||
viewer.addHandler('open', openHandler);
|
viewer.addHandler('open', openHandler);
|
||||||
viewer.open(WIDE_PATH);
|
viewer.open(WIDE_PATH);
|
||||||
});
|
});
|
||||||
|
// End fitBounds tests.
|
||||||
|
|
||||||
asyncTest('panBy', function(){
|
asyncTest('panBy', function(){
|
||||||
var openHandler = function(event) {
|
var openHandler = function(event) {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
<script src="/build/openseadragon/openseadragon.js"></script>
|
<script src="/build/openseadragon/openseadragon.js"></script>
|
||||||
<script src="/test/helpers/legacy.mouse.shim.js"></script>
|
<script src="/test/helpers/legacy.mouse.shim.js"></script>
|
||||||
<script src="/test/helpers/test.js"></script>
|
<script src="/test/helpers/test.js"></script>
|
||||||
|
<script src="/test/helpers/touch.js"></script>
|
||||||
|
|
||||||
<!-- Polyfill must be inserted first because it is testing functions
|
<!-- Polyfill must be inserted first because it is testing functions
|
||||||
reassignments which could be done by other test. -->
|
reassignments which could be done by other test. -->
|
||||||
|
Loading…
Reference in New Issue
Block a user