// ### Send query functions ### //function to get current queryData from a given queryFormElement function getQueryData(queryFormElement) { // Get query form element and save its data on submit and send this data via formData = new FormData(queryFormElement); queryData = {"context": formData.get("context"), // global declaration "hits_per_page": formData.get("hits_per_page"), "query": formData.get("query")}; return queryData } // Function to send a query to the CQP server using queryData gatherd with getQueryData function sendQuery(event) { // checks if one query has been finished before // if true result download will be disabled again until query is finished // also shows progress bar again if (queryFinished) { exportQueryResultsElement.classList.add("disabled"); queryResultsDeterminateElement.parentNode.parentNode.classList.remove("hide"); } event.preventDefault(); queryData = getQueryData(queryFormElement); nopaque.socket.emit("corpus_analysis_query", queryData.query); // full results object declaration, global declaration! // will always be reset if a query is sent, so that only the chunks of the // current query will be saved in it result = {}; result["matches"] = []; result["cpos_lookup"] = {}; result["text_lookup"] = {}; result["loaded_match_count"] = 0; result["match_count"] = 0; result["query"] = ""; // some hiding/showing for loading animation queryLoadingElement.classList.remove("hide"); queryResultsTableElement.classList.add("hide"); nopaque.toast("Query has been sent!"); resultListOptions = {page: queryData["hits_per_page"], pagination: [{ name: "paginationTop", paginationClass: "paginationTop", innerWindow: 8, outerWindow: 1 }, { paginationClass: "paginationBottom", innerWindow: 8, outerWindow: 1 }], valueNames: ["titles", "lc", "hit", "rc", {data: ["index"]}], item: ``}; resultList = new ResultList('result-list', resultListOptions); resultList.clear(); // empty list for new query } // ### Recieve results from sent query and display them in a list etc. ### // Function used when CQP server sends back the query results using socketio function recieveResults(response) { // ERROR code checking if (response["code"] === 0) { console.log("[SUCCESS] corpus_analysis_init"); console.log("Code:" + response["code"]); // further code execution of this code block starting in line 342 } else if (response["code"] === 1) { queryResultsTableElement.classList.add("hide"); queryLoadingElement.classList.add("hide"); nopaque.toast("Invalid query entered!", "red"); console.log("[ERROR] corpus_analysis_init"); console.log("Code:" + response["code"]); return; // no further code execution of this code block } else { console.log("[ERROR] corpus_analysis_init"); console.log("Code:" + response["code"]); return; // no further code execution of this code block } // logs the current recieved chunk chunk = {}; chunk = response["result"]; console.log("### corpus_analysis chunk ###"); console.log(chunk); // logs and extends/push/update the current recieved chunk to the // result Object console.log("### corpus analysis updated result json ###"); result["matches"].push(...chunk["matches"]); Object.assign(result["cpos_lookup"], chunk["cpos_lookup"]); Object.assign(result["text_lookup"], chunk["text_lookup"]); result["match_count"] = chunk["match_count"]; console.log("Before Current match count", result["loaded_match_count"]); result["query"] = queryData["query"]; console.log(result); // Some hiding and showing of loading animations queryLoadingElement.classList.add("hide"); queryResultsTableElement.classList.remove("hide"); let queryResultsElement = document.getElementById("query-results"); queryResultsElement.innerHTML = ""; // check if query has any results if (chunk["matches"].length === 0) { queryResultsTableElement.classList.add("hide"); nopaque.toast("No results for this query!"); return; } // List building/appending the chunks when query had results match_count = chunk["match_count"]; let count_corpus_files = Object.keys(result["text_lookup"]).length; let resultItems = []; // list for holding every row item // get infos for full match row for (let [index, match] of chunk["matches"].entries()) { resultItems.push({...match, ...{"index": index + result["loaded_match_count"]}}); } resultList.add(resultItems, items => { for (let item of items) { item.elm = resultList.createResultRowElement(item, chunk); } resultList.update(); changeContext(); // sets lr context on first result load }); result["loaded_match_count"] += Object.keys(chunk["matches"]).length; console.log("After current match count", result["loaded_match_count"]); let queryResultsMetadataElement = document.getElementById("query-results-metadata"); queryResultsMetadataElement.innerHTML = `

The query resulted in a total of ${chunk["match_count"]} matches.

${result["loaded_match_count"]} of ${result["match_count"]} matches in ${count_corpus_files} corpus files have been loaded.help

`; let queryResultsInteractionElement = document.getElementById("interaction-elements"); queryResultsInteractionElement.appendChild(exportQueryResultsElement); let queryResultsHeadElement = document.getElementById("query-results-head"); queryResultsHeadElement.classList.remove("hide"); queryStatus = result["loaded_match_count"] / result["match_count"] * 100; console.log(queryStatus); queryResultsDeterminateElement.style["width"] = `${queryStatus}%`; console.log(queryResultsDeterminateElement.style["width"]); let toolTipInfoElement = document.getElementById("tool-tip-info"); toolTipInfoElement.addEventListener("mouseover", function(event) { M.Tooltip.init(toolTipInfoElement, {"html": `

The Server is still sending you your results. Functions like "Export Results" and "Match Inspect" will be available after all matches have been loaded.

`, "inDuration": 1500, "margin": 15, "position": "top", "transitionMovement": 0}) }); // enable download and inspect when query is finished // also sets queryFinished to true if (queryStatus === 100) { queryFinished = true; // global declaration to set downlaod button and inspects buttons back to disabled for new queries queryResultsDeterminateElement.parentNode.parentNode.classList.add("hide"); exportQueryResultsElement.classList.remove("disabled"); toolTipInfoElement.classList.add("hide"); queryResultsMetadataElement.innerHTML = `

The query resulted in a total of ${chunk["match_count"]} matches.

${result["loaded_match_count"]} of ${result["match_count"]} matches in ${count_corpus_files} corpus files have been loaded.check_circle

`; activateInspect(); } } // ### Functions to inspect one match, to show more details ### // activate inspect buttons if queryFinished is true function activateInspect() { if (queryFinished) { let inspectBtnElements = document.getElementsByClassName("inspect"); for (let inspectBtn of inspectBtnElements) { inspectBtn.classList.remove("disabled"); } } } //gets result cpos infos for one dataIndex to send back to the server function inspect(dataIndex) { console.log("Inspect!"); console.log(result["matches"][dataIndex]["hit"]); contextModal.open(); nopaque.socket.emit("inspect_match", {"cpos": result["matches"][dataIndex]["hit"]}); } function showMatchContext(message) { console.log("### match_context ###"); console.log("Incoming data:", message); contextResultsElement.innerHTML = "

 

"; document.getElementById("context-modal-loading").classList.add("hide"); document.getElementById("context-modal-ready").classList.remove("hide"); let sentenceElement, token, tokenElement; for (let [key, value] of Object.entries(message['context_s_cpos'])) { sentenceElement = document.createElement("p"); for (cpos of value) { token = message["cpos_lookup"][cpos]; tokenElement = document.createElement("span"); tokenElement.classList.add("token"); if (message["match_cpos_list"].includes(cpos)) { tokenElement.classList.add("bold"); tokenElement.classList.add("light-green"); } tokenElement.dataset.cpos = cpos; tokenElement.innerText = token["word"]; var expertModeSwitchElement = document.getElementById("expert-mode-switch"); if (expertModeSwitchElement.checked) { expertModeOn([tokenElement], message); } sentenceElement.append(tokenElement); sentenceElement.append(document.createTextNode(" ")); } contextResultsElement.append(sentenceElement); } } // ### Display options changing live how the matches are being displayed ### // Event function that changes the shown hits per page. // Just alters the resultList.page property function changeHitsPerPage(event) { try { resultList.page = event.target.value; resultList.update(); nopaque.toast("Updated matches per page.") } catch (e) { console.log("resultList has no results right now. Live update of items per page is useless for now."); } } // Event function triggered on context select change and also if pagination is clicked function changeContext(event) { // newValue = event.target.value; // cannot use this anymore due to reuse of this function in the above paginationElements eventListener console.log("Event", event); try { if (event.type === "change") { nopaque.toast("Updated context per match!"); } } catch (e) { console.log(e); console.log("This error is expected."); } finally { var contextPerItemElement = document.getElementById("context"); newValue = contextPerItemElement.value; console.log(newValue); var lc = document.getElementsByClassName("left-context"); var rc = document.getElementsByClassName("right-context"); for (let element of lc) { array = Array.from(element.childNodes); for (let element of array.slice(newValue)) { element.classList.add("hide"); } for (let element of array.slice(0, newValue)) { element.classList.remove("hide"); } } for (let element of rc) { array = Array.from(element.childNodes); for (let element of array.slice(newValue)) { element.classList.add("hide"); } for (let element of array.slice(0, newValue)) { element.classList.remove("hide"); } } } } // ### Expert view event functions ### // Event function to check if pagination is used and then look if // expertModeSwitchElement is checked // if checked than expertModeOn is executed // if unchecked expertModeOff is executed function eventHandlerCheck(event) { console.log("pagination used!"); console.log(expertModeSwitchElement.checked); if (expertModeSwitchElement.checked) { expertModeOn(event.currentTarget.tokenElements, result); } else if (!expertModeSwitchElement.checked) { event.preventDefault(); console.log("prevented! Destroy"); expertModeOff(event.currentTarget.tokenElements); } } // function to apply extra information and animation to every token function expertModeOn(tokenElements, result_lookup) { console.log("expertModeOn!"); for (tokenElement of tokenElements) { tokenElement.classList.add("chip"); tokenElement.classList.add("hoverable"); tokenElement.classList.add("expert-view"); token = result_lookup["cpos_lookup"][tokenElement.dataset.cpos]; tokenElement.addEventListener("mouseover", function(event) { console.log("Mouseover!"); console.log(event.target); token = result_lookup["cpos_lookup"][event.target.dataset.cpos]; addToolTipToTokenElement(event.target, token); }) } } // fuction that creates Tooltip for one token and extracts the corresponding // infos from the result JSON function addToolTipToTokenElement(tokenElement, token) { M.Tooltip.init(tokenElement, {"html": `
Token information Source information
Word: ${token["word"]}
Lemma: ${token["lemma"]}
POS: ${token["pos"]}
Simple POS: ${token["simple_pos"]}
NER: ${token["ner"]}
Title: ${result["text_lookup"][token["text"]]["title"]}
Author: ${result["text_lookup"][token["text"]]["author"]}
Publishing year: ${result["text_lookup"][token["text"]]["publishing_year"]}
`, "inDuration": 1500, "margin": 15, "position": "top", "transitionMovement": 0}); } // function to remove extra informations and animations from tokens function expertModeOff(tokenElements) { console.log("expertModeOff!"); for (tokenElement of tokenElements) { tokenElement.classList.remove("chip"); tokenElement.classList.remove("hoverable"); tokenElement.classList.remove("expert-view"); tokenElement.outerHTML = tokenElement.outerHTML; // this is actually a workaround, but it works pretty fast } } // ### Download results functions ### // function creates a unique and safe filename for the download function createDownloadFilename() { // get and create metadata console.log("Create Metadata!"); var today = new Date(); var currentDate = today.getUTCFullYear() + '-' + (today.getUTCMonth() +1) + '-' + today.getUTCDate(); var currentTime = today.getUTCHours() + ":" + today.getUTCMinutes() + ":" + today.getUTCSeconds(); var safeFilename = result['query'].replace(/[^a-z0-9_-]/gi, "_"); var resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename; return resultFilename } // function to download the results as JSON function downloadJSONRessource(resultFilename) { // stringify JSON object for json download var dataStr = JSON.stringify(result, undefined, "\t"); // use tabs to save some space // get downloadResultsElement downloadElement = document.getElementById("download-results-json"); // start actual download download(downloadElement, dataStr, resultFilename, "text/json", ".json") } // Function to download data as a Blob created from a string, should be multi purpose function download(downloadElem, dataStr, filename, type, filenameSlug) { console.log("Start Download!"); filename += filenameSlug; var file = new Blob([dataStr], {type: type}); if (window.navigator.msSaveOrOpenBlob) // IE10+ window.navigator.msSaveOrOpenBlob(file, filename); else { // Others var url = URL.createObjectURL(file); downloadElem.href = url; downloadElem.download = filename; } }