mirror of
https://github.com/openseadragon/openseadragon.git
synced 2025-01-19 17:21:50 +03:00
Merge branch 'master' into loaded
This commit is contained in:
commit
03b2a86a9d
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"OpenSeadragon": true,
|
"OpenSeadragon": true,
|
||||||
"define": false
|
"define": false,
|
||||||
|
"module": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,5 +1,5 @@
|
|||||||
# OpenSeadragon
|
# OpenSeadragon
|
||||||
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master)](http://travis-ci.org/openseadragon/openseadragon)
|
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openseadragon/openseadragon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master)](http://travis-ci.org/openseadragon/openseadragon)
|
||||||
|
|
||||||
An open-source, web-based viewer for zoomable images, implemented in pure JavaScript.
|
An open-source, web-based viewer for zoomable images, implemented in pure JavaScript.
|
||||||
|
|
||||||
|
@ -1,8 +1,30 @@
|
|||||||
OPENSEADRAGON CHANGELOG
|
OPENSEADRAGON CHANGELOG
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
2.1.1: (in progress)
|
2.2.2: (in progress)
|
||||||
|
|
||||||
|
* Fixed CORS bug in IE 10 (#967)
|
||||||
|
* Added support for commonjs (#984)
|
||||||
|
* Added an option to addTiledImage to change the crossOriginPolicy (#981)
|
||||||
|
* Fixed issue with tiles not appearing with wrapHorizontal/wrapVertical if you pan too far away from the origin (#987)
|
||||||
|
|
||||||
|
2.2.1:
|
||||||
|
|
||||||
|
* Fixed problems with zoom/pan constraints with certain extreme settings (#965)
|
||||||
|
* Fixed an issue causing the browser to crash on iOS (#966)
|
||||||
|
|
||||||
|
2.2.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and
|
||||||
|
Viewport.contentAspectY have been removed. (#846)
|
||||||
|
* BREAKING CHANGE: The Overlay.getBounds method now takes the viewport as parameter. (#896)
|
||||||
|
* 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)
|
||||||
@ -15,6 +37,29 @@ OPENSEADRAGON CHANGELOG
|
|||||||
* Added globalCompositeOperation option for tiledImage, to allow for different transfer modes (#814)
|
* Added globalCompositeOperation option for tiledImage, to allow for different transfer modes (#814)
|
||||||
* Added Viewer.addSimpleImage method for easily adding non-tiled images (#827)
|
* Added Viewer.addSimpleImage method for easily adding non-tiled images (#827)
|
||||||
* DziTileSource now works properly with DZI files that have no extension (#835)
|
* DziTileSource now works properly with DZI files that have no extension (#835)
|
||||||
|
* Fixed content clipping with rotation (#463, #567 and #833)
|
||||||
|
* Fixed navigator not being rotated when viewport rotation is set in constructor (#840)
|
||||||
|
* Fixed: Viewer.setMouseNavEnabled wasn't affecting all of the viewer's trackers (#845)
|
||||||
|
* Fixed: with scrollToZoom disabled, the viewer caused page scrolling to slow down (#858)
|
||||||
|
* Added Viewer.getOverlayById and Overlay.getBounds functions (#853)
|
||||||
|
* 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)
|
||||||
|
* Fixed an issue in viewer.addTiledImage with replace:true when viewer has navigator (#948)
|
||||||
|
|
||||||
2.1.0:
|
2.1.0:
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openseadragon",
|
"name": "openseadragon",
|
||||||
"version": "2.1.0",
|
"version": "2.2.1",
|
||||||
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
|
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
|
||||||
"keywords": ["image", "zoom", "pan", "openseadragon", "seadragon", "deepzoom", "dzi", "iiif", "osm", "tms"],
|
"keywords": ["image", "zoom", "pan", "openseadragon", "seadragon", "deepzoom", "dzi", "iiif", "osm", "tms"],
|
||||||
"homepage": "http://openseadragon.github.io/",
|
"homepage": "http://openseadragon.github.io/",
|
||||||
|
107
src/drawer.js
107
src/drawer.js
@ -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);
|
||||||
var canvas = context.canvas;
|
if (bounds) {
|
||||||
context.clearRect( 0, 0, canvas.width, canvas.height );
|
context.clearRect(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||||
|
} else {
|
||||||
|
var canvas = context.canvas;
|
||||||
|
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -382,47 +386,80 @@ $.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.
|
||||||
* Use scale to draw at a lower scale and then enlarge onto the main canvas.
|
* @param {Float} [options.scale=1] The scale at which tiles were drawn on
|
||||||
* @param {OpenSeadragon.Point} [translate] A translation vector that was used to draw the tiles
|
* the sketch. Default is 1.
|
||||||
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
|
* Use scale to draw at a lower scale and then enlarge onto the main canvas.
|
||||||
* @returns {undefined}
|
* @param {OpenSeadragon.Point} [options.translate] A translation vector
|
||||||
|
* that was used to draw the tiles
|
||||||
|
* @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;
|
||||||
var position = translate instanceof $.Point ?
|
compositeOperation = options.compositeOperation;
|
||||||
translate :
|
var bounds = options.bounds;
|
||||||
new $.Point(0, 0);
|
|
||||||
|
|
||||||
var widthExt = 0;
|
|
||||||
var heightExt = 0;
|
|
||||||
if (translate) {
|
|
||||||
var widthDiff = this.sketchCanvas.width - this.canvas.width;
|
|
||||||
var heightDiff = this.sketchCanvas.height - this.canvas.height;
|
|
||||||
widthExt = Math.round(widthDiff / 2);
|
|
||||||
heightExt = Math.round(heightDiff / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context.save();
|
this.context.save();
|
||||||
this.context.globalAlpha = opacity;
|
this.context.globalAlpha = opacity;
|
||||||
if (compositeOperation) {
|
if (compositeOperation) {
|
||||||
this.context.globalCompositeOperation = compositeOperation;
|
this.context.globalCompositeOperation = compositeOperation;
|
||||||
}
|
}
|
||||||
this.context.drawImage(
|
if (bounds) {
|
||||||
this.sketchCanvas,
|
this.context.drawImage(
|
||||||
position.x - widthExt * scale,
|
this.sketchCanvas,
|
||||||
position.y - heightExt * scale,
|
bounds.x,
|
||||||
(this.canvas.width + 2 * widthExt) * scale,
|
bounds.y,
|
||||||
(this.canvas.height + 2 * heightExt) * scale,
|
bounds.width,
|
||||||
-widthExt,
|
bounds.height,
|
||||||
-heightExt,
|
bounds.x,
|
||||||
this.canvas.width + 2 * widthExt,
|
bounds.y,
|
||||||
this.canvas.height + 2 * heightExt
|
bounds.width,
|
||||||
);
|
bounds.height
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
scale = options.scale || 1;
|
||||||
|
translate = options.translate;
|
||||||
|
var position = translate instanceof $.Point ?
|
||||||
|
translate : new $.Point(0, 0);
|
||||||
|
|
||||||
|
var widthExt = 0;
|
||||||
|
var heightExt = 0;
|
||||||
|
if (translate) {
|
||||||
|
var widthDiff = this.sketchCanvas.width - this.canvas.width;
|
||||||
|
var heightDiff = this.sketchCanvas.height - this.canvas.height;
|
||||||
|
widthExt = Math.round(widthDiff / 2);
|
||||||
|
heightExt = Math.round(heightDiff / 2);
|
||||||
|
}
|
||||||
|
this.context.drawImage(
|
||||||
|
this.sketchCanvas,
|
||||||
|
position.x - widthExt * scale,
|
||||||
|
position.y - heightExt * scale,
|
||||||
|
(this.canvas.width + 2 * widthExt) * scale,
|
||||||
|
(this.canvas.height + 2 * heightExt) * scale,
|
||||||
|
-widthExt,
|
||||||
|
-heightExt,
|
||||||
|
this.canvas.width + 2 * widthExt,
|
||||||
|
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.
|
||||||
|
@ -166,14 +166,6 @@
|
|||||||
return new $.Point(0, 0);
|
return new $.Point(0, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* @function
|
|
||||||
* @param {Number} level
|
|
||||||
* @param {OpenSeadragon.Point} point
|
|
||||||
*/
|
|
||||||
getTileAtPoint: function (level, point) {
|
|
||||||
return new $.Point(0, 0);
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a tile url
|
* Retrieves a tile url
|
||||||
* @function
|
* @function
|
||||||
|
@ -169,16 +169,6 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* @function
|
|
||||||
* @param {Number} level
|
|
||||||
* @param {OpenSeadragon.Point} point
|
|
||||||
*/
|
|
||||||
getTileAtPoint: function( level, point ) {
|
|
||||||
return new $.Point( 0, 0 );
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
@ -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 ){
|
||||||
@ -199,11 +199,18 @@ $.Navigator = function( options ){
|
|||||||
this.displayRegionContainer.appendChild(this.displayRegion);
|
this.displayRegionContainer.appendChild(this.displayRegion);
|
||||||
this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
|
this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);
|
||||||
|
|
||||||
|
function rotate(degrees) {
|
||||||
|
_setTransformRotate(_this.displayRegionContainer, degrees);
|
||||||
|
_setTransformRotate(_this.displayRegion, -degrees);
|
||||||
|
_this.viewport.setRotation(degrees);
|
||||||
|
}
|
||||||
if (options.navigatorRotate) {
|
if (options.navigatorRotate) {
|
||||||
|
var degrees = options.viewer.viewport ?
|
||||||
|
options.viewer.viewport.getRotation() :
|
||||||
|
options.viewer.degrees || 0;
|
||||||
|
rotate(degrees);
|
||||||
options.viewer.addHandler("rotate", function (args) {
|
options.viewer.addHandler("rotate", function (args) {
|
||||||
_setTransformRotate(_this.displayRegionContainer, args.degrees);
|
rotate(args.degrees);
|
||||||
_setTransformRotate(_this.displayRegion, -args.degrees);
|
|
||||||
_this.viewport.setRotation(args.degrees);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,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.
|
||||||
@ -258,6 +239,12 @@
|
|||||||
* A zoom percentage ( where 1 is 100% ) of the highest resolution level.
|
* A zoom percentage ( where 1 is 100% ) of the highest resolution level.
|
||||||
* When zoomed in beyond this value alternative compositing will be used to
|
* When zoomed in beyond this value alternative compositing will be used to
|
||||||
* smooth out the edges between tiles. This will have a performance impact.
|
* smooth out the edges between tiles. This will have a performance impact.
|
||||||
|
* Can be set to Infinity to turn it off.
|
||||||
|
* Note: This setting is ignored on iOS devices due to a known bug (See {@link https://github.com/openseadragon/openseadragon/issues/952})
|
||||||
|
*
|
||||||
|
* @property {Boolean} [iOSDevice=?]
|
||||||
|
* True if running on an iOS device, false otherwise.
|
||||||
|
* Used to disable certain features that behave differently on iOS devices.
|
||||||
*
|
*
|
||||||
* @property {Boolean} [autoResize=true]
|
* @property {Boolean} [autoResize=true]
|
||||||
* Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
|
* Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
|
||||||
@ -373,16 +360,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 +392,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,24 +682,13 @@
|
|||||||
* 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}
|
||||||
*/
|
*/
|
||||||
window.OpenSeadragon = window.OpenSeadragon || function( options ){
|
function OpenSeadragon( options ){
|
||||||
|
|
||||||
return new OpenSeadragon.Viewer( options );
|
return new OpenSeadragon.Viewer( options );
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof define === 'function' && define.amd) {
|
|
||||||
define(function () {
|
|
||||||
return (window.OpenSeadragon);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
(function( $ ){
|
(function( $ ){
|
||||||
|
|
||||||
|
|
||||||
@ -842,6 +822,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
|
||||||
@ -983,6 +978,18 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isIOSDevice = function () {
|
||||||
|
if (typeof navigator !== 'object') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var userAgent = navigator.userAgent;
|
||||||
|
if (typeof userAgent !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return userAgent.indexOf('iPhone') !== -1 ||
|
||||||
|
userAgent.indexOf('iPad') !== -1 ||
|
||||||
|
userAgent.indexOf('iPod') !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
$.extend( $, /** @lends OpenSeadragon */{
|
$.extend( $, /** @lends OpenSeadragon */{
|
||||||
/**
|
/**
|
||||||
@ -1033,6 +1040,7 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
|
minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
|
||||||
maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
|
maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
|
||||||
smoothTileEdgesMinZoom: 1.1, //-> higher than maxZoomPixelRatio disables it
|
smoothTileEdgesMinZoom: 1.1, //-> higher than maxZoomPixelRatio disables it
|
||||||
|
iOSDevice: isIOSDevice(),
|
||||||
pixelsPerWheelLine: 40,
|
pixelsPerWheelLine: 40,
|
||||||
autoResize: true,
|
autoResize: true,
|
||||||
preserveImageSizeOnResize: false, // requires autoResize=true
|
preserveImageSizeOnResize: false, // requires autoResize=true
|
||||||
@ -1065,6 +1073,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 +1331,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).
|
||||||
@ -2058,12 +2110,13 @@ if (typeof define === 'function' && define.amd) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (withCredentials) {
|
|
||||||
request.withCredentials = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
request.open( "GET", url, true );
|
request.open( "GET", url, true );
|
||||||
|
|
||||||
|
if (withCredentials) {
|
||||||
|
request.withCredentials = true;
|
||||||
|
}
|
||||||
|
|
||||||
request.send( null );
|
request.send( null );
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var msg = e.message;
|
var msg = e.message;
|
||||||
@ -2533,185 +2586,21 @@ 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 ) {
|
// Universal Module Definition, supports CommonJS, AMD and simple script tag
|
||||||
doc = xhr.responseXML;
|
(function (root, factory) {
|
||||||
} else if ( xhr.responseText ) {
|
if (typeof define === 'function' && define.amd) {
|
||||||
doc = $.parseXml( xhr.responseText );
|
// expose as amd module
|
||||||
}
|
define([], factory);
|
||||||
|
} else if (typeof module === 'object' && module.exports) {
|
||||||
return processDZIXml( doc, tilesUrl );
|
// expose as commonjs module
|
||||||
|
module.exports = factory();
|
||||||
|
} else {
|
||||||
|
// expose as window.OpenSeadragon
|
||||||
|
root.OpenSeadragon = factory();
|
||||||
}
|
}
|
||||||
|
}(this, function () {
|
||||||
/**
|
return OpenSeadragon;
|
||||||
* @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 ));
|
|
||||||
|
503
src/overlay.js
503
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 = {
|
||||||
@ -107,73 +127,67 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element = options.element;
|
this.element = options.element;
|
||||||
this.scales = options.location instanceof $.Rect;
|
this.style = options.element.style;
|
||||||
this.bounds = new $.Rect(
|
this._init(options);
|
||||||
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;
|
|
||||||
// rects are always top-left
|
|
||||||
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:
|
}
|
||||||
position.x -= size.x / 2;
|
if (properties.isHorizontallyCentered) {
|
||||||
break;
|
position.x -= size.x / 2;
|
||||||
case $.OverlayPlacement.TOP_RIGHT:
|
} else if (properties.isRight) {
|
||||||
position.x -= size.x;
|
position.x -= size.x;
|
||||||
break;
|
}
|
||||||
case $.OverlayPlacement.RIGHT:
|
if (properties.isVerticallyCentered) {
|
||||||
position.x -= size.x;
|
position.y -= size.y / 2;
|
||||||
position.y -= size.y / 2;
|
} else if (properties.isBottom) {
|
||||||
break;
|
position.y -= size.y;
|
||||||
case $.OverlayPlacement.BOTTOM_RIGHT:
|
|
||||||
position.x -= size.x;
|
|
||||||
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,115 +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 {
|
||||||
style.left = position.x + "px";
|
var style = this.style;
|
||||||
style.top = position.y + "px";
|
style.left = position.x + "px";
|
||||||
|
style.top = position.y + "px";
|
||||||
|
if (this.width !== null) {
|
||||||
|
style.width = size.x + "px";
|
||||||
|
}
|
||||||
|
if (this.height !== null) {
|
||||||
|
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";
|
style.position = "absolute";
|
||||||
|
|
||||||
if (style.display != 'none') {
|
if (style.display !== 'none') {
|
||||||
style.display = 'block';
|
style.display = 'block';
|
||||||
}
|
|
||||||
|
|
||||||
if ( scales ) {
|
|
||||||
style.width = size.x + "px";
|
|
||||||
style.height = size.y + "px";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
// 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;
|
|
||||||
this.bounds = new $.Rect(
|
|
||||||
location.x,
|
|
||||||
location.y,
|
|
||||||
location.width,
|
|
||||||
location.height
|
|
||||||
);
|
|
||||||
// rects are always top-left
|
|
||||||
this.placement = location instanceof $.Point ?
|
|
||||||
placement :
|
|
||||||
$.OverlayPlacement.TOP_LEFT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var rotate = 0;
|
||||||
|
if (viewport.degrees &&
|
||||||
|
this.rotationMode !== $.OverlayRotationMode.NO_ROTATION) {
|
||||||
|
// BOUNDING_BOX is only valid if both directions get scaled.
|
||||||
|
// Get replaced by EXACT otherwise.
|
||||||
|
if (this.rotationMode === $.OverlayRotationMode.BOUNDING_BOX &&
|
||||||
|
this.width !== null && this.height !== null) {
|
||||||
|
var rect = new $.Rect(position.x, position.y, size.x, size.y);
|
||||||
|
var boundingBox = this._getBoundingBox(rect, viewport.degrees);
|
||||||
|
position = boundingBox.getTopLeft();
|
||||||
|
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
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
getBounds: function(viewport) {
|
||||||
|
$.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.
|
||||||
@ -65,6 +69,7 @@
|
|||||||
* @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}.
|
* @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}.
|
||||||
* @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.
|
* @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.
|
||||||
* @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.
|
* @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.
|
||||||
|
* @param {Boolean} [options.iOSDevice] - See {@link OpenSeadragon.Options}.
|
||||||
* @param {Number} [options.opacity=1] - Opacity the tiled image should be drawn at.
|
* @param {Number} [options.opacity=1] - Opacity the tiled image should be drawn at.
|
||||||
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
|
* @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.
|
||||||
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
|
* @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.
|
||||||
@ -122,6 +127,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
|
||||||
@ -144,6 +154,7 @@ $.TiledImage = function( options ) {
|
|||||||
alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend,
|
alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend,
|
||||||
minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio,
|
minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio,
|
||||||
smoothTileEdgesMinZoom: $.DEFAULT_SETTINGS.smoothTileEdgesMinZoom,
|
smoothTileEdgesMinZoom: $.DEFAULT_SETTINGS.smoothTileEdgesMinZoom,
|
||||||
|
iOSDevice: $.DEFAULT_SETTINGS.iOSDevice,
|
||||||
debugMode: $.DEFAULT_SETTINGS.debugMode,
|
debugMode: $.DEFAULT_SETTINGS.debugMode,
|
||||||
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
|
crossOriginPolicy: $.DEFAULT_SETTINGS.crossOriginPolicy,
|
||||||
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
placeholderFillStyle: $.DEFAULT_SETTINGS.placeholderFillStyle,
|
||||||
@ -172,6 +183,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) {
|
||||||
/**
|
/**
|
||||||
@ -240,9 +255,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
|
|||||||
* Draws the TiledImage to its Drawer.
|
* Draws the TiledImage to its Drawer.
|
||||||
*/
|
*/
|
||||||
draw: function() {
|
draw: function() {
|
||||||
this._midDraw = true;
|
if (this.opacity !== 0) {
|
||||||
updateViewport( this );
|
this._midDraw = true;
|
||||||
this._midDraw = false;
|
updateViewport(this);
|
||||||
|
this._midDraw = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,6 +289,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.
|
||||||
*/
|
*/
|
||||||
@ -541,6 +578,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.
|
||||||
@ -648,6 +746,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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -686,7 +789,6 @@ function updateViewport( tiledImage ) {
|
|||||||
Math.log( 2 )
|
Math.log( 2 )
|
||||||
))
|
))
|
||||||
),
|
),
|
||||||
degrees = tiledImage.viewport.degrees,
|
|
||||||
renderPixelRatioC,
|
renderPixelRatioC,
|
||||||
renderPixelRatioT,
|
renderPixelRatioT,
|
||||||
zeroRatioT,
|
zeroRatioT,
|
||||||
@ -694,26 +796,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 = intersection;
|
||||||
viewportBounds.y -= orthBounds.height / 2;
|
|
||||||
viewportBounds.width += orthBounds.width;
|
|
||||||
viewportBounds.height += orthBounds.height;
|
|
||||||
}
|
}
|
||||||
|
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();
|
||||||
@ -879,10 +978,14 @@ function updateLevel( tiledImage, haveDrawn, drawLevel, level, levelOpacity, lev
|
|||||||
|
|
||||||
resetCoverage( tiledImage.coverage, level );
|
resetCoverage( tiledImage.coverage, level );
|
||||||
|
|
||||||
if ( !tiledImage.wrapHorizontal ) {
|
if ( tiledImage.wrapHorizontal ) {
|
||||||
|
tileTL.x -= 1; // left invisible column (othervise we will have empty space after scroll at left)
|
||||||
|
} else {
|
||||||
tileBR.x = Math.min( tileBR.x, numberOfTiles.x - 1 );
|
tileBR.x = Math.min( tileBR.x, numberOfTiles.x - 1 );
|
||||||
}
|
}
|
||||||
if ( !tiledImage.wrapVertical ) {
|
if ( tiledImage.wrapVertical ) {
|
||||||
|
tileTL.y -= 1; // top invisible row (othervise we will have empty space after scroll at top)
|
||||||
|
} else {
|
||||||
tileBR.y = Math.min( tileBR.y, numberOfTiles.y - 1 );
|
tileBR.y = Math.min( tileBR.y, numberOfTiles.y - 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1333,24 +1436,25 @@ 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 && !tiledImage.iOSDevice) {
|
||||||
// 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.
|
||||||
|
// Note: Disabled on iOS devices per default as it causes a native crash
|
||||||
useSketch = true;
|
useSketch = true;
|
||||||
sketchScale = tile.getScaleForEdgeSmoothing();
|
sketchScale = tile.getScaleForEdgeSmoothing();
|
||||||
sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
|
sketchTranslate = tile.getTranslationForEdgeSmoothing(sketchScale,
|
||||||
@ -1358,8 +1462,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
|
||||||
@ -1405,7 +1518,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;
|
||||||
@ -1442,7 +1555,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);
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||||||
this.aspectRatio = ( options.width && options.height ) ?
|
this.aspectRatio = ( options.width && options.height ) ?
|
||||||
( options.width / options.height ) : 1;
|
( options.width / options.height ) : 1;
|
||||||
this.dimensions = new $.Point( options.width, options.height );
|
this.dimensions = new $.Point( options.width, options.height );
|
||||||
|
|
||||||
if ( this.tileSize ){
|
if ( this.tileSize ){
|
||||||
this._tileWidth = this._tileHeight = this.tileSize;
|
this._tileWidth = this._tileHeight = this.tileSize;
|
||||||
delete this.tileSize;
|
delete this.tileSize;
|
||||||
@ -212,7 +212,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
|
|||||||
this._tileHeight = 0;
|
this._tileHeight = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0;
|
this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0;
|
||||||
this.minLevel = options.minLevel ? options.minLevel : 0;
|
this.minLevel = options.minLevel ? options.minLevel : 0;
|
||||||
this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ?
|
this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ?
|
||||||
@ -240,7 +240,7 @@ $.TileSource.prototype = {
|
|||||||
);
|
);
|
||||||
return this._tileWidth;
|
return this._tileWidth;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the tileWidth for a given level.
|
* Return the tileWidth for a given level.
|
||||||
* Subclasses should override this if tileWidth can be different at different levels
|
* Subclasses should override this if tileWidth can be different at different levels
|
||||||
@ -331,7 +331,7 @@ $.TileSource.prototype = {
|
|||||||
Math.floor( rect.x / this.getTileWidth(i) ),
|
Math.floor( rect.x / this.getTileWidth(i) ),
|
||||||
Math.floor( rect.y / this.getTileHeight(i) )
|
Math.floor( rect.y / this.getTileHeight(i) )
|
||||||
);
|
);
|
||||||
|
|
||||||
if( tiles.x + 1 >= tilesPerSide.x && tiles.y + 1 >= tilesPerSide.y ){
|
if( tiles.x + 1 >= tilesPerSide.x && tiles.y + 1 >= tilesPerSide.y ){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -345,11 +345,11 @@ $.TileSource.prototype = {
|
|||||||
* @param {OpenSeadragon.Point} point
|
* @param {OpenSeadragon.Point} point
|
||||||
*/
|
*/
|
||||||
getTileAtPoint: function( level, point ) {
|
getTileAtPoint: function( level, point ) {
|
||||||
var pixel = point.times( this.dimensions.x ).times( this.getLevelScale(level) ),
|
var numTiles = this.getNumTiles( level );
|
||||||
tx = Math.floor( pixel.x / this.getTileWidth(level) ),
|
return new $.Point(
|
||||||
ty = Math.floor( pixel.y / this.getTileHeight(level) );
|
Math.floor( (point.x * numTiles.x) / 1 ),
|
||||||
|
Math.floor( (point.y * numTiles.y * this.dimensions.x) / this.dimensions.y )
|
||||||
return new $.Point( tx, ty );
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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
|
||||||
|
254
src/viewer.js
254
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 );
|
||||||
this.canvas.tabIndex = (options.tabIndex === undefined ? 0 : options.tabIndex);
|
if (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";
|
||||||
@ -330,7 +337,7 @@ $.Viewer = function( options ) {
|
|||||||
|
|
||||||
this.world.addHandler('metrics-change', function(event) {
|
this.world.addHandler('metrics-change', function(event) {
|
||||||
if (_this.viewport) {
|
if (_this.viewport) {
|
||||||
_this.viewport.setHomeBounds(_this.world.getHomeBounds(), _this.world.getContentFactor());
|
_this.viewport._setContentBounds(_this.world.getHomeBounds(), _this.world.getContentFactor());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -359,7 +366,7 @@ $.Viewer = function( options ) {
|
|||||||
margins: this.viewportMargins
|
margins: this.viewportMargins
|
||||||
});
|
});
|
||||||
|
|
||||||
this.viewport.setHomeBounds(this.world.getHomeBounds(), this.world.getContentFactor());
|
this.viewport._setContentBounds(this.world.getHomeBounds(), this.world.getContentFactor());
|
||||||
|
|
||||||
// Create the image loader
|
// Create the image loader
|
||||||
this.imageLoader = new $.ImageLoader({
|
this.imageLoader = new $.ImageLoader({
|
||||||
@ -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,
|
||||||
@ -779,6 +787,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
*/
|
*/
|
||||||
setMouseNavEnabled: function( enabled ){
|
setMouseNavEnabled: function( enabled ){
|
||||||
this.innerTracker.setTracking( enabled );
|
this.innerTracker.setTracking( enabled );
|
||||||
|
this.outerTracker.setTracking( enabled );
|
||||||
/**
|
/**
|
||||||
* Raised when mouse/touch navigation is enabled or disabled (see {@link OpenSeadragon.Viewer#setMouseNavEnabled}).
|
* Raised when mouse/touch navigation is enabled or disabled (see {@link OpenSeadragon.Viewer#setMouseNavEnabled}).
|
||||||
*
|
*
|
||||||
@ -910,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
|
||||||
@ -973,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;
|
||||||
@ -1203,11 +1220,17 @@ $.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.
|
||||||
* @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default.
|
* @param {Number} [options.opacity] Opacity the tiled image should be drawn at by default.
|
||||||
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
* @param {String} [options.compositeOperation] How the image is composited onto other images.
|
||||||
|
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
|
||||||
|
* overriding viewer.crossOriginPolicy.
|
||||||
* @param {Function} [options.success] A function that gets called when the image is
|
* @param {Function} [options.success] A function that gets called when the image is
|
||||||
* successfully added. It's passed the event object which contains a single property:
|
* successfully added. It's passed the event object which contains a single property:
|
||||||
* "item", the resulting TiledImage.
|
* "item", the resulting TiledImage.
|
||||||
@ -1243,6 +1266,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
if (options.compositeOperation === undefined) {
|
if (options.compositeOperation === undefined) {
|
||||||
options.compositeOperation = this.compositeOperation;
|
options.compositeOperation = this.compositeOperation;
|
||||||
}
|
}
|
||||||
|
if (options.crossOriginPolicy === undefined) {
|
||||||
|
options.crossOriginPolicy = options.tileSource.crossOriginPolicy !== undefined ? options.tileSource.crossOriginPolicy : this.crossOriginPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
var myQueueItem = {
|
var myQueueItem = {
|
||||||
options: options
|
options: options
|
||||||
@ -1305,7 +1331,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
|
|
||||||
this._loadQueue.push(myQueueItem);
|
this._loadQueue.push(myQueueItem);
|
||||||
|
|
||||||
getTileSourceImplementation( this, options.tileSource, function( tileSource ) {
|
getTileSourceImplementation( this, options.tileSource, options, function( tileSource ) {
|
||||||
|
|
||||||
myQueueItem.tileSource = tileSource;
|
myQueueItem.tileSource = tileSource;
|
||||||
|
|
||||||
@ -1338,6 +1364,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,
|
||||||
@ -1352,7 +1380,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
alwaysBlend: _this.alwaysBlend,
|
alwaysBlend: _this.alwaysBlend,
|
||||||
minPixelRatio: _this.minPixelRatio,
|
minPixelRatio: _this.minPixelRatio,
|
||||||
smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
|
smoothTileEdgesMinZoom: _this.smoothTileEdgesMinZoom,
|
||||||
crossOriginPolicy: _this.crossOriginPolicy,
|
iOSDevice: _this.iOSDevice,
|
||||||
|
crossOriginPolicy: queueItem.options.crossOriginPolicy,
|
||||||
debugMode: _this.debugMode
|
debugMode: _this.debugMode
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1374,6 +1403,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
|
|
||||||
if (_this.navigator) {
|
if (_this.navigator) {
|
||||||
optionsClone = $.extend({}, queueItem.options, {
|
optionsClone = $.extend({}, queueItem.options, {
|
||||||
|
replace: false, // navigator already removed the layer, nothing to replace
|
||||||
originalTiledImage: tiledImage,
|
originalTiledImage: tiledImage,
|
||||||
tileSource: queueItem.tileSource
|
tileSource: queueItem.tileSource
|
||||||
});
|
});
|
||||||
@ -1779,10 +1809,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
|
||||||
@ -1824,7 +1856,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', {
|
||||||
@ -1842,8 +1874,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.
|
||||||
@ -1869,7 +1901,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', {
|
||||||
@ -1944,6 +1976,27 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds an overlay identified by the reference element or element id
|
||||||
|
* and returns it as an object, return null if not found.
|
||||||
|
* @method
|
||||||
|
* @param {Element|String} element - A reference to the element or an
|
||||||
|
* element id which represents the overlay content.
|
||||||
|
* @return {OpenSeadragon.Overlay} the matching overlay or null if none found.
|
||||||
|
*/
|
||||||
|
getOverlayById: function( element ) {
|
||||||
|
var i;
|
||||||
|
|
||||||
|
element = $.getElement( element );
|
||||||
|
i = getOverlayIndex( this.currentOverlays, element );
|
||||||
|
|
||||||
|
if (i>=0) {
|
||||||
|
return this.currentOverlays[i];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the sequence buttons.
|
* Updates the sequence buttons.
|
||||||
* @function OpenSeadragon.Viewer.prototype._updateSequenceButtons
|
* @function OpenSeadragon.Viewer.prototype._updateSequenceButtons
|
||||||
@ -2062,7 +2115,7 @@ function _getSafeElemSize (oElement) {
|
|||||||
* @function
|
* @function
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function getTileSourceImplementation( viewer, tileSource, successCallback,
|
function getTileSourceImplementation( viewer, tileSource, imgOptions, successCallback,
|
||||||
failCallback ) {
|
failCallback ) {
|
||||||
var _this = viewer;
|
var _this = viewer;
|
||||||
|
|
||||||
@ -2096,7 +2149,8 @@ function getTileSourceImplementation( viewer, tileSource, successCallback,
|
|||||||
//If its still a string it means it must be a url at this point
|
//If its still a string it means it must be a url at this point
|
||||||
tileSource = new $.TileSource({
|
tileSource = new $.TileSource({
|
||||||
url: tileSource,
|
url: tileSource,
|
||||||
crossOriginPolicy: viewer.crossOriginPolicy,
|
crossOriginPolicy: imgOptions.crossOriginPolicy !== undefined ?
|
||||||
|
imgOptions.crossOriginPolicy : viewer.crossOriginPolicy,
|
||||||
ajaxWithCredentials: viewer.ajaxWithCredentials,
|
ajaxWithCredentials: viewer.ajaxWithCredentials,
|
||||||
useCanvas: viewer.useCanvas,
|
useCanvas: viewer.useCanvas,
|
||||||
success: function( event ) {
|
success: function( event ) {
|
||||||
@ -2108,8 +2162,10 @@ function getTileSourceImplementation( viewer, tileSource, successCallback,
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
} else if ($.isPlainObject(tileSource) || tileSource.nodeType) {
|
} else if ($.isPlainObject(tileSource) || tileSource.nodeType) {
|
||||||
if (!tileSource.crossOriginPolicy && viewer.crossOriginPolicy) {
|
if (tileSource.crossOriginPolicy === undefined &&
|
||||||
tileSource.crossOriginPolicy = viewer.crossOriginPolicy;
|
(imgOptions.crossOriginPolicy !== undefined || viewer.crossOriginPolicy !== undefined)) {
|
||||||
|
tileSource.crossOriginPolicy = imgOptions.crossOriginPolicy !== undefined ?
|
||||||
|
imgOptions.crossOriginPolicy : viewer.crossOriginPolicy;
|
||||||
}
|
}
|
||||||
if (tileSource.ajaxWithCredentials === undefined) {
|
if (tileSource.ajaxWithCredentials === undefined) {
|
||||||
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
|
tileSource.ajaxWithCredentials = viewer.ajaxWithCredentials;
|
||||||
@ -2169,37 +2225,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;
|
||||||
overlay.px,
|
var y = overlay.y;
|
||||||
overlay.py,
|
if (overlay.px !== undefined) {
|
||||||
overlay.width,
|
var rect = viewer.viewport.imageToViewportRectangle(new $.Rect(
|
||||||
overlay.height
|
overlay.px,
|
||||||
) ) :
|
overlay.py,
|
||||||
new $.Rect(
|
width || 0,
|
||||||
overlay.x,
|
height || 0));
|
||||||
overlay.y,
|
x = rect.x;
|
||||||
overlay.width,
|
y = rect.y;
|
||||||
overlay.height
|
width = width !== undefined ? rect.width : undefined;
|
||||||
);
|
height = height !== undefined ? rect.height : undefined;
|
||||||
} 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({
|
||||||
@ -2207,7 +2254,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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2520,22 +2570,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();
|
||||||
}
|
}
|
||||||
@ -2554,7 +2607,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,
|
||||||
@ -2846,7 +2899,10 @@ function onCanvasScroll( event ) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false; // We are swallowing this event
|
gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
|
||||||
|
if (gestureSettings && gestureSettings.scrollToZoom) {
|
||||||
|
return false; // We are swallowing this event
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2938,33 +2994,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3049,27 +3098,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();
|
||||||
|
666
src/viewport.js
666
src/viewport.js
File diff suppressed because it is too large
Load Diff
44
src/world.js
44
src/world.js
@ -375,34 +375,40 @@ $.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._contentSize.equals(oldContentSize)) {
|
!this._homeBounds.equals(oldHomeBounds) ||
|
||||||
|
!this._contentSize.equals(oldContentSize)) {
|
||||||
/**
|
/**
|
||||||
* Raised when the home bounds or content factor change.
|
* Raised when the home bounds or content factor change.
|
||||||
* @event metrics-change
|
* @event metrics-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
@ -365,4 +365,62 @@
|
|||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
asyncTest( 'CrossOriginPolicyOption', function () {
|
||||||
|
|
||||||
|
browserSupportsImgCrossOrigin(function(supported) {
|
||||||
|
if (!supported) {
|
||||||
|
expect(0);
|
||||||
|
start();
|
||||||
|
} else {
|
||||||
|
viewer.crossOriginPolicy = "Anonymous";
|
||||||
|
viewer.smoothTileEdgesMinZoom = Infinity;
|
||||||
|
viewer.addTiledImage( {
|
||||||
|
tileSource: {
|
||||||
|
type: 'legacy-image-pyramid',
|
||||||
|
levels: [ {
|
||||||
|
url: corsImg,
|
||||||
|
width: 135,
|
||||||
|
height: 155
|
||||||
|
} ]
|
||||||
|
},
|
||||||
|
crossOriginPolicy : false
|
||||||
|
} );
|
||||||
|
viewer.addHandler('tile-drawn', function() {
|
||||||
|
ok(OpenSeadragon.isCanvasTainted(viewer.drawer.context.canvas),
|
||||||
|
"Canvas should be tainted.");
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} );
|
||||||
|
asyncTest( 'CrossOriginPolicyTileSource', function () {
|
||||||
|
|
||||||
|
browserSupportsImgCrossOrigin(function(supported) {
|
||||||
|
if (!supported) {
|
||||||
|
expect(0);
|
||||||
|
start();
|
||||||
|
} else {
|
||||||
|
viewer.crossOriginPolicy = false;
|
||||||
|
viewer.smoothTileEdgesMinZoom = Infinity;
|
||||||
|
viewer.addTiledImage( {
|
||||||
|
tileSource: {
|
||||||
|
type: 'legacy-image-pyramid',
|
||||||
|
levels: [ {
|
||||||
|
url: corsImg,
|
||||||
|
width: 135,
|
||||||
|
height: 155
|
||||||
|
} ],
|
||||||
|
crossOriginPolicy : "Anonymous"
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
viewer.addHandler('tile-drawn', function() {
|
||||||
|
ok(!OpenSeadragon.isCanvasTainted(viewer.drawer.context.canvas),
|
||||||
|
"Canvas should not be tainted.");
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} );
|
||||||
})();
|
})();
|
||||||
|
@ -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) {
|
||||||
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
|
asyncTest( 'MouseTracker: touch events', function () {
|
||||||
tracker = viewer.innerTracker,
|
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
|
||||||
origClickHandler,
|
tracker = viewer.innerTracker,
|
||||||
origDragHandler,
|
touches;
|
||||||
dragCount = 10,
|
|
||||||
originalZoom = 0,
|
|
||||||
originalBounds = null;
|
|
||||||
|
|
||||||
var onOpen = function ( event ) {
|
var reset = function () {
|
||||||
viewer.removeHandler( 'open', onOpen );
|
touches = [];
|
||||||
|
TouchUtil.reset();
|
||||||
// Hook viewer events to set preventDefaultAction
|
|
||||||
origClickHandler = tracker.clickHandler;
|
|
||||||
tracker.clickHandler = function ( event ) {
|
|
||||||
event.preventDefaultAction = true;
|
|
||||||
return origClickHandler( event );
|
|
||||||
};
|
|
||||||
origDragHandler = tracker.dragHandler;
|
|
||||||
tracker.dragHandler = function ( event ) {
|
|
||||||
event.preventDefaultAction = true;
|
|
||||||
return origDragHandler( event );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
originalZoom = viewer.viewport.getZoom();
|
var assessTouchExpectations = function ( expected ) {
|
||||||
originalBounds = viewer.viewport.getBounds();
|
var pointersList = tracker.getActivePointersListByType( 'touch' );
|
||||||
|
if ('captureCount' in expected) {
|
||||||
var event = {
|
equal( pointersList.captureCount, expected.captureCount, expected.description + 'Pointer capture count matches expected (' + expected.captureCount + ')' );
|
||||||
clientX:1,
|
}
|
||||||
clientY:1
|
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 ) {
|
||||||
|
viewer.removeHandler( 'open', onOpen );
|
||||||
|
|
||||||
|
TouchUtil.initTracker( tracker );
|
||||||
|
|
||||||
|
// start-end-end (multi-touch start event)
|
||||||
|
reset();
|
||||||
|
touches = TouchUtil.start( [0,0], [20,20] );
|
||||||
|
assessTouchExpectations({
|
||||||
|
description: 'start-end-end (multi-touch start event) [capture]: ',
|
||||||
|
captureCount: 2,
|
||||||
|
contacts: 2,
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
viewer.addHandler( 'open', onOpen );
|
||||||
|
viewer.open( '/test/data/testpattern.dzi' );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------
|
||||||
|
asyncTest('Viewer: preventDefaultAction', function() {
|
||||||
|
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(
|
||||||
|
new OpenSeadragon.Rect(-249.5, -0.25, 500, 0.5),
|
||||||
|
bounds,
|
||||||
|
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]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -78,9 +78,11 @@
|
|||||||
|
|
||||||
if (navigator === null) {
|
if (navigator === null) {
|
||||||
navigator = $(".navigator");
|
navigator = $(".navigator");
|
||||||
navigatorScaleFactor = Math.min(navigator.width() / viewer.viewport.contentSize.x, navigator.height() / viewer.viewport.contentSize.y);
|
navigatorScaleFactor = Math.min(
|
||||||
displayRegionWidth = viewer.viewport.contentSize.x * navigatorScaleFactor;
|
navigator.width() / viewer.viewport._contentSize.x,
|
||||||
displayRegionHeight = viewer.viewport.contentSize.y * navigatorScaleFactor;
|
navigator.height() / viewer.viewport._contentSize.y);
|
||||||
|
displayRegionWidth = viewer.viewport._contentSize.x * navigatorScaleFactor;
|
||||||
|
displayRegionHeight = viewer.viewport._contentSize.y * navigatorScaleFactor;
|
||||||
contentStartFromLeft = (navigator.width() - displayRegionWidth) / 2;
|
contentStartFromLeft = (navigator.width() - displayRegionWidth) / 2;
|
||||||
contentStartFromTop = (navigator.height() - displayRegionHeight) / 2;
|
contentStartFromTop = (navigator.height() - displayRegionHeight) / 2;
|
||||||
}
|
}
|
||||||
@ -91,8 +93,8 @@
|
|||||||
regionBoundsInPoints = new OpenSeadragon.Rect(expectedDisplayRegionXLocation, expectedDisplayRegionYLocation, expectedDisplayRegionWidth, expectedDisplayRegionHeight);
|
regionBoundsInPoints = new OpenSeadragon.Rect(expectedDisplayRegionXLocation, expectedDisplayRegionYLocation, expectedDisplayRegionWidth, expectedDisplayRegionHeight);
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.log('Image width: ' + viewer.viewport.contentSize.x + '\n' +
|
console.log('Image width: ' + viewer.viewport._contentSize.x + '\n' +
|
||||||
'Image height: ' + viewer.viewport.contentSize.y + '\n' +
|
'Image height: ' + viewer.viewport._contentSize.y + '\n' +
|
||||||
'navigator.width(): ' + navigator.width() + '\n' +
|
'navigator.width(): ' + navigator.width() + '\n' +
|
||||||
'navigator.height(): ' + navigator.height() + '\n' +
|
'navigator.height(): ' + navigator.height() + '\n' +
|
||||||
'navigatorScaleFactor: ' + navigatorScaleFactor + '\n' +
|
'navigatorScaleFactor: ' + navigatorScaleFactor + '\n' +
|
||||||
@ -867,4 +869,57 @@
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
asyncTest('Viewer rotation applied to navigator by default', function() {
|
||||||
|
|
||||||
|
viewer = OpenSeadragon({
|
||||||
|
id: 'example',
|
||||||
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
|
tileSources: '/test/data/tall.dzi',
|
||||||
|
springStiffness: 100, // Faster animation = faster tests
|
||||||
|
showNavigator: true,
|
||||||
|
degrees: 45
|
||||||
|
});
|
||||||
|
viewer.addHandler('open', function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
|
||||||
|
var navigator = viewer.navigator;
|
||||||
|
|
||||||
|
equal(navigator.viewport.getRotation(), 45,
|
||||||
|
"Rotation set in constructor should be applied to navigator by default.");
|
||||||
|
|
||||||
|
viewer.viewport.setRotation(90);
|
||||||
|
equal(navigator.viewport.getRotation(), 90,
|
||||||
|
"Rotation set by setRotation should be applied to navigator by default.");
|
||||||
|
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('Viewer rotation not applied to navigator when navigatorRotate=false', function() {
|
||||||
|
|
||||||
|
viewer = OpenSeadragon({
|
||||||
|
id: 'example',
|
||||||
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
|
tileSources: '/test/data/tall.dzi',
|
||||||
|
springStiffness: 100, // Faster animation = faster tests
|
||||||
|
showNavigator: true,
|
||||||
|
degrees: 45,
|
||||||
|
navigatorRotate: false
|
||||||
|
});
|
||||||
|
viewer.addHandler('open', function openHandler() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
|
||||||
|
var navigator = viewer.navigator;
|
||||||
|
|
||||||
|
equal(navigator.viewport.getRotation(), 0,
|
||||||
|
"Rotation set in constructor should not be applied to navigator when navigatorRotate is false.");
|
||||||
|
|
||||||
|
viewer.viewport.setRotation(90);
|
||||||
|
equal(navigator.viewport.getRotation(), 0,
|
||||||
|
"Rotation set by setRotation should not be applied to navigator when navigatorRotate is false.");
|
||||||
|
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
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,18 +4,18 @@
|
|||||||
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();
|
||||||
|
|
||||||
viewer = OpenSeadragon({
|
viewer = OpenSeadragon({
|
||||||
id: 'example',
|
id: 'example',
|
||||||
prefixUrl: '/build/openseadragon/images/',
|
prefixUrl: '/build/openseadragon/images/',
|
||||||
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',
|
||||||
@ -305,7 +378,7 @@
|
|||||||
var rect = testRects[i].times(viewport.getContainerSize());
|
var rect = testRects[i].times(viewport.getContainerSize());
|
||||||
viewport.resetContentSize(rect.getSize());
|
viewport.resetContentSize(rect.getSize());
|
||||||
propEqual(
|
propEqual(
|
||||||
viewport.contentSize,
|
viewport._contentSize,
|
||||||
rect.getSize(),
|
rect.getSize(),
|
||||||
"Reset content size correctly."
|
"Reset content size correctly."
|
||||||
);
|
);
|
||||||
@ -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,135 @@
|
|||||||
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(
|
||||||
|
new OpenSeadragon.Rect(0.7, 0.7, 1, 1),
|
||||||
|
bounds,
|
||||||
|
EPSILON,
|
||||||
|
"Viewport.applyConstraints should move viewport.");
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open(DZI_PATH);
|
||||||
|
});
|
||||||
|
|
||||||
|
asyncTest('applyConstraints with visibilityRatio = 1 shouldn\'t bounce around', function() {
|
||||||
|
var openHandler = function() {
|
||||||
|
viewer.removeHandler('open', openHandler);
|
||||||
|
var viewport = viewer.viewport;
|
||||||
|
|
||||||
|
viewport.visibilityRatio = 1;
|
||||||
|
viewport.zoomTo(0.5, undefined, true);
|
||||||
|
viewport.panBy(new OpenSeadragon.Point(0.75, 0), true);
|
||||||
|
viewport.applyConstraints(true);
|
||||||
|
var bounds = viewport.getBounds();
|
||||||
|
Util.assertRectangleEquals(
|
||||||
|
new OpenSeadragon.Rect(-0.5, 1, 2, 2),
|
||||||
|
bounds,
|
||||||
|
EPSILON,
|
||||||
|
"Viewport.applyConstraints should move viewport to the center, not to a side.");
|
||||||
|
start();
|
||||||
|
};
|
||||||
|
viewer.addHandler('open', openHandler);
|
||||||
|
viewer.open(TALL_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 +565,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 +598,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 +608,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 +677,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…
x
Reference in New Issue
Block a user