From 9781425602a80f17a11c61bf19a72a6de351cf3e Mon Sep 17 00:00:00 2001
From: Inga Kirschnick <inga.kirschnick@uni-bielefeld.de>
Date: Wed, 3 Aug 2022 16:21:11 +0200
Subject: [PATCH] Query Builder New Design

---
 app/static/js/CorpusAnalysis/QueryBuilder.js | 103 ++++++++++++++++---
 app/templates/test/analyse_corpus.html.j2    |  95 ++++++++++-------
 2 files changed, 146 insertions(+), 52 deletions(-)

diff --git a/app/static/js/CorpusAnalysis/QueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder.js
index c8916c38..fb4f6b2e 100644
--- a/app/static/js/CorpusAnalysis/QueryBuilder.js
+++ b/app/static/js/CorpusAnalysis/QueryBuilder.js
@@ -2,29 +2,27 @@ class ConcordanceQueryBuilder {
 
     constructor() {
 
-    
-      this.positionalAttrList = {
-        "emptyToken": {prettyText: "empty token", cqlOpening: "[", tokenValue:"", cqlClosing: "]"},
-        "word": {prettyText: "word", cqlOpening: "[word=", tokenValue: "", cqlClosing: "]"},
-        "lemma": {prettyText: "lemma", cqlOpening: "[lemma=", tokenValue:"", cqlClosing: "]"},
-        "pos": {prettyText: "pos", cqlOpening: "[pos=", tokenValue:"",  cqlClosing: "]"},
-        "simplePos": {prettyText: "simple_pos", cqlOpening: "[simple_pos=", tokenValue:"", cqlClosing: "]"}
-      }  
 
       this.elements = {
         
         counter: 0,
         yourQueryContent: [],
         queryContent:[],
+        closeQueryBuilder: document.querySelector("#close-query-builder"),
 
         //#region QueryBuilder Elements
         concordanceQueryBuilder: document.querySelector("#concordance-query-builder"),
         concordanceQueryBuilderButton: document.querySelector("#concordance-query-builder-button"),
+        positionalAttrButton: document.querySelector('#positional-attr-button'),
+        positionalAttrArea: document.querySelector('#positional-attr'),
         positionalAttr: document.querySelector("#token-attr"),
-        structuralAttr: document.querySelector("#structural-attr"),
+        structuralAttrButton: document.querySelector('#structural-attr-button'),
+        structuralAttrArea: document.querySelector("#structural-attr"),
+        queryContainer: document.querySelector('#query-container'),
         buttonPreparer: document.querySelector("#button-preparer"),
         yourQuery: document.querySelector("#your-query"),
         insertQueryButton: document.querySelector("#insert-query-button"),
+        queryPreview: document.querySelector('#query-preview'),
         tokenQuery: document.querySelector("#token-query"),
         tokenBuilderContent: document.querySelector("#token-builder-content"),
         buildTokenButton: document.querySelector("#build-token-button"),
@@ -113,9 +111,14 @@ class ConcordanceQueryBuilder {
         //#endregion Token Attributes
       }
 
+      this.elements.closeQueryBuilder.addEventListener("click", () => {this.closeQueryBuilderModal();});
+
       this.elements.concordanceQueryBuilderButton.addEventListener("click", () => {this.clearAll();});
       this.elements.insertQueryButton.addEventListener("click", () => {this.insertQuery();});
 
+      this.elements.positionalAttrButton.addEventListener("click", () => {this.showPositionalAttrArea();});
+      this.elements.structuralAttrButton.addEventListener("click", () => {this.showStructuralAttrArea();});
+
       //#region Structural Attribute Event Listeners
       this.elements.sentence.addEventListener("click", () => {this.addSentence();});
       this.elements.entity.addEventListener("click", () => {this.addEntity();});
@@ -492,8 +495,21 @@ class ConcordanceQueryBuilder {
     }
 
     tokenButtonfactory(dataType, prettyText, tokenText) {
+        let chipColor = 'style="background-color:#';
+        if (dataType === 'pos' || dataType === 'word' || dataType === 'lemma' || dataType === 'simplePos'){
+            chipColor += 'EF60B4';
+        }else if (dataType === "emptyToken"){
+            chipColor += '43C6FC';
+        }else if (dataType === "and" || dataType === "or"){
+            chipColor += 'FFCC00';
+        }else if (dataType === "incidenceModifier" || dataType === "betweenNM" || dataType === "exactlyN"){
+            chipColor += '2FBBAB';
+        }else {
+            chipColor = '';
+        }
+
         tokenText = encodeURI(tokenText);
-        this.elements.buttonPreparer.innerHTML += '<a class="btn-small waves-effect waves-light" style="margin-left:3px" data-type="' + dataType + '" data-tokentext="' + tokenText + '">' + prettyText + '</a>';
+        this.elements.buttonPreparer.innerHTML += '<div class="chip"' + chipColor +'" data-type="' + dataType + '" data-tokentext="' + tokenText + '">' + prettyText + '<i class="material-icons close">close</i></div>';
         let tokenDummyButton = this.elements.buttonPreparer.querySelector(':first-child');
         tokenDummyButton.addEventListener("click", () => {this.deleteTokenAttr(tokenDummyButton);});
         this.elements.tokenQuery.appendChild(tokenDummyButton);
@@ -538,7 +554,7 @@ class ConcordanceQueryBuilder {
         let emptyTokenCheck = false;
 
         for (let element of this.elements.tokenQuery.childNodes) {
-            tokenQueryContent += ' ' + element.text + ' ';
+            tokenQueryContent += ' ' + element.firstChild.data + ' ';
             tokenQueryText += ' ' + decodeURI(element.dataset.tokentext);
             if (element.dataset.type === "emptyToken"){
                 emptyTokenCheck = true;
@@ -547,7 +563,7 @@ class ConcordanceQueryBuilder {
 
         if (emptyTokenCheck === false){
             tokenQueryText = '[' + tokenQueryText + ']';
-        }        
+        }
 
         this.buttonfactory('token', tokenQueryContent, tokenQueryText);
         tokenQueryContent = '';
@@ -564,12 +580,66 @@ class ConcordanceQueryBuilder {
 
     //#region General Functions
 
+    closeQueryBuilderModal(){
+        let instance = M.Modal.getInstance(this.elements.concordanceQueryBuilder);
+        instance.close();
+    }
+
+    showPositionalAttrArea(){
+        this.elements.positionalAttrArea.classList.remove('hide');
+        this.elements.structuralAttrArea.classList.add('hide');
+    }
+
+    showStructuralAttrArea(){
+        this.elements.positionalAttrArea.classList.add('hide');
+        this.elements.structuralAttrArea.classList.remove('hide');
+    }
+
     buttonfactory(dataType, prettyText, queryText) {
+        let chipColor = 'style="background-color:#';
+        if (dataType === 'start-sentence' || dataType === 'end-sentence'){
+            chipColor += 'FD9720';
+        }else if (dataType === "start-empty-entity" || dataType === "start-entity" || dataType === "end-entity"){
+            chipColor += 'A6E22D';
+        }else if (dataType === "text-annotation"){
+            chipColor += '2FBBAB';
+        }else if (dataType === "token"){
+            chipColor += '28B3D1';
+        }else {
+            chipColor = '';
+        }
+
         queryText = encodeURI(queryText);
-        this.elements.buttonPreparer.innerHTML += '<a class="btn-small waves-effect waves-light" style="margin-left:3px" data-type="' + dataType + '" data-query="' + queryText + '">' + prettyText + '</a>';
+        this.elements.buttonPreparer.innerHTML += '<div class="chip"' + chipColor +'" data-type="' +  dataType + '" data-query="' + queryText + '">' + prettyText + '<i class="material-icons close">close</i></div>';
         let dummyButton = this.elements.buttonPreparer.querySelector(':first-child');
         dummyButton.addEventListener("click", () => {this.deleteAttr(dummyButton);});
         this.elements.yourQuery.appendChild(dummyButton);
+        this.elements.queryContainer.classList.remove("hide");
+
+        this.queryPreviewBuilder();
+
+    }
+
+    queryPreviewBuilder(){
+        
+        this.elements.yourQueryContent = [];
+        
+        for (let element of this.elements.yourQuery.childNodes) {
+            
+            let queryElement = decodeURI(element.dataset.query);
+            if (queryElement.includes("<")){
+                queryElement = queryElement.replace("<", "&#60;");
+            }
+            if (queryElement.includes(">")){
+                queryElement = queryElement.replace(">", "&#62;");
+            }
+            this.elements.yourQueryContent.push(queryElement);
+        }
+        
+
+        let queryString = this.elements.yourQueryContent.join(' ');
+        queryString += ";";
+        this.elements.queryPreview.innerHTML = queryString;
     }
 
     deleteAttr(attr) {
@@ -639,8 +709,10 @@ class ConcordanceQueryBuilder {
         this.elements.counter -= 1;
         if(this.elements.counter === 0){
             this.elements.insertQueryButton.classList.add("disabled");
-            
+            this.elements.queryContainer.classList.add("hide");
         }
+
+        this.queryPreviewBuilder();
     }
 
     insertQuery() {
@@ -666,9 +738,12 @@ class ConcordanceQueryBuilder {
         this.elements.buildTokenButton.classList.add("disabled");
         this.elements.insertQueryButton.classList.add("disabled");
         this.elements.concordanceQueryBuilder.classList.remove('modal-close');
+        this.elements.positionalAttrArea.classList.add('hide');
+        this.elements.structuralAttrArea.classList.add('hide');
         this.hideEverything(); 
         this.buttonDisabler('start');
         this.elements.yourQuery.innerHTML = '';
+        this.elements.queryContainer.classList.add("hide");
     }
     //#endregion General Functions
 }
diff --git a/app/templates/test/analyse_corpus.html.j2 b/app/templates/test/analyse_corpus.html.j2
index 5b95003d..d698cc5c 100644
--- a/app/templates/test/analyse_corpus.html.j2
+++ b/app/templates/test/analyse_corpus.html.j2
@@ -1,6 +1,8 @@
 {% extends "base.html.j2" %}
 {% import "materialize/wtf.html.j2" as wtf %}
-
+<style>
+  a   {color: #FFFFFF;}
+</style>
 
 
 {% block main_attribs %} class="service-scheme" data-service="corpus-analysis" id="corpus-analysis-app-container"{% endblock main_attribs %}
@@ -252,17 +254,49 @@
 
 <div class="modal" id="concordance-query-builder" style="width:70%;">
   <div class="modal-content">
-    <h4>Query-Builder</h4>
-    <ul class="tabs">
-      <li class="tab"><a class="active" href="#structural-attr">Add structural attributes</a></li>
-      <li class="tab"><a href="#positional-attr">Add new token</a></li>
-    </ul>
+    <nav style="background-color:#AA9CC9; margin-top:-25px; margin-left:-25px; width:105%;">
+      <div class="nav-wrapper" style="padding-left:15px">
+        <a href="#!" class="brand-logo"><i class="material-icons">build</i>Query Builder</a>
+        <i class="material-icons close right" style="margin-right: 25px; cursor:pointer;" id="close-query-builder">close</i>
+      </div>
+    </nav>
+    <p></p>
+
+    <div id="query-container" class="hide">
+      <div class="row">
+        <h6 class="col s7">Your Query:</h6>
+      </div>
+      <div id="button-preparer"></div>
+      <div class="row">
+        <div class="col s10" id="your-query" style="border-bottom-style: solid; border-bottom-width:1px;"></div>
+        <a class="btn-small disabled waves-effect waves-teal col s1" id="insert-query-button" style="background-color:#00426f; text-align:center">
+          <i class="material-icons">send</i>
+        </a>
+      </div>
+      <p>Preview:</p>
+      <p id="query-preview"></p>
+      <p></p>
+      <br>
+    </div>
+    
     
-    <div id="structural-attr">
+    <h6>Use the following options to build your query:</h6>
+    <p></p>
+    <a class="btn-large waves-effect waves-light tooltipped" id="positional-attr-button" data-position="bottom" data-tooltip="Search for any token, for example a word, a lemma or a part-of-speech tag">Add new token to your query</a>
+    <a class="btn-large waves-effect waves-light tooltipped" id="structural-attr-button" data-position="bottom" data-tooltip="Structure your query with structural attributes, for example sentences, entities or annotate the text">Add structural attributes to your query</a>
+    
+    <div id="structural-attr" class="hide">
+      <p></p>
+      <h6 style="margin-left:15px;">Which structural attribute do you want to add to your query?</h6>
       <p></p>
-      <a class="btn-small waves-effect waves-light" id="sentence">sentence</a>
-      <a class="btn-small waves-effect waves-light" id="entity">entity</a>
-      <a class="btn-small waves-effect waves-light" id="text-annotation">text annotation</a>
+      <div class="row">
+        <div class="col s12">
+          <a class="btn-small waves-effect waves-light" id="sentence" style="background-color:#FD9720">sentence</a>
+          <a class="btn-small waves-effect waves-light" id="entity" style="background-color:#A6E22D">entity</a>
+          <a class="btn-small waves-effect waves-light" id="text-annotation" style="background-color:#2FBBAB">text annotation</a>
+        </div>
+      </div>
+      
 
       <div id="entity-builder" class="hide">
         <p></p>
@@ -304,7 +338,6 @@
               </select>
           </div>
         </div>
-        <div data-inga=""></div>
       </div>
     
 
@@ -343,18 +376,20 @@
       </div>
     </div>
 
-    <div id="positional-attr">
+    <div id="positional-attr" class="hide">
+      <p></p>
+      <h6 style="margin-left:15px;">Which kind of token are you looking for?</h6>
       <p></p>
       <div class="row">
         <div id="token-attr" class="col s12">
-            <a class="btn-small waves-effect waves-light" id="empty-token" style="background-color:#43c6fc">empty token</a>
-            <a class="btn-small waves-effect waves-light" id="word" style="background-color:#ef60b4">word</a>
-            <a class="btn-small waves-effect waves-light" id="lemma" style="background-color:#ef60b4">lemma</a>
-            <a class="btn-small waves-effect waves-light" id="pos" style="background-color:#ef60b4">pos</a>
-            <a class="btn-small waves-effect waves-light" id="simple-pos-button" style="background-color:#ef60b4">simple_pos</a>
-            <a class="btn-small waves-effect waves-light disabled" id="or" style="background-color:#fc0">or</a>
-            <a class="btn-small waves-effect waves-light disabled" id="and" style="background-color:#fc0">and</a>
-            <a class="dropdown-trigger btn-small disabled waves-effect waves-light" href="#" data-target="incidence-modifiers" style="background-color:#2fbbab">incidence modifiers</a>
+            <a class="btn-small tooltipped waves-effect waves-light" data-position="bottom" data-tooltip="You can look for an empty token. It is NOT recommended to search for an empty token <br> without an incidence modifier, because each token would matches this pattern." id="empty-token" style="background-color:#43c6fc">empty token</a>
+            <a class="btn-small tooltipped waves-effect waves-light" id="word" style="background-color:#ef60b4" data-position="bottom" data-tooltip="You can search for a word and modify the corresponding search">word</a>
+            <a class="btn-small tooltipped waves-effect waves-light" id="lemma" style="background-color:#ef60b4" data-position="bottom" data-tooltip="You can search for a lemma and modify the corresponding search">lemma</a>
+            <a class="btn-small tooltipped waves-effect waves-light" id="pos" style="background-color:#ef60b4" data-position="bottom" data-tooltip="You can search for a part of speech tag">pos</a>
+            <a class="btn-small tooltipped waves-effect waves-light" id="simple-pos-button" style="background-color:#ef60b4" data-position="bottom" data-tooltip="You can search for a simple part of speech tag">simple_pos</a>
+            <a class="btn-small tooltipped waves-effect waves-light disabled" id="or" style="background-color:#fc0" data-position="bottom" data-tooltip="You can add another condition to your token. <br>At least one must be fulfilled">or</a>
+            <a class="btn-small tooltipped waves-effect waves-light disabled" id="and" style="background-color:#fc0" data-position="bottom" data-tooltip="You can add another condition to your token. <br>Both must be fulfilled">and</a>
+            <a class="dropdown-trigger tooltipped btn-small disabled waves-effect waves-light" href="#" data-target="incidence-modifiers" data-position="bottom" data-tooltip="Incidence Modifiers are special characters or patterns, <br>which determine how often a character represented previously should occur." style="background-color:#2fbbab">incidence modifiers</a>
         </div>
       </div>
       <div id="token-builder-content">
@@ -525,27 +560,11 @@
       <div id="token-query"></div>
       <p></p>
       <a class="btn waves-effect disabled waves-light" style="background-color:#00426f" id="build-token-button">
-          Add token 
+          Add token to your query
           <i class="material-icons right">check</i>
       </a>
     </div>
-
-    <br><hr>
-    <div>
-      <h5>Your Query</h5>
-      <p><i>Examples: </i></p>
-      <p>
-        &lt;ent_type=&quot;PERSON&quot;&gt; []* &lt;/ent_type&gt; []* 
-        [simple_pos=&quot;VERB&quot;] :: match.text_publishing_year=&quot;1991&quot;;
-      </p>
-      <div id="button-preparer"></div>
-      <div id="your-query"></div>
-      <p></p>
-      <a class="btn disabled waves-effect waves-light" id="insert-query-button" style="background-color:#00426f">
-          Insert 
-          <i class="material-icons right">send</i>
-      </a>
-    </div>
+    
   </div>
 </div>
 
-- 
GitLab