2020-03-26 14:28:11 +00:00
// ### corpus analysis client status functions
function sendAbortSignal ( ) {
console . log ( ` Sending status "abort signal" from client side. ` ) ;
message = { 'status' : "abort" } ;
nopaque . socket . emit ( "send_analysis_status_cli" , message ) ;
setAnalysisStatus ( "abort" ) ;
}
function setAnalysisStatus ( statusStr ) {
analysisClientStatus [ sessionId ] = statusStr ;
console . log ( ` Set new status " ${ statusStr } " for " ${ sessionId } " on client side. ` ) ;
console . log ( "Object with status is:" , analysisClientStatus ) ;
}
function checkAnalysisStatus ( sessionId ) {
let status = analysisClientStatus [ sessionId ] ;
console . log ( ` Check status on client side for ${ sessionId } . ` , analysisClientStatus ) ;
return status ;
}
2020-03-17 13:00:41 +00:00
// ### Send query functions ###
//function to get current queryData from a given queryFormElement
function getQueryData ( queryFormElement ) {
2020-03-19 12:59:31 +00:00
let formData
let queryData
2020-03-17 13:00:41 +00:00
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
2020-03-26 14:28:11 +00:00
async function sendQuery ( event ) {
const delay = ms => new Promise ( res => setTimeout ( res , ms ) ) ;
2020-03-19 12:59:31 +00:00
result ; // global declaration, holds query results as JSON
resultList ; // global declaration, list.js object displaying the results
let queryData ; // holds data of query form
let resultListOptions ; // holding the options for the ResultList object
2020-03-26 14:28:11 +00:00
let analysisStatus ;
2020-03-18 14:52:53 +00:00
// checks if one query has been finished before
2020-03-19 12:59:31 +00:00
// if true, result download will be disabled again until query is finished
2020-03-18 14:52:53 +00:00
// also shows progress bar again
if ( queryFinished ) {
exportQueryResultsElement . classList . add ( "disabled" ) ;
queryResultsDeterminateElement . parentNode . parentNode . classList . remove ( "hide" ) ;
}
2020-03-26 14:28:11 +00:00
console . log ( "Sending query." ) ;
console . log ( "Session status are: " , analysisClientStatus ) ;
2020-03-17 13:00:41 +00:00
event . preventDefault ( ) ;
2020-03-26 14:28:11 +00:00
analysisStatus = checkAnalysisStatus ( sessionId ) ;
2020-03-17 13:00:41 +00:00
queryData = getQueryData ( queryFormElement ) ;
2020-03-26 14:28:11 +00:00
if ( analysisStatus === "idle" ) {
2020-03-28 18:29:19 +00:00
nopaque . flash ( "Query has been sent!" ) ;
2020-03-26 14:28:11 +00:00
nopaque . socket . emit ( "corpus_analysis_query" , queryData . query ) ;
helperSendQuery ( queryData ) ;
analysisStatus = checkAnalysisStatus ( sessionId ) ;
} else if ( analysisStatus === "running" ) {
sendAbortSignal ( ) ;
analysisStatus = checkAnalysisStatus ( sessionId ) ;
while ( analysisStatus === "abort" ) {
queryResultsTableElement . classList . add ( "hide" ) ;
queryLoadingElement . classList . remove ( "hide" ) ;
analysisStatus = checkAnalysisStatus ( sessionId ) ;
await delay ( 100 ) ;
}
sendQuery ( event )
}
}
function helperSendQuery ( queryData ) {
2020-03-17 13:00:41 +00:00
// 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
2020-03-23 11:14:32 +00:00
result = { } ; // full JSON object holding match results
result [ "matches" ] = [ ] ; // list of all amtches with lc and rc
result [ "cpos_lookup" ] = { } ; // object contains all cpos as key value pair
result [ "text_lookup" ] = { } ; // same as above for all text ids
result [ "loaded_match_count" ] = 0 ; // how many matches have been recieved
result [ "num_matches_total" ] ; // how many should have been recieved/total nr
result [ "query" ] = "" ; // the query as a string
2020-03-17 13:00:41 +00:00
// some hiding/showing for loading animation
queryLoadingElement . classList . remove ( "hide" ) ;
queryResultsTableElement . classList . add ( "hide" ) ;
resultListOptions = { page : queryData [ "hits_per_page" ] ,
pagination : [ {
name : "paginationTop" ,
paginationClass : "paginationTop" ,
innerWindow : 8 ,
outerWindow : 1
} , {
paginationClass : "paginationBottom" ,
innerWindow : 8 ,
outerWindow : 1
} ] ,
2020-03-28 18:29:19 +00:00
valueNames : [ "titles" , "lc" , "c" , "rc" , { data : [ "index" ] } ] ,
2020-03-17 13:00:41 +00:00
item : ` <span class="hidden"></span> ` } ;
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 ) {
2020-03-19 10:04:36 +00:00
let toolTipInfoElement ;
2020-03-19 12:59:31 +00:00
let chunk ;
let countCorpusFiles ;
let queryData ;
let queryResultsElement ;
let resultItems ;
let queryResultsMetadataElement ;
let queryResultsInteractionElement ;
let queryResultsHeadElement ;
let queryStatus ;
2020-03-19 10:04:36 +00:00
queryFinished = false ;
2020-03-18 14:52:53 +00:00
2020-03-23 09:28:07 +00:00
console . log ( response ) ;
2020-03-17 13:00:41 +00:00
// 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" ) ;
2020-03-28 18:29:19 +00:00
nopaque . flash ( "error" , "Invalid query entered!" ) ;
2020-03-17 13:00:41 +00:00
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
2020-03-23 11:14:32 +00:00
chunk = response [ "chunk" ] ;
2020-03-23 08:10:35 +00:00
//chunk = response["chunk"];
2020-03-17 13:00:41 +00:00
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" ] ) ;
2020-03-23 11:14:32 +00:00
result [ "num_matches_total" ] = response [ "num_matches_total" ] ;
2020-03-23 08:10:35 +00:00
//result["match_count"] = response["match_count"];
2020-03-17 13:00:41 +00:00
console . log ( "Before Current match count" , result [ "loaded_match_count" ] ) ;
2020-03-19 12:59:31 +00:00
queryData = getQueryData ( queryFormElement ) ;
2020-03-17 13:00:41 +00:00
result [ "query" ] = queryData [ "query" ] ;
console . log ( result ) ;
// Some hiding and showing of loading animations
2020-03-26 14:28:11 +00:00
analysisStatus = checkAnalysisStatus ( ) ;
if ( analysisStatus === "abort" ) {
queryResultsTableElement . classList . add ( "hide" ) ;
queryLoadingElement . classList . remove ( "hide" ) ;
} else {
queryResultsTableElement . classList . remove ( "hide" ) ;
queryLoadingElement . classList . add ( "hide" ) ;
queryResultsElement = document . getElementById ( "query-results" ) ;
queryResultsElement . innerHTML = "" ;
}
2020-03-17 13:00:41 +00:00
// check if query has any results
if ( chunk [ "matches" ] . length === 0 ) {
queryResultsTableElement . classList . add ( "hide" ) ;
2020-03-28 18:29:19 +00:00
nopaque . flash ( "No results for this query!" ) ;
2020-03-17 13:00:41 +00:00
return ;
}
// List building/appending the chunks when query had results
2020-03-19 12:59:31 +00:00
countCorpusFiles = Object . keys ( result [ "text_lookup" ] ) . length ;
2020-03-17 13:00:41 +00:00
2020-03-19 12:59:31 +00:00
resultItems = [ ] ; // list for holding every row item
2020-03-17 13:00:41 +00:00
// 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" ] ) ;
2020-03-19 12:59:31 +00:00
queryResultsMetadataElement = document . getElementById ( "query-results-metadata" ) ;
2020-03-23 11:14:32 +00:00
queryResultsMetadataElement . innerHTML = ` <p>The query resulted in a total of ${ result [ "num_matches_total" ] } matches. </p> <p> ${ result [ "loaded_match_count" ] } of ${ result [ "num_matches_total" ] } matches in ${ countCorpusFiles } corpus files have been loaded.</p><p><i class="material-icons" id="tooltip-info">help</i>The Server is still sending your results. Functions like "Export Results" and "Match Inspect" will be available after all matches have been loaded.</p> ` ;
2020-03-19 12:59:31 +00:00
queryResultsInteractionElement = document . getElementById ( "interaction-elements" ) ;
2020-03-18 14:52:53 +00:00
queryResultsInteractionElement . appendChild ( exportQueryResultsElement ) ;
2020-03-19 12:59:31 +00:00
queryResultsHeadElement = document . getElementById ( "query-results-head" ) ;
2020-03-18 14:52:53 +00:00
queryResultsHeadElement . classList . remove ( "hide" ) ;
2020-03-23 11:14:32 +00:00
queryStatus = response [ "progress" ] ;
console . log ( "QUERY STATUS:" , queryStatus ) ;
2020-03-18 14:52:53 +00:00
queryResultsDeterminateElement . style [ "width" ] = ` ${ queryStatus } % ` ;
console . log ( queryResultsDeterminateElement . style [ "width" ] ) ;
2020-03-19 11:04:14 +00:00
2020-03-18 14:52:53 +00:00
// enable download and inspect when query is finished
// also sets queryFinished to true
if ( queryStatus === 100 ) {
2020-03-19 08:25:23 +00:00
queryFinished = true ; // global declaration to set downlaod button and inspects buttons back to disabled for new queries
2020-03-18 14:52:53 +00:00
queryResultsDeterminateElement . parentNode . parentNode . classList . add ( "hide" ) ;
exportQueryResultsElement . classList . remove ( "disabled" ) ;
2020-03-23 11:14:32 +00:00
queryResultsMetadataElement . innerHTML = ` <p>The query resulted in a total of ${ result [ "num_matches_total" ] } matches. </p> <p> ${ result [ "loaded_match_count" ] } of ${ result [ "num_matches_total" ] } matches in ${ countCorpusFiles } corpus files have been loaded.<i class="material-icons">check_circle</i></p> ` ;
2020-03-19 08:25:23 +00:00
activateInspect ( ) ;
2020-03-18 14:52:53 +00:00
}
}
// ### Functions to inspect one match, to show more details ###
// activate inspect buttons if queryFinished is true
function activateInspect ( ) {
2020-03-19 12:59:31 +00:00
let inspectBtnElements ;
2020-03-18 14:52:53 +00:00
if ( queryFinished ) {
2020-03-19 12:59:31 +00:00
inspectBtnElements = document . getElementsByClassName ( "inspect" ) ;
2020-03-18 14:52:53 +00:00
for ( let inspectBtn of inspectBtnElements ) {
inspectBtn . classList . remove ( "disabled" ) ;
}
}
2020-03-17 13:00:41 +00:00
}
2020-03-18 14:52:53 +00:00
//gets result cpos infos for one dataIndex to send back to the server
2020-03-17 13:00:41 +00:00
function inspect ( dataIndex ) {
console . log ( "Inspect!" ) ;
2020-03-28 18:29:19 +00:00
console . log ( result [ "matches" ] [ dataIndex ] [ "c" ] ) ;
2020-03-17 13:00:41 +00:00
contextModal . open ( ) ;
2020-03-28 18:29:19 +00:00
nopaque . socket . emit ( "inspect_match" , { "cpos" : result [ "matches" ] [ dataIndex ] [ "c" ] } ) ;
2020-03-17 13:00:41 +00:00
}
2020-03-18 14:52:53 +00:00
function showMatchContext ( message ) {
2020-03-19 12:59:31 +00:00
let contextResultsElement ;
let sentenceElement
let token ;
let tokenElement ;
2020-03-18 14:52:53 +00:00
console . log ( "### match_context ###" ) ;
console . log ( "Incoming data:" , message ) ;
2020-03-19 12:59:31 +00:00
contextResultsElement = document . getElementById ( "context-results" ) ;
2020-03-18 14:52:53 +00:00
contextResultsElement . innerHTML = "<p> </p>" ;
document . getElementById ( "context-modal-loading" ) . classList . add ( "hide" ) ;
document . getElementById ( "context-modal-ready" ) . classList . remove ( "hide" ) ;
for ( let [ key , value ] of Object . entries ( message [ 'context_s_cpos' ] ) ) {
sentenceElement = document . createElement ( "p" ) ;
2020-03-19 12:59:31 +00:00
for ( let cpos of value ) {
2020-03-18 14:52:53 +00:00
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 ) ;
}
}
2020-03-17 13:00:41 +00:00
// ### 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 ( ) ;
2020-03-28 18:29:19 +00:00
nopaque . flash ( "Updated matches per page." )
2020-03-17 13:00:41 +00:00
} 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 ) {
2020-03-19 12:59:31 +00:00
let newContextValue ;
let lc ;
let rc ;
let array ;
2020-03-17 13:00:41 +00:00
try {
if ( event . type === "change" ) {
2020-03-28 18:29:19 +00:00
nopaque . flash ( "Updated context per match!" ) ;
2020-03-17 13:00:41 +00:00
}
} catch ( e ) {
console . log ( e ) ;
2020-03-18 14:52:53 +00:00
console . log ( "This error is expected." ) ;
2020-03-17 13:00:41 +00:00
} finally {
2020-03-19 12:59:31 +00:00
newContextValue = document . getElementById ( "context" ) . value ;
2020-03-26 14:28:11 +00:00
console . log ( "Context value is:" , newContextValue ) ;
2020-03-19 12:59:31 +00:00
lc = document . getElementsByClassName ( "left-context" ) ;
rc = document . getElementsByClassName ( "right-context" ) ;
2020-03-17 13:00:41 +00:00
for ( let element of lc ) {
array = Array . from ( element . childNodes ) ;
2020-03-19 12:59:31 +00:00
for ( let element of array . slice ( newContextValue ) ) {
2020-03-17 13:00:41 +00:00
element . classList . add ( "hide" ) ;
}
2020-03-19 12:59:31 +00:00
for ( let element of array . slice ( 0 , newContextValue ) ) {
2020-03-17 13:00:41 +00:00
element . classList . remove ( "hide" ) ;
}
}
for ( let element of rc ) {
array = Array . from ( element . childNodes ) ;
2020-03-19 12:59:31 +00:00
for ( let element of array . slice ( newContextValue ) ) {
2020-03-17 13:00:41 +00:00
element . classList . add ( "hide" ) ;
}
2020-03-19 12:59:31 +00:00
for ( let element of array . slice ( 0 , newContextValue ) ) {
2020-03-17 13:00:41 +00:00
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 ) {
2020-03-19 12:59:31 +00:00
let token ;
2020-03-17 13:00:41 +00:00
console . log ( "expertModeOn!" ) ;
2020-03-19 12:59:31 +00:00
for ( let tokenElement of tokenElements ) {
2020-03-17 13:00:41 +00:00
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 ) ;
2020-03-19 12:59:31 +00:00
} ) ;
2020-03-17 13:00:41 +00:00
}
}
// 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" : ` <table>
< tr >
< th > Token information < / t h >
< th > Source information < / t h >
< / t r >
< tr >
< td class = "left-align" >
Word : $ { token [ "word" ] } < br >
Lemma : $ { token [ "lemma" ] } < br >
POS : $ { token [ "pos" ] } < br >
Simple POS : $ { token [ "simple_pos" ] } < br >
NER : $ { token [ "ner" ] }
< / t d >
< td class = "left-align" >
Title : $ { result [ "text_lookup" ] [ token [ "text" ] ] [ "title" ] } < br >
Author : $ { result [ "text_lookup" ] [ token [ "text" ] ] [ "author" ] } < br >
Publishing year : $ { result [ "text_lookup" ] [ token [ "text" ] ] [ "publishing_year" ] }
< / t d >
< / t r >
< / t a b l e > ` ,
"inDuration" : 1500 ,
"margin" : 15 ,
"position" : "top" ,
"transitionMovement" : 0 } ) ;
}
// function to remove extra informations and animations from tokens
function expertModeOff ( tokenElements ) {
console . log ( "expertModeOff!" ) ;
2020-03-19 12:59:31 +00:00
for ( let tokenElement of tokenElements ) {
2020-03-17 13:00:41 +00:00
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 ( ) {
2020-03-19 12:59:31 +00:00
let today ;
let currentDate ;
let currentTime ;
let safeFilename ;
let resultFilename ;
2020-03-17 13:00:41 +00:00
// get and create metadata
console . log ( "Create Metadata!" ) ;
2020-03-19 12:59:31 +00:00
today = new Date ( ) ;
currentDate = today . getUTCFullYear ( ) + '-' + ( today . getUTCMonth ( ) + 1 ) + '-' + today . getUTCDate ( ) ;
currentTime = today . getUTCHours ( ) + ":" + today . getUTCMinutes ( ) + ":" + today . getUTCSeconds ( ) ;
safeFilename = result [ 'query' ] . replace ( /[^a-z0-9_-]/gi , "_" ) ;
resultFilename = "UTC-" + currentDate + "_" + currentTime + "_" + safeFilename ;
2020-03-17 13:00:41 +00:00
return resultFilename
}
// function to download the results as JSON
function downloadJSONRessource ( resultFilename ) {
2020-03-19 12:59:31 +00:00
let dataStr ;
let downloadElement ;
2020-03-17 13:00:41 +00:00
// stringify JSON object for json download
2020-03-19 12:59:31 +00:00
dataStr = JSON . stringify ( result , undefined , "\t" ) ; // use tabs to save some space
2020-03-17 13:00:41 +00:00
// 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 ) {
2020-03-19 12:59:31 +00:00
let file ;
2020-03-17 13:00:41 +00:00
console . log ( "Start Download!" ) ;
filename += filenameSlug ;
2020-03-19 12:59:31 +00:00
file = new Blob ( [ dataStr ] , { type : type } ) ;
2020-03-17 13:00:41 +00:00
if ( window . navigator . msSaveOrOpenBlob ) // IE10+
window . navigator . msSaveOrOpenBlob ( file , filename ) ;
else { // Others
var url = URL . createObjectURL ( file ) ;
downloadElem . href = url ;
downloadElem . download = filename ;
}
2020-03-23 08:10:35 +00:00
}