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 09566c29..122add1e 100644 --- a/web/app/static/js/modules/corpus_analysis/client/Client.js +++ b/web/app/static/js/modules/corpus_analysis/client/Client.js @@ -16,7 +16,7 @@ class Client { this.logging = logging; this.requestQueryProgress = 0; this.socket = socket; - this.socketEventListeners = {}; + this.eventListeners = {}; this.connected = false; @@ -44,9 +44,9 @@ class Client { } // Registers one or more SocketEventListeners to the Client. - setSocketEventListeners(socketEventListeners) { - for (let socketEventListener of socketEventListeners) { - this.socketEventListeners[socketEventListener.type] = socketEventListener; + setSocketEventListeners(eventListeners) { + for (let eventListener of eventListeners) { + this.eventListeners[eventListener.type] = eventListener; } } @@ -55,7 +55,7 @@ class Client { * type strings because they double as the socket event event names. */ loadSocketEventListeners() { - for (let [type, listener] of Object.entries(this.socketEventListeners)) { + for (let [type, listener] of Object.entries(this.eventListeners)) { listener.listenerFunction(type, this); } } @@ -66,8 +66,8 @@ class Client { */ notifyView(caseIdentifier, detailObject={}) { detailObject.caseIdentifier = caseIdentifier; - const event = new CustomEvent('notify', { detail: detailObject }); - console.info('Dispatching Notification:', caseIdentifier); + const event = new CustomEvent('notify-view', { detail: detailObject }); + console.info('Client dispatching Notification:', caseIdentifier); document.dispatchEvent(event); } @@ -105,16 +105,38 @@ class Client { 'socket.emit for the query', queryStr); this.socket.emit('corpus_analysis_query', queryStr); } + + // create results data either from all results or from all marked sub results + 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) { + tmp_first_cpos.push(results.data.matches[dataIndex].c[0]); + tmp_last_cpos.push(results.data.matches[dataIndex].c[1]); + } + nopaque.socket.emit("corpus_analysis_inspect_match", + { + type: resultsType, + data_indexes: dataIndexes, + first_cpos: tmp_first_cpos, + last_cpos: tmp_last_cpos, + }); + } + } + /** * This class is used to create an SocketEventListener. * Input are an identifying type string, the listener function and callbacks * which will be executed as part of the listener function. The identifying * type string is also used as the socket event event identifier. */ -class SocketEventListener { - constructor(type, listenerFunction, args=null) { +class ClientEventListener { + constructor(type, listenerFunction) { this.listenerCallbacks = {}; this.listenerFunction = listenerFunction; this.type = type; @@ -127,8 +149,8 @@ class SocketEventListener { } } - /** Shorthand to execute all registered callbacks with same args in insertion - * order. + /** Shorthand to execute all registered callbacks with same defaultArgs + * in insertion order. * NOTE: * Since ECMAScript 2015, objects do preserve creation order for * string and Symbol keys. In JavaScript engines that comply with the @@ -136,11 +158,18 @@ class SocketEventListener { * yield the keys in order of insertion. * So all modern Browsers. */ - executeCallbacks(payload) { + executeCallbacks(defaultArgs) { for (let [type, listenerCallback] of Object.entries(this.listenerCallbacks)) { - listenerCallback.callbackFunction(payload, ...listenerCallback.args); + listenerCallback.callbackFunction(...defaultArgs, + ...listenerCallback.args); } } + // use this if you only want to execute a specific registered callback + executeCallback(defaultArgs, type) { + let listenerCallback = this.listenerCallbacks[type]; + listenerCallback.callbackFunction(...defaultArgs, + ...listenerCallback.args); + } } /** @@ -159,6 +188,6 @@ class ListenerCallback { // export Classes from this module export { Client, - SocketEventListener, + ClientEventListener, ListenerCallback, }; \ No newline at end of file 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 afda9fe7..145bdaa5 100644 --- a/web/app/static/js/modules/corpus_analysis/client/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/client/callbacks.js @@ -46,116 +46,24 @@ function saveQueryData(args) { } } -function querySetup(payload, client) { - // deletes old data from query issued before this new query - client.results.clearAll(); - // load necessary HTMLElements with selectory syntax and save them as fields - client.getHTMLElements(['#query-progress-bar', '#query-results-user-feedback', - '#recieved-match-count', '#total-match-count', - '#text-lookup-count', '#text-lookup-titles', - '#query-results-create', '#add-to-sub-results']); - client.recievedMatchCount.textContent = 0; - client.totalMatchCount.textContent = `${payload.match_count}`; - client.queryResultsUserFeedback.classList.toggle('hide'); - client.queryProgressBar.classList.toggle('hide'); - client.queryProgressBar.lastElementChild.style.width = '0%'; - if (client.dynamicMode) { - client.addToSubResults.toggleAttribute('disabled'); - client.queryResultsCreate.classList.toggle('disabled'); - } +function getResultsData(args) { + let [resultsType, dataIndexes, client, results, rest] = arguments; + client.notifyView('results-data-recieving'); + client.getResultsData(resultsType, dataIndexes, results); } -/** - * This callback should be registered to the SocketEventListener 'recieveQueryData' - * It takes the incoming chunk and renders the results using the - * results.jsList object. It can either handle live incoming data chunks or - * already loaded/imported results data. - */ -function queryRenderResults(payload, client) { - client.getHTMLElements(['#recieved-match-count', '#match-count', - '#display-options-form-expert_mode']); - const renderResults = (data) => { - /** - * resultItem saves the incoming chunk matches as objects to add those later - * to the client.results.jsList - */ - let resultItems = []; - // get infos for full match row - for (let [index, match] of data.matches.entries()) { - resultItems.push({...match, ...{'index': index + client.results.data.matches.length}}); - } - client.results.jsList.add(resultItems, (items) => { - for (let item of items) { - item.elm = client.results.jsList.createResultRowElement(item, data); - } - }); - } - if (client.dynamicMode) { - if (payload.chunk.cpos_ranges == true) { - client.results.data['cpos_ranges'] = true; - } else { - client.results.data['cpos_ranges'] = false; - } - renderResults(payload.chunk); - helperQueryRenderResults(payload, client); - console.info('Result progress is:', client.requestQueryProgress); - if (client.requestQueryProgress === 100) { - /** - * activate, hide or show elements if all reults have been recieved - * also load some new elements taht have not ben loaded before - */ - client.queryProgressBar.classList.toggle('hide'); - client.queryResultsUserFeedback.classList.toggle('hide'); - client.queryResultsCreate.classList.toggle('disabled'); - client.addToSubResults.toggleAttribute('disabled'); - // addToSubResultsElement.removeAttribute("disabled"); - // // inital expert mode check and sub results activation - // client.results.jsList.activateInspect(); - // if (addToSubResultsElement.checked) { - // client.results.jsList.activateAddToSubResults(); - // } - // if (expertModeSwitchElement.checked) { - // client.results.jsList.expertModeOn("query-display"); - // } - } - } else if (!client.dynamicMode) { - renderResults(payload); - helperQueryRenderResults({'chunk': payload}, client); - client.queryProgressBar.classList.toggle('hide'); - client.queryResultsUserFeedback.classList.toggle('hide'); - client.results.jsList.activateInspect(); - if (client.displayOptionsFormExpertMode.checked) { - client.results.jsList.expertModeOn("query-display"); - } - } -} - -/** - * Helper function that saves result data into the client.results.data object. - * Also does some showing and hiding of Elements and user feedback text. - */ -function helperQueryRenderResults (payload, client) { - // updating table on finished item creation callback via createResultRowElement - client.results.jsList.update(); - client.results.jsList.changeContext(); // sets lr context on first result load - // incorporating new chunk results into full results - client.results.data.matches.push(...payload.chunk.matches); - client.results.data.addData(payload.chunk.cpos_lookup, 'cpos_lookup'); - client.results.data.addData(payload.chunk.text_lookup, 'text_lookup'); - // complete metaData - // client.results.metaData.add(); - // show user current and total match count - client.recievedMatchCount.textContent = `${client.results.data.matches.length}`; - client.textLookupCount.textContent = `${Object.keys(client.results.data.text_lookup).length}`; - let titles = new Array(); - for (let [key, value] of Object.entries(client.results.data.text_lookup)) { - titles.push(`${value.title} (${value.publishing_year})`); - }; - client.textLookupTitles.textContent = `${titles.join(", ")}`; - // update progress bar and requestQueryProgress - client.queryProgressBar.lastElementChild.style.width = `${payload.progress}%`; - client.requestQueryProgress = payload.progress; +function saveResultsData(args) { + let [payload, client, results, rest] = arguments; + // code to save results data depending on type + console.info('Results data has been saved.'); + client.notifyView('results-data-recieved'); } // export callbacks -export { prepareQueryData, saveMetaData, saveQueryData }; \ No newline at end of file +export { + prepareQueryData, + saveMetaData, + saveQueryData, + getResultsData, + saveResultsData, +}; \ No newline at end of file diff --git a/web/app/static/js/modules/corpus_analysis/client/listeners.js b/web/app/static/js/modules/corpus_analysis/client/listeners.js index 681bf6c6..11bfc22b 100644 --- a/web/app/static/js/modules/corpus_analysis/client/listeners.js +++ b/web/app/static/js/modules/corpus_analysis/client/listeners.js @@ -1,9 +1,14 @@ /** * This file contains the listener functions which can be assigned to the * coprus_analysis client. So that the incoming data/status informations will - * be handled. + * be handled. There are several listeners listening for socket .io events. + * Further below one javascript custom event listener is specified. This + * listener listens for javascript custom events which are being dispatched by + * the View (resultsList). */ +// Listeners for socket io events + /** * Recieves a corpus analysis connected signal via socket.io. */ @@ -50,7 +55,7 @@ function recieveMetaData(type, client) { console.info(`corpus_analysis_meta_data: ${response.code} - ${response.msg}`); console.info(response); // executing the registered callbacks - client.socketEventListeners[type].executeCallbacks(response.payload); + client.eventListeners[type].executeCallbacks([response.payload]); console.groupEnd(); } else { console.group('Failed to recieve meta data.'); @@ -81,7 +86,7 @@ function recieveQueryStatus(type, client) { console.info(`corpus_analysis_query: ${response.code} - ${response.msg}`); console.info(response); // executing the registered callbacks - client.socketEventListeners[type].executeCallbacks(response.payload); + client.eventListeners[type].executeCallbacks([response.payload]); console.groupEnd(); } else { console.group('corpus_analysis_query: Client failed recieving', @@ -113,7 +118,7 @@ function recieveQueryData(type, client) { /** * Execute registered callbacks and notify View. */ - client.socketEventListeners[type].executeCallbacks(response.payload); + client.eventListeners[type].executeCallbacks([response.payload]); console.info('Added chunk data to results.data.'); console.groupEnd(); } else { @@ -125,18 +130,72 @@ function recieveQueryData(type, client) { } }); } else { - console.group('corpus_analysis_query_results: Loading query data.') + console.group('corpus_analysis_query_results: Loading query data.'); console.info('Client loading imported query data from database.'); // executing the registered callbacks - client.socketEventListeners[type].executeCallbacks(); + client.eventListeners[type].executeCallbacks(); console.groupEnd(); } } +/** + * Recieves the data requested by the create Results or sub results button + */ +function recieveResultsData(type, client) { + client.socket.on(type, (response) => { + /** + * Check if request for session was OK. + * If OK execute registered callbacks and notify View. + */ + if (response.code === 200) { + console.group('Client recieving results data') + console.info('corpus_analysis_inspect_match: Client recieving results data', + 'via socket.on'); + console.info(`corpus_analysis_inspect_match: ${response.code} - ${response.msg}`); + console.info(response); + // executing the registered callbacks + client.eventListeners[type].executeCallbacks([response.payload]); + console.groupEnd(); + } else { + console.group('Failed to recieve results data.'); + console.error('corpus_analysis_inspect_match: Client failed to recieve', + 'results data via socket.on'); + let errorText = `Error ${response.payload.code} - ${response.payload.msg}`; + console.error(`corpus_analysis_inspect_match: ${errorText}`); + console.groupEnd(); + } + }); +} + +/* + * This is the javascript custom event listener, listening for events + * dispatched by the View. + */ +function recieveViewNotification(type, client) { + document.addEventListener(type, (event) => { + let caseIdentifier = event.detail.caseIdentifier; + switch(caseIdentifier) { + case 'get-results': + console.info('Client getting full results for export.'); + // execute callback or functions + client.eventListeners[type].executeCallback([event.detail.resultsType, + event.detail.dataIndexes], + caseIdentifier); + break + default: + console.error('Recieved unkown notification case identifier from View'); + // do something to not crash the analysis session? + // maybe unnecessary + } + }); +} + // export listeners from this module export { recieveConnected, recieveMetaData, recieveQueryStatus, - recieveQueryData + recieveQueryData, + recieveViewNotification, + recieveResultsData, }; \ No newline at end of file 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 3e8edf7e..c2f0c3bc 100644 --- a/web/app/static/js/modules/corpus_analysis/model/Results.js +++ b/web/app/static/js/modules/corpus_analysis/model/Results.js @@ -8,6 +8,7 @@ class Results { constructor() { this.data = new Data(); this.metaData = new MetaData(); + this.fullResultsData = new Data(); this.subResultsData = new Data(); console.info('Initialized the Results object.'); } @@ -15,12 +16,12 @@ class Results { init() { this.data.init(); this.metaData.init(); + this.fullResultsData = new Data(); this.subResultsData.init(); } } - class Data { // Sets empty object structure. Also usefull to delete old results. // matchCount default is 0 @@ -94,29 +95,6 @@ class Data { this.download(downloadElement, dataStr, resultFilename, "text/json", ".json") } - // create results data either from all results or from al lmarked sub results - createResultsData(type) { - // deactivate inspect, because cqp server cannot handle multiple requests - results.jsList.deactivateInspect(); - activateInspectInteraction.setCallback("noCheck", - results.jsList.deactivateInspect, - results.jsList); - // set flag that results are being created to avoid reactivation of - // sub results creation if marked matches are changed - resultCreationRunning = true; - console.log(resultCreationRunning); - if (type === "sub-results") { - resultsCreateElement.classList.add("disabled"); // cqp server cannot handle more than one request at a time. Thus we deactivate the resultsCreateElement - let tmp = [...results.jsList.addToSubResultsIdsToShow].sort(function(a, b){return a-b}); - let dataIndexes = []; - tmp.forEach((index) => dataIndexes.push(index - 1)); - results.jsList.getMatchWithContext(dataIndexes, "sub-results"); - } else if (type === "results") { - subResultsCreateElement.classList.add("disabled"); // cqp server cannot handle more than one request at a time. Thus we deactivate the subResultsCreateElement - let dataIndexes = [...Array(results.data.match_count).keys()]; - results.jsList.getMatchWithContext(dataIndexes, "results"); - } - } } class MetaData { 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 146913ed..6eb5e5c5 100644 --- a/web/app/static/js/modules/corpus_analysis/view/ResultsView.js +++ b/web/app/static/js/modules/corpus_analysis/view/ResultsView.js @@ -2,7 +2,7 @@ * This class implements a NotificationListener that is listening for the * specified */ -class NotificationListener { +class ViewEventListener { constructor(type, listenerFunction) { this.listenerFunction = listenerFunction; this.type = type; @@ -61,7 +61,7 @@ class ResultsList extends List { * class field in the ResultsList object with the query selector * string as the key. The selector will be converted to a valid JavaScript * Field name i. e. #html-id-string -> this.htmlIdString - * The value will be the identifed element fetched with the querySelector + * The value will be the identifed element or elements fetched with the querySelector * method. */ // TODO: multipleResults=false, atattchSomeCallback=false ? @@ -73,6 +73,7 @@ class ResultsList extends List { element = document.querySelector(selector); } else { elements = document.querySelectorAll(selector); + elements = [...elements]; } let cleanKey = []; selector = selector.replace(/_/g, '-'); @@ -106,6 +107,17 @@ class ResultsList extends List { } } + /** + * This functions sends events to the Client to trigger specific functions to + * trigger new data requests from the server. + */ + notifyClient(caseIdentifier, detailObject={}) { + detailObject.caseIdentifier = caseIdentifier; + const event = new CustomEvent('notify-client', { detail: detailObject }); + console.info('Client dispatching Notification:', caseIdentifier); + document.dispatchEvent(event); + } + /** * Creates cpos either from ranges or not. */ @@ -255,25 +267,6 @@ class ResultsList extends List { } } - // Triggers emit to get full match context from server for a number of - // matches identified by their data_index. - getMatchWithContext(dataIndexes, type) { - let tmp_first_cpos = []; - let tmp_last_cpos = []; - for (let dataIndex of dataIndexes) { - tmp_first_cpos.push(results.data.matches[dataIndex].c[0]); - tmp_last_cpos.push(results.data.matches[dataIndex].c[1]); - } - nopaque.socket.emit("corpus_analysis_inspect_match", - { - type: type, - data_indexes: dataIndexes, - first_cpos: tmp_first_cpos, - last_cpos: tmp_last_cpos, - } - ); - } - // ###### Functions to inspect one match, to show more details ###### // activate inspect buttons if progress is 100 activateInspect() { @@ -786,7 +779,7 @@ class ResultsList extends List { return matchRowElement } - // creates the HTML table code for the metadata vie in the corpus analysis interface + // creates the HTML table code for the metadata view in the corpus analysis interface createMetaDataForModal(metaDataObject) { let html = `
@@ -838,12 +831,12 @@ class ResultsList extends List { } // Creates the text details for the texts shown in the corpus analysis metadata modal. - createTextDetails(metaDataObject) { + createTextDetails(metaData) { let metadataKey = event.target.dataset.metadataKey; let textKey = event.target.dataset.textKey; - let textData = metaDataObject[metadataKey][textKey]; - let bibliographicData = document.getElementById(`bibliographic-data-${metadataKey}-${textKey}`); - bibliographicData.innerHTML = ""; + let textData = metaData[metadataKey][textKey]; + let bibliographicData = document.querySelector(`#bibliographic-data-${metadataKey}-${textKey}`); + bibliographicData.textContent = ''; for (let [key, value] of Object.entries(textData)) { bibliographicData.insertAdjacentHTML("afterbegin", ` @@ -854,4 +847,4 @@ class ResultsList extends List { }; // export classses -export { NotificationListener, ResultsList }; \ No newline at end of file +export { ViewEventListener, ResultsList }; \ No newline at end of file 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 46b67fb7..d3133754 100644 --- a/web/app/static/js/modules/corpus_analysis/view/callbacks.js +++ b/web/app/static/js/modules/corpus_analysis/view/callbacks.js @@ -98,8 +98,14 @@ function queryDataRecievedCallback(resultsList, detail) { // show or enable some things for the user resultsList.queryResultsCreate.classList.toggle('disabled'); resultsList.addToSubResults.removeAttribute("disabled"); +} +function resultsDataRecievingCallback(resultsList, detail) { + // hide or disable elments taht would trigger another cqp data request +} +function resultsDataRecievedCallback(resultsList, detail) { + // hide or show the right stuff } // export the callbacks @@ -110,4 +116,6 @@ export { queryDataPreparingCallback, queryDataRecievingCallback, queryDataRecievedCallback, + resultsDataRecievingCallback, + resultsDataRecievedCallback, }; \ No newline at end of file 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 b549a2f3..ff849219 100644 --- a/web/app/static/js/modules/corpus_analysis/view/listeners.js +++ b/web/app/static/js/modules/corpus_analysis/view/listeners.js @@ -14,43 +14,55 @@ import { queryDataPreparingCallback, queryDataRecievingCallback, queryDataRecievedCallback, + resultsDataRecievingCallback, + resultsDataRecievedCallback, } from './callbacks.js'; -function recieveNotification(eventType, resultsList) { +function recieveClientNotification(eventType, resultsList) { document.addEventListener(eventType, (event) => { let caseIdentifier = event.detail.caseIdentifier; switch (caseIdentifier) { case 'connecting': - console.info('Recieved notification:', caseIdentifier); + console.info('View recieved notification:', caseIdentifier); connectingCallback(resultsList, event.detail); // execute callback break; case 'connected': - console.info('Recieved notification:', caseIdentifier); + console.info('View recieved notification:', caseIdentifier); connectedCallback(resultsList, event.detail); break; case 'connecting-failed': - console.info('Recieved notification:', caseIdentifier); + console.info('View recieved notification:', caseIdentifier); // execute callback connectingFaildeCallback(resultsList, event.detail); break; case 'query-data-prepareing': - console.info('Recieved notification:', caseIdentifier); + console.info('View recieved notification:', caseIdentifier); // execute callback queryDataPreparingCallback(resultsList, event.detail); break; case 'query-data-recieving': - console.info('Recieved notification:', caseIdentifier); + console.info('View recieved notification:', caseIdentifier); // execute callback queryDataRecievingCallback(resultsList, event.detail); break; case 'query-data-recieved': - console.info('Recieved notification:', caseIdentifier); + console.info('View recieved notification:', caseIdentifier); // execute callback queryDataRecievedCallback(resultsList, event.detail); break; + case 'results-data-recieving': + console.info('View recieved notification:', caseIdentifier); + // execute callback + resultsDataRecievedCallback(resultsList, event.detail); + break; + case 'results-data-recieved': + console.info('View recieved notification:', caseIdentifier); + // execute callback + resultsDataRecievedCallback(resultsList, event.detail); + break; default: - console.error('Recieved unkown notification case identifier'); + console.error('Recieved unkown notification case identifier from Client'); // do something to not crash the analysis session? // maybe unnecessary } @@ -58,4 +70,4 @@ function recieveNotification(eventType, resultsList) { } // export listeners -export { recieveNotification }; \ No newline at end of file +export { recieveClientNotification }; \ No newline at end of file diff --git a/web/app/static/js/modules/corpus_analysis/view/spinner.js b/web/app/static/js/modules/corpus_analysis/view/spinner.js new file mode 100644 index 00000000..5c4b7422 --- /dev/null +++ b/web/app/static/js/modules/corpus_analysis/view/spinner.js @@ -0,0 +1,17 @@ +// loading spinner animation HTML +const loadingSpinnerHTML = ` +
+
+
+
+
+
+
+
+
+
+
+ `; + +//export +export { loadingSpinnerHTML }; diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2 index 3b932839..09e7b6fb 100644 --- a/web/app/templates/corpora/analyse_corpus.html.j2 +++ b/web/app/templates/corpora/analyse_corpus.html.j2 @@ -70,35 +70,45 @@ * First Phase: * Document content is loaded and scripts are being imported and executed. */ + // import Client classes import { Client, - SocketEventListener, + ClientEventListener, ListenerCallback, } from '../../static/js/modules/corpus_analysis/client/Client.js'; +// import client listener functions import { recieveConnected, recieveMetaData, recieveQueryStatus, recieveQueryData, + recieveViewNotification, + recieveResultsData, } from '../../static/js/modules/corpus_analysis/client/listeners.js'; -import { - Results, -} from '../../static/js/modules/corpus_analysis/model/Results.js'; +// import client listener callbacks import { prepareQueryData, saveQueryData, saveMetaData, + getResultsData, + saveResultsData, } from '../../static/js/modules/corpus_analysis/client/callbacks.js'; import { - NotificationListener, + Results, +} from '../../static/js/modules/corpus_analysis/model/Results.js'; +import { + ViewEventListener, ResultsList, } from '../../static/js/modules/corpus_analysis/view/ResultsView.js'; import { - recieveNotification, + recieveClientNotification, } from '../../static/js/modules/corpus_analysis/view/listeners.js'; import { scrollToTop, } from '../../static/js/modules/corpus_analysis/view/scrollToTop.js' +import { + loadingSpinnerHTML, +} from '../../static/js/modules/corpus_analysis/view/spinner.js' /** * Second Phase: @@ -120,44 +130,59 @@ document.addEventListener("DOMContentLoaded", () => { let resultsList = new ResultsList('result-list', ResultsList.options); /** * Register listeners listening to socket.io events and their callbacks - * Afterwards load them. + * Afterwards load them. Also registers listeners listening for custom + * javascript events. */ - const listenForConnected = new SocketEventListener('corpus_analysis_init', + const listenForConnected = new ClientEventListener('corpus_analysis_init', recieveConnected); - const listenForMetaData = new SocketEventListener('corpus_analysis_meta_data', + const listenForMetaData = new ClientEventListener('corpus_analysis_meta_data', recieveMetaData); const metaDataCallback = new ListenerCallback('corpus_analysis_meta_data', saveMetaData, [client, results]); listenForMetaData.setCallbacks([metaDataCallback]); - const listenForQueryStatus = new SocketEventListener('corpus_analysis_query', + const listenForQueryStatus = new ClientEventListener('corpus_analysis_query', recieveQueryStatus); const queryStatusCallback = new ListenerCallback('corpus_analysis_query', prepareQueryData, [client, results]); listenForQueryStatus.setCallbacks([queryStatusCallback]); - const listenForQueryData = new SocketEventListener('corpus_analysis_query_results', + const listenForQueryData = new ClientEventListener('corpus_analysis_query_results', recieveQueryData); const queryDataCallback = new ListenerCallback('corpus_analysis_query_results', saveQueryData, [client, results]); listenForQueryData.setCallbacks([queryDataCallback]); + const listenForResults = new ClientEventListener('corpus_analysis_inspect_match', + recieveResultsData); + const resultsDataCallback = new ListenerCallback('corpus_analysis_inspect_match', + saveResultsData, + [client, results]); + listenForResults.setCallbacks([resultsDataCallback]); + // listen for javascript custom notifications + const listenForViewNotification = new ClientEventListener('notify-client', + recieveViewNotification); + const getResultsCallback = new ListenerCallback('get-results', + getResultsData, + [client, results]); + listenForViewNotification.setCallbacks([getResultsCallback]); client.setSocketEventListeners([listenForConnected, listenForQueryStatus, listenForQueryData, - listenForMetaData]); + listenForMetaData, + listenForViewNotification, + listenForResults]); client.loadSocketEventListeners(); /** * Register resultsList listeners listening to nitification events. */ - const listenForNotification = new NotificationListener('notify', - recieveNotification); - resultsList.setNotificationListeners([listenForNotification]); + const listenForClientNotification = new ViewEventListener('notify-view', + recieveClientNotification); + resultsList.setNotificationListeners([listenForClientNotification]); resultsList.loadNotificationListeners(); // Connect client to server client.notifyView('connecting'); client.connect(); - // Send a query and recieve its answer data let queryFormElement = document.querySelector('#query-form'); queryFormElement.addEventListener('submit', (event) => { @@ -181,20 +206,66 @@ document.addEventListener("DOMContentLoaded", () => { results.data.getQueryStr(queryFormElement); client.query(results.data.query); }); - /** - * Display events - * 1. live update of hits per page if hits per page value is changed - */ + // Get all needed HTMLElements for the following event listeners resultsList.getHTMLElements([ '#display-options-form-results_per_page', - '#display-options-form-result_context' + '#display-options-form-result_context', + '#show-meta-data', + '#meta-data-modal', + '#meta-data-modal-content', + '#query-results-create' + ]); + /** + * Display events: Following event listeners are handleing the + * live update of hits per page if hits per page value is changed and the + * context size of every match. + */ resultsList.displayOptionsFormResultsPerPage.onchange = () => { resultsList.changeHitsPerPage(); }; resultsList.displayOptionsFormResultContext.onchange = () => { resultsList.changeContext(); }; + /** + * 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 + }); + resultsList.showMetaData.onclick = () => { + resultsList.metaDataModalContent.textContent = ''; + let table = resultsList.createMetaDataForModal(results.metaData); + resultsList.metaDataModalContent.insertAdjacentHTML('afterbegin', table); + resultsList.metaDataModal.open(); + let collapsibles = resultsList.metaDataModalContent.querySelectorAll(".text-metadata"); + for (let collapsible of collapsibles) { + collapsible.onclick = () => { + let elems = resultsList.metaDataModalContent.querySelectorAll('.collapsible'); + let instances = M.Collapsible.init(elems, {accordion: false}); + resultsList.createTextDetails(results.metaData); + } + } + }; + /** + * The following event listeners are handeling the data export. + */ + resultsList.queryResultsCreate.onclick = () => { + resultsList.queryResultsCreate.querySelector('i').classList.toggle('hide'); + resultsList.queryResultsCreate.innerText = 'Creating...'; + resultsList.queryResultsCreate.insertAdjacentHTML('afterbegin', + loadingSpinnerHTML); + resultsList.notifyClient('get-results', { resultsType: 'full-results', + dataIndexes: [...Array(results.data.match_count).keys()]}); + } diff --git a/web/app/templates/interactions/infos.html.j2 b/web/app/templates/interactions/infos.html.j2 index 409c5338..43cf41bb 100644 --- a/web/app/templates/interactions/infos.html.j2 +++ b/web/app/templates/interactions/infos.html.j2 @@ -7,7 +7,7 @@ result.-->
-