From 68bba623b6ee1c69cfa8f2f201a54cf1129cac33 Mon Sep 17 00:00:00 2001 From: azivner Date: Mon, 26 Mar 2018 21:50:47 -0400 Subject: [PATCH] split up autocomplete related functionality --- src/public/javascripts/dialogs/add_link.js | 5 +- .../javascripts/dialogs/edit_tree_prefix.js | 3 +- .../javascripts/dialogs/jump_to_note.js | 3 +- .../javascripts/dialogs/recent_notes.js | 3 +- .../javascripts/services/autocomplete.js | 100 ++++++++++++++++++ src/public/javascripts/services/init.js | 76 ++----------- src/public/javascripts/services/link.js | 2 +- src/public/javascripts/services/tree.js | 91 +--------------- src/public/javascripts/services/tree_utils.js | 35 ++++++ 9 files changed, 155 insertions(+), 163 deletions(-) create mode 100644 src/public/javascripts/services/autocomplete.js diff --git a/src/public/javascripts/dialogs/add_link.js b/src/public/javascripts/dialogs/add_link.js index 2f79cfdde..5ad638580 100644 --- a/src/public/javascripts/dialogs/add_link.js +++ b/src/public/javascripts/dialogs/add_link.js @@ -3,6 +3,7 @@ import cloningService from '../services/cloning.js'; import linkService from '../services/link.js'; import noteDetailService from '../services/note_detail.js'; import treeUtils from '../services/tree_utils.js'; +import autocompleteService from '../services/autocomplete.js'; const $dialog = $("#add-link-dialog"); const $form = $("#add-link-form"); @@ -46,13 +47,13 @@ async function showDialog() { $linkTitle.val(''); function setDefaultLinkTitle(noteId) { - const noteTitle = treeService.getNoteTitle(noteId); + const noteTitle = treeUtils.getNoteTitle(noteId); $linkTitle.val(noteTitle); } $autoComplete.autocomplete({ - source: await treeService.getAutocompleteItems(), + source: await autocompleteService.getAutocompleteItems(), minLength: 0, change: () => { const val = $autoComplete.val(); diff --git a/src/public/javascripts/dialogs/edit_tree_prefix.js b/src/public/javascripts/dialogs/edit_tree_prefix.js index 41a607804..e4f69c9b1 100644 --- a/src/public/javascripts/dialogs/edit_tree_prefix.js +++ b/src/public/javascripts/dialogs/edit_tree_prefix.js @@ -1,6 +1,7 @@ import treeService from '../services/tree.js'; import server from '../services/server.js'; import treeCache from "../services/tree_cache.js"; +import treeUtils from "../services/tree_utils.js"; const $dialog = $("#edit-tree-prefix-dialog"); const $form = $("#edit-tree-prefix-form"); @@ -24,7 +25,7 @@ async function showDialog() { $treePrefixInput.val(branch.prefix).focus(); - const noteTitle = treeService.getNoteTitle(currentNode.data.noteId); + const noteTitle = treeUtils.getNoteTitle(currentNode.data.noteId); $noteTitle.html(noteTitle); } diff --git a/src/public/javascripts/dialogs/jump_to_note.js b/src/public/javascripts/dialogs/jump_to_note.js index cf3a20a93..50f2f5e22 100644 --- a/src/public/javascripts/dialogs/jump_to_note.js +++ b/src/public/javascripts/dialogs/jump_to_note.js @@ -1,6 +1,7 @@ import treeService from '../services/tree.js'; import linkService from '../services/link.js'; import utils from '../services/utils.js'; +import autocompleteService from '../services/autocomplete.js'; const $dialog = $("#jump-to-note-dialog"); const $autoComplete = $("#jump-to-note-autocomplete"); @@ -17,7 +18,7 @@ async function showDialog() { }); await $autoComplete.autocomplete({ - source: await utils.stopWatch("building autocomplete", treeService.getAutocompleteItems), + source: await utils.stopWatch("building autocomplete", autocompleteService.getAutocompleteItems), minLength: 0 }); } diff --git a/src/public/javascripts/dialogs/recent_notes.js b/src/public/javascripts/dialogs/recent_notes.js index d51df4db8..41fed8013 100644 --- a/src/public/javascripts/dialogs/recent_notes.js +++ b/src/public/javascripts/dialogs/recent_notes.js @@ -2,6 +2,7 @@ import treeService from '../services/tree.js'; import messagingService from '../services/messaging.js'; import server from '../services/server.js'; import utils from "../services/utils.js"; +import treeUtils from "../services/tree_utils.js"; const $dialog = $("#recent-notes-dialog"); const $searchInput = $('#recent-notes-search-input'); @@ -46,7 +47,7 @@ async function showDialog() { let noteTitle; try { - noteTitle = await treeService.getNotePathTitle(notePath); + noteTitle = await treeUtils.getNotePathTitle(notePath); } catch (e) { noteTitle = "[error - can't find note title]"; diff --git a/src/public/javascripts/services/autocomplete.js b/src/public/javascripts/services/autocomplete.js new file mode 100644 index 000000000..cea699667 --- /dev/null +++ b/src/public/javascripts/services/autocomplete.js @@ -0,0 +1,100 @@ +import treeCache from "./tree_cache.js"; +import treeUtils from "./tree_utils.js"; + +async function getAutocompleteItems(parentNoteId, notePath, titlePath) { + if (!parentNoteId) { + parentNoteId = 'root'; + } + + const parentNote = await treeCache.getNote(parentNoteId); + const childNotes = await parentNote.getChildNotes(); + + if (!childNotes.length) { + return []; + } + + if (!notePath) { + notePath = ''; + } + + if (!titlePath) { + titlePath = ''; + } + + // https://github.com/zadam/trilium/issues/46 + // unfortunately not easy to implement because we don't have an easy access to note's isProtected property + + const autocompleteItems = []; + + for (const childNote of childNotes) { + if (childNote.hideInAutocomplete) { + continue; + } + + const childNotePath = (notePath ? (notePath + '/') : '') + childNote.noteId; + const childTitlePath = (titlePath ? (titlePath + ' / ') : '') + await treeUtils.getNoteTitle(childNote.noteId, parentNoteId); + + autocompleteItems.push({ + value: childTitlePath + ' (' + childNotePath + ')', + label: childTitlePath + }); + + const childItems = await getAutocompleteItems(childNote.noteId, childNotePath, childTitlePath); + + for (const childItem of childItems) { + autocompleteItems.push(childItem); + } + } + + return autocompleteItems; +} + +// Overrides the default autocomplete filter function to search for matched on atleast 1 word in each of the input term's words +$.ui.autocomplete.filter = (array, terms) => { + if (!terms) { + return array; + } + + const startDate = new Date(); + + const results = []; + const tokens = terms.toLowerCase().split(" "); + + for (const item of array) { + const lcLabel = item.label.toLowerCase(); + + const found = tokens.every(token => lcLabel.indexOf(token) !== -1); + if (!found) { + continue; + } + + // this is not completely correct and might cause minor problems with note with names containing this " / " + const lastSegmentIndex = lcLabel.lastIndexOf(" / "); + + if (lastSegmentIndex !== -1) { + const lastSegment = lcLabel.substr(lastSegmentIndex + 3); + + // at least some token needs to be in the last segment (leaf note), otherwise this + // particular note is not that interesting (query is satisfied by parent note) + const foundInLastSegment = tokens.some(token => lastSegment.indexOf(token) !== -1); + + if (!foundInLastSegment) { + continue; + } + } + + results.push(item); + + if (results.length > 100) { + break; + } + } + + console.log("Search took " + (new Date().getTime() - startDate.getTime()) + "ms"); + + return results; +}; + +export default { + getAutocompleteItems +}; \ No newline at end of file diff --git a/src/public/javascripts/services/init.js b/src/public/javascripts/services/init.js index 6398dc27f..f50d392ad 100644 --- a/src/public/javascripts/services/init.js +++ b/src/public/javascripts/services/init.js @@ -13,33 +13,19 @@ jQuery.hotkeys.options.filterInputAcceptingElements = false; jQuery.hotkeys.options.filterContentEditable = false; jQuery.hotkeys.options.filterTextInputs = false; -$(document).bind('keydown', 'alt+m', e => { - $(".hide-toggle").toggleClass("suppressed"); - - e.preventDefault(); -}); +utils.bindShortcut('alt+m', e => $(".hide-toggle").toggleClass("suppressed")); // hide (toggle) everything except for the note content for distraction free writing -$(document).bind('keydown', 'alt+t', e => { +utils.bindShortcut('alt+t', e => { const date = new Date(); const dateString = utils.formatDateTime(date); linkService.addTextToEditor(dateString); - - e.preventDefault(); }); -$(document).bind('keydown', 'f5', () => { - utils.reloadApp(); +utils.bindShortcut('f5', utils.reloadApp); - return false; -}); - -$(document).bind('keydown', 'ctrl+r', () => { - utils.reloadApp(); - - return false; -}); +utils.bindShortcut('ctrl+r', utils.reloadApp); $(document).bind('keydown', 'ctrl+shift+i', () => { if (utils.isElectron()) { @@ -62,22 +48,18 @@ $(document).bind('keydown', 'ctrl+f', () => { } }); -$(document).bind('keydown', "ctrl+shift+up", () => { +utils.bindShortcut("ctrl+shift+up", () => { const node = treeService.getCurrentNode(); node.navigate($.ui.keyCode.UP, true); $("#note-detail").focus(); - - return false; }); -$(document).bind('keydown', "ctrl+shift+down", () => { +utils.bindShortcut("ctrl+shift+down", () => { const node = treeService.getCurrentNode(); node.navigate($.ui.keyCode.DOWN, true); $("#note-detail").focus(); - - return false; }); $(document).bind('keydown', 'ctrl+-', () => { @@ -110,52 +92,6 @@ $(window).on('beforeunload', () => { noteDetailService.saveNoteIfChanged(); }); -// Overrides the default autocomplete filter function to search for matched on atleast 1 word in each of the input term's words -$.ui.autocomplete.filter = (array, terms) => { - if (!terms) { - return array; - } - - const startDate = new Date(); - - const results = []; - const tokens = terms.toLowerCase().split(" "); - - for (const item of array) { - const lcLabel = item.label.toLowerCase(); - - const found = tokens.every(token => lcLabel.indexOf(token) !== -1); - if (!found) { - continue; - } - - // this is not completely correct and might cause minor problems with note with names containing this " / " - const lastSegmentIndex = lcLabel.lastIndexOf(" / "); - - if (lastSegmentIndex !== -1) { - const lastSegment = lcLabel.substr(lastSegmentIndex + 3); - - // at least some token needs to be in the last segment (leaf note), otherwise this - // particular note is not that interesting (query is satisfied by parent note) - const foundInLastSegment = tokens.some(token => lastSegment.indexOf(token) !== -1); - - if (!foundInLastSegment) { - continue; - } - } - - results.push(item); - - if (results.length > 100) { - break; - } - } - - console.log("Search took " + (new Date().getTime() - startDate.getTime()) + "ms"); - - return results; -}; - $(document).tooltip({ items: "#note-detail a", content: function(callback) { diff --git a/src/public/javascripts/services/link.js b/src/public/javascripts/services/link.js index 5f2a84e8c..4d6a5b75e 100644 --- a/src/public/javascripts/services/link.js +++ b/src/public/javascripts/services/link.js @@ -27,7 +27,7 @@ function createNoteLink(notePath, noteTitle) { if (!noteTitle) { const noteId = treeUtils.getNoteIdFromNotePath(notePath); - noteTitle = treeService.getNoteTitle(noteId); + noteTitle = treeUtils.getNoteTitle(noteId); } const noteLink = $("", { diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 43c1d5c67..d7e0669d7 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -23,22 +23,6 @@ const $scrollToCurrentNoteButton = $("#scroll-to-current-note-button"); let startNotePath = null; -async function getNoteTitle(noteId, parentNoteId = null) { - utils.assertArguments(noteId); - - let {title} = await treeCache.getNote(noteId); - - if (parentNoteId !== null) { - const branch = await treeCache.getBranchByChildParent(noteId, parentNoteId); - - if (branch && branch.prefix) { - title = branch.prefix + ' - ' + title; - } - } - - return title; -} - // note that if you want to access data like noteId or isProtected, you need to go into "data" property function getCurrentNode() { return $tree.fancytree("getActiveNode"); @@ -78,7 +62,7 @@ async function setPrefix(branchId, prefix) { } async function setNodeTitleWithPrefix(node) { - const noteTitle = await getNoteTitle(node.data.noteId); + const noteTitle = await treeUtils.getNoteTitle(node.data.noteId); const branch = await treeCache.getBranch(node.data.branchId); const prefix = branch.prefix; @@ -300,7 +284,7 @@ async function showParentList(noteId, node) { const parentNotePath = await getSomeNotePath(parentNote); // this is to avoid having root notes leading '/' const notePath = parentNotePath ? (parentNotePath + '/' + noteId) : noteId; - const title = await getNotePathTitle(notePath); + const title = await treeUtils.getNotePathTitle(notePath); let item; @@ -316,22 +300,6 @@ async function showParentList(noteId, node) { } } -async function getNotePathTitle(notePath) { - utils.assertArguments(notePath); - - const titlePath = []; - - let parentNoteId = 'root'; - - for (const noteId of notePath.split('/')) { - titlePath.push(await getNoteTitle(noteId, parentNoteId)); - - parentNoteId = noteId; - } - - return titlePath.join(' / '); -} - async function getSomeNotePath(note) { utils.assertArguments(note); @@ -695,54 +663,6 @@ function setProtected(noteId, isProtected) { setBranchBackgroundBasedOnProtectedStatus(noteId); } -async function getAutocompleteItems(parentNoteId, notePath, titlePath) { - if (!parentNoteId) { - parentNoteId = 'root'; - } - - const parentNote = await treeCache.getNote(parentNoteId); - const childNotes = await parentNote.getChildNotes(); - - if (!childNotes.length) { - return []; - } - - if (!notePath) { - notePath = ''; - } - - if (!titlePath) { - titlePath = ''; - } - - // https://github.com/zadam/trilium/issues/46 - // unfortunately not easy to implement because we don't have an easy access to note's isProtected property - - const autocompleteItems = []; - - for (const childNote of childNotes) { - if (childNote.hideInAutocomplete) { - continue; - } - - const childNotePath = (notePath ? (notePath + '/') : '') + childNote.noteId; - const childTitlePath = (titlePath ? (titlePath + ' / ') : '') + await getNoteTitle(childNote.noteId, parentNoteId); - - autocompleteItems.push({ - value: childTitlePath + ' (' + childNotePath + ')', - label: childTitlePath - }); - - const childItems = await getAutocompleteItems(childNote.noteId, childNotePath, childTitlePath); - - for (const childItem of childItems) { - autocompleteItems.push(childItem); - } - } - - return autocompleteItems; -} - async function setNoteTitle(noteId, title) { utils.assertArguments(noteId); @@ -886,18 +806,15 @@ export default { scrollToCurrentNote, setBranchBackgroundBasedOnProtectedStatus, setProtected, - getCurrentNode, expandToNote, activateNode, + getCurrentNode, getCurrentNotePath, - getNoteTitle, setCurrentNotePathToHash, - getAutocompleteItems, setNoteTitle, + setPrefix, createNewTopLevelNote, createNote, - setPrefix, - getNotePathTitle, removeParentChildRelation, setParentChildRelation, getSelectedNodes, diff --git a/src/public/javascripts/services/tree_utils.js b/src/public/javascripts/services/tree_utils.js index 4f189aa4d..5e8686be8 100644 --- a/src/public/javascripts/services/tree_utils.js +++ b/src/public/javascripts/services/tree_utils.js @@ -1,4 +1,5 @@ import utils from './utils.js'; +import treeCache from "./tree_cache.js"; const $tree = $("#tree"); @@ -30,9 +31,43 @@ function getNotePath(node) { return path.reverse().join("/"); } +async function getNoteTitle(noteId, parentNoteId = null) { + utils.assertArguments(noteId); + + let {title} = await treeCache.getNote(noteId); + + if (parentNoteId !== null) { + const branch = await treeCache.getBranchByChildParent(noteId, parentNoteId); + + if (branch && branch.prefix) { + title = branch.prefix + ' - ' + title; + } + } + + return title; +} + +async function getNotePathTitle(notePath) { + utils.assertArguments(notePath); + + const titlePath = []; + + let parentNoteId = 'root'; + + for (const noteId of notePath.split('/')) { + titlePath.push(await getNoteTitle(noteId, parentNoteId)); + + parentNoteId = noteId; + } + + return titlePath.join(' / '); +} + export default { getParentProtectedStatus, getNodeByKey, getNotePath, getNoteIdFromNotePath, + getNoteTitle, + getNotePathTitle, }; \ No newline at end of file