diff --git a/app/static/js/CorpusAnalysis/QueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder.js index c51bc4a5..379a39ea 100644 --- a/app/static/js/CorpusAnalysis/QueryBuilder.js +++ b/app/static/js/CorpusAnalysis/QueryBuilder.js @@ -33,7 +33,19 @@ class ConcordanceQueryBuilder { document.querySelector('#corpus-analysis-concordance-positional-attr-modal'), { onOpenStart: () => { + this.tokenAttributeBuilderFunctions.preparePositionalAttrModal(); this.tokenAttributeBuilderFunctions.optionToggleHandler(); + }, + 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/ElementReferencesQueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js index 17df5ed2..bfd6fd86 100644 --- a/app/static/js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js +++ b/app/static/js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js @@ -3,6 +3,8 @@ class ElementReferencesQueryBuilder { // General Elements this.queryInputField = document.querySelector('#corpus-analysis-concordance-query-builder-input-field'); this.queryChipElements = []; + this.editingModusOn = false; + this.editedQueryChipElementIndex = undefined; // Structural Attribute Builder Elements this.structuralAttrModal = M.Modal.getInstance(document.querySelector('#corpus-analysis-concordance-structural-attr-modal')); @@ -12,6 +14,7 @@ class ElementReferencesQueryBuilder { this.englishEntTypeSelection = document.querySelector('#corpus-analysis-concordance-english-ent-type-selection'); this.germanEntTypeSelection = document.querySelector('#corpus-analysis-concordance-german-ent-type-selection'); this.textAnnotationSelection = document.querySelector('#corpus-analysis-concordance-text-annotation-options'); + this.textAnnotationInput = document.querySelector('#corpus-analysis-concordance-text-annotation-input'); // Token Attribute Builder Elements this.positionalAttrModal = M.Modal.getInstance(document.querySelector('#corpus-analysis-concordance-positional-attr-modal')); diff --git a/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js index 28f2aeea..6ab84169 100644 --- a/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js +++ b/app/static/js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js @@ -5,7 +5,7 @@ class GeneralFunctionsQueryBuilder { toggleClass(elements, className, action){ elements.forEach(element => { - document.querySelector('[data-toggle-area="' + element + '"]').classList[action](className); + document.querySelector(`[data-toggle-area="${element}"]`).classList[action](className); }); } @@ -22,7 +22,7 @@ class GeneralFunctionsQueryBuilder { removePlaceholder() { let placeholder = this.elements.queryInputField.querySelector('#corpus-analysis-concordance-query-builder-input-field-placeholder'); - if (placeholder) { + if (placeholder && this.elements.queryInputField !== undefined) { this.elements.queryInputField.innerHTML = ''; } } @@ -41,21 +41,33 @@ class GeneralFunctionsQueryBuilder { }) } + 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) { + + 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} - ${isClosingTag ? 'lock_open' : 'close'} + ${prettyQueryText}${isEditable ? 'edit': ''} + ${isClosingTag ? 'lock_open' : 'close'} ` ); - this.actionListeners(queryChipElement, isClosingTag); + this.actionListeners(queryChipElement); queryChipElement.addEventListener('dragstart', this.handleDragStart.bind(this, queryChipElement)); queryChipElement.addEventListener('dragend', this.handleDragEnd); @@ -80,7 +92,7 @@ class GeneralFunctionsQueryBuilder { this.queryPreviewBuilder(); } - actionListeners(queryChipElement, isClosingTag) { + 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')) { @@ -89,22 +101,60 @@ class GeneralFunctionsQueryBuilder { } } }); - queryChipElement.querySelector('i').addEventListener('click', () => { - if (isClosingTag) { - this.lockClosingChipElement(queryChipElement); - } else { + 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) { + 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; + default: + break; + } + } + lockClosingChipElement(queryChipElement) { let chipIndex = Array.from(this.elements.queryInputField.children).indexOf(queryChipElement); - this.queryChipFactory(queryChipElement.dataset.type, queryChipElement.firstChild.textContent, queryChipElement.dataset.query, chipIndex+1); + this.submitQueryChipElement(queryChipElement.dataset.type, queryChipElement.firstChild.textContent, queryChipElement.dataset.query, chipIndex+1); this.deleteChipElement(queryChipElement); this.updateChipList(); } + + deleteChipElement(attr) { + if (attr.dataset.type === "start-sentence") { + this.elements.sentenceElement.innerHTML = 'Sentence'; + } else if (attr.dataset.type === "start-entity" || attr.dataset.type === "start-empty-entity") { + this.elements.entityElement.innerHTML = 'Entity'; + } + this.elements.queryInputField.removeChild(attr); + if (this.elements.queryInputField.children.length === 0) { + this.addPlaceholder(); + } + this.updateChipList(); + this.queryPreviewBuilder(); + } + 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'); @@ -176,20 +226,6 @@ class GeneralFunctionsQueryBuilder { queryPreview.parentNode.classList.toggle('hide', queryString === ';'); } - deleteChipElement(attr) { - if (attr.dataset.type === "start-sentence") { - this.elements.sentenceElement.innerHTML = 'Sentence'; - } else if (attr.dataset.type === "start-entity" || attr.dataset.type === "start-empty-entity") { - this.elements.entityElement.innerHTML = 'Entity'; - } - this.elements.queryInputField.removeChild(attr); - if (this.elements.queryInputField.children.length === 0) { - this.addPlaceholder(); - } - this.updateChipList(); - this.queryPreviewBuilder(); - } - selectChipElement(attr) { document.querySelectorAll('.chip.teal').forEach(element => { if (element !== attr) { @@ -207,7 +243,7 @@ class GeneralFunctionsQueryBuilder { // 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.queryChipFactory('token-incidence-modifier', incidenceModifierPretty, incidenceModifier, selectedChipIndex+1); + this.submitQueryChipElement('token-incidence-modifier', incidenceModifierPretty, incidenceModifier, selectedChipIndex+1); this.selectChipElement(selectedChip); } @@ -243,7 +279,7 @@ class GeneralFunctionsQueryBuilder { let expertModeInputFieldValue = document.querySelector('#corpus-analysis-concordance-form-query').value; let chipElements = this.parseTextToChip(expertModeInputFieldValue); for (let chipElement of chipElements) { - this.queryChipFactory(chipElement['type'], chipElement['pretty'], chipElement['query']); + this.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query']); } } diff --git a/app/static/js/CorpusAnalysis/QueryBuilder/StructuralAttributeBuilderFunctionsQueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder/StructuralAttributeBuilderFunctionsQueryBuilder.js index c9bb402c..15f7bc57 100644 --- a/app/static/js/CorpusAnalysis/QueryBuilder/StructuralAttributeBuilderFunctionsQueryBuilder.js +++ b/app/static/js/CorpusAnalysis/QueryBuilder/StructuralAttributeBuilderFunctionsQueryBuilder.js @@ -9,23 +9,48 @@ class StructuralAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQu }); }); document.querySelector('.ent-type-selection-action[data-ent-type="any"]').addEventListener('click', () => { - this.queryChipFactory('start-empty-entity', 'Entity Start', ''); - this.queryChipFactory('end-entity', 'Entity End', '', null, true); - this.resetAndCloseStructuralAttrModal(); + 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.queryChipFactory('start-entity', `Entity Type=${event.target.value}`, ``); - this.queryChipFactory('end-entity', 'Entity End', '', null, true); - this.resetAndCloseStructuralAttrModal(); + 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.queryChipFactory('start-sentence', 'Sentence Start', ''); - this.queryChipFactory('end-sentence', 'Sentence End', '', null, true); - this.resetAndCloseStructuralAttrModal(); + 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'); @@ -57,19 +82,8 @@ class StructuralAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQu }, 3000); } else { let queryText = `:: match.text_${textAnnotationOptions.value}="${textAnnotationInput.value}"`; - this.queryChipFactory('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText); - this.resetAndCloseStructuralAttrModal(); + this.submitQueryChipElement('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText); + this.elements.structuralAttrModal.close(); } } - - resetAndCloseStructuralAttrModal() { - let textAnnotatinInput = document.querySelector('#corpus-analysis-concordance-text-annotation-input'); - textAnnotatinInput.value = ''; - this.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]); - this.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address'); - - this.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add'); - this.elements.structuralAttrModal.close(); - } - } diff --git a/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js b/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js index 1ac6835a..0e4c4bd1 100644 --- a/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js +++ b/app/static/js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js @@ -3,20 +3,8 @@ class TokenAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQueryBu super(elements); this.elements = elements; - this.elements.positionalAttrSelection.addEventListener('change', (event) => { - this.toggleClass(['word', 'lemma', 'english-pos', 'german-pos', 'simple-pos'], 'hide', 'add'); - if (event.target.value !== 'empty-token') { - this.toggleClass([event.target.value], 'hide', 'remove'); - this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add'); - this.resetMaterializeSelection([this.elements.englishPosSelection, this.elements.germanPosSelection, this.elements.simplePosSelection]); - } - if (event.target.value === 'word' || event.target.value === 'lemma') { - this.toggleClass(['input-field-options'], 'hide', 'remove'); - } else if (event.target.value === 'empty-token'){ - this.addTokenToQuery(); - } else { - this.toggleClass(['input-field-options'], 'hide', 'add'); - } + this.elements.positionalAttrSelection.addEventListener('change', () => { + this.preparePositionalAttrModal(); }); // Options for positional attribute selection @@ -33,6 +21,46 @@ class TokenAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQueryBu this.elements.simplePosSelection.addEventListener('change', () => {this.optionToggleHandler();}); } + resetPositionalAttrModal() { + let originalSelectionList = + ` + + + + + + + `; + this.elements.positionalAttrSelection.innerHTML = originalSelectionList; + this.elements.tokenQuery.innerHTML = ''; + this.toggleClass(['word', 'lemma', 'english-pos', 'german-pos', 'simple-pos'], 'hide', 'add'); + this.toggleClass(['word', 'input-field-options'], 'hide', 'remove'); + this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add'); + + document.querySelector('#corpus-analysis-concordance-positional-attr-selection option[value="word"]').selected = true; + + this.resetMaterializeSelection([this.elements.englishPosSelection, this.elements.germanPosSelection, this.elements.simplePosSelection]); + this.resetMaterializeSelection([this.elements.positionalAttrSelection], "word"); + this.elements.editingModusOn = false; + this.elements.editedQueryChipElementIndex = undefined; + } + + preparePositionalAttrModal() { + let selection = this.elements.positionalAttrSelection.value; + if (selection !== 'empty-token') { + this.toggleClass([selection], 'hide', 'remove'); + this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add'); + this.resetMaterializeSelection([this.elements.englishPosSelection, this.elements.germanPosSelection, this.elements.simplePosSelection]); + } + 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() { let input; @@ -154,8 +182,7 @@ class TokenAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQueryBu // isTokenQueryInvalid looks if a valid value is passed. If the input fields/dropdowns are empty (isTokenQueryInvalid === true), no token is added. if (this.elements.isTokenQueryInvalid === false) { tokenQueryCQLText = '[' + tokenQueryCQLText + ']'; - this.queryChipFactory('token', tokenQueryPrettyText, tokenQueryCQLText); - this.resetPositionalAttrModal(); + this.submitQueryChipElement('token', tokenQueryPrettyText, tokenQueryCQLText); this.elements.positionalAttrModal.close(); } } @@ -308,27 +335,4 @@ class TokenAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQueryBu this.toggleClass(['input-field-options'], 'hide', 'add'); } } - - - resetPositionalAttrModal() { - let originalSelectionList = - ` - - - - - - - `; - this.elements.positionalAttrSelection.innerHTML = originalSelectionList; - this.elements.tokenQuery.innerHTML = ''; - this.toggleClass(['word', 'lemma', 'english-pos', 'german-pos', 'simple-pos'], 'hide', 'add'); - this.toggleClass(['word', 'input-field-options'], 'hide', 'remove'); - this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add'); - - document.querySelector('#corpus-analysis-concordance-positional-attr-selection option[value="word"]').selected = true; - - this.resetMaterializeSelection([this.elements.englishPosSelection, this.elements.germanPosSelection, this.elements.simplePosSelection]); - this.resetMaterializeSelection([this.elements.positionalAttrSelection], "word"); - } } diff --git a/app/templates/corpora/_analysis/query_builder/_query_builder.html.j2 b/app/templates/corpora/_analysis/query_builder/_query_builder.html.j2 index c4f63943..836bdf83 100644 --- a/app/templates/corpora/_analysis/query_builder/_query_builder.html.j2 +++ b/app/templates/corpora/_analysis/query_builder/_query_builder.html.j2 @@ -65,49 +65,49 @@


- Add Entity of any type + Add Entity of any type

- - + +
- +
@@ -385,7 +385,6 @@ {{ exactly_n_modal_content("character") }} {{ exactly_nm_modal_content("character") }} - {% endmacro %}