(function( $ ) {
$.ExtendedDziTileSource = class extends $.TileSource {
constructor(options) {
super(options);
var i,
rect,
level;
this._levelRects = {};
this.tilesUrl = options.tilesUrl;
this.fileFormat = options.fileFormat;
this.displayRects = options.displayRects;
if (this.displayRects) {
for (i = this.displayRects.length - 1; i >= 0; i--) {
rect = this.displayRects[i];
for (level = rect.minLevel; level <= rect.maxLevel; level++) {
if (!this._levelRects[level]) {
this._levelRects[level] = [];
}
this._levelRects[level].push(rect);
}
}
}
if (!this.fileFormat) this.fileFormat = ".jpg";
}
supports( data, url ){
var ns;
if ( data.ImageArray ) {
ns = data.ImageArray.xmlns;
} else if ( data.documentElement ) {
if ("ImageArray" == data.documentElement.localName || "ImageArray" == data.documentElement.tagName) {
ns = data.documentElement.namespaceURI;
}
}
ns = ns || "";
return ns.indexOf('rationai.fi.muni.cz/deepzoom/images') !== -1;
}
configure( data, url, postData ){
var options = $.isPlainObject(data) ? configureFromObject(this, data) : configureFromXML(this, data);
const targetUrl = typeof postData === "string" ? postData : url;
if (targetUrl.endsWith("jpg.dzi") || targetUrl.endsWith("png.dzi") || targetUrl.endsWith("jpeg.dzi")) {
options.maxLevel = options.minLevel = 0;
}
if (postData) {
options.postData = postData.replace(/([^\/]+?)(\.(dzi|xml|js)?(\?[^\/]*)?)?\/?$/, '$1_files/');
} else if (url) {
url = url.replace(
/([^\/]+?)(\.(dzi|xml|js)?(\?[^\/]*)?)?\/?$/, '$1_files/');
}
if (url && !options.tilesUrl) {
options.tilesUrl = url;
if (url.search(/\.(dzi|xml|js)\?/) != -1) {
options.queryParams = url.match(/\?.*/);
}else{
options.queryParams = '';
}
}
return options;
}
getTileUrl( level, x, y ) {
return this.getUrl(level, x, y);
}
getUrl( level, x, y, tiles=this.tilesUrl ) {
return this.postData ? `${tiles}${this.queryParams}`
: `${tiles}${level}/${x}_${y}.${this.fileFormat}${this.queryParams}`;
}
getTileAjaxHeaders( level, x, y ) {
return {'Content-type': 'application/x-www-form-urlencoded'};
}
getTilePostData(level, x, y) {
return this.getPostData(level, x, y, this.postData);
}
getPostData(level, x, y, data) {
return data ? `${data}${level}/${x}_${y}.${this.fileFormat}` : null;
}
getMetadata() {
return this.ImageArray?.[0] || {};
}
getDisplayMetadata() {
const fields = [];
if (this.tilesUrl) fields.push({ label: "Tiles URL", value: String(this.tilesUrl) });
if (this.fileFormat) fields.push({ label: "Format", value: String(this.fileFormat) });
if (this.width != null && this.height != null) {
fields.push({ label: "Dimensions", value: `${this.width} × ${this.height} px` });
}
if (Number.isFinite(this.maxLevel)) {
fields.push({ label: "Pyramid levels", value: this.maxLevel + 1 });
}
const meta = this.getMetadata();
if (meta?.error) return [{ title: "Slide unavailable", description: String(meta.error) }];
return fields.length ? [{ title: "DZI slide", fields }] : [];
}
setFormat(format) {
this.fileFormat = format;
let blackImage = (context, resolve, reject) => {
const canvas = document.createElement('canvas');
canvas.width = context.getTileWidth();
canvas.height = context.getTileHeight();
const ctx = canvas.getContext('2d');
ctx.fillRect(0, 0, canvas.width, canvas.height);
const img = new Image(canvas.width, canvas.height);
img.onload = () => {
blackImage = (context, ready, _) => ready(img);
resolve(img);
};
img.onerror = img.onabort = reject;
img.src = canvas.toDataURL();
};
if (format === "zip") {
this.__cached_downloadTileStart = this.downloadTileStart;
this.downloadTileStart = function(context) {
const abort = context.fail.bind(context, "Load aborted.");
if (!context.loadWithAjax) {
abort("DeepZoomExt protocol with ZIP does not support fetching data without ajax!");
}
var dataStore = context.userData;
const _this = this;
dataStore.request = OpenSeadragon.makeAjaxRequest({
url: context.src,
withCredentials: context.ajaxWithCredentials,
headers: context.ajaxHeaders,
responseType: "arraybuffer",
postData: context.postData,
success: async function(request) {
var blb;
try {
blb = new window.Blob([request.response]);
} catch (e) {
var BlobBuilder = (
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder
);
if (e.name === 'TypeError' && BlobBuilder) {
var bb = new BlobBuilder();
bb.append(request.response);
blb = bb.getBlob();
}
}
if (blb.size === 0) {
return abort("Empty image response.");
}
const {zip, entries} = await unzipit.unzipRaw(blb);
Promise.all(
Object.entries(entries).map(([name, entry]) => {
return new Promise((resolve, reject) => {
entry.blob().then(blob => {
if (blob.size > 0) {
const img = new Image();
const objUrl = URL.createObjectURL(blob);
img.onload = () => {
resolve(img);
URL.revokeObjectURL(objUrl);
};
img.onerror = img.onabort = reject;
img.src = objUrl;
} else blackImage(_this, resolve, reject);
});
});
})
).then(result =>
context.finish(result, dataStore.request, "image")
).catch(
abort
);
},
error(request) {
abort("Image load aborted - XHR error");
}
});
}
this.__cached_downloadTileAbort = this.downloadTileAbort;
this.downloadTileAbort = OpenSeadragon.TileSource.prototype.downloadTileAbort;
} else if (this.__cached_downloadTileStart) {
this.downloadTileStart = this.__cached_downloadTileStart;
this.downloadTileAbort = this.__cached_downloadTileAbort;
}
}
getTileHashKey(level, x, y, url, ajaxHeaders, postData) {
return `${x}_${y}/${level}/${this.postData}`;
}
getTileCacheDataAsContext2D(cacheObject) {
if (!cacheObject._renderedContext) {
if (Array.isArray(cacheObject._data)) {
cacheObject._data = cacheObject._data[0];
} else if (Array.isArray(cacheObject.data)) {
cacheObject.data = cacheObject.data[0];
}
}
return super.getTileCacheDataAsContext2D(cacheObject);
}
tileExists( level, x, y ) {
let rects = this._levelRects[ level ],
rect,
scale,
xMin,
yMin,
xMax,
yMax,
i;
if ((this.minLevel && level < this.minLevel) || (this.maxLevel && level > this.maxLevel)) {
return false;
}
if ( !rects || !rects.length ) {
return true;
}
for ( i = rects.length - 1; i >= 0; i-- ) {
rect = rects[ i ];
if ( level < rect.minLevel || level > rect.maxLevel ) {
continue;
}
scale = this.getLevelScale( level );
xMin = rect.x * scale;
yMin = rect.y * scale;
xMax = xMin + rect.width * scale;
yMax = yMin + rect.height * scale;
xMin = Math.floor( xMin / this._tileWidth );
yMin = Math.floor( yMin / this._tileWidth );
xMax = Math.ceil( xMax / this._tileWidth );
yMax = Math.ceil( yMax / this._tileWidth );
if ( xMin <= x && x < xMax && yMin <= y && y < yMax ) {
return true;
}
}
return false;
}
}
function configureFromXML( tileSource, xmlDoc ){
if ( !xmlDoc || !xmlDoc.documentElement ) {
throw new Error( $.getString( "Errors.Xml" ) );
}
var imagesArray = xmlDoc.documentElement,
root = null,
rootName = imagesArray.localName || imagesArray.tagName,
ns = xmlDoc.documentElement.namespaceURI,
configuration = {ImageArray: []},
displayRects = [],
dispRectNodes,
dispRectNode,
rectNode,
sizeNode,
i;
if (imagesArray.childNodes.length < 1) throw new Error( "No images defined. There are zero images to display." );
if ( rootName == "ImageArray" ) {
try {
let selectedNode = 0,
maxWidth = Infinity,
maxHeight = Infinity;
for (let child = 0; child < imagesArray.childNodes.length; child++) {
root = imagesArray.childNodes[child];
sizeNode = root.getElementsByTagName("Size" )[ 0 ];
if (sizeNode === undefined) {
sizeNode = root.getElementsByTagNameNS(ns, "Size" )[ 0 ];
}
let width = parseInt( sizeNode.getAttribute( "Width" ), 10 );
let height = parseInt( sizeNode.getAttribute( "Height" ), 10 );
if ( !$.imageFormatSupported( root.getAttribute( "Format" ) ) ) {
throw new Error(
$.getString( "Errors.ImageFormat", root.getAttribute( "Format" ).toUpperCase() )
);
}
configuration.ImageArray.push({
xmlns: "http://rationai.fi.muni.cz/deepzoom/images",
Url: root.getAttribute( "Url" ),
Format: root.getAttribute( "Format" ),
DisplayRect: null,
Overlap: parseInt( root.getAttribute( "Overlap" ), 10 ),
TileSize: parseInt( root.getAttribute( "TileSize" ), 10 ),
Size: {
Height: height,
Width: width
}
});
}
root = imagesArray.childNodes[selectedNode];
dispRectNodes = root.getElementsByTagName("DisplayRect");
if (dispRectNodes === undefined) {
dispRectNodes = root.getElementsByTagNameNS(ns, "DisplayRect")[ 0 ];
}
for ( i = 0; i < dispRectNodes.length; i++ ) {
dispRectNode = dispRectNodes[ i ];
rectNode = dispRectNode.getElementsByTagName("Rect")[ 0 ];
if (rectNode === undefined) {
rectNode = dispRectNode.getElementsByTagNameNS(ns, "Rect")[ 0 ];
}
displayRects.push({
Rect: {
X: parseInt( rectNode.getAttribute( "X" ), 10 ),
Y: parseInt( rectNode.getAttribute( "Y" ), 10 ),
Width: parseInt( rectNode.getAttribute( "Width" ), 10 ),
Height: parseInt( rectNode.getAttribute( "Height" ), 10 ),
MinLevel: parseInt( dispRectNode.getAttribute( "MinLevel" ), 10 ),
MaxLevel: parseInt( dispRectNode.getAttribute( "MaxLevel" ), 10 )
}
});
}
if( displayRects.length ){
configuration.DisplayRect = displayRects;
}
return configureFromObject( tileSource, configuration );
} 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" ) {
root = imagesArray.childNodes[0];
let messageNode = root.getElementsByTagName("Message")[0];
let message = messageNode.firstChild.nodeValue;
throw new Error(message);
}
throw new Error( $.getString( "Errors.Dzi" ) );
}
function configureFromObject( tileSource, configuration ){
var firstImage = configuration.ImageArray[0],
fileFormat = firstImage.Format,
dispRectData = configuration.DisplayRect || [],
width = Infinity,
height = Infinity,
tileSize = undefined,
tileOverlap = undefined,
displayRects = [],
rectData,
i;
for (let i = 0; i < configuration.ImageArray.length; i++) {
let image = configuration.ImageArray[i],
imageWidth = parseInt( image.Size.Width, 10 ),
imageHeight = parseInt( image.Size.Height, 10 ),
imageTileSize = parseInt( image.TileSize, 10 ),
imageTileOverlap = parseInt( image.Overlap, 10 );
if (imageWidth < 1 || imageHeight < 1) {
image.error = "Missing image data.";
continue;
}
if (tileSize === undefined) {
tileSize = imageTileSize;
}
if (tileOverlap === undefined) {
tileOverlap = imageTileOverlap;
}
if (imageTileSize !== tileSize || imageTileOverlap !== tileOverlap) {
image.error = "Incompatible layer: the rendering might contain artifacts.";
}
if (imageWidth < width || imageHeight < height) {
width = imageWidth;
height = imageHeight;
}
}
for ( i = 0; i < dispRectData.length; i++ ) {
rectData = dispRectData[ i ].Rect;
displayRects.push( new $.DisplayRect(
parseInt( rectData.X, 10 ),
parseInt( rectData.Y, 10 ),
parseInt( rectData.Width, 10 ),
parseInt( rectData.Height, 10 ),
parseInt( rectData.MinLevel, 10 ),
parseInt( rectData.MaxLevel, 10 )
));
}
return $.extend(true, {
width: width,
height: height,
tileSize: tileSize,
tileOverlap: tileOverlap,
minLevel: null,
maxLevel: null,
fileFormat: fileFormat,
displayRects: displayRects
}, configuration );
}
})(OpenSeadragon);