Fix docs, commit before upstream merge.

This commit is contained in:
Aiosa 2024-03-03 14:50:01 +01:00
parent 63f0adbc15
commit 360f0d6796
7 changed files with 107 additions and 54 deletions

View File

@ -48,7 +48,7 @@
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests. * @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
* @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads * @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * @param {String} [options.postData] - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null * see TileSource::getTilePostData) or null
* @param {Function} [options.callback] - Called once image has been downloaded. * @param {Function} [options.callback] - Called once image has been downloaded.
* @param {Function} [options.abort] - Called when this image job is aborted. * @param {Function} [options.abort] - Called when this image job is aborted.
* @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete. * @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.
@ -193,7 +193,7 @@ $.ImageLoader.prototype = {
* @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX. * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.
* @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads * @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads
* @param {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form, * @param {String} [options.postData] - POST parameters (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null * see TileSource::getTilePostData) or null
* @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX * @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX
* requests. * requests.
* @param {Function} [options.callback] - Called once image has been downloaded. * @param {Function} [options.callback] - Called once image has been downloaded.

View File

@ -2383,7 +2383,7 @@ function OpenSeadragon( options ){
* @param {Object} options.headers - headers to add to the AJAX request * @param {Object} options.headers - headers to add to the AJAX request
* @param {String} options.responseType - the response type of the AJAX request * @param {String} options.responseType - the response type of the AJAX request
* @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * @param {String} options.postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData), GET method used if null * see TileSource::getTilePostData), GET method used if null
* @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials * @param {Boolean} [options.withCredentials=false] - whether to set the XHR's withCredentials
* @throws {Error} * @throws {Error}
* @returns {XMLHttpRequest} * @returns {XMLHttpRequest}
@ -2697,7 +2697,7 @@ function OpenSeadragon( options ){
//@private, runs tile update event //@private, runs tile update event
invalidateTile: function(tile, image, tStamp, viewer, i = -1) { invalidateTile: function(tile, image, tStamp, viewer, i = -1) {
console.log(i, "tile: process", tile); //console.log(i, "tile: process", tile);
//todo consider also ability to cut execution of ongoing event if outdated by providing comparison timestamp //todo consider also ability to cut execution of ongoing event if outdated by providing comparison timestamp
viewer.raiseEventAwaiting('tile-needs-update', { viewer.raiseEventAwaiting('tile-needs-update', {

View File

@ -53,7 +53,7 @@
* drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing * drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing
* with HTML the entire tile is always used. * with HTML the entire tile is always used.
* @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * @param {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null * see TileSource::getTilePostData) or null
* @param {String} cacheKey key to act as a tile cache, must be unique for tiles with unique image data * @param {String} cacheKey key to act as a tile cache, must be unique for tiles with unique image data
*/ */
$.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData, cacheKey) { $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, ajaxHeaders, sourceBounds, postData, cacheKey) {
@ -112,7 +112,7 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* Post parameters for this tile. For example, it can be an URL-encoded string * Post parameters for this tile. For example, it can be an URL-encoded string
* in k1=v1&k2=v2... format, or a JSON, or a FormData instance... or null if no POST request used * in k1=v1&k2=v2... format, or a JSON, or a FormData instance... or null if no POST request used
* @member {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * @member {String} postData HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null * see TileSource::getTilePostData) or null
* @memberof OpenSeadragon.Tile# * @memberof OpenSeadragon.Tile#
*/ */
this.postData = postData; this.postData = postData;
@ -300,14 +300,15 @@ $.Tile.prototype = {
return this._cKey; return this._cKey;
}, },
set cacheKey(value) { set cacheKey(value) {
if (this._cKey !== value) { if (value === this.cacheKey) {
let ref = this._caches[this._cKey]; return;
if (ref) {
// make sure we free drawer internal cache
ref.destroyInternalCache();
}
this._cKey = value;
} }
const cache = this.getCache(value);
if (!cache) {
// It's better to first set cache, then change the key to existing one. Warn if otherwise.
$.console.warn("[Tile.cacheKey] should not be set manually. Use addCache() with setAsMain=true.");
}
this._updateMainCacheKey(value);
}, },
/** /**
@ -431,11 +432,19 @@ $.Tile.prototype = {
$.console.error("[Tile.cacheImageRecord] property has been deprecated. Use Tile::addCache."); $.console.error("[Tile.cacheImageRecord] property has been deprecated. Use Tile::addCache.");
const cache = this._caches[this.cacheKey]; const cache = this._caches[this.cacheKey];
if (!value) { if (cache) {
this.removeCache(this.cacheKey); this.removeCache(this.cacheKey);
} else { }
const _this = this;
cache.await().then(x => _this.addCache(this.cacheKey, x, cache.type, false)); if (value) {
// Note: the value's data is probably not preserved - if a cacheKey cache exists, it will ignore
// data - it would have to call setData(...)
// TODO: call setData() ?
if (value.loaded) {
this.addCache(this.cacheKey, value.data, value.type, true, false);
} else {
value.await().then(x => this.addCache(this.cacheKey, x, value.type, true, false));
}
} }
}, },
@ -492,11 +501,8 @@ $.Tile.prototype = {
if (preserveOriginalData && this.cacheKey === this.originalCacheKey) { if (preserveOriginalData && this.cacheKey === this.originalCacheKey) {
//caches equality means we have only one cache: //caches equality means we have only one cache:
// change current pointer to a new cache and create it: new tiles will // create new cache record with main cache key changed to 'mod'
// not arrive at this data, but at originalCacheKey state return this.addCache("mod://" + this.originalCacheKey, value, type, true)._promise;
// todo setting cache key makes the notification trigger ensure we do not do unnecessary stuff
this.cacheKey = "mod://" + this.originalCacheKey;
return this.addCache(this.cacheKey, value, type)._promise;
} }
//else overwrite cache //else overwrite cache
const cache = this.getCache(this.cacheKey); const cache = this.getCache(this.cacheKey);
@ -512,7 +518,7 @@ $.Tile.prototype = {
* @param {string} [key=this.cacheKey] cache key to read that belongs to this tile * @param {string} [key=this.cacheKey] cache key to read that belongs to this tile
* @return {OpenSeadragon.CacheRecord} * @return {OpenSeadragon.CacheRecord}
*/ */
getCache: function(key = this.cacheKey) { getCache: function(key = this._cKey) {
const cache = this._caches[key]; const cache = this._caches[key];
if (cache) { if (cache) {
cache.withTileReference(this); cache.withTileReference(this);
@ -526,20 +532,21 @@ $.Tile.prototype = {
* value and extend it with some another unique content, by default overrides the existing * value and extend it with some another unique content, by default overrides the existing
* main cache used for drawing, if not existing. * main cache used for drawing, if not existing.
* @param {*} data data to cache - this data will be IGNORED if cache already exists! * @param {*} data data to cache - this data will be IGNORED if cache already exists!
* @param {?string} type data type, will be guessed if not provided * @param {string} [type=undefined] data type, will be guessed if not provided
* @param {boolean} [setAsMain=false] if true, the key will be set as the tile.cacheKey
* @param [_safely=true] private * @param [_safely=true] private
* @returns {OpenSeadragon.CacheRecord|null} - The cache record the tile was attached to. * @returns {OpenSeadragon.CacheRecord|null} - The cache record the tile was attached to.
*/ */
addCache: function(key, data, type = undefined, _safely = true) { addCache: function(key, data, type = undefined, setAsMain = false, _safely = true) {
if (!this.tiledImage) { if (!this.tiledImage) {
return null; //async can access outside its lifetime return null; //async can access outside its lifetime
} }
if (!type) { if (!type) {
if (!this.tiledImage.__typeWarningReported) { if (!this.__typeWarningReported) {
$.console.warn(this, "[Tile.addCache] called without type specification. " + $.console.warn(this, "[Tile.addCache] called without type specification. " +
"Automated deduction is potentially unsafe: prefer specification of data type explicitly."); "Automated deduction is potentially unsafe: prefer specification of data type explicitly.");
this.tiledImage.__typeWarningReported = true; this.__typeWarningReported = true;
} }
type = $.convertor.guessType(data); type = $.convertor.guessType(data);
} }
@ -568,9 +575,31 @@ $.Tile.prototype = {
} }
this._caches[key] = cachedItem; this._caches[key] = cachedItem;
} }
// Update cache key if differs and main requested
if (!writesToRenderingCache && setAsMain) {
this._updateMainCacheKey(key);
}
return cachedItem; return cachedItem;
}, },
/**
* Sets the main cache key for this tile and
* performs necessary updates
* @param value
* @private
*/
_updateMainCacheKey: function(value) {
let ref = this._caches[this._cKey];
if (ref) {
// make sure we free drawer internal cache
ref.destroyInternalCache();
}
this._cKey = value;
// when key changes the image probably needs re-render
this.tiledImage.redraw();
},
/** /**
* Get the number of caches available to this tile * Get the number of caches available to this tile
* @returns {number} number of caches * @returns {number} number of caches
@ -585,11 +614,32 @@ $.Tile.prototype = {
* @param {boolean} [freeIfUnused=true] set to false if zombie should be created * @param {boolean} [freeIfUnused=true] set to false if zombie should be created
*/ */
removeCache: function(key, freeIfUnused = true) { removeCache: function(key, freeIfUnused = true) {
if (this.cacheKey === key) { if (!this._caches[key]) {
if (this.cacheKey !== this.originalCacheKey) { // try to erase anyway in case the cache got stuck in memory
this.cacheKey = this.originalCacheKey; this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused);
return;
}
const currentMainKey = this.cacheKey,
originalDataKey = this.originalCacheKey,
sameBuiltinKeys = currentMainKey === originalDataKey;
if (!sameBuiltinKeys && originalDataKey === key) {
$.console.warn("[Tile.removeCache] original data must not be manually deleted: other parts of the code might rely on it!",
"If you want the tile not to preserve the original data, toggle of data perseverance in tile.setData().");
return;
}
if (currentMainKey === key) {
if (!sameBuiltinKeys && this._caches[originalDataKey]) {
// if we have original data let's revert back
// TODO consider calling drawer.getDataToDraw(...)
// or even better, first ensure the data is compatible and then update...?
this._updateMainCacheKey(originalDataKey);
} else { } else {
$.console.warn("[Tile.removeCache] trying to remove the only cache that is used to draw the tile!"); $.console.warn("[Tile.removeCache] trying to remove the only cache that can be used to draw the tile!",
"If you want to remove the main cache, first set different cache as main with tile.addCache()");
return;
} }
} }
if (this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused)) { if (this.tiledImage._tileCache.unloadCacheForTile(this, key, freeIfUnused)) {

View File

@ -106,7 +106,7 @@
*/ */
setDataAs(data, type) { setDataAs(data, type) {
//allow set data with destroyed state, destroys the data if necessary //allow set data with destroyed state, destroys the data if necessary
$.console.assert(data !== undefined, "[CacheRecord.setDataAs] needs valid data to set!"); $.console.assert(data !== undefined && data !== null, "[CacheRecord.setDataAs] needs valid data to set!");
if (this._conversionJobQueue) { if (this._conversionJobQueue) {
//delay saving if ongiong conversion, these were registered first //delay saving if ongiong conversion, these were registered first
let resolver = null; let resolver = null;
@ -412,7 +412,7 @@
_triggerNeedsDraw() { _triggerNeedsDraw() {
for (let tile of this._tiles) { for (let tile of this._tiles) {
tile.tiledImage._needsDraw = true; tile.tiledImage.redraw();
} }
} }

View File

@ -2095,14 +2095,9 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
* @param {?Boolean} [withEvent=true] do not trigger event if true * @param {?Boolean} [withEvent=true] do not trigger event if true
*/ */
_setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) { _setTileLoaded: function(tile, data, cutoff, tileRequest, dataType, withEvent = true) {
const originalDelete = tile.unload;
tile.unload = (function () {
throw `Cannot unload tile while being loaded!`;
});
tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back tile.tiledImage = this; //unloaded with tile.unload(), so we need to set it back
// does nothing if tile.cacheKey already present // does nothing if tile.cacheKey already present
tile.addCache(tile.cacheKey, data, dataType, false); tile.addCache(tile.cacheKey, data, dataType, false, false);
let resolver = null, let resolver = null,
increment = 0, increment = 0,
@ -2135,13 +2130,11 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
} }
return cacheRef; return cacheRef;
}).then(_ => { }).then(_ => {
tile.unload = originalDelete;
tile.loading = false; tile.loading = false;
tile.loaded = true; tile.loaded = true;
resolver(tile); resolver(tile);
}); });
} else { } else {
tile.unload = originalDelete;
tile.loading = false; tile.loading = false;
tile.loaded = true; tile.loaded = true;
resolver(tile); resolver(tile);

View File

@ -55,7 +55,8 @@
* @param {Object} options * @param {Object} options
* You can either specify a URL, or literally define the TileSource (by specifying * You can either specify a URL, or literally define the TileSource (by specifying
* width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former, * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,
* the extending class is expected to implement 'getImageInfo' and 'configure'. * the extending class is expected to implement 'supports' and 'configure'.
* Note that _in this case, the child class of getImageInfo() is ignored!_
* For the latter, the construction is assumed to occur through * For the latter, the construction is assumed to occur through
* the extending classes implementation of 'configure'. * the extending classes implementation of 'configure'.
* @param {String} [options.url] * @param {String} [options.url]
@ -72,6 +73,7 @@
* @param {Boolean} [options.splitHashDataForPost] * @param {Boolean} [options.splitHashDataForPost]
* First occurrence of '#' in the options.url is used to split URL * First occurrence of '#' in the options.url is used to split URL
* and the latter part is treated as POST data (applies to getImageInfo(...)) * and the latter part is treated as POST data (applies to getImageInfo(...))
* Does not work if getImageInfo() is overridden and used (see the options description)
* @param {Number} [options.width] * @param {Number} [options.width]
* Width of the source image at max resolution in pixels. * Width of the source image at max resolution in pixels.
* @param {Number} [options.height] * @param {Number} [options.height]
@ -176,6 +178,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
* @memberof OpenSeadragon.TileSource# * @memberof OpenSeadragon.TileSource#
*/ */
// TODO potentially buggy behavior: what if .url is used by child class before it calls super constructor?
// this can happen if old JS class definition is used
if( 'string' === $.type( arguments[ 0 ] ) ){ if( 'string' === $.type( arguments[ 0 ] ) ){
this.url = arguments[0]; this.url = arguments[0];
} }
@ -431,6 +435,12 @@ $.TileSource.prototype = {
/** /**
* Responsible for retrieving, and caching the * Responsible for retrieving, and caching the
* image metadata pertinent to this TileSources implementation. * image metadata pertinent to this TileSources implementation.
* There are three scenarios of opening a tile source:
* 1) if it is a string parseable as XML or JSON, the string is converted to an object
* 2) if it is a string, then
* internally, this method
* else
*
* @function * @function
* @param {String} url * @param {String} url
* @throws {Error} * @throws {Error}
@ -560,7 +570,7 @@ $.TileSource.prototype = {
* @property {String} message * @property {String} message
* @property {String} source * @property {String} source
* @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form, * @property {String} postData - HTTP POST data (usually but not necessarily in k=v&k2=v2... form,
* see TileSource::getPostData) or null * see TileSource::getTilePostData) or null
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
_this.raiseEvent( 'open-failed', { _this.raiseEvent( 'open-failed', {
@ -777,7 +787,7 @@ $.TileSource.prototype = {
* @param {Boolean} [context.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests. * @param {Boolean} [context.ajaxWithCredentials] - Whether to set withCredentials on AJAX requests.
* @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads * @param {String} [context.crossOriginPolicy] - CORS policy to use for downloads
* @param {?String|?Object} [context.postData] - HTTP POST data (usually but not necessarily * @param {?String|?Object} [context.postData] - HTTP POST data (usually but not necessarily
* in k=v&k2=v2... form, see TileSource::getPostData) or null * in k=v&k2=v2... form, see TileSource::getTilePostData) or null
* @param {*} [context.userData] - Empty object to attach your own data and helper variables to. * @param {*} [context.userData] - Empty object to attach your own data and helper variables to.
* @param {Function} [context.finish] - Should be called unless abort() was executed upon successful * @param {Function} [context.finish] - Should be called unless abort() was executed upon successful
* data retrieval. * data retrieval.

View File

@ -252,15 +252,15 @@
//load data //load data
const tile00 = createFakeTile('foo.jpg', fakeTiledImage0); const tile00 = createFakeTile('foo.jpg', fakeTiledImage0);
tile00.addCache(tile00.cacheKey, 0, T_A, false); tile00.addCache(tile00.cacheKey, 0, T_A, false, false);
const tile01 = createFakeTile('foo2.jpg', fakeTiledImage0); const tile01 = createFakeTile('foo2.jpg', fakeTiledImage0);
tile01.addCache(tile01.cacheKey, 0, T_B, false); tile01.addCache(tile01.cacheKey, 0, T_B, false, false);
const tile10 = createFakeTile('foo3.jpg', fakeTiledImage1); const tile10 = createFakeTile('foo3.jpg', fakeTiledImage1);
tile10.addCache(tile10.cacheKey, 0, T_C, false); tile10.addCache(tile10.cacheKey, 0, T_C, false, false);
const tile11 = createFakeTile('foo3.jpg', fakeTiledImage1); const tile11 = createFakeTile('foo3.jpg', fakeTiledImage1);
tile11.addCache(tile11.cacheKey, 0, T_C, false); tile11.addCache(tile11.cacheKey, 0, T_C, false, false);
const tile12 = createFakeTile('foo.jpg', fakeTiledImage1); const tile12 = createFakeTile('foo.jpg', fakeTiledImage1);
tile12.addCache(tile12.cacheKey, 0, T_A, false); tile12.addCache(tile12.cacheKey, 0, T_A, false, false);
const collideGetSet = async (tile, type) => { const collideGetSet = async (tile, type) => {
const value = await tile.getData(type, false); const value = await tile.getData(type, false);
@ -446,15 +446,15 @@
//load data //load data
const tile00 = createFakeTile('foo.jpg', fakeTiledImage0); const tile00 = createFakeTile('foo.jpg', fakeTiledImage0);
tile00.addCache(tile00.cacheKey, 0, T_A, false); tile00.addCache(tile00.cacheKey, 0, T_A, false, false);
const tile01 = createFakeTile('foo2.jpg', fakeTiledImage0); const tile01 = createFakeTile('foo2.jpg', fakeTiledImage0);
tile01.addCache(tile01.cacheKey, 0, T_B, false); tile01.addCache(tile01.cacheKey, 0, T_B, false, false);
const tile10 = createFakeTile('foo3.jpg', fakeTiledImage1); const tile10 = createFakeTile('foo3.jpg', fakeTiledImage1);
tile10.addCache(tile10.cacheKey, 0, T_C, false); tile10.addCache(tile10.cacheKey, 0, T_C, false, false);
const tile11 = createFakeTile('foo3.jpg', fakeTiledImage1); const tile11 = createFakeTile('foo3.jpg', fakeTiledImage1);
tile11.addCache(tile11.cacheKey, 0, T_C, false); tile11.addCache(tile11.cacheKey, 0, T_C, false, false);
const tile12 = createFakeTile('foo.jpg', fakeTiledImage1); const tile12 = createFakeTile('foo.jpg', fakeTiledImage1);
tile12.addCache(tile12.cacheKey, 0, T_A, false); tile12.addCache(tile12.cacheKey, 0, T_A, false, false);
//test set/get data in async env //test set/get data in async env
(async function() { (async function() {