diff --git a/app/static/js/CorpusAnalysis/QueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder.js
deleted file mode 100644
index b37cdf14..00000000
--- a/app/static/js/CorpusAnalysis/QueryBuilder.js
+++ /dev/null
@@ -1,52 +0,0 @@
-class ConcordanceQueryBuilder {
-
- constructor() {
-
- this.elements = new ElementReferencesQueryBuilder();
- this.generalFunctions = new GeneralFunctionsQueryBuilder(this.elements);
- this.tokenAttributeBuilderFunctions = new TokenAttributeBuilderFunctionsQueryBuilder(this.elements);
- this.structuralAttributeBuilderFunctions = new StructuralAttributeBuilderFunctionsQueryBuilder(this.elements);
-
- // 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));
- }
- });
-
- // 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));
- }
- });
-
- document.querySelector('#corpus-analysis-concordance-text-annotation-submit').addEventListener('click', () => this.structuralAttributeBuilderFunctions.textAnnotationSubmitHandler());
-
- this.elements.positionalAttrModal = M.Modal.init(
- document.querySelector('#corpus-analysis-concordance-positional-attr-modal'),
- {
- onOpenStart: () => {
- this.tokenAttributeBuilderFunctions.preparePositionalAttrModal();
- },
- onCloseStart: () => {
- this.tokenAttributeBuilderFunctions.resetPositionalAttrModal();
- }
- }
- );
- this.elements.structuralAttrModal = M.Modal.init(
- document.querySelector('#corpus-analysis-concordance-structural-attr-modal'),
- {
- onCloseStart: () => {
- this.structuralAttributeBuilderFunctions.resetStructuralAttrModal();
- }
- }
- );
- }
-}
diff --git a/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js
deleted file mode 100644
index 481e9b6d..00000000
--- a/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js
+++ /dev/null
@@ -1,505 +0,0 @@
-class GeneralFunctionsQueryBuilder {
- 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('Click on a button to add a query component');
- 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(
- `
-
- ${prettyQueryText}${isEditable ? 'edit': ''}
- ${isClosingTag ? 'lock_open' : 'close'}
-
- `
- );
- 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 (index || isLastChildTextAnnotation) {
- let insertingElement = isLastChildTextAnnotation ? lastChild : this.elements.queryChipElements[index];
- this.elements.queryInputField.insertBefore(queryChipElement, insertingElement);
- } 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 => {
- 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) {
- //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.toggleEditingAreaStructureAttrModal('add');
- let entType = queryChipElement.dataset.query.replace(//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.toggleEditingAreaStructureAttrModal('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;
- }
- }
-
- 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';
-
- // let chipIndex = Array.from(this.elements.queryInputField.children).indexOf(queryChipElement);
- // this.submitQueryChipElement(queryChipElement.dataset.type, queryChipElement.firstChild.textContent, queryChipElement.dataset.query, chipIndex+1);
- // this.deleteChipElement(queryChipElement);
- // this.updateChipList();
- }
-
- 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':
- console.log(Array.from(this.elements.queryInputField.children)[elementIndex+1]);
- let nextElement = Array.from(this.elements.queryInputField.children)[elementIndex+1];
- if (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');
- setTimeout(() => {
- let targetChipElement = Utils.HTMLToElement('Drop here');
- for (let element of queryChips) {
- if (element === queryChipElement.nextSibling) {continue;}
- let targetChipClone = targetChipElement.cloneNode(true);
- if (element === queryChipElement && queryChips[queryChips.length - 1] !== element) {
- queryChips[queryChips.length - 1].insertAdjacentElement('afterend', targetChipClone);
- } else {
- element.insertAdjacentElement('beforebegin', 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);
- }
-
- 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.resetQueryInputField();
- let expertModeInputFieldValue = document.querySelector('#corpus-analysis-concordance-form-query').value;
- let chipElements = this.parseTextToChip(expertModeInputFieldValue);
- for (let chipElement of chipElements) {
- this.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query']);
- }
- }
-
- parseTextToChip(query) {
- const parsingElementDict = {
- '': {
- pretty: 'Sentence Start',
- type: 'start-sentence'
- },
- '<\/s>': {
- pretty: 'Sentence End',
- type: 'end-sentence'
- },
- '': {
- pretty: 'Entity Start',
- type: 'start-empty-entity'
- },
- '': {
- 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'
- },
- '(? `(${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 '':
- prettyText = `Entity Type=${stringElement.replace(//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 '(? {
- 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', '');
- this.submitQueryChipElement('end-entity', 'Entity End', '', 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}`, ``, null, false, true);
- if (!this.elements.editingModusOn) {
- this.submitQueryChipElement('end-entity', 'Entity End', '', 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}`, ``, null, false, true);
- if (!this.elements.editingModusOn) {
- this.submitQueryChipElement('end-entity', 'Entity End', '', 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.toggleEditingAreaStructureAttrModal('remove');
- this.elements.editingModusOn = false;
- this.elements.editedQueryChipElementIndex = undefined;
- }
-
- toggleEditingAreaStructureAttrModal(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);
- }
-
- actionButtonInStrucAttrModalHandler(action) {
- switch (action) {
- case 'sentence':
- this.submitQueryChipElement('start-sentence', 'Sentence Start', '');
- this.submitQueryChipElement('end-sentence', 'Sentence End', '', 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();
- }
- }
-}
diff --git a/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js
deleted file mode 100644
index bebb5d83..00000000
--- a/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js
+++ /dev/null
@@ -1,264 +0,0 @@
-class TokenAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQueryBuilder {
- constructor(elements) {
- super(elements);
- this.elements = 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();});
- }
-
- resetPositionalAttrModal() {
- let originalSelectionList =
- `
-
-
-
-
-
-
- `;
- 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');
- } 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 =
- `
-
-
-
-
-
- `;
- 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();
- }
-}
diff --git a/app/static/js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js b/app/static/js/CorpusAnalysis/query-builder/element-references.js
similarity index 100%
rename from app/static/js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js
rename to app/static/js/CorpusAnalysis/query-builder/element-references.js
diff --git a/app/static/js/CorpusAnalysis/query-builder/index.js b/app/static/js/CorpusAnalysis/query-builder/index.js
new file mode 100644
index 00000000..82a01a7c
--- /dev/null
+++ b/app/static/js/CorpusAnalysis/query-builder/index.js
@@ -0,0 +1,937 @@
+class ConcordanceQueryBuilder {
+
+ constructor() {
+ this.elements = new ElementReferencesQueryBuilder();
+
+ //#region QB Constructor
+ // 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.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".
+ 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.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", () => {
+ 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();
+ }
+ });
+ //#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', '');
+ this.submitQueryChipElement('end-entity', 'Entity End', '', 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}`, ``, null, false, true);
+ if (!this.elements.editingModusOn) {
+ this.submitQueryChipElement('end-entity', 'Entity End', '', 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}`, ``, null, false, true);
+ if (!this.elements.editingModusOn) {
+ this.submitQueryChipElement('end-entity', 'Entity End', '', 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
+ 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.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);
+ }
+ }
+
+ parseTextToChip(query) {
+ const parsingElementDict = {
+ '': {
+ pretty: 'Sentence Start',
+ type: 'start-sentence'
+ },
+ '<\/s>': {
+ pretty: 'Sentence End',
+ type: 'end-sentence'
+ },
+ '': {
+ pretty: 'Entity Start',
+ type: 'start-empty-entity'
+ },
+ '': {
+ 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'
+ },
+ '(? `(${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 '':
+ prettyText = `Entity Type=${stringElement.replace(//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 '(? {
+ 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('Click on a button to add a query component');
+ 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(
+ `
+
+ ${prettyQueryText}${isEditable ? 'edit': ''}
+ ${isClosingTag ? 'lock_open' : 'close'}
+
+ `
+ );
+ 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(//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('Drop here');
+ 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', '');
+ this.submitQueryChipElement('end-sentence', 'Sentence End', '', 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 =
+ `
+
+
+
+
+
+
+ `;
+ 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 =
+ `
+
+
+
+
+
+ `;
+ 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
+}
diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2
index a1897b56..53b2b763 100644
--- a/app/templates/_scripts.html.j2
+++ b/app/templates/_scripts.html.j2
@@ -103,13 +103,10 @@
{%- assets
filters='rjsmin',
output='gen/CorpusAnalysis.%(version)s.js',
- 'js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js',
- 'js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js',
- 'js/CorpusAnalysis/QueryBuilder/StructuralAttributeBuilderFunctionsQueryBuilder.js',
- 'js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js',
+ 'js/CorpusAnalysis/query-builder/index.js',
+ 'js/CorpusAnalysis/query-builder/element-references.js',
'js/CorpusAnalysis/CorpusAnalysisApp.js',
'js/CorpusAnalysis/CorpusAnalysisConcordance.js',
- 'js/CorpusAnalysis/QueryBuilder.js',
'js/CorpusAnalysis/CorpusAnalysisReader.js',
'js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js'
%}
diff --git a/app/templates/corpora/_analysis/concordance.html.j2 b/app/templates/corpora/_analysis/concordance.html.j2
index b160c2fb..f50578bd 100644
--- a/app/templates/corpora/_analysis/concordance.html.j2
+++ b/app/templates/corpora/_analysis/concordance.html.j2
@@ -130,21 +130,5 @@
{% endmacro %}