OpenSeadragon.Tools = class {
constructor(context) {
if (context.tools) throw "OSD Tools already instantiated on the given viewer instance!";
context.tools = this;
this.viewer = context;
}
async raiseAwaitEvent(context, eventName, eventArgs = undefined) {
let events = context.events[ eventName ];
if ( !events || !events.length ) {
return null;
}
events = events.length === 1 ?
[ events[ 0 ] ] :
Array.apply( null, events );
eventArgs = eventArgs || {};
const length = events.length;
async function loop(index) {
if ( index >= length || !events[ index ] ) {
return;
}
eventArgs.stopPropagation = function () {
index = length;
};
eventArgs.eventSource = context;
eventArgs.userData = events[ index ].userData;
let result = events[ index ].handler( eventArgs );
if (result && OpenSeadragon.type(result) === "promise") {
await result;
}
await loop(index + 1);
}
return await loop(0);
}
focus(params) {
this.constructor.focus(this.viewer, params);
}
static focus(context, params) {
let view = context.viewport,
_centerSpringXAnimationTime = view.centerSpringX.animationTime,
_centerSpringYAnimationTime = view.centerSpringY.animationTime,
_zoomSpringAnimationTime = view.zoomSpring.animationTime;
let duration = params.animationTime || params.duration;
if (!isNaN(duration)) {
view.centerSpringX.animationTime =
view.centerSpringY.animationTime =
view.zoomSpring.animationTime =
duration;
}
let transition = params.springStiffness || params.transition;
if (!isNaN(transition)) {
view.centerSpringX.springStiffness =
view.centerSpringY.springStiffness =
view.zoomSpring.springStiffness =
transition;
}
if ((params.point && params.zoomLevel) && (params.preferSameZoom || !params.bounds)) {
view.panTo(params.point, params.immediately);
view.zoomTo(params.zoomLevel, params.immediately);
} else if (params.bounds) {
view.fitBoundsWithConstraints(params.bounds, params.immediately);
} else {
throw "No valid focus data provided!";
}
view.applyConstraints();
view.centerSpringX.animationTime = _centerSpringXAnimationTime;
view.centerSpringY.animationTime = _centerSpringYAnimationTime;
view.zoomSpring.animationTime = _zoomSpringAnimationTime;
}
screenshot(toImage, size = {}, focus=undefined) {
return this.constructor.screenshot(this.viewer, toImage, size, focus);
}
static screenshot(context, toImage, size = {}, focus=undefined) {
if (context.drawer.canvas.width < 1) return undefined;
let drawCtx = context.drawer.context;
if (!drawCtx) throw "OpenSeadragon must render with canvasses!";
if (!focus) focus = new OpenSeadragon.Rect(0, 0, window.innerWidth, window.innerHeight);
size.width = size.width || focus.width;
size.height = size.height || focus.height;
let ar = size.width / size.height;
if (focus.width < focus.height) focus.width *= ar;
else focus.height /= ar;
if (toImage) {
let data = drawCtx.getImageData(focus.x,focus.y, focus.width, focus.height);
let canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = size.width;
canvas.height = size.height;
ctx.putImageData(data, 0, 0);
let img = document.createElement("img");
img.src = canvas.toDataURL();
return img;
}
return drawCtx;
}
offlineScreenshot(region, targetSize, onfinish, outputSize=targetSize) {
let referencedTiledImage = this.viewer.scalebar.getReferencedTiledImage();
let referencedSource = referencedTiledImage.source;
function download(tiledImage, level, x, y, onload, onfail) {
let tileSource = tiledImage.source;
let numTiles = tileSource.getNumTiles( level );
let xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
let yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
let bounds = tiledImage.getTileBounds( level, x, y );
let sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
let exists = tileSource.tileExists( level, xMod, yMod );
let url = tileSource.getTileUrl( level, xMod, yMod );
let post = tileSource.getTilePostData( level, xMod, yMod );
let ajaxHeaders;
if (tiledImage.loadTilesWithAjax) {
ajaxHeaders = tileSource.getTileAjaxHeaders( level, xMod, yMod );
if (OpenSeadragon.isPlainObject(tiledImage.ajaxHeaders)) {
ajaxHeaders = $.extend({}, tiledImage.ajaxHeaders, ajaxHeaders);
}
} else {
ajaxHeaders = null;
}
let tile = new OpenSeadragon.Tile(
level,
x,
y,
bounds,
exists,
url,
undefined,
tiledImage.loadTilesWithAjax,
ajaxHeaders,
sourceBounds,
post,
tileSource.getTileHashKey(level, xMod, yMod, url, ajaxHeaders, post)
);
tile.loading = true;
tiledImage._imageLoader.addJob({
src: tile.getUrl(),
tile: tile,
source: tiledImage.source,
postData: tile.postData,
loadWithAjax: tile.loadWithAjax,
ajaxHeaders: tile.ajaxHeaders,
crossOriginPolicy: tiledImage.crossOriginPolicy,
ajaxWithCredentials: tiledImage.ajaxWithCredentials,
callback: function( data, errorMsg, tileRequest ){
tile.loading = false;
if ( !data ) {
tile.exists = false;
onfail(data, tile);
return;
}
tile.tiledImage = tiledImage;
onload(data, tile);
},
abort: function() {
tile.loading = false;
onfail(data, tile);
}
});
}
function buildImageForLayer(tiledImage, region, level, onBuilt) {
let source = tiledImage.source,
viewportX = region.x / referencedSource.width,
viewportY = region.y / referencedSource.width,
viewportXAndWidth = (region.x+region.width-1) / referencedSource.width,
viewportYAdnHeight = (region.y+region.height-1) / referencedSource.width;
let tileXY = source.getTileAtPoint(level, new OpenSeadragon.Point(viewportX, viewportY)),
tileXWY = source.getTileAtPoint(level, new OpenSeadragon.Point(viewportXAndWidth, viewportY)),
tileXYH = source.getTileAtPoint(level, new OpenSeadragon.Point(viewportX, viewportYAdnHeight)),
tileXWYH = source.getTileAtPoint(level, new OpenSeadragon.Point(viewportXAndWidth, viewportYAdnHeight));
let scale = referencedSource.getLevelScale(level),
tileWidth = source.getTileWidth(level),
tileHeight = source.getTileHeight(level),
x = Math.floor(region.x * scale),
y = Math.floor(region.y * scale),
w = Math.floor(region.width * scale),
h = Math.floor(region.height * scale),
canvas = document.createElement('canvas'),
c2d = canvas.getContext('2d');
canvas.width = w;
canvas.height = h;
function draw(data, tile) {
let sx = tileWidth * tile.x - x, sy = tileHeight * tile.y - y,
sDx = 0, sDy = 0,
dw = tile.sourceBounds.width, dh = tile.sourceBounds.height;
if (sx < 0) {
dw += sx;
sDx = -sx;
sx = 0;
}
if (sDy < 0) {
dh += sy;
sDy = -sy;
sy = 0;
}
tile.setCache(data, tile.cacheKey);
const canvas = tile.getCanvasContext ? tile.getCanvasContext()?.canvas : tile.getImage();
c2d.drawImage(canvas, sDx, sDy, dw, dh, sx, sy, dw, dh);
finish();
}
function fill(data, tile) {
console.log("aborted", data);
finish();
}
function finish() {
count--;
if (count === 0) {
onBuilt(canvas);
}
}
let count = 4;
download(tiledImage, level, tileXY.x, tileXY.y, draw, fill);
if (tileXY.x !== tileXWY.x) download(tiledImage, level, tileXWY.x, tileXWY.y, draw, fill);
else count--;
if (tileXY.y !== tileXYH.y) download(tiledImage, level, tileXYH.x, tileXYH.y, draw, fill);
else count--;
if (count === 4) download(tiledImage, level, tileXWYH.x, tileXWYH.y, draw, fill);
else count--;
}
let canvasCache = {};
const itemCount = 1;
let steps = itemCount;
for (let itemIndex = 0; itemIndex < itemCount; itemIndex++) {
const tImage = VIEWER.world.getItemAt(itemIndex);
const handler = (index, canvas) => {
steps--;
canvasCache[index] = canvas;
if (steps < 1) {
let outputCanvas = document.createElement('canvas'),
c2d = outputCanvas.getContext('2d');
outputCanvas.width = outputSize.width;
outputCanvas.height = outputSize.height;
for (let i=0; i < itemCount; i++) {
const c = canvasCache[i];
if (c) c2d.drawImage(c, 0, 0, c.width, c.height);
}
onfinish(outputCanvas);
}
};
const level = this.constructor._bestLevelForTiledImage(tImage, region, targetSize);
buildImageForLayer(tImage, region, level, handler.bind(null, itemIndex));
}
}
static _bestLevelForTiledImage(image, region, targetSize) {
function getDiff(source, level) {
let scale = source.getLevelScale(level);
return Math.min(Math.abs(region.width * scale - targetSize.width),
Math.abs(region.height * scale - targetSize.height));
}
let source = image.source,
bestLevel = source.maxLevel,
d = getDiff(source, bestLevel);
for (let i = source.maxLevel-1; i >= source.minLevel; i--) {
let dd = getDiff(source, i);
if (dd > d) break;
bestLevel = i;
d = dd;
}
return bestLevel;
}
link(child) {
this.constructor.link(child, this.viewer);
}
static link(child, parent) {
if (child.__linkHandler) child.removeHandler(child.__linkHandler);
if (parent.__linkHandler) parent.removeHandler(parent.__linkHandler);
child.__linkHandler = function (e) {
if (child.__synced) {
child.__synced = false;
return;
}
parent.__synced = true;
OpenSeadragon.Tools.syncViewers(child, parent);
};
parent.__linkHandler = function (e) {
if (parent.__synced) {
parent.__synced = false;
return;
}
child.__synced = true;
OpenSeadragon.Tools.syncViewers(parent, child);
};
child.addHandler('viewport-change', child.__linkHandler);
parent.addHandler('viewport-change', parent.__linkHandler);
OpenSeadragon.Tools.syncViewers(child, parent);
}
syncViewers(viewer, otherViewer) {
this.constructor.syncViewers(viewer, otherViewer);
}
static syncViewers(viewer, otherViewer) {
this._syncViewports(viewer.viewport, otherViewer.viewport);
}
static _syncViewports(viewport, otherViewport) {
otherViewport.fitBoundsWithConstraints(viewport.getBounds(), true);
}
};
OpenSeadragon.LinkedViewport = class {
constructor(context, parentContext) {
this.p = parentContext; this.ch = context;
}
resetContentSize(contentSize) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.resetContentSize(contentSize);
}
getHomeZoom() { return this.ch.getHomeZoom(); }
getHomeBounds() { return this.ch.getHomeBounds(); }
getHomeBoundsNoRotate() { return this.ch.getHomeBoundsNoRotate(); }
getMinZoom() { return this.ch.getMinZoom(); }
getMaxZoom() { return this.ch.getMaxZoom(); }
getAspectRatio() { return this.ch.getAspectRatio(); }
getContainerSize() { return this.ch.getContainerSize(); }
getMargins() { return this.ch.getMargins(); }
setMargins(margins) { this.ch.setMargins(margins); }
getBounds(current) { return this.ch.getBounds(current); }
getBoundsNoRotate(current) { return this.ch.getBoundsNoRotate(current); }
getBoundsWithMargins(current) { return this.ch.getBoundsWithMargins(current); }
getBoundsNoRotateWithMargins(current) { return this.ch.getBoundsNoRotateWithMargins(current); }
getCenter(current) { return this.ch.getCenter(current); }
getZoom(current) { return this.ch.getZoom(current); }
getConstrainedBounds(current) { return this.ch.getConstrainedBounds(current); }
getRotation() { return this.ch.getRotation(); }
goHome(immediately) {
this.p.goHome(immediately);
return this.ch.goHome(immediately);
}
applyConstraints(immediately) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.applyConstraints(immediately);
}
ensureVisible(immediately) {
return this.applyConstraints(immediately);
}
fitBounds(bounds, immediately) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.fitBounds(bounds, immediately);
}
fitBoundsWithConstraints(bounds, immediately) {
return this.ch.fitBoundsWithConstraints(bounds, immediately);
}
fitVertically(immediately) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.fitVertically(immediately);
}
fitHorizontally(immediately) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.fitHorizontally(immediately);
}
panBy( delta, immediately ) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.panBy(delta, immediately);
}
panTo( center, immediately ) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.panTo(center, immediately);
}
zoomBy(factor, refPoint, immediately) {
this.p.zoomBy(factor, refPoint, immediately);
return this.ch.zoomBy(factor, refPoint, immediately);
}
zoomTo(zoom, refPoint, immediately) {
this.p.zoomTo(zoom, refPoint, immediately);
return this.ch.zoomTo(zoom, refPoint, immediately);
}
setRotation(degrees) {
this.p.setRotation(degrees);
return this.ch.setRotation(degrees);
}
resize( newContainerSize, maintain ) {
OpenSeadragon.Tools._syncViewports(this.ch, this.p);
return this.ch.resize(newContainerSize, maintain);
}
update() {
this.p.update();
return this.ch.update();
}
deltaPixelsFromPointsNoRotate(deltaPoints, current) {
return this.ch.deltaPixelsFromPointsNoRotate(deltaPoints, current);
}
deltaPixelsFromPoints(deltaPoints, current) {
return this.ch.deltaPixelsFromPointsNoRotate(deltaPoints, current);
}
deltaPointsFromPixelsNoRotate(deltaPixels, current) {
return this.ch.deltaPixelsFromPointsNoRotate(deltaPixels, current);
}
deltaPointsFromPixels(deltaPixels, current) {
return this.ch.deltaPointsFromPixels(deltaPixels, current);
}
pixelFromPointNoRotate(point, current) {
return this.ch.pixelFromPointNoRotate(point, current);
}
pixelFromPoint(point, current) { return this.ch.pixelFromPoint(point, current); }
pointFromPixelNoRotate(pixel, current) {
return this.ch.pointFromPixelNoRotate(pixel, current);
}
pointFromPixel(pixel, current) {
return this.ch.pointFromPixel(pixel, current);
}
viewportToImageCoordinates(viewerX, viewerY) {
return this.ch.viewportToImageCoordinates(viewerX, viewerY);
}
imageToViewportCoordinates(imageX, imageY) {
return this.ch.imageToViewportCoordinates(imageX, imageY);
}
imageToViewportRectangle(imageX, imageY, pixelWidth, pixelHeight) {
return this.ch.imageToViewportRectangle(imageX, imageY, pixelWidth, pixelHeight);
}
viewportToImageRectangle(viewerX, viewerY, pointWidth, pointHeight) {
return this.ch.viewportToImageRectangle(viewerX, viewerY, pointWidth, pointHeight);
}
viewerElementToImageCoordinates( pixel ) {
return this.ch.viewerElementToImageCoordinates(pixel);
}
imageToViewerElementCoordinates( pixel ) {
return this.ch.imageToViewerElementCoordinates(pixel);
}
windowToImageCoordinates(pixel) {
return this.ch.windowToImageCoordinates(pixel);
}
imageToWindowCoordinates(pixel) {
return this.ch.imageToWindowCoordinates(pixel);
}
viewerElementToViewportCoordinates( pixel ) {
return this.ch.viewerElementToViewportCoordinates(pixel);
}
viewportToViewerElementCoordinates( point ) {
return this.ch.viewportToViewerElementCoordinates(point);
}
viewerElementToViewportRectangle(rectangle) {
return this.ch.viewerElementToViewportRectangle(rectangle);
}
viewportToViewerElementRectangle(rectangle) {
return this.ch.viewportToViewerElementRectangle(rectangle);
}
windowToViewportCoordinates(pixel) {
return this.ch.windowToViewportCoordinates(pixel);
}
viewportToWindowCoordinates(point) {
return this.ch.viewportToWindowCoordinates(point);
}
viewportToImageZoom(viewportZoom) {
return this.ch.viewportToImageZoom(viewportZoom);
}
imageToViewportZoom(imageZoom) {
return this.ch.imageToViewportZoom(imageZoom);
}
toggleFlip() {
this.p.toggleFlip();
return this.ch.toggleFlip();
}
getFlip() {
return this.ch.getFlip();
}
setFlip( state ) {
this.p.setFlip(state);
return this.ch.setFlip(state);
}
};