mirror of
				https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
				synced 2025-11-04 04:12:45 +00:00 
			
		
		
		
	QB parts as extensions
This commit is contained in:
		@@ -1,6 +1,29 @@
 | 
				
			|||||||
class GeneralQueryBuilderFunctions {
 | 
					class GeneralQueryBuilderFunctions {
 | 
				
			||||||
  constructor(elements) {
 | 
					  name = 'General Query Builder Functions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(app, elements) {
 | 
				
			||||||
 | 
					    this.app = app;
 | 
				
			||||||
    this.elements = elements;
 | 
					    this.elements = elements;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    this.incidenceModifierEventListeners();
 | 
				
			||||||
 | 
					    this.nAndMInputSubmitEventListeners();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let queryBuilderDisplay = document.querySelector("#corpus-analysis-concordance-query-builder-display");
 | 
				
			||||||
 | 
					    let expertModeDisplay = document.querySelector("#corpus-analysis-concordance-expert-mode-display");
 | 
				
			||||||
 | 
					    let expertModeSwitch = document.querySelector("#corpus-analysis-concordance-expert-mode-switch");
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    expertModeSwitch.addEventListener("change", () => {
 | 
				
			||||||
 | 
					      const isChecked = expertModeSwitch.checked;
 | 
				
			||||||
 | 
					      if (isChecked) {
 | 
				
			||||||
 | 
					        queryBuilderDisplay.classList.add("hide");
 | 
				
			||||||
 | 
					        expertModeDisplay.classList.remove("hide");
 | 
				
			||||||
 | 
					        this.switchToExpertModeParser();
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        queryBuilderDisplay.classList.remove("hide");
 | 
				
			||||||
 | 
					        expertModeDisplay.classList.add("hide");
 | 
				
			||||||
 | 
					        this.switchToQueryBuilderParser();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  toggleClass(elements, className, action){
 | 
					  toggleClass(elements, className, action){
 | 
				
			||||||
@@ -124,106 +147,20 @@ class GeneralQueryBuilderFunctions {
 | 
				
			|||||||
    this.elements.editedQueryChipElementIndex = Array.from(this.elements.queryInputField.children).indexOf(queryChipElement);
 | 
					    this.elements.editedQueryChipElementIndex = Array.from(this.elements.queryInputField.children).indexOf(queryChipElement);
 | 
				
			||||||
    switch (queryChipElement.dataset.type) {
 | 
					    switch (queryChipElement.dataset.type) {
 | 
				
			||||||
      case 'start-entity':
 | 
					      case 'start-entity':
 | 
				
			||||||
        this.editStartEntityChipElement(queryChipElement);
 | 
					        this.app.extensions.structuralAttributeBuilderFunctions.editStartEntityChipElement(queryChipElement);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'text-annotation':
 | 
					      case 'text-annotation':
 | 
				
			||||||
        this.editTextAnnotationChipElement(queryChipElement);
 | 
					        this.app.extensions.structuralAttributeBuilderFunctions.editTextAnnotationChipElement(queryChipElement);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'token':
 | 
					      case 'token':
 | 
				
			||||||
        let queryElementsContent = this.prepareQueryElementsContent(queryChipElement);
 | 
					        let queryElementsContent = this.app.extensions.tokenAttributeBuilderFunctions.prepareTokenQueryElementsContent(queryChipElement);
 | 
				
			||||||
        this.editTokenChipElement(queryElementsContent);
 | 
					        this.app.extensions.tokenAttributeBuilderFunctions.editTokenChipElement(queryElementsContent);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break;
 | 
					        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) {
 | 
					  lockClosingChipElement(queryChipElement) {
 | 
				
			||||||
    queryChipElement.dataset.closingTag = 'false';
 | 
					    queryChipElement.dataset.closingTag = 'false';
 | 
				
			||||||
    let lockIcon = queryChipElement.querySelector('[data-chip-action="lock"]');
 | 
					    let lockIcon = queryChipElement.querySelector('[data-chip-action="lock"]');
 | 
				
			||||||
@@ -385,111 +322,165 @@ class GeneralQueryBuilderFunctions {
 | 
				
			|||||||
    this.tokenIncidenceModifierHandler(input, pretty_input);
 | 
					    this.tokenIncidenceModifierHandler(input, pretty_input);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //#region Functions from other classes
 | 
					  incidenceModifierEventListeners() {
 | 
				
			||||||
 | 
					    // Eventlisteners for the incidence modifiers. There are two different types of incidence modifiers: token and character incidence modifiers.
 | 
				
			||||||
  //TODO: Move these functions back to their og classes and make it work.
 | 
					    document.querySelectorAll('.incidence-modifier-selection').forEach(button => {
 | 
				
			||||||
 | 
					      let dropdownId = button.parentNode.parentNode.id;
 | 
				
			||||||
  toggleEditingAreaStructuralAttrModal(action) {
 | 
					      if (dropdownId === 'corpus-analysis-concordance-token-incidence-modifiers-dropdown') {
 | 
				
			||||||
    // If the user edits a query chip element, the corresponding editing area is displayed and the other areas are hidden or disabled.
 | 
					        button.addEventListener('click', () => this.tokenIncidenceModifierHandler(button.dataset.token, button.innerHTML));
 | 
				
			||||||
    this.toggleClass(['sentence-button', 'entity-button', 'text-annotation-button', 'any-type-entity-button'], 'disabled', action);
 | 
					      } else if (dropdownId === 'corpus-analysis-concordance-character-incidence-modifiers-dropdown') {
 | 
				
			||||||
 | 
					        button.addEventListener('click', () => this.characterIncidenceModifierHandler(button));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  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 === '') {
 | 
					  nAndMInputSubmitEventListeners() {
 | 
				
			||||||
      this.disableTokenSubmit();
 | 
					    // Eventlisteners for the submit of n- and m-values of the incidence modifier modal for "exactly n" or "between n and m".
 | 
				
			||||||
    } else {
 | 
					    document.querySelectorAll('.n-m-submit-button').forEach(button => {
 | 
				
			||||||
      tokenQueryCQLText = `[${tokenQueryCQLText}]`;
 | 
					      let modalId = button.dataset.modalId;
 | 
				
			||||||
      this.submitQueryChipElement('token', tokenQueryPrettyText, tokenQueryCQLText, null, false, kindOfToken === 'empty-token' ? false : true);
 | 
					      if (modalId === 'corpus-analysis-concordance-exactly-n-token-modal' || modalId === 'corpus-analysis-concordance-between-nm-token-modal') {
 | 
				
			||||||
      this.elements.positionalAttrModal.close();
 | 
					        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.app.extensions.tokenAttributeBuilderFunctions.characterNMSubmitHandler(modalId));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switchToExpertModeParser() {
 | 
				
			||||||
 | 
					    let expertModeInputField = document.querySelector('#corpus-analysis-concordance-form-query');
 | 
				
			||||||
 | 
					    expertModeInputField.value = '';
 | 
				
			||||||
 | 
					    let queryBuilderInputFieldValue = Utils.unescape(document.querySelector('#corpus-analysis-concordance-query-preview').innerHTML.trim());
 | 
				
			||||||
 | 
					    if (queryBuilderInputFieldValue !== "" && queryBuilderInputFieldValue !== ";") {
 | 
				
			||||||
 | 
					      expertModeInputField.value = queryBuilderInputFieldValue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  kindOfTokenCheck(kindOfToken) {
 | 
					  switchToQueryBuilderParser() {
 | 
				
			||||||
    return kindOfToken === 'english-pos' || kindOfToken === 'german-pos' ? 'pos' : kindOfToken;
 | 
					    this.resetQueryInputField();
 | 
				
			||||||
 | 
					    let expertModeInputFieldValue = document.querySelector('#corpus-analysis-concordance-form-query').value;
 | 
				
			||||||
 | 
					    let chipElements = this.parseTextToChip(expertModeInputFieldValue);
 | 
				
			||||||
 | 
					    let closingTagElements = ['end-sentence', 'end-entity'];
 | 
				
			||||||
 | 
					    let editableElements = ['start-entity', 'text-annotation', 'token'];
 | 
				
			||||||
 | 
					    for (let chipElement of chipElements) {
 | 
				
			||||||
 | 
					      let isClosingTag = closingTagElements.includes(chipElement['type']);
 | 
				
			||||||
 | 
					      let isEditable = editableElements.includes(chipElement['type']);
 | 
				
			||||||
 | 
					      if (chipElement['query'] === '[]'){
 | 
				
			||||||
 | 
					        isEditable = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query'], null, isClosingTag, isEditable);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  disableTokenSubmit() {
 | 
					  parseTextToChip(query) {
 | 
				
			||||||
    this.elements.tokenSubmitButton.classList.add('red');
 | 
					    const parsingElementDict = {
 | 
				
			||||||
    this.elements.noValueMessage.classList.remove('hide');
 | 
					      '<s>': {
 | 
				
			||||||
    setTimeout(() => {
 | 
					        pretty: 'Sentence Start',
 | 
				
			||||||
      this.elements.tokenSubmitButton.classList.remove('red');
 | 
					        type: 'start-sentence'
 | 
				
			||||||
    }, 500);
 | 
					      },
 | 
				
			||||||
    setTimeout(() => {
 | 
					      '<\/s>': {
 | 
				
			||||||
      this.elements.noValueMessage.classList.add('hide');
 | 
					        pretty: 'Sentence End',
 | 
				
			||||||
    }, 3000);
 | 
					        type: 'end-sentence'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '<ent>': {
 | 
				
			||||||
 | 
					        pretty: 'Entity Start',
 | 
				
			||||||
 | 
					        type: 'start-empty-entity'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '<ent_type="([A-Z]+)">': {
 | 
				
			||||||
 | 
					        pretty: '',
 | 
				
			||||||
 | 
					        type: 'start-entity'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '<\\\/ent(_type)?>': {
 | 
				
			||||||
 | 
					        pretty: 'Entity End',
 | 
				
			||||||
 | 
					        type: 'end-entity'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      ':: ?match\\.text_[A-Za-z]+="[^"]+"': {
 | 
				
			||||||
 | 
					        pretty: '',
 | 
				
			||||||
 | 
					        type: 'text-annotation'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '\\[(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?((\\&|\\|) ?(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?)*\\]': {
 | 
				
			||||||
 | 
					        pretty: '',
 | 
				
			||||||
 | 
					        type: 'token'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '\\[\\]': {
 | 
				
			||||||
 | 
					        pretty: 'Empty Token',
 | 
				
			||||||
 | 
					        type: 'token'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '(?<!\\[) ?\\+ ?(?![^\\]]\\])': {
 | 
				
			||||||
 | 
					        pretty: ' one or more (+)',
 | 
				
			||||||
 | 
					        type: 'token-incidence-modifier'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '(?<!\\[) ?\\* ?(?![^\\]]\\])': {
 | 
				
			||||||
 | 
					        pretty: 'zero or more (*)',
 | 
				
			||||||
 | 
					        type: 'token-incidence-modifier'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '(?<!\\[) ?\\? ?(?![^\\]]\\])': {
 | 
				
			||||||
 | 
					        pretty: 'zero or one (?)',
 | 
				
			||||||
 | 
					        type: 'token-incidence-modifier'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '(?<!\\[) ?\\{[0-9]+} ?(?![^\\]]\\])': {
 | 
				
			||||||
 | 
					        pretty: '',
 | 
				
			||||||
 | 
					        type: 'token-incidence-modifier'
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      '(?<!\\[) ?\\{[0-9]+(,[0-9]+)?} ?(?![^\\]]\\])': {
 | 
				
			||||||
 | 
					        pretty: '',
 | 
				
			||||||
 | 
					        type: 'token-incidence-modifier'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  //#endregion Functions from other classes
 | 
					    let chipElements = [];
 | 
				
			||||||
 | 
					    let regexPattern = Object.keys(parsingElementDict).map(pattern => `(${pattern})`).join('|');
 | 
				
			||||||
 | 
					    const regex = new RegExp(regexPattern, 'gi');
 | 
				
			||||||
 | 
					    let match;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					    while ((match = regex.exec(query)) !== null) {
 | 
				
			||||||
 | 
					      // this is necessary to avoid infinite loops with zero-width matches
 | 
				
			||||||
 | 
					      if (match.index === regex.lastIndex) {
 | 
				
			||||||
 | 
					        regex.lastIndex++;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let stringElement = match[0];
 | 
				
			||||||
 | 
					      for (let [pattern, chipElement] of Object.entries(parsingElementDict)) {
 | 
				
			||||||
 | 
					        const parsingRegex = new RegExp(pattern, 'gi');
 | 
				
			||||||
 | 
					        if (parsingRegex.exec(stringElement)) {
 | 
				
			||||||
 | 
					          // Creating the pretty text for the chip element
 | 
				
			||||||
 | 
					          let prettyText;
 | 
				
			||||||
 | 
					          switch (pattern) {
 | 
				
			||||||
 | 
					            case '<ent_type="([A-Z]+)">':
 | 
				
			||||||
 | 
					              prettyText = `Entity Type=${stringElement.replace(/<ent_type="|">/g, '')}`;
 | 
				
			||||||
 | 
					              break;
 | 
				
			||||||
 | 
					            case ':: ?match\\.text_[A-Za-z]+="[^"]+"':
 | 
				
			||||||
 | 
					              prettyText = stringElement.replace(/:: ?match\.text_|"|"/g, '');
 | 
				
			||||||
 | 
					              break;
 | 
				
			||||||
 | 
					            case '\\[(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?((\\&|\\|) ?(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?)*\\]':
 | 
				
			||||||
 | 
					              let doubleQuotes = /(word|lemma|pos|simple_pos)="[^"]+"/gi;
 | 
				
			||||||
 | 
					              let singleQuotes = /(word|lemma|pos|simple_pos)='[^']+'/gi;
 | 
				
			||||||
 | 
					              if (doubleQuotes.exec(stringElement)) {
 | 
				
			||||||
 | 
					                prettyText = stringElement.replace(/^\[|\]$|"/g, '');
 | 
				
			||||||
 | 
					              } else if (singleQuotes.exec(stringElement)) {
 | 
				
			||||||
 | 
					                prettyText = stringElement.replace(/^\[|\]$|'/g, '');
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              prettyText = prettyText.replace(/\&/g, ' and ').replace(/\|/g, ' or ');
 | 
				
			||||||
 | 
					              break;
 | 
				
			||||||
 | 
					            case '(?<!\\[) ?\\{[0-9]+} ?(?![^\\]]\\])':
 | 
				
			||||||
 | 
					              prettyText = `exactly ${stringElement.replace(/{|}/g, '')} (${stringElement})`;
 | 
				
			||||||
 | 
					              break;
 | 
				
			||||||
 | 
					            case '(?<!\\[) ?\\{[0-9]+(,[0-9]+)?} ?(?![^\\]]\\])':
 | 
				
			||||||
 | 
					              prettyText = `between${stringElement.replace(/{|}/g, ' ').replace(',', ' and ')}(${stringElement})`;
 | 
				
			||||||
 | 
					              break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					              prettyText = chipElement.pretty;
 | 
				
			||||||
 | 
					              break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          chipElements.push({
 | 
				
			||||||
 | 
					            type: chipElement.type,
 | 
				
			||||||
 | 
					            pretty: prettyText,
 | 
				
			||||||
 | 
					            query: stringElement
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					    return chipElements;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,192 +1,15 @@
 | 
				
			|||||||
class ConcordanceQueryBuilder {
 | 
					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);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.incidenceModifierEventListeners();
 | 
					    this.extensions = {
 | 
				
			||||||
    this.nAndMInputSubmitEventListeners();
 | 
					      generalFunctions: new GeneralQueryBuilderFunctions(this, this.elements),
 | 
				
			||||||
 | 
					      structuralAttributeBuilderFunctions: new StructuralAttributeBuilderFunctions(this, this.elements),
 | 
				
			||||||
 | 
					      tokenAttributeBuilderFunctions: new TokenAttributeBuilderFunctions(this, this.elements),
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    let queryBuilderDisplay = document.querySelector("#corpus-analysis-concordance-query-builder-display");
 | 
					 | 
				
			||||||
    let expertModeDisplay = document.querySelector("#corpus-analysis-concordance-expert-mode-display");
 | 
					 | 
				
			||||||
    let expertModeSwitch = document.querySelector("#corpus-analysis-concordance-expert-mode-switch");
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    expertModeSwitch.addEventListener("change", () => {
 | 
					 | 
				
			||||||
      const isChecked = expertModeSwitch.checked;
 | 
					 | 
				
			||||||
      if (isChecked) {
 | 
					 | 
				
			||||||
        queryBuilderDisplay.classList.add("hide");
 | 
					 | 
				
			||||||
        expertModeDisplay.classList.remove("hide");
 | 
					 | 
				
			||||||
        this.switchToExpertModeParser();
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        queryBuilderDisplay.classList.remove("hide");
 | 
					 | 
				
			||||||
        expertModeDisplay.classList.add("hide");
 | 
					 | 
				
			||||||
        this.switchToQueryBuilderParser();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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() {
 | 
					 | 
				
			||||||
    let expertModeInputField = document.querySelector('#corpus-analysis-concordance-form-query');
 | 
					 | 
				
			||||||
    expertModeInputField.value = '';
 | 
					 | 
				
			||||||
    let queryBuilderInputFieldValue = Utils.unescape(document.querySelector('#corpus-analysis-concordance-query-preview').innerHTML.trim());
 | 
					 | 
				
			||||||
    if (queryBuilderInputFieldValue !== "" && queryBuilderInputFieldValue !== ";") {
 | 
					 | 
				
			||||||
      expertModeInputField.value = queryBuilderInputFieldValue;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  switchToQueryBuilderParser() {
 | 
					 | 
				
			||||||
    this.generalFunctions.resetQueryInputField();
 | 
					 | 
				
			||||||
    let expertModeInputFieldValue = document.querySelector('#corpus-analysis-concordance-form-query').value;
 | 
					 | 
				
			||||||
    let chipElements = this.parseTextToChip(expertModeInputFieldValue);
 | 
					 | 
				
			||||||
    let closingTagElements = ['end-sentence', 'end-entity'];
 | 
					 | 
				
			||||||
    let editableElements = ['start-entity', 'text-annotation', 'token'];
 | 
					 | 
				
			||||||
    for (let chipElement of chipElements) {
 | 
					 | 
				
			||||||
      let isClosingTag = closingTagElements.includes(chipElement['type']);
 | 
					 | 
				
			||||||
      let isEditable = editableElements.includes(chipElement['type']);
 | 
					 | 
				
			||||||
      if (chipElement['query'] === '[]'){
 | 
					 | 
				
			||||||
        isEditable = false;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      this.generalFunctions.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query'], null, isClosingTag, isEditable);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  parseTextToChip(query) {
 | 
					 | 
				
			||||||
    const parsingElementDict = {
 | 
					 | 
				
			||||||
      '<s>': {
 | 
					 | 
				
			||||||
        pretty: 'Sentence Start',
 | 
					 | 
				
			||||||
        type: 'start-sentence'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '<\/s>': {
 | 
					 | 
				
			||||||
        pretty: 'Sentence End',
 | 
					 | 
				
			||||||
        type: 'end-sentence'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '<ent>': {
 | 
					 | 
				
			||||||
        pretty: 'Entity Start',
 | 
					 | 
				
			||||||
        type: 'start-empty-entity'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '<ent_type="([A-Z]+)">': {
 | 
					 | 
				
			||||||
        pretty: '',
 | 
					 | 
				
			||||||
        type: 'start-entity'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '<\\\/ent(_type)?>': {
 | 
					 | 
				
			||||||
        pretty: 'Entity End',
 | 
					 | 
				
			||||||
        type: 'end-entity'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      ':: ?match\\.text_[A-Za-z]+="[^"]+"': {
 | 
					 | 
				
			||||||
        pretty: '',
 | 
					 | 
				
			||||||
        type: 'text-annotation'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '\\[(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?((\\&|\\|) ?(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?)*\\]': {
 | 
					 | 
				
			||||||
        pretty: '',
 | 
					 | 
				
			||||||
        type: 'token'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '\\[\\]': {
 | 
					 | 
				
			||||||
        pretty: 'Empty Token',
 | 
					 | 
				
			||||||
        type: 'token'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '(?<!\\[) ?\\+ ?(?![^\\]]\\])': {
 | 
					 | 
				
			||||||
        pretty: ' one or more (+)',
 | 
					 | 
				
			||||||
        type: 'token-incidence-modifier'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '(?<!\\[) ?\\* ?(?![^\\]]\\])': {
 | 
					 | 
				
			||||||
        pretty: 'zero or more (*)',
 | 
					 | 
				
			||||||
        type: 'token-incidence-modifier'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '(?<!\\[) ?\\? ?(?![^\\]]\\])': {
 | 
					 | 
				
			||||||
        pretty: 'zero or one (?)',
 | 
					 | 
				
			||||||
        type: 'token-incidence-modifier'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '(?<!\\[) ?\\{[0-9]+} ?(?![^\\]]\\])': {
 | 
					 | 
				
			||||||
        pretty: '',
 | 
					 | 
				
			||||||
        type: 'token-incidence-modifier'
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      '(?<!\\[) ?\\{[0-9]+(,[0-9]+)?} ?(?![^\\]]\\])': {
 | 
					 | 
				
			||||||
        pretty: '',
 | 
					 | 
				
			||||||
        type: 'token-incidence-modifier'
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
    let chipElements = [];
 | 
					 | 
				
			||||||
    let regexPattern = Object.keys(parsingElementDict).map(pattern => `(${pattern})`).join('|');
 | 
					 | 
				
			||||||
    const regex = new RegExp(regexPattern, 'gi');
 | 
					 | 
				
			||||||
    let match;
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
    while ((match = regex.exec(query)) !== null) {
 | 
					 | 
				
			||||||
      // this is necessary to avoid infinite loops with zero-width matches
 | 
					 | 
				
			||||||
      if (match.index === regex.lastIndex) {
 | 
					 | 
				
			||||||
        regex.lastIndex++;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      let stringElement = match[0];
 | 
					 | 
				
			||||||
      for (let [pattern, chipElement] of Object.entries(parsingElementDict)) {
 | 
					 | 
				
			||||||
        const parsingRegex = new RegExp(pattern, 'gi');
 | 
					 | 
				
			||||||
        if (parsingRegex.exec(stringElement)) {
 | 
					 | 
				
			||||||
          // Creating the pretty text for the chip element
 | 
					 | 
				
			||||||
          let prettyText;
 | 
					 | 
				
			||||||
          switch (pattern) {
 | 
					 | 
				
			||||||
            case '<ent_type="([A-Z]+)">':
 | 
					 | 
				
			||||||
              prettyText = `Entity Type=${stringElement.replace(/<ent_type="|">/g, '')}`;
 | 
					 | 
				
			||||||
              break;
 | 
					 | 
				
			||||||
            case ':: ?match\\.text_[A-Za-z]+="[^"]+"':
 | 
					 | 
				
			||||||
              prettyText = stringElement.replace(/:: ?match\.text_|"|"/g, '');
 | 
					 | 
				
			||||||
              break;
 | 
					 | 
				
			||||||
            case '\\[(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?((\\&|\\|) ?(word|lemma|pos|simple_pos)=(("[^"]+")|(\\u0027[^\\u0027]+\\u0027)) ?(%c)? ?)*\\]':
 | 
					 | 
				
			||||||
              let doubleQuotes = /(word|lemma|pos|simple_pos)="[^"]+"/gi;
 | 
					 | 
				
			||||||
              let singleQuotes = /(word|lemma|pos|simple_pos)='[^']+'/gi;
 | 
					 | 
				
			||||||
              if (doubleQuotes.exec(stringElement)) {
 | 
					 | 
				
			||||||
                prettyText = stringElement.replace(/^\[|\]$|"/g, '');
 | 
					 | 
				
			||||||
              } else if (singleQuotes.exec(stringElement)) {
 | 
					 | 
				
			||||||
                prettyText = stringElement.replace(/^\[|\]$|'/g, '');
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
              prettyText = prettyText.replace(/\&/g, ' and ').replace(/\|/g, ' or ');
 | 
					 | 
				
			||||||
              break;
 | 
					 | 
				
			||||||
            case '(?<!\\[) ?\\{[0-9]+} ?(?![^\\]]\\])':
 | 
					 | 
				
			||||||
              prettyText = `exactly ${stringElement.replace(/{|}/g, '')} (${stringElement})`;
 | 
					 | 
				
			||||||
              break;
 | 
					 | 
				
			||||||
            case '(?<!\\[) ?\\{[0-9]+(,[0-9]+)?} ?(?![^\\]]\\])':
 | 
					 | 
				
			||||||
              prettyText = `between${stringElement.replace(/{|}/g, ' ').replace(',', ' and ')}(${stringElement})`;
 | 
					 | 
				
			||||||
              break;
 | 
					 | 
				
			||||||
            default:
 | 
					 | 
				
			||||||
              prettyText = chipElement.pretty;
 | 
					 | 
				
			||||||
              break;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          chipElements.push({
 | 
					 | 
				
			||||||
            type: chipElement.type,
 | 
					 | 
				
			||||||
            pretty: prettyText,
 | 
					 | 
				
			||||||
            query: stringElement
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          break;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
    return chipElements;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
class StructuralAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
					class StructuralAttributeBuilderFunctions {
 | 
				
			||||||
  constructor(elements) {
 | 
					  name = 'Token Attribute Builder Functions';
 | 
				
			||||||
    super(elements);
 | 
					
 | 
				
			||||||
 | 
					  constructor(app, elements) {
 | 
				
			||||||
 | 
					    this.app = app;
 | 
				
			||||||
 | 
					    this.elements = elements;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    this.structuralAttrModalEventlisteners();
 | 
					    this.structuralAttrModalEventlisteners();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,32 +26,32 @@ class StructuralAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    document.querySelector('.ent-type-selection-action[data-ent-type="any"]').addEventListener('click', () => {
 | 
					    document.querySelector('.ent-type-selection-action[data-ent-type="any"]').addEventListener('click', () => {
 | 
				
			||||||
      this.submitQueryChipElement('start-empty-entity', 'Entity Start', '<ent>');
 | 
					      this.app.extensions.generalFunctions.submitQueryChipElement('start-empty-entity', 'Entity Start', '<ent>');
 | 
				
			||||||
      this.submitQueryChipElement('end-entity', 'Entity End', '</ent>', null, true);
 | 
					      this.app.extensions.generalFunctions.submitQueryChipElement('end-entity', 'Entity End', '</ent>', null, true);
 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    document.querySelector('.ent-type-selection-action[data-ent-type="english"]').addEventListener('change', (event) => {
 | 
					    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);
 | 
					      this.app.extensions.generalFunctions.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
 | 
				
			||||||
      if (!this.elements.editingModusOn) {
 | 
					      if (!this.elements.editingModusOn) {
 | 
				
			||||||
        this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
					        this.app.extensions.generalFunctions.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    document.querySelector('.ent-type-selection-action[data-ent-type="german"]').addEventListener('change', (event) => {
 | 
					    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); 
 | 
					      this.app.extensions.generalFunctions.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true); 
 | 
				
			||||||
      if (!this.elements.editingModusOn) {
 | 
					      if (!this.elements.editingModusOn) {
 | 
				
			||||||
        this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
					        this.app.extensions.generalFunctions.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  resetStructuralAttrModal() {
 | 
					  resetStructuralAttrModal() {
 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]);
 | 
					    this.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]);
 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address');
 | 
					    this.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address');
 | 
				
			||||||
    this.elements.textAnnotationInput.value = '';
 | 
					    this.elements.textAnnotationInput.value = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add');
 | 
					    this.app.extensions.generalFunctions.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add');
 | 
				
			||||||
    this.toggleEditingAreaStructuralAttrModal('remove');
 | 
					    this.toggleEditingAreaStructuralAttrModal('remove');
 | 
				
			||||||
    this.elements.editingModusOn = false;
 | 
					    this.elements.editingModusOn = false;
 | 
				
			||||||
    this.elements.editedQueryChipElementIndex = undefined;
 | 
					    this.elements.editedQueryChipElementIndex = undefined;
 | 
				
			||||||
@@ -57,23 +60,28 @@ class StructuralAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			|||||||
  actionButtonInStrucAttrModalHandler(action) {
 | 
					  actionButtonInStrucAttrModalHandler(action) {
 | 
				
			||||||
    switch (action) {
 | 
					    switch (action) {
 | 
				
			||||||
      case 'sentence':
 | 
					      case 'sentence':
 | 
				
			||||||
        this.submitQueryChipElement('start-sentence', 'Sentence Start', '<s>');
 | 
					        this.app.extensions.generalFunctions.submitQueryChipElement('start-sentence', 'Sentence Start', '<s>');
 | 
				
			||||||
        this.submitQueryChipElement('end-sentence', 'Sentence End', '</s>', null, true);
 | 
					        this.app.extensions.generalFunctions.submitQueryChipElement('end-sentence', 'Sentence End', '</s>', null, true);
 | 
				
			||||||
        this.elements.structuralAttrModal.close();
 | 
					        this.elements.structuralAttrModal.close();
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'entity':
 | 
					      case 'entity':
 | 
				
			||||||
        this.toggleClass(['entity-builder'], 'hide', 'toggle');
 | 
					        this.app.extensions.generalFunctions.toggleClass(['entity-builder'], 'hide', 'toggle');
 | 
				
			||||||
        this.toggleClass(['text-annotation-builder'], 'hide', 'add');
 | 
					        this.app.extensions.generalFunctions.toggleClass(['text-annotation-builder'], 'hide', 'add');
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      case 'meta-data':
 | 
					      case 'meta-data':
 | 
				
			||||||
        this.toggleClass(['text-annotation-builder'], 'hide', 'toggle');
 | 
					        this.app.extensions.generalFunctions.toggleClass(['text-annotation-builder'], 'hide', 'toggle');
 | 
				
			||||||
        this.toggleClass(['entity-builder'], 'hide', 'add');
 | 
					        this.app.extensions.generalFunctions.toggleClass(['entity-builder'], 'hide', 'add');
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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.app.extensions.generalFunctions.toggleClass(['sentence-button', 'entity-button', 'text-annotation-button', 'any-type-entity-button'], 'disabled', action);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  textAnnotationSubmitHandler() {
 | 
					  textAnnotationSubmitHandler() {
 | 
				
			||||||
    let noValueMetadataMessage = document.querySelector('#corpus-analysis-concordance-no-value-metadata-message');
 | 
					    let noValueMetadataMessage = document.querySelector('#corpus-analysis-concordance-no-value-metadata-message');
 | 
				
			||||||
    let textAnnotationSubmit = document.querySelector('#corpus-analysis-concordance-text-annotation-submit');
 | 
					    let textAnnotationSubmit = document.querySelector('#corpus-analysis-concordance-text-annotation-submit');
 | 
				
			||||||
@@ -91,8 +99,29 @@ class StructuralAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			|||||||
      }, 3000);
 | 
					      }, 3000);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      let queryText = `:: match.text_${textAnnotationOptions.value}="${textAnnotationInput.value}"`;
 | 
					      let queryText = `:: match.text_${textAnnotationOptions.value}="${textAnnotationInput.value}"`;
 | 
				
			||||||
      this.submitQueryChipElement('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText, null, false, true);
 | 
					      this.app.extensions.generalFunctions.submitQueryChipElement('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText, null, false, true);
 | 
				
			||||||
      this.elements.structuralAttrModal.close();
 | 
					      this.elements.structuralAttrModal.close();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editStartEntityChipElement(queryChipElement) {
 | 
				
			||||||
 | 
					    this.elements.structuralAttrModal.open();
 | 
				
			||||||
 | 
					    this.app.extensions.generalFunctions.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.app.extensions.generalFunctions.resetMaterializeSelection([selection], entType);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editTextAnnotationChipElement(queryChipElement) {
 | 
				
			||||||
 | 
					    this.elements.structuralAttrModal.open();
 | 
				
			||||||
 | 
					    this.app.extensions.generalFunctions.toggleClass(['text-annotation-builder'], 'hide', 'remove');
 | 
				
			||||||
 | 
					    this.toggleEditingAreaStructuralAttrModal('add');
 | 
				
			||||||
 | 
					    let [textAnnotationSelection, textAnnotationContent] = queryChipElement.dataset.query
 | 
				
			||||||
 | 
					      .replace(/:: ?match\.text_|"|"/g, '')
 | 
				
			||||||
 | 
					      .split('=');
 | 
				
			||||||
 | 
					    this.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.textAnnotationSelection], textAnnotationSelection);
 | 
				
			||||||
 | 
					    this.elements.textAnnotationInput.value = textAnnotationContent;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
class TokenAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
					class TokenAttributeBuilderFunctions {
 | 
				
			||||||
  constructor(elements) {
 | 
					  name = 'Token Attribute Builder Functions';
 | 
				
			||||||
    super(elements);
 | 
					
 | 
				
			||||||
 | 
					  constructor(app, elements) {
 | 
				
			||||||
 | 
					    this.app = app;
 | 
				
			||||||
 | 
					    this.elements = elements;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.elements.positionalAttrSelection.addEventListener('change', () => {
 | 
					    this.elements.positionalAttrSelection.addEventListener('change', () => {
 | 
				
			||||||
      this.preparePositionalAttrModal();
 | 
					      this.preparePositionalAttrModal();
 | 
				
			||||||
@@ -39,9 +42,9 @@ class TokenAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			|||||||
    this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
 | 
					    this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
 | 
				
			||||||
    this.elements.tokenQuery.innerHTML = '';
 | 
					    this.elements.tokenQuery.innerHTML = '';
 | 
				
			||||||
    this.elements.tokenBuilderContent.innerHTML = '';
 | 
					    this.elements.tokenBuilderContent.innerHTML = '';
 | 
				
			||||||
    this.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
					    this.app.extensions.generalFunctions.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
				
			||||||
    this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
					    this.app.extensions.generalFunctions.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.positionalAttrSelection], "word");
 | 
					    this.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.positionalAttrSelection], "word");
 | 
				
			||||||
    this.elements.ignoreCaseCheckbox.checked = false;
 | 
					    this.elements.ignoreCaseCheckbox.checked = false;
 | 
				
			||||||
    this.elements.editingModusOn = false;
 | 
					    this.elements.editingModusOn = false;
 | 
				
			||||||
    this.elements.editedQueryChipElementIndex = undefined;
 | 
					    this.elements.editedQueryChipElementIndex = undefined;
 | 
				
			||||||
@@ -175,8 +178,168 @@ class TokenAttributeBuilderFunctions extends GeneralQueryBuilderFunctions {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.resetMaterializeSelection([this.elements.positionalAttrSelection], selection);
 | 
					    this.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.positionalAttrSelection], selection);
 | 
				
			||||||
    this.preparePositionalAttrModal();
 | 
					    this.preparePositionalAttrModal();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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.app.extensions.generalFunctions.toggleClass(['input-field-options'], 'hide', 'remove');
 | 
				
			||||||
 | 
					    } else if (selection === 'empty-token'){
 | 
				
			||||||
 | 
					      this.addTokenToQuery();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.app.extensions.generalFunctions.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.app.extensions.generalFunctions.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
 | 
				
			||||||
 | 
					    } else if (this.elements.positionalAttrSelection.querySelectorAll('option').length === 1) {
 | 
				
			||||||
 | 
					      this.app.extensions.generalFunctions.toggleClass(['and'], 'disabled', 'add');
 | 
				
			||||||
 | 
					      this.app.extensions.generalFunctions.toggleClass(['or'], 'disabled', 'remove');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.app.extensions.generalFunctions.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.app.extensions.generalFunctions.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);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  editTokenChipElement(queryElementsContent) {
 | 
				
			||||||
 | 
					    this.elements.positionalAttrModal.open();
 | 
				
			||||||
 | 
					    queryElementsContent.forEach((queryElement) => {
 | 
				
			||||||
 | 
					      this.app.extensions.generalFunctions.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.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.positionalAttrSelection], queryElement.tokenAttr);
 | 
				
			||||||
 | 
					          this.preparePositionalAttrModal();
 | 
				
			||||||
 | 
					          this.app.extensions.generalFunctions.resetMaterializeSelection([this.elements.tokenBuilderContent.querySelector('select')], queryElement.tokenValue);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case 'simple_pos':
 | 
				
			||||||
 | 
					          this.app.extensions.generalFunctions.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);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  prepareTokenQueryElementsContent(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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user