mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 12:22:47 +00:00 
			
		
		
		
	Split QB back to mult. classes, as far as possible
This commit is contained in:
		@@ -0,0 +1,495 @@
 | 
				
			|||||||
 | 
					class GeneralQueryBuilderFunctions {
 | 
				
			||||||
 | 
					  constructor(elements) {
 | 
				
			||||||
 | 
					    this.elements = elements;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toggleClass(elements, className, action){
 | 
				
			||||||
 | 
					    elements.forEach(element => {
 | 
				
			||||||
 | 
					      document.querySelector(`[data-toggle-area="${element}"]`).classList[action](className);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  resetQueryInputField() {
 | 
				
			||||||
 | 
					    this.elements.queryInputField.innerHTML = '';
 | 
				
			||||||
 | 
					    this.addPlaceholder();
 | 
				
			||||||
 | 
					    this.updateChipList();
 | 
				
			||||||
 | 
					    this.queryPreviewBuilder();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updateChipList() {
 | 
				
			||||||
 | 
					    this.elements.queryChipElements = this.elements.queryInputField.querySelectorAll('.query-component');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  removePlaceholder() {
 | 
				
			||||||
 | 
					    let placeholder = this.elements.queryInputField.querySelector('#corpus-analysis-concordance-query-builder-input-field-placeholder');  
 | 
				
			||||||
 | 
					    if (placeholder && this.elements.queryInputField !== undefined) {
 | 
				
			||||||
 | 
					      this.elements.queryInputField.innerHTML = '';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addPlaceholder() {
 | 
				
			||||||
 | 
					    let placeholder = Utils.HTMLToElement('<span id="corpus-analysis-concordance-query-builder-input-field-placeholder">Click on a button to add a query component</span>');
 | 
				
			||||||
 | 
					    this.elements.queryInputField.appendChild(placeholder);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  resetMaterializeSelection(selectionElements, value = "default") {
 | 
				
			||||||
 | 
					    selectionElements.forEach(selectionElement => {
 | 
				
			||||||
 | 
					      if (selectionElement.querySelector(`option[value=${value}]`) !== null) {
 | 
				
			||||||
 | 
					        selectionElement.querySelector(`option[value=${value}]`).selected = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let instance = M.FormSelect.getInstance(selectionElement);
 | 
				
			||||||
 | 
					      instance.destroy();
 | 
				
			||||||
 | 
					      M.FormSelect.init(selectionElement);
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  submitQueryChipElement(dataType = undefined, prettyQueryText = undefined, queryText = undefined, index = null, isClosingTag = false, isEditable = false) {
 | 
				
			||||||
 | 
					    if (this.elements.editingModusOn) {
 | 
				
			||||||
 | 
					      let editedQueryChipElement = this.elements.queryChipElements[this.elements.editedQueryChipElementIndex];
 | 
				
			||||||
 | 
					      editedQueryChipElement.dataset.type = dataType;
 | 
				
			||||||
 | 
					      editedQueryChipElement.dataset.query = queryText;
 | 
				
			||||||
 | 
					      editedQueryChipElement.firstChild.textContent = prettyQueryText;
 | 
				
			||||||
 | 
					      this.updateChipList();
 | 
				
			||||||
 | 
					      this.queryPreviewBuilder();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.queryChipFactory(dataType, prettyQueryText, queryText, index, isClosingTag, isEditable);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  queryChipFactory(dataType, prettyQueryText, queryText, index = null, isClosingTag = false, isEditable = false) {
 | 
				
			||||||
 | 
					    // Creates a new query chip element, adds Eventlisteners for selection, deletion and drag and drop and appends it to the query input field.
 | 
				
			||||||
 | 
					    queryText = Utils.escape(queryText);
 | 
				
			||||||
 | 
					    prettyQueryText = Utils.escape(prettyQueryText);
 | 
				
			||||||
 | 
					    let queryChipElement = Utils.HTMLToElement(
 | 
				
			||||||
 | 
					      `
 | 
				
			||||||
 | 
					        <span class="chip query-component" data-type="${dataType}" data-query="${queryText}" draggable="true" data-closing-tag="${isClosingTag}">
 | 
				
			||||||
 | 
					          ${prettyQueryText}${isEditable ? '<i class="material-icons chip-action-button" data-chip-action="edit" style="padding-left:5px; font-size:18px; cursor:pointer;">edit</i>': ''}
 | 
				
			||||||
 | 
					          ${isClosingTag ? '<i class="material-icons chip-action-button" data-chip-action="lock" style="padding-top:5px; font-size:20px; cursor:pointer;">lock_open</i>' : '<i class="material-icons close chip-action-button" data-chip-action="delete">close</i>'}
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      `
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    this.actionListeners(queryChipElement);
 | 
				
			||||||
 | 
					    queryChipElement.addEventListener('dragstart', this.handleDragStart.bind(this, queryChipElement));
 | 
				
			||||||
 | 
					    queryChipElement.addEventListener('dragend', this.handleDragEnd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Ensures that metadata is always at the end of the query and if an index is given, inserts the query chip at the given index and if there is a closing tag, inserts the query chip before the closing tag.
 | 
				
			||||||
 | 
					    this.removePlaceholder();
 | 
				
			||||||
 | 
					    let lastChild = this.elements.queryInputField.lastChild;
 | 
				
			||||||
 | 
					    let isLastChildTextAnnotation = lastChild && lastChild.dataset.type === 'text-annotation';
 | 
				
			||||||
 | 
					    if (!index) {
 | 
				
			||||||
 | 
					      let closingTagElement = this.elements.queryInputField.querySelector('[data-closing-tag="true"]');
 | 
				
			||||||
 | 
					      if (closingTagElement) {
 | 
				
			||||||
 | 
					        index = Array.from(this.elements.queryInputField.children).indexOf(closingTagElement);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (dataType !== 'text-annotation' && index) {
 | 
				
			||||||
 | 
					      this.elements.queryInputField.insertBefore(queryChipElement, this.elements.queryChipElements[index]);
 | 
				
			||||||
 | 
					    } else if (dataType !== 'text-annotation' && isLastChildTextAnnotation) {
 | 
				
			||||||
 | 
					      this.elements.queryInputField.insertBefore(queryChipElement, lastChild);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.elements.queryInputField.appendChild(queryChipElement);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.updateChipList();
 | 
				
			||||||
 | 
					    this.queryPreviewBuilder();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  actionListeners(queryChipElement) {
 | 
				
			||||||
 | 
					    let notQuantifiableDataTypes = ['start-sentence', 'end-sentence', 'start-entity', 'start-empty-entity', 'end-entity', 'token-incidence-modifier'];
 | 
				
			||||||
 | 
					    queryChipElement.addEventListener('click', (event) => {
 | 
				
			||||||
 | 
					      if (event.target.classList.contains('chip')) {
 | 
				
			||||||
 | 
					        if (!notQuantifiableDataTypes.includes(queryChipElement.dataset.type)) {
 | 
				
			||||||
 | 
					          this.selectChipElement(queryChipElement);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    let chipActionButtons = queryChipElement.querySelectorAll('.chip-action-button');
 | 
				
			||||||
 | 
					    // chipActionButtons.forEach(button => {
 | 
				
			||||||
 | 
					    for (let button of chipActionButtons) {
 | 
				
			||||||
 | 
					      button.addEventListener('click', (event) => {
 | 
				
			||||||
 | 
					      if (event.target.dataset.chipAction === 'delete') {
 | 
				
			||||||
 | 
					        this.deleteChipElement(queryChipElement);
 | 
				
			||||||
 | 
					      } else if (event.target.dataset.chipAction === 'edit') {
 | 
				
			||||||
 | 
					        this.editChipElement(queryChipElement);
 | 
				
			||||||
 | 
					      } else if (event.target.dataset.chipAction === 'lock') {
 | 
				
			||||||
 | 
					        this.lockClosingChipElement(queryChipElement);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    // });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editChipElement(queryChipElement) {
 | 
				
			||||||
 | 
					    this.elements.editingModusOn = true;
 | 
				
			||||||
 | 
					    this.elements.editedQueryChipElementIndex = Array.from(this.elements.queryInputField.children).indexOf(queryChipElement);
 | 
				
			||||||
 | 
					    switch (queryChipElement.dataset.type) {
 | 
				
			||||||
 | 
					      case 'start-entity':
 | 
				
			||||||
 | 
					        this.editStartEntityChipElement(queryChipElement);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'text-annotation':
 | 
				
			||||||
 | 
					        this.editTextAnnotationChipElement(queryChipElement);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'token':
 | 
				
			||||||
 | 
					        let queryElementsContent = this.prepareQueryElementsContent(queryChipElement);
 | 
				
			||||||
 | 
					        this.editTokenChipElement(queryElementsContent);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editStartEntityChipElement(queryChipElement) {
 | 
				
			||||||
 | 
					    this.elements.structuralAttrModal.open();
 | 
				
			||||||
 | 
					    this.toggleClass(['entity-builder'], 'hide', 'remove');
 | 
				
			||||||
 | 
					    this.toggleEditingAreaStructuralAttrModal('add');
 | 
				
			||||||
 | 
					    let entType = queryChipElement.dataset.query.replace(/<ent_type="|">/g, '');
 | 
				
			||||||
 | 
					    let isEnglishEntType = this.elements.englishEntTypeSelection.querySelector(`option[value=${entType}]`) !== null;
 | 
				
			||||||
 | 
					    let selection = isEnglishEntType ? this.elements.englishEntTypeSelection : this.elements.germanEntTypeSelection;
 | 
				
			||||||
 | 
					    this.resetMaterializeSelection([selection], entType);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editTextAnnotationChipElement(queryChipElement) {
 | 
				
			||||||
 | 
					    this.elements.structuralAttrModal.open();
 | 
				
			||||||
 | 
					    this.toggleClass(['text-annotation-builder'], 'hide', 'remove');
 | 
				
			||||||
 | 
					    this.structuralAttributeBuilderFunctions.toggleEditingAreaStructuralAttrModal('add');
 | 
				
			||||||
 | 
					    let [textAnnotationSelection, textAnnotationContent] = queryChipElement.dataset.query
 | 
				
			||||||
 | 
					      .replace(/:: ?match\.text_|"|"/g, '')
 | 
				
			||||||
 | 
					      .split('=');
 | 
				
			||||||
 | 
					    this.resetMaterializeSelection([this.elements.textAnnotationSelection], textAnnotationSelection);
 | 
				
			||||||
 | 
					    this.elements.textAnnotationInput.value = textAnnotationContent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  prepareQueryElementsContent(queryChipElement) {
 | 
				
			||||||
 | 
					    //this regex searches for word or lemma or pos or simple_pos="any string within single or double quotes" followed by one or no ignore case markers, followed by one or no condition characters.
 | 
				
			||||||
 | 
					    let regex = new RegExp('(word|lemma|pos|simple_pos)=(("[^"]+")|(\\\\u0027[^\\\\u0027]+\\\\u0027)) ?(%c)? ?(\\&|\\|)?', 'gm');
 | 
				
			||||||
 | 
					    let m;
 | 
				
			||||||
 | 
					    let queryElementsContent = [];
 | 
				
			||||||
 | 
					    while ((m = regex.exec(queryChipElement.dataset.query)) !== null) {
 | 
				
			||||||
 | 
					      // this is necessary to avoid infinite loops with zero-width matches
 | 
				
			||||||
 | 
					      if (m.index === regex.lastIndex) {
 | 
				
			||||||
 | 
					          regex.lastIndex++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let tokenAttr = m[1];
 | 
				
			||||||
 | 
					      // Passes english-pos by default so that the template is added. In editTokenChipElement it is then checked whether it is english-pos or german-pos.
 | 
				
			||||||
 | 
					      if (tokenAttr === 'pos') {
 | 
				
			||||||
 | 
					        tokenAttr = 'english-pos';
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let tokenValue = m[2].replace(/"|'/g, '');
 | 
				
			||||||
 | 
					      let ignoreCase = false;
 | 
				
			||||||
 | 
					      let condition = undefined;
 | 
				
			||||||
 | 
					      m.forEach((match) => {
 | 
				
			||||||
 | 
					        if (match === "%c") {
 | 
				
			||||||
 | 
					          ignoreCase = true;
 | 
				
			||||||
 | 
					        } else if (match === "&") {
 | 
				
			||||||
 | 
					          condition = "and";
 | 
				
			||||||
 | 
					        } else if (match === "|") {
 | 
				
			||||||
 | 
					          condition = "or";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      queryElementsContent.push({tokenAttr: tokenAttr, tokenValue: tokenValue, ignoreCase: ignoreCase, condition: condition});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return queryElementsContent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editTokenChipElement(queryElementsContent) {
 | 
				
			||||||
 | 
					    this.elements.positionalAttrModal.open();
 | 
				
			||||||
 | 
					    queryElementsContent.forEach((queryElement) => {
 | 
				
			||||||
 | 
					      this.resetMaterializeSelection([this.elements.positionalAttrSelection], queryElement.tokenAttr);
 | 
				
			||||||
 | 
					      this.preparePositionalAttrModal();
 | 
				
			||||||
 | 
					      switch (queryElement.tokenAttr) {
 | 
				
			||||||
 | 
					        case 'word':
 | 
				
			||||||
 | 
					        case 'lemma':
 | 
				
			||||||
 | 
					          this.elements.tokenBuilderContent.querySelector('input').value = queryElement.tokenValue;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case 'english-pos':
 | 
				
			||||||
 | 
					          // English-pos is selected by default. Then it is checked whether the passed token value occurs in the english-pos selection. If not, the selection is reseted and changed to german-pos.
 | 
				
			||||||
 | 
					          let selection = this.elements.tokenBuilderContent.querySelector('select');
 | 
				
			||||||
 | 
					          queryElement.tokenAttr = selection.querySelector(`option[value=${queryElement.tokenValue}]`) ? 'english-pos' : 'german-pos';
 | 
				
			||||||
 | 
					          this.resetMaterializeSelection([this.elements.positionalAttrSelection], queryElement.tokenAttr);
 | 
				
			||||||
 | 
					          this.preparePositionalAttrModal();
 | 
				
			||||||
 | 
					          this.resetMaterializeSelection([this.elements.tokenBuilderContent.querySelector('select')], queryElement.tokenValue);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case 'simple_pos':
 | 
				
			||||||
 | 
					          this.resetMaterializeSelection([this.elements.tokenBuilderContent.querySelector('select')], queryElement.tokenValue);
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (queryElement.ignoreCase) {
 | 
				
			||||||
 | 
					        this.elements.ignoreCaseCheckbox.checked = true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (queryElement.condition !== undefined) {
 | 
				
			||||||
 | 
					        this.conditionHandler(queryElement.condition, true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  lockClosingChipElement(queryChipElement) {
 | 
				
			||||||
 | 
					    queryChipElement.dataset.closingTag = 'false';
 | 
				
			||||||
 | 
					    let lockIcon = queryChipElement.querySelector('[data-chip-action="lock"]');
 | 
				
			||||||
 | 
					    lockIcon.textContent = 'lock';
 | 
				
			||||||
 | 
					    //TODO: Write unlock-Function?
 | 
				
			||||||
 | 
					    lockIcon.dataset.chipAction = 'unlock';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  deleteChipElement(attr) {
 | 
				
			||||||
 | 
					    let elementIndex = Array.from(this.elements.queryInputField.children).indexOf(attr);
 | 
				
			||||||
 | 
					    switch (attr.dataset.type) {
 | 
				
			||||||
 | 
					      case 'start-sentence':
 | 
				
			||||||
 | 
					        this.deletingClosingTagHandler(elementIndex, 'end-sentence');
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'start-entity':
 | 
				
			||||||
 | 
					        this.deletingClosingTagHandler(elementIndex, 'end-entity');
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'token':
 | 
				
			||||||
 | 
					        let nextElement = Array.from(this.elements.queryInputField.children)[elementIndex+1];
 | 
				
			||||||
 | 
					        if (nextElement !== undefined && nextElement.dataset.type === 'token-incidence-modifier') {
 | 
				
			||||||
 | 
					          this.deleteChipElement(nextElement);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.elements.queryInputField.removeChild(attr);
 | 
				
			||||||
 | 
					    if (this.elements.queryInputField.children.length === 0) {
 | 
				
			||||||
 | 
					      this.addPlaceholder();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.updateChipList();
 | 
				
			||||||
 | 
					    this.queryPreviewBuilder();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deletingClosingTagHandler(elementIndex, closingTagType) {
 | 
				
			||||||
 | 
					    let closingTags = this.elements.queryInputField.querySelectorAll(`[data-type="${closingTagType}"]`);
 | 
				
			||||||
 | 
					    for (let i = 0; i < closingTags.length; i++) {
 | 
				
			||||||
 | 
					      let closingTag = closingTags[i];
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					      if (Array.from(this.elements.queryInputField.children).indexOf(closingTag) > elementIndex) {
 | 
				
			||||||
 | 
					        this.deleteChipElement(closingTag);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleDragStart(queryChipElement, event) {
 | 
				
			||||||
 | 
					    // is called when a query chip is dragged. It creates a dropzone (in form of a chip) for the dragged chip and adds it to the query input field.
 | 
				
			||||||
 | 
					    let queryChips = this.elements.queryInputField.querySelectorAll('.query-component');
 | 
				
			||||||
 | 
					    if (queryChipElement.dataset.type === 'token-incidence-modifier') {
 | 
				
			||||||
 | 
					      queryChips = this.elements.queryInputField.querySelectorAll('.query-component[data-type="token"]');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      let targetChipElement = Utils.HTMLToElement('<span class="chip drop-target">Drop here</span>');
 | 
				
			||||||
 | 
					      for (let element of queryChips) {
 | 
				
			||||||
 | 
					        if (element === this.elements.queryInputField.querySelectorAll('.query-component')[0]) {
 | 
				
			||||||
 | 
					          let secondTargetChipClone = targetChipElement.cloneNode(true);
 | 
				
			||||||
 | 
					          element.insertAdjacentElement('beforebegin', secondTargetChipClone);
 | 
				
			||||||
 | 
					          this.addDragDropListeners(secondTargetChipClone, queryChipElement);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (element === queryChipElement || element.nextSibling === queryChipElement) {continue;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let targetChipClone = targetChipElement.cloneNode(true);
 | 
				
			||||||
 | 
					        element.insertAdjacentElement('afterend', targetChipClone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.addDragDropListeners(targetChipClone, queryChipElement);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleDragEnd(event) {
 | 
				
			||||||
 | 
					    document.querySelectorAll('.drop-target').forEach(target => target.remove());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addDragDropListeners(targetChipClone, queryChipElement) {
 | 
				
			||||||
 | 
					    targetChipClone.addEventListener('dragover', (event) => {
 | 
				
			||||||
 | 
					      event.preventDefault();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    targetChipClone.addEventListener('dragenter', (event) => {
 | 
				
			||||||
 | 
					      event.preventDefault();
 | 
				
			||||||
 | 
					      event.target.style.borderStyle = 'solid dotted';
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    targetChipClone.addEventListener('dragleave', (event) => {
 | 
				
			||||||
 | 
					      event.preventDefault();
 | 
				
			||||||
 | 
					      event.target.style.borderStyle = 'hidden';
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    targetChipClone.addEventListener('drop', (event) => {
 | 
				
			||||||
 | 
					      let dropzone = event.target;
 | 
				
			||||||
 | 
					      dropzone.parentElement.replaceChild(queryChipElement, dropzone);
 | 
				
			||||||
 | 
					      this.updateChipList();
 | 
				
			||||||
 | 
					      this.queryPreviewBuilder();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  queryPreviewBuilder() {
 | 
				
			||||||
 | 
					    // Builds the query preview in the form of pure CQL and displays it in the query preview field.
 | 
				
			||||||
 | 
					    let queryPreview = document.querySelector('#corpus-analysis-concordance-query-preview');
 | 
				
			||||||
 | 
					    let queryInputFieldContent = [];
 | 
				
			||||||
 | 
					    this.elements.queryChipElements.forEach(element => {
 | 
				
			||||||
 | 
					      let queryElement = element.dataset.query;
 | 
				
			||||||
 | 
					      if (queryElement !== undefined) {
 | 
				
			||||||
 | 
					        queryElement = Utils.escape(queryElement);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      queryInputFieldContent.push(queryElement);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    let queryString = queryInputFieldContent.join(' ');
 | 
				
			||||||
 | 
					    let replacements = {
 | 
				
			||||||
 | 
					      ' +': '+',
 | 
				
			||||||
 | 
					      ' *': '*',
 | 
				
			||||||
 | 
					      ' ?': '?',
 | 
				
			||||||
 | 
					      ' {': '{'
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (let key in replacements) {
 | 
				
			||||||
 | 
					      queryString = queryString.replace(key, replacements[key]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    queryString += ';';
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    queryPreview.innerHTML = queryString;
 | 
				
			||||||
 | 
					    queryPreview.parentNode.classList.toggle('hide', queryString === ';');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  selectChipElement(attr) {
 | 
				
			||||||
 | 
					    document.querySelectorAll('.chip.teal').forEach(element => {
 | 
				
			||||||
 | 
					        if (element !== attr) {
 | 
				
			||||||
 | 
					          element.classList.remove('teal', 'lighten-2');
 | 
				
			||||||
 | 
					          this.toggleClass(['token-incidence-modifiers'], 'disabled', 'add');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.toggleClass(['token-incidence-modifiers'], 'disabled', 'toggle');
 | 
				
			||||||
 | 
					    attr.classList.toggle('teal');
 | 
				
			||||||
 | 
					    attr.classList.toggle('lighten-5');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tokenIncidenceModifierHandler(incidenceModifier, incidenceModifierPretty) {
 | 
				
			||||||
 | 
					    // Adds a token incidence modifier to the query input field.
 | 
				
			||||||
 | 
					    let selectedChip = this.elements.queryInputField.querySelector('.chip.teal');
 | 
				
			||||||
 | 
					    let selectedChipIndex = Array.from(this.elements.queryInputField.children).indexOf(selectedChip);
 | 
				
			||||||
 | 
					    this.submitQueryChipElement('token-incidence-modifier', incidenceModifierPretty, incidenceModifier, selectedChipIndex+1);
 | 
				
			||||||
 | 
					    this.selectChipElement(selectedChip);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tokenNMSubmitHandler(modalId) {
 | 
				
			||||||
 | 
					    // Adds a token incidence modifier (exactly n or between n and m) to the query input field.
 | 
				
			||||||
 | 
					    let modal = document.querySelector(`#${modalId}`);
 | 
				
			||||||
 | 
					    let input_n = modal.querySelector('.n-m-input[data-value-type="n"]').value;
 | 
				
			||||||
 | 
					    let input_m = modal.querySelector('.n-m-input[data-value-type="m"]') || undefined;
 | 
				
			||||||
 | 
					    input_m = input_m !== undefined ? input_m.value : '';
 | 
				
			||||||
 | 
					    let input = `{${input_n}${input_m !== '' ? ',' : ''}${input_m}}`;
 | 
				
			||||||
 | 
					    let pretty_input = `between ${input_n} and ${input_m} (${input})`;
 | 
				
			||||||
 | 
					    if (input_m === '') {
 | 
				
			||||||
 | 
					     pretty_input = `exactly ${input_n} (${input})`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let instance = M.Modal.getInstance(modal);
 | 
				
			||||||
 | 
					    instance.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.tokenIncidenceModifierHandler(input, pretty_input);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //#region Functions from other classes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //TODO: Move these functions back to their og classes and make it work.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toggleEditingAreaStructuralAttrModal(action) {
 | 
				
			||||||
 | 
					    // If the user edits a query chip element, the corresponding editing area is displayed and the other areas are hidden or disabled.
 | 
				
			||||||
 | 
					    this.toggleClass(['sentence-button', 'entity-button', 'text-annotation-button', 'any-type-entity-button'], 'disabled', action);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  preparePositionalAttrModal() {
 | 
				
			||||||
 | 
					    let selection = this.elements.positionalAttrSelection.value;
 | 
				
			||||||
 | 
					    if (selection !== 'empty-token') {
 | 
				
			||||||
 | 
					      let selectionTemplate = document.querySelector(`.token-builder-section[data-token-builder-section="${selection}"]`);
 | 
				
			||||||
 | 
					      let selectionTemplateClone = selectionTemplate.content.cloneNode(true);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					      this.elements.tokenBuilderContent.innerHTML = '';
 | 
				
			||||||
 | 
					      this.elements.tokenBuilderContent.appendChild(selectionTemplateClone);
 | 
				
			||||||
 | 
					      if (this.elements.tokenBuilderContent.querySelector('select') !== null) {
 | 
				
			||||||
 | 
					        let selectElement = this.elements.tokenBuilderContent.querySelector('select');
 | 
				
			||||||
 | 
					        M.FormSelect.init(selectElement);
 | 
				
			||||||
 | 
					        selectElement.addEventListener('change', () => {this.optionToggleHandler();});
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.elements.tokenBuilderContent.querySelector('input').addEventListener('input', () => {this.optionToggleHandler();});
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.optionToggleHandler();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (selection === 'word' || selection === 'lemma') {
 | 
				
			||||||
 | 
					      this.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
				
			||||||
 | 
					    } else if (selection === 'empty-token'){
 | 
				
			||||||
 | 
					      this.addTokenToQuery();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.toggleClass(['input-field-options'], 'hide', 'add');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tokenInputCheck(elem) {
 | 
				
			||||||
 | 
					    return elem.querySelector('select') !== null ? elem.querySelector('select') : elem.querySelector('input');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  optionToggleHandler() {
 | 
				
			||||||
 | 
					    let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
				
			||||||
 | 
					    if (input.value === '' && this.elements.editingModusOn === false) {
 | 
				
			||||||
 | 
					      this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
				
			||||||
 | 
					    } else if (this.elements.positionalAttrSelection.querySelectorAll('option').length === 1) {
 | 
				
			||||||
 | 
					      this.toggleClass(['and'], 'disabled', 'add');
 | 
				
			||||||
 | 
					      this.toggleClass(['or'], 'disabled', 'remove');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'remove');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addTokenToQuery() {
 | 
				
			||||||
 | 
					    let tokenQueryPrettyText = '';
 | 
				
			||||||
 | 
					    let tokenQueryCQLText = '';
 | 
				
			||||||
 | 
					    let input;
 | 
				
			||||||
 | 
					    let kindOfToken = this.kindOfTokenCheck(this.elements.positionalAttrSelection.value);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Takes all rows of the token query (if there is a query concatenation).
 | 
				
			||||||
 | 
					    // Adds their contents to tokenQueryPrettyText and tokenQueryCQLText, which will later be expanded with the current input field.
 | 
				
			||||||
 | 
					    let tokenQueryRows = this.elements.tokenQuery.querySelectorAll('.row');
 | 
				
			||||||
 | 
					    tokenQueryRows.forEach(row => {
 | 
				
			||||||
 | 
					      let ignoreCaseCheckbox = row.querySelector('input[type="checkbox"]');
 | 
				
			||||||
 | 
					      let c = ignoreCaseCheckbox !== null && ignoreCaseCheckbox.checked ? ' %c' : '';
 | 
				
			||||||
 | 
					      let tokenQueryRowInput = this.tokenInputCheck(row.querySelector('.token-query-template-content'));
 | 
				
			||||||
 | 
					      let tokenQueryKindOfToken = this.kindOfTokenCheck(tokenQueryRowInput.closest('.input-field').dataset.kindOfToken);
 | 
				
			||||||
 | 
					      let tokenConditionPrettyText = row.querySelector('[data-condition-pretty-text]').dataset.conditionPrettyText;
 | 
				
			||||||
 | 
					      let tokenConditionCQLText = row.querySelector('[data-condition-cql-text]').dataset.conditionCqlText;
 | 
				
			||||||
 | 
					      tokenQueryPrettyText += `${tokenQueryKindOfToken}=${tokenQueryRowInput.value}${c} ${tokenConditionPrettyText} `;
 | 
				
			||||||
 | 
					      tokenQueryCQLText += `${tokenQueryKindOfToken}="${tokenQueryRowInput.value}"${c} ${tokenConditionCQLText}`;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    if (kindOfToken === 'empty-token') {
 | 
				
			||||||
 | 
					      tokenQueryPrettyText += 'empty token';
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      let c = this.elements.ignoreCaseCheckbox.checked ? ' %c' : '';
 | 
				
			||||||
 | 
					      input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
				
			||||||
 | 
					      tokenQueryPrettyText += `${kindOfToken}=${input.value}${c}`;
 | 
				
			||||||
 | 
					      tokenQueryCQLText += `${kindOfToken}="${input.value}"${c}`;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // isTokenQueryInvalid looks if a valid value is passed. If the input fields/dropdowns are empty (isTokenQueryInvalid === true), no token is added.
 | 
				
			||||||
 | 
					    if (this.elements.positionalAttrSelection.value !== 'empty-token' && input.value === '') {
 | 
				
			||||||
 | 
					      this.disableTokenSubmit();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      tokenQueryCQLText = `[${tokenQueryCQLText}]`;
 | 
				
			||||||
 | 
					      this.submitQueryChipElement('token', tokenQueryPrettyText, tokenQueryCQLText, null, false, kindOfToken === 'empty-token' ? false : true);
 | 
				
			||||||
 | 
					      this.elements.positionalAttrModal.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  kindOfTokenCheck(kindOfToken) {
 | 
				
			||||||
 | 
					    return kindOfToken === 'english-pos' || kindOfToken === 'german-pos' ? 'pos' : kindOfToken;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  disableTokenSubmit() {
 | 
				
			||||||
 | 
					    this.elements.tokenSubmitButton.classList.add('red');
 | 
				
			||||||
 | 
					    this.elements.noValueMessage.classList.remove('hide');
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      this.elements.tokenSubmitButton.classList.remove('red');
 | 
				
			||||||
 | 
					    }, 500);
 | 
				
			||||||
 | 
					    setTimeout(() => {
 | 
				
			||||||
 | 
					      this.elements.noValueMessage.classList.add('hide');
 | 
				
			||||||
 | 
					    }, 3000);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //#endregion Functions from other classes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2,53 +2,16 @@ class ConcordanceQueryBuilder {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  constructor() {
 | 
					  constructor() {
 | 
				
			||||||
    this.elements = new ElementReferencesQueryBuilder();
 | 
					    this.elements = new ElementReferencesQueryBuilder();
 | 
				
			||||||
 | 
					    this.generalFunctions = new GeneralQueryBuilderFunctions(this.elements);
 | 
				
			||||||
 | 
					    this.tokenAttributeBuilderFunctions = new TokenAttributeBuilderFunctions(this.elements);
 | 
				
			||||||
 | 
					    this.structuralAttributeBuilderFunctions = new StructuralAttributeBuilderFunctions(this.elements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //#region QB Constructor
 | 
					    this.incidenceModifierEventListeners();
 | 
				
			||||||
    // Eventlisteners for the incidence modifiers. There are two different types of incidence modifiers: token and character incidence modifiers.
 | 
					    this.nAndMInputSubmitEventListeners();
 | 
				
			||||||
    document.querySelectorAll('.incidence-modifier-selection').forEach(button => {
 | 
					 | 
				
			||||||
      let dropdownId = button.parentNode.parentNode.id;
 | 
					 | 
				
			||||||
      if (dropdownId === 'corpus-analysis-concordance-token-incidence-modifiers-dropdown') {
 | 
					 | 
				
			||||||
        button.addEventListener('click', () => this.tokenIncidenceModifierHandler(button.dataset.token, button.innerHTML));
 | 
					 | 
				
			||||||
      } else if (dropdownId === 'corpus-analysis-concordance-character-incidence-modifiers-dropdown') {
 | 
					 | 
				
			||||||
        button.addEventListener('click', () => this.characterIncidenceModifierHandler(button));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Eventlisteners for the submit of n- and m-values of the incidence modifier modal for "exactly n" or "between n and m".
 | 
					    let queryBuilderDisplay = document.querySelector("#corpus-analysis-concordance-query-builder-display");
 | 
				
			||||||
    document.querySelectorAll('.n-m-submit-button').forEach(button => {
 | 
					    let expertModeDisplay = document.querySelector("#corpus-analysis-concordance-expert-mode-display");
 | 
				
			||||||
      let modalId = button.dataset.modalId;
 | 
					    let expertModeSwitch = document.querySelector("#corpus-analysis-concordance-expert-mode-switch");
 | 
				
			||||||
      if (modalId === 'corpus-analysis-concordance-exactly-n-token-modal' || modalId === 'corpus-analysis-concordance-between-nm-token-modal') {
 | 
					 | 
				
			||||||
        button.addEventListener('click', () => this.tokenNMSubmitHandler(modalId));
 | 
					 | 
				
			||||||
      } else if (modalId === 'corpus-analysis-concordance-exactly-n-character-modal' || modalId === 'corpus-analysis-concordance-between-nm-character-modal') {
 | 
					 | 
				
			||||||
        button.addEventListener('click', () => this.characterNMSubmitHandler(modalId));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    document.querySelector('#corpus-analysis-concordance-text-annotation-submit').addEventListener('click', () => this.textAnnotationSubmitHandler());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.elements.positionalAttrModal = M.Modal.init(
 | 
					 | 
				
			||||||
      document.querySelector('#corpus-analysis-concordance-positional-attr-modal'), 
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        onOpenStart: () => {
 | 
					 | 
				
			||||||
          this.preparePositionalAttrModal();
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onCloseStart: () => {
 | 
					 | 
				
			||||||
          this.resetPositionalAttrModal();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    this.elements.structuralAttrModal = M.Modal.init(
 | 
					 | 
				
			||||||
      document.querySelector('#corpus-analysis-concordance-structural-attr-modal'), 
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        onCloseStart: () => {
 | 
					 | 
				
			||||||
          this.resetStructuralAttrModal();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let queryBuilderDisplay = document.getElementById("corpus-analysis-concordance-query-builder-display");
 | 
					 | 
				
			||||||
    let expertModeDisplay = document.getElementById("corpus-analysis-concordance-expert-mode-display");
 | 
					 | 
				
			||||||
    let expertModeSwitch = document.getElementById("corpus-analysis-concordance-expert-mode-switch");
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    expertModeSwitch.addEventListener("change", () => {
 | 
					    expertModeSwitch.addEventListener("change", () => {
 | 
				
			||||||
      const isChecked = expertModeSwitch.checked;
 | 
					      const isChecked = expertModeSwitch.checked;
 | 
				
			||||||
@@ -62,50 +25,33 @@ class ConcordanceQueryBuilder {
 | 
				
			|||||||
        this.switchToQueryBuilderParser();
 | 
					        this.switchToQueryBuilderParser();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    //#endregion QB Constructor
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    //#region Structural Attribute Builder Constructor
 | 
					 | 
				
			||||||
    document.querySelectorAll('[data-structural-attr-modal-action-button]').forEach(button => {
 | 
					 | 
				
			||||||
      button.addEventListener('click', () => {
 | 
					 | 
				
			||||||
        this.actionButtonInStrucAttrModalHandler(button.dataset.structuralAttrModalActionButton);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelector('.ent-type-selection-action[data-ent-type="any"]').addEventListener('click', () => {
 | 
					 | 
				
			||||||
      this.submitQueryChipElement('start-empty-entity', 'Entity Start', '<ent>');
 | 
					 | 
				
			||||||
      this.submitQueryChipElement('end-entity', 'Entity End', '</ent>', null, true);
 | 
					 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelector('.ent-type-selection-action[data-ent-type="english"]').addEventListener('change', (event) => {
 | 
					 | 
				
			||||||
      this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
 | 
					 | 
				
			||||||
      if (!this.elements.editingModusOn) {
 | 
					 | 
				
			||||||
        this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    document.querySelector('.ent-type-selection-action[data-ent-type="german"]').addEventListener('change', (event) => {
 | 
					 | 
				
			||||||
      this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true); 
 | 
					 | 
				
			||||||
      if (!this.elements.editingModusOn) {
 | 
					 | 
				
			||||||
        this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    //#endregion Structural Attribute Builder Constructor
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    //#region Token Attribute Builder Constructor
 | 
					 | 
				
			||||||
    this.elements.positionalAttrSelection.addEventListener('change', () => {
 | 
					 | 
				
			||||||
      this.preparePositionalAttrModal();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Options for positional attribute selection
 | 
					 | 
				
			||||||
    document.querySelectorAll('.positional-attr-options-action-button[data-options-action]').forEach(button => {
 | 
					 | 
				
			||||||
      button.addEventListener('click', () => {this.actionButtonInOptionSectionHandler(button.dataset.optionsAction);});
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.elements.tokenSubmitButton.addEventListener('click', () => {this.addTokenToQuery();});
 | 
					 | 
				
			||||||
    //#endregion Token Attribute Builder Constructor
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //#region QB Functions
 | 
					  incidenceModifierEventListeners() {
 | 
				
			||||||
 | 
					    // Eventlisteners for the incidence modifiers. There are two different types of incidence modifiers: token and character incidence modifiers.
 | 
				
			||||||
 | 
					    document.querySelectorAll('.incidence-modifier-selection').forEach(button => {
 | 
				
			||||||
 | 
					      let dropdownId = button.parentNode.parentNode.id;
 | 
				
			||||||
 | 
					      if (dropdownId === 'corpus-analysis-concordance-token-incidence-modifiers-dropdown') {
 | 
				
			||||||
 | 
					        button.addEventListener('click', () => this.generalFunctions.tokenIncidenceModifierHandler(button.dataset.token, button.innerHTML));
 | 
				
			||||||
 | 
					      } else if (dropdownId === 'corpus-analysis-concordance-character-incidence-modifiers-dropdown') {
 | 
				
			||||||
 | 
					        button.addEventListener('click', () => this.tokenAttributeBuilderFunctions.characterIncidenceModifierHandler(button));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nAndMInputSubmitEventListeners() {
 | 
				
			||||||
 | 
					    // Eventlisteners for the submit of n- and m-values of the incidence modifier modal for "exactly n" or "between n and m".
 | 
				
			||||||
 | 
					    document.querySelectorAll('.n-m-submit-button').forEach(button => {
 | 
				
			||||||
 | 
					      let modalId = button.dataset.modalId;
 | 
				
			||||||
 | 
					      if (modalId === 'corpus-analysis-concordance-exactly-n-token-modal' || modalId === 'corpus-analysis-concordance-between-nm-token-modal') {
 | 
				
			||||||
 | 
					        button.addEventListener('click', () => this.generalFunctions.tokenNMSubmitHandler(modalId));
 | 
				
			||||||
 | 
					      } else if (modalId === 'corpus-analysis-concordance-exactly-n-character-modal' || modalId === 'corpus-analysis-concordance-between-nm-character-modal') {
 | 
				
			||||||
 | 
					        button.addEventListener('click', () => this.tokenAttributeBuilderFunctions.characterNMSubmitHandler(modalId));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  switchToExpertModeParser() {
 | 
					  switchToExpertModeParser() {
 | 
				
			||||||
    let expertModeInputField = document.querySelector('#corpus-analysis-concordance-form-query');
 | 
					    let expertModeInputField = document.querySelector('#corpus-analysis-concordance-form-query');
 | 
				
			||||||
    expertModeInputField.value = '';
 | 
					    expertModeInputField.value = '';
 | 
				
			||||||
@@ -116,7 +62,7 @@ class ConcordanceQueryBuilder {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  switchToQueryBuilderParser() {
 | 
					  switchToQueryBuilderParser() {
 | 
				
			||||||
    this.resetQueryInputField();
 | 
					    this.generalFunctions.resetQueryInputField();
 | 
				
			||||||
    let expertModeInputFieldValue = document.querySelector('#corpus-analysis-concordance-form-query').value;
 | 
					    let expertModeInputFieldValue = document.querySelector('#corpus-analysis-concordance-form-query').value;
 | 
				
			||||||
    let chipElements = this.parseTextToChip(expertModeInputFieldValue);
 | 
					    let chipElements = this.parseTextToChip(expertModeInputFieldValue);
 | 
				
			||||||
    let closingTagElements = ['end-sentence', 'end-entity'];
 | 
					    let closingTagElements = ['end-sentence', 'end-entity'];
 | 
				
			||||||
@@ -127,7 +73,7 @@ class ConcordanceQueryBuilder {
 | 
				
			|||||||
      if (chipElement['query'] === '[]'){
 | 
					      if (chipElement['query'] === '[]'){
 | 
				
			||||||
        isEditable = false;
 | 
					        isEditable = false;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query'], null, isClosingTag, isEditable);
 | 
					      this.generalFunctions.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query'], null, isClosingTag, isEditable);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -242,696 +188,5 @@ class ConcordanceQueryBuilder {
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
    return chipElements;
 | 
					    return chipElements;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  //#endregion QB Functions
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  //#region General Functions
 | 
					 | 
				
			||||||
  toggleClass(elements, className, action){
 | 
					 | 
				
			||||||
    elements.forEach(element => {
 | 
					 | 
				
			||||||
      document.querySelector(`[data-toggle-area="${element}"]`).classList[action](className);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  resetQueryInputField() {
 | 
					 | 
				
			||||||
    console.log("resetQueryInputField");
 | 
					 | 
				
			||||||
    this.elements.queryInputField.innerHTML = '';
 | 
					 | 
				
			||||||
    this.addPlaceholder();
 | 
					 | 
				
			||||||
    this.updateChipList();
 | 
					 | 
				
			||||||
    this.queryPreviewBuilder();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  updateChipList() {
 | 
					 | 
				
			||||||
    this.elements.queryChipElements = this.elements.queryInputField.querySelectorAll('.query-component');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  removePlaceholder() {
 | 
					 | 
				
			||||||
    let placeholder = this.elements.queryInputField.querySelector('#corpus-analysis-concordance-query-builder-input-field-placeholder');  
 | 
					 | 
				
			||||||
    if (placeholder && this.elements.queryInputField !== undefined) {
 | 
					 | 
				
			||||||
      this.elements.queryInputField.innerHTML = '';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  addPlaceholder() {
 | 
					 | 
				
			||||||
    let placeholder = Utils.HTMLToElement('<span id="corpus-analysis-concordance-query-builder-input-field-placeholder">Click on a button to add a query component</span>');
 | 
					 | 
				
			||||||
    this.elements.queryInputField.appendChild(placeholder);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  resetMaterializeSelection(selectionElements, value = "default") {
 | 
					 | 
				
			||||||
    selectionElements.forEach(selectionElement => {
 | 
					 | 
				
			||||||
      if (selectionElement.querySelector(`option[value=${value}]`) !== null) {
 | 
					 | 
				
			||||||
        selectionElement.querySelector(`option[value=${value}]`).selected = true;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      let instance = M.FormSelect.getInstance(selectionElement);
 | 
					 | 
				
			||||||
      instance.destroy();
 | 
					 | 
				
			||||||
      M.FormSelect.init(selectionElement);
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  submitQueryChipElement(dataType = undefined, prettyQueryText = undefined, queryText = undefined, index = null, isClosingTag = false, isEditable = false) {
 | 
					 | 
				
			||||||
    if (this.elements.editingModusOn) {
 | 
					 | 
				
			||||||
      let editedQueryChipElement = this.elements.queryChipElements[this.elements.editedQueryChipElementIndex];
 | 
					 | 
				
			||||||
      editedQueryChipElement.dataset.type = dataType;
 | 
					 | 
				
			||||||
      editedQueryChipElement.dataset.query = queryText;
 | 
					 | 
				
			||||||
      editedQueryChipElement.firstChild.textContent = prettyQueryText;
 | 
					 | 
				
			||||||
      this.updateChipList();
 | 
					 | 
				
			||||||
      this.queryPreviewBuilder();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.queryChipFactory(dataType, prettyQueryText, queryText, index, isClosingTag, isEditable);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  queryChipFactory(dataType, prettyQueryText, queryText, index = null, isClosingTag = false, isEditable = false) {
 | 
					 | 
				
			||||||
    // Creates a new query chip element, adds Eventlisteners for selection, deletion and drag and drop and appends it to the query input field.
 | 
					 | 
				
			||||||
    queryText = Utils.escape(queryText);
 | 
					 | 
				
			||||||
    prettyQueryText = Utils.escape(prettyQueryText);
 | 
					 | 
				
			||||||
    let queryChipElement = Utils.HTMLToElement(
 | 
					 | 
				
			||||||
      `
 | 
					 | 
				
			||||||
        <span class="chip query-component" data-type="${dataType}" data-query="${queryText}" draggable="true" data-closing-tag="${isClosingTag}">
 | 
					 | 
				
			||||||
          ${prettyQueryText}${isEditable ? '<i class="material-icons chip-action-button" data-chip-action="edit" style="padding-left:5px; font-size:18px; cursor:pointer;">edit</i>': ''}
 | 
					 | 
				
			||||||
          ${isClosingTag ? '<i class="material-icons chip-action-button" data-chip-action="lock" style="padding-top:5px; font-size:20px; cursor:pointer;">lock_open</i>' : '<i class="material-icons close chip-action-button" data-chip-action="delete">close</i>'}
 | 
					 | 
				
			||||||
        </span>
 | 
					 | 
				
			||||||
      `
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
    this.actionListeners(queryChipElement);
 | 
					 | 
				
			||||||
    queryChipElement.addEventListener('dragstart', this.handleDragStart.bind(this, queryChipElement));
 | 
					 | 
				
			||||||
    queryChipElement.addEventListener('dragend', this.handleDragEnd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ensures that metadata is always at the end of the query and if an index is given, inserts the query chip at the given index and if there is a closing tag, inserts the query chip before the closing tag.
 | 
					 | 
				
			||||||
    this.removePlaceholder();
 | 
					 | 
				
			||||||
    let lastChild = this.elements.queryInputField.lastChild;
 | 
					 | 
				
			||||||
    let isLastChildTextAnnotation = lastChild && lastChild.dataset.type === 'text-annotation';
 | 
					 | 
				
			||||||
    if (!index) {
 | 
					 | 
				
			||||||
      let closingTagElement = this.elements.queryInputField.querySelector('[data-closing-tag="true"]');
 | 
					 | 
				
			||||||
      if (closingTagElement) {
 | 
					 | 
				
			||||||
        index = Array.from(this.elements.queryInputField.children).indexOf(closingTagElement);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (dataType !== 'text-annotation' && index) {
 | 
					 | 
				
			||||||
      this.elements.queryInputField.insertBefore(queryChipElement, this.elements.queryChipElements[index]);
 | 
					 | 
				
			||||||
    } else if (dataType !== 'text-annotation' && isLastChildTextAnnotation) {
 | 
					 | 
				
			||||||
      this.elements.queryInputField.insertBefore(queryChipElement, lastChild);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.elements.queryInputField.appendChild(queryChipElement);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.updateChipList();
 | 
					 | 
				
			||||||
    this.queryPreviewBuilder();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  actionListeners(queryChipElement) {
 | 
					 | 
				
			||||||
    let notQuantifiableDataTypes = ['start-sentence', 'end-sentence', 'start-entity', 'start-empty-entity', 'end-entity', 'token-incidence-modifier'];
 | 
					 | 
				
			||||||
    queryChipElement.addEventListener('click', (event) => {
 | 
					 | 
				
			||||||
      if (event.target.classList.contains('chip')) {
 | 
					 | 
				
			||||||
        if (!notQuantifiableDataTypes.includes(queryChipElement.dataset.type)) {
 | 
					 | 
				
			||||||
          this.selectChipElement(queryChipElement);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    let chipActionButtons = queryChipElement.querySelectorAll('.chip-action-button');
 | 
					 | 
				
			||||||
    // chipActionButtons.forEach(button => {
 | 
					 | 
				
			||||||
    for (let button of chipActionButtons) {
 | 
					 | 
				
			||||||
      button.addEventListener('click', (event) => {
 | 
					 | 
				
			||||||
      if (event.target.dataset.chipAction === 'delete') {
 | 
					 | 
				
			||||||
        this.deleteChipElement(queryChipElement);
 | 
					 | 
				
			||||||
      } else if (event.target.dataset.chipAction === 'edit') {
 | 
					 | 
				
			||||||
        this.editChipElement(queryChipElement);
 | 
					 | 
				
			||||||
      } else if (event.target.dataset.chipAction === 'lock') {
 | 
					 | 
				
			||||||
        this.lockClosingChipElement(queryChipElement);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    // });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //hier wird this.toggleEditingAreaStructuralAttrModal('add'); aufgerufen aus StructuralAttributeBuilderFunctionsQueryBuilder.js
 | 
					 | 
				
			||||||
  editChipElement(queryChipElement) {
 | 
					 | 
				
			||||||
    //TODO: Split this function into smaller functionss
 | 
					 | 
				
			||||||
    this.elements.editingModusOn = true;
 | 
					 | 
				
			||||||
    this.elements.editedQueryChipElementIndex = Array.from(this.elements.queryInputField.children).indexOf(queryChipElement);
 | 
					 | 
				
			||||||
    switch (queryChipElement.dataset.type) {
 | 
					 | 
				
			||||||
      case 'start-entity':
 | 
					 | 
				
			||||||
        this.elements.structuralAttrModal.open();
 | 
					 | 
				
			||||||
        this.toggleClass(['entity-builder'], 'hide', 'remove');
 | 
					 | 
				
			||||||
        this.toggleEditingAreaStructuralAttrModal('add');
 | 
					 | 
				
			||||||
        let entType = queryChipElement.dataset.query.replace(/<ent_type="|">/g, '');
 | 
					 | 
				
			||||||
        let isEnglishEntType = this.elements.englishEntTypeSelection.querySelector(`option[value=${entType}]`) !== null;
 | 
					 | 
				
			||||||
        let selection = isEnglishEntType ? this.elements.englishEntTypeSelection : this.elements.germanEntTypeSelection;
 | 
					 | 
				
			||||||
        this.resetMaterializeSelection([selection], entType);
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'text-annotation':
 | 
					 | 
				
			||||||
        this.elements.structuralAttrModal.open();
 | 
					 | 
				
			||||||
        this.toggleClass(['text-annotation-builder'], 'hide', 'remove');
 | 
					 | 
				
			||||||
        this.toggleEditingAreaStructuralAttrModal('add');
 | 
					 | 
				
			||||||
        let [textAnnotationSelection, textAnnotationContent] = queryChipElement.dataset.query
 | 
					 | 
				
			||||||
          .replace(/:: ?match\.text_|"|"/g, '')
 | 
					 | 
				
			||||||
          .split('=');
 | 
					 | 
				
			||||||
        this.resetMaterializeSelection([this.elements.textAnnotationSelection], textAnnotationSelection);
 | 
					 | 
				
			||||||
        this.elements.textAnnotationInput.value = textAnnotationContent;
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'token':
 | 
					 | 
				
			||||||
        //this regex searches for word or lemma or pos or simple_pos="any string within single or double quotes" followed by one or no ignore case markers, followed by one or no condition characters.
 | 
					 | 
				
			||||||
      	let regex = new RegExp('(word|lemma|pos|simple_pos)=(("[^"]+")|(\\\\u0027[^\\\\u0027]+\\\\u0027)) ?(%c)? ?(\\&|\\|)?', 'gm');
 | 
					 | 
				
			||||||
        let m;
 | 
					 | 
				
			||||||
        let queryElementsContent = [];
 | 
					 | 
				
			||||||
        while ((m = regex.exec(queryChipElement.dataset.query)) !== null) {
 | 
					 | 
				
			||||||
          // this is necessary to avoid infinite loops with zero-width matches
 | 
					 | 
				
			||||||
          if (m.index === regex.lastIndex) {
 | 
					 | 
				
			||||||
              regex.lastIndex++;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          let tokenAttr = m[1];
 | 
					 | 
				
			||||||
          // Passes english-pos by default so that the template is added. In editTokenChipElement it is then checked whether it is english-pos or german-pos.
 | 
					 | 
				
			||||||
          if (tokenAttr === 'pos') {
 | 
					 | 
				
			||||||
            tokenAttr = 'english-pos';
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          let tokenValue = m[2].replace(/"|'/g, '');
 | 
					 | 
				
			||||||
          let ignoreCase = false;
 | 
					 | 
				
			||||||
          let condition = undefined;
 | 
					 | 
				
			||||||
          m.forEach((match) => {
 | 
					 | 
				
			||||||
            if (match === "%c") {
 | 
					 | 
				
			||||||
              ignoreCase = true;
 | 
					 | 
				
			||||||
            } else if (match === "&") {
 | 
					 | 
				
			||||||
              condition = "and";
 | 
					 | 
				
			||||||
            } else if (match === "|") {
 | 
					 | 
				
			||||||
              condition = "or";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          queryElementsContent.push({tokenAttr: tokenAttr, tokenValue: tokenValue, ignoreCase: ignoreCase, condition: condition});
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this.editTokenChipElement(queryElementsContent);
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      default:
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //hier wird this.preparePositionalAttrModal(); und this.conditionHandler(); aufgerufen aus TokenAttributeBuilderFunctionsQueryBuilder.js
 | 
					 | 
				
			||||||
  editTokenChipElement(queryElementsContent) {
 | 
					 | 
				
			||||||
    this.elements.positionalAttrModal.open();
 | 
					 | 
				
			||||||
    for (let queryElement of queryElementsContent) {
 | 
					 | 
				
			||||||
    // queryElementsContent.forEach((queryElement) => {
 | 
					 | 
				
			||||||
      this.resetMaterializeSelection([this.elements.positionalAttrSelection], queryElement.tokenAttr);
 | 
					 | 
				
			||||||
      this.preparePositionalAttrModal();
 | 
					 | 
				
			||||||
      switch (queryElement.tokenAttr) {
 | 
					 | 
				
			||||||
        case 'word':
 | 
					 | 
				
			||||||
        case 'lemma':
 | 
					 | 
				
			||||||
          this.elements.tokenBuilderContent.querySelector('input').value = queryElement.tokenValue;
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case 'english-pos':
 | 
					 | 
				
			||||||
          // English-pos is selected by default. Then it is checked whether the passed token value occurs in the english-pos selection. If not, the selection is reseted and changed to german-pos.
 | 
					 | 
				
			||||||
          let selection = this.elements.tokenBuilderContent.querySelector('select');
 | 
					 | 
				
			||||||
          queryElement.tokenAttr = selection.querySelector(`option[value=${queryElement.tokenValue}]`) ? 'english-pos' : 'german-pos';
 | 
					 | 
				
			||||||
          this.resetMaterializeSelection([this.elements.positionalAttrSelection], queryElement.tokenAttr);
 | 
					 | 
				
			||||||
          this.preparePositionalAttrModal();
 | 
					 | 
				
			||||||
          this.resetMaterializeSelection([this.elements.tokenBuilderContent.querySelector('select')], queryElement.tokenValue);
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        case 'simple_pos':
 | 
					 | 
				
			||||||
          this.resetMaterializeSelection([this.elements.tokenBuilderContent.querySelector('select')], queryElement.tokenValue);
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (queryElement.ignoreCase) {
 | 
					 | 
				
			||||||
        this.elements.ignoreCaseCheckbox.checked = true;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (queryElement.condition !== undefined) {
 | 
					 | 
				
			||||||
        this.conditionHandler(queryElement.condition, true);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  lockClosingChipElement(queryChipElement) {
 | 
					 | 
				
			||||||
    queryChipElement.dataset.closingTag = 'false';
 | 
					 | 
				
			||||||
    let lockIcon = queryChipElement.querySelector('[data-chip-action="lock"]');
 | 
					 | 
				
			||||||
    lockIcon.textContent = 'lock';
 | 
					 | 
				
			||||||
    //TODO: Write unlock-Function?
 | 
					 | 
				
			||||||
    lockIcon.dataset.chipAction = 'unlock';
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  deleteChipElement(attr) {
 | 
					 | 
				
			||||||
    let elementIndex = Array.from(this.elements.queryInputField.children).indexOf(attr);
 | 
					 | 
				
			||||||
    switch (attr.dataset.type) {
 | 
					 | 
				
			||||||
      case 'start-sentence':
 | 
					 | 
				
			||||||
        this.deletingClosingTagHandler(elementIndex, 'end-sentence');
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'start-entity':
 | 
					 | 
				
			||||||
        this.deletingClosingTagHandler(elementIndex, 'end-entity');
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'token':
 | 
					 | 
				
			||||||
        let nextElement = Array.from(this.elements.queryInputField.children)[elementIndex+1];
 | 
					 | 
				
			||||||
        if (nextElement !== undefined && nextElement.dataset.type === 'token-incidence-modifier') {
 | 
					 | 
				
			||||||
          this.deleteChipElement(nextElement);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      default:
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.elements.queryInputField.removeChild(attr);
 | 
					 | 
				
			||||||
    if (this.elements.queryInputField.children.length === 0) {
 | 
					 | 
				
			||||||
      this.addPlaceholder();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.updateChipList();
 | 
					 | 
				
			||||||
    this.queryPreviewBuilder();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  deletingClosingTagHandler(elementIndex, closingTagType) {
 | 
					 | 
				
			||||||
    let closingTags = this.elements.queryInputField.querySelectorAll(`[data-type="${closingTagType}"]`);
 | 
					 | 
				
			||||||
    for (let i = 0; i < closingTags.length; i++) {
 | 
					 | 
				
			||||||
      let closingTag = closingTags[i];
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
      if (Array.from(this.elements.queryInputField.children).indexOf(closingTag) > elementIndex) {
 | 
					 | 
				
			||||||
        this.deleteChipElement(closingTag);
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleDragStart(queryChipElement, event) {
 | 
					 | 
				
			||||||
    // is called when a query chip is dragged. It creates a dropzone (in form of a chip) for the dragged chip and adds it to the query input field.
 | 
					 | 
				
			||||||
    let queryChips = this.elements.queryInputField.querySelectorAll('.query-component');
 | 
					 | 
				
			||||||
    if (queryChipElement.dataset.type === 'token-incidence-modifier') {
 | 
					 | 
				
			||||||
      queryChips = this.elements.queryInputField.querySelectorAll('.query-component[data-type="token"]');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
      let targetChipElement = Utils.HTMLToElement('<span class="chip drop-target">Drop here</span>');
 | 
					 | 
				
			||||||
      for (let element of queryChips) {
 | 
					 | 
				
			||||||
        if (element === this.elements.queryInputField.querySelectorAll('.query-component')[0]) {
 | 
					 | 
				
			||||||
          let secondTargetChipClone = targetChipElement.cloneNode(true);
 | 
					 | 
				
			||||||
          element.insertAdjacentElement('beforebegin', secondTargetChipClone);
 | 
					 | 
				
			||||||
          this.addDragDropListeners(secondTargetChipClone, queryChipElement);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (element === queryChipElement || element.nextSibling === queryChipElement) {continue;}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let targetChipClone = targetChipElement.cloneNode(true);
 | 
					 | 
				
			||||||
        element.insertAdjacentElement('afterend', targetChipClone);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.addDragDropListeners(targetChipClone, queryChipElement);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }, 0);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleDragEnd(event) {
 | 
					 | 
				
			||||||
    document.querySelectorAll('.drop-target').forEach(target => target.remove());
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  addDragDropListeners(targetChipClone, queryChipElement) {
 | 
					 | 
				
			||||||
    targetChipClone.addEventListener('dragover', (event) => {
 | 
					 | 
				
			||||||
      event.preventDefault();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    targetChipClone.addEventListener('dragenter', (event) => {
 | 
					 | 
				
			||||||
      event.preventDefault();
 | 
					 | 
				
			||||||
      event.target.style.borderStyle = 'solid dotted';
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    targetChipClone.addEventListener('dragleave', (event) => {
 | 
					 | 
				
			||||||
      event.preventDefault();
 | 
					 | 
				
			||||||
      event.target.style.borderStyle = 'hidden';
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    targetChipClone.addEventListener('drop', (event) => {
 | 
					 | 
				
			||||||
      let dropzone = event.target;
 | 
					 | 
				
			||||||
      dropzone.parentElement.replaceChild(queryChipElement, dropzone);
 | 
					 | 
				
			||||||
      this.updateChipList();
 | 
					 | 
				
			||||||
      this.queryPreviewBuilder();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  queryPreviewBuilder() {
 | 
					 | 
				
			||||||
    // Builds the query preview in the form of pure CQL and displays it in the query preview field.
 | 
					 | 
				
			||||||
    let queryPreview = document.querySelector('#corpus-analysis-concordance-query-preview');
 | 
					 | 
				
			||||||
    let queryInputFieldContent = [];
 | 
					 | 
				
			||||||
    this.elements.queryChipElements.forEach(element => {
 | 
					 | 
				
			||||||
      let queryElement = element.dataset.query;
 | 
					 | 
				
			||||||
      if (queryElement !== undefined) {
 | 
					 | 
				
			||||||
        queryElement = Utils.escape(queryElement);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      queryInputFieldContent.push(queryElement);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
    let queryString = queryInputFieldContent.join(' ');
 | 
					 | 
				
			||||||
    let replacements = {
 | 
					 | 
				
			||||||
      ' +': '+',
 | 
					 | 
				
			||||||
      ' *': '*',
 | 
					 | 
				
			||||||
      ' ?': '?',
 | 
					 | 
				
			||||||
      ' {': '{'
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (let key in replacements) {
 | 
					 | 
				
			||||||
      queryString = queryString.replace(key, replacements[key]);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    queryString += ';';
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
    queryPreview.innerHTML = queryString;
 | 
					 | 
				
			||||||
    queryPreview.parentNode.classList.toggle('hide', queryString === ';');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  selectChipElement(attr) {
 | 
					 | 
				
			||||||
    document.querySelectorAll('.chip.teal').forEach(element => {
 | 
					 | 
				
			||||||
        if (element !== attr) {
 | 
					 | 
				
			||||||
          element.classList.remove('teal', 'lighten-2');
 | 
					 | 
				
			||||||
          this.toggleClass(['token-incidence-modifiers'], 'disabled', 'add');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.toggleClass(['token-incidence-modifiers'], 'disabled', 'toggle');
 | 
					 | 
				
			||||||
    attr.classList.toggle('teal');
 | 
					 | 
				
			||||||
    attr.classList.toggle('lighten-5');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  tokenIncidenceModifierHandler(incidenceModifier, incidenceModifierPretty) {
 | 
					 | 
				
			||||||
    // Adds a token incidence modifier to the query input field.
 | 
					 | 
				
			||||||
    let selectedChip = this.elements.queryInputField.querySelector('.chip.teal');
 | 
					 | 
				
			||||||
    let selectedChipIndex = Array.from(this.elements.queryInputField.children).indexOf(selectedChip);
 | 
					 | 
				
			||||||
    this.submitQueryChipElement('token-incidence-modifier', incidenceModifierPretty, incidenceModifier, selectedChipIndex+1);
 | 
					 | 
				
			||||||
    this.selectChipElement(selectedChip);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  tokenNMSubmitHandler(modalId) {
 | 
					 | 
				
			||||||
    // Adds a token incidence modifier (exactly n or between n and m) to the query input field.
 | 
					 | 
				
			||||||
    let modal = document.querySelector(`#${modalId}`);
 | 
					 | 
				
			||||||
    let input_n = modal.querySelector('.n-m-input[data-value-type="n"]').value;
 | 
					 | 
				
			||||||
    let input_m = modal.querySelector('.n-m-input[data-value-type="m"]') || undefined;
 | 
					 | 
				
			||||||
    input_m = input_m !== undefined ? input_m.value : '';
 | 
					 | 
				
			||||||
    let input = `{${input_n}${input_m !== '' ? ',' : ''}${input_m}}`;
 | 
					 | 
				
			||||||
    let pretty_input = `between ${input_n} and ${input_m} (${input})`;
 | 
					 | 
				
			||||||
    if (input_m === '') {
 | 
					 | 
				
			||||||
     pretty_input = `exactly ${input_n} (${input})`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let instance = M.Modal.getInstance(modal);
 | 
					 | 
				
			||||||
    instance.close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.tokenIncidenceModifierHandler(input, pretty_input);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  //#endregion General Functions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //#region Structural Attribute Builder Functions
 | 
					 | 
				
			||||||
  // Hier wird resetMaterializeSelection() und toggleClass() aufgerufen, das in GeneralFunctionsQueryBuilder definiert ist.
 | 
					 | 
				
			||||||
  resetStructuralAttrModal() {
 | 
					 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]);
 | 
					 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address');
 | 
					 | 
				
			||||||
    this.elements.textAnnotationInput.value = '';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add');
 | 
					 | 
				
			||||||
    this.toggleEditingAreaStructuralAttrModal('remove');
 | 
					 | 
				
			||||||
    this.elements.editingModusOn = false;
 | 
					 | 
				
			||||||
    this.elements.editedQueryChipElementIndex = undefined;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Hier wird toggleClass() aufgerufen, das in GeneralFunctionsQueryBuilder definiert ist.
 | 
					 | 
				
			||||||
  toggleEditingAreaStructuralAttrModal(action) {
 | 
					 | 
				
			||||||
    // If the user edits a query chip element, the corresponding editing area is displayed and the other areas are hidden or disabled.
 | 
					 | 
				
			||||||
    this.toggleClass(['sentence-button', 'entity-button', 'text-annotation-button', 'any-type-entity-button'], 'disabled', action);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Hier wird toggleClass() und submitQueryChipElement() aufgerufen, das in GeneralFunctionsQueryBuilder definiert ist.
 | 
					 | 
				
			||||||
  actionButtonInStrucAttrModalHandler(action) {
 | 
					 | 
				
			||||||
    switch (action) {
 | 
					 | 
				
			||||||
      case 'sentence':
 | 
					 | 
				
			||||||
        this.submitQueryChipElement('start-sentence', 'Sentence Start', '<s>');
 | 
					 | 
				
			||||||
        this.submitQueryChipElement('end-sentence', 'Sentence End', '</s>', null, true);
 | 
					 | 
				
			||||||
        this.elements.structuralAttrModal.close();
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'entity':
 | 
					 | 
				
			||||||
        this.toggleClass(['entity-builder'], 'hide', 'toggle');
 | 
					 | 
				
			||||||
        this.toggleClass(['text-annotation-builder'], 'hide', 'add');
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'meta-data':
 | 
					 | 
				
			||||||
        this.toggleClass(['text-annotation-builder'], 'hide', 'toggle');
 | 
					 | 
				
			||||||
        this.toggleClass(['entity-builder'], 'hide', 'add');
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      default:
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Hier wird submitQueryChipElement() aufgerufen, das in GeneralFunctionsQueryBuilder definiert ist.
 | 
					 | 
				
			||||||
  textAnnotationSubmitHandler() {
 | 
					 | 
				
			||||||
    let noValueMetadataMessage = document.querySelector('#corpus-analysis-concordance-no-value-metadata-message');
 | 
					 | 
				
			||||||
    let textAnnotationSubmit = document.querySelector('#corpus-analysis-concordance-text-annotation-submit');
 | 
					 | 
				
			||||||
    let textAnnotationInput = document.querySelector('#corpus-analysis-concordance-text-annotation-input');
 | 
					 | 
				
			||||||
    let textAnnotationOptions = document.querySelector('#corpus-analysis-concordance-text-annotation-options');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (textAnnotationInput.value === '') {
 | 
					 | 
				
			||||||
      textAnnotationSubmit.classList.add('red');
 | 
					 | 
				
			||||||
      noValueMetadataMessage.classList.remove('hide');
 | 
					 | 
				
			||||||
      setTimeout(() => {
 | 
					 | 
				
			||||||
        textAnnotationSubmit.classList.remove('red');
 | 
					 | 
				
			||||||
      }, 500);
 | 
					 | 
				
			||||||
      setTimeout(() => {
 | 
					 | 
				
			||||||
        noValueMetadataMessage.classList.add('hide');
 | 
					 | 
				
			||||||
      }, 3000);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      let queryText = `:: match.text_${textAnnotationOptions.value}="${textAnnotationInput.value}"`;
 | 
					 | 
				
			||||||
      this.submitQueryChipElement('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText, null, false, true);
 | 
					 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  //#endregion Structural Attribute Builder Functions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //#region Token Attribute Builder Functions
 | 
					 | 
				
			||||||
  resetPositionalAttrModal() {
 | 
					 | 
				
			||||||
    let originalSelectionList = 
 | 
					 | 
				
			||||||
      `
 | 
					 | 
				
			||||||
        <option value="word" selected>word</option>
 | 
					 | 
				
			||||||
        <option value="lemma" >lemma</option>
 | 
					 | 
				
			||||||
        <option value="english-pos">english pos</option>
 | 
					 | 
				
			||||||
        <option value="german-pos">german pos</option>
 | 
					 | 
				
			||||||
        <option value="simple_pos">simple_pos</option>
 | 
					 | 
				
			||||||
        <option value="empty-token">empty token</option>
 | 
					 | 
				
			||||||
      `;
 | 
					 | 
				
			||||||
    this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
 | 
					 | 
				
			||||||
    this.elements.tokenQuery.innerHTML = '';
 | 
					 | 
				
			||||||
    this.elements.tokenBuilderContent.innerHTML = '';
 | 
					 | 
				
			||||||
    this.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
					 | 
				
			||||||
    this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
					 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.positionalAttrSelection], "word");
 | 
					 | 
				
			||||||
    this.elements.ignoreCaseCheckbox.checked = false;
 | 
					 | 
				
			||||||
    this.elements.editingModusOn = false;
 | 
					 | 
				
			||||||
    this.elements.editedQueryChipElementIndex = undefined;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  preparePositionalAttrModal() {
 | 
					 | 
				
			||||||
    let selection = this.elements.positionalAttrSelection.value;
 | 
					 | 
				
			||||||
    if (selection !== 'empty-token') {
 | 
					 | 
				
			||||||
      let selectionTemplate = document.querySelector(`.token-builder-section[data-token-builder-section="${selection}"]`);
 | 
					 | 
				
			||||||
      let selectionTemplateClone = selectionTemplate.content.cloneNode(true);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
      this.elements.tokenBuilderContent.innerHTML = '';
 | 
					 | 
				
			||||||
      this.elements.tokenBuilderContent.appendChild(selectionTemplateClone);
 | 
					 | 
				
			||||||
      if (this.elements.tokenBuilderContent.querySelector('select') !== null) {
 | 
					 | 
				
			||||||
        let selectElement = this.elements.tokenBuilderContent.querySelector('select');
 | 
					 | 
				
			||||||
        M.FormSelect.init(selectElement);
 | 
					 | 
				
			||||||
        selectElement.addEventListener('change', () => {this.optionToggleHandler();});
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        this.elements.tokenBuilderContent.querySelector('input').addEventListener('input', () => {this.optionToggleHandler();});
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.optionToggleHandler();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (selection === 'word' || selection === 'lemma') {
 | 
					 | 
				
			||||||
      this.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
					 | 
				
			||||||
    } else if (selection === 'empty-token'){
 | 
					 | 
				
			||||||
      this.addTokenToQuery();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.toggleClass(['input-field-options'], 'hide', 'add');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  tokenInputCheck(elem) {
 | 
					 | 
				
			||||||
    return elem.querySelector('select') !== null ? elem.querySelector('select') : elem.querySelector('input');
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  optionToggleHandler() {
 | 
					 | 
				
			||||||
    let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
					 | 
				
			||||||
    if (input.value === '' && this.elements.editingModusOn === false) {
 | 
					 | 
				
			||||||
      this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
					 | 
				
			||||||
    } else if (this.elements.positionalAttrSelection.querySelectorAll('option').length === 1) {
 | 
					 | 
				
			||||||
      this.toggleClass(['and'], 'disabled', 'add');
 | 
					 | 
				
			||||||
      this.toggleClass(['or'], 'disabled', 'remove');
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'remove');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  disableTokenSubmit() {
 | 
					 | 
				
			||||||
    this.elements.tokenSubmitButton.classList.add('red');
 | 
					 | 
				
			||||||
    this.elements.noValueMessage.classList.remove('hide');
 | 
					 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
      this.elements.tokenSubmitButton.classList.remove('red');
 | 
					 | 
				
			||||||
    }, 500);
 | 
					 | 
				
			||||||
    setTimeout(() => {
 | 
					 | 
				
			||||||
      this.elements.noValueMessage.classList.add('hide');
 | 
					 | 
				
			||||||
    }, 3000);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  addTokenToQuery() {
 | 
					 | 
				
			||||||
    let tokenQueryPrettyText = '';
 | 
					 | 
				
			||||||
    let tokenQueryCQLText = '';
 | 
					 | 
				
			||||||
    let input;
 | 
					 | 
				
			||||||
    let kindOfToken = this.kindOfTokenCheck(this.elements.positionalAttrSelection.value);
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Takes all rows of the token query (if there is a query concatenation).
 | 
					 | 
				
			||||||
    // Adds their contents to tokenQueryPrettyText and tokenQueryCQLText, which will later be expanded with the current input field.
 | 
					 | 
				
			||||||
    let tokenQueryRows = this.elements.tokenQuery.querySelectorAll('.row');
 | 
					 | 
				
			||||||
    tokenQueryRows.forEach(row => {
 | 
					 | 
				
			||||||
      let ignoreCaseCheckbox = row.querySelector('input[type="checkbox"]');
 | 
					 | 
				
			||||||
      let c = ignoreCaseCheckbox !== null && ignoreCaseCheckbox.checked ? ' %c' : '';
 | 
					 | 
				
			||||||
      let tokenQueryRowInput = this.tokenInputCheck(row.querySelector('.token-query-template-content'));
 | 
					 | 
				
			||||||
      let tokenQueryKindOfToken = this.kindOfTokenCheck(tokenQueryRowInput.closest('.input-field').dataset.kindOfToken);
 | 
					 | 
				
			||||||
      let tokenConditionPrettyText = row.querySelector('[data-condition-pretty-text]').dataset.conditionPrettyText;
 | 
					 | 
				
			||||||
      let tokenConditionCQLText = row.querySelector('[data-condition-cql-text]').dataset.conditionCqlText;
 | 
					 | 
				
			||||||
      tokenQueryPrettyText += `${tokenQueryKindOfToken}=${tokenQueryRowInput.value}${c} ${tokenConditionPrettyText} `;
 | 
					 | 
				
			||||||
      tokenQueryCQLText += `${tokenQueryKindOfToken}="${tokenQueryRowInput.value}"${c} ${tokenConditionCQLText}`;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    if (kindOfToken === 'empty-token') {
 | 
					 | 
				
			||||||
      tokenQueryPrettyText += 'empty token';
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      let c = this.elements.ignoreCaseCheckbox.checked ? ' %c' : '';
 | 
					 | 
				
			||||||
      input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
					 | 
				
			||||||
      tokenQueryPrettyText += `${kindOfToken}=${input.value}${c}`;
 | 
					 | 
				
			||||||
      tokenQueryCQLText += `${kindOfToken}="${input.value}"${c}`;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // isTokenQueryInvalid looks if a valid value is passed. If the input fields/dropdowns are empty (isTokenQueryInvalid === true), no token is added.
 | 
					 | 
				
			||||||
    if (this.elements.positionalAttrSelection.value !== 'empty-token' && input.value === '') {
 | 
					 | 
				
			||||||
      this.disableTokenSubmit();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      tokenQueryCQLText = `[${tokenQueryCQLText}]`;
 | 
					 | 
				
			||||||
      this.submitQueryChipElement('token', tokenQueryPrettyText, tokenQueryCQLText, null, false, kindOfToken === 'empty-token' ? false : true);
 | 
					 | 
				
			||||||
      this.elements.positionalAttrModal.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  kindOfTokenCheck(kindOfToken) {
 | 
					 | 
				
			||||||
    return kindOfToken === 'english-pos' || kindOfToken === 'german-pos' ? 'pos' : kindOfToken;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  actionButtonInOptionSectionHandler(elem) {
 | 
					 | 
				
			||||||
    let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
					 | 
				
			||||||
    switch (elem) {
 | 
					 | 
				
			||||||
      case 'option-group':
 | 
					 | 
				
			||||||
        input.value  += '(option1|option2)';
 | 
					 | 
				
			||||||
        let firstIndex = input.value.indexOf('option1');
 | 
					 | 
				
			||||||
        let lastIndex = firstIndex + 'option1'.length;
 | 
					 | 
				
			||||||
        input.focus();
 | 
					 | 
				
			||||||
        input.setSelectionRange(firstIndex, lastIndex);
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'wildcard-char':
 | 
					 | 
				
			||||||
        input.value += '.';
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'and':
 | 
					 | 
				
			||||||
        this.conditionHandler('and');
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      case 'or':
 | 
					 | 
				
			||||||
        this.conditionHandler('or');
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      default:
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.optionToggleHandler();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  characterIncidenceModifierHandler(elem) {
 | 
					 | 
				
			||||||
    let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
					 | 
				
			||||||
    input.value += elem.dataset.token;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  characterNMSubmitHandler(modalId) {
 | 
					 | 
				
			||||||
    let modal = document.querySelector(`#${modalId}`);
 | 
					 | 
				
			||||||
    let input_n = modal.querySelector('.n-m-input[data-value-type="n"]').value;
 | 
					 | 
				
			||||||
    let input_m = modal.querySelector('.n-m-input[data-value-type="m"]') || undefined;
 | 
					 | 
				
			||||||
    input_m = input_m !== undefined ? ',' + input_m.value : '';
 | 
					 | 
				
			||||||
    let input = `${input_n}${input_m}`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let instance = M.Modal.getInstance(modal);
 | 
					 | 
				
			||||||
    instance.close();
 | 
					 | 
				
			||||||
    let tokenInput = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
					 | 
				
			||||||
    tokenInput.value += '{' + input + '}';
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  conditionHandler(conditionText, editMode = false) {
 | 
					 | 
				
			||||||
    let tokenQueryTemplateClone = this.elements.tokenQueryTemplate.content.cloneNode(true);
 | 
					 | 
				
			||||||
    tokenQueryTemplateClone.querySelector('.token-query-template-content').appendChild(this.elements.tokenBuilderContent.firstElementChild);
 | 
					 | 
				
			||||||
    let notSelectedButton = tokenQueryTemplateClone.querySelector(`[data-condition-pretty-text]:not([data-condition-pretty-text="${conditionText}"])`);
 | 
					 | 
				
			||||||
    let deleteButton = tokenQueryTemplateClone.querySelector(`[data-token-query-content-action="delete"]`);
 | 
					 | 
				
			||||||
    deleteButton.addEventListener('click', (event) => {
 | 
					 | 
				
			||||||
      this.deleteTokenQueryRow(event.target);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    notSelectedButton.parentNode.removeChild(notSelectedButton);
 | 
					 | 
				
			||||||
    this.elements.tokenQuery.appendChild(tokenQueryTemplateClone);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Deleting the options which do not make sense in the context of the condition like "word" AND "word". Also sets selection default.
 | 
					 | 
				
			||||||
    let selectionDefault = "word";
 | 
					 | 
				
			||||||
    let optionDeleteList = ['empty-token'];
 | 
					 | 
				
			||||||
    if (conditionText === 'and') {
 | 
					 | 
				
			||||||
      switch (this.elements.positionalAttrSelection.value) {
 | 
					 | 
				
			||||||
        case 'english-pos' || 'german-pos':
 | 
					 | 
				
			||||||
          optionDeleteList.push('english-pos', 'german-pos');
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
          optionDeleteList.push(this.elements.positionalAttrSelection.value);
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      let originalSelectionList = 
 | 
					 | 
				
			||||||
      `
 | 
					 | 
				
			||||||
        <option value="word" selected>word</option>
 | 
					 | 
				
			||||||
        <option value="lemma" >lemma</option>
 | 
					 | 
				
			||||||
        <option value="english-pos">english pos</option>
 | 
					 | 
				
			||||||
        <option value="german-pos">german pos</option>
 | 
					 | 
				
			||||||
        <option value="simple_pos">simple_pos</option>
 | 
					 | 
				
			||||||
      `;
 | 
					 | 
				
			||||||
      this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
 | 
					 | 
				
			||||||
      M.FormSelect.init(this.elements.positionalAttrSelection);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    let lastTokenQueryRow = this.elements.tokenQuery.lastElementChild;
 | 
					 | 
				
			||||||
    if(lastTokenQueryRow.querySelector('[data-kind-of-token="word"]') || lastTokenQueryRow.querySelector('[data-kind-of-token="lemma"]')) {
 | 
					 | 
				
			||||||
      this.appendIgnoreCaseCheckbox(lastTokenQueryRow.querySelector('.token-query-template-content'), this.elements.ignoreCaseCheckbox.checked);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.elements.ignoreCaseCheckbox.checked = false;
 | 
					 | 
				
			||||||
    this.setTokenSelection(selectionDefault, optionDeleteList);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  deleteTokenQueryRow(deleteButton) {
 | 
					 | 
				
			||||||
    let deletedRow = deleteButton.closest('.row');
 | 
					 | 
				
			||||||
    let condition = deletedRow.querySelector('[data-condition-pretty-text]').dataset.conditionPrettyText;
 | 
					 | 
				
			||||||
    if (condition === 'and') {
 | 
					 | 
				
			||||||
      let kindOfToken = deletedRow.querySelector('[data-kind-of-token]').dataset.kindOfToken;
 | 
					 | 
				
			||||||
      switch (kindOfToken) {
 | 
					 | 
				
			||||||
        case 'english-pos' || 'german-pos':
 | 
					 | 
				
			||||||
          this.createOptionElementForPosAttrSelection('english-pos');
 | 
					 | 
				
			||||||
          this.createOptionElementForPosAttrSelection('german-pos');
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        default:
 | 
					 | 
				
			||||||
          this.createOptionElementForPosAttrSelection(kindOfToken);
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      M.FormSelect.init(this.elements.positionalAttrSelection);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    deletedRow.remove();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  createOptionElementForPosAttrSelection(kindOfToken) {
 | 
					 | 
				
			||||||
    let option = document.createElement('option');
 | 
					 | 
				
			||||||
    option.value = kindOfToken;
 | 
					 | 
				
			||||||
    option.text = kindOfToken;
 | 
					 | 
				
			||||||
    this.elements.positionalAttrSelection.appendChild(option);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  appendIgnoreCaseCheckbox(parentElement, checked = false) {
 | 
					 | 
				
			||||||
    let ignoreCaseCheckboxClone = document.querySelector('#ignore-case-checkbox-template').content.cloneNode(true);
 | 
					 | 
				
			||||||
    parentElement.appendChild(ignoreCaseCheckboxClone);
 | 
					 | 
				
			||||||
    M.Tooltip.init(parentElement.querySelectorAll('.tooltipped'));
 | 
					 | 
				
			||||||
    if (checked) {
 | 
					 | 
				
			||||||
      parentElement.querySelector('input[type="checkbox"]').checked = true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  setTokenSelection(selection, optionDeleteList) {
 | 
					 | 
				
			||||||
    optionDeleteList.forEach(option => {
 | 
					 | 
				
			||||||
      if (this.elements.positionalAttrSelection.querySelector(`option[value=${option}]`) !== null) {
 | 
					 | 
				
			||||||
        this.elements.positionalAttrSelection.querySelector(`option[value=${option}]`).remove();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.positionalAttrSelection], selection);
 | 
					 | 
				
			||||||
    this.preparePositionalAttrModal();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  //#endregion Token Attribute Builder Functions
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					class StructuralAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			||||||
 | 
					  constructor(elements) {
 | 
				
			||||||
 | 
					    super(elements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.structuralAttrModalEventlisteners();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.querySelector('#corpus-analysis-concordance-text-annotation-submit').addEventListener('click', () => this.textAnnotationSubmitHandler());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.elements.structuralAttrModal = M.Modal.init(
 | 
				
			||||||
 | 
					      document.querySelector('#corpus-analysis-concordance-structural-attr-modal'), 
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        onCloseStart: () => {
 | 
				
			||||||
 | 
					          this.resetStructuralAttrModal();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  structuralAttrModalEventlisteners() {
 | 
				
			||||||
 | 
					    document.querySelectorAll('[data-structural-attr-modal-action-button]').forEach(button => {
 | 
				
			||||||
 | 
					      button.addEventListener('click', () => {
 | 
				
			||||||
 | 
					        this.actionButtonInStrucAttrModalHandler(button.dataset.structuralAttrModalActionButton);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelector('.ent-type-selection-action[data-ent-type="any"]').addEventListener('click', () => {
 | 
				
			||||||
 | 
					      this.submitQueryChipElement('start-empty-entity', 'Entity Start', '<ent>');
 | 
				
			||||||
 | 
					      this.submitQueryChipElement('end-entity', 'Entity End', '</ent>', null, true);
 | 
				
			||||||
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelector('.ent-type-selection-action[data-ent-type="english"]').addEventListener('change', (event) => {
 | 
				
			||||||
 | 
					      this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
 | 
				
			||||||
 | 
					      if (!this.elements.editingModusOn) {
 | 
				
			||||||
 | 
					        this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    document.querySelector('.ent-type-selection-action[data-ent-type="german"]').addEventListener('change', (event) => {
 | 
				
			||||||
 | 
					      this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true); 
 | 
				
			||||||
 | 
					      if (!this.elements.editingModusOn) {
 | 
				
			||||||
 | 
					        this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  resetStructuralAttrModal() {
 | 
				
			||||||
 | 
					    this.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]);
 | 
				
			||||||
 | 
					    this.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address');
 | 
				
			||||||
 | 
					    this.elements.textAnnotationInput.value = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add');
 | 
				
			||||||
 | 
					    this.toggleEditingAreaStructuralAttrModal('remove');
 | 
				
			||||||
 | 
					    this.elements.editingModusOn = false;
 | 
				
			||||||
 | 
					    this.elements.editedQueryChipElementIndex = undefined;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  actionButtonInStrucAttrModalHandler(action) {
 | 
				
			||||||
 | 
					    switch (action) {
 | 
				
			||||||
 | 
					      case 'sentence':
 | 
				
			||||||
 | 
					        this.submitQueryChipElement('start-sentence', 'Sentence Start', '<s>');
 | 
				
			||||||
 | 
					        this.submitQueryChipElement('end-sentence', 'Sentence End', '</s>', null, true);
 | 
				
			||||||
 | 
					        this.elements.structuralAttrModal.close();
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'entity':
 | 
				
			||||||
 | 
					        this.toggleClass(['entity-builder'], 'hide', 'toggle');
 | 
				
			||||||
 | 
					        this.toggleClass(['text-annotation-builder'], 'hide', 'add');
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'meta-data':
 | 
				
			||||||
 | 
					        this.toggleClass(['text-annotation-builder'], 'hide', 'toggle');
 | 
				
			||||||
 | 
					        this.toggleClass(['entity-builder'], 'hide', 'add');
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  textAnnotationSubmitHandler() {
 | 
				
			||||||
 | 
					    let noValueMetadataMessage = document.querySelector('#corpus-analysis-concordance-no-value-metadata-message');
 | 
				
			||||||
 | 
					    let textAnnotationSubmit = document.querySelector('#corpus-analysis-concordance-text-annotation-submit');
 | 
				
			||||||
 | 
					    let textAnnotationInput = document.querySelector('#corpus-analysis-concordance-text-annotation-input');
 | 
				
			||||||
 | 
					    let textAnnotationOptions = document.querySelector('#corpus-analysis-concordance-text-annotation-options');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (textAnnotationInput.value === '') {
 | 
				
			||||||
 | 
					      textAnnotationSubmit.classList.add('red');
 | 
				
			||||||
 | 
					      noValueMetadataMessage.classList.remove('hide');
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        textAnnotationSubmit.classList.remove('red');
 | 
				
			||||||
 | 
					      }, 500);
 | 
				
			||||||
 | 
					      setTimeout(() => {
 | 
				
			||||||
 | 
					        noValueMetadataMessage.classList.add('hide');
 | 
				
			||||||
 | 
					      }, 3000);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      let queryText = `:: match.text_${textAnnotationOptions.value}="${textAnnotationInput.value}"`;
 | 
				
			||||||
 | 
					      this.submitQueryChipElement('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText, null, false, true);
 | 
				
			||||||
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					class TokenAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			||||||
 | 
					  constructor(elements) {
 | 
				
			||||||
 | 
					    super(elements);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.elements.positionalAttrSelection.addEventListener('change', () => {
 | 
				
			||||||
 | 
					      this.preparePositionalAttrModal();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Options for positional attribute selection
 | 
				
			||||||
 | 
					    document.querySelectorAll('.positional-attr-options-action-button[data-options-action]').forEach(button => {
 | 
				
			||||||
 | 
					      button.addEventListener('click', () => {this.actionButtonInOptionSectionHandler(button.dataset.optionsAction);});
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.elements.tokenSubmitButton.addEventListener('click', () => {this.addTokenToQuery();});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.elements.positionalAttrModal = M.Modal.init(
 | 
				
			||||||
 | 
					      document.querySelector('#corpus-analysis-concordance-positional-attr-modal'), 
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        onOpenStart: () => {
 | 
				
			||||||
 | 
					          this.preparePositionalAttrModal();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        onCloseStart: () => {
 | 
				
			||||||
 | 
					          this.resetPositionalAttrModal();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  resetPositionalAttrModal() {
 | 
				
			||||||
 | 
					    let originalSelectionList = 
 | 
				
			||||||
 | 
					      `
 | 
				
			||||||
 | 
					        <option value="word" selected>word</option>
 | 
				
			||||||
 | 
					        <option value="lemma" >lemma</option>
 | 
				
			||||||
 | 
					        <option value="english-pos">english pos</option>
 | 
				
			||||||
 | 
					        <option value="german-pos">german pos</option>
 | 
				
			||||||
 | 
					        <option value="simple_pos">simple_pos</option>
 | 
				
			||||||
 | 
					        <option value="empty-token">empty token</option>
 | 
				
			||||||
 | 
					      `;
 | 
				
			||||||
 | 
					    this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
 | 
				
			||||||
 | 
					    this.elements.tokenQuery.innerHTML = '';
 | 
				
			||||||
 | 
					    this.elements.tokenBuilderContent.innerHTML = '';
 | 
				
			||||||
 | 
					    this.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
				
			||||||
 | 
					    this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
				
			||||||
 | 
					    this.resetMaterializeSelection([this.elements.positionalAttrSelection], "word");
 | 
				
			||||||
 | 
					    this.elements.ignoreCaseCheckbox.checked = false;
 | 
				
			||||||
 | 
					    this.elements.editingModusOn = false;
 | 
				
			||||||
 | 
					    this.elements.editedQueryChipElementIndex = undefined;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  actionButtonInOptionSectionHandler(elem) {
 | 
				
			||||||
 | 
					    let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
				
			||||||
 | 
					    switch (elem) {
 | 
				
			||||||
 | 
					      case 'option-group':
 | 
				
			||||||
 | 
					        input.value  += '(option1|option2)';
 | 
				
			||||||
 | 
					        let firstIndex = input.value.indexOf('option1');
 | 
				
			||||||
 | 
					        let lastIndex = firstIndex + 'option1'.length;
 | 
				
			||||||
 | 
					        input.focus();
 | 
				
			||||||
 | 
					        input.setSelectionRange(firstIndex, lastIndex);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'wildcard-char':
 | 
				
			||||||
 | 
					        input.value += '.';
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'and':
 | 
				
			||||||
 | 
					        this.conditionHandler('and');
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'or':
 | 
				
			||||||
 | 
					        this.conditionHandler('or');
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.optionToggleHandler();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  characterIncidenceModifierHandler(elem) {
 | 
				
			||||||
 | 
					    let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
				
			||||||
 | 
					    input.value += elem.dataset.token;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  characterNMSubmitHandler(modalId) {
 | 
				
			||||||
 | 
					    let modal = document.querySelector(`#${modalId}`);
 | 
				
			||||||
 | 
					    let input_n = modal.querySelector('.n-m-input[data-value-type="n"]').value;
 | 
				
			||||||
 | 
					    let input_m = modal.querySelector('.n-m-input[data-value-type="m"]') || undefined;
 | 
				
			||||||
 | 
					    input_m = input_m !== undefined ? ',' + input_m.value : '';
 | 
				
			||||||
 | 
					    let input = `${input_n}${input_m}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let instance = M.Modal.getInstance(modal);
 | 
				
			||||||
 | 
					    instance.close();
 | 
				
			||||||
 | 
					    let tokenInput = this.tokenInputCheck(this.elements.tokenBuilderContent);
 | 
				
			||||||
 | 
					    tokenInput.value += '{' + input + '}';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conditionHandler(conditionText) {
 | 
				
			||||||
 | 
					    let tokenQueryTemplateClone = this.elements.tokenQueryTemplate.content.cloneNode(true);
 | 
				
			||||||
 | 
					    tokenQueryTemplateClone.querySelector('.token-query-template-content').appendChild(this.elements.tokenBuilderContent.firstElementChild);
 | 
				
			||||||
 | 
					    let notSelectedButton = tokenQueryTemplateClone.querySelector(`[data-condition-pretty-text]:not([data-condition-pretty-text="${conditionText}"])`);
 | 
				
			||||||
 | 
					    let deleteButton = tokenQueryTemplateClone.querySelector(`[data-token-query-content-action="delete"]`);
 | 
				
			||||||
 | 
					    deleteButton.addEventListener('click', (event) => {
 | 
				
			||||||
 | 
					      this.deleteTokenQueryRow(event.target);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    notSelectedButton.parentNode.removeChild(notSelectedButton);
 | 
				
			||||||
 | 
					    this.elements.tokenQuery.appendChild(tokenQueryTemplateClone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Deleting the options which do not make sense in the context of the condition like "word" AND "word". Also sets selection default.
 | 
				
			||||||
 | 
					    let selectionDefault = "word";
 | 
				
			||||||
 | 
					    let optionDeleteList = ['empty-token'];
 | 
				
			||||||
 | 
					    if (conditionText === 'and') {
 | 
				
			||||||
 | 
					      switch (this.elements.positionalAttrSelection.value) {
 | 
				
			||||||
 | 
					        case 'english-pos' || 'german-pos':
 | 
				
			||||||
 | 
					          optionDeleteList.push('english-pos', 'german-pos');
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          optionDeleteList.push(this.elements.positionalAttrSelection.value);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      let originalSelectionList = 
 | 
				
			||||||
 | 
					      `
 | 
				
			||||||
 | 
					        <option value="word" selected>word</option>
 | 
				
			||||||
 | 
					        <option value="lemma" >lemma</option>
 | 
				
			||||||
 | 
					        <option value="english-pos">english pos</option>
 | 
				
			||||||
 | 
					        <option value="german-pos">german pos</option>
 | 
				
			||||||
 | 
					        <option value="simple_pos">simple_pos</option>
 | 
				
			||||||
 | 
					      `;
 | 
				
			||||||
 | 
					      this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
 | 
				
			||||||
 | 
					      M.FormSelect.init(this.elements.positionalAttrSelection);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let lastTokenQueryRow = this.elements.tokenQuery.lastElementChild;
 | 
				
			||||||
 | 
					    if(lastTokenQueryRow.querySelector('[data-kind-of-token="word"]') || lastTokenQueryRow.querySelector('[data-kind-of-token="lemma"]')) {
 | 
				
			||||||
 | 
					      this.appendIgnoreCaseCheckbox(lastTokenQueryRow.querySelector('.token-query-template-content'), this.elements.ignoreCaseCheckbox.checked);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.elements.ignoreCaseCheckbox.checked = false;
 | 
				
			||||||
 | 
					    this.setTokenSelection(selectionDefault, optionDeleteList);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deleteTokenQueryRow(deleteButton) {
 | 
				
			||||||
 | 
					    let deletedRow = deleteButton.closest('.row');
 | 
				
			||||||
 | 
					    let condition = deletedRow.querySelector('[data-condition-pretty-text]').dataset.conditionPrettyText;
 | 
				
			||||||
 | 
					    if (condition === 'and') {
 | 
				
			||||||
 | 
					      let kindOfToken = deletedRow.querySelector('[data-kind-of-token]').dataset.kindOfToken;
 | 
				
			||||||
 | 
					      switch (kindOfToken) {
 | 
				
			||||||
 | 
					        case 'english-pos' || 'german-pos':
 | 
				
			||||||
 | 
					          this.createOptionElementForPosAttrSelection('english-pos');
 | 
				
			||||||
 | 
					          this.createOptionElementForPosAttrSelection('german-pos');
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          this.createOptionElementForPosAttrSelection(kindOfToken);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      M.FormSelect.init(this.elements.positionalAttrSelection);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    deletedRow.remove();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createOptionElementForPosAttrSelection(kindOfToken) {
 | 
				
			||||||
 | 
					    let option = document.createElement('option');
 | 
				
			||||||
 | 
					    option.value = kindOfToken;
 | 
				
			||||||
 | 
					    option.text = kindOfToken;
 | 
				
			||||||
 | 
					    this.elements.positionalAttrSelection.appendChild(option);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  appendIgnoreCaseCheckbox(parentElement, checked = false) {
 | 
				
			||||||
 | 
					    let ignoreCaseCheckboxClone = document.querySelector('#ignore-case-checkbox-template').content.cloneNode(true);
 | 
				
			||||||
 | 
					    parentElement.appendChild(ignoreCaseCheckboxClone);
 | 
				
			||||||
 | 
					    M.Tooltip.init(parentElement.querySelectorAll('.tooltipped'));
 | 
				
			||||||
 | 
					    if (checked) {
 | 
				
			||||||
 | 
					      parentElement.querySelector('input[type="checkbox"]').checked = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setTokenSelection(selection, optionDeleteList) {
 | 
				
			||||||
 | 
					    optionDeleteList.forEach(option => {
 | 
				
			||||||
 | 
					      if (this.elements.positionalAttrSelection.querySelector(`option[value=${option}]`) !== null) {
 | 
				
			||||||
 | 
					        this.elements.positionalAttrSelection.querySelector(`option[value=${option}]`).remove();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.resetMaterializeSelection([this.elements.positionalAttrSelection], selection);
 | 
				
			||||||
 | 
					    this.preparePositionalAttrModal();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -105,6 +105,9 @@
 | 
				
			|||||||
  output='gen/CorpusAnalysis.%(version)s.js',
 | 
					  output='gen/CorpusAnalysis.%(version)s.js',
 | 
				
			||||||
  'js/CorpusAnalysis/query-builder/index.js',
 | 
					  'js/CorpusAnalysis/query-builder/index.js',
 | 
				
			||||||
  'js/CorpusAnalysis/query-builder/element-references.js',
 | 
					  'js/CorpusAnalysis/query-builder/element-references.js',
 | 
				
			||||||
 | 
					  'js/CorpusAnalysis/query-builder/general-query-builder-functions.js',
 | 
				
			||||||
 | 
					  'js/CorpusAnalysis/query-builder/structural-attribute-builder-functions.js',
 | 
				
			||||||
 | 
					  'js/CorpusAnalysis/query-builder/token-attribute-builder-functions.js',
 | 
				
			||||||
  'js/CorpusAnalysis/CorpusAnalysisApp.js',
 | 
					  'js/CorpusAnalysis/CorpusAnalysisApp.js',
 | 
				
			||||||
  'js/CorpusAnalysis/CorpusAnalysisConcordance.js',
 | 
					  'js/CorpusAnalysis/CorpusAnalysisConcordance.js',
 | 
				
			||||||
  'js/CorpusAnalysis/CorpusAnalysisReader.js',
 | 
					  'js/CorpusAnalysis/CorpusAnalysisReader.js',
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user