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('
').appendTo(result); - data['links'] = gs_getResultLineLinks(data, baseurl, renderurl); - head.append('- '+data.project+' -'); - head.append('
'+gs_text_trim(data.projectDescription,100)+'
'); - head.append(''); - var detail = jQuery('
').appendTo(result); - detail.append(''+data.dataset+' : '+data.name+''); - detail.append('
'+gs_text_trim(data.description,250,false,' ')+'
'); - var foot = jQuery('
[
').appendTo(result); - var fv = jQuery('Full Viewer').appendTo(foot); - foot.append(' Paper '); - fv.on('click', data.links.fv_click(data.datasetId, data.imageId)); - foot.append('Figure '); - foot.append('] by '+data.author+' - '+data.timestamp+''); - return result; -}; - -/** - * Open the full viewer for a specific image. - * Passing the dataset is needed to allow showing 'Figure List' on the viewer toolbar. - */ -function gs_popViewer (did, iid, baseurl) { - if (iid == null) { - return true; - } - if (did == null && typeof iid == 'string') { - iid = iid.split('/'); - did = parseInt(iid[1]); - iid = parseInt(iid[0]); - } - var w = window.open(baseurl+'img_detail/' + iid + '/' + did, '_blank', - "toolbar=yes,location=yes,directories=yes,status=yes,menubar=yes, scrollbars=yes,resizable=yes,width=800,height=800"); - return false; -} - - -/** - * Search images and fill in results. - */ -function gs_searchImgs (text, baseurl, renderurl, result_cb) { - if (text.length > 0) { - jQuery('#search-results-summary').removeClass('ajax-error').html('searching for "'+text+'"'); - jQuery('#search-results').html('loading...'); - if (renderurl == null) { - renderurl = baseurl; - } - $.getJSON(baseurl+'search/', {text: text, ctx: 'imgs', grabData: true, key: 'meta'}, function(data) { -shown = 0; - if (data.length) { - jQuery('#search-results').html(''); - for (e in data) { - var elm = gs_showResultLine(jQuery('#search-results'), data[e], baseurl, renderurl); - if (elm != null) { - result_cb && result_cb(data[e], elm); - shown++; - } - } - } - if (shown == 0) { - jQuery('#search-results').html('no results'); - jQuery('#search-results-summary').html('search for "'+text+'": no results.'); - } else { - jQuery('#search-results-summary').html('search for "'+text+'":
showing 1 to '+shown+' of '+shown+' total.'); - } - }); - } -} - -function downloadLandingDialog (anchor, msg, cb) { - if (!msg) { - msg = "

Your download will start in a few moments

"; - } - var ccb = function (e) { - cb && cb(e); - } - gs_choiceModalDialog(msg, - [{label: 'close', data: 1}], - ccb, - {css: {width: '50%', left: '25%'}} - ); - if (anchor) { - var dliframe = $('iframe[name=dliframe]'); - if (!dliframe.length) { - dliframe = $('').appendTo('body'); - } - dliframe.attr('src', $(anchor).attr('href')); - //var w = window.open($(anchor).attr('href')); - //location.href = $(anchor).attr('href'); - } - return false; -} - diff --git a/omeroweb/webgateway/static/webgateway/js/ome.popup.js b/omeroweb/webgateway/static/webgateway/js/ome.popup.js index f53afd7a7f..551c105bea 100644 --- a/omeroweb/webgateway/static/webgateway/js/ome.popup.js +++ b/omeroweb/webgateway/static/webgateway/js/ome.popup.js @@ -937,7 +937,6 @@ if (false) { // set to 'true' to run. NB: Need to uncomment ' exceptions = 'addEventListener,document,location,navigator,window'.split(','); exceptions.push("jQuery", "$"); // Ignore jQuery etc... exceptions.push("isClientPhone", "callback", "isClientTouch", "isIE"); // from panojs/utils.js - exceptions.push("sanitizeHexColor", "toRGB", "rgbToHex", "parseQuery", "downloadLandingDialog"); // from ome.gs_utils.js // All these from PanoJS exceptions.push("PanoJS", "PanoControls", "BisqueISLevel", "BisqueISPyramid", "formatInt"); exceptions.push("ImgcnvPyramid", "ImgcnvLevel", "InfoControl", "Metadata", "OsdControl", "ROIControl", "ScaleBarControl"); diff --git a/omeroweb/webgateway/static/webgateway/js/omero_image.js b/omeroweb/webgateway/static/webgateway/js/omero_image.js index d5adaed0a9..5b4d9ca006 100644 --- a/omeroweb/webgateway/static/webgateway/js/omero_image.js +++ b/omeroweb/webgateway/static/webgateway/js/omero_image.js @@ -99,19 +99,32 @@ window.setImageDefaults = function (viewport, obj, callback, skip_apply) { if (!skip_apply) applyRDCW(viewport); var old = $(obj).html(); - gs_modalJson(viewport.viewport_server + '/saveImgRDef/'+viewport.loadedImg.id+'/?'+viewport.getQuery(true), - {}, - function(success, rv) { - $(obj).html(old).prop('disabled', false); - if (!(success && rv)) { - alert('Setting image defaults failed. Success: ' + success + ' Response: ' + rv); + OME.progress_overlay(new Promise(function (resolve) { + var cb = function (success) { + return function (data, textStatus, errorThrown) { + resolve(); + var rv = success ? data : errorThrown || textStatus; + $(obj).html(old).prop('disabled', false); + if (!(success && rv)) { + alert('Setting image defaults failed. Success: ' + success + ' Response: ' + rv); + } + if (callback) { + callback(); + } + viewport.setSaved(); + updateUndoRedo(viewport); } - if (callback) { - callback(); - } - viewport.setSaved(); - updateUndoRedo(viewport); + } + jQuery.ajax({ + type: "POST", + url: viewport.viewport_server + '/saveImgRDef/'+viewport.loadedImg.id+'/?'+viewport.getQuery(true), + data: {}, + success: cb(true), + error: cb(false), + dataType: "jsonp", + traditional: true }); + }), 'Saving...'); return false; };