Add logic for data export etc.

This commit is contained in:
Stephan Porada 2020-08-26 16:55:24 +02:00
parent 763183435d
commit 64f8f82fe8
10 changed files with 286 additions and 211 deletions

View File

@ -16,7 +16,7 @@ class Client {
this.logging = logging; this.logging = logging;
this.requestQueryProgress = 0; this.requestQueryProgress = 0;
this.socket = socket; this.socket = socket;
this.socketEventListeners = {}; this.eventListeners = {};
this.connected = false; this.connected = false;
@ -44,9 +44,9 @@ class Client {
} }
// Registers one or more SocketEventListeners to the Client. // Registers one or more SocketEventListeners to the Client.
setSocketEventListeners(socketEventListeners) { setSocketEventListeners(eventListeners) {
for (let socketEventListener of socketEventListeners) { for (let eventListener of eventListeners) {
this.socketEventListeners[socketEventListener.type] = socketEventListener; this.eventListeners[eventListener.type] = eventListener;
} }
} }
@ -55,7 +55,7 @@ class Client {
* type strings because they double as the socket event event names. * type strings because they double as the socket event event names.
*/ */
loadSocketEventListeners() { loadSocketEventListeners() {
for (let [type, listener] of Object.entries(this.socketEventListeners)) { for (let [type, listener] of Object.entries(this.eventListeners)) {
listener.listenerFunction(type, this); listener.listenerFunction(type, this);
} }
} }
@ -66,8 +66,8 @@ class Client {
*/ */
notifyView(caseIdentifier, detailObject={}) { notifyView(caseIdentifier, detailObject={}) {
detailObject.caseIdentifier = caseIdentifier; detailObject.caseIdentifier = caseIdentifier;
const event = new CustomEvent('notify', { detail: detailObject }); const event = new CustomEvent('notify-view', { detail: detailObject });
console.info('Dispatching Notification:', caseIdentifier); console.info('Client dispatching Notification:', caseIdentifier);
document.dispatchEvent(event); document.dispatchEvent(event);
} }
@ -105,16 +105,38 @@ class Client {
'socket.emit for the query', queryStr); 'socket.emit for the query', queryStr);
this.socket.emit('corpus_analysis_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. * This class is used to create an SocketEventListener.
* Input are an identifying type string, the listener function and callbacks * Input are an identifying type string, the listener function and callbacks
* which will be executed as part of the listener function. The identifying * which will be executed as part of the listener function. The identifying
* type string is also used as the socket event event identifier. * type string is also used as the socket event event identifier.
*/ */
class SocketEventListener { class ClientEventListener {
constructor(type, listenerFunction, args=null) { constructor(type, listenerFunction) {
this.listenerCallbacks = {}; this.listenerCallbacks = {};
this.listenerFunction = listenerFunction; this.listenerFunction = listenerFunction;
this.type = type; this.type = type;
@ -127,8 +149,8 @@ class SocketEventListener {
} }
} }
/** Shorthand to execute all registered callbacks with same args in insertion /** Shorthand to execute all registered callbacks with same defaultArgs
* order. * in insertion order.
* NOTE: * NOTE:
* Since ECMAScript 2015, objects do preserve creation order for * Since ECMAScript 2015, objects do preserve creation order for
* string and Symbol keys. In JavaScript engines that comply with the * string and Symbol keys. In JavaScript engines that comply with the
@ -136,11 +158,18 @@ class SocketEventListener {
* yield the keys in order of insertion. * yield the keys in order of insertion.
* So all modern Browsers. * So all modern Browsers.
*/ */
executeCallbacks(payload) { executeCallbacks(defaultArgs) {
for (let [type, listenerCallback] of Object.entries(this.listenerCallbacks)) { 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 Classes from this module
export { export {
Client, Client,
SocketEventListener, ClientEventListener,
ListenerCallback, ListenerCallback,
}; };

View File

@ -46,116 +46,24 @@ function saveQueryData(args) {
} }
} }
function querySetup(payload, client) { function getResultsData(args) {
// deletes old data from query issued before this new query let [resultsType, dataIndexes, client, results, rest] = arguments;
client.results.clearAll(); client.notifyView('results-data-recieving');
// load necessary HTMLElements with selectory syntax and save them as fields client.getResultsData(resultsType, dataIndexes, results);
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 saveResultsData(args) {
* This callback should be registered to the SocketEventListener 'recieveQueryData' let [payload, client, results, rest] = arguments;
* It takes the incoming chunk and renders the results using the // code to save results data depending on type
* results.jsList object. It can either handle live incoming data chunks or console.info('Results data has been saved.');
* already loaded/imported results data. client.notifyView('results-data-recieved');
*/
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;
} }
// export callbacks // export callbacks
export { prepareQueryData, saveMetaData, saveQueryData }; export {
prepareQueryData,
saveMetaData,
saveQueryData,
getResultsData,
saveResultsData,
};

View File

@ -1,9 +1,14 @@
/** /**
* This file contains the listener functions which can be assigned to the * This file contains the listener functions which can be assigned to the
* coprus_analysis client. So that the incoming data/status informations will * 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. * 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(`corpus_analysis_meta_data: ${response.code} - ${response.msg}`);
console.info(response); console.info(response);
// executing the registered callbacks // executing the registered callbacks
client.socketEventListeners[type].executeCallbacks(response.payload); client.eventListeners[type].executeCallbacks([response.payload]);
console.groupEnd(); console.groupEnd();
} else { } else {
console.group('Failed to recieve meta data.'); 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(`corpus_analysis_query: ${response.code} - ${response.msg}`);
console.info(response); console.info(response);
// executing the registered callbacks // executing the registered callbacks
client.socketEventListeners[type].executeCallbacks(response.payload); client.eventListeners[type].executeCallbacks([response.payload]);
console.groupEnd(); console.groupEnd();
} else { } else {
console.group('corpus_analysis_query: Client failed recieving', console.group('corpus_analysis_query: Client failed recieving',
@ -113,7 +118,7 @@ function recieveQueryData(type, client) {
/** /**
* Execute registered callbacks and notify View. * 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.info('Added chunk data to results.data.');
console.groupEnd(); console.groupEnd();
} else { } else {
@ -125,18 +130,72 @@ function recieveQueryData(type, client) {
} }
}); });
} else { } 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.'); console.info('Client loading imported query data from database.');
// executing the registered callbacks // executing the registered callbacks
client.socketEventListeners[type].executeCallbacks(); client.eventListeners[type].executeCallbacks();
console.groupEnd(); 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 listeners from this module
export { export {
recieveConnected, recieveConnected,
recieveMetaData, recieveMetaData,
recieveQueryStatus, recieveQueryStatus,
recieveQueryData recieveQueryData,
recieveViewNotification,
recieveResultsData,
}; };

View File

@ -8,6 +8,7 @@ class Results {
constructor() { constructor() {
this.data = new Data(); this.data = new Data();
this.metaData = new MetaData(); this.metaData = new MetaData();
this.fullResultsData = new Data();
this.subResultsData = new Data(); this.subResultsData = new Data();
console.info('Initialized the Results object.'); console.info('Initialized the Results object.');
} }
@ -15,12 +16,12 @@ class Results {
init() { init() {
this.data.init(); this.data.init();
this.metaData.init(); this.metaData.init();
this.fullResultsData = new Data();
this.subResultsData.init(); this.subResultsData.init();
} }
} }
class Data { class Data {
// Sets empty object structure. Also usefull to delete old results. // Sets empty object structure. Also usefull to delete old results.
// matchCount default is 0 // matchCount default is 0
@ -94,29 +95,6 @@ class Data {
this.download(downloadElement, dataStr, resultFilename, "text/json", ".json") 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 { class MetaData {

View File

@ -2,7 +2,7 @@
* This class implements a NotificationListener that is listening for the * This class implements a NotificationListener that is listening for the
* specified * specified
*/ */
class NotificationListener { class ViewEventListener {
constructor(type, listenerFunction) { constructor(type, listenerFunction) {
this.listenerFunction = listenerFunction; this.listenerFunction = listenerFunction;
this.type = type; this.type = type;
@ -61,7 +61,7 @@ class ResultsList extends List {
* class field in the ResultsList object with the query selector * class field in the ResultsList object with the query selector
* string as the key. The selector will be converted to a valid JavaScript * string as the key. The selector will be converted to a valid JavaScript
* Field name i. e. #html-id-string -> this.htmlIdString * 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. * method.
*/ */
// TODO: multipleResults=false, atattchSomeCallback=false ? // TODO: multipleResults=false, atattchSomeCallback=false ?
@ -73,6 +73,7 @@ class ResultsList extends List {
element = document.querySelector(selector); element = document.querySelector(selector);
} else { } else {
elements = document.querySelectorAll(selector); elements = document.querySelectorAll(selector);
elements = [...elements];
} }
let cleanKey = []; let cleanKey = [];
selector = selector.replace(/_/g, '-'); 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. * 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 ###### // ###### Functions to inspect one match, to show more details ######
// activate inspect buttons if progress is 100 // activate inspect buttons if progress is 100
activateInspect() { activateInspect() {
@ -786,7 +779,7 @@ class ResultsList extends List {
return matchRowElement 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) { createMetaDataForModal(metaDataObject) {
let html = `<div class="col s12"> let html = `<div class="col s12">
<table class="highlight"> <table class="highlight">
@ -838,12 +831,12 @@ class ResultsList extends List {
} }
// Creates the text details for the texts shown in the corpus analysis metadata modal. // 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 metadataKey = event.target.dataset.metadataKey;
let textKey = event.target.dataset.textKey; let textKey = event.target.dataset.textKey;
let textData = metaDataObject[metadataKey][textKey]; let textData = metaData[metadataKey][textKey];
let bibliographicData = document.getElementById(`bibliographic-data-${metadataKey}-${textKey}`); let bibliographicData = document.querySelector(`#bibliographic-data-${metadataKey}-${textKey}`);
bibliographicData.innerHTML = ""; bibliographicData.textContent = '';
for (let [key, value] of Object.entries(textData)) { for (let [key, value] of Object.entries(textData)) {
bibliographicData.insertAdjacentHTML("afterbegin", bibliographicData.insertAdjacentHTML("afterbegin",
` `
@ -854,4 +847,4 @@ class ResultsList extends List {
}; };
// export classses // export classses
export { NotificationListener, ResultsList }; export { ViewEventListener, ResultsList };

View File

@ -98,8 +98,14 @@ function queryDataRecievedCallback(resultsList, detail) {
// show or enable some things for the user // show or enable some things for the user
resultsList.queryResultsCreate.classList.toggle('disabled'); resultsList.queryResultsCreate.classList.toggle('disabled');
resultsList.addToSubResults.removeAttribute("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 // export the callbacks
@ -110,4 +116,6 @@ export {
queryDataPreparingCallback, queryDataPreparingCallback,
queryDataRecievingCallback, queryDataRecievingCallback,
queryDataRecievedCallback, queryDataRecievedCallback,
resultsDataRecievingCallback,
resultsDataRecievedCallback,
}; };

View File

@ -14,43 +14,55 @@ import {
queryDataPreparingCallback, queryDataPreparingCallback,
queryDataRecievingCallback, queryDataRecievingCallback,
queryDataRecievedCallback, queryDataRecievedCallback,
resultsDataRecievingCallback,
resultsDataRecievedCallback,
} from './callbacks.js'; } from './callbacks.js';
function recieveNotification(eventType, resultsList) { function recieveClientNotification(eventType, resultsList) {
document.addEventListener(eventType, (event) => { document.addEventListener(eventType, (event) => {
let caseIdentifier = event.detail.caseIdentifier; let caseIdentifier = event.detail.caseIdentifier;
switch (caseIdentifier) { switch (caseIdentifier) {
case 'connecting': case 'connecting':
console.info('Recieved notification:', caseIdentifier); console.info('View recieved notification:', caseIdentifier);
connectingCallback(resultsList, event.detail); connectingCallback(resultsList, event.detail);
// execute callback // execute callback
break; break;
case 'connected': case 'connected':
console.info('Recieved notification:', caseIdentifier); console.info('View recieved notification:', caseIdentifier);
connectedCallback(resultsList, event.detail); connectedCallback(resultsList, event.detail);
break; break;
case 'connecting-failed': case 'connecting-failed':
console.info('Recieved notification:', caseIdentifier); console.info('View recieved notification:', caseIdentifier);
// execute callback // execute callback
connectingFaildeCallback(resultsList, event.detail); connectingFaildeCallback(resultsList, event.detail);
break; break;
case 'query-data-prepareing': case 'query-data-prepareing':
console.info('Recieved notification:', caseIdentifier); console.info('View recieved notification:', caseIdentifier);
// execute callback // execute callback
queryDataPreparingCallback(resultsList, event.detail); queryDataPreparingCallback(resultsList, event.detail);
break; break;
case 'query-data-recieving': case 'query-data-recieving':
console.info('Recieved notification:', caseIdentifier); console.info('View recieved notification:', caseIdentifier);
// execute callback // execute callback
queryDataRecievingCallback(resultsList, event.detail); queryDataRecievingCallback(resultsList, event.detail);
break; break;
case 'query-data-recieved': case 'query-data-recieved':
console.info('Recieved notification:', caseIdentifier); console.info('View recieved notification:', caseIdentifier);
// execute callback // execute callback
queryDataRecievedCallback(resultsList, event.detail); queryDataRecievedCallback(resultsList, event.detail);
break; 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: 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? // do something to not crash the analysis session?
// maybe unnecessary // maybe unnecessary
} }
@ -58,4 +70,4 @@ function recieveNotification(eventType, resultsList) {
} }
// export listeners // export listeners
export { recieveNotification }; export { recieveClientNotification };

View File

@ -0,0 +1,17 @@
// loading spinner animation HTML
const loadingSpinnerHTML = `
<div class="preloader-wrapper button-icon-spinner small active">
<div class="spinner-layer spinner-green-only">
<div class="circle-clipper left">
<div class="circle"></div>
</div><div class="gap-patch">
<div class="circle"></div>
</div><div class="circle-clipper right">
<div class="circle"></div>
</div>
</div>
</div>
`;
//export
export { loadingSpinnerHTML };

View File

@ -70,35 +70,45 @@
* First Phase: * First Phase:
* Document content is loaded and scripts are being imported and executed. * Document content is loaded and scripts are being imported and executed.
*/ */
// import Client classes
import { import {
Client, Client,
SocketEventListener, ClientEventListener,
ListenerCallback, ListenerCallback,
} from '../../static/js/modules/corpus_analysis/client/Client.js'; } from '../../static/js/modules/corpus_analysis/client/Client.js';
// import client listener functions
import { import {
recieveConnected, recieveConnected,
recieveMetaData, recieveMetaData,
recieveQueryStatus, recieveQueryStatus,
recieveQueryData, recieveQueryData,
recieveViewNotification,
recieveResultsData,
} from '../../static/js/modules/corpus_analysis/client/listeners.js'; } from '../../static/js/modules/corpus_analysis/client/listeners.js';
import { // import client listener callbacks
Results,
} from '../../static/js/modules/corpus_analysis/model/Results.js';
import { import {
prepareQueryData, prepareQueryData,
saveQueryData, saveQueryData,
saveMetaData, saveMetaData,
getResultsData,
saveResultsData,
} from '../../static/js/modules/corpus_analysis/client/callbacks.js'; } from '../../static/js/modules/corpus_analysis/client/callbacks.js';
import { import {
NotificationListener, Results,
} from '../../static/js/modules/corpus_analysis/model/Results.js';
import {
ViewEventListener,
ResultsList, ResultsList,
} from '../../static/js/modules/corpus_analysis/view/ResultsView.js'; } from '../../static/js/modules/corpus_analysis/view/ResultsView.js';
import { import {
recieveNotification, recieveClientNotification,
} from '../../static/js/modules/corpus_analysis/view/listeners.js'; } from '../../static/js/modules/corpus_analysis/view/listeners.js';
import { import {
scrollToTop, scrollToTop,
} from '../../static/js/modules/corpus_analysis/view/scrollToTop.js' } from '../../static/js/modules/corpus_analysis/view/scrollToTop.js'
import {
loadingSpinnerHTML,
} from '../../static/js/modules/corpus_analysis/view/spinner.js'
/** /**
* Second Phase: * Second Phase:
@ -120,44 +130,59 @@ document.addEventListener("DOMContentLoaded", () => {
let resultsList = new ResultsList('result-list', ResultsList.options); let resultsList = new ResultsList('result-list', ResultsList.options);
/** /**
* Register listeners listening to socket.io events and their callbacks * 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); recieveConnected);
const listenForMetaData = new SocketEventListener('corpus_analysis_meta_data', const listenForMetaData = new ClientEventListener('corpus_analysis_meta_data',
recieveMetaData); recieveMetaData);
const metaDataCallback = new ListenerCallback('corpus_analysis_meta_data', const metaDataCallback = new ListenerCallback('corpus_analysis_meta_data',
saveMetaData, saveMetaData,
[client, results]); [client, results]);
listenForMetaData.setCallbacks([metaDataCallback]); listenForMetaData.setCallbacks([metaDataCallback]);
const listenForQueryStatus = new SocketEventListener('corpus_analysis_query', const listenForQueryStatus = new ClientEventListener('corpus_analysis_query',
recieveQueryStatus); recieveQueryStatus);
const queryStatusCallback = new ListenerCallback('corpus_analysis_query', const queryStatusCallback = new ListenerCallback('corpus_analysis_query',
prepareQueryData, prepareQueryData,
[client, results]); [client, results]);
listenForQueryStatus.setCallbacks([queryStatusCallback]); listenForQueryStatus.setCallbacks([queryStatusCallback]);
const listenForQueryData = new SocketEventListener('corpus_analysis_query_results', const listenForQueryData = new ClientEventListener('corpus_analysis_query_results',
recieveQueryData); recieveQueryData);
const queryDataCallback = new ListenerCallback('corpus_analysis_query_results', const queryDataCallback = new ListenerCallback('corpus_analysis_query_results',
saveQueryData, saveQueryData,
[client, results]); [client, results]);
listenForQueryData.setCallbacks([queryDataCallback]); 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, client.setSocketEventListeners([listenForConnected,
listenForQueryStatus, listenForQueryStatus,
listenForQueryData, listenForQueryData,
listenForMetaData]); listenForMetaData,
listenForViewNotification,
listenForResults]);
client.loadSocketEventListeners(); client.loadSocketEventListeners();
/** /**
* Register resultsList listeners listening to nitification events. * Register resultsList listeners listening to nitification events.
*/ */
const listenForNotification = new NotificationListener('notify', const listenForClientNotification = new ViewEventListener('notify-view',
recieveNotification); recieveClientNotification);
resultsList.setNotificationListeners([listenForNotification]); resultsList.setNotificationListeners([listenForClientNotification]);
resultsList.loadNotificationListeners(); resultsList.loadNotificationListeners();
// Connect client to server // Connect client to server
client.notifyView('connecting'); client.notifyView('connecting');
client.connect(); client.connect();
// Send a query and recieve its answer data // Send a query and recieve its answer data
let queryFormElement = document.querySelector('#query-form'); let queryFormElement = document.querySelector('#query-form');
queryFormElement.addEventListener('submit', (event) => { queryFormElement.addEventListener('submit', (event) => {
@ -181,20 +206,66 @@ document.addEventListener("DOMContentLoaded", () => {
results.data.getQueryStr(queryFormElement); results.data.getQueryStr(queryFormElement);
client.query(results.data.query); client.query(results.data.query);
}); });
/** // Get all needed HTMLElements for the following event listeners
* Display events
* 1. live update of hits per page if hits per page value is changed
*/
resultsList.getHTMLElements([ resultsList.getHTMLElements([
'#display-options-form-results_per_page', '#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.displayOptionsFormResultsPerPage.onchange = () => {
resultsList.changeHitsPerPage(); resultsList.changeHitsPerPage();
}; };
resultsList.displayOptionsFormResultContext.onchange = () => { resultsList.displayOptionsFormResultContext.onchange = () => {
resultsList.changeContext(); 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()]});
}

View File

@ -7,7 +7,7 @@ result.-->
<div class="divider" style="margin-bottom: 10px;"></div> <div class="divider" style="margin-bottom: 10px;"></div>
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<button id="show-metadata" <button id="show-meta-data"
class="waves-effect class="waves-effect
waves-light waves-light
btn-flat btn-flat