diff options
Diffstat (limited to 'uploader/static')
-rw-r--r-- | uploader/static/css/styles.css | 171 | ||||
-rw-r--r-- | uploader/static/js/datatables.js | 115 | ||||
-rw-r--r-- | uploader/static/js/files.js | 118 | ||||
-rw-r--r-- | uploader/static/js/misc.js | 6 | ||||
-rw-r--r-- | uploader/static/js/populations.js | 22 | ||||
-rw-r--r-- | uploader/static/js/species.js | 21 |
6 files changed, 392 insertions, 61 deletions
diff --git a/uploader/static/css/styles.css b/uploader/static/css/styles.css index 834c563..d41441d 100644 --- a/uploader/static/css/styles.css +++ b/uploader/static/css/styles.css @@ -1,115 +1,164 @@ +* { + box-sizing: border-box; +} + body { margin: 0.7em; - box-sizing: border-box; display: grid; - grid-template-columns: 1fr 6fr; - grid-template-rows: 5em 100%; + grid-template-columns: 1fr 9fr; grid-gap: 20px; - font-family: Georgia, Garamond, serif; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-style: normal; + font-size: 20px; } #header { - grid-column: 1/3; - width: 100%; - /* background: cyan; */ - padding-top: 0.5em; - border-radius: 0.5em; + /* Place it in the parent element */ + grid-column-start: 1; + grid-column-end: 3; + /* Define layout for the children elements */ + display: grid; + grid-template-columns: 8fr 2fr; + + /* Content styling */ background-color: #336699; - border-color: #080808; color: #FFFFFF; - background-image: none; + border-radius: 3px; + min-height: 30px; } -#header .header { - font-size: 2em; - display: inline-block; - text-align: center; -} +#header #header-text { + /* Place it in the parent element */ + grid-column-start: 1; + grid-column-end: 2; -#header .header-nav { - display: inline-block; - color: #FFFFFF; + /* Content styling */ + padding-left: 1em; } -#header .header-nav li { - border-width: 1px; - border-color: #FFFFFF; - vertical-align: middle; - margin: 0.2em; +#header #header-nav { + /* Place it in the parent element */ + grid-column-start: 2; + grid-column-end: 3; } -#header .header-nav a { +#header #header-nav .nav li a { + /* Content styling */ color: #FFFFFF; - text-decoration: none; + background: #4477AA; + border: solid 5px #336699; + border-radius: 5px; + font-size: 0.7em; + text-align: center; + padding: 1px 7px; } #nav-sidebar { - grid-column: 1/2; - /* background: #e5e5ff; */ - padding-top: 0.5em; - border-radius: 0.5em; - font-size: 1.2em; + /* Place it in the parent element */ + grid-column-start: 1; + grid-column-end: 2; } -#main { - grid-column: 2/3; - width: 100%; - /* background: gray; */ +#nav-sidebar .nav li a:hover { border-radius: 0.5em; } -.pagetitle { - padding-top: 0.5em; - /* background: pink; */ +#nav-sidebar .nav .activemenu { + border-style: solid; border-radius: 0.5em; - /* background-color: #6699CC; */ - /* background-color: #77AADD; */ + border-color: #AAAAAA; + background-color: #EFEFEF; +} + +#main { + /* Place it in the parent element */ + grid-column-start: 2; + grid-column-end: 3; + + /* Define layout for the children elements */ + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 4em 100%; + grid-gap: 1em; +} + +#main #pagetitle { + /* Place it in the parent element */ + grid-column-start: 1; + grid-column-end: 3; + + /* Content-styling */ + border-radius: 3px; background-color: #88BBEE; } -.pagetitle h1 { - text-align: center; +#main #pagetitle .title { + font-size: 1.4em; text-transform: capitalize; + padding-left: 0.5em; +} + +#main #all-content { + /* Place it in the parent element */ + grid-column-start: 1; + grid-column-end: 3; + + /* Define layout for the children elements */ + display: grid; + grid-template-columns: 7fr 3fr; /* For a maximum screen width of 1366 pixels */ + grid-gap: 1.5em; +} + +#main #all-content .row { + margin: 0 2px; } -.pagetitle .breadcrumb { +#main #all-content #main-content { + background: #FFFFFF; + max-width: 950px; +} + +#pagetitle .breadcrumb { background: none; + text-transform: capitalize; + font-size: 0.75em; } -.pagetitle .breadcrumb .active a { +#pagetitle .breadcrumb .active a { color: #333333; } -.pagetitle .breadcrumb a { +#pagetitle .breadcrumb a { color: #666666; } -.main-content { - font-size: 1.275em; +.heading { + border-bottom: solid #EEBB88; + text-transform: capitalize; } -.breadcrumb { +.subheading { + padding: 1em 0 0.1em 0.5em; + border-bottom: solid #88BBEE; text-transform: capitalize; } -dd { - margin-left: 3em; - font-size: 0.88em; - padding-bottom: 1em; +input[type="search"] { + border-radius: 5px; } -input[type="submit"] { - text-transform: capitalize; +.btn { + text-transform: Capitalize; } -.card { - margin-top: 0.3em; - border-width: 1px; - border-style: solid; - border-radius: 0.3em; - border-color: #AAAAAA; - padding: 0.5em; +table.dataTable thead th, table.dataTable tfoot th{ + border-right: 1px solid white; + color: white; + background-color: #369; +} + +table.dataTable tbody tr.selected { + background-color: #ffee99 !important; } diff --git a/uploader/static/js/datatables.js b/uploader/static/js/datatables.js new file mode 100644 index 0000000..9782a60 --- /dev/null +++ b/uploader/static/js/datatables.js @@ -0,0 +1,115 @@ +/** Handlers for events in datatables **/ + +var dtAddRowSelectionHandler = (tableId) => { + $(tableId).on("draw.dt", (event) => { + $(".chk-row-select").on("change", (event) => { + var checkboxOrRadio = event.target; + var tablerow = checkboxOrRadio.parentElement.parentElement; + var tableclass = tablerow.getAttribute("class"); + if(checkboxOrRadio.checked) { + if (checkboxOrRadio.type == "radio") { + $(tableId + " tr").each((index, row) => { + var rowKlass = $(row).attr("class") || ""; + row.setAttribute( + "class", rowKlass.replaceAll("selected", "").trim()); + }); + } + tablerow.setAttribute("class", `${tableclass} selected`); + } + else { + tablerow.setAttribute( + "class", tableclass.replaceAll("selected", "").trim()); + } + }); + }); +}; + + +var toggleCheck = (checkboxOrRadio) => { + if (checkboxOrRadio.length > 0) { + var currentState = checkboxOrRadio.prop("checked"); + var newState = !currentState; + if (currentState == true && checkboxOrRadio.attr("type").toLowerCase() == "radio") { + // We don't want to toggle from true to false by clicking on the row + // if it is a radio button. + newState = currentState; + } + checkboxOrRadio.prop("checked", newState); + checkboxOrRadio.trigger("change"); + } +}; + + +var dtAddRowClickHandler = (tableId) => { + $(tableId).on("draw.dt", (event) => { + $(tableId + " tbody tr").on("click", (event) => { + var row = event.target.closest("tr"); + var checkboxOrRadio = $(row).find(".chk-row-select"); + toggleCheck(checkboxOrRadio); + }); + }); +}; + + +var dtAddCommonHandlers = (tableId) => { + dtAddRowSelectionHandler(tableId); + dtAddRowClickHandler(tableId); +}; + +var addTableLength = (menuList, lengthToAdd, dataLength) => { + if(dataLength >= lengthToAdd) { + newList = structuredClone(menuList);//menuList.slice(0, menuList.length); // shallow copy + newList.push(lengthToAdd); + return newList; + } + return menuList; +}; + +var defaultLengthMenu = (data) => { + menuList = [] + var lengths = [10, 25, 50, 100, 1000, data.length]; + lengths.forEach((len) => { + menuList = addTableLength(menuList, len, data.length); + }); + return menuList; +}; + +var buildDataTable = (tableId, data = [], columns = [], userSettings = {}) => { + var defaultSettings = { + responsive: true, + /* == Scroller settings == */ + scroller: true, + paging: true, // MUST be true for scroller to work + sDom: "iti", + scrollY: "100vh", + scrollCollapse: true, + /* == END: Scroller settings == */ + lengthMenu: defaultLengthMenu(data), + language: { + processing: "Processing… Please wait.", + loadingRecords: "Loading population — Please wait.", + lengthMenu: "Show _MENU_ populations", + info: "Showing _START_ to _END_ of _TOTAL_ populations" + }, + data: data, + columns: columns, + drawCallback: (settings) => { + $(this[0]).find("tbody tr").each((idx, row) => { + var arow = $(row); + var checkboxOrRadio = arow.find(".chk-row-select"); + if (checkboxOrRadio) { + if (arow.hasClass("selected")) { + checkboxOrRadio.prop("checked", true); + } else { + checkboxOrRadio.prop("checked", false); + } + } + }); + } + } + var theDataTable = $(tableId).DataTable({ + ...defaultSettings, + ...userSettings + }); + return theDataTable; +}; diff --git a/uploader/static/js/files.js b/uploader/static/js/files.js new file mode 100644 index 0000000..9d6bca1 --- /dev/null +++ b/uploader/static/js/files.js @@ -0,0 +1,118 @@ +var readFirstNLines = (thefile, count, process_content_fns) => { + var reader = new FileReader(); + if(typeof thefile !== "undefined" && thefile !== null) { + reader.addEventListener("load", (event) => { + var content = event + .target + .result + .split("\n") + .slice(0, count) + .map((line) => {return line.trim("\r");}); + process_content_fns.forEach((fn) => {fn(content);}); + }); + reader.readAsText(thefile); + } +}; +var read_first_n_lines = readFirstNLines; + + +var readBinaryFile = (file) => { + return new Promise((resolve, reject) => { + var _reader = new FileReader(); + _reader.onload = (event) => {resolve(_reader.result);}; + _reader.readAsArrayBuffer(file); + }); +}; + + +var Uint8ArrayToHex = (arr) => { + var toHex = (val) => { + _hex = val.toString(16); + if(_hex.length < 2) { + return "0" + val; + } + return _hex; + }; + _hexstr = "" + arr.forEach((val) => {_hexstr += toHex(val)}); + return _hexstr +}; + + +var computeFileChecksum = (file) => { + return readBinaryFile(file) + .then((content) => { + return window.crypto.subtle.digest( + "SHA-256", new Uint8Array(content)); + }).then((digest) => { + return Uint8ArrayToHex(new Uint8Array(digest)) + }); +}; + + +var defaultResumableHandler = (event) => { + throw new Error("Please provide a valid event handler!"); +}; + +var addHandler = (resumable, handlername, handler) => { + if(resumable.support) { + resumable.on(handlername, (handler || defaultResumableHandler)); + } + return resumable; +}; + + +var makeResumableHandler = (handlername) => { + return (resumable, handler) => { + return addHandler(resumable, handlername, handler); + }; +}; + + +var fileSuccessHandler = makeResumableHandler("fileSuccess"); +var fileProgressHandler = makeResumableHandler("fileProgress"); +var fileAddedHandler = makeResumableHandler("fileAdded"); +var filesAddedHandler = makeResumableHandler("filesAdded"); +var filesRetryHandler = makeResumableHandler("filesRetry"); +var filesErrorHandler = makeResumableHandler("filesError"); +var uploadStartHandler = makeResumableHandler("uploadStart"); +var completeHandler = makeResumableHandler("complete"); +var progressHandler = makeResumableHandler("progress"); +var errorHandler = makeResumableHandler("error"); + + +var markResumableDragAndDropElement = (resumable, fileinput, droparea, browsebutton) => { + if(resumable.support) { + //Hide file input element and display drag&drop UI + add_class(fileinput, "hidden"); + remove_class(droparea, "hidden"); + + // Define UI elements for browse and drag&drop + resumable.assignDrop(droparea); + resumable.assignBrowse(browsebutton); + } + + return resumable; +}; + + +var makeResumableElement = (targeturi, fileinput, droparea, uploadbutton, filetype) => { + var resumable = Resumable({ + target: targeturi, + fileType: filetype, + maxFiles: 1, + forceChunkSize: true, + generateUniqueIdentifier: (file, event) => { + return computeFileChecksum(file).then((checksum) => { + var _relativePath = (file.webkitRelativePath + || file.relativePath + || file.fileName + || file.name); + return checksum + "-" + _relativePath.replace( + /[^a-zA-Z0-9_-]/img, ""); + }); + } + }); + + return resumable; +}; diff --git a/uploader/static/js/misc.js b/uploader/static/js/misc.js new file mode 100644 index 0000000..cf7b39e --- /dev/null +++ b/uploader/static/js/misc.js @@ -0,0 +1,6 @@ +"Miscellaneous functions and event-handlers" + +$(".not-implemented").click((event) => { + event.preventDefault(); + alert("This feature is not implemented yet. Please bear with us."); +}); diff --git a/uploader/static/js/populations.js b/uploader/static/js/populations.js new file mode 100644 index 0000000..73e298a --- /dev/null +++ b/uploader/static/js/populations.js @@ -0,0 +1,22 @@ +$(() => { + dtAddCommonHandlers("#tbl-select-population"); + var populationsDataTable = buildDataTable( + "#tbl-select-population", + JSON.parse( + $("#tbl-select-population").attr("data-populations-list")), + [ + { + data: (apopulation) => { + return `<input type="radio" name="population_id"` + + `id="rdo_population_id_${apopulation.InbredSetId}" ` + + `value="${apopulation.InbredSetId}" ` + + `class="chk-row-select">`; + } + }, + { + data: (apopulation) => { + return `${apopulation.FullName} (${apopulation.InbredSetName})`; + } + } + ]); +}); diff --git a/uploader/static/js/species.js b/uploader/static/js/species.js new file mode 100644 index 0000000..c1374c6 --- /dev/null +++ b/uploader/static/js/species.js @@ -0,0 +1,21 @@ +$(() => { + dtAddCommonHandlers("#tbl-select-species"); + var speciesDataTable = buildDataTable( + "#tbl-select-species", + JSON.parse( + $("#tbl-select-species").attr("data-species-list")), + [ + { + data: (aspecies) => { + return `<input type="radio" name="species_id"` + + `id="rdo_species_id_${aspecies.SpeciesId}" ` + + `value="${aspecies.SpeciesId}" class="chk-row-select">`; + } + }, + { + data: (aspecies) => { + return `${aspecies.FullName} (${aspecies.SpeciesName})`; + } + } + ]); +}); |