|
|
|
@ -2,25 +2,13 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
constructor() {
|
|
|
|
|
this.elements = new nopaque.corpus_analysis.query_builder.ElementReferences();
|
|
|
|
|
|
|
|
|
|
this.incidenceModifierEventListeners();
|
|
|
|
|
this.nAndMInputSubmitEventListeners();
|
|
|
|
|
this.addEventListenersToQueryElementTarget();
|
|
|
|
|
this.addEventListenersToIncidenceModifier();
|
|
|
|
|
this.addEventListenersToNAndMInputSubmit();
|
|
|
|
|
|
|
|
|
|
let queryBuilderDisplay = document.querySelector("#corpus-analysis-concordance-query-builder-display");
|
|
|
|
|
let expertModeDisplay = document.querySelector("#corpus-analysis-concordance-expert-mode-display");
|
|
|
|
|
let expertModeSwitch = document.querySelector("#corpus-analysis-concordance-expert-mode-switch");
|
|
|
|
|
|
|
|
|
|
expertModeSwitch.addEventListener("change", () => {
|
|
|
|
|
const isChecked = expertModeSwitch.checked;
|
|
|
|
|
if (isChecked) {
|
|
|
|
|
queryBuilderDisplay.classList.add("hide");
|
|
|
|
|
expertModeDisplay.classList.remove("hide");
|
|
|
|
|
this.switchToExpertModeParser();
|
|
|
|
|
} else {
|
|
|
|
|
queryBuilderDisplay.classList.remove("hide");
|
|
|
|
|
expertModeDisplay.classList.add("hide");
|
|
|
|
|
this.switchToQueryBuilderParser();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
this.elements.deleteQueryButton.addEventListener('click', () => {this.resetQueryInputField()});
|
|
|
|
|
|
|
|
|
|
this.expertModeQueryBuilderSwitchHandler();
|
|
|
|
|
|
|
|
|
|
this.extensions = {
|
|
|
|
|
structuralAttributeBuilderFunctions: new nopaque.corpus_analysis.query_builder.StructuralAttributeBuilderFunctions(this),
|
|
|
|
@ -28,6 +16,38 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addEventListenersToQueryElementTarget() {
|
|
|
|
|
this.elements.queryElementTarget.addEventListener('click', () => {
|
|
|
|
|
this.elements.positionalAttrModal.open();
|
|
|
|
|
});
|
|
|
|
|
this.elements.queryElementTarget.addEventListener('dragstart', this.handleDragStart.bind(this, this.elements.queryElementTarget));
|
|
|
|
|
this.elements.queryElementTarget.addEventListener('dragend', this.handleDragEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addEventListenersToIncidenceModifier() {
|
|
|
|
|
// 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.extensions.tokenAttributeBuilderFunctions.characterIncidenceModifierHandler(button));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addEventListenersToNAndMInputSubmit() {
|
|
|
|
|
// 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.extensions.tokenAttributeBuilderFunctions.characterNMSubmitHandler(modalId));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toggleClass(elements, className, action) {
|
|
|
|
|
elements.forEach(element => {
|
|
|
|
|
document.querySelector(`[data-toggle-area="${element}"]`).classList[action](className);
|
|
|
|
@ -36,26 +56,27 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
|
|
|
|
|
resetQueryInputField() {
|
|
|
|
|
this.elements.queryInputField.innerHTML = '';
|
|
|
|
|
this.addPlaceholder();
|
|
|
|
|
this.addQueryElementTarget();
|
|
|
|
|
this.updateChipList();
|
|
|
|
|
this.queryPreviewBuilder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addQueryElementTarget() {
|
|
|
|
|
let queryElementTarget = nopaque.Utils.HTMLToElement(
|
|
|
|
|
`
|
|
|
|
|
<a class="query-element-target btn-floating btn-small blue-grey lighten-4 waves-effect waves-light tooltipped" style="margin-bottom:10px; margin-right:5px;" draggable="true" data-position="bottom" data-tooltip="Add an Element to your query">
|
|
|
|
|
<i class="material-icons">add</i>
|
|
|
|
|
</a>
|
|
|
|
|
`
|
|
|
|
|
);
|
|
|
|
|
this.elements.queryInputField.appendChild(queryElementTarget);
|
|
|
|
|
this.elements.queryElementTarget = queryElementTarget;
|
|
|
|
|
this.addEventListenersToQueryElementTarget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = nopaque.Utils.HTMLToElement('<span id="corpus-analysis-concordance-query-builder-input-field-placeholder">Click on a button to add a query component</span>');
|
|
|
|
|
this.elements.queryInputField.appendChild(placeholder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resetMaterializeSelection(selectionElements, value = "default") {
|
|
|
|
|
selectionElements.forEach(selectionElement => {
|
|
|
|
@ -89,32 +110,32 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
`
|
|
|
|
|
<span class="chip query-component" data-type="${dataType}" data-query="${queryText}" draggable="true" data-closing-tag="${isClosingTag}">
|
|
|
|
|
${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>'}
|
|
|
|
|
${isClosingTag ? '' : '<i class="material-icons close chip-action-button" data-chip-action="delete">close</i>'}
|
|
|
|
|
</span>
|
|
|
|
|
`
|
|
|
|
|
);
|
|
|
|
|
this.addActionListeners(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();
|
|
|
|
|
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) {
|
|
|
|
|
this.elements.queryInputField.insertBefore(queryChipElement, this.elements.queryChipElements[index]);
|
|
|
|
|
// If an index is given, inserts the query chip after the given index (only relevant for Incidence Modifier) and if there is a closing tag, inserts the query chip before the closing tag.
|
|
|
|
|
if (index !== null) {
|
|
|
|
|
this.updateChipList();
|
|
|
|
|
this.elements.queryChipElements[index].after(queryChipElement);
|
|
|
|
|
} else {
|
|
|
|
|
this.elements.queryInputField.appendChild(queryChipElement);
|
|
|
|
|
this.elements.queryInputField.insertBefore(queryChipElement, this.elements.queryElementTarget);
|
|
|
|
|
}
|
|
|
|
|
if (isClosingTag) {
|
|
|
|
|
this.moveQueryElementTarget(queryChipElement);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.updateChipList();
|
|
|
|
|
this.queryPreviewBuilder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
moveQueryElementTarget(element) {
|
|
|
|
|
this.elements.queryInputField.insertBefore(this.elements.queryElementTarget, element);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addActionListeners(queryChipElement) {
|
|
|
|
|
let notQuantifiableDataTypes = ['start-sentence', 'end-sentence', 'start-entity', 'start-empty-entity', 'end-entity', 'token-incidence-modifier'];
|
|
|
|
|
queryChipElement.addEventListener('click', (event) => {
|
|
|
|
@ -154,20 +175,13 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.deleteClosingTagHandler(elementIndex, 'end-sentence');
|
|
|
|
|
break;
|
|
|
|
|
case 'start-empty-entity':
|
|
|
|
|
case 'start-entity':
|
|
|
|
|
this.deleteClosingTagHandler(elementIndex, 'end-entity');
|
|
|
|
|
break;
|
|
|
|
@ -180,9 +194,6 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
this.elements.queryInputField.removeChild(attr);
|
|
|
|
|
if (this.elements.queryInputField.children.length === 0) {
|
|
|
|
|
this.addPlaceholder();
|
|
|
|
|
}
|
|
|
|
|
this.updateChipList();
|
|
|
|
|
this.queryPreviewBuilder();
|
|
|
|
|
}
|
|
|
|
@ -217,13 +228,17 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
|
|
|
|
|
let targetChipClone = targetChipElement.cloneNode(true);
|
|
|
|
|
element.insertAdjacentElement('afterend', targetChipClone);
|
|
|
|
|
|
|
|
|
|
//TODO: Change to two different functions for drag and drop
|
|
|
|
|
this.addDragDropListeners(targetChipClone, queryChipElement);
|
|
|
|
|
}
|
|
|
|
|
}, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleDragEnd() {
|
|
|
|
|
handleDragEnd(event) {
|
|
|
|
|
// is called when a query chip is dropped. It removes the dropzones and initializes the tooltips if the dragged element is the query element target.
|
|
|
|
|
if (event.target.classList.contains('query-element-target')) {
|
|
|
|
|
M.Tooltip.init(event.target);
|
|
|
|
|
}
|
|
|
|
|
document.querySelectorAll('.drop-target').forEach(target => target.remove());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -292,8 +307,8 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
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);
|
|
|
|
|
let selectedChipIndex = Array.from(this.elements.queryChipElements).indexOf(selectedChip);
|
|
|
|
|
this.submitQueryChipElement('token-incidence-modifier', incidenceModifierPretty, incidenceModifier, selectedChipIndex);
|
|
|
|
|
this.selectChipElement(selectedChip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -315,26 +330,27 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
this.tokenIncidenceModifierHandler(input, pretty_input);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
incidenceModifierEventListeners() {
|
|
|
|
|
// Eventlisteners for the incidence modifiers. There are two different types of incidence modifiers: token and character incidence modifiers.
|
|
|
|
|
document.querySelectorAll('.incidence-modifier-selection').forEach(button => {
|
|
|
|
|
let dropdownId = button.parentNode.parentNode.id;
|
|
|
|
|
if (dropdownId === 'corpus-analysis-concordance-token-incidence-modifiers-dropdown') {
|
|
|
|
|
button.addEventListener('click', () => this.tokenIncidenceModifierHandler(button.dataset.token, button.innerHTML));
|
|
|
|
|
} else if (dropdownId === 'corpus-analysis-concordance-character-incidence-modifiers-dropdown') {
|
|
|
|
|
button.addEventListener('click', () => this.extensions.tokenAttributeBuilderFunctions.characterIncidenceModifierHandler(button));
|
|
|
|
|
}
|
|
|
|
|
expertModeQueryBuilderSwitchHandler() {
|
|
|
|
|
let queryBuilderDisplay = document.querySelector("#corpus-analysis-concordance-query-builder-display");
|
|
|
|
|
let expertModeDisplay = document.querySelector("#corpus-analysis-concordance-expert-mode-display");
|
|
|
|
|
let expertModeSwitch = document.querySelector("#corpus-analysis-concordance-expert-mode-switch");
|
|
|
|
|
let submitModal = M.Modal.getInstance(document.querySelector('#corpus-analysis-concordance-switch-to-query-builder-submit-modal'));
|
|
|
|
|
|
|
|
|
|
let confirmSwitchToQueryBuilderButton = document.querySelector('.switch-action[data-switch-action="confirm"]');
|
|
|
|
|
confirmSwitchToQueryBuilderButton.addEventListener("click", () => {
|
|
|
|
|
queryBuilderDisplay.classList.remove("hide");
|
|
|
|
|
expertModeDisplay.classList.add("hide");
|
|
|
|
|
this.switchToQueryBuilderParser();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nAndMInputSubmitEventListeners() {
|
|
|
|
|
// Eventlisteners for the submit of n- and m-values of the incidence modifier modal for "exactly n" or "between n and m".
|
|
|
|
|
document.querySelectorAll('.n-m-submit-button').forEach(button => {
|
|
|
|
|
let modalId = button.dataset.modalId;
|
|
|
|
|
if (modalId === 'corpus-analysis-concordance-exactly-n-token-modal' || modalId === 'corpus-analysis-concordance-between-nm-token-modal') {
|
|
|
|
|
button.addEventListener('click', () => this.tokenNMSubmitHandler(modalId));
|
|
|
|
|
} else if (modalId === 'corpus-analysis-concordance-exactly-n-character-modal' || modalId === 'corpus-analysis-concordance-between-nm-character-modal') {
|
|
|
|
|
button.addEventListener('click', () => this.extensions.tokenAttributeBuilderFunctions.characterNMSubmitHandler(modalId));
|
|
|
|
|
expertModeSwitch.addEventListener("change", () => {
|
|
|
|
|
const isChecked = expertModeSwitch.checked;
|
|
|
|
|
if (isChecked) {
|
|
|
|
|
queryBuilderDisplay.classList.add("hide");
|
|
|
|
|
expertModeDisplay.classList.remove("hide");
|
|
|
|
|
this.switchToExpertModeParser();
|
|
|
|
|
} else {
|
|
|
|
|
submitModal.open();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@ -353,7 +369,7 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
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', 'token'];
|
|
|
|
|
let editableElements = ['start-entity', 'token'];
|
|
|
|
|
for (let chipElement of chipElements) {
|
|
|
|
|
let isClosingTag = closingTagElements.includes(chipElement['type']);
|
|
|
|
|
let isEditable = editableElements.includes(chipElement['type']);
|
|
|
|
@ -361,9 +377,6 @@ nopaque.corpus_analysis.query_builder.QueryBuilder = class QueryBuilder {
|
|
|
|
|
isEditable = false;
|
|
|
|
|
}
|
|
|
|
|
this.submitQueryChipElement(chipElement['type'], chipElement['pretty'], chipElement['query'], null, isClosingTag, isEditable);
|
|
|
|
|
if (isClosingTag) {
|
|
|
|
|
this.lockClosingChipElement(this.elements.queryChipElements[this.elements.queryChipElements.length-1]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|