7864ddf3a6d058dbe29f706519921cd55ccc4c67 chmalee Thu May 2 09:04:45 2024 -0700 Work in progress on building form for choosing db and hub along with files to submit diff --git src/hg/js/hgMyData.js src/hg/js/hgMyData.js index 2859570..dff2ea5 100644 --- src/hg/js/hgMyData.js +++ src/hg/js/hgMyData.js @@ -52,32 +52,30 @@ let li = document.getElementById(liId); return li; } function newButton(text) { /* Creates a new button with some text as the label */ let newBtn = document.createElement("label"); newBtn.classList.add("button"); newBtn.textContent = text; return newBtn; } function createInput() { /* Create a new input element for a file picker */ let input = document.createElement("input"); - //input.style.opacity = 0; - //input.style.float = "right"; input.multiple = true; input.type = "file"; input.id = "hiddenFileInput"; input.style = "display: none"; input.addEventListener("change", listPickedFiles); return input; } function requestsPending() { /* Return true if requests are still pending, which means it needs to * have been sent(). aborted requests are considered done for this purpose */ for (let [req, f] of uiState.pendingQueue) { if (req._req !== null) { xreq = req._req._xhr; if (xreq.readyState != XMLHttpRequest.DONE && xreq.readyState != XMLHttpRequest.UNSENT) { @@ -150,30 +148,31 @@ ele.appendChild(progMeter); progMeter.ctx = progMeter.getContext('2d'); progMeter.ctx.fillStyle = 'orange'; progMeter.updateProgress = (percent) => { // update the progress meter for this elem if (percent === 100) { progMeter.ctx.fillStyle = 'green'; } progMeter.ctx.fillRect(0, 0, (progMeterWidth * percent) / 100, progMeterHeight); }; progMeter.updateProgress(0); return progMeter; } function submitPickedFiles() { + let tusdServer = getTusdEndpoint(); let onBeforeRequest = function(req) { let xhr = req.getUnderlyingObject(req); xhr.withCredentials = true; }; let onSuccess = function(req) { // remove the selected file from the input element and the ul list // FileList is a read only setting, so we have to make // a new one without this req delete uiState.toUpload[req.name]; let i, newReqObj = {}; for (i = 0; i < uiState.pendingQueue.length; i++) { fname = uiState.pendingQueue[i][1].name; @@ -263,53 +262,102 @@ submitBtn.id = "submitPicked"; submitBtn.classList.add("button"); submitBtn.textContent = "Submit"; submitBtn.addEventListener("click", submitPickedFiles); btnRow.append(submitBtn); } } function removeClearSubmitButtons() { let btn = document.getElementById("clearPicked"); btn.parentNode.removeChild(btn); btn = document.getElementById("submitPicked"); btn.parentNode.removeChild(btn); } + function makeGenomeSelect(formName, fileName) { + let genomeInp = document.createElement("select"); + genomeInp.classList.add("genomePicker"); + genomeInp.name = `${fileName}#genomeInput`; + genomeInp.id = `${fileName}#genomeInput`; + genomeInp.form = formName; + return genomeInp; + } + + function makeTypeSelect(formName, fileName) { + let typeInp = document.createElement("select"); + typeInp.classList.add("typePicker"); + typeInp.name = `${fileName}#typeInput`; + typeInp.id = `${fileName}#typeInput`; + let choices = ["hub.txt", "bigBed", "bam", "vcf", "bigWig"]; + choices.forEach( (e) => { + let choice = document.createElement("option"); + choice.id = e; + choice.label = e; + choice.value = e; + typeInp.appendChild(choice); + }); + typeInp.form = formName; + return typeInp; + } + + function makeHubSelect(formName, fileName) { + let hubInp = document.createElement("select"); + hubInp.classList.add("hubPicker"); + hubInp.name = `${fileName}#hubInput`; + hubInp.id = `${fileName}#hubInput`; + hubInp.form = formName; + } + + function makeFormControlsForFile(li, formName, fileName) { + typeInp = makeTypeSelect(formName, fileName); + genomeInp = makeGenomeSelect(formName, fileName); + hubInp = makeGenomeSelect(formName, fileName); + li.append(typeInp); + li.append(genomeInp); + li.append(hubInp); + } + function listPickedFiles() { - //uiState.input.click(); // let the user choose files: + // let the user choose files: if (uiState.input.files.length === 0) { console.log("not input"); return; - //togglePickStateMessage(true); - //removeClearSubmitButtons(); } else { - let displayList = document.getElementsByClassName("pickedFiles"); - if (displayList.length === 0) { + let displayList; + let displayListForm = document.getElementsByClassName("pickedFilesForm"); + if (displayListForm.length === 0) { + displayListForm = document.createElement("form"); + displayListForm.id = "displayListForm"; + displayListForm.classList.add("pickedFilesForm"); displayList = document.createElement("ul"); displayList.classList.add("pickedFiles"); - uiState.pickedList.appendChild(displayList); + displayListForm.appendChild(displayList); + uiState.pickedList.appendChild(displayListForm); } else { - displayList = displayList[0]; + displayList = displayListForm[0].firstChild; } for (let file of uiState.input.files ) { if (file.name in uiState.toUpload) { continue; } // create a list for the user to see let li = document.createElement("li"); li.classList.add("pickedFile"); li.id = `${file.name}#li`; li.textContent = `File name: ${file.name}, file size: ${prettyFileSize(file.size)}`; + // Add the form controls for this file: + makeFormControlsForFile(li, "displayListForm", file.name); + displayList.appendChild(li); // finally add it for us uiState.toUpload[file.name] = file; } togglePickStateMessage(false); addClearSubmitButtons(); } // always clear the input element uiState.input = createInput(); } function dataTablePrintSize(data, type, row, meta) { return prettyFileSize(data); } @@ -328,31 +376,31 @@ // Note that repeated requests, like from a bot, will return 404 as a correct response console.log(`sending delete req for ${fname}`); const endpoint = "../cgi-bin/hgHubConnect?deleteFile=" + fname; if (!(endpoint in pendingDeletes)) { const xhr = new XMLHttpRequest(); pendingDeletes[endpoint] = xhr; this.xhr = xhr; this.xhr.open("DELETE", endpoint, true); this.xhr.send(); deleteFileFromTable(rowIx, fname); delete pendingDeletes[endpoint]; } } function viewInGenomeBrowser(rowIx, fname) { - // redirect to hgTracks with this track as a custom track + // redirect to hgTracks with this track open in the hub if (typeof uiState.userUrl !== "undefined" && uiState.userUrl.length > 0) { bigBedExts = [".bb", ".bigBed", ".vcf.gz", ".vcf", ".bam", ".bw", ".bigWig"]; let i; for (i = 0; i < bigBedExts.length; i++) { if (fname.toLowerCase().endsWith(bigBedExts[i].toLowerCase())) { // TODO: tusd should return this location in it's response after // uploading a file and then we can look it up somehow, the cgi can // write the links directly into the html directly for prev uploaded files maybe? window.location.assign("../cgi-bin/hgTracks?db=hg38&hgt.customText=" + uiState.userUrl + fname); return false; } } } } @@ -370,111 +418,103 @@ let tableInitOptions = { //columnDefs: [{orderable: false, targets: [0,1]}], columnDefs: [ { orderable: false, targets: 0, title: "", render: function(data, type, row) { return ""; } }, { orderable: false, targets: 1, data: "action", title: "Action", render: function(data, type, row) { - // TODO: add event handler on delete button // click to call hgHubDelete file return ""; } }, { targets: 3, render: function(data, type, row) { return dataTablePrintSize(data); } } ], columns: [ {data: "", }, {data: "", }, + {data: "hub", title: "Hub"}, + {data: "genome", title: "Genome"}, {data: "name", title: "File name"}, {data: "size", title: "File size", render: dataTablePrintSize}, {data: "createTime", title: "Creation Time"}, ], - order: [[4, 'desc']], + order: [[6, 'desc']], drawCallback: function(settings) { let btns = document.querySelectorAll('.deleteFileBtn'); let i; for (i = 0; i < btns.length; i++) { let fnameNode = btns[i].parentNode.nextElementSibling.childNodes[0]; if (fnameNode.nodeName !== "#text") {continue;} let fname = fnameNode.nodeValue; btns[i].addEventListener("click", (e) => { deleteFile(i, fname); }); } btns = document.querySelectorAll('.viewInBtn'); for (i = 0; i < btns.length; i++) { let fnameNode = btns[i].parentNode.nextElementSibling.childNodes[0]; if (fnameNode.nodeName !== "#text") {continue;} let fname = fnameNode.nodeValue; btns[i].addEventListener("click", (e) => { viewInGenomeBrowser(i, fname); }); } }, }; function showExistingFiles(d) { // Make the DataTable for each file - //$(document).on("draw.dt", function() {alert("table redrawn");}); tableInitOptions.data = d; let table = $("#filesTable").DataTable(tableInitOptions); } function checkJsonData(jsonData, callerName) { // Return true if jsonData isn't empty and doesn't contain an error; // otherwise complain on behalf of caller. if (! jsonData) { alert(callerName + ': empty response from server'); } else if (jsonData.error) { console.error(jsonData.error); alert(callerName + ': error from server: ' + jsonData.error); } else if (jsonData.warning) { alert("Warning: " + jsonData.warning); return true; } else { if (debugCartJson) { console.log('from server:\n', jsonData); } return true; } return false; } function updateStateAndPage(jsonData, doSaveHistory) { // Update uiState with new values and update the page. _.assign(uiState, jsonData); - /* - urlVars = {"db": db, "search": uiState.search, "showSearchResults": ""}; - // changing the url allows the history to be associated to a specific url - var urlParts = changeUrl(urlVars); - if (doSaveHistory) - saveHistory(uiState, urlParts); - changeSearchResultsLabel(); - */ } function handleRefreshState(jsonData) { if (checkJsonData(jsonData, 'handleRefreshState')) { updateStateAndPage(jsonData, true); } $("#spinner").remove(); } function init() { cart.setCgi('hgMyData'); cart.debug(debugCartJson); if (!useTus) { console.log("tus is not supported, falling back to XMLHttpRequest"); }