Skip to content
Snippets Groups Projects
Commit 1ff9c8bf authored by Inga Kirschnick's avatar Inga Kirschnick
Browse files

Query Builder in one class

parent fbb32ef5
No related branches found
No related tags found
No related merge requests found
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();
}
}
);
}
}
class StructuralAttributeBuilderFunctionsQueryBuilder extends GeneralFunctionsQueryBuilder {
constructor(elements) {
super(elements);
this.elements = elements;
document.querySelectorAll('[data-structural-attr-modal-action-button]').forEach(button => {
button.addEventListener('click', () => {
this.actionButtonInStrucAttrModalHandler(button.dataset.structuralAttrModalActionButton);
});
});
document.querySelector('.ent-type-selection-action[data-ent-type="any"]').addEventListener('click', () => {
this.submitQueryChipElement('start-empty-entity', 'Entity Start', '<ent>');
this.submitQueryChipElement('end-entity', 'Entity End', '</ent>', null, true);
this.elements.structuralAttrModal.close();
});
document.querySelector('.ent-type-selection-action[data-ent-type="english"]').addEventListener('change', (event) => {
this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
if (!this.elements.editingModusOn) {
this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
}
this.elements.structuralAttrModal.close();
});
document.querySelector('.ent-type-selection-action[data-ent-type="german"]').addEventListener('change', (event) => {
this.submitQueryChipElement('start-entity', `Entity Type=${event.target.value}`, `<ent_type="${event.target.value}">`, null, false, true);
if (!this.elements.editingModusOn) {
this.submitQueryChipElement('end-entity', 'Entity End', '</ent_type>', null, true);
}
this.elements.structuralAttrModal.close();
});
}
resetStructuralAttrModal() {
this.resetMaterializeSelection([this.elements.englishEntTypeSelection, this.elements.germanEntTypeSelection]);
this.resetMaterializeSelection([this.elements.textAnnotationSelection], 'address');
this.elements.textAnnotationInput.value = '';
this.toggleClass(['entity-builder', 'text-annotation-builder'], 'hide', 'add');
this.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', '<s>');
this.submitQueryChipElement('end-sentence', 'Sentence End', '</s>', null, true);
this.elements.structuralAttrModal.close();
break;
case 'entity':
this.toggleClass(['entity-builder'], 'hide', 'toggle');
this.toggleClass(['text-annotation-builder'], 'hide', 'add');
break;
case 'meta-data':
this.toggleClass(['text-annotation-builder'], 'hide', 'toggle');
this.toggleClass(['entity-builder'], 'hide', 'add');
break;
default:
break;
}
}
textAnnotationSubmitHandler() {
let noValueMetadataMessage = document.querySelector('#corpus-analysis-concordance-no-value-metadata-message');
let textAnnotationSubmit = document.querySelector('#corpus-analysis-concordance-text-annotation-submit');
let textAnnotationInput = document.querySelector('#corpus-analysis-concordance-text-annotation-input');
let textAnnotationOptions = document.querySelector('#corpus-analysis-concordance-text-annotation-options');
if (textAnnotationInput.value === '') {
textAnnotationSubmit.classList.add('red');
noValueMetadataMessage.classList.remove('hide');
setTimeout(() => {
textAnnotationSubmit.classList.remove('red');
}, 500);
setTimeout(() => {
noValueMetadataMessage.classList.add('hide');
}, 3000);
} else {
let queryText = `:: match.text_${textAnnotationOptions.value}="${textAnnotationInput.value}"`;
this.submitQueryChipElement('text-annotation', `${textAnnotationOptions.value}=${textAnnotationInput.value}`, queryText, null, false, true);
this.elements.structuralAttrModal.close();
}
}
}
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 =
`
<option value="word" selected>word</option>
<option value="lemma" >lemma</option>
<option value="english-pos">english pos</option>
<option value="german-pos">german pos</option>
<option value="simple_pos">simple_pos</option>
<option value="empty-token">empty token</option>
`;
this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
this.elements.tokenQuery.innerHTML = '';
this.elements.tokenBuilderContent.innerHTML = '';
this.toggleClass(['input-field-options'], 'hide', 'remove');
this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
this.resetMaterializeSelection([this.elements.positionalAttrSelection], "word");
this.elements.ignoreCaseCheckbox.checked = false;
this.elements.editingModusOn = false;
this.elements.editedQueryChipElementIndex = undefined;
}
preparePositionalAttrModal() {
let selection = this.elements.positionalAttrSelection.value;
if (selection !== 'empty-token') {
let selectionTemplate = document.querySelector(`.token-builder-section[data-token-builder-section="${selection}"]`);
let selectionTemplateClone = selectionTemplate.content.cloneNode(true);
this.elements.tokenBuilderContent.innerHTML = '';
this.elements.tokenBuilderContent.appendChild(selectionTemplateClone);
if (this.elements.tokenBuilderContent.querySelector('select') !== null) {
let selectElement = this.elements.tokenBuilderContent.querySelector('select');
M.FormSelect.init(selectElement);
selectElement.addEventListener('change', () => {this.optionToggleHandler();});
} else {
this.elements.tokenBuilderContent.querySelector('input').addEventListener('input', () => {this.optionToggleHandler();});
}
}
this.optionToggleHandler();
if (selection === 'word' || selection === 'lemma') {
this.toggleClass(['input-field-options'], 'hide', 'remove');
} else if (selection === 'empty-token'){
this.addTokenToQuery();
} else {
this.toggleClass(['input-field-options'], 'hide', 'add');
}
}
tokenInputCheck(elem) {
return elem.querySelector('select') !== null ? elem.querySelector('select') : elem.querySelector('input');
}
optionToggleHandler() {
let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
if (input.value === '' && this.elements.editingModusOn === false) {
this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'add');
} else if (this.elements.positionalAttrSelection.querySelectorAll('option').length === 1) {
this.toggleClass(['and'], 'disabled', 'add');
} else {
this.toggleClass(['incidence-modifiers', 'or', 'and'], 'disabled', 'remove');
}
}
disableTokenSubmit() {
this.elements.tokenSubmitButton.classList.add('red');
this.elements.noValueMessage.classList.remove('hide');
setTimeout(() => {
this.elements.tokenSubmitButton.classList.remove('red');
}, 500);
setTimeout(() => {
this.elements.noValueMessage.classList.add('hide');
}, 3000);
}
addTokenToQuery() {
let tokenQueryPrettyText = '';
let tokenQueryCQLText = '';
let input;
let kindOfToken = this.kindOfTokenCheck(this.elements.positionalAttrSelection.value);
// Takes all rows of the token query (if there is a query concatenation).
// Adds their contents to tokenQueryPrettyText and tokenQueryCQLText, which will later be expanded with the current input field.
let tokenQueryRows = this.elements.tokenQuery.querySelectorAll('.row');
tokenQueryRows.forEach(row => {
let ignoreCaseCheckbox = row.querySelector('input[type="checkbox"]');
let c = ignoreCaseCheckbox !== null && ignoreCaseCheckbox.checked ? ' %c' : '';
let tokenQueryRowInput = this.tokenInputCheck(row.querySelector('.token-query-template-content'));
let tokenQueryKindOfToken = this.kindOfTokenCheck(tokenQueryRowInput.closest('.input-field').dataset.kindOfToken);
let tokenConditionPrettyText = row.querySelector('[data-condition-pretty-text]').dataset.conditionPrettyText;
let tokenConditionCQLText = row.querySelector('[data-condition-cql-text]').dataset.conditionCqlText;
tokenQueryPrettyText += `${tokenQueryKindOfToken}=${tokenQueryRowInput.value}${c} ${tokenConditionPrettyText} `;
tokenQueryCQLText += `${tokenQueryKindOfToken}="${tokenQueryRowInput.value}"${c} ${tokenConditionCQLText}`;
});
if (kindOfToken === 'empty-token') {
tokenQueryPrettyText += 'empty token';
} else {
let c = this.elements.ignoreCaseCheckbox.checked ? ' %c' : '';
input = this.tokenInputCheck(this.elements.tokenBuilderContent);
tokenQueryPrettyText += `${kindOfToken}=${input.value}${c}`;
tokenQueryCQLText += `${kindOfToken}="${input.value}"${c}`;
}
// isTokenQueryInvalid looks if a valid value is passed. If the input fields/dropdowns are empty (isTokenQueryInvalid === true), no token is added.
if (this.elements.positionalAttrSelection.value !== 'empty-token' && input.value === '') {
this.disableTokenSubmit();
} else {
tokenQueryCQLText = `[${tokenQueryCQLText}]`;
this.submitQueryChipElement('token', tokenQueryPrettyText, tokenQueryCQLText, null, false, kindOfToken === 'empty-token' ? false : true);
this.elements.positionalAttrModal.close();
}
}
kindOfTokenCheck(kindOfToken) {
return kindOfToken === 'english-pos' || kindOfToken === 'german-pos' ? 'pos' : kindOfToken;
}
actionButtonInOptionSectionHandler(elem) {
let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
switch (elem) {
case 'option-group':
input.value += '(option1|option2)';
let firstIndex = input.value.indexOf('option1');
let lastIndex = firstIndex + 'option1'.length;
input.focus();
input.setSelectionRange(firstIndex, lastIndex);
break;
case 'wildcard-char':
input.value += '.';
break;
case 'and':
this.conditionHandler('and');
break;
case 'or':
this.conditionHandler('or');
break;
default:
break;
}
this.optionToggleHandler();
}
characterIncidenceModifierHandler(elem) {
let input = this.tokenInputCheck(this.elements.tokenBuilderContent);
input.value += elem.dataset.token;
}
characterNMSubmitHandler(modalId) {
let modal = document.querySelector(`#${modalId}`);
let input_n = modal.querySelector('.n-m-input[data-value-type="n"]').value;
let input_m = modal.querySelector('.n-m-input[data-value-type="m"]') || undefined;
input_m = input_m !== undefined ? ',' + input_m.value : '';
let input = `${input_n}${input_m}`;
let instance = M.Modal.getInstance(modal);
instance.close();
let tokenInput = this.tokenInputCheck(this.elements.tokenBuilderContent);
tokenInput.value += '{' + input + '}';
}
conditionHandler(conditionText, editMode = false) {
let tokenQueryTemplateClone = this.elements.tokenQueryTemplate.content.cloneNode(true);
tokenQueryTemplateClone.querySelector('.token-query-template-content').appendChild(this.elements.tokenBuilderContent.firstElementChild);
let notSelectedButton = tokenQueryTemplateClone.querySelector(`[data-condition-pretty-text]:not([data-condition-pretty-text="${conditionText}"])`);
let deleteButton = tokenQueryTemplateClone.querySelector(`[data-token-query-content-action="delete"]`);
deleteButton.addEventListener('click', (event) => {
this.deleteTokenQueryRow(event.target);
});
notSelectedButton.parentNode.removeChild(notSelectedButton);
this.elements.tokenQuery.appendChild(tokenQueryTemplateClone);
// Deleting the options which do not make sense in the context of the condition like "word" AND "word". Also sets selection default.
let selectionDefault = "word";
let optionDeleteList = ['empty-token'];
if (conditionText === 'and') {
switch (this.elements.positionalAttrSelection.value) {
case 'english-pos' || 'german-pos':
optionDeleteList.push('english-pos', 'german-pos');
break;
default:
optionDeleteList.push(this.elements.positionalAttrSelection.value);
break;
}
} else {
let originalSelectionList =
`
<option value="word" selected>word</option>
<option value="lemma" >lemma</option>
<option value="english-pos">english pos</option>
<option value="german-pos">german pos</option>
<option value="simple_pos">simple_pos</option>
`;
this.elements.positionalAttrSelection.innerHTML = originalSelectionList;
M.FormSelect.init(this.elements.positionalAttrSelection);
}
let lastTokenQueryRow = this.elements.tokenQuery.lastElementChild;
if(lastTokenQueryRow.querySelector('[data-kind-of-token="word"]') || lastTokenQueryRow.querySelector('[data-kind-of-token="lemma"]')) {
this.appendIgnoreCaseCheckbox(lastTokenQueryRow.querySelector('.token-query-template-content'), this.elements.ignoreCaseCheckbox.checked);
}
this.elements.ignoreCaseCheckbox.checked = false;
this.setTokenSelection(selectionDefault, optionDeleteList);
}
deleteTokenQueryRow(deleteButton) {
let deletedRow = deleteButton.closest('.row');
let condition = deletedRow.querySelector('[data-condition-pretty-text]').dataset.conditionPrettyText;
if (condition === 'and') {
let kindOfToken = deletedRow.querySelector('[data-kind-of-token]').dataset.kindOfToken;
switch (kindOfToken) {
case 'english-pos' || 'german-pos':
this.createOptionElementForPosAttrSelection('english-pos');
this.createOptionElementForPosAttrSelection('german-pos');
break;
default:
this.createOptionElementForPosAttrSelection(kindOfToken);
break;
}
M.FormSelect.init(this.elements.positionalAttrSelection);
}
deletedRow.remove();
}
createOptionElementForPosAttrSelection(kindOfToken) {
let option = document.createElement('option');
option.value = kindOfToken;
option.text = kindOfToken;
this.elements.positionalAttrSelection.appendChild(option);
}
appendIgnoreCaseCheckbox(parentElement, checked = false) {
let ignoreCaseCheckboxClone = document.querySelector('#ignore-case-checkbox-template').content.cloneNode(true);
parentElement.appendChild(ignoreCaseCheckboxClone);
M.Tooltip.init(parentElement.querySelectorAll('.tooltipped'));
if (checked) {
parentElement.querySelector('input[type="checkbox"]').checked = true;
}
}
setTokenSelection(selection, optionDeleteList) {
optionDeleteList.forEach(option => {
if (this.elements.positionalAttrSelection.querySelector(`option[value=${option}]`) !== null) {
this.elements.positionalAttrSelection.querySelector(`option[value=${option}]`).remove();
}
});
this.resetMaterializeSelection([this.elements.positionalAttrSelection], selection);
this.preparePositionalAttrModal();
}
}
...@@ -103,13 +103,10 @@ ...@@ -103,13 +103,10 @@
{%- assets {%- assets
filters='rjsmin', filters='rjsmin',
output='gen/CorpusAnalysis.%(version)s.js', output='gen/CorpusAnalysis.%(version)s.js',
'js/CorpusAnalysis/QueryBuilder/ElementReferencesQueryBuilder.js', 'js/CorpusAnalysis/query-builder/index.js',
'js/CorpusAnalysis/QueryBuilder/GeneralFunctionsQueryBuilder.js', 'js/CorpusAnalysis/query-builder/element-references.js',
'js/CorpusAnalysis/QueryBuilder/StructuralAttributeBuilderFunctionsQueryBuilder.js',
'js/CorpusAnalysis/QueryBuilder/TokenAttributeBuilderFunctionsQueryBuilder.js',
'js/CorpusAnalysis/CorpusAnalysisApp.js', 'js/CorpusAnalysis/CorpusAnalysisApp.js',
'js/CorpusAnalysis/CorpusAnalysisConcordance.js', 'js/CorpusAnalysis/CorpusAnalysisConcordance.js',
'js/CorpusAnalysis/QueryBuilder.js',
'js/CorpusAnalysis/CorpusAnalysisReader.js', 'js/CorpusAnalysis/CorpusAnalysisReader.js',
'js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js' 'js/CorpusAnalysis/CorpusAnalysisStaticVisualization.js'
%} %}
......
...@@ -130,21 +130,5 @@ ...@@ -130,21 +130,5 @@
<script> <script>
const corpusAnalysisConcordance = new CorpusAnalysisConcordance(corpusAnalysisApp); const corpusAnalysisConcordance = new CorpusAnalysisConcordance(corpusAnalysisApp);
const concordanceQueryBuilder = new ConcordanceQueryBuilder(); const concordanceQueryBuilder = new ConcordanceQueryBuilder();
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", function() {
if (this.checked) {
queryBuilderDisplay.classList.add("hide");
expertModeDisplay.classList.remove("hide");
concordanceQueryBuilder.generalFunctions.switchToExpertModeParser();
} else {
queryBuilderDisplay.classList.remove("hide");
expertModeDisplay.classList.add("hide");
concordanceQueryBuilder.generalFunctions.switchToQueryBuilderParser();
}
});
</script> </script>
{% endmacro %} {% endmacro %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment