import noteAutocompleteService from "../services/note_autocomplete.js"; import server from "../services/server.js"; import TabAwareWidget from "./tab_aware_widget.js"; import treeCache from "../services/tree_cache.js"; import ws from "../services/ws.js"; import toastService from "../services/toast.js"; import DeleteNoteSearchAction from "./search_actions/delete_note.js"; import DeleteLabelSearchAction from "./search_actions/delete_label.js"; import DeleteRelationSearchAction from "./search_actions/delete_relation.js"; import RenameLabelSearchAction from "./search_actions/rename_label.js"; import SetLabelValueSearchAction from "./search_actions/set_label_value.js"; import SetRelationTargetSearchAction from "./search_actions/set_relation_target.js"; import RenameRelationSearchAction from "./search_actions/rename_relation.js"; import ExecuteScriptSearchAction from "./search_actions/execute_script.js" import SearchString from "./search_options/search_string.js"; import FastSearch from "./search_options/fast_search.js"; import Ancestor from "./search_options/ancestor.js"; import IncludeArchivedNotes from "./search_options/include_archived_notes.js"; import OrderBy from "./search_options/order_by.js"; const TPL = `
Add search option:
`; const OPTION_CLASSES = [ SearchString, Ancestor, FastSearch, IncludeArchivedNotes, OrderBy ]; const ACTION_CLASSES = {}; for (const clazz of [ DeleteNoteSearchAction, DeleteLabelSearchAction, DeleteRelationSearchAction, RenameLabelSearchAction, RenameRelationSearchAction, SetLabelValueSearchAction, SetRelationTargetSearchAction, ExecuteScriptSearchAction ]) { ACTION_CLASSES[clazz.actionName] = clazz; } export default class SearchDefinitionWidget extends TabAwareWidget { static getType() { return "search"; } renderTitle(note) { return { show: note.type === 'search', activate: true, $title: 'Search' }; } doRender() { this.$widget = $(TPL); this.$component = this.$widget.find('.search-definition-widget'); this.contentSized(); this.overflowing(); this.$widget.on('click', '[data-search-option-add]', async event => { const searchOptionName = $(event.target).attr('data-search-option-add'); const clazz = OPTION_CLASSES.find(SearchOptionClass => SearchOptionClass.optionName === searchOptionName); if (clazz) { await clazz.create(this.noteId); } else { logError(`Unknown search option ${searchOptionName}`); } this.refresh(); }); this.$widget.on('click', '[data-action-add]', async event => { const actionName = $(event.target).attr('data-action-add'); await server.post(`notes/${this.noteId}/attributes`, { type: 'label', name: 'action', value: JSON.stringify({ name: actionName }) }); this.$widget.find('.action-add-toggle').dropdown('toggle'); await ws.waitForMaxKnownEntityChangeId(); this.refresh(); }); this.$widget.on('click', '[data-action-conf-del]', async event => { const attributeId = $(event.target).closest('[data-attribute-id]').attr('data-attribute-id'); await server.remove(`notes/${this.noteId}/attributes/${attributeId}`); await ws.waitForMaxKnownEntityChangeId(); this.refresh(); }); this.$searchOptions = this.$widget.find('.search-options'); this.$actionOptions = this.$widget.find('.action-options'); this.$searchButton = this.$widget.find('.search-button'); this.$searchButton.on('click', () => this.triggerCommand('refreshResults')); this.$searchAndExecuteButton = this.$widget.find('.search-and-execute-button'); this.$searchAndExecuteButton.on('click', () => this.searchAndExecute()); } async refreshResultsCommand() { await treeCache.reloadNotes([this.noteId]); this.triggerEvent('searchRefreshed', {tabId: this.tabContext.tabId}); } async refreshSearchDefinitionCommand() { await this.refresh(); } async refreshWithNote(note) { this.$component.show(); this.$searchOptions.empty(); for (const OptionClass of OPTION_CLASSES) { const {attributeType, optionName} = OptionClass; const attr = this.note.getAttribute(attributeType, optionName); this.$widget.find(`[data-search-option-add='${optionName}'`).toggle(!attr); if (attr) { const searchOption = new OptionClass(attr, this.note).setParent(this); this.child(searchOption); this.$searchOptions.append(searchOption.render()); } } this.$actionOptions.empty(); const actionLabels = this.note.getLabels('action'); for (const actionAttr of actionLabels) { let actionDef; try { actionDef = JSON.parse(actionAttr.value); } catch (e) { logError(`Parsing of attribute: '${actionAttr.value}' failed with error: ${e.message}`); continue; } const ActionClass = ACTION_CLASSES[actionDef.name]; if (!ActionClass) { logError(`No action class for '${actionDef.name}' found.`); continue; } const action = new ActionClass(actionAttr, actionDef).setParent(this); this.child(action); this.$actionOptions.append(action.render()); } this.$searchAndExecuteButton.css('visibility', actionLabels.length > 0 ? 'visible' : 'hidden'); } getContent() { return ''; } async searchAndExecute() { await server.post(`search-and-execute-note/${this.noteId}`); this.triggerCommand('refreshResults'); toastService.showMessage('Actions have been executed.', 3000); } }