mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-03 20:02:47 +00:00 
			
		
		
		
	integrate nopaque repo
This commit is contained in:
		@@ -1,23 +0,0 @@
 | 
			
		||||
MIT License
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2019 Alexander Shutau
 | 
			
		||||
 | 
			
		||||
All rights reserved.
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,18 +0,0 @@
 | 
			
		||||
Copyright 2012 Dharmafly. All rights reserved.
 | 
			
		||||
Permission is hereby granted,y free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to
 | 
			
		||||
deal in the Software without restriction, including without limitation the
 | 
			
		||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | 
			
		||||
sell copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | 
			
		||||
IN THE SOFTWARE. 
 | 
			
		||||
							
								
								
									
										36
									
								
								app/static/js/JSONPatch.js/jsonpatch.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										36
									
								
								app/static/js/JSONPatch.js/jsonpatch.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2011-2014 Jonny Strömberg, jonnystromberg.com
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										2
									
								
								app/static/js/List.js/list.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								app/static/js/List.js/list.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,21 +0,0 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2014-2018 Materialize
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
SOFTWARE.
 | 
			
		||||
							
								
								
									
										6
									
								
								app/static/js/Materialize/materialize.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								app/static/js/Materialize/materialize.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2014 Guillermo Rauch
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -1,196 +0,0 @@
 | 
			
		||||
class CorpusAnalysisClient {
 | 
			
		||||
  constructor(corpusId, socket) {
 | 
			
		||||
    this.callbacks = {};
 | 
			
		||||
    this.corpusId = corpusId;
 | 
			
		||||
    this.displays = {};
 | 
			
		||||
    this.socket = socket;
 | 
			
		||||
 | 
			
		||||
    // socket on event for corpous analysis initialization
 | 
			
		||||
    socket.on("corpus_analysis_init", (response) => {
 | 
			
		||||
      let errorText;
 | 
			
		||||
 | 
			
		||||
      if (response.code === 200) {
 | 
			
		||||
        console.log(`corpus_analysis_init: ${response.code} - ${response.msg}`);
 | 
			
		||||
        if (this.callbacks.init != undefined) {
 | 
			
		||||
          this.callbacks.init(response.payload);
 | 
			
		||||
          this.callbacks.get_metadata();  // should hold the function getMetaData
 | 
			
		||||
        }
 | 
			
		||||
        if (this.displays.init != undefined) {
 | 
			
		||||
          this.displays.init.setVisibilityByStatus("success");
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        errorText = `Error ${response.code} - ${response.msg}`;
 | 
			
		||||
        if (this.displays.init.errorContainer != undefined)  {
 | 
			
		||||
          this.displays.init.errorContainer.innerHTML = `<p class="red-text">` +
 | 
			
		||||
                   `<i class="material-icons tiny">error</i> ${errorText}</p>`;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.displays.init != undefined)  {
 | 
			
		||||
          this.displays.init.setVisibilityByStatus("error");
 | 
			
		||||
        }
 | 
			
		||||
        console.error(`corpus_analysis_init: ${errorText}`);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // socket on event for recieving meta
 | 
			
		||||
    socket.on('corpus_analysis_send_meta_data', (response) => {
 | 
			
		||||
      let errorText;
 | 
			
		||||
 | 
			
		||||
      if (response.code === 200) {
 | 
			
		||||
        console.log(`corpus_analysis_send_meta_data: ${response.code} - ${response.msg} - ${response.desc}`);
 | 
			
		||||
        if (this.callbacks.recv_meta_data != undefined) {
 | 
			
		||||
          this.callbacks.recv_meta_data(response.payload);
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        errorText = `Error ${response.code} - ${response.msg}`;
 | 
			
		||||
        if (this.displays.init.errorContainer != undefined)  {
 | 
			
		||||
          this.displays.init.errorContainer.innerHTML = `<p class="red-text">` +
 | 
			
		||||
                   `<i class="material-icons tiny">error</i> ${errorText}</p>`;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.displays.init != undefined)  {
 | 
			
		||||
          this.displays.init.setVisibilityByStatus("error");
 | 
			
		||||
        }
 | 
			
		||||
        console.error(`corpus_analysis_send_meta_data: ${errorText}`);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // socket on event for recieveing query results
 | 
			
		||||
    socket.on("corpus_analysis_query", (response) => {
 | 
			
		||||
      let errorText;
 | 
			
		||||
 | 
			
		||||
      if (response.code === 200) {
 | 
			
		||||
        console.log(`corpus_analysis_query: ${response.code} - ${response.msg}`);
 | 
			
		||||
        if (this.callbacks.query != undefined)  {
 | 
			
		||||
          this.callbacks.query(response.payload);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.displays.query != undefined)  {
 | 
			
		||||
          this.displays.query.setVisibilityByStatus("success");
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        errorText = `Error ${response.payload.code} - ${response.payload.msg}`;
 | 
			
		||||
        nopaque.flash(errorText, "error");
 | 
			
		||||
        if (this.displays.query.errorContainer != undefined)  {
 | 
			
		||||
          this.displays.query.errorContainer.innerHTML = `<p class="red-text">`+
 | 
			
		||||
                    `<i class="material-icons tiny">error</i> ${errorText}</p>`;
 | 
			
		||||
        }
 | 
			
		||||
        if (this.displays.query != undefined)  {
 | 
			
		||||
          this.displays.query.setVisibilityByStatus("error");
 | 
			
		||||
        }
 | 
			
		||||
        console.error(`corpus_analysis_query: ${errorText}`);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    socket.on("corpus_analysis_query_results", (response) => {
 | 
			
		||||
        if (this.callbacks.query_results != undefined) {
 | 
			
		||||
          this.callbacks.query_results(response.payload);
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    socket.on("corpus_analysis_inspect_match", (response) => {
 | 
			
		||||
      if (this.callbacks.query_match_context != undefined) {
 | 
			
		||||
        this.callbacks.query_match_context(response);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init() {
 | 
			
		||||
    if (this.displays.init.errorContainer != undefined)  {
 | 
			
		||||
      this.displays.init.errorContainer.innerHTML == "";
 | 
			
		||||
    }
 | 
			
		||||
    if (this.displays.init != undefined)  {
 | 
			
		||||
      this.displays.init.setVisibilityByStatus("waiting");
 | 
			
		||||
    }
 | 
			
		||||
    this.socket.emit("corpus_analysis_init", this.corpusId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getMetaData() {
 | 
			
		||||
    // just emits thos to tell the server to gather all meta dat infos and send
 | 
			
		||||
    // those back
 | 
			
		||||
    this.socket.emit("corpus_analysis_get_meta_data", this.corpusId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  query(queryStr) {
 | 
			
		||||
    let displayOptionsData;
 | 
			
		||||
    let resultListOptions;
 | 
			
		||||
 | 
			
		||||
    if (this.displays.query.errorContainer != undefined)  {
 | 
			
		||||
      this.displays.query.errorContainer.innerHTML == "";
 | 
			
		||||
    }
 | 
			
		||||
    if (this.displays.query != undefined)  {
 | 
			
		||||
      this.displays.query.setVisibilityByStatus("waiting");
 | 
			
		||||
    }
 | 
			
		||||
    nopaque.socket.emit("corpus_analysis_query", queryStr);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setCallback(type, callback) {
 | 
			
		||||
    // saves callback functions into an object. Key is function type, callback
 | 
			
		||||
    // is the callback function
 | 
			
		||||
    this.callbacks[type] = callback;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setDisplay(type, display) {
 | 
			
		||||
    this.displays[type] = display;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CorpusAnalysisDisplay {
 | 
			
		||||
  constructor(element) {
 | 
			
		||||
    this.element = element;
 | 
			
		||||
    this.errorContainer = element.querySelector(".error-container");
 | 
			
		||||
    this.showOnError = element.querySelectorAll(".show-on-error");
 | 
			
		||||
    this.showOnSuccess = element.querySelectorAll(".show-on-success");
 | 
			
		||||
    this.showWhileWaiting = element.querySelectorAll(".show-while-waiting");
 | 
			
		||||
    this.hideOnComplete = element.querySelectorAll(".hide-on-complete")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setVisibilityByStatus(status) {
 | 
			
		||||
    switch (status) {
 | 
			
		||||
      case "error":
 | 
			
		||||
        for (let element of this.showOnError) {
 | 
			
		||||
          element.classList.remove("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showOnSuccess) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showWhileWaiting) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case "success":
 | 
			
		||||
        for (let element of this.showOnError) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showOnSuccess) {
 | 
			
		||||
          element.classList.remove("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showWhileWaiting) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      case "waiting":
 | 
			
		||||
        for (let element of this.showOnError) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showOnSuccess) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showWhileWaiting) {
 | 
			
		||||
          element.classList.remove("hide");
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      default:
 | 
			
		||||
        // Hide all
 | 
			
		||||
        for (let element of this.showOnError) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showOnSuccess) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of this.showWhileWaiting) {
 | 
			
		||||
          element.classList.add("hide");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,90 +0,0 @@
 | 
			
		||||
class Results {
 | 
			
		||||
  constructor(data, jsList , metaData) {
 | 
			
		||||
  this.data = data;
 | 
			
		||||
  this.jsList = jsList;
 | 
			
		||||
  this.metaData = metaData
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clearAll() {
 | 
			
		||||
    this.jsList.clear();
 | 
			
		||||
    this.jsList.update();
 | 
			
		||||
    this.data.init();
 | 
			
		||||
    this.metaData.init();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Data {
 | 
			
		||||
  // Sets empty object structure. Also usefull to delete old results.
 | 
			
		||||
  // matchCount default is 0
 | 
			
		||||
  init(matchCount = 0) {
 | 
			
		||||
    this["matches"] = [];  // list of all c with lc and rc
 | 
			
		||||
    this["cpos_lookup"] = {};  // object contains all this key value pair
 | 
			
		||||
    this["text_lookup"] = {};  // same as above for all text ids
 | 
			
		||||
    this["match_count"] = matchCount;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addData(jsonData) {
 | 
			
		||||
    Object.assign(this, jsonData);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // get query as string from form Element
 | 
			
		||||
  getQueryStr(queryFormElement) {
 | 
			
		||||
    // gets query
 | 
			
		||||
    let queryFormData;
 | 
			
		||||
    let queryStr;
 | 
			
		||||
    queryFormData = new FormData(queryFormElement);
 | 
			
		||||
    queryStr = queryFormData.get("query-form-query");
 | 
			
		||||
    this["query"] = queryStr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // function creates a unique and safe filename for the download
 | 
			
		||||
  createDownloadFilename(suffix) {
 | 
			
		||||
    let today;
 | 
			
		||||
    let currentDate;
 | 
			
		||||
    let currentTime;
 | 
			
		||||
    let safeFilename;
 | 
			
		||||
    let resultFilename;
 | 
			
		||||
    // get and create metadata
 | 
			
		||||
    today = new Date();
 | 
			
		||||
    currentDate = `${today.getUTCFullYear()}` +
 | 
			
		||||
                  `-${(today.getUTCMonth() + 1)}` +
 | 
			
		||||
                  `-${today.getUTCDate()}`;
 | 
			
		||||
    currentTime = `${today.getUTCHours()}h` +
 | 
			
		||||
                  `${today.getUTCMinutes()}m` +
 | 
			
		||||
                  `${today.getUTCSeconds()}s`;
 | 
			
		||||
    safeFilename = this.query.replace(/[^a-z0-9_-]/gi, "_");
 | 
			
		||||
    resultFilename = `UTC-${currentDate}_${currentTime}_${safeFilename}_${suffix}`;
 | 
			
		||||
    return resultFilename
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Function to download data as Blob created from string
 | 
			
		||||
  // should be private but that is not yet a feature of javascript 08.04.2020
 | 
			
		||||
  download(downloadElement, dataStr, filename, type, filenameSlug) {
 | 
			
		||||
    console.log("Start Download!");
 | 
			
		||||
    let file;
 | 
			
		||||
    filename += filenameSlug;
 | 
			
		||||
    file = new Blob([dataStr], {type: type});
 | 
			
		||||
        var url = URL.createObjectURL(file);
 | 
			
		||||
        downloadElement.href = url;
 | 
			
		||||
        downloadElement.download = filename;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // function to download the results as JSON
 | 
			
		||||
  downloadJSONRessource(resultFilename, downloadData, downloadElement) {
 | 
			
		||||
    let dataStr;
 | 
			
		||||
    // stringify JSON object for json download
 | 
			
		||||
    // use tabs to save some space
 | 
			
		||||
    dataStr = JSON.stringify(downloadData, undefined, "\t");
 | 
			
		||||
    // start actual download
 | 
			
		||||
    this.download(downloadElement, dataStr, resultFilename, "text/json", ".json")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class MetaData {
 | 
			
		||||
  // Sets empty object structure when no input is given.
 | 
			
		||||
  // if json object like input is given class fields are created from this
 | 
			
		||||
  init(json = {}) {
 | 
			
		||||
    Object.assign(this, json);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
function recvMetaData(payload) {
 | 
			
		||||
  results.metaData.init(payload)
 | 
			
		||||
  console.log(results.metaData);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function querySetup(payload) {
 | 
			
		||||
  // This is called when a query was successfull
 | 
			
		||||
  // some hiding and resetting
 | 
			
		||||
  queryResultsExportElement.classList.add("disabled");
 | 
			
		||||
  queryResultsDeterminateElement.style.width = "0%";
 | 
			
		||||
  queryResultsProgressElement.classList.remove("hide");
 | 
			
		||||
  queryResultsUserFeedbackElement.classList.remove("hide");
 | 
			
		||||
  // some initial values
 | 
			
		||||
  receivedMatchCountElement.innerText = "0";
 | 
			
		||||
  textLookupCountElement.innerText = "0";
 | 
			
		||||
  matchCountElement.innerText = payload.match_count;
 | 
			
		||||
  // always re initializes results to delete old results from it
 | 
			
		||||
  // this has to be done here again because the last chunk from old results was still being recieved
 | 
			
		||||
  results.clearAll()
 | 
			
		||||
  // Get query string again
 | 
			
		||||
  results.data.getQueryStr(queryFormElement);
 | 
			
		||||
  results.data.match_count = payload.match_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function queryRenderResults(payload) {
 | 
			
		||||
  let resultItems;  // array of built html result items row element
 | 
			
		||||
  // This is called when results are transmitted and being recieved
 | 
			
		||||
  console.log("Current recieved chunk:", payload.chunk);
 | 
			
		||||
  if (payload.chunk.cpos_ranges == true) {
 | 
			
		||||
    results.data["cpos_ranges"] = true;
 | 
			
		||||
  } else {
 | 
			
		||||
    results.data["cpos_ranges"] = false;
 | 
			
		||||
  }
 | 
			
		||||
  // update progress bar
 | 
			
		||||
  queryResultsDeterminateElement.style.width = `${payload.progress}%`;
 | 
			
		||||
  // building the result list js list from incoming chunk
 | 
			
		||||
  resultItems = []; // list for holding every row item
 | 
			
		||||
  // get infos for full match row
 | 
			
		||||
  for (let [index, match] of payload.chunk.matches.entries()) {
 | 
			
		||||
    resultItems.push({...match, ...{"index": index + results.data.matches.length}});
 | 
			
		||||
  }
 | 
			
		||||
  results.jsList.add(resultItems, (items) => {
 | 
			
		||||
    for (let item of items) {
 | 
			
		||||
      item.elm = results.jsList.createResultRowElement(item, payload.chunk);
 | 
			
		||||
    }
 | 
			
		||||
    results.jsList.update();
 | 
			
		||||
    results.jsList.changeContext(); // sets lr context on first result load
 | 
			
		||||
  });
 | 
			
		||||
  // incorporating new chunk results into full results
 | 
			
		||||
  results.data.matches.push(...payload.chunk.matches);
 | 
			
		||||
  Object.assign(results.data.cpos_lookup, payload.chunk.cpos_lookup);
 | 
			
		||||
  Object.assign(results.data.text_lookup, payload.chunk.text_lookup);
 | 
			
		||||
  // show user current and total match count
 | 
			
		||||
  receivedMatchCountElement.innerText = `${results.data.matches.length}`;
 | 
			
		||||
  textLookupCountElement.innerText = `${Object.keys(results.data.text_lookup).length}`;
 | 
			
		||||
  console.log("Results recieved:", results.data);
 | 
			
		||||
  // upate progress status
 | 
			
		||||
  progress = payload.progress;  // global declaration
 | 
			
		||||
  if (progress === 100) {
 | 
			
		||||
    queryResultsProgressElement.classList.add("hide");
 | 
			
		||||
    queryResultsUserFeedbackElement.classList.add("hide");
 | 
			
		||||
    queryResultsExportElement.classList.remove("disabled");
 | 
			
		||||
    results.jsList.activateInspect();
 | 
			
		||||
  }
 | 
			
		||||
  // inital expert mode check and activation
 | 
			
		||||
  if (expertModeSwitchElement.checked) {
 | 
			
		||||
    results.jsList.expertModeOn("query-display");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,219 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * The nopaque object is used as a namespace for nopaque specific functions and
 | 
			
		||||
 * variables.
 | 
			
		||||
 */
 | 
			
		||||
var nopaque = {};
 | 
			
		||||
 | 
			
		||||
// nopaque ressources
 | 
			
		||||
nopaque.socket = undefined;
 | 
			
		||||
 | 
			
		||||
// User data
 | 
			
		||||
nopaque.user = {};
 | 
			
		||||
nopaque.user.isAuthenticated = undefined;
 | 
			
		||||
nopaque.user.settings = {};
 | 
			
		||||
nopaque.user.settings.darkMode = undefined;
 | 
			
		||||
nopaque.corporaSubscribers = [];
 | 
			
		||||
nopaque.jobsSubscribers = [];
 | 
			
		||||
 | 
			
		||||
// Foreign user (user inspected with admin credentials) data
 | 
			
		||||
nopaque.foreignUser = {};
 | 
			
		||||
nopaque.foreignUser.isAuthenticated = undefined;
 | 
			
		||||
nopaque.foreignUser.settings = {};
 | 
			
		||||
nopaque.foreignUser.settings.darkMode = undefined;
 | 
			
		||||
nopaque.foreignCorporaSubscribers = [];
 | 
			
		||||
nopaque.foreignJobsSubscribers = [];
 | 
			
		||||
 | 
			
		||||
nopaque.flashedMessages = undefined;
 | 
			
		||||
 | 
			
		||||
// nopaque functions
 | 
			
		||||
nopaque.socket = {};
 | 
			
		||||
nopaque.socket.init = function() {
 | 
			
		||||
  nopaque.socket = io({transports: ['websocket']});
 | 
			
		||||
  // Add event handlers
 | 
			
		||||
  nopaque.socket.on("user_data_stream_init", function(msg) {
 | 
			
		||||
    nopaque.user = JSON.parse(msg);
 | 
			
		||||
    for (let subscriber of nopaque.corporaSubscribers) {subscriber._init(nopaque.user.corpora);}
 | 
			
		||||
    for (let subscriber of nopaque.jobsSubscribers) {subscriber._init(nopaque.user.jobs);}
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  nopaque.socket.on("user_data_stream_update", function(msg) {
 | 
			
		||||
    var patch;
 | 
			
		||||
 | 
			
		||||
    patch = JSON.parse(msg);
 | 
			
		||||
    nopaque.user = jsonpatch.apply_patch(nopaque.user, patch);
 | 
			
		||||
    corpora_patch = patch.filter(operation => operation.path.startsWith("/corpora"));
 | 
			
		||||
    jobs_patch = patch.filter(operation => operation.path.startsWith("/jobs"));
 | 
			
		||||
    for (let subscriber of nopaque.corporaSubscribers) {subscriber._update(corpora_patch);}
 | 
			
		||||
    for (let subscriber of nopaque.jobsSubscribers) {subscriber._update(jobs_patch);}
 | 
			
		||||
    if (["all", "end"].includes(nopaque.user.settings.job_status_site_notifications)) {
 | 
			
		||||
      for (operation of jobs_patch) {
 | 
			
		||||
        /* "/jobs/{jobId}/..." -> ["{jobId}", ...] */
 | 
			
		||||
        pathArray = operation.path.split("/").slice(2);
 | 
			
		||||
        if (operation.op === "replace" && pathArray[1] === "status") {
 | 
			
		||||
          if (nopaque.user.settings.job_status_site_notifications === "end" && !["complete", "failed"].includes(operation.value)) {continue;}
 | 
			
		||||
          nopaque.flash(`[<a href="/jobs/${pathArray[0]}">${nopaque.user.jobs[pathArray[0]].title}</a>] New status: ${operation.value}`, "job");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  nopaque.socket.on("foreign_user_data_stream_init", function(msg) {
 | 
			
		||||
    nopaque.foreignUser = JSON.parse(msg);
 | 
			
		||||
    for (let subscriber of nopaque.foreignCorporaSubscribers) {subscriber._init(nopaque.foreignUser.corpora);}
 | 
			
		||||
    for (let subscriber of nopaque.foreignJobsSubscribers) {subscriber._init(nopaque.foreignUser.jobs);}
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  nopaque.socket.on("foreign_user_data_stream_update", function(msg) {
 | 
			
		||||
    var patch;
 | 
			
		||||
 | 
			
		||||
    patch = JSON.parse(msg);
 | 
			
		||||
    nopaque.foreignUser = jsonpatch.apply_patch(nopaque.foreignUser, patch);
 | 
			
		||||
    corpora_patch = patch.filter(operation => operation.path.startsWith("/corpora"));
 | 
			
		||||
    jobs_patch = patch.filter(operation => operation.path.startsWith("/jobs"));
 | 
			
		||||
    for (let subscriber of nopaque.foreignCorporaSubscribers) {subscriber._update(corpora_patch);}
 | 
			
		||||
    for (let subscriber of nopaque.foreignJobsSubscribers) {subscriber._update(jobs_patch);}
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nopaque.Forms = {};
 | 
			
		||||
nopaque.Forms.init = function() {
 | 
			
		||||
  var abortRequestElement, parentElement, progressElement, progressModal,
 | 
			
		||||
      progressModalElement, request, submitElement;
 | 
			
		||||
 | 
			
		||||
  for (let form of document.querySelectorAll(".nopaque-submit-form")) {
 | 
			
		||||
    submitElement = form.querySelector('button[type="submit"]');
 | 
			
		||||
    submitElement.addEventListener("click", function() {
 | 
			
		||||
      for (let selectElement of form.querySelectorAll('select')) {
 | 
			
		||||
        if (selectElement.value === "") {
 | 
			
		||||
          parentElement = selectElement.closest(".input-field");
 | 
			
		||||
          parentElement.querySelector(".select-dropdown").classList.add("invalid");
 | 
			
		||||
          for (let helperTextElement of parentElement.querySelectorAll(".helper-text")) {
 | 
			
		||||
            helperTextElement.remove();
 | 
			
		||||
          }
 | 
			
		||||
          parentElement.insertAdjacentHTML("beforeend", `<span class="helper-text red-text">Please select an option.</span>`);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    request = new XMLHttpRequest();
 | 
			
		||||
    if (form.dataset.hasOwnProperty("progressModal")) {
 | 
			
		||||
      progressModalElement = document.getElementById(form.dataset.progressModal);
 | 
			
		||||
      progressModal = M.Modal.getInstance(progressModalElement);
 | 
			
		||||
      progressModal.options.dismissible = false;
 | 
			
		||||
      abortRequestElement = progressModalElement.querySelector(".abort-request");
 | 
			
		||||
      abortRequestElement.addEventListener("click", function() {request.abort();});
 | 
			
		||||
      progressElement = progressModalElement.querySelector(".determinate");
 | 
			
		||||
    }
 | 
			
		||||
    form.addEventListener("submit", function(event) {
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      var formData;
 | 
			
		||||
 | 
			
		||||
      formData = new FormData(form);
 | 
			
		||||
      // Initialize progress modal
 | 
			
		||||
      if (progressModalElement) {
 | 
			
		||||
        progressElement.style.width = "0%";
 | 
			
		||||
        progressModal.open();
 | 
			
		||||
      }
 | 
			
		||||
      request.open("POST", window.location.href);
 | 
			
		||||
      request.send(formData);
 | 
			
		||||
    });
 | 
			
		||||
    request.addEventListener("load", function(event) {
 | 
			
		||||
      var fieldElement;
 | 
			
		||||
 | 
			
		||||
      if (request.status === 201) {
 | 
			
		||||
        window.location.href = JSON.parse(this.responseText).redirect_url;
 | 
			
		||||
      }
 | 
			
		||||
      if (request.status === 400) {
 | 
			
		||||
        console.log(request);
 | 
			
		||||
        for (let [field, errors] of Object.entries(JSON.parse(this.responseText))) {
 | 
			
		||||
          fieldElement = form.querySelector(`input[name$="${field}"]`).closest(".input-field");
 | 
			
		||||
          for (let error of errors) {
 | 
			
		||||
            fieldElement.insertAdjacentHTML("beforeend", `<span class="helper-text red-text">${error}</span>`);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (progressModalElement) {
 | 
			
		||||
          progressModal.close();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (request.status === 500) {
 | 
			
		||||
        location.reload();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    if (progressModalElement) {
 | 
			
		||||
      request.upload.addEventListener("progress", function(event) {
 | 
			
		||||
        progressElement.style.width = Math.floor(100 * event.loaded / event.total).toString() + "%";
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nopaque.Navigation = {};
 | 
			
		||||
nopaque.Navigation.init = function() {
 | 
			
		||||
  /* ### Initialize sidenav-main ### */
 | 
			
		||||
  for (let entry of document.querySelectorAll("#sidenav-main a")) {
 | 
			
		||||
    if (entry.href === window.location.href) {
 | 
			
		||||
      entry.parentNode.classList.add("active");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
nopaque.flash = function() {
 | 
			
		||||
  var classes, toast, toastActionElement;
 | 
			
		||||
 | 
			
		||||
  switch (arguments.length) {
 | 
			
		||||
    case 1:
 | 
			
		||||
      category = "message";
 | 
			
		||||
      message = arguments[0];
 | 
			
		||||
      break;
 | 
			
		||||
    case 2:
 | 
			
		||||
      message = arguments[0];
 | 
			
		||||
      category = arguments[1];
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      console.error("Usage: nopaque.flash(message) or nopaque.flash(message, category)")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (category) {
 | 
			
		||||
    case "corpus":
 | 
			
		||||
      message = `<i class="left material-icons">book</i>${message}`;
 | 
			
		||||
      break;
 | 
			
		||||
    case "error":
 | 
			
		||||
      message = `<i class="left material-icons red-text">error</i>${message}`;
 | 
			
		||||
      break;
 | 
			
		||||
    case "job":
 | 
			
		||||
      message = `<i class="left material-icons">work</i>${message}`;
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      message = `<i class="left material-icons">notifications</i>${message}`;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  toast = M.toast({html: `<span>${message}</span>
 | 
			
		||||
                          <button data-action="close" class="btn-flat toast-action white-text">
 | 
			
		||||
                            <i class="material-icons">close</i>
 | 
			
		||||
                          </button>`});
 | 
			
		||||
  toastActionElement = toast.el.querySelector('.toast-action[data-action="close"]');
 | 
			
		||||
  if (toastActionElement) {
 | 
			
		||||
    toastActionElement.addEventListener("click", function() {
 | 
			
		||||
      toast.dismiss();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
document.addEventListener("DOMContentLoaded", () => {
 | 
			
		||||
  // Disable all option elements with no value
 | 
			
		||||
  for (let optionElement of document.querySelectorAll('option[value=""]')) {
 | 
			
		||||
    optionElement.disabled = true;
 | 
			
		||||
  }
 | 
			
		||||
  M.AutoInit();
 | 
			
		||||
  M.CharacterCounter.init(document.querySelectorAll('input[data-length][type="text"]'));
 | 
			
		||||
  M.Dropdown.init(document.querySelectorAll("#nav-notifications, #nav-account"),
 | 
			
		||||
                  {alignment: "right", constrainWidth: false, coverTrigger: false});
 | 
			
		||||
  nopaque.Forms.init();
 | 
			
		||||
  nopaque.Navigation.init();
 | 
			
		||||
  while (nopaque.flashedMessages.length) {
 | 
			
		||||
    flashedMessage = nopaque.flashedMessages.shift();
 | 
			
		||||
    nopaque.flash(flashedMessage[1], flashedMessage[0]);
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
@@ -1,606 +0,0 @@
 | 
			
		||||
class RessourceList extends List {
 | 
			
		||||
  constructor(idOrElement, subscriberList, type, options={}) {
 | 
			
		||||
    if (!['corpus', 'job'].includes(type)) {
 | 
			
		||||
      console.error("Unknown Type!");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    super(idOrElement, {...RessourceList.options['common'],
 | 
			
		||||
                        ...RessourceList.options[type],
 | 
			
		||||
                        ...options});
 | 
			
		||||
    this.type = type;
 | 
			
		||||
    subscriberList.push(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  _init(ressources) {
 | 
			
		||||
    this.clear();
 | 
			
		||||
    this.addRessources(Object.values(ressources));
 | 
			
		||||
    this.sort("creation_date", {order: "desc"});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  _update(patch) {
 | 
			
		||||
    let item, pathArray;
 | 
			
		||||
 | 
			
		||||
    for (let operation of patch) {
 | 
			
		||||
      /* "/{ressourceName}/{ressourceId}/..." -> ["{ressourceId}", "..."] */
 | 
			
		||||
      pathArray = operation.path.split("/").slice(2);
 | 
			
		||||
      switch(operation.op) {
 | 
			
		||||
        case "add":
 | 
			
		||||
          if (pathArray.includes("results")) {break;}
 | 
			
		||||
          this.addRessources([operation.value]);
 | 
			
		||||
          break;
 | 
			
		||||
        case "remove":
 | 
			
		||||
          this.remove("id", pathArray[0]);
 | 
			
		||||
          break;
 | 
			
		||||
        case "replace":
 | 
			
		||||
          item = this.get("id", pathArray[0])[0];
 | 
			
		||||
          switch(pathArray[1]) {
 | 
			
		||||
            case "status":
 | 
			
		||||
              item.values({status: operation.value,
 | 
			
		||||
                           "analyse-link": ["analysing", "prepared", "start analysis"].includes(operation.value) ? `/corpora/${pathArray[0]}/analyse` : ""});
 | 
			
		||||
              break;
 | 
			
		||||
            default:
 | 
			
		||||
              break;
 | 
			
		||||
          }
 | 
			
		||||
        default:
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  addRessources(ressources) {
 | 
			
		||||
    this.add(ressources.map(x => RessourceList.dataMapper[this.type](x)));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
RessourceList.dataMapper = {
 | 
			
		||||
  corpus: corpus => ({creation_date: corpus.creation_date,
 | 
			
		||||
                      description: corpus.description,
 | 
			
		||||
                      id: corpus.id,
 | 
			
		||||
                      "analyse-link": ["analysing", "prepared", "start analysis"].includes(corpus.status) ? `/corpora/${corpus.id}/analyse` : "",
 | 
			
		||||
                      "edit-link": `/corpora/${corpus.id}`,
 | 
			
		||||
                      status: corpus.status,
 | 
			
		||||
                      title: corpus.title}),
 | 
			
		||||
  job: job => ({creation_date: job.creation_date,
 | 
			
		||||
                description: job.description,
 | 
			
		||||
                id: job.id,
 | 
			
		||||
                link: `/jobs/${job.id}`,
 | 
			
		||||
                service: job.service,
 | 
			
		||||
                status: job.status,
 | 
			
		||||
                title: job.title})
 | 
			
		||||
};
 | 
			
		||||
RessourceList.options = {
 | 
			
		||||
  common: {page: 4, pagination: {innerWindow: 8, outerWindow: 1}},
 | 
			
		||||
  corpus: {item: `<tr>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <a class="btn-floating disabled">
 | 
			
		||||
                        <i class="material-icons service">book</i>
 | 
			
		||||
                      </a>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <b class="title"></b><br>
 | 
			
		||||
                      <i class="description"></i>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                      <span class="badge new status" data-badge-caption="">
 | 
			
		||||
                      </span>
 | 
			
		||||
                    </td>
 | 
			
		||||
                    <td class="right-align">
 | 
			
		||||
                      <a class="btn-small edit-link waves-effect waves-light">
 | 
			
		||||
                        <i class="material-icons">edit</i>
 | 
			
		||||
                      </a>
 | 
			
		||||
                      <a class="btn-small analyse-link waves-effect waves-light">
 | 
			
		||||
                        Analyse<i class="material-icons right">search</i>
 | 
			
		||||
                      </a>
 | 
			
		||||
                    </td>
 | 
			
		||||
                  </tr>`,
 | 
			
		||||
           valueNames: ["creation_date", "description", "title",
 | 
			
		||||
                        {data: ["id"]},
 | 
			
		||||
                        {name: "analyse-link", attr: "href"},
 | 
			
		||||
                        {name: "edit-link", attr: "href"},
 | 
			
		||||
                        {name: "status", attr: "data-status"}]},
 | 
			
		||||
  job: {item: `<tr>
 | 
			
		||||
                 <td>
 | 
			
		||||
                   <a class="btn-floating disabled">
 | 
			
		||||
                     <i class="material-icons service"></i>
 | 
			
		||||
                   </a>
 | 
			
		||||
                 </td>
 | 
			
		||||
                 <td>
 | 
			
		||||
                   <b class="title"></b><br>
 | 
			
		||||
                   <i class="description"></i>
 | 
			
		||||
                 </td>
 | 
			
		||||
                 <td>
 | 
			
		||||
                   <span class="badge new status" data-badge-caption=""></span>
 | 
			
		||||
                 </td>
 | 
			
		||||
                 <td class="right-align">
 | 
			
		||||
                   <a class="btn-small link waves-effect waves-light">
 | 
			
		||||
                    View<i class="material-icons right">send</i>
 | 
			
		||||
                  </a>
 | 
			
		||||
                 </td>
 | 
			
		||||
               </tr>`,
 | 
			
		||||
        valueNames: ["creation_date", "description", "title",
 | 
			
		||||
                     {data: ["id"]},
 | 
			
		||||
                     {name: "link", attr: "href"},
 | 
			
		||||
                     {name: "service", attr: "data-service"},
 | 
			
		||||
                     {name: "status", attr: "data-status"}]}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResultsList extends List {
 | 
			
		||||
  constructor(idOrElement, options={}) {
 | 
			
		||||
  super(idOrElement, options);
 | 
			
		||||
  this.eventTokens = {};  // all span tokens which are holdeing events if expert
 | 
			
		||||
  // mode is on. Collected here to delete later on
 | 
			
		||||
  this.currentExpertTokenElements = {}; // all token elements which have added
 | 
			
		||||
  // classes like chip and hoverable for expert view. Collected
 | 
			
		||||
  //here to delete later on
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // get display options from display options form element
 | 
			
		||||
  static getDisplayOptions(displayOptionsFormElement) {
 | 
			
		||||
    // gets display options parameters
 | 
			
		||||
    let displayOptionsFormData
 | 
			
		||||
    let displayOptionsData;
 | 
			
		||||
    displayOptionsFormData = new FormData(displayOptionsFormElement);
 | 
			
		||||
    displayOptionsData =
 | 
			
		||||
      {
 | 
			
		||||
        "resultsPerPage": displayOptionsFormData.get("display-options-form-results_per_page"),
 | 
			
		||||
        "resultsContex": displayOptionsFormData.get("display-options-form-result_context"),
 | 
			
		||||
        "expertMode": displayOptionsFormData.get("display-options-form-expert_mode")
 | 
			
		||||
      };
 | 
			
		||||
    return displayOptionsData
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ###### Functions to inspect one match, to show more details ######
 | 
			
		||||
  // activate inspect buttons if progress is 100
 | 
			
		||||
  activateInspect() {
 | 
			
		||||
    let inspectBtnElements;
 | 
			
		||||
    if (progress === 100) {
 | 
			
		||||
      inspectBtnElements = document.getElementsByClassName("inspect");
 | 
			
		||||
      for (let inspectBtn of inspectBtnElements) {
 | 
			
		||||
        inspectBtn.classList.remove("disabled");
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //gets result cpos infos for one dataIndex to send back to the server
 | 
			
		||||
  inspect(dataIndex) {
 | 
			
		||||
    this.contextId = dataIndex;
 | 
			
		||||
    let contextResultsElement;
 | 
			
		||||
    contextResultsElement = document.getElementById("context-results");
 | 
			
		||||
    contextResultsElement.innerHTML = "";  // clear it from old inspects
 | 
			
		||||
    contextModal.open();
 | 
			
		||||
    nopaque.socket.emit("corpus_analysis_inspect_match",
 | 
			
		||||
            {
 | 
			
		||||
              payload: {
 | 
			
		||||
                       first_cpos: results.data.matches[dataIndex].c[0],
 | 
			
		||||
                       last_cpos: results.data.matches[dataIndex].c[1],
 | 
			
		||||
                      }
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  HTMLTStrToElement(htmlStr) {
 | 
			
		||||
    // https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
 | 
			
		||||
    let template = document.createElement("template");
 | 
			
		||||
    htmlStr = htmlStr.trim();
 | 
			
		||||
    template.innerHTML = htmlStr;
 | 
			
		||||
    return template.content.firstChild;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  showMatchContext(response) {
 | 
			
		||||
    this.contextData;
 | 
			
		||||
    let c;
 | 
			
		||||
    let contextModalLoading;
 | 
			
		||||
    let contextModalReady;
 | 
			
		||||
    let contextResultsElement;
 | 
			
		||||
    let highlightSentencesSwitchElement;
 | 
			
		||||
    let htmlTokenStr;
 | 
			
		||||
    let lc;
 | 
			
		||||
    let modalExpertModeSwitchElement;
 | 
			
		||||
    let modalTokenElements;
 | 
			
		||||
    let nrOfContextSentences;
 | 
			
		||||
    let partElement;
 | 
			
		||||
    let rc;
 | 
			
		||||
    let token;
 | 
			
		||||
    let tokenHTMLArray;
 | 
			
		||||
    let tokenHTMlElement;
 | 
			
		||||
    let uniqueContextS;
 | 
			
		||||
    let uniqueS;
 | 
			
		||||
 | 
			
		||||
    this.contextData = response.payload;
 | 
			
		||||
    this.contextData["query"] = results.data.query;
 | 
			
		||||
    this.contextData["context_id"] = this.contextId;
 | 
			
		||||
    Object.assign(this.contextData, results.metaData);
 | 
			
		||||
    contextResultsElement = document.getElementById("context-results");
 | 
			
		||||
    modalExpertModeSwitchElement = document.getElementById("inspect-display-options-form-expert_mode_inspect");
 | 
			
		||||
    highlightSentencesSwitchElement = document.getElementById("inspect-display-options-form-highlight_sentences");
 | 
			
		||||
    nrOfContextSentences = document.getElementById("context-sentences");
 | 
			
		||||
    uniqueS = new Set();
 | 
			
		||||
    uniqueContextS = new Set();
 | 
			
		||||
    // check if cpos ranges are used or not
 | 
			
		||||
    if (this.contextData.cpos_ranges == true) {
 | 
			
		||||
      // python range like function from MDN
 | 
			
		||||
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
 | 
			
		||||
      const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
 | 
			
		||||
      lc = range(this.contextData.match.lc[0], this.contextData.match.lc[1], 1)
 | 
			
		||||
      c = range(this.contextData.match.c[0], this.contextData.match.c[1], 1)
 | 
			
		||||
      rc = range(this.contextData.match.rc[0], this.contextData.match.rc[1], 1)
 | 
			
		||||
    } else {
 | 
			
		||||
      lc = this.contextData.match.lc;
 | 
			
		||||
      c = this.contextData.match.c;
 | 
			
		||||
      rc = this.contextData.match.rc;
 | 
			
		||||
    }
 | 
			
		||||
    // create sentence strings as tokens
 | 
			
		||||
    tokenHTMLArray = [];
 | 
			
		||||
    for (let cpos of lc) {
 | 
			
		||||
      token = this.contextData.cpos_lookup[cpos];
 | 
			
		||||
      uniqueS.add(token.s)
 | 
			
		||||
      htmlTokenStr = `<span class="token"` +
 | 
			
		||||
                           `data-sid="${token.s}"` +
 | 
			
		||||
                           `data-cpos="${cpos}">` +
 | 
			
		||||
                       `${token.word}` +
 | 
			
		||||
                     `</span>`;
 | 
			
		||||
      tokenHTMlElement = this.HTMLTStrToElement(htmlTokenStr)
 | 
			
		||||
      tokenHTMLArray.push(tokenHTMlElement);
 | 
			
		||||
    }
 | 
			
		||||
    for (let cpos of c) {
 | 
			
		||||
      token = this.contextData.cpos_lookup[cpos];
 | 
			
		||||
      uniqueContextS.add(token.s);
 | 
			
		||||
      uniqueS.add(token.s);
 | 
			
		||||
      htmlTokenStr = `<span class="token bold light-green"` +
 | 
			
		||||
                           `data-sid="${token.s}"` +
 | 
			
		||||
                           `data-cpos="${cpos}"` +
 | 
			
		||||
                           `style="text-decoration-line: underline;">` +
 | 
			
		||||
                       `${token.word}` +
 | 
			
		||||
                     `</span>`;
 | 
			
		||||
      tokenHTMlElement = this.HTMLTStrToElement(htmlTokenStr)
 | 
			
		||||
      tokenHTMLArray.push(tokenHTMlElement);
 | 
			
		||||
    }
 | 
			
		||||
    this.contextData["context_s_ids"] = Array.from(uniqueContextS);
 | 
			
		||||
    for (let cpos of rc) {
 | 
			
		||||
      token = this.contextData.cpos_lookup[cpos];
 | 
			
		||||
      uniqueS.add(token.s)
 | 
			
		||||
      htmlTokenStr = `<span class="token"` +
 | 
			
		||||
                           `data-sid="${token.s}"` +
 | 
			
		||||
                           `data-cpos="${cpos}">` +
 | 
			
		||||
                       `${token.word}` +
 | 
			
		||||
                     `</span>`;
 | 
			
		||||
      tokenHTMlElement = this.HTMLTStrToElement(htmlTokenStr)
 | 
			
		||||
      tokenHTMLArray.push(tokenHTMlElement);
 | 
			
		||||
    }
 | 
			
		||||
    // console.log(tokenHTMLArray);
 | 
			
		||||
    // console.log(uniqueS);
 | 
			
		||||
 | 
			
		||||
    for (let sId of uniqueS) {
 | 
			
		||||
      let htmlSentence = `<span class="sentence" data-sid="${sId}"></span>`;
 | 
			
		||||
      let sentenceElement = this.HTMLTStrToElement(htmlSentence);
 | 
			
		||||
      for (let tokenElement of tokenHTMLArray) {
 | 
			
		||||
        if (tokenElement.dataset.sid == sId) {
 | 
			
		||||
          sentenceElement.appendChild(tokenElement);
 | 
			
		||||
          sentenceElement.insertAdjacentHTML("beforeend", ` `);
 | 
			
		||||
        } else {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      contextResultsElement.appendChild(sentenceElement);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // add inspect display options events
 | 
			
		||||
    modalExpertModeSwitchElement.onchange = (event) => {
 | 
			
		||||
      if (event.target.checked) {
 | 
			
		||||
        this.expertModeOn("context-results");
 | 
			
		||||
      } else {
 | 
			
		||||
        this.expertModeOff("context-results")
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    highlightSentencesSwitchElement.onchange = (event) => {
 | 
			
		||||
      if (event.target.checked) {
 | 
			
		||||
        this.higlightContextSentences();
 | 
			
		||||
      } else {
 | 
			
		||||
      this.unhighlightContextSentences();
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    nrOfContextSentences.onchange = (event) => {
 | 
			
		||||
      // console.log(event.target.value);
 | 
			
		||||
      this.changeSentenceContext(event.target.value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // checks on new modal opening if switches are checked
 | 
			
		||||
    // if switches are checked functions are executed
 | 
			
		||||
    if (modalExpertModeSwitchElement.checked) {
 | 
			
		||||
      this.expertModeOn("context-results");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (highlightSentencesSwitchElement.checked) {
 | 
			
		||||
      this.higlightContextSentences();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // checks the value of the number of sentences to show on modal opening
 | 
			
		||||
    // sets context sentences accordingly
 | 
			
		||||
    this.changeSentenceContext(nrOfContextSentences.value)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  higlightContextSentences() {
 | 
			
		||||
    let sentences;
 | 
			
		||||
    sentences = document.getElementById("context-results").getElementsByClassName("sentence");
 | 
			
		||||
      for (let s of sentences) {
 | 
			
		||||
        s.insertAdjacentHTML("beforeend", `<span><br><br></span>`)
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  unhighlightContextSentences() {
 | 
			
		||||
    let sentences;
 | 
			
		||||
    let br;
 | 
			
		||||
    sentences = document.getElementById("context-results").getElementsByClassName("sentence");
 | 
			
		||||
      for (let s of sentences) {
 | 
			
		||||
        br = s.lastChild;
 | 
			
		||||
        br.remove();
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  changeSentenceContext(sValue, maxSValue=10) {
 | 
			
		||||
    let array;
 | 
			
		||||
    let sentences;
 | 
			
		||||
    let toHideArray;
 | 
			
		||||
    let toShowArray;
 | 
			
		||||
    sValue = maxSValue - sValue;
 | 
			
		||||
    // console.log(sValue);
 | 
			
		||||
    sentences = document.getElementById("context-results").getElementsByClassName("sentence");
 | 
			
		||||
    array = Array.from(sentences);
 | 
			
		||||
    if (sValue != 0) {
 | 
			
		||||
      toHideArray = array.slice(0, sValue).concat(array.slice(-(sValue)));
 | 
			
		||||
      toShowArray = array.slice(sValue, 9).concat(array.slice(9, -(sValue)))
 | 
			
		||||
    } else {
 | 
			
		||||
      toHideArray = [];
 | 
			
		||||
      toShowArray = array;
 | 
			
		||||
    }
 | 
			
		||||
    // console.log(array);
 | 
			
		||||
    // console.log("#######");
 | 
			
		||||
    // console.log(toHideArray);
 | 
			
		||||
    for (let s of toHideArray) {
 | 
			
		||||
      s.classList.add("hide");
 | 
			
		||||
    }
 | 
			
		||||
    for (let s of toShowArray) {
 | 
			
		||||
      s.classList.remove("hide");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ###### Display options changing live how the matches are being displayed ######
 | 
			
		||||
 | 
			
		||||
  // Event function that changes the shown hits per page.
 | 
			
		||||
  // Just alters the resultsList.page property
 | 
			
		||||
  changeHitsPerPage(event) {
 | 
			
		||||
    try {
 | 
			
		||||
      // console.log(this);
 | 
			
		||||
      this.page = event.target.value;
 | 
			
		||||
      this.update();
 | 
			
		||||
      this.activateInspect();
 | 
			
		||||
      if (expertModeSwitchElement.checked) {
 | 
			
		||||
        this.expertModeOn("query-display");  // page holds new result rows, so add new tooltips
 | 
			
		||||
      }
 | 
			
		||||
      nopaque.flash("Updated matches per page.", "corpus")
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      // console.log(e);
 | 
			
		||||
      // console.log("resultsList has no results right now.");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Event function triggered on context select change
 | 
			
		||||
  // also if pagination is clicked
 | 
			
		||||
  changeContext(event) {
 | 
			
		||||
    let array;
 | 
			
		||||
    let lc;
 | 
			
		||||
    let newContextValue;
 | 
			
		||||
    let rc;
 | 
			
		||||
    try {
 | 
			
		||||
        if (event.type === "change") {
 | 
			
		||||
            nopaque.flash("Updated context per match!", "corpus");
 | 
			
		||||
        }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
    } finally {
 | 
			
		||||
        newContextValue = document.getElementById("display-options-form-result_context").value;
 | 
			
		||||
        lc = document.getElementsByClassName("left-context");
 | 
			
		||||
        rc = document.getElementsByClassName("right-context");
 | 
			
		||||
        for (let element of lc) {
 | 
			
		||||
          array = Array.from(element.childNodes);
 | 
			
		||||
          for (let element of array.reverse().slice(newContextValue)) {
 | 
			
		||||
            element.classList.add("hide");
 | 
			
		||||
          }
 | 
			
		||||
          for (let element of array.slice(0, newContextValue)) {
 | 
			
		||||
            element.classList.remove("hide");
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        for (let element of rc) {
 | 
			
		||||
          array = Array.from(element.childNodes);
 | 
			
		||||
          for (let element of array.slice(newContextValue)) {
 | 
			
		||||
            element.classList.add("hide");
 | 
			
		||||
          }
 | 
			
		||||
          for (let element of array.slice(0, newContextValue)) {
 | 
			
		||||
            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
 | 
			
		||||
  eventHandlerCheck(event) {
 | 
			
		||||
    if (expertModeSwitchElement.checked) {
 | 
			
		||||
      this.expertModeOn("query-display");
 | 
			
		||||
    } else if (!expertModeSwitchElement.checked) {
 | 
			
		||||
      event.preventDefault();
 | 
			
		||||
      this.expertModeOff("query-display");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // function to create a tooltip for the current hovered token
 | 
			
		||||
  tooltipEventCreate(event) {
 | 
			
		||||
    // console.log("Create Tooltip on mouseover.");
 | 
			
		||||
    let token;
 | 
			
		||||
    token = results.data.cpos_lookup[event.target.dataset.cpos];
 | 
			
		||||
    if (!token) {
 | 
			
		||||
      token = this.contextData.cpos_lookup[event.target.dataset.cpos];
 | 
			
		||||
    }
 | 
			
		||||
    this.addToolTipToTokenElement(event.target, token);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Function to destroy the current Tooltip for the current hovered tooltip
 | 
			
		||||
  // on mouse leave
 | 
			
		||||
  tooltipEventDestroy(event) {
 | 
			
		||||
    // console.log("Tooltip destroy on leave.");
 | 
			
		||||
    this.currentTooltipElement.destroy();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  expertModeOn(htmlId) {
 | 
			
		||||
    // torn the expert mode on for all tokens in the DOM element identified by its htmlID
 | 
			
		||||
    this.currentExpertTokenElements[htmlId] = document.getElementById(htmlId).getElementsByClassName("token");
 | 
			
		||||
    this.tooltipEventCreateBind = this.tooltipEventCreate.bind(this);
 | 
			
		||||
    this.tooltipEventDestroyBind = this.tooltipEventDestroy.bind(this);
 | 
			
		||||
    this.eventTokens[htmlId] = [];
 | 
			
		||||
    for (let tokenElement of this.currentExpertTokenElements[htmlId]) {
 | 
			
		||||
      tokenElement.classList.add("chip", "hoverable", "expert-view");
 | 
			
		||||
      tokenElement.onmouseover = this.tooltipEventCreateBind;
 | 
			
		||||
      tokenElement.onmouseout = this.tooltipEventDestroyBind;
 | 
			
		||||
      this.eventTokens[htmlId].push(tokenElement);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // fuction that creates Tooltip for one token and extracts the corresponding
 | 
			
		||||
  // infos from the result JSON
 | 
			
		||||
  addToolTipToTokenElement(tokenElement, token) {
 | 
			
		||||
    this.currentTooltipElement;
 | 
			
		||||
    this.currentTooltipElement = M.Tooltip.init(tokenElement,
 | 
			
		||||
     {"html": `<table>
 | 
			
		||||
                 <tr>
 | 
			
		||||
                   <th>Token information</th>
 | 
			
		||||
                   <th>Source information</th>
 | 
			
		||||
                 </tr>
 | 
			
		||||
                 <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}
 | 
			
		||||
                   </td>
 | 
			
		||||
                   <td class="left-align">
 | 
			
		||||
                     Title: ${results.data.text_lookup[token.text].title}
 | 
			
		||||
                     <br>
 | 
			
		||||
                     Author: ${results.data.text_lookup[token.text].author}
 | 
			
		||||
                     <br>
 | 
			
		||||
                     Publishing year: ${results.data.text_lookup[token.text].publishing_year}
 | 
			
		||||
                   </td>
 | 
			
		||||
                 </tr>
 | 
			
		||||
               </table>`}
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // function to remove extra informations and animations from tokens
 | 
			
		||||
  expertModeOff(htmlId) {
 | 
			
		||||
    // console.log("Expert mode is off.");
 | 
			
		||||
    for (let tokenElement of this.currentExpertTokenElements[htmlId]) {
 | 
			
		||||
      tokenElement.classList.remove("chip", "hoverable", "expert-view");
 | 
			
		||||
    }
 | 
			
		||||
    this.currentExpertTokenElements[htmlId] = [];
 | 
			
		||||
 | 
			
		||||
    for (let eventToken of this.eventTokens[htmlId]) {
 | 
			
		||||
      eventToken.onmouseover = "";
 | 
			
		||||
      eventToken.onmouseout = "";
 | 
			
		||||
    }
 | 
			
		||||
  this.eventTokens[htmlId] = [];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createResultRowElement(item, chunk) {
 | 
			
		||||
    let c;
 | 
			
		||||
    let cCellElement;
 | 
			
		||||
    let cpos;
 | 
			
		||||
    let inspectBtn
 | 
			
		||||
    let lc;
 | 
			
		||||
    let lcCellElement;
 | 
			
		||||
    let matchNrElement;
 | 
			
		||||
    let matchRowElement;
 | 
			
		||||
    let rc;
 | 
			
		||||
    let rcCellElement;
 | 
			
		||||
    let textTitles;
 | 
			
		||||
    let textTitlesCellElement;
 | 
			
		||||
    let token;
 | 
			
		||||
    let values;
 | 
			
		||||
    // gather values from item
 | 
			
		||||
    values = item.values();
 | 
			
		||||
    if (chunk.cpos_ranges == true) {
 | 
			
		||||
      // python range like function from MDN
 | 
			
		||||
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Sequence_generator_(range)
 | 
			
		||||
      const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
 | 
			
		||||
      lc = range(values.lc[0], values.lc[1], 1)
 | 
			
		||||
      c = range(values.c[0], values.c[1], 1)
 | 
			
		||||
      rc = range(values.rc[0], values.rc[1], 1)
 | 
			
		||||
    } else {
 | 
			
		||||
      lc = values.lc;
 | 
			
		||||
      c = values.c;
 | 
			
		||||
      rc = values.rc;
 | 
			
		||||
    }
 | 
			
		||||
    // get infos for full match row
 | 
			
		||||
    matchRowElement = document.createElement("tr");
 | 
			
		||||
    matchRowElement.setAttribute("data-index", values.index)
 | 
			
		||||
    lcCellElement = document.createElement("td");
 | 
			
		||||
    lcCellElement.classList.add("left-context");
 | 
			
		||||
    matchRowElement.appendChild(lcCellElement);
 | 
			
		||||
    for (cpos of lc) {
 | 
			
		||||
      token = chunk.cpos_lookup[cpos];
 | 
			
		||||
      lcCellElement.insertAdjacentHTML("beforeend",
 | 
			
		||||
        `<span class="token" data-cpos="${cpos}">${token.word} </span>`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // get infos for hit of match
 | 
			
		||||
    textTitles = new Set();
 | 
			
		||||
    cCellElement = document.createElement("td");
 | 
			
		||||
    cCellElement.classList.add("match-hit");
 | 
			
		||||
    textTitlesCellElement = document.createElement("td");
 | 
			
		||||
    textTitlesCellElement.classList.add("titles");
 | 
			
		||||
    matchNrElement = document.createElement("td");
 | 
			
		||||
    matchNrElement.classList.add("match-nr");
 | 
			
		||||
    matchRowElement.appendChild(cCellElement);
 | 
			
		||||
    for (cpos of c) {
 | 
			
		||||
      token = chunk.cpos_lookup[cpos];
 | 
			
		||||
      cCellElement.insertAdjacentHTML("beforeend",
 | 
			
		||||
        `<span class="token" data-cpos="${cpos}">${token.word} </span>`);
 | 
			
		||||
      // get text titles of every hit cpos token
 | 
			
		||||
      textTitles.add(chunk.text_lookup[token.text].title);
 | 
			
		||||
      // add button to trigger more context to every match td
 | 
			
		||||
      inspectBtn = document.createElement("a");
 | 
			
		||||
      inspectBtn.setAttribute("class", `btn-floating btn-flat waves-effect` +
 | 
			
		||||
                                       `waves-light grey right inspect disabled`
 | 
			
		||||
                              );
 | 
			
		||||
      inspectBtn.innerHTML = '<i class="material-icons">search</i>';
 | 
			
		||||
      inspectBtn.onclick = () => {this.inspect(values.index)};
 | 
			
		||||
    }
 | 
			
		||||
    // add text titles at front as first td of one row
 | 
			
		||||
    cCellElement.appendChild(inspectBtn);
 | 
			
		||||
    textTitlesCellElement.innerText = [...textTitles].join(", ");
 | 
			
		||||
    matchRowElement.insertAdjacentHTML("afterbegin", textTitlesCellElement.outerHTML);
 | 
			
		||||
    matchNrElement.innerText = values.index + 1;
 | 
			
		||||
    matchRowElement.insertAdjacentHTML("afterbegin", matchNrElement.outerHTML);
 | 
			
		||||
 | 
			
		||||
    // get infos for right context of match
 | 
			
		||||
    rcCellElement = document.createElement("td");
 | 
			
		||||
    rcCellElement.classList.add("right-context");
 | 
			
		||||
    matchRowElement.appendChild(rcCellElement);
 | 
			
		||||
    for (cpos of rc) {
 | 
			
		||||
      token = chunk.cpos_lookup[cpos];
 | 
			
		||||
      rcCellElement.insertAdjacentHTML("beforeend",
 | 
			
		||||
        `<span class="token" data-cpos="${cpos}">${token.word} </span>`);
 | 
			
		||||
    }
 | 
			
		||||
    return matchRowElement
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user