Skip to content

Commit

Permalink
Show warning message prior to large matrix downloads. See issue #502.
Browse files Browse the repository at this point in the history
If the number of array elements to download exceeds a threshold (currently
one million array elements) show a warning to the user that the download
may kill the browser due to memory exhaustion.

If the user chooses to proceed, a progress bar is displayed.

If the warning dialog is displayed, the user can cancel the download.

For large downloads there is a noticeable delay between when we have
finished all processing (and hide the dialog) and when the browser is
ready to save the file.  The browser can still crash during this time.
I'm not sure if there's anything that we can do about that.
  • Loading branch information
bmbroom committed Jun 13, 2023
1 parent 7b9121d commit 06acff0
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 7 deletions.
3 changes: 3 additions & 0 deletions NGCHM/WebContent/css/NGCHM.css
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@ iframe.nopointer {
background-color: #CBDBF6;
}

span.errorMessage {
color: red;
}

div#droptarget {
position: absolute;
Expand Down
52 changes: 45 additions & 7 deletions NGCHM/WebContent/javascript/Linkout.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,12 +400,42 @@ var linkoutsVersion = 'undefined';
matrix.push ([""].concat(colLabels).join('\t')+'\n');

let accessWindow = null; // Hold onto accessWindow until next one created to ensure tiles stay in cache.
processRow(0);
let canceled = false;

const warningSize = 1000000;
const warningShown = rowLabels.length * colLabels.length >= warningSize;

if (warningShown) {
showDownloadWarning ();
} else {
processRow(0);
}

function showDownloadWarning () {
UHM.initMessageBox ();
UHM.setMessageBoxHeader ('Large Download Notice');
UHM.setMessageBoxText ("<br>The requested download is very large. <span class='errorMessage'>It may exhaust the browser's memory and crash the window or the browser without warning.</span><br><br>");
UHM.setMessageBoxButton ('cancel', { type: 'text', text: 'Cancel', tooltip: 'Cancel the download', disableOnClick: true, default: false }, () => {
canceled = true;
UHM.messageBoxCancel();
});
UHM.setMessageBoxButton ('go', { type: 'text', text: 'Proceed', tooltip: 'Continue the download', disableOnClick: true, default: true }, () => {
UHM.showMsgBoxProgressBar ();
processRow (0);
});
UHM.displayMessageBox();
}

function processRow (row) {
if (canceled) {
return;
}
if (warningShown) {
UHM.msgBoxProgressMeter (row / rowLabels.length);
}
if (row >= rowLabels.length) {
// All requested rows processed. Make matrix available for download.
downloadSelectedData (heatMap, matrix, "Matrix");
downloadSelectedData (heatMap, matrix, "Matrix", warningShown);
} else {
const rowItem = rowItems[row];
// Get access window for this row and the columns requested.
Expand Down Expand Up @@ -884,7 +914,7 @@ var linkoutsVersion = 'undefined';
covarData.push ([axisLabels[i]].concat(labels.map(lbl => classBars[lbl].values[i])));
}
const rows = covarData.map (row => row.join('\t') + '\n');
downloadSelectedData (heatMap, rows, covarAxis);
downloadSelectedData (heatMap, rows, covarAxis, false);
}

function downloadPartialClassBar (labels, covarAxis) {
Expand All @@ -899,7 +929,7 @@ var linkoutsVersion = 'undefined';
covarData.push ([axisLabels[i]].concat(labels.map(lbl => classBars[lbl].values[labelIndex[i]-1])));
}
const rows = covarData.map (row => row.join('\t') + '\n');
downloadSelectedData (heatMap, rows, covarAxis);
downloadSelectedData (heatMap, rows, covarAxis, false);
}

LNK.copySelectionToClipboard = function(labels,axis){
Expand Down Expand Up @@ -1017,20 +1047,25 @@ var linkoutsVersion = 'undefined';
// Rows is an array of tab-separated row data.
// The first row should be column labels.
// The first field in each row should be a row label.
function downloadSelectedData (heatMap, rows, axis) {
function downloadSelectedData (heatMap, rows, axis, warningShown) {
try {
const fileName = heatMap.getMapInformation().name + "_" + axis + "_Data.tsv";
download (fileName, rows);
download (fileName, rows, warningShown);
} catch (error) {
console.error ('Matrix download is too large');
if (warningShown) {
UHM.setMessageBoxHeader("Matrix Download Failed");
UHM.setMessageBoxText("<br>The Matrix download failed, probably because is was too large.<br>");
}
}
}

function download(filename, text) {
function download(filename, text, warningShown) {
const blob = new Blob (text, { type: 'text/plain' });
const reader = new FileReader();
reader.onerror = function (e) {
console.error ('Failed to convert to data URL', e, reader);
throw e;
};
reader.onload = function (e) {
const element = document.createElement('a');
Expand All @@ -1040,6 +1075,9 @@ var linkoutsVersion = 'undefined';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
if (warningShown) {
UHM.messageBoxCancel();
}
};
reader.readAsDataURL (blob);
}
Expand Down

0 comments on commit 06acff0

Please sign in to comment.