diff --git a/web/app/static/js/modules/corpus_analysis/client/callbacks.js b/web/app/static/js/modules/corpus_analysis/client/callbacks.js index 835bebf9..f6ee898c 100644 --- a/web/app/static/js/modules/corpus_analysis/client/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/client/callbacks.js @@ -61,6 +61,9 @@ function saveResultsData(args) { } else if (type === 'sub-results') { console.info('Saving sub-results data.'); objectKey = 'subResultsData'; + } else if (type = 'inspect-results') { + objectKey = 'inspectResultsData' + console.info('Saving inspect-results data'); } results[objectKey].init(); results[objectKey].matches.push(...payload.matches); diff --git a/web/app/static/js/modules/corpus_analysis/model/Results.js b/web/app/static/js/modules/corpus_analysis/model/Results.js index c2f0c3bc..ea43c696 100644 --- a/web/app/static/js/modules/corpus_analysis/model/Results.js +++ b/web/app/static/js/modules/corpus_analysis/model/Results.js @@ -10,14 +10,16 @@ class Results { this.metaData = new MetaData(); this.fullResultsData = new Data(); this.subResultsData = new Data(); + this.inspectResultsData = new Data(); console.info('Initialized the Results object.'); } init() { this.data.init(); this.metaData.init(); - this.fullResultsData = new Data(); + this.fullResultsData.init(); this.subResultsData.init(); + this.inspectResultsData.init(); } } @@ -55,31 +57,24 @@ class Data { // function creates a unique and safe filename for the download createDownloadFilename(suffix) { - let today; - let currentDate; - let currentTime; - let safeFilename; - let resultFilename; - // get and create metadata - today = new Date(); - currentDate = `${today.getUTCFullYear()}` + - `-${(today.getUTCMonth() + 1)}` + - `-${today.getUTCDate()}`; - currentTime = `${today.getUTCHours()}h` + - `${today.getUTCMinutes()}m` + - `${today.getUTCSeconds()}s`; - safeFilename = this.query.replace(/[^a-z0-9_-]/gi, "_"); - resultFilename = `UTC-${currentDate}_${currentTime}_${safeFilename}_${suffix}`; + let today = new Date(); + let currentDate = `${today.getUTCFullYear()}` + + `-${(today.getUTCMonth() + 1)}` + + `-${today.getUTCDate()}`; + let currentTime = `${today.getUTCHours()}h` + + `${today.getUTCMinutes()}m` + + `${today.getUTCSeconds()}s`; + let safeFilename = this.query.replace(/[^a-z0-9_-]/gi, "_"); + let resultFilename = `UTC-${currentDate}_${currentTime}_${safeFilename}_${suffix}`; return resultFilename } - - // Function to download data as Blob created from string - // should be private but that is not yet a feature of javascript 08.04.2020 + /** + * Function to download data as Blob created from string. + * Should be private but that is not yet a feature of javascript 08.04.2020 + */ download(downloadElement, dataStr, filename, type, filenameSlug) { - console.log('Start Download!'); - let file; filename += filenameSlug; - file = new Blob([dataStr], {type: type}); + let file = new Blob([dataStr], {type: type}); var url = URL.createObjectURL(file); downloadElement.href = url; downloadElement.download = filename; @@ -87,11 +82,12 @@ class Data { // function to download the results as JSON downloadJSONRessource(resultFilename, downloadData, downloadElement) { - let dataStr; - // stringify JSON object for json download - // use tabs to save some space - dataStr = JSON.stringify(downloadData, undefined, "\t"); - // start actual download + /** + * Stringify JSON object for json download. + * Use tabs to save some space. + */ + let dataStr = JSON.stringify(downloadData, undefined, "\t"); + // Start actual download this.download(downloadElement, dataStr, resultFilename, "text/json", ".json") } diff --git a/web/app/static/js/modules/corpus_analysis/view/ResultsView.js b/web/app/static/js/modules/corpus_analysis/view/ResultsView.js index db5b0d73..96db8dcb 100644 --- a/web/app/static/js/modules/corpus_analysis/view/ResultsView.js +++ b/web/app/static/js/modules/corpus_analysis/view/ResultsView.js @@ -56,6 +56,22 @@ class ResultsList extends List { this.clientIsBusy = false; } + /** + * Init function that gets all needed HTML Elements. Implement this, or not? + * Or build a check into the get HTMLElements function if element already exists. + * Also think aobut saving alle elements in resultsList.es.nameOfElement + */ + + /** + * Function to clear/reset some class field values. Usefull if a new query + * hase been issued by the user. + */ + resetFields() { + this.addToSubResultsIdsToShow = new Set(); + this.addToSubResultsStatus = {}; + } + + /** * Function that takes one or more query selector * strings in an array as an input. The function then creates a @@ -65,7 +81,6 @@ class ResultsList extends List { * The value will be the identifed element or elements fetched with the querySelector * method. */ - // TODO: multipleResults=false, atattchSomeCallback=false ? getHTMLElements(arrayOfSelectors) { for (let selector of arrayOfSelectors) { let element; @@ -178,31 +193,9 @@ class ResultsList extends List { return displayOptionsData } - // ###### Functions to add one match to a sub-results ###### - // activate the add buttons - activateAddToSubResults() { - subResultsIdListElement.classList.remove("hide"); - if (subResultsExportElement.classList.contains("hide")) { - subResultsCreateElement.classList.remove("hide"); - } - let addToSubResultsBtnElements = document.getElementsByClassName("add"); - for (let addToSubResultsBtn of addToSubResultsBtnElements) { - addToSubResultsBtn.classList.remove("hide"); - } - } - // deactivate the add buttons - deactivateAddToSubResults() { - subResultsIdListElement.classList.add("hide"); - subResultsCreateElement.classList.add("hide"); - let addToSubResultsBtnElements = document.getElementsByClassName("add"); - for (let addToSubResultsBtn of addToSubResultsBtnElements) { - addToSubResultsBtn.classList.add("hide"); - } - } - // Used in addToSubResults and inspect to toggle the design of the check // buttons according to its checked unchecked status. - helperActivateBtn(btn) { + helperActivateAddBtn(btn) { btn.classList.remove("grey"); btn.classList.add("green"); btn.textContent = "check"; @@ -210,7 +203,7 @@ class ResultsList extends List { // Used in addToSubResults and inspect to toggle the design of the check // buttons according to its checked unchecked status. - helperDeactivateBtn(btn) { + helperDeactivateAddBtn(btn) { btn.classList.remove("green"); btn.classList.add("grey"); btn.textContent = "add"; @@ -222,48 +215,44 @@ class ResultsList extends List { // true. Adds match to sub-results if status is false if status is true it // removes it. addToSubResults(dataIndex, tableCall=true) { - let textarea = subResultsIdListElement.getElementsByTagName("textarea")[0]; if (!this.addToSubResultsStatus[dataIndex] || this.addToSubResultsStatus === undefined) { // add button is activated because status is either false or undefined - this.helperActivateBtn(event.target); + this.helperActivateAddBtn(event.target); this.addToSubResultsStatus[dataIndex] = true; // sets status to true this.addToSubResultsIdsToShow.add(dataIndex + 1); // + 1 because user does not see zero indexd data indexes - textarea.textContent = [...this.addToSubResultsIdsToShow].sort(function(a, b){return a-b}).join(", "); // automaticalle sorts ids into the textarea in ascending order - M.textareaAutoResize(textarea); // after an insert textarea has to be resized manually - nrMarkedMatches.textContent = [...this.addToSubResultsIdsToShow].length; + this.subResultsMatchIds.textContent = [...this.addToSubResultsIdsToShow].sort(function(a, b){return a-b}).join(", "); // automaticalle sorts ids into the textarea in ascending order + M.textareaAutoResize(this.subResultsMatchIds); // after an insert textarea has to be resized manually + this.nrMarkedMatches.textContent = [...this.addToSubResultsIdsToShow].length; } else if (this.addToSubResultsStatus[dataIndex]) { // add button is deactivated because status is true - this.helperDeactivateBtn(event.target); + this.helperDeactivateAddBtn(event.target); this.addToSubResultsStatus[dataIndex] = false; // sets status to false this.addToSubResultsIdsToShow.delete(dataIndex + 1); // + 1 because user does not see zero indexd data indexes - textarea.textContent = [...this.addToSubResultsIdsToShow].sort(function(a, b){return a-b}).join(", "); // automaticalle sorts ids into the textarea in ascending order - nrMarkedMatches.textContent = [...this.addToSubResultsIdsToShow].length; - M.textareaAutoResize(textarea); // after an insert textarea has to be resized manually + this.subResultsMatchIds.textContent = [...this.addToSubResultsIdsToShow].sort(function(a, b){return a-b}).join(", "); // automaticalle sorts ids into the textarea in ascending order + this.nrMarkedMatches.textContent = [...this.addToSubResultsIdsToShow].length; + M.textareaAutoResize(this.subResultsMatchIds); // after an insert textarea has to be resized manually } // Toggles the create button according to the number of ids in addToSubResultsIdsToShow if ([...this.addToSubResultsIdsToShow].length > 0) { - subResultsCreateElement.classList.remove("disabled"); + this.subResultsCreate.classList.toggle('disabled', false); } else if ([...this.addToSubResultsIdsToShow].length === 0) { - subResultsCreateElement.classList.add("disabled"); - } - if (resultCreationRunning) { - subResultsCreateElement.classList.add("disabled"); + this.subResultsCreate.classList.toggle('disabled', true); } // After a match as been added or removed the export button will be // hidden because the sub-results have been altered and have to be built // again. Thus subResultsCreateElement has to be shown again. - subResultsExportElement.classList.add("hide"); - subResultsCreateElement.classList.remove("hide"); + this.subResultsExport.classList.add("hide"); + this.subResultsCreate.classList.remove("hide"); // Also activate/deactivate buttons in the table/jsList results accordingly //if button in inspect was activated/deactivated. // This part only runs if tableCall is false. if (!tableCall) { - let tableAddBtn = document.getElementById("query-results").querySelectorAll(`[data-index="${dataIndex}"]`)[0].getElementsByClassName('add')[0].firstElementChild; // gets the add button from the list view + let tableAddBtn = document.getElementById('query-results').querySelectorAll(`[data-index="${dataIndex}"]`)[0].getElementsByClassName('add')[0].firstElementChild; // gets the add button from the list view if (this.addToSubResultsStatus[dataIndex]) { - this.helperActivateBtn(tableAddBtn); + this.helperActivateAddBtn(tableAddBtn); } else { - this.helperDeactivateBtn(tableAddBtn); + this.helperDeactivateAddBtn(tableAddBtn); } } } @@ -335,16 +324,19 @@ class ResultsList extends List { // gets result cpos infos for one dataIndex (list of length 1) to send back to // the server inspect(dataIndex, type) { - let contextResultsElement; + // initialize context modal + this.getHTMLElements(['#context-modal', + '#context-results']); + this.contextModal = M.Modal.init(this.contextModal); // get result infos from server and show them in context modal this.contextId = dataIndex[0]; - contextResultsElement = document.getElementById("context-results"); - contextResultsElement.innerHTML = ""; // clear it from old inspects - this.getMatchWithContext(dataIndex, type); + this.contextResults.innerHTML = ""; // clear it from old inspects + this.notifyClient('get-results', {resultsType: 'inspect-results', + dataIndexes: [dataIndex]}); // match nr for user to display derived from data_index let contextMatchNrElement = document.getElementById("context-match-nr"); contextMatchNrElement.textContent = this.contextId + 1; - contextModal.open(); + this.contextModal.open(); // add a button to add this match to sub results with onclick event let classes = `btn-floating btn waves-effect` + `waves-light grey right` @@ -352,18 +344,18 @@ class ResultsList extends List { addToSubResultsIdsBtn.setAttribute("class", classes + ` add`); addToSubResultsIdsBtn.innerHTML = 'add'; addToSubResultsIdsBtn.onclick= () => {this.addToSubResults(dataIndex[0], false)}; - // checks if a button has already been added to the inspect modal and removes it - if (addToSubResultsFromInspectElement.children.length > 0) { - addToSubResultsFromInspectElement.firstElementChild.remove(); - } - // Changes the design of the add button according to its checked status - // upon opening the inspect modal. - if (this.addToSubResultsStatus[dataIndex[0]]) { - this.helperActivateBtn(addToSubResultsIdsBtn.firstElementChild); - } else if (!this.addToSubResultsStatus[dataIndex[0]]) { - this.helperDeactivateBtn(addToSubResultsIdsBtn.firstElementChild); - } - addToSubResultsFromInspectElement.appendChild(addToSubResultsIdsBtn); + // // checks if a button has already been added to the inspect modal and removes it + // if (addToSubResultsFromInspectElement.children.length > 0) { + // addToSubResultsFromInspectElement.firstElementChild.remove(); + // } + // // Changes the design of the add button according to its checked status + // // upon opening the inspect modal. + // if (this.addToSubResultsStatus[dataIndex[0]]) { + // this.helperActivateAddBtn(addToSubResultsIdsBtn.firstElementChild); + // } else if (!this.addToSubResultsStatus[dataIndex[0]]) { + // this.helperDeactivateAddBtn(addToSubResultsIdsBtn.firstElementChild); + // } + // addToSubResultsFromInspectElement.appendChild(addToSubResultsIdsBtn); } // create Element from HTML String helper function @@ -395,7 +387,6 @@ class ResultsList extends List { let uniqueS; this.contextData = response.payload; - console.log(this.contextData); this.contextData["cpos_ranges"] = response.payload.cpos_ranges; this.contextData["query"] = results.data.query; this.contextData["context_id"] = this.contextId; diff --git a/web/app/static/js/modules/corpus_analysis/view/callbacks.js b/web/app/static/js/modules/corpus_analysis/view/callbacks.js index 15393a2f..a2673493 100644 --- a/web/app/static/js/modules/corpus_analysis/view/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/view/callbacks.js @@ -5,8 +5,16 @@ */ function disableElementsGeneralCallback(resultsList, detail) { - resultsList.fullResultsCreate.classList.toggle('disabled', true); - resultsList.subResultsCreate.classList.toggle('disabled', true); + if (detail.type === 'full-results') { + resultsList.fullResultsCreate.classList.toggle('hide', false); + resultsList.fullResultsExport.classList.toggle('hide', true); + } else if (detail.type === 'sub-results') { + resultsList.subResultsCreate.classList.toggle('hide', false); + resultsList.subResultsExport.classList.toggle('hide', true); + } else { + resultsList.fullResultsCreate.classList.toggle('disabled', true); + resultsList.subResultsCreate.classList.toggle('disabled', true); + } resultsList.deactivateInspect(); } @@ -14,7 +22,6 @@ function enableElementsGeneralCallback(resultsList, detail) { resultsList.fullResultsCreate.classList.toggle('disabled'); resultsList.subResultsCreate.classList.toggle('disabled'); resultsList.activateInspect(); - } function connectingCallback(resultsList, detail) { @@ -53,17 +60,24 @@ function queryDataPreparingCallback(resultsList, detail) { '#query-results-user-feedback', '#query-progress-bar', '#query-results-create', - '#add-to-sub-results' + '#sub-results-match-ids', + '#nr-marked-matches', ]); - // show or enable some things for the user - resultsList.interactionsMenu.classList.toggle('hide', false) - resultsList.queryResultsUserFeedback.classList.toggle('hide', false); - resultsList.queryProgressBar.classList.toggle('hide', false); - // set some initial values for the user feedback + // show or enable some things for the user + resultsList.interactionsMenu.classList.toggle('hide', false) + resultsList.queryResultsUserFeedback.classList.toggle('hide', false); + resultsList.queryProgressBar.classList.toggle('hide', false); + /** + * Set some initial values for the user feedback + * or reset values for new issued query + */ resultsList.recievedMatchCount.textContent = 0; resultsList.totalMatchCount.textContent = results.data.match_count; resultsList.textLookupTitles.textContent = ''; resultsList.textLookupCount.textContent = 0; + resultsList.nrMarkedMatches.textContent = 0; + resultsList.subResultsMatchIds.textContent = ''; + resultsList.resetFields(); } function queryDataRecievingCallback(resultsList, detail) { diff --git a/web/app/static/js/modules/corpus_analysis/view/listeners.js b/web/app/static/js/modules/corpus_analysis/view/listeners.js index af790ef8..8c322e63 100644 --- a/web/app/static/js/modules/corpus_analysis/view/listeners.js +++ b/web/app/static/js/modules/corpus_analysis/view/listeners.js @@ -40,6 +40,11 @@ function recieveClientNotification(eventType, resultsList) { break; case 'query-data-prepareing': console.info('View recieved notification:', caseIdentifier); + // some extra hiding and showing (this should be done less confusing) + resultsList.fullResultsExport.classList.toggle('hide', true); + resultsList.subResultsExport.classList.toggle('hide', true); + resultsList.fullResultsCreate.classList.toggle('hide', false); + resultsList.subResultsCreate.classList.toggle('hide', false); // execute callbacks disableElementsGeneralCallback(resultsList, event.detail); queryDataPreparingCallback(resultsList, event.detail); @@ -54,6 +59,8 @@ function recieveClientNotification(eventType, resultsList) { // execute callbacks queryDataRecievedCallback(resultsList, event.detail); enableElementsGeneralCallback(resultsList, event.detail); + // create sub-results is disabled per default until matches have been added + resultsList.subResultsCreate.classList.toggle('disabled', true); break; case 'results-data-recieving': console.info('View recieved notification:', caseIdentifier); @@ -64,6 +71,7 @@ function recieveClientNotification(eventType, resultsList) { case 'results-data-recieved': console.info('View recieved notification:', caseIdentifier); // execute callbacks + console.info(event.detail); resultsDataRecievedCallback(resultsList, event.detail); enableElementsGeneralCallback(resultsList, event.detail); break; diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2 index c0abaa99..c82c6310 100644 --- a/web/app/templates/corpora/analyse_corpus.html.j2 +++ b/web/app/templates/corpora/analyse_corpus.html.j2 @@ -217,8 +217,24 @@ document.addEventListener("DOMContentLoaded", () => { '#sub-results-create', '#full-results-export', '#sub-results-export', - '#download-results-json' + '#download-results-json', + '#query-results-download-modal', + '#query-results-table' ]); + /** + * The following event Listener handles the add-btn and the inspect-btn + * onclick events via bubbleing. + */ + resultsList.queryResultsTable.addEventListener('click', (event) => { + let dataIndex; + if (event.target.classList.contains('inspect-btn')) { + dataIndex = parseInt(event.target.closest('tr').dataset.index); + resultsList.inspect([dataIndex], 'inspect'); + } else if (event.target.classList.contains('add-btn')) { + dataIndex = parseInt(event.target.closest('tr').dataset.index); + resultsList.addToSubResults(dataIndex); + } + }) /** * Display events: Following event listeners are handleing the * live update of hits per page if hits per page value is changed and the @@ -234,15 +250,11 @@ document.addEventListener("DOMContentLoaded", () => { * The following event listener handel the Show metadata button and its * functionality. Before the needed modal is initialized. */ - let deleteOverlay = () => { - let overlay = document.querySelector(".modal-overlay"); - overlay.remove(); - }; resultsList.metaDataModal= M.Modal.init(resultsList.metaDataModal, { 'preventScrolling': false, 'opacity': 0.0, 'dismissible': false, - 'onOpenEnd': deleteOverlay + 'onOpenEnd': (() => {document.querySelector(".modal-overlay").remove()}) }); resultsList.showMetaData.onclick = () => { resultsList.metaDataModalContent.textContent = ''; @@ -287,27 +299,29 @@ document.addEventListener("DOMContentLoaded", () => { resultsList.notifyClient('get-results', { resultsType: 'sub-results', dataIndexes: dataIndexes}); } + /** + * Before the downland events are added the needed modal is initialized. + */ + resultsList.queryResultsDownloadModal = M.Modal.init(resultsList.queryResultsDownloadModal); // Open download modal when full results export button is pressed resultsList.fullResultsExport.onclick = () => { - exportModal.open(); + resultsList.queryResultsDownloadModal.open(); // add onclick to download JSON button and download the file - let downloadResultsJSONElement = document.querySelector('#download-results-json') - downloadResultsJSONElement.onclick = () => { - let filename = results.fullResultsData.createDownloadFilename('matches-results'); + resultsList.downloadResultsJson.onclick = () => { + let filename = results.fullResultsData.createDownloadFilename('full-results'); results.fullResultsData.addData(results.metaData); - results.fullResultsData.downloadJSONRessource(filename, results.resultsData, - downloadResultsJSONElement)}; + results.fullResultsData.downloadJSONRessource(filename, results.fullResultsData, + resultsList.downloadResultsJson)}; } // Open download modal when sub results export button is pressed - resultsList.fullResultsExport.onclick = () => { - exportModal.open(); + resultsList.subResultsExport.onclick = () => { + resultsList.queryResultsDownloadModal.open(); // add onclick to download JSON button and download the file - let downloadResultsJSONElement = document.querySelector('#download-results-json') - downloadResultsJSONElement.onclick = () => { - let filename = results.subResultsData.createDownloadFilename('matches-results'); + resultsList.downloadResultsJson.onclick = () => { + let filename = results.subResultsData.createDownloadFilename('sub-results'); results.subResultsData.addData(results.metaData); - results.subResultsData.downloadJSONRessource(filename, results.resultsData, - downloadResultsJSONElement)}; + results.subResultsData.downloadJSONRessource(filename, results.subResultsData, + resultsList.downloadResultsJson)}; } // enable scroll to Top diff --git a/web/app/templates/interactions/create.html.j2 b/web/app/templates/interactions/create.html.j2 index 5f6069ce..81df4531 100644 --- a/web/app/templates/interactions/create.html.j2 +++ b/web/app/templates/interactions/create.html.j2 @@ -11,9 +11,9 @@ results.--> button in the list or inspect view.
-Added matches:
+matches added for sub-results: