Editing Entity Type

This commit is contained in:
Inga Kirschnick 2023-10-09 15:42:01 +02:00
parent c6be72d0a7
commit 2dec17b1b9
6 changed files with 192 additions and 124 deletions

View File

@ -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();
}
}
);

View File

@ -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'));

View File

@ -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(
`
<span class="chip query-component" data-type="${dataType}" data-query="${queryText}" draggable="true" data-closing-tag="${isClosingTag}">
${prettyQueryText}
${isClosingTag ? '<i class="material-icons" style="padding-top:5px; font-size:20px; cursor:pointer;">lock_open</i>' : '<i class="material-icons close">close</i>'}
${prettyQueryText}${isEditable ? '<i class="material-icons chip-action-button" data-chip-action="edit" style="padding-left:5px; font-size:18px; cursor:pointer;">edit</i>': ''}
${isClosingTag ? '<i class="material-icons chip-action-button" data-chip-action="lock" style="padding-top:5px; font-size:20px; cursor:pointer;">lock_open</i>' : '<i class="material-icons close chip-action-button" data-chip-action="delete">close</i>'}
</span>
`
);
this.actionListeners(queryChipElement, 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(/<ent_type="|">/g, '');
let isEnglishEntType = this.elements.englishEntTypeSelection.querySelector(`option[value=${entType}]`) !== null;
let selection = isEnglishEntType ? this.elements.englishEntTypeSelection : this.elements.germanEntTypeSelection;
this.resetMaterializeSelection([selection], entType);
break;
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']);
}
}

View File

@ -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', '<ent>');
this.queryChipFactory('end-entity', 'Entity End', '</ent>', null, true);
this.resetAndCloseStructuralAttrModal();
this.submitQueryChipElement('start-empty-entity', 'Entity Start', '<ent>');
this.submitQueryChipElement('end-entity', 'Entity End', '</ent>', null, true);
this.elements.structuralAttrModal.close();
});
document.querySelector('.ent-type-selection-action[data-ent-type="english"]').addEventListener('change', (event) => {
this.queryChipFactory('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`);
this.queryChipFactory('end-entity', 'Entity End', '</ent_type>', null, true);
this.resetAndCloseStructuralAttrModal();
this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
if (!this.elements.editingModusOn) {
this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
}
this.elements.structuralAttrModal.close();
});
document.querySelector('.ent-type-selection-action[data-ent-type="german"]').addEventListener('change', (event) => {
this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
if (!this.elements.editingModusOn) {
this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
}
this.elements.structuralAttrModal.close();
});
}
resetStructuralAttrModal() {
this.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]);
this.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address');
this.elements.textAnnotationInput.value = '';
this.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add');
this.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', '<s>');
this.queryChipFactory('end-sentence', 'Sentence End', '</s>', null, true);
this.resetAndCloseStructuralAttrModal();
this.submitQueryChipElement('start-sentence', 'Sentence Start', '<s>');
this.submitQueryChipElement('end-sentence', 'Sentence End', '</s>', null, true);
this.elements.structuralAttrModal.close();
break;
case 'entity':
this.toggleClass(['entity-builder'], 'hide', 'toggle');
@ -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();
}
}

View File

@ -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 =
`
<option value="word" selected>word</option>
<option value="lemma" >lemma</option>
<option value="english-pos">english pos</option>
<option value="german-pos">german pos</option>
<option value="simple-pos">simple_pos</option>
<option value="empty-token">empty token</option>
`;
this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
this.elements.tokenQuery.innerHTML = '';
this.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 =
`
<option value="word" selected>word</option>
<option value="lemma" >lemma</option>
<option value="english-pos">english pos</option>
<option value="german-pos">german pos</option>
<option value="simple-pos">simple_pos</option>
<option value="empty-token">empty token</option>
`;
this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
this.elements.tokenQuery.innerHTML = '';
this.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");
}
}

View File

@ -65,49 +65,49 @@
<br>
<div class="row">
<div class="col s12">
<a class="btn-small waves-effect waves-light" data-structural-attr-modal-action-button="sentence">sentence</a>
<a class="btn-small waves-effect waves-light" data-structural-attr-modal-action-button="entity">entity</a>
<a class="btn-small waves-effect waves-light" data-structural-attr-modal-action-button="meta-data">Meta Data</a>
<a class="btn-small waves-effect waves-light" data-structural-attr-modal-action-button="sentence" data-toggle-area="sentence-button">sentence</a>
<a class="btn-small waves-effect waves-light" data-structural-attr-modal-action-button="entity" data-toggle-area="entity-button">entity</a>
<a class="btn-small waves-effect waves-light" data-structural-attr-modal-action-button="meta-data" data-toggle-area="text-annotation-button">Meta Data</a>
</div>
</div>
<div id="corpus-analysis-concordance-entity-builder" data-toggle-area="entity-builder" class="hide">
<p></p>
<br>
<div class="row">
<a class="btn waves-effect waves-light col ent-type-selection-action" data-ent-type="any">Add Entity of any type</a>
<a class="btn waves-effect waves-light col ent-type-selection-action" data-ent-type="any" data-toggle-area="any-type-entity-button">Add Entity of any type</a>
<p class="col s1 l1"></p>
<div class= "input-field col s3">
<select id="corpus-analysis-concordance-english-ent-type-selection" name="englishenttype" class="ent-type-selection-action" data-ent-type="english">
<option value="default" disabled selected>English ent_type</option>
<option value="CARDINAL">CARDINAL</option>
<option value="DATE">DATE</option>
<option value="EVENT">EVENT</option>
<option value="FAC">FAC</option>
<option value="GPE">GPE</option>
<option value="LANGUAGE">LANGUAGE</option>
<option value="LAW">LAW</option>
<option value="LOC">LOC</option>
<option value="MONEY">MONEY</option>
<option value="NORP">NORP</option>
<option value="ORDINAL">ORDINAL</option>
<option value="ORG">ORG</option>
<option value="PERCENT">PERCENT</option>
<option value="PERSON">PERSON</option>
<option value="PRODUCT">PRODUCT</option>
<option value="QUANTITY">QUANTITY</option>
<option value="TIME">TIME</option>
<option value="WORK_OF_ART">WORK_OF_ART</option>
</select>
<label>Entity Type</label>
<select id="corpus-analysis-concordance-english-ent-type-selection" name="englishenttype" class="ent-type-selection-action" data-ent-type="english">
<option value="default" disabled selected>English ent_type</option>
<option value="CARDINAL">CARDINAL</option>
<option value="DATE">DATE</option>
<option value="EVENT">EVENT</option>
<option value="FAC">FAC</option>
<option value="GPE">GPE</option>
<option value="LANGUAGE">LANGUAGE</option>
<option value="LAW">LAW</option>
<option value="LOC">LOC</option>
<option value="MONEY">MONEY</option>
<option value="NORP">NORP</option>
<option value="ORDINAL">ORDINAL</option>
<option value="ORG">ORG</option>
<option value="PERCENT">PERCENT</option>
<option value="PERSON">PERSON</option>
<option value="PRODUCT">PRODUCT</option>
<option value="QUANTITY">QUANTITY</option>
<option value="TIME">TIME</option>
<option value="WORK_OF_ART">WORK_OF_ART</option>
</select>
<label>Entity Type</label>
</div>
<div class= "input-field col s3">
<select id="corpus-analysis-concordance-german-ent-type-selection" name="germanenttype" class="ent-type-selection-action" data-ent-type="german">
<option value="default" disabled selected>German ent_type</option>
<option value="LOC">LOC</option>
<option value="MISC">MISC</option>
<option value="ORG">ORG</option>
<option value="PER">PER</option>
</select>
<select id="corpus-analysis-concordance-german-ent-type-selection" name="germanenttype" class="ent-type-selection-action" data-ent-type="german">
<option value="default" disabled selected>German ent_type</option>
<option value="LOC">LOC</option>
<option value="MISC">MISC</option>
<option value="ORG">ORG</option>
<option value="PER">PER</option>
</select>
</div>
</div>
</div>
@ -385,7 +385,6 @@
{{ exactly_n_modal_content("character") }}
{{ exactly_nm_modal_content("character") }}
</div>
</div>
</div>
{% endmacro %}