diff --git a/web/app/static/js/modules/corpus_analysis/client/Client.js b/web/app/static/js/modules/corpus_analysis/client/Client.js index 67c0104b..4c7acc31 100644 --- a/web/app/static/js/modules/corpus_analysis/client/Client.js +++ b/web/app/static/js/modules/corpus_analysis/client/Client.js @@ -102,11 +102,12 @@ class Client { this.socket.emit('corpus_analysis_query', queryStr); } - // create results data either from all results or from all marked sub results + /** + * Create results data either from all results or from all marked sub results + * Triggers emit to get full match context from server for a number of + * matches identified by their data_index. + **/ getResultsData(resultsType, dataIndexes, results) { - // TODO: where to put all the stuff that deactivates all the buttons because cqp server cannot handle mutliple requests? - // Triggers emit to get full match context from server for a number of - // matches identified by their data_index. let tmp_first_cpos = []; let tmp_last_cpos = []; for (let dataIndex of dataIndexes) { @@ -168,7 +169,7 @@ class ClientEventListener { /** * This class is used to create an ListenerCallback which will be registered - * to an SocketEventListener so the Listener cann invoke the ListenerCallback + * to an SocketEventListener so the Listener can invoke the ListenerCallback * callback functions. */ class ListenerCallback { 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 f6ee898c..64bb24d4 100644 --- a/web/app/static/js/modules/corpus_analysis/client/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/client/callbacks.js @@ -65,6 +65,7 @@ function saveResultsData(args) { objectKey = 'inspectResultsData' console.info('Saving inspect-results data'); } + // Save incoming data results[objectKey].init(); results[objectKey].matches.push(...payload.matches); results[objectKey].addData(payload.cpos_lookup, "cpos_lookup"); @@ -75,7 +76,8 @@ function saveResultsData(args) { results[objectKey].match_count = [...payload.matches].length; results[objectKey].cpos_ranges = payload.cpos_ranges; console.info('Results data has been saved.', results); - client.notifyView('results-data-recieved', {type: type}); + client.notifyView('results-data-recieved', {type: type, + results: results}); } // export callbacks diff --git a/web/app/static/js/modules/corpus_analysis/main.js b/web/app/static/js/modules/corpus_analysis/main.js deleted file mode 100644 index e69de29b..00000000 diff --git a/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js b/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js index 7856cf48..0fa3fbb7 100644 --- a/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js +++ b/web/app/static/js/modules/corpus_analysis/view/InteractionElement.js @@ -1,4 +1,10 @@ -class InteractionElement { +// TODO: Simplify this so that things are executed on change page and on first +// load look below at function doc strings + +// one interaction can have more than one type when the associatee callback +// can be executed + +class Interaction { constructor(htmlId="", checkStatus=true, disabledBefore=true, @@ -6,14 +12,15 @@ class InteractionElement { hideBefore=true, hideAfter=false) { this.htmlId = htmlId; - this.element = (htmlId) => {this.element = document.getElementById(htmlId);} + this.element = ((htmlId) => { + document.getElementById(htmlId); + })() this.checkStatus = checkStatus; this.callbacks = {}; this.disabledBefore = disabledBefore; this.disabledAfter = disabledAfter; this.hideBefore = hideBefore; this.hideAfter = hideAfter; - this.element(this.htmlId); } setCallback(trigger, callback, bindThis, args=[]) { @@ -31,16 +38,22 @@ class InteractionElement { } } -class InteractionElements { +class Interactions { constructor() { this.interactions = []; } - addInteractions (interactionsArray) { - this.interactions.push(...interactionsArray); + addInteractions (interactions) { + this.interactions.push(...interactions); } - onChangeExecute() { + /** + * This function executes all registered interactions of the type + * onElementChange. + * Interactions and their associated callbacks will be executed every time + * a chante event occurs on the specified element. + */ + onElementChangeExecute() { // checks if a change for every interactionElement happens and executes // the callbacks accordingly for (let interaction of this.interactions) { @@ -61,6 +74,23 @@ class InteractionElements { } }; } + + /** + * This function executes all registered interactions of the type onQueryLoad. + * Interactions and their associated callbacks will be executed once if when + * the first query results of a query request are being displayed. + */ + onQueryLoadExecute() { + } + + /** + * This function executes all registered interactions of the type + * onPageChange. + * Interactions and their associated callbacks will be executed everytime if + * the user used the page navigation and a new result page is being displayed. + */ + onPageChangeExecute() { + } } // export Classes 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 96db8dcb..46c82f02 100644 --- a/web/app/static/js/modules/corpus_analysis/view/ResultsView.js +++ b/web/app/static/js/modules/corpus_analysis/view/ResultsView.js @@ -48,6 +48,7 @@ class ResultsList extends List { * here to delete later on */ this.currentExpertTokenElements = {}; + // TODO: Rename both variables to something more descreptive and clear // holds True/false for check buttons used to add matches tu sub-results. If checked, it is True. If unchecked, it is false. Buttons for this have the class add. Those little round check buttons. this.addToSubResultsStatus = {}; this.addToSubResultsIdsToShow = new Set(); // If check button is pressed its corresponding data_index is saved in this set. The set is shown to the user. @@ -57,9 +58,10 @@ class ResultsList extends List { } /** + * // TODO: * 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 + * Also think about saving alle elements in resultsList.es.nameOfElement */ /** @@ -239,16 +241,22 @@ class ResultsList extends List { } else if ([...this.addToSubResultsIdsToShow].length === 0) { 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. + /** + * 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. + */ 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. + /** + * 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 + this.getHTMLElements(['#query-results-table']); + let container = this.queryResultsTable.querySelector(`[data-index="${dataIndex}"]`); + let tableAddBtn = container.querySelector('.add-btn'); // gets the add button from the list view if (this.addToSubResultsStatus[dataIndex]) { this.helperActivateAddBtn(tableAddBtn); } else { @@ -325,8 +333,11 @@ class ResultsList extends List { // the server inspect(dataIndex, type) { // initialize context modal - this.getHTMLElements(['#context-modal', - '#context-results']); + this.getHTMLElements([ + '#context-modal', + '#context-results', + '#create-inspect-menu', + ]); this.contextModal = M.Modal.init(this.contextModal); // get result infos from server and show them in context modal this.contextId = dataIndex[0]; @@ -344,18 +355,15 @@ 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.helperActivateAddBtn(addToSubResultsIdsBtn.firstElementChild); - // } else if (!this.addToSubResultsStatus[dataIndex[0]]) { - // this.helperDeactivateAddBtn(addToSubResultsIdsBtn.firstElementChild); - // } - // addToSubResultsFromInspectElement.appendChild(addToSubResultsIdsBtn); + // checks if the match has or has not been added to sub results yet + // sets the color and status of the button accordingly + if (this.addToSubResultsStatus[dataIndex[0]]) { + this.helperActivateAddBtn(addToSubResultsIdsBtn.firstElementChild); + } else if (!this.addToSubResultsStatus[dataIndex[0]]) { + this.helperDeactivateAddBtn(addToSubResultsIdsBtn.firstElementChild); + } + this.createInspectMenu.innerHTML = ''; + this.createInspectMenu.appendChild(addToSubResultsIdsBtn); } // create Element from HTML String helper function @@ -369,42 +377,26 @@ class ResultsList extends List { // Used as a callback to handle incoming match context results when inspect // has been used. - showMatchContext(response) { - this.contextData; - let contextModalLoading; - let contextModalReady; - let contextResultsElement; - let highlightSentencesSwitchElement; - let htmlTokenStr; - let modalExpertModeSwitchElement; - let modalTokenElements; - let nrOfContextSentences; - let partElement; - let token; - let tokenHTMLArray; - let tokenHTMlElement; - let uniqueContextS; - let uniqueS; + showMatchContext(results) { - this.contextData = response.payload; - this.contextData["cpos_ranges"] = response.payload.cpos_ranges; - this.contextData["query"] = results.data.query; - this.contextData["context_id"] = this.contextId; - this.contextData["match_count"] = this.contextData.matches.length - this.contextData["corpus_type"] = "inspect-result" - Object.assign(this.contextData, results.metaData); - contextResultsElement = document.getElementById("context-results"); - modalExpertModeSwitchElement = document.getElementById("inspect-display-options-form-expert_mode_inspect"); - highlightSentencesSwitchElement = document.getElementById("inspect-display-options-form-highlight_sentences"); - nrOfContextSentences = document.getElementById("context-sentences"); - uniqueS = new Set(); - uniqueContextS = new Set(); - let {lc, c, rc} = this.helperCreateCpos(this.contextData.cpos_ranges, - this.contextData.matches[0]) + this.getHTMLElements([ + '#context-results', + '#inspect-display-options-form-expert_mode_inspect', + '#inspect-display-options-form-highlight_sentences', + '#context-sentences' + ]) + + let uniqueS = new Set(); + let uniqueContextS = new Set(); + let {lc, c, rc} = this.helperCreateCpos(results.inspectResultsData.cpos_ranges, + results.inspectResultsData.matches[0]); // create sentence strings as tokens - tokenHTMLArray = []; + let tokenHTMLArray = []; + let htmlTokenStr = ``; + let tokenHTMlElement; + let token; for (let cpos of lc) { - token = this.contextData.cpos_lookup[cpos]; + token = results.inspectResultsData.cpos_lookup[cpos]; uniqueS.add(token.s) htmlTokenStr = ` { + this.inspectDisplayOptionsFormExpertModeInspect.onchange = (event) => { if (event.target.checked) { - this.expertModeOn("context-results"); + this.expertModeOn("context-results", results); } else { this.expertModeOff("context-results") } }; - highlightSentencesSwitchElement.onchange = (event) => { + this.inspectDisplayOptionsFormHighlightSentences.onchange = (event) => { if (event.target.checked) { this.higlightContextSentences(); } else { @@ -471,24 +463,24 @@ class ResultsList extends List { } }; - nrOfContextSentences.onchange = (event) => { + this.contextSentences.onchange = (event) => { // console.log(event.target.value); this.changeSentenceContext(event.target.value); } // checks on new modal opening if switches are checked // if switches are checked functions are executed - if (modalExpertModeSwitchElement.checked) { - this.expertModeOn("context-results"); + if (this.inspectDisplayOptionsFormExpertModeInspect.checked) { + this.expertModeOn("context-results", results); } - if (highlightSentencesSwitchElement.checked) { + if (this.inspectDisplayOptionsFormHighlightSentences.checked) { this.higlightContextSentences(); } // checks the value of the number of sentences to show on modal opening // sets context sentences accordingly - this.changeSentenceContext(nrOfContextSentences.value) + this.changeSentenceContext(this.contextSentences.value); } // splits context text into sentences based on spacy sentence split @@ -597,24 +589,25 @@ class ResultsList extends List { // ###### Expert view event functions ###### // function to create a tooltip for the current hovered token - tooltipEventCreate(event, client) { + tooltipEventCreate(event, results) { // console.log("Create Tooltip on mouseover."); - let token = client.results.data.cpos_lookup[event.target.dataset.cpos]; + let token; + token = results.data.cpos_lookup[event.target.dataset.cpos]; if (!token) { - token = this.contextData.cpos_lookup[event.target.dataset.cpos]; + token = results.inspectResultsData.cpos_lookup[event.target.dataset.cpos]; } - this.addToolTipToTokenElement(event.target, token, client); + this.addToolTipToTokenElement(event.target, token, results); } // Function to destroy the current Tooltip for the current hovered tooltip // on mouse leave - tooltipEventDestroy() { + tooltipEventDestroy(event) { // console.log("Tooltip destroy on leave."); this.currentTooltipElement.destroy(); } // turn the expert mode on for all tokens in the DOM element identified by its htmlID - expertModeOn(htmlId, client) { + expertModeOn(htmlId, results) { if (!Array.isArray(this.currentExpertTokenElements[htmlId])) { this.currentExpertTokenElements[htmlId] = []; } @@ -625,15 +618,15 @@ class ResultsList extends List { for (let tokenElement of this.currentExpertTokenElements[htmlId]) { tokenElement.classList.add("chip", "hoverable", "expert-view"); const eventCreate = (event, arg) => this.tooltipEventCreate(event, arg); - tokenElement.onmouseover = (event) => eventCreate(event, client); - tokenElement.onmouseout = () => this.tooltipEventDestroy(); + tokenElement.onmouseover = (event) => eventCreate(event, results); + tokenElement.onmouseout = (event) => this.tooltipEventDestroy(event); this.eventTokens[htmlId].push(tokenElement); } } // fuction that creates Tooltip for one token and extracts the corresponding // infos from the result JSON - addToolTipToTokenElement(tokenElement, token, client) { + addToolTipToTokenElement(tokenElement, token, results) { this.currentTooltipElement; this.currentTooltipElement = M.Tooltip.init(tokenElement, {"html": ` @@ -650,11 +643,11 @@ class ResultsList extends List { NER: ${token.ner}
- Title: ${client.results.data.text_lookup[token.text].title} + Title: ${results.data.text_lookup[token.text].title}
- Author: ${client.results.data.text_lookup[token.text].author} + Author: ${results.data.text_lookup[token.text].author}
- Publishing year: ${client.results.data.text_lookup[token.text].publishing_year} + Publishing year: ${results.data.text_lookup[token.text].publishing_year}
`} 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 a2673493..8e0a36c3 100644 --- a/web/app/static/js/modules/corpus_analysis/view/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/view/callbacks.js @@ -133,12 +133,13 @@ function resultsDataRecievingCallback(resultsList, detail) { function resultsDataRecievedCallback(resultsList, detail) { resultsList.clientIsBusy = false; + // create strings for create buttons depending on type const handleType = (keyPrefix, text) => { // hides the create element after results have been recieved and reset it resultsList[`${keyPrefix}Create`].classList.toggle('hide'); resultsList[`${keyPrefix}Create`].textContent = `Create ${text}`; resultsList[`${keyPrefix}Create`].insertAdjacentHTML('beforeend', - `build`); + `build`); // show and highlight export button resultsList[`${keyPrefix}Export`].classList.toggle('hide', false); resultsList[`${keyPrefix}Export`].classList.toggle('pulse', true); @@ -151,6 +152,15 @@ function resultsDataRecievedCallback(resultsList, detail) { handleType('fullResults', 'Results'); } else if (detail.type ==='sub-results') { handleType('subResults', 'Sub-Results'); + } else if (detail.type ==='inspect-results') { + if (resultsList.addToSubResultsIdsToShow.size === 0) { + /** + * Prevent create sub results button from being activated if it is disabled + * and no matches have been marked by the user for sub results creation. + */ + resultsList.subResultsCreate.classList.toggle('disabled', true); + } + resultsList.showMatchContext(detail.results); } } 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 8c322e63..469bcaf2 100644 --- a/web/app/static/js/modules/corpus_analysis/view/listeners.js +++ b/web/app/static/js/modules/corpus_analysis/view/listeners.js @@ -72,8 +72,8 @@ function recieveClientNotification(eventType, resultsList) { console.info('View recieved notification:', caseIdentifier); // execute callbacks console.info(event.detail); - resultsDataRecievedCallback(resultsList, event.detail); enableElementsGeneralCallback(resultsList, event.detail); + resultsDataRecievedCallback(resultsList, event.detail); break; default: console.error('Recieved unkown notification case identifier from Client'); diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2 index c82c6310..86c4978f 100644 --- a/web/app/templates/corpora/analyse_corpus.html.j2 +++ b/web/app/templates/corpora/analyse_corpus.html.j2 @@ -219,8 +219,20 @@ document.addEventListener("DOMContentLoaded", () => { '#sub-results-export', '#download-results-json', '#query-results-download-modal', - '#query-results-table' + '#query-results-table', + '#display-options-form-expert_mode', ]); + /** + * The following event Listener handles the expert mode switch for the list + */ + resultsList.displayOptionsFormExpertMode.onchange = (event) => { + if (event.target.checked) { + resultsList.expertModeOn('query-display', results); + } else { + resultsList.expertModeOff('query-display'); + } + }; + /** * The following event Listener handles the add-btn and the inspect-btn * onclick events via bubbleing. @@ -235,8 +247,9 @@ document.addEventListener("DOMContentLoaded", () => { resultsList.addToSubResults(dataIndex); } }) + /** - * Display events: Following event listeners are handleing the + * Display events: Following event listeners are handling the * live update of hits per page if hits per page value is changed and the * context size of every match. */ @@ -246,6 +259,7 @@ document.addEventListener("DOMContentLoaded", () => { resultsList.displayOptionsFormResultContext.onchange = () => { resultsList.changeContext(); }; + /** * The following event listener handel the Show metadata button and its * functionality. Before the needed modal is initialized. @@ -270,7 +284,7 @@ document.addEventListener("DOMContentLoaded", () => { } } }; - resultsList.addToSubResults.on + /** * The following event listeners are handeling the data export. * 1. Create full-results diff --git a/web/app/templates/modals/context_modal.html.j2 b/web/app/templates/modals/context_modal.html.j2 index 82b133d7..2b57a83e 100644 --- a/web/app/templates/modals/context_modal.html.j2 +++ b/web/app/templates/modals/context_modal.html.j2 @@ -8,7 +8,7 @@
Display
-
+
{{ inspect_display_options_form.expert_mode_inspect.label.text }}
-
+
{{ inspect_display_options_form.highlight_sentences.label.text }}
- {% if not imported %}
Create
Add to Sub Results -
+
+ {# The needed button is created and added via javascript #}
- {% endif %}