diff --git a/omeroweb/webclient/static/webclient/javascript/ome.progress_overlay.js b/omeroweb/webclient/static/webclient/javascript/ome.progress_overlay.js
new file mode 100644
index 0000000000..2c05fa3301
--- /dev/null
+++ b/omeroweb/webclient/static/webclient/javascript/ome.progress_overlay.js
@@ -0,0 +1,60 @@
+/* globals OME, $ */
+
+/**
+ * Create an overlay that blocks the user from interacting with the UI,
+ * for example to prevent a long-running action from being interrupted
+ * or repeated before it is completed.
+ * @param promise When this promise resolves (successfully or otherwise) the overlay closes
+ * @param message The optional message to display (defaults to "Please wait")
+ * @param quiet Don't print timing to console
+ * @returns {*|jQuery} The jQuery element holding the dialog; no need to do anything with it
+ */
+OME.progress_overlay = function (promise, message, quiet) {
+ 'use strict';
+ const startTime = new Date().getTime();
+ const dialog = $('
' + (message || 'Please wait') + '
')
+ .appendTo(document.body)
+ .dialog({
+ modal: true,
+ autoOpen: true,
+ closeOnEscape: false,
+ draggable: false,
+ resizable: false,
+ classes: {
+ 'ui-dialog': 'ome-modal-progress',
+ }
+ });
+ promise.finally(() => {
+ dialog.dialog('destroy').remove();
+ if (!quiet) {
+ window.console.log('UI blocked for ' + (new Date().getTime() - startTime).toString() + 'ms');
+ }
+ });
+ return dialog;
+};
+
+// add styles
+(function () {
+ 'use strict';
+ const styleSheet = document.createElement("style");
+ styleSheet.innerText = `
+ .ome-modal-progress button {
+ display: none;
+ }
+
+ .ome-modal-progress .ui-dialog-content {
+ justify-content: center;
+ align-items: center;
+ font-size: larger;
+ display: flex;
+ }
+
+ .ome-modal-progress .spinner {
+ background-image: url("data:image/gif;base64,R0lGODlhEAAQAPIAAP///zxKW9HU2G95hjxKW4eQmqCnr6yyuSH5BAkKAAAAIf4aQ3JlYXRlZCB3aXRoIGFqYXhsb2FkLmluZm8AIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOw==");
+ width: 16px;
+ height: 16px;
+ display: inline-block;
+ }
+ `;
+ document.head.appendChild(styleSheet);
+})();
\ No newline at end of file
diff --git a/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js b/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js
index aa483cbb1b..744b09177e 100644
--- a/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js
+++ b/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js
@@ -381,6 +381,7 @@ OME.refreshThumbnails = function(options) {
spw_selector = "#image-" + options.imageId + ", #wellImages li[data-imageId='" + options.imageId + "']";
}
+ var promise = Promise.resolve();
// Try SPW data or Search data by directly updating thumb src...
var $thumbs = $(spw_selector + ", " + search_selector);
if ($thumbs.length > 0){
@@ -392,7 +393,7 @@ OME.refreshThumbnails = function(options) {
// filter out empty wells etc.
return img_id.length > 0;
}).get();
- OME.load_thumbnails(
+ promise = OME.load_thumbnails(
options.thumbnail_url,
iids, options.thumbnailsBatch,
options.defaultThumbnail
@@ -407,59 +408,68 @@ OME.refreshThumbnails = function(options) {
data = {'imageId': options.imageId};
}
var e = {'type': type};
- update_thumbnails_panel(e, data);
+ promise = update_thumbnails_panel(e, data);
}
// Update viewport via global variable
if (!options.ignorePreview && OME.preview_viewport && OME.preview_viewport.loadedImg.id) {
OME.preview_viewport.load(OME.preview_viewport.loadedImg.id);
}
+
+ return promise;
};
-OME.load_thumbnails = function(thumbnails_url, input, batch, dthumb) {
+OME.load_thumbnails = function (thumbnails_url, input, batch, dthumb) {
// load thumbnails in a batches
if (input.length > 0 && batch > 0) {
- var iids = input.slice(0 , batch)
+ var iids = input.slice(0, batch)
if (iids.length > 0) {
- $.ajax({
- type: "GET",
- url: thumbnails_url,
- data: $.param( { id: iids }, true),
- dataType: 'json',
- success: function(data){
- var invalid_thumbs = [];
- $.each(data, function(key, value) {
- if (value !== null) {
- // SPW Plate and WellImages
- $("img#image-"+key).attr("src", value);
- $("#wellImages li[data-imageId='" + key + "'] img").attr("src", value);
- $("#well_birds_eye img[data-imageid='" + key + "']").attr("src", value);
- // Search results
- $("#image_icon-" + key + " img").attr("src", value);
+ var promise = new Promise(function (resolve) {
+ $.ajax({
+ type: "GET",
+ url: thumbnails_url,
+ data: $.param({id: iids}, true),
+ dataType: 'json',
+ success: function (data) {
+ var invalid_thumbs = [];
+ $.each(data, function (key, value) {
+ if (value !== null) {
+ // SPW Plate and WellImages
+ $("img#image-" + key).attr("src", value);
+ $("#wellImages li[data-imageId='" + key + "'] img").attr("src", value);
+ $("#well_birds_eye img[data-imageid='" + key + "']").attr("src", value);
+ // Search results
+ $("#image_icon-" + key + " img").attr("src", value);
+ } else {
+ invalid_thumbs.push(key);
+ }
+ });
+ // If we got invalid thumbnails as a set and ALL failed, try re-loading 1 at a time
+ if (invalid_thumbs.length === iids.length && batch > 1) {
+ OME.load_thumbnails(thumbnails_url, invalid_thumbs, 1, dthumb).then(resolve);
} else {
- invalid_thumbs.push(key);
+ // If only some thumbs failed (or single thumb failed), show placeholder
+ if ((invalid_thumbs.length < iids.length) || (batch === 1 && invalid_thumbs.length === 1)) {
+ // If batch > 1 then we try loading again, otherwise we failed...
+ invalid_thumbs.forEach(function (key) {
+ $("img#image-" + key).attr("src", dthumb);
+ $("#wellImages li[data-imageId='" + key + "'] img").attr("src", dthumb);
+ $("#image_icon-" + key + " img").attr("src", dthumb);
+ });
+ }
+ resolve();
}
- });
- // If we got invalid thumbnails as a set and ALL failed, try re-loading 1 at a time
- if (invalid_thumbs.length === iids.length && batch > 1) {
- OME.load_thumbnails(thumbnails_url, invalid_thumbs, 1, dthumb);
- }
- // If only some thumbs failed (or single thumb failed), show placeholder
- if ((invalid_thumbs.length < iids.length) || (batch === 1 && invalid_thumbs.length === 1)) {
- // If batch > 1 then we try loading again, otherwise we failed...
- invalid_thumbs.forEach(function(key){
- $("img#image-"+key).attr("src", dthumb);
- $("#wellImages li[data-imageId='" + key + "'] img").attr("src", dthumb);
- $("#image_icon-" + key + " img").attr("src", dthumb);
- });
- }
- }
+ },
+ error: resolve,
+ });
});
input = input.slice(batch, input.length);
- OME.load_thumbnails(thumbnails_url, input, batch, dthumb);
+ return Promise.all([promise, OME.load_thumbnails(thumbnails_url, input, batch, dthumb)]);
}
}
-}
+ return Promise.resolve();
+};
+
OME.load_thumbnail = function(iid, thumbnails_url, callback) {
// load thumbnails in a batches
$.ajax({
@@ -1142,17 +1152,23 @@ OME.applyRenderingSettings = function(rdef_url, selected) {
function() {
var clicked_button_text = rdef_confirm_dialog.data("clicked_button");
if (clicked_button_text === "OK") {
- $.ajax({
- type: "POST",
- dataType: 'text',
- traditional: true,
- url: rdef_url,
- data: data,
- success: function(data){
- // update thumbnails
- OME.refreshThumbnails();
- }
- });
+ OME.progress_overlay(
+ new Promise((resolve) => {
+ $.ajax({
+ type: "POST",
+ dataType: 'text',
+ traditional: true,
+ url: rdef_url,
+ data: data,
+ success: function(data){
+ // update thumbnails
+ OME.refreshThumbnails().finally(() => resolve());
+ },
+ error: resolve,
+ });
+ }),
+ 'Applying rendering settings...'
+ );
}
},
"Change Rendering Settings?",
diff --git a/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html b/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html
index 5c1d9364f0..acbc41d81a 100644
--- a/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html
+++ b/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html
@@ -321,26 +321,28 @@
$("#rdef-save-all")
.attr('title', "Apply and Save settings to all images in " + pid)
.prop('disabled', false).removeClass("button-disabled")
- .on('click', function(){
- var $span = $('span', this),
- spanTxt = $span.text();
- $span.text("Saving...");
- var typeId = pid.split("-");
- var rdefQry = OME.preview_viewport.getQuery(true);
- // need to paste current settings to all images...
- var url = "{% url 'webgateway_copy_image_rdef_json' %}"+ "?" + rdefQry;
- data = {
- "toids": typeId[1],
- "to_type": typeId[0],
- "imageId": OME.preview_viewport.loadedImg.id // Need imageId for 'apply to all'
- }
- $.ajax({
- type: "POST",
- dataType: 'text',
- traditional: true,
- url: url,
- data: data,
- success: function(data){
+ .on('click', function () {
+ OME.progress_overlay(
+ new Promise((resolve) => {
+ var $span = $('span', this),
+ spanTxt = $span.text();
+ $span.text("Saving...");
+ var typeId = pid.split("-");
+ var rdefQry = OME.preview_viewport.getQuery(true);
+ // need to paste current settings to all images...
+ var url = "{% url 'webgateway_copy_image_rdef_json' %}" + "?" + rdefQry;
+ data = {
+ "toids": typeId[1],
+ "to_type": typeId[0],
+ "imageId": OME.preview_viewport.loadedImg.id // Need imageId for 'apply to all'
+ }
+ $.ajax({
+ type: "POST",
+ dataType: 'text',
+ traditional: true,
+ url: url,
+ data: data,
+ success: function (data) {
$span.text(spanTxt);
// update thumbnails
OME.refreshThumbnails({
@@ -348,10 +350,13 @@
'thumbnail_url': "{% url 'get_thumbnails_json' %}",
'defaultThumbnail': "{% static 'webgateway/img/image128.png' %}",
'thumbnailsBatch': 1
- });
+ }).finally(() => resolve());
updateMyRdef(OME.preview_viewport.getQuery());
- }
- });
+ },
+ error: resolve,
+ });
+ }),
+ 'Saving to all...');
});
}
{% endif %}
diff --git a/omeroweb/webclient/templates/webclient/base/base.html b/omeroweb/webclient/templates/webclient/base/base.html
index 10558b7df5..50f8258c29 100755
--- a/omeroweb/webclient/templates/webclient/base/base.html
+++ b/omeroweb/webclient/templates/webclient/base/base.html
@@ -44,6 +44,7 @@
+
diff --git a/omeroweb/webclient/templates/webclient/base/base_container.html b/omeroweb/webclient/templates/webclient/base/base_container.html
index 8cc6f88860..97c279454f 100644
--- a/omeroweb/webclient/templates/webclient/base/base_container.html
+++ b/omeroweb/webclient/templates/webclient/base/base_container.html
@@ -94,7 +94,7 @@
-
+
diff --git a/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html b/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html
index d800ffff92..01b61b00fd 100644
--- a/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html
+++ b/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html
@@ -161,14 +161,14 @@
$("#content_details").html("");
parentId = undefined;
clearThumbnailsPanel();
- return;
+ return Promise.resolve();
}
var dtype = selected[0].type;
if (selected.length > 1 && dtype !== "image") {
$("#content_details").html("");
parentId = undefined;
clearThumbnailsPanel();
- return;
+ return Promise.resolve();
}
// parent node could be dataset, orphaned, share or tag
@@ -182,12 +182,12 @@
} else if (dtype === "plate" || dtype === "acquisition") {
parentId = undefined;
load_spw(event, data);
- return;
+ return Promise.resolve();
// All other types have blank centre panel
} else {
parentId = undefined;
clearThumbnailsPanel();
- return;
+ return Promise.resolve();
}
if (!parentNode) {
@@ -206,7 +206,7 @@
highlightSelectedThumbs(selected);
- return;
+ return Promise.resolve();
}
// update single thumbnail, see OME.refreshThumbnails
if (event.type === "refreshThumb") {
@@ -217,7 +217,7 @@
$("li#image_icon-"+data.imageId+ " img").attr("src", thumb);
}
);
- return;
+ return Promise.resolve();
}
parentId = newParentId;
@@ -312,7 +312,7 @@
if (parentNode.type === "share") {
thumbnailsBatch = 1;
}
- OME.load_thumbnails(
+ var promise = OME.load_thumbnails(
thumbUrl, iids, thumbnailsBatch,
"{% static 'webgateway/img/image128.png' %}"
);
@@ -331,7 +331,7 @@
// scroll to selected thumbnail (if any)
focusThumbnail();
- return;
+ return promise;
}
// Update thumbnails when we switch between plugins
diff --git a/omeroweb/webgateway/static/webgateway/js/ome.gs_utils.js b/omeroweb/webgateway/static/webgateway/js/ome.gs_utils.js
index 80f011a71a..319f789c45 100644
--- a/omeroweb/webgateway/static/webgateway/js/ome.gs_utils.js
+++ b/omeroweb/webgateway/static/webgateway/js/ome.gs_utils.js
@@ -79,8 +79,6 @@ function parseQuery (q) {
return Params;
}
-var gs_modalJson_cb;
-
/**
* Lazy loader for the blockUI plugin.
*/
@@ -120,48 +118,6 @@ function gs_choiceModalDialog (message, choices, callback, blockui_opts, cancel_
return;
}
-function gs_choiceModalJson (message, choices, callback, blockui_opts, cancel_callback) {
-// if (!gs_loadBlockUI (function () {gs_choiceModalJson(message, choices, callback, blockui_opts, cancel_callback);})) {
-// return;
-// }
- var gs_modalJson_cb = function (idx) {
- jQuery.unblockUI();
- if (choices[idx].url != null) {
- gs_modalJson(choices[idx].url, choices[idx].data, callback);
- } else if (cancel_callback) {
- cancel_callback();
- }
- return false;
- }
- return gs_choiceModalDialog(message,choices,callback,blockui_opts,cancel_callback,gs_modalJson_cb);
-// for (i in choices) {
-// message += ''
-// }
-// if (!blockui_opts) {
-// blockui_opts = {};
-// }
-// jQuery.blockUI({message: message, css: blockui_opts.css});
-// return;
-}
-
-/**
- * Calls a jsonp url, just like $.getJson, but also looks out for errors.
- * The call is made in a make-believe synchronous fashion, by adding a semi-transparent overlay and disabling controls.
- */
-function gs_modalJson (url, data, callback) {
- if (!gs_loadBlockUI (function () {gs_modalJson(url,data,callback);})) {
- return;
- }
- jQuery.blockUI();
- var cb = function (result, rv) {
- jQuery.unblockUI();
- if (callback) {
- callback(result, rv);
- }
- }
- gs_json (url, data, cb);
-}
-
function gs_json (url, data, callback) {
var cb = function (result) {
return function (data, textStatus, errorThrown) {
@@ -181,160 +137,3 @@ function gs_json (url, data, callback) {
traditional: true
});
}
-
-/**
- * Trims text to a maximum length, or up to the first line break optionally
- * hyst is an hysteresis value stating the minimum trimmed nr of chars for trimming to occur.
- */
-function gs_text_trim (text, length, hyst, nobreakline, snl) {
- if (hyst === undefined) {
- hyst = 0;
- }
- var p = nobreakline && text.indexOf('\n') || -1;
- var trimmed = text;
- // Cut to newline?
- if (p>0 && p').appendTo(container);
- var head = jQuery('